1c6be2bd0SJeffle Xu // SPDX-License-Identifier: GPL-2.0-or-later 2c6be2bd0SJeffle Xu /* 3c6be2bd0SJeffle Xu * Copyright (C) 2022, Alibaba Cloud 48b7adf1dSJia Zhu * Copyright (C) 2022, Bytedance Inc. All rights reserved. 5c6be2bd0SJeffle Xu */ 6c6be2bd0SJeffle Xu #include <linux/fscache.h> 7c6be2bd0SJeffle Xu #include "internal.h" 8c6be2bd0SJeffle Xu 98b7adf1dSJia Zhu static DEFINE_MUTEX(erofs_domain_list_lock); 107d419637SJia Zhu static DEFINE_MUTEX(erofs_domain_cookies_lock); 118b7adf1dSJia Zhu static LIST_HEAD(erofs_domain_list); 12a9849560SJia Zhu static struct vfsmount *erofs_pseudo_mnt; 138b7adf1dSJia Zhu 14d435d532SXin Yin static struct netfs_io_request *erofs_fscache_alloc_request(struct address_space *mapping, 15d435d532SXin Yin loff_t start, size_t len) 16d435d532SXin Yin { 17d435d532SXin Yin struct netfs_io_request *rreq; 18d435d532SXin Yin 19d435d532SXin Yin rreq = kzalloc(sizeof(struct netfs_io_request), GFP_KERNEL); 20d435d532SXin Yin if (!rreq) 21d435d532SXin Yin return ERR_PTR(-ENOMEM); 22d435d532SXin Yin 23d435d532SXin Yin rreq->start = start; 24d435d532SXin Yin rreq->len = len; 25d435d532SXin Yin rreq->mapping = mapping; 26b5cb79dcSXin Yin rreq->inode = mapping->host; 27d435d532SXin Yin INIT_LIST_HEAD(&rreq->subrequests); 28d435d532SXin Yin refcount_set(&rreq->ref, 1); 29d435d532SXin Yin return rreq; 30d435d532SXin Yin } 31d435d532SXin Yin 32d435d532SXin Yin static void erofs_fscache_put_request(struct netfs_io_request *rreq) 33d435d532SXin Yin { 34d435d532SXin Yin if (!refcount_dec_and_test(&rreq->ref)) 35d435d532SXin Yin return; 36d435d532SXin Yin if (rreq->cache_resources.ops) 37d435d532SXin Yin rreq->cache_resources.ops->end_operation(&rreq->cache_resources); 38d435d532SXin Yin kfree(rreq); 39d435d532SXin Yin } 40d435d532SXin Yin 41d435d532SXin Yin static void erofs_fscache_put_subrequest(struct netfs_io_subrequest *subreq) 42d435d532SXin Yin { 43d435d532SXin Yin if (!refcount_dec_and_test(&subreq->ref)) 44d435d532SXin Yin return; 45d435d532SXin Yin erofs_fscache_put_request(subreq->rreq); 46d435d532SXin Yin kfree(subreq); 47d435d532SXin Yin } 48d435d532SXin Yin 49d435d532SXin Yin static void erofs_fscache_clear_subrequests(struct netfs_io_request *rreq) 50d435d532SXin Yin { 51d435d532SXin Yin struct netfs_io_subrequest *subreq; 52d435d532SXin Yin 53d435d532SXin Yin while (!list_empty(&rreq->subrequests)) { 54d435d532SXin Yin subreq = list_first_entry(&rreq->subrequests, 55d435d532SXin Yin struct netfs_io_subrequest, rreq_link); 56d435d532SXin Yin list_del(&subreq->rreq_link); 57d435d532SXin Yin erofs_fscache_put_subrequest(subreq); 58d435d532SXin Yin } 59d435d532SXin Yin } 60d435d532SXin Yin 61d435d532SXin Yin static void erofs_fscache_rreq_unlock_folios(struct netfs_io_request *rreq) 62d435d532SXin Yin { 63d435d532SXin Yin struct netfs_io_subrequest *subreq; 64d435d532SXin Yin struct folio *folio; 65d435d532SXin Yin unsigned int iopos = 0; 66d435d532SXin Yin pgoff_t start_page = rreq->start / PAGE_SIZE; 67d435d532SXin Yin pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1; 68d435d532SXin Yin bool subreq_failed = false; 69d435d532SXin Yin 70d435d532SXin Yin XA_STATE(xas, &rreq->mapping->i_pages, start_page); 71d435d532SXin Yin 72d435d532SXin Yin subreq = list_first_entry(&rreq->subrequests, 73d435d532SXin Yin struct netfs_io_subrequest, rreq_link); 74d435d532SXin Yin subreq_failed = (subreq->error < 0); 75d435d532SXin Yin 76d435d532SXin Yin rcu_read_lock(); 77d435d532SXin Yin xas_for_each(&xas, folio, last_page) { 78d435d532SXin Yin unsigned int pgpos = 79d435d532SXin Yin (folio_index(folio) - start_page) * PAGE_SIZE; 80d435d532SXin Yin unsigned int pgend = pgpos + folio_size(folio); 81d435d532SXin Yin bool pg_failed = false; 82d435d532SXin Yin 83d435d532SXin Yin for (;;) { 84d435d532SXin Yin if (!subreq) { 85d435d532SXin Yin pg_failed = true; 86d435d532SXin Yin break; 87d435d532SXin Yin } 88d435d532SXin Yin 89d435d532SXin Yin pg_failed |= subreq_failed; 90d435d532SXin Yin if (pgend < iopos + subreq->len) 91d435d532SXin Yin break; 92d435d532SXin Yin 93d435d532SXin Yin iopos += subreq->len; 94d435d532SXin Yin if (!list_is_last(&subreq->rreq_link, 95d435d532SXin Yin &rreq->subrequests)) { 96d435d532SXin Yin subreq = list_next_entry(subreq, rreq_link); 97d435d532SXin Yin subreq_failed = (subreq->error < 0); 98d435d532SXin Yin } else { 99d435d532SXin Yin subreq = NULL; 100d435d532SXin Yin subreq_failed = false; 101d435d532SXin Yin } 102d435d532SXin Yin if (pgend == iopos) 103d435d532SXin Yin break; 104d435d532SXin Yin } 105d435d532SXin Yin 106d435d532SXin Yin if (!pg_failed) 107d435d532SXin Yin folio_mark_uptodate(folio); 108d435d532SXin Yin 109d435d532SXin Yin folio_unlock(folio); 110d435d532SXin Yin } 111d435d532SXin Yin rcu_read_unlock(); 112d435d532SXin Yin } 113d435d532SXin Yin 114d435d532SXin Yin static void erofs_fscache_rreq_complete(struct netfs_io_request *rreq) 115d435d532SXin Yin { 116d435d532SXin Yin erofs_fscache_rreq_unlock_folios(rreq); 117d435d532SXin Yin erofs_fscache_clear_subrequests(rreq); 118d435d532SXin Yin erofs_fscache_put_request(rreq); 119d435d532SXin Yin } 120d435d532SXin Yin 121d435d532SXin Yin static void erofc_fscache_subreq_complete(void *priv, 122d435d532SXin Yin ssize_t transferred_or_error, bool was_async) 123d435d532SXin Yin { 124d435d532SXin Yin struct netfs_io_subrequest *subreq = priv; 125d435d532SXin Yin struct netfs_io_request *rreq = subreq->rreq; 126d435d532SXin Yin 127d435d532SXin Yin if (IS_ERR_VALUE(transferred_or_error)) 128d435d532SXin Yin subreq->error = transferred_or_error; 129d435d532SXin Yin 130d435d532SXin Yin if (atomic_dec_and_test(&rreq->nr_outstanding)) 131d435d532SXin Yin erofs_fscache_rreq_complete(rreq); 132d435d532SXin Yin 133d435d532SXin Yin erofs_fscache_put_subrequest(subreq); 134d435d532SXin Yin } 135d435d532SXin Yin 136ec00b5e2SJeffle Xu /* 137ec00b5e2SJeffle Xu * Read data from fscache and fill the read data into page cache described by 138d435d532SXin Yin * @rreq, which shall be both aligned with PAGE_SIZE. @pstart describes 139ec00b5e2SJeffle Xu * the start physical address in the cache file. 140ec00b5e2SJeffle Xu */ 141d435d532SXin Yin static int erofs_fscache_read_folios_async(struct fscache_cookie *cookie, 142d435d532SXin Yin struct netfs_io_request *rreq, loff_t pstart) 143ec00b5e2SJeffle Xu { 144ec00b5e2SJeffle Xu enum netfs_io_source source; 145d435d532SXin Yin struct super_block *sb = rreq->mapping->host->i_sb; 146d435d532SXin Yin struct netfs_io_subrequest *subreq; 147d435d532SXin Yin struct netfs_cache_resources *cres = &rreq->cache_resources; 148ec00b5e2SJeffle Xu struct iov_iter iter; 149d435d532SXin Yin loff_t start = rreq->start; 150d435d532SXin Yin size_t len = rreq->len; 151ec00b5e2SJeffle Xu size_t done = 0; 152ec00b5e2SJeffle Xu int ret; 153ec00b5e2SJeffle Xu 154d435d532SXin Yin atomic_set(&rreq->nr_outstanding, 1); 155d435d532SXin Yin 156ec00b5e2SJeffle Xu ret = fscache_begin_read_operation(cres, cookie); 157ec00b5e2SJeffle Xu if (ret) 158d435d532SXin Yin goto out; 159ec00b5e2SJeffle Xu 160ec00b5e2SJeffle Xu while (done < len) { 161d435d532SXin Yin subreq = kzalloc(sizeof(struct netfs_io_subrequest), 162d435d532SXin Yin GFP_KERNEL); 163d435d532SXin Yin if (subreq) { 164d435d532SXin Yin INIT_LIST_HEAD(&subreq->rreq_link); 165d435d532SXin Yin refcount_set(&subreq->ref, 2); 166d435d532SXin Yin subreq->rreq = rreq; 167d435d532SXin Yin refcount_inc(&rreq->ref); 168d435d532SXin Yin } else { 169d435d532SXin Yin ret = -ENOMEM; 170d435d532SXin Yin goto out; 171d435d532SXin Yin } 172ec00b5e2SJeffle Xu 173d435d532SXin Yin subreq->start = pstart + done; 174d435d532SXin Yin subreq->len = len - done; 175d435d532SXin Yin subreq->flags = 1 << NETFS_SREQ_ONDEMAND; 176d435d532SXin Yin 177d435d532SXin Yin list_add_tail(&subreq->rreq_link, &rreq->subrequests); 178d435d532SXin Yin 179d435d532SXin Yin source = cres->ops->prepare_read(subreq, LLONG_MAX); 180d435d532SXin Yin if (WARN_ON(subreq->len == 0)) 181ec00b5e2SJeffle Xu source = NETFS_INVALID_READ; 182ec00b5e2SJeffle Xu if (source != NETFS_READ_FROM_CACHE) { 183ec00b5e2SJeffle Xu erofs_err(sb, "failed to fscache prepare_read (source %d)", 184ec00b5e2SJeffle Xu source); 185ec00b5e2SJeffle Xu ret = -EIO; 186d435d532SXin Yin subreq->error = ret; 187d435d532SXin Yin erofs_fscache_put_subrequest(subreq); 188ec00b5e2SJeffle Xu goto out; 189ec00b5e2SJeffle Xu } 190ec00b5e2SJeffle Xu 191d435d532SXin Yin atomic_inc(&rreq->nr_outstanding); 192d435d532SXin Yin 193d435d532SXin Yin iov_iter_xarray(&iter, READ, &rreq->mapping->i_pages, 194d435d532SXin Yin start + done, subreq->len); 195d435d532SXin Yin 196d435d532SXin Yin ret = fscache_read(cres, subreq->start, &iter, 197d435d532SXin Yin NETFS_READ_HOLE_FAIL, 198d435d532SXin Yin erofc_fscache_subreq_complete, subreq); 199d435d532SXin Yin if (ret == -EIOCBQUEUED) 200d435d532SXin Yin ret = 0; 201ec00b5e2SJeffle Xu if (ret) { 202ec00b5e2SJeffle Xu erofs_err(sb, "failed to fscache_read (ret %d)", ret); 203ec00b5e2SJeffle Xu goto out; 204ec00b5e2SJeffle Xu } 205ec00b5e2SJeffle Xu 206d435d532SXin Yin done += subreq->len; 207ec00b5e2SJeffle Xu } 208ec00b5e2SJeffle Xu out: 209d435d532SXin Yin if (atomic_dec_and_test(&rreq->nr_outstanding)) 210d435d532SXin Yin erofs_fscache_rreq_complete(rreq); 211d435d532SXin Yin 212ec00b5e2SJeffle Xu return ret; 213ec00b5e2SJeffle Xu } 214ec00b5e2SJeffle Xu 215fdaf9a58SLinus Torvalds static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio) 2165375e7c8SJeffle Xu { 2175375e7c8SJeffle Xu int ret; 2185375e7c8SJeffle Xu struct super_block *sb = folio_mapping(folio)->host->i_sb; 219d435d532SXin Yin struct netfs_io_request *rreq; 2205375e7c8SJeffle Xu struct erofs_map_dev mdev = { 2215375e7c8SJeffle Xu .m_deviceid = 0, 2225375e7c8SJeffle Xu .m_pa = folio_pos(folio), 2235375e7c8SJeffle Xu }; 2245375e7c8SJeffle Xu 2255375e7c8SJeffle Xu ret = erofs_map_dev(sb, &mdev); 2265375e7c8SJeffle Xu if (ret) 2275375e7c8SJeffle Xu goto out; 2285375e7c8SJeffle Xu 229d435d532SXin Yin rreq = erofs_fscache_alloc_request(folio_mapping(folio), 230d435d532SXin Yin folio_pos(folio), folio_size(folio)); 2315bd9628bSSun Ke if (IS_ERR(rreq)) { 2325bd9628bSSun Ke ret = PTR_ERR(rreq); 233d435d532SXin Yin goto out; 2345bd9628bSSun Ke } 235d435d532SXin Yin 236d435d532SXin Yin return erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 237d435d532SXin Yin rreq, mdev.m_pa); 2385375e7c8SJeffle Xu out: 2395375e7c8SJeffle Xu folio_unlock(folio); 2405375e7c8SJeffle Xu return ret; 2415375e7c8SJeffle Xu } 2425375e7c8SJeffle Xu 2431ae9470cSJingbo Xu /* 2441ae9470cSJingbo Xu * Read into page cache in the range described by (@pos, @len). 2451ae9470cSJingbo Xu * 2461ae9470cSJingbo Xu * On return, the caller is responsible for page unlocking if the output @unlock 2471ae9470cSJingbo Xu * is true, or the callee will take this responsibility through netfs_io_request 2481ae9470cSJingbo Xu * interface. 2491ae9470cSJingbo Xu * 2501ae9470cSJingbo Xu * The return value is the number of bytes successfully handled, or negative 2511ae9470cSJingbo Xu * error code on failure. The only exception is that, the length of the range 2521ae9470cSJingbo Xu * instead of the error code is returned on failure after netfs_io_request is 2531ae9470cSJingbo Xu * allocated, so that .readahead() could advance rac accordingly. 2541ae9470cSJingbo Xu */ 2551ae9470cSJingbo Xu static int erofs_fscache_data_read(struct address_space *mapping, 2561ae9470cSJingbo Xu loff_t pos, size_t len, bool *unlock) 257bd735bdaSJeffle Xu { 2581ae9470cSJingbo Xu struct inode *inode = mapping->host; 2591ae9470cSJingbo Xu struct super_block *sb = inode->i_sb; 2601ae9470cSJingbo Xu struct netfs_io_request *rreq; 2611ae9470cSJingbo Xu struct erofs_map_blocks map; 2621ae9470cSJingbo Xu struct erofs_map_dev mdev; 2631ae9470cSJingbo Xu struct iov_iter iter; 2641ae9470cSJingbo Xu size_t count; 2651ae9470cSJingbo Xu int ret; 2661ae9470cSJingbo Xu 2671ae9470cSJingbo Xu *unlock = true; 2681ae9470cSJingbo Xu 2691ae9470cSJingbo Xu map.m_la = pos; 2701ae9470cSJingbo Xu ret = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW); 2711ae9470cSJingbo Xu if (ret) 2721ae9470cSJingbo Xu return ret; 2731ae9470cSJingbo Xu 2741ae9470cSJingbo Xu if (map.m_flags & EROFS_MAP_META) { 275bd735bdaSJeffle Xu struct erofs_buf buf = __EROFS_BUF_INITIALIZER; 276bd735bdaSJeffle Xu erofs_blk_t blknr; 2771ae9470cSJingbo Xu size_t offset, size; 2781ae9470cSJingbo Xu void *src; 279bd735bdaSJeffle Xu 280bd735bdaSJeffle Xu /* For tail packing layout, the offset may be non-zero. */ 2811ae9470cSJingbo Xu offset = erofs_blkoff(map.m_pa); 2821ae9470cSJingbo Xu blknr = erofs_blknr(map.m_pa); 2831ae9470cSJingbo Xu size = map.m_llen; 284bd735bdaSJeffle Xu 285bd735bdaSJeffle Xu src = erofs_read_metabuf(&buf, sb, blknr, EROFS_KMAP); 286bd735bdaSJeffle Xu if (IS_ERR(src)) 287bd735bdaSJeffle Xu return PTR_ERR(src); 288bd735bdaSJeffle Xu 2891ae9470cSJingbo Xu iov_iter_xarray(&iter, READ, &mapping->i_pages, pos, PAGE_SIZE); 29075e43355SJingbo Xu if (copy_to_iter(src + offset, size, &iter) != size) { 29175e43355SJingbo Xu erofs_put_metabuf(&buf); 2921ae9470cSJingbo Xu return -EFAULT; 29375e43355SJingbo Xu } 2941ae9470cSJingbo Xu iov_iter_zero(PAGE_SIZE - size, &iter); 295bd735bdaSJeffle Xu erofs_put_metabuf(&buf); 2961ae9470cSJingbo Xu return PAGE_SIZE; 297bd735bdaSJeffle Xu } 298bd735bdaSJeffle Xu 2991442b02bSJeffle Xu if (!(map.m_flags & EROFS_MAP_MAPPED)) { 300e6d9f9baSJingbo Xu count = len; 3011ae9470cSJingbo Xu iov_iter_xarray(&iter, READ, &mapping->i_pages, pos, count); 3021ae9470cSJingbo Xu iov_iter_zero(count, &iter); 3031ae9470cSJingbo Xu return count; 304bd735bdaSJeffle Xu } 305bd735bdaSJeffle Xu 306e6d9f9baSJingbo Xu count = min_t(size_t, map.m_llen - (pos - map.m_la), len); 307e6d9f9baSJingbo Xu DBG_BUGON(!count || count % PAGE_SIZE); 308e6d9f9baSJingbo Xu 3091442b02bSJeffle Xu mdev = (struct erofs_map_dev) { 3101442b02bSJeffle Xu .m_deviceid = map.m_deviceid, 3111442b02bSJeffle Xu .m_pa = map.m_pa, 3121442b02bSJeffle Xu }; 3131442b02bSJeffle Xu ret = erofs_map_dev(sb, &mdev); 3141442b02bSJeffle Xu if (ret) 3151442b02bSJeffle Xu return ret; 3161ae9470cSJingbo Xu 3171ae9470cSJingbo Xu rreq = erofs_fscache_alloc_request(mapping, pos, count); 3181ae9470cSJingbo Xu if (IS_ERR(rreq)) 3191ae9470cSJingbo Xu return PTR_ERR(rreq); 3201ae9470cSJingbo Xu 3211ae9470cSJingbo Xu *unlock = false; 3221ae9470cSJingbo Xu erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 3231ae9470cSJingbo Xu rreq, mdev.m_pa + (pos - map.m_la)); 3241ae9470cSJingbo Xu return count; 3251442b02bSJeffle Xu } 3261442b02bSJeffle Xu 3271ae9470cSJingbo Xu static int erofs_fscache_read_folio(struct file *file, struct folio *folio) 328c665b394SJeffle Xu { 3291ae9470cSJingbo Xu bool unlock; 3301ae9470cSJingbo Xu int ret; 3311ae9470cSJingbo Xu 3321ae9470cSJingbo Xu DBG_BUGON(folio_size(folio) != EROFS_BLKSIZ); 3331ae9470cSJingbo Xu 3341ae9470cSJingbo Xu ret = erofs_fscache_data_read(folio_mapping(folio), folio_pos(folio), 3351ae9470cSJingbo Xu folio_size(folio), &unlock); 336d435d532SXin Yin if (unlock) { 3371ae9470cSJingbo Xu if (ret > 0) 338c665b394SJeffle Xu folio_mark_uptodate(folio); 339c665b394SJeffle Xu folio_unlock(folio); 340c665b394SJeffle Xu } 3411ae9470cSJingbo Xu return ret < 0 ? ret : 0; 342d435d532SXin Yin } 343c665b394SJeffle Xu 344c665b394SJeffle Xu static void erofs_fscache_readahead(struct readahead_control *rac) 345c665b394SJeffle Xu { 3461ae9470cSJingbo Xu struct folio *folio; 3471ae9470cSJingbo Xu size_t len, done = 0; 3481ae9470cSJingbo Xu loff_t start, pos; 3491ae9470cSJingbo Xu bool unlock; 3501ae9470cSJingbo Xu int ret, size; 351c665b394SJeffle Xu 352c665b394SJeffle Xu if (!readahead_count(rac)) 353c665b394SJeffle Xu return; 354c665b394SJeffle Xu 355c665b394SJeffle Xu start = readahead_pos(rac); 356c665b394SJeffle Xu len = readahead_length(rac); 357c665b394SJeffle Xu 358c665b394SJeffle Xu do { 359c665b394SJeffle Xu pos = start + done; 3601ae9470cSJingbo Xu ret = erofs_fscache_data_read(rac->mapping, pos, 3611ae9470cSJingbo Xu len - done, &unlock); 3621ae9470cSJingbo Xu if (ret <= 0) 363c665b394SJeffle Xu return; 364c665b394SJeffle Xu 3651ae9470cSJingbo Xu size = ret; 3661ae9470cSJingbo Xu while (size) { 3671ae9470cSJingbo Xu folio = readahead_folio(rac); 3681ae9470cSJingbo Xu size -= folio_size(folio); 3691ae9470cSJingbo Xu if (unlock) { 370c665b394SJeffle Xu folio_mark_uptodate(folio); 371c665b394SJeffle Xu folio_unlock(folio); 372c665b394SJeffle Xu } 3731ae9470cSJingbo Xu } 3741ae9470cSJingbo Xu } while ((done += ret) < len); 375c665b394SJeffle Xu } 376c665b394SJeffle Xu 3773c265d7dSJeffle Xu static const struct address_space_operations erofs_fscache_meta_aops = { 378fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_meta_read_folio, 3793c265d7dSJeffle Xu }; 3803c265d7dSJeffle Xu 3811442b02bSJeffle Xu const struct address_space_operations erofs_fscache_access_aops = { 382fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_read_folio, 383c665b394SJeffle Xu .readahead = erofs_fscache_readahead, 3841442b02bSJeffle Xu }; 3851442b02bSJeffle Xu 3868b7adf1dSJia Zhu static void erofs_fscache_domain_put(struct erofs_domain *domain) 3878b7adf1dSJia Zhu { 3888b7adf1dSJia Zhu if (!domain) 3898b7adf1dSJia Zhu return; 3908b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 3918b7adf1dSJia Zhu if (refcount_dec_and_test(&domain->ref)) { 3928b7adf1dSJia Zhu list_del(&domain->list); 393a9849560SJia Zhu if (list_empty(&erofs_domain_list)) { 394a9849560SJia Zhu kern_unmount(erofs_pseudo_mnt); 395a9849560SJia Zhu erofs_pseudo_mnt = NULL; 396a9849560SJia Zhu } 3978b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 3988b7adf1dSJia Zhu fscache_relinquish_volume(domain->volume, NULL, false); 3998b7adf1dSJia Zhu kfree(domain->domain_id); 4008b7adf1dSJia Zhu kfree(domain); 4018b7adf1dSJia Zhu return; 4028b7adf1dSJia Zhu } 4038b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4048b7adf1dSJia Zhu } 4058b7adf1dSJia Zhu 4068b7adf1dSJia Zhu static int erofs_fscache_register_volume(struct super_block *sb) 4078b7adf1dSJia Zhu { 4088b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 409*39bfcb81SJingbo Xu char *domain_id = sbi->domain_id; 4108b7adf1dSJia Zhu struct fscache_volume *volume; 4118b7adf1dSJia Zhu char *name; 4128b7adf1dSJia Zhu int ret = 0; 4138b7adf1dSJia Zhu 4148b7adf1dSJia Zhu name = kasprintf(GFP_KERNEL, "erofs,%s", 415*39bfcb81SJingbo Xu domain_id ? domain_id : sbi->fsid); 4168b7adf1dSJia Zhu if (!name) 4178b7adf1dSJia Zhu return -ENOMEM; 4188b7adf1dSJia Zhu 4198b7adf1dSJia Zhu volume = fscache_acquire_volume(name, NULL, NULL, 0); 4208b7adf1dSJia Zhu if (IS_ERR_OR_NULL(volume)) { 4218b7adf1dSJia Zhu erofs_err(sb, "failed to register volume for %s", name); 4228b7adf1dSJia Zhu ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; 4238b7adf1dSJia Zhu volume = NULL; 4248b7adf1dSJia Zhu } 4258b7adf1dSJia Zhu 4268b7adf1dSJia Zhu sbi->volume = volume; 4278b7adf1dSJia Zhu kfree(name); 4288b7adf1dSJia Zhu return ret; 4298b7adf1dSJia Zhu } 4308b7adf1dSJia Zhu 4318b7adf1dSJia Zhu static int erofs_fscache_init_domain(struct super_block *sb) 4328b7adf1dSJia Zhu { 4338b7adf1dSJia Zhu int err; 4348b7adf1dSJia Zhu struct erofs_domain *domain; 4358b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 4368b7adf1dSJia Zhu 4378b7adf1dSJia Zhu domain = kzalloc(sizeof(struct erofs_domain), GFP_KERNEL); 4388b7adf1dSJia Zhu if (!domain) 4398b7adf1dSJia Zhu return -ENOMEM; 4408b7adf1dSJia Zhu 441*39bfcb81SJingbo Xu domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL); 4428b7adf1dSJia Zhu if (!domain->domain_id) { 4438b7adf1dSJia Zhu kfree(domain); 4448b7adf1dSJia Zhu return -ENOMEM; 4458b7adf1dSJia Zhu } 4468b7adf1dSJia Zhu 4478b7adf1dSJia Zhu err = erofs_fscache_register_volume(sb); 4488b7adf1dSJia Zhu if (err) 4498b7adf1dSJia Zhu goto out; 4508b7adf1dSJia Zhu 451a9849560SJia Zhu if (!erofs_pseudo_mnt) { 452a9849560SJia Zhu erofs_pseudo_mnt = kern_mount(&erofs_fs_type); 453a9849560SJia Zhu if (IS_ERR(erofs_pseudo_mnt)) { 454a9849560SJia Zhu err = PTR_ERR(erofs_pseudo_mnt); 455a9849560SJia Zhu goto out; 456a9849560SJia Zhu } 457a9849560SJia Zhu } 458a9849560SJia Zhu 4598b7adf1dSJia Zhu domain->volume = sbi->volume; 4608b7adf1dSJia Zhu refcount_set(&domain->ref, 1); 4618b7adf1dSJia Zhu list_add(&domain->list, &erofs_domain_list); 4628b7adf1dSJia Zhu sbi->domain = domain; 4638b7adf1dSJia Zhu return 0; 4648b7adf1dSJia Zhu out: 4658b7adf1dSJia Zhu kfree(domain->domain_id); 4668b7adf1dSJia Zhu kfree(domain); 4678b7adf1dSJia Zhu return err; 4688b7adf1dSJia Zhu } 4698b7adf1dSJia Zhu 4708b7adf1dSJia Zhu static int erofs_fscache_register_domain(struct super_block *sb) 4718b7adf1dSJia Zhu { 4728b7adf1dSJia Zhu int err; 4738b7adf1dSJia Zhu struct erofs_domain *domain; 4748b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 4758b7adf1dSJia Zhu 4768b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 4778b7adf1dSJia Zhu list_for_each_entry(domain, &erofs_domain_list, list) { 478*39bfcb81SJingbo Xu if (!strcmp(domain->domain_id, sbi->domain_id)) { 4798b7adf1dSJia Zhu sbi->domain = domain; 4808b7adf1dSJia Zhu sbi->volume = domain->volume; 4818b7adf1dSJia Zhu refcount_inc(&domain->ref); 4828b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4838b7adf1dSJia Zhu return 0; 4848b7adf1dSJia Zhu } 4858b7adf1dSJia Zhu } 4868b7adf1dSJia Zhu err = erofs_fscache_init_domain(sb); 4878b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4888b7adf1dSJia Zhu return err; 4898b7adf1dSJia Zhu } 4908b7adf1dSJia Zhu 4917d419637SJia Zhu static 4927d419637SJia Zhu struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb, 4933c265d7dSJeffle Xu char *name, bool need_inode) 494b02c602fSJeffle Xu { 495b02c602fSJeffle Xu struct fscache_volume *volume = EROFS_SB(sb)->volume; 496b02c602fSJeffle Xu struct erofs_fscache *ctx; 497b02c602fSJeffle Xu struct fscache_cookie *cookie; 4983c265d7dSJeffle Xu int ret; 499b02c602fSJeffle Xu 500b02c602fSJeffle Xu ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 501b02c602fSJeffle Xu if (!ctx) 502e1de2da0SJia Zhu return ERR_PTR(-ENOMEM); 503b02c602fSJeffle Xu 504b02c602fSJeffle Xu cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE, 505b02c602fSJeffle Xu name, strlen(name), NULL, 0, 0); 506b02c602fSJeffle Xu if (!cookie) { 507b02c602fSJeffle Xu erofs_err(sb, "failed to get cookie for %s", name); 5083c265d7dSJeffle Xu ret = -EINVAL; 5093c265d7dSJeffle Xu goto err; 510b02c602fSJeffle Xu } 511b02c602fSJeffle Xu 512b02c602fSJeffle Xu fscache_use_cookie(cookie, false); 513b02c602fSJeffle Xu ctx->cookie = cookie; 514b02c602fSJeffle Xu 5153c265d7dSJeffle Xu if (need_inode) { 5163c265d7dSJeffle Xu struct inode *const inode = new_inode(sb); 5173c265d7dSJeffle Xu 5183c265d7dSJeffle Xu if (!inode) { 5193c265d7dSJeffle Xu erofs_err(sb, "failed to get anon inode for %s", name); 5203c265d7dSJeffle Xu ret = -ENOMEM; 5213c265d7dSJeffle Xu goto err_cookie; 5223c265d7dSJeffle Xu } 5233c265d7dSJeffle Xu 5243c265d7dSJeffle Xu set_nlink(inode, 1); 5253c265d7dSJeffle Xu inode->i_size = OFFSET_MAX; 5263c265d7dSJeffle Xu inode->i_mapping->a_ops = &erofs_fscache_meta_aops; 5273c265d7dSJeffle Xu mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); 5283c265d7dSJeffle Xu 5293c265d7dSJeffle Xu ctx->inode = inode; 5303c265d7dSJeffle Xu } 5313c265d7dSJeffle Xu 532e1de2da0SJia Zhu return ctx; 5333c265d7dSJeffle Xu 5343c265d7dSJeffle Xu err_cookie: 5353c265d7dSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 5363c265d7dSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 5373c265d7dSJeffle Xu err: 5383c265d7dSJeffle Xu kfree(ctx); 539e1de2da0SJia Zhu return ERR_PTR(ret); 540b02c602fSJeffle Xu } 541b02c602fSJeffle Xu 5427d419637SJia Zhu static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx) 543b02c602fSJeffle Xu { 544b02c602fSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 545b02c602fSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 5463c265d7dSJeffle Xu iput(ctx->inode); 5477d419637SJia Zhu kfree(ctx->name); 548b02c602fSJeffle Xu kfree(ctx); 549b02c602fSJeffle Xu } 550b02c602fSJeffle Xu 5517d419637SJia Zhu static 5527d419637SJia Zhu struct erofs_fscache *erofs_fscache_domain_init_cookie(struct super_block *sb, 5537d419637SJia Zhu char *name, bool need_inode) 5547d419637SJia Zhu { 5557d419637SJia Zhu int err; 5567d419637SJia Zhu struct inode *inode; 5577d419637SJia Zhu struct erofs_fscache *ctx; 5587d419637SJia Zhu struct erofs_domain *domain = EROFS_SB(sb)->domain; 5597d419637SJia Zhu 5607d419637SJia Zhu ctx = erofs_fscache_acquire_cookie(sb, name, need_inode); 5617d419637SJia Zhu if (IS_ERR(ctx)) 5627d419637SJia Zhu return ctx; 5637d419637SJia Zhu 5647d419637SJia Zhu ctx->name = kstrdup(name, GFP_KERNEL); 5657d419637SJia Zhu if (!ctx->name) { 5667d419637SJia Zhu err = -ENOMEM; 5677d419637SJia Zhu goto out; 5687d419637SJia Zhu } 5697d419637SJia Zhu 5707d419637SJia Zhu inode = new_inode(erofs_pseudo_mnt->mnt_sb); 5717d419637SJia Zhu if (!inode) { 5727d419637SJia Zhu err = -ENOMEM; 5737d419637SJia Zhu goto out; 5747d419637SJia Zhu } 5757d419637SJia Zhu 5767d419637SJia Zhu ctx->domain = domain; 5777d419637SJia Zhu ctx->anon_inode = inode; 5787d419637SJia Zhu inode->i_private = ctx; 5797d419637SJia Zhu refcount_inc(&domain->ref); 5807d419637SJia Zhu return ctx; 5817d419637SJia Zhu out: 5827d419637SJia Zhu erofs_fscache_relinquish_cookie(ctx); 5837d419637SJia Zhu return ERR_PTR(err); 5847d419637SJia Zhu } 5857d419637SJia Zhu 5867d419637SJia Zhu static 5877d419637SJia Zhu struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, 5887d419637SJia Zhu char *name, bool need_inode) 5897d419637SJia Zhu { 5907d419637SJia Zhu struct inode *inode; 5917d419637SJia Zhu struct erofs_fscache *ctx; 5927d419637SJia Zhu struct erofs_domain *domain = EROFS_SB(sb)->domain; 5937d419637SJia Zhu struct super_block *psb = erofs_pseudo_mnt->mnt_sb; 5947d419637SJia Zhu 5957d419637SJia Zhu mutex_lock(&erofs_domain_cookies_lock); 596ce4b8156SDawei Li spin_lock(&psb->s_inode_list_lock); 5977d419637SJia Zhu list_for_each_entry(inode, &psb->s_inodes, i_sb_list) { 5987d419637SJia Zhu ctx = inode->i_private; 5997d419637SJia Zhu if (!ctx || ctx->domain != domain || strcmp(ctx->name, name)) 6007d419637SJia Zhu continue; 6017d419637SJia Zhu igrab(inode); 602ce4b8156SDawei Li spin_unlock(&psb->s_inode_list_lock); 6037d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 6047d419637SJia Zhu return ctx; 6057d419637SJia Zhu } 606ce4b8156SDawei Li spin_unlock(&psb->s_inode_list_lock); 6077d419637SJia Zhu ctx = erofs_fscache_domain_init_cookie(sb, name, need_inode); 6087d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 6097d419637SJia Zhu return ctx; 6107d419637SJia Zhu } 6117d419637SJia Zhu 6127d419637SJia Zhu struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, 6137d419637SJia Zhu char *name, bool need_inode) 6147d419637SJia Zhu { 615*39bfcb81SJingbo Xu if (EROFS_SB(sb)->domain_id) 6167d419637SJia Zhu return erofs_domain_register_cookie(sb, name, need_inode); 6177d419637SJia Zhu return erofs_fscache_acquire_cookie(sb, name, need_inode); 6187d419637SJia Zhu } 6197d419637SJia Zhu 6207d419637SJia Zhu void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) 6217d419637SJia Zhu { 6227d419637SJia Zhu bool drop; 6237d419637SJia Zhu struct erofs_domain *domain; 6247d419637SJia Zhu 6257d419637SJia Zhu if (!ctx) 6267d419637SJia Zhu return; 6277d419637SJia Zhu domain = ctx->domain; 6287d419637SJia Zhu if (domain) { 6297d419637SJia Zhu mutex_lock(&erofs_domain_cookies_lock); 6307d419637SJia Zhu drop = atomic_read(&ctx->anon_inode->i_count) == 1; 6317d419637SJia Zhu iput(ctx->anon_inode); 6327d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 6337d419637SJia Zhu if (!drop) 6347d419637SJia Zhu return; 6357d419637SJia Zhu } 6367d419637SJia Zhu 6377d419637SJia Zhu erofs_fscache_relinquish_cookie(ctx); 6387d419637SJia Zhu erofs_fscache_domain_put(domain); 6397d419637SJia Zhu } 6407d419637SJia Zhu 641c6be2bd0SJeffle Xu int erofs_fscache_register_fs(struct super_block *sb) 642c6be2bd0SJeffle Xu { 6438b7adf1dSJia Zhu int ret; 644c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 645e1de2da0SJia Zhu struct erofs_fscache *fscache; 646c6be2bd0SJeffle Xu 647*39bfcb81SJingbo Xu if (sbi->domain_id) 6488b7adf1dSJia Zhu ret = erofs_fscache_register_domain(sb); 6498b7adf1dSJia Zhu else 6508b7adf1dSJia Zhu ret = erofs_fscache_register_volume(sb); 6518b7adf1dSJia Zhu if (ret) 6528b7adf1dSJia Zhu return ret; 653c6be2bd0SJeffle Xu 6548b7adf1dSJia Zhu /* acquired domain/volume will be relinquished in kill_sb() on error */ 655*39bfcb81SJingbo Xu fscache = erofs_fscache_register_cookie(sb, sbi->fsid, true); 656e1de2da0SJia Zhu if (IS_ERR(fscache)) 657e1de2da0SJia Zhu return PTR_ERR(fscache); 658e1de2da0SJia Zhu 659e1de2da0SJia Zhu sbi->s_fscache = fscache; 660e1de2da0SJia Zhu return 0; 661c6be2bd0SJeffle Xu } 662c6be2bd0SJeffle Xu 663c6be2bd0SJeffle Xu void erofs_fscache_unregister_fs(struct super_block *sb) 664c6be2bd0SJeffle Xu { 665c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 666c6be2bd0SJeffle Xu 667e1de2da0SJia Zhu erofs_fscache_unregister_cookie(sbi->s_fscache); 6688b7adf1dSJia Zhu 6698b7adf1dSJia Zhu if (sbi->domain) 6708b7adf1dSJia Zhu erofs_fscache_domain_put(sbi->domain); 6718b7adf1dSJia Zhu else 672c6be2bd0SJeffle Xu fscache_relinquish_volume(sbi->volume, NULL, false); 6738b7adf1dSJia Zhu 674e1de2da0SJia Zhu sbi->s_fscache = NULL; 675c6be2bd0SJeffle Xu sbi->volume = NULL; 6768b7adf1dSJia Zhu sbi->domain = NULL; 677c6be2bd0SJeffle Xu } 678