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) { 7837020bbbSJingbo Xu unsigned int pgpos, pgend; 79d435d532SXin Yin bool pg_failed = false; 80d435d532SXin Yin 8137020bbbSJingbo Xu if (xas_retry(&xas, folio)) 8237020bbbSJingbo Xu continue; 8337020bbbSJingbo Xu 8437020bbbSJingbo Xu pgpos = (folio_index(folio) - start_page) * PAGE_SIZE; 8537020bbbSJingbo Xu pgend = pgpos + folio_size(folio); 8637020bbbSJingbo Xu 87d435d532SXin Yin for (;;) { 88d435d532SXin Yin if (!subreq) { 89d435d532SXin Yin pg_failed = true; 90d435d532SXin Yin break; 91d435d532SXin Yin } 92d435d532SXin Yin 93d435d532SXin Yin pg_failed |= subreq_failed; 94d435d532SXin Yin if (pgend < iopos + subreq->len) 95d435d532SXin Yin break; 96d435d532SXin Yin 97d435d532SXin Yin iopos += subreq->len; 98d435d532SXin Yin if (!list_is_last(&subreq->rreq_link, 99d435d532SXin Yin &rreq->subrequests)) { 100d435d532SXin Yin subreq = list_next_entry(subreq, rreq_link); 101d435d532SXin Yin subreq_failed = (subreq->error < 0); 102d435d532SXin Yin } else { 103d435d532SXin Yin subreq = NULL; 104d435d532SXin Yin subreq_failed = false; 105d435d532SXin Yin } 106d435d532SXin Yin if (pgend == iopos) 107d435d532SXin Yin break; 108d435d532SXin Yin } 109d435d532SXin Yin 110d435d532SXin Yin if (!pg_failed) 111d435d532SXin Yin folio_mark_uptodate(folio); 112d435d532SXin Yin 113d435d532SXin Yin folio_unlock(folio); 114d435d532SXin Yin } 115d435d532SXin Yin rcu_read_unlock(); 116d435d532SXin Yin } 117d435d532SXin Yin 118d435d532SXin Yin static void erofs_fscache_rreq_complete(struct netfs_io_request *rreq) 119d435d532SXin Yin { 120d435d532SXin Yin erofs_fscache_rreq_unlock_folios(rreq); 121d435d532SXin Yin erofs_fscache_clear_subrequests(rreq); 122d435d532SXin Yin erofs_fscache_put_request(rreq); 123d435d532SXin Yin } 124d435d532SXin Yin 125d435d532SXin Yin static void erofc_fscache_subreq_complete(void *priv, 126d435d532SXin Yin ssize_t transferred_or_error, bool was_async) 127d435d532SXin Yin { 128d435d532SXin Yin struct netfs_io_subrequest *subreq = priv; 129d435d532SXin Yin struct netfs_io_request *rreq = subreq->rreq; 130d435d532SXin Yin 131d435d532SXin Yin if (IS_ERR_VALUE(transferred_or_error)) 132d435d532SXin Yin subreq->error = transferred_or_error; 133d435d532SXin Yin 134d435d532SXin Yin if (atomic_dec_and_test(&rreq->nr_outstanding)) 135d435d532SXin Yin erofs_fscache_rreq_complete(rreq); 136d435d532SXin Yin 137d435d532SXin Yin erofs_fscache_put_subrequest(subreq); 138d435d532SXin Yin } 139d435d532SXin Yin 140ec00b5e2SJeffle Xu /* 141ec00b5e2SJeffle Xu * Read data from fscache and fill the read data into page cache described by 142d435d532SXin Yin * @rreq, which shall be both aligned with PAGE_SIZE. @pstart describes 143ec00b5e2SJeffle Xu * the start physical address in the cache file. 144ec00b5e2SJeffle Xu */ 145d435d532SXin Yin static int erofs_fscache_read_folios_async(struct fscache_cookie *cookie, 146d435d532SXin Yin struct netfs_io_request *rreq, loff_t pstart) 147ec00b5e2SJeffle Xu { 148ec00b5e2SJeffle Xu enum netfs_io_source source; 149d435d532SXin Yin struct super_block *sb = rreq->mapping->host->i_sb; 150d435d532SXin Yin struct netfs_io_subrequest *subreq; 151d435d532SXin Yin struct netfs_cache_resources *cres = &rreq->cache_resources; 152ec00b5e2SJeffle Xu struct iov_iter iter; 153d435d532SXin Yin loff_t start = rreq->start; 154d435d532SXin Yin size_t len = rreq->len; 155ec00b5e2SJeffle Xu size_t done = 0; 156ec00b5e2SJeffle Xu int ret; 157ec00b5e2SJeffle Xu 158d435d532SXin Yin atomic_set(&rreq->nr_outstanding, 1); 159d435d532SXin Yin 160ec00b5e2SJeffle Xu ret = fscache_begin_read_operation(cres, cookie); 161ec00b5e2SJeffle Xu if (ret) 162d435d532SXin Yin goto out; 163ec00b5e2SJeffle Xu 164ec00b5e2SJeffle Xu while (done < len) { 165d435d532SXin Yin subreq = kzalloc(sizeof(struct netfs_io_subrequest), 166d435d532SXin Yin GFP_KERNEL); 167d435d532SXin Yin if (subreq) { 168d435d532SXin Yin INIT_LIST_HEAD(&subreq->rreq_link); 169d435d532SXin Yin refcount_set(&subreq->ref, 2); 170d435d532SXin Yin subreq->rreq = rreq; 171d435d532SXin Yin refcount_inc(&rreq->ref); 172d435d532SXin Yin } else { 173d435d532SXin Yin ret = -ENOMEM; 174d435d532SXin Yin goto out; 175d435d532SXin Yin } 176ec00b5e2SJeffle Xu 177d435d532SXin Yin subreq->start = pstart + done; 178d435d532SXin Yin subreq->len = len - done; 179d435d532SXin Yin subreq->flags = 1 << NETFS_SREQ_ONDEMAND; 180d435d532SXin Yin 181d435d532SXin Yin list_add_tail(&subreq->rreq_link, &rreq->subrequests); 182d435d532SXin Yin 183d435d532SXin Yin source = cres->ops->prepare_read(subreq, LLONG_MAX); 184d435d532SXin Yin if (WARN_ON(subreq->len == 0)) 185ec00b5e2SJeffle Xu source = NETFS_INVALID_READ; 186ec00b5e2SJeffle Xu if (source != NETFS_READ_FROM_CACHE) { 187ec00b5e2SJeffle Xu erofs_err(sb, "failed to fscache prepare_read (source %d)", 188ec00b5e2SJeffle Xu source); 189ec00b5e2SJeffle Xu ret = -EIO; 190d435d532SXin Yin subreq->error = ret; 191d435d532SXin Yin erofs_fscache_put_subrequest(subreq); 192ec00b5e2SJeffle Xu goto out; 193ec00b5e2SJeffle Xu } 194ec00b5e2SJeffle Xu 195d435d532SXin Yin atomic_inc(&rreq->nr_outstanding); 196d435d532SXin Yin 197*de4eda9dSAl Viro iov_iter_xarray(&iter, ITER_DEST, &rreq->mapping->i_pages, 198d435d532SXin Yin start + done, subreq->len); 199d435d532SXin Yin 200d435d532SXin Yin ret = fscache_read(cres, subreq->start, &iter, 201d435d532SXin Yin NETFS_READ_HOLE_FAIL, 202d435d532SXin Yin erofc_fscache_subreq_complete, subreq); 203d435d532SXin Yin if (ret == -EIOCBQUEUED) 204d435d532SXin Yin ret = 0; 205ec00b5e2SJeffle Xu if (ret) { 206ec00b5e2SJeffle Xu erofs_err(sb, "failed to fscache_read (ret %d)", ret); 207ec00b5e2SJeffle Xu goto out; 208ec00b5e2SJeffle Xu } 209ec00b5e2SJeffle Xu 210d435d532SXin Yin done += subreq->len; 211ec00b5e2SJeffle Xu } 212ec00b5e2SJeffle Xu out: 213d435d532SXin Yin if (atomic_dec_and_test(&rreq->nr_outstanding)) 214d435d532SXin Yin erofs_fscache_rreq_complete(rreq); 215d435d532SXin Yin 216ec00b5e2SJeffle Xu return ret; 217ec00b5e2SJeffle Xu } 218ec00b5e2SJeffle Xu 219fdaf9a58SLinus Torvalds static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio) 2205375e7c8SJeffle Xu { 2215375e7c8SJeffle Xu int ret; 2225375e7c8SJeffle Xu struct super_block *sb = folio_mapping(folio)->host->i_sb; 223d435d532SXin Yin struct netfs_io_request *rreq; 2245375e7c8SJeffle Xu struct erofs_map_dev mdev = { 2255375e7c8SJeffle Xu .m_deviceid = 0, 2265375e7c8SJeffle Xu .m_pa = folio_pos(folio), 2275375e7c8SJeffle Xu }; 2285375e7c8SJeffle Xu 2295375e7c8SJeffle Xu ret = erofs_map_dev(sb, &mdev); 2305375e7c8SJeffle Xu if (ret) 2315375e7c8SJeffle Xu goto out; 2325375e7c8SJeffle Xu 233d435d532SXin Yin rreq = erofs_fscache_alloc_request(folio_mapping(folio), 234d435d532SXin Yin folio_pos(folio), folio_size(folio)); 2355bd9628bSSun Ke if (IS_ERR(rreq)) { 2365bd9628bSSun Ke ret = PTR_ERR(rreq); 237d435d532SXin Yin goto out; 2385bd9628bSSun Ke } 239d435d532SXin Yin 240d435d532SXin Yin return erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 241d435d532SXin Yin rreq, mdev.m_pa); 2425375e7c8SJeffle Xu out: 2435375e7c8SJeffle Xu folio_unlock(folio); 2445375e7c8SJeffle Xu return ret; 2455375e7c8SJeffle Xu } 2465375e7c8SJeffle Xu 2471ae9470cSJingbo Xu /* 2481ae9470cSJingbo Xu * Read into page cache in the range described by (@pos, @len). 2491ae9470cSJingbo Xu * 2501ae9470cSJingbo Xu * On return, the caller is responsible for page unlocking if the output @unlock 2511ae9470cSJingbo Xu * is true, or the callee will take this responsibility through netfs_io_request 2521ae9470cSJingbo Xu * interface. 2531ae9470cSJingbo Xu * 2541ae9470cSJingbo Xu * The return value is the number of bytes successfully handled, or negative 2551ae9470cSJingbo Xu * error code on failure. The only exception is that, the length of the range 2561ae9470cSJingbo Xu * instead of the error code is returned on failure after netfs_io_request is 2571ae9470cSJingbo Xu * allocated, so that .readahead() could advance rac accordingly. 2581ae9470cSJingbo Xu */ 2591ae9470cSJingbo Xu static int erofs_fscache_data_read(struct address_space *mapping, 2601ae9470cSJingbo Xu loff_t pos, size_t len, bool *unlock) 261bd735bdaSJeffle Xu { 2621ae9470cSJingbo Xu struct inode *inode = mapping->host; 2631ae9470cSJingbo Xu struct super_block *sb = inode->i_sb; 2641ae9470cSJingbo Xu struct netfs_io_request *rreq; 2651ae9470cSJingbo Xu struct erofs_map_blocks map; 2661ae9470cSJingbo Xu struct erofs_map_dev mdev; 2671ae9470cSJingbo Xu struct iov_iter iter; 2681ae9470cSJingbo Xu size_t count; 2691ae9470cSJingbo Xu int ret; 2701ae9470cSJingbo Xu 2711ae9470cSJingbo Xu *unlock = true; 2721ae9470cSJingbo Xu 2731ae9470cSJingbo Xu map.m_la = pos; 2741ae9470cSJingbo Xu ret = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW); 2751ae9470cSJingbo Xu if (ret) 2761ae9470cSJingbo Xu return ret; 2771ae9470cSJingbo Xu 2781ae9470cSJingbo Xu if (map.m_flags & EROFS_MAP_META) { 279bd735bdaSJeffle Xu struct erofs_buf buf = __EROFS_BUF_INITIALIZER; 280bd735bdaSJeffle Xu erofs_blk_t blknr; 2811ae9470cSJingbo Xu size_t offset, size; 2821ae9470cSJingbo Xu void *src; 283bd735bdaSJeffle Xu 284bd735bdaSJeffle Xu /* For tail packing layout, the offset may be non-zero. */ 2851ae9470cSJingbo Xu offset = erofs_blkoff(map.m_pa); 2861ae9470cSJingbo Xu blknr = erofs_blknr(map.m_pa); 2871ae9470cSJingbo Xu size = map.m_llen; 288bd735bdaSJeffle Xu 289bd735bdaSJeffle Xu src = erofs_read_metabuf(&buf, sb, blknr, EROFS_KMAP); 290bd735bdaSJeffle Xu if (IS_ERR(src)) 291bd735bdaSJeffle Xu return PTR_ERR(src); 292bd735bdaSJeffle Xu 293*de4eda9dSAl Viro iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, PAGE_SIZE); 29475e43355SJingbo Xu if (copy_to_iter(src + offset, size, &iter) != size) { 29575e43355SJingbo Xu erofs_put_metabuf(&buf); 2961ae9470cSJingbo Xu return -EFAULT; 29775e43355SJingbo Xu } 2981ae9470cSJingbo Xu iov_iter_zero(PAGE_SIZE - size, &iter); 299bd735bdaSJeffle Xu erofs_put_metabuf(&buf); 3001ae9470cSJingbo Xu return PAGE_SIZE; 301bd735bdaSJeffle Xu } 302bd735bdaSJeffle Xu 3031442b02bSJeffle Xu if (!(map.m_flags & EROFS_MAP_MAPPED)) { 304e6d9f9baSJingbo Xu count = len; 305*de4eda9dSAl Viro iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, count); 3061ae9470cSJingbo Xu iov_iter_zero(count, &iter); 3071ae9470cSJingbo Xu return count; 308bd735bdaSJeffle Xu } 309bd735bdaSJeffle Xu 310e6d9f9baSJingbo Xu count = min_t(size_t, map.m_llen - (pos - map.m_la), len); 311e6d9f9baSJingbo Xu DBG_BUGON(!count || count % PAGE_SIZE); 312e6d9f9baSJingbo Xu 3131442b02bSJeffle Xu mdev = (struct erofs_map_dev) { 3141442b02bSJeffle Xu .m_deviceid = map.m_deviceid, 3151442b02bSJeffle Xu .m_pa = map.m_pa, 3161442b02bSJeffle Xu }; 3171442b02bSJeffle Xu ret = erofs_map_dev(sb, &mdev); 3181442b02bSJeffle Xu if (ret) 3191442b02bSJeffle Xu return ret; 3201ae9470cSJingbo Xu 3211ae9470cSJingbo Xu rreq = erofs_fscache_alloc_request(mapping, pos, count); 3221ae9470cSJingbo Xu if (IS_ERR(rreq)) 3231ae9470cSJingbo Xu return PTR_ERR(rreq); 3241ae9470cSJingbo Xu 3251ae9470cSJingbo Xu *unlock = false; 3261ae9470cSJingbo Xu erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 3271ae9470cSJingbo Xu rreq, mdev.m_pa + (pos - map.m_la)); 3281ae9470cSJingbo Xu return count; 3291442b02bSJeffle Xu } 3301442b02bSJeffle Xu 3311ae9470cSJingbo Xu static int erofs_fscache_read_folio(struct file *file, struct folio *folio) 332c665b394SJeffle Xu { 3331ae9470cSJingbo Xu bool unlock; 3341ae9470cSJingbo Xu int ret; 3351ae9470cSJingbo Xu 3361ae9470cSJingbo Xu DBG_BUGON(folio_size(folio) != EROFS_BLKSIZ); 3371ae9470cSJingbo Xu 3381ae9470cSJingbo Xu ret = erofs_fscache_data_read(folio_mapping(folio), folio_pos(folio), 3391ae9470cSJingbo Xu folio_size(folio), &unlock); 340d435d532SXin Yin if (unlock) { 3411ae9470cSJingbo Xu if (ret > 0) 342c665b394SJeffle Xu folio_mark_uptodate(folio); 343c665b394SJeffle Xu folio_unlock(folio); 344c665b394SJeffle Xu } 3451ae9470cSJingbo Xu return ret < 0 ? ret : 0; 346d435d532SXin Yin } 347c665b394SJeffle Xu 348c665b394SJeffle Xu static void erofs_fscache_readahead(struct readahead_control *rac) 349c665b394SJeffle Xu { 3501ae9470cSJingbo Xu struct folio *folio; 3511ae9470cSJingbo Xu size_t len, done = 0; 3521ae9470cSJingbo Xu loff_t start, pos; 3531ae9470cSJingbo Xu bool unlock; 3541ae9470cSJingbo Xu int ret, size; 355c665b394SJeffle Xu 356c665b394SJeffle Xu if (!readahead_count(rac)) 357c665b394SJeffle Xu return; 358c665b394SJeffle Xu 359c665b394SJeffle Xu start = readahead_pos(rac); 360c665b394SJeffle Xu len = readahead_length(rac); 361c665b394SJeffle Xu 362c665b394SJeffle Xu do { 363c665b394SJeffle Xu pos = start + done; 3641ae9470cSJingbo Xu ret = erofs_fscache_data_read(rac->mapping, pos, 3651ae9470cSJingbo Xu len - done, &unlock); 3661ae9470cSJingbo Xu if (ret <= 0) 367c665b394SJeffle Xu return; 368c665b394SJeffle Xu 3691ae9470cSJingbo Xu size = ret; 3701ae9470cSJingbo Xu while (size) { 3711ae9470cSJingbo Xu folio = readahead_folio(rac); 3721ae9470cSJingbo Xu size -= folio_size(folio); 3731ae9470cSJingbo Xu if (unlock) { 374c665b394SJeffle Xu folio_mark_uptodate(folio); 375c665b394SJeffle Xu folio_unlock(folio); 376c665b394SJeffle Xu } 3771ae9470cSJingbo Xu } 3781ae9470cSJingbo Xu } while ((done += ret) < len); 379c665b394SJeffle Xu } 380c665b394SJeffle Xu 3813c265d7dSJeffle Xu static const struct address_space_operations erofs_fscache_meta_aops = { 382fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_meta_read_folio, 3833c265d7dSJeffle Xu }; 3843c265d7dSJeffle Xu 3851442b02bSJeffle Xu const struct address_space_operations erofs_fscache_access_aops = { 386fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_read_folio, 387c665b394SJeffle Xu .readahead = erofs_fscache_readahead, 3881442b02bSJeffle Xu }; 3891442b02bSJeffle Xu 3908b7adf1dSJia Zhu static void erofs_fscache_domain_put(struct erofs_domain *domain) 3918b7adf1dSJia Zhu { 3928b7adf1dSJia Zhu if (!domain) 3938b7adf1dSJia Zhu return; 3948b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 3958b7adf1dSJia Zhu if (refcount_dec_and_test(&domain->ref)) { 3968b7adf1dSJia Zhu list_del(&domain->list); 397a9849560SJia Zhu if (list_empty(&erofs_domain_list)) { 398a9849560SJia Zhu kern_unmount(erofs_pseudo_mnt); 399a9849560SJia Zhu erofs_pseudo_mnt = NULL; 400a9849560SJia Zhu } 4018b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4028b7adf1dSJia Zhu fscache_relinquish_volume(domain->volume, NULL, false); 4038b7adf1dSJia Zhu kfree(domain->domain_id); 4048b7adf1dSJia Zhu kfree(domain); 4058b7adf1dSJia Zhu return; 4068b7adf1dSJia Zhu } 4078b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4088b7adf1dSJia Zhu } 4098b7adf1dSJia Zhu 4108b7adf1dSJia Zhu static int erofs_fscache_register_volume(struct super_block *sb) 4118b7adf1dSJia Zhu { 4128b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 41339bfcb81SJingbo Xu char *domain_id = sbi->domain_id; 4148b7adf1dSJia Zhu struct fscache_volume *volume; 4158b7adf1dSJia Zhu char *name; 4168b7adf1dSJia Zhu int ret = 0; 4178b7adf1dSJia Zhu 4188b7adf1dSJia Zhu name = kasprintf(GFP_KERNEL, "erofs,%s", 41939bfcb81SJingbo Xu domain_id ? domain_id : sbi->fsid); 4208b7adf1dSJia Zhu if (!name) 4218b7adf1dSJia Zhu return -ENOMEM; 4228b7adf1dSJia Zhu 4238b7adf1dSJia Zhu volume = fscache_acquire_volume(name, NULL, NULL, 0); 4248b7adf1dSJia Zhu if (IS_ERR_OR_NULL(volume)) { 4258b7adf1dSJia Zhu erofs_err(sb, "failed to register volume for %s", name); 4268b7adf1dSJia Zhu ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; 4278b7adf1dSJia Zhu volume = NULL; 4288b7adf1dSJia Zhu } 4298b7adf1dSJia Zhu 4308b7adf1dSJia Zhu sbi->volume = volume; 4318b7adf1dSJia Zhu kfree(name); 4328b7adf1dSJia Zhu return ret; 4338b7adf1dSJia Zhu } 4348b7adf1dSJia Zhu 4358b7adf1dSJia Zhu static int erofs_fscache_init_domain(struct super_block *sb) 4368b7adf1dSJia Zhu { 4378b7adf1dSJia Zhu int err; 4388b7adf1dSJia Zhu struct erofs_domain *domain; 4398b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 4408b7adf1dSJia Zhu 4418b7adf1dSJia Zhu domain = kzalloc(sizeof(struct erofs_domain), GFP_KERNEL); 4428b7adf1dSJia Zhu if (!domain) 4438b7adf1dSJia Zhu return -ENOMEM; 4448b7adf1dSJia Zhu 44539bfcb81SJingbo Xu domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL); 4468b7adf1dSJia Zhu if (!domain->domain_id) { 4478b7adf1dSJia Zhu kfree(domain); 4488b7adf1dSJia Zhu return -ENOMEM; 4498b7adf1dSJia Zhu } 4508b7adf1dSJia Zhu 4518b7adf1dSJia Zhu err = erofs_fscache_register_volume(sb); 4528b7adf1dSJia Zhu if (err) 4538b7adf1dSJia Zhu goto out; 4548b7adf1dSJia Zhu 455a9849560SJia Zhu if (!erofs_pseudo_mnt) { 456a9849560SJia Zhu erofs_pseudo_mnt = kern_mount(&erofs_fs_type); 457a9849560SJia Zhu if (IS_ERR(erofs_pseudo_mnt)) { 458a9849560SJia Zhu err = PTR_ERR(erofs_pseudo_mnt); 459a9849560SJia Zhu goto out; 460a9849560SJia Zhu } 461a9849560SJia Zhu } 462a9849560SJia Zhu 4638b7adf1dSJia Zhu domain->volume = sbi->volume; 4648b7adf1dSJia Zhu refcount_set(&domain->ref, 1); 4658b7adf1dSJia Zhu list_add(&domain->list, &erofs_domain_list); 4668b7adf1dSJia Zhu sbi->domain = domain; 4678b7adf1dSJia Zhu return 0; 4688b7adf1dSJia Zhu out: 4698b7adf1dSJia Zhu kfree(domain->domain_id); 4708b7adf1dSJia Zhu kfree(domain); 4718b7adf1dSJia Zhu return err; 4728b7adf1dSJia Zhu } 4738b7adf1dSJia Zhu 4748b7adf1dSJia Zhu static int erofs_fscache_register_domain(struct super_block *sb) 4758b7adf1dSJia Zhu { 4768b7adf1dSJia Zhu int err; 4778b7adf1dSJia Zhu struct erofs_domain *domain; 4788b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 4798b7adf1dSJia Zhu 4808b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 4818b7adf1dSJia Zhu list_for_each_entry(domain, &erofs_domain_list, list) { 48239bfcb81SJingbo Xu if (!strcmp(domain->domain_id, sbi->domain_id)) { 4838b7adf1dSJia Zhu sbi->domain = domain; 4848b7adf1dSJia Zhu sbi->volume = domain->volume; 4858b7adf1dSJia Zhu refcount_inc(&domain->ref); 4868b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4878b7adf1dSJia Zhu return 0; 4888b7adf1dSJia Zhu } 4898b7adf1dSJia Zhu } 4908b7adf1dSJia Zhu err = erofs_fscache_init_domain(sb); 4918b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4928b7adf1dSJia Zhu return err; 4938b7adf1dSJia Zhu } 4948b7adf1dSJia Zhu 4957d419637SJia Zhu static 4967d419637SJia Zhu struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb, 4973c265d7dSJeffle Xu char *name, bool need_inode) 498b02c602fSJeffle Xu { 499b02c602fSJeffle Xu struct fscache_volume *volume = EROFS_SB(sb)->volume; 500b02c602fSJeffle Xu struct erofs_fscache *ctx; 501b02c602fSJeffle Xu struct fscache_cookie *cookie; 5023c265d7dSJeffle Xu int ret; 503b02c602fSJeffle Xu 504b02c602fSJeffle Xu ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 505b02c602fSJeffle Xu if (!ctx) 506e1de2da0SJia Zhu return ERR_PTR(-ENOMEM); 507b02c602fSJeffle Xu 508b02c602fSJeffle Xu cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE, 509b02c602fSJeffle Xu name, strlen(name), NULL, 0, 0); 510b02c602fSJeffle Xu if (!cookie) { 511b02c602fSJeffle Xu erofs_err(sb, "failed to get cookie for %s", name); 5123c265d7dSJeffle Xu ret = -EINVAL; 5133c265d7dSJeffle Xu goto err; 514b02c602fSJeffle Xu } 515b02c602fSJeffle Xu 516b02c602fSJeffle Xu fscache_use_cookie(cookie, false); 517b02c602fSJeffle Xu ctx->cookie = cookie; 518b02c602fSJeffle Xu 5193c265d7dSJeffle Xu if (need_inode) { 5203c265d7dSJeffle Xu struct inode *const inode = new_inode(sb); 5213c265d7dSJeffle Xu 5223c265d7dSJeffle Xu if (!inode) { 5233c265d7dSJeffle Xu erofs_err(sb, "failed to get anon inode for %s", name); 5243c265d7dSJeffle Xu ret = -ENOMEM; 5253c265d7dSJeffle Xu goto err_cookie; 5263c265d7dSJeffle Xu } 5273c265d7dSJeffle Xu 5283c265d7dSJeffle Xu set_nlink(inode, 1); 5293c265d7dSJeffle Xu inode->i_size = OFFSET_MAX; 5303c265d7dSJeffle Xu inode->i_mapping->a_ops = &erofs_fscache_meta_aops; 5313c265d7dSJeffle Xu mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); 5323c265d7dSJeffle Xu 5333c265d7dSJeffle Xu ctx->inode = inode; 5343c265d7dSJeffle Xu } 5353c265d7dSJeffle Xu 536e1de2da0SJia Zhu return ctx; 5373c265d7dSJeffle Xu 5383c265d7dSJeffle Xu err_cookie: 5393c265d7dSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 5403c265d7dSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 5413c265d7dSJeffle Xu err: 5423c265d7dSJeffle Xu kfree(ctx); 543e1de2da0SJia Zhu return ERR_PTR(ret); 544b02c602fSJeffle Xu } 545b02c602fSJeffle Xu 5467d419637SJia Zhu static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx) 547b02c602fSJeffle Xu { 548b02c602fSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 549b02c602fSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 5503c265d7dSJeffle Xu iput(ctx->inode); 5517d419637SJia Zhu kfree(ctx->name); 552b02c602fSJeffle Xu kfree(ctx); 553b02c602fSJeffle Xu } 554b02c602fSJeffle Xu 5557d419637SJia Zhu static 5567d419637SJia Zhu struct erofs_fscache *erofs_fscache_domain_init_cookie(struct super_block *sb, 5577d419637SJia Zhu char *name, bool need_inode) 5587d419637SJia Zhu { 5597d419637SJia Zhu int err; 5607d419637SJia Zhu struct inode *inode; 5617d419637SJia Zhu struct erofs_fscache *ctx; 5627d419637SJia Zhu struct erofs_domain *domain = EROFS_SB(sb)->domain; 5637d419637SJia Zhu 5647d419637SJia Zhu ctx = erofs_fscache_acquire_cookie(sb, name, need_inode); 5657d419637SJia Zhu if (IS_ERR(ctx)) 5667d419637SJia Zhu return ctx; 5677d419637SJia Zhu 5687d419637SJia Zhu ctx->name = kstrdup(name, GFP_KERNEL); 5697d419637SJia Zhu if (!ctx->name) { 5707d419637SJia Zhu err = -ENOMEM; 5717d419637SJia Zhu goto out; 5727d419637SJia Zhu } 5737d419637SJia Zhu 5747d419637SJia Zhu inode = new_inode(erofs_pseudo_mnt->mnt_sb); 5757d419637SJia Zhu if (!inode) { 5767d419637SJia Zhu err = -ENOMEM; 5777d419637SJia Zhu goto out; 5787d419637SJia Zhu } 5797d419637SJia Zhu 5807d419637SJia Zhu ctx->domain = domain; 5817d419637SJia Zhu ctx->anon_inode = inode; 5827d419637SJia Zhu inode->i_private = ctx; 5837d419637SJia Zhu refcount_inc(&domain->ref); 5847d419637SJia Zhu return ctx; 5857d419637SJia Zhu out: 5867d419637SJia Zhu erofs_fscache_relinquish_cookie(ctx); 5877d419637SJia Zhu return ERR_PTR(err); 5887d419637SJia Zhu } 5897d419637SJia Zhu 5907d419637SJia Zhu static 5917d419637SJia Zhu struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, 5927d419637SJia Zhu char *name, bool need_inode) 5937d419637SJia Zhu { 5947d419637SJia Zhu struct inode *inode; 5957d419637SJia Zhu struct erofs_fscache *ctx; 5967d419637SJia Zhu struct erofs_domain *domain = EROFS_SB(sb)->domain; 5977d419637SJia Zhu struct super_block *psb = erofs_pseudo_mnt->mnt_sb; 5987d419637SJia Zhu 5997d419637SJia Zhu mutex_lock(&erofs_domain_cookies_lock); 600ce4b8156SDawei Li spin_lock(&psb->s_inode_list_lock); 6017d419637SJia Zhu list_for_each_entry(inode, &psb->s_inodes, i_sb_list) { 6027d419637SJia Zhu ctx = inode->i_private; 6037d419637SJia Zhu if (!ctx || ctx->domain != domain || strcmp(ctx->name, name)) 6047d419637SJia Zhu continue; 6057d419637SJia Zhu igrab(inode); 606ce4b8156SDawei Li spin_unlock(&psb->s_inode_list_lock); 6077d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 6087d419637SJia Zhu return ctx; 6097d419637SJia Zhu } 610ce4b8156SDawei Li spin_unlock(&psb->s_inode_list_lock); 6117d419637SJia Zhu ctx = erofs_fscache_domain_init_cookie(sb, name, need_inode); 6127d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 6137d419637SJia Zhu return ctx; 6147d419637SJia Zhu } 6157d419637SJia Zhu 6167d419637SJia Zhu struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, 6177d419637SJia Zhu char *name, bool need_inode) 6187d419637SJia Zhu { 61939bfcb81SJingbo Xu if (EROFS_SB(sb)->domain_id) 6207d419637SJia Zhu return erofs_domain_register_cookie(sb, name, need_inode); 6217d419637SJia Zhu return erofs_fscache_acquire_cookie(sb, name, need_inode); 6227d419637SJia Zhu } 6237d419637SJia Zhu 6247d419637SJia Zhu void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) 6257d419637SJia Zhu { 6267d419637SJia Zhu bool drop; 6277d419637SJia Zhu struct erofs_domain *domain; 6287d419637SJia Zhu 6297d419637SJia Zhu if (!ctx) 6307d419637SJia Zhu return; 6317d419637SJia Zhu domain = ctx->domain; 6327d419637SJia Zhu if (domain) { 6337d419637SJia Zhu mutex_lock(&erofs_domain_cookies_lock); 6347d419637SJia Zhu drop = atomic_read(&ctx->anon_inode->i_count) == 1; 6357d419637SJia Zhu iput(ctx->anon_inode); 6367d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 6377d419637SJia Zhu if (!drop) 6387d419637SJia Zhu return; 6397d419637SJia Zhu } 6407d419637SJia Zhu 6417d419637SJia Zhu erofs_fscache_relinquish_cookie(ctx); 6427d419637SJia Zhu erofs_fscache_domain_put(domain); 6437d419637SJia Zhu } 6447d419637SJia Zhu 645c6be2bd0SJeffle Xu int erofs_fscache_register_fs(struct super_block *sb) 646c6be2bd0SJeffle Xu { 6478b7adf1dSJia Zhu int ret; 648c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 649e1de2da0SJia Zhu struct erofs_fscache *fscache; 650c6be2bd0SJeffle Xu 65139bfcb81SJingbo Xu if (sbi->domain_id) 6528b7adf1dSJia Zhu ret = erofs_fscache_register_domain(sb); 6538b7adf1dSJia Zhu else 6548b7adf1dSJia Zhu ret = erofs_fscache_register_volume(sb); 6558b7adf1dSJia Zhu if (ret) 6568b7adf1dSJia Zhu return ret; 657c6be2bd0SJeffle Xu 6588b7adf1dSJia Zhu /* acquired domain/volume will be relinquished in kill_sb() on error */ 65939bfcb81SJingbo Xu fscache = erofs_fscache_register_cookie(sb, sbi->fsid, true); 660e1de2da0SJia Zhu if (IS_ERR(fscache)) 661e1de2da0SJia Zhu return PTR_ERR(fscache); 662e1de2da0SJia Zhu 663e1de2da0SJia Zhu sbi->s_fscache = fscache; 664e1de2da0SJia Zhu return 0; 665c6be2bd0SJeffle Xu } 666c6be2bd0SJeffle Xu 667c6be2bd0SJeffle Xu void erofs_fscache_unregister_fs(struct super_block *sb) 668c6be2bd0SJeffle Xu { 669c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 670c6be2bd0SJeffle Xu 671e1de2da0SJia Zhu erofs_fscache_unregister_cookie(sbi->s_fscache); 6728b7adf1dSJia Zhu 6738b7adf1dSJia Zhu if (sbi->domain) 6748b7adf1dSJia Zhu erofs_fscache_domain_put(sbi->domain); 6758b7adf1dSJia Zhu else 676c6be2bd0SJeffle Xu fscache_relinquish_volume(sbi->volume, NULL, false); 6778b7adf1dSJia Zhu 678e1de2da0SJia Zhu sbi->s_fscache = NULL; 679c6be2bd0SJeffle Xu sbi->volume = NULL; 6808b7adf1dSJia Zhu sbi->domain = NULL; 681c6be2bd0SJeffle Xu } 682