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); 108b7adf1dSJia Zhu static LIST_HEAD(erofs_domain_list); 11*a9849560SJia Zhu static struct vfsmount *erofs_pseudo_mnt; 128b7adf1dSJia Zhu 13d435d532SXin Yin static struct netfs_io_request *erofs_fscache_alloc_request(struct address_space *mapping, 14d435d532SXin Yin loff_t start, size_t len) 15d435d532SXin Yin { 16d435d532SXin Yin struct netfs_io_request *rreq; 17d435d532SXin Yin 18d435d532SXin Yin rreq = kzalloc(sizeof(struct netfs_io_request), GFP_KERNEL); 19d435d532SXin Yin if (!rreq) 20d435d532SXin Yin return ERR_PTR(-ENOMEM); 21d435d532SXin Yin 22d435d532SXin Yin rreq->start = start; 23d435d532SXin Yin rreq->len = len; 24d435d532SXin Yin rreq->mapping = mapping; 25b5cb79dcSXin Yin rreq->inode = mapping->host; 26d435d532SXin Yin INIT_LIST_HEAD(&rreq->subrequests); 27d435d532SXin Yin refcount_set(&rreq->ref, 1); 28d435d532SXin Yin return rreq; 29d435d532SXin Yin } 30d435d532SXin Yin 31d435d532SXin Yin static void erofs_fscache_put_request(struct netfs_io_request *rreq) 32d435d532SXin Yin { 33d435d532SXin Yin if (!refcount_dec_and_test(&rreq->ref)) 34d435d532SXin Yin return; 35d435d532SXin Yin if (rreq->cache_resources.ops) 36d435d532SXin Yin rreq->cache_resources.ops->end_operation(&rreq->cache_resources); 37d435d532SXin Yin kfree(rreq); 38d435d532SXin Yin } 39d435d532SXin Yin 40d435d532SXin Yin static void erofs_fscache_put_subrequest(struct netfs_io_subrequest *subreq) 41d435d532SXin Yin { 42d435d532SXin Yin if (!refcount_dec_and_test(&subreq->ref)) 43d435d532SXin Yin return; 44d435d532SXin Yin erofs_fscache_put_request(subreq->rreq); 45d435d532SXin Yin kfree(subreq); 46d435d532SXin Yin } 47d435d532SXin Yin 48d435d532SXin Yin static void erofs_fscache_clear_subrequests(struct netfs_io_request *rreq) 49d435d532SXin Yin { 50d435d532SXin Yin struct netfs_io_subrequest *subreq; 51d435d532SXin Yin 52d435d532SXin Yin while (!list_empty(&rreq->subrequests)) { 53d435d532SXin Yin subreq = list_first_entry(&rreq->subrequests, 54d435d532SXin Yin struct netfs_io_subrequest, rreq_link); 55d435d532SXin Yin list_del(&subreq->rreq_link); 56d435d532SXin Yin erofs_fscache_put_subrequest(subreq); 57d435d532SXin Yin } 58d435d532SXin Yin } 59d435d532SXin Yin 60d435d532SXin Yin static void erofs_fscache_rreq_unlock_folios(struct netfs_io_request *rreq) 61d435d532SXin Yin { 62d435d532SXin Yin struct netfs_io_subrequest *subreq; 63d435d532SXin Yin struct folio *folio; 64d435d532SXin Yin unsigned int iopos = 0; 65d435d532SXin Yin pgoff_t start_page = rreq->start / PAGE_SIZE; 66d435d532SXin Yin pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1; 67d435d532SXin Yin bool subreq_failed = false; 68d435d532SXin Yin 69d435d532SXin Yin XA_STATE(xas, &rreq->mapping->i_pages, start_page); 70d435d532SXin Yin 71d435d532SXin Yin subreq = list_first_entry(&rreq->subrequests, 72d435d532SXin Yin struct netfs_io_subrequest, rreq_link); 73d435d532SXin Yin subreq_failed = (subreq->error < 0); 74d435d532SXin Yin 75d435d532SXin Yin rcu_read_lock(); 76d435d532SXin Yin xas_for_each(&xas, folio, last_page) { 77d435d532SXin Yin unsigned int pgpos = 78d435d532SXin Yin (folio_index(folio) - start_page) * PAGE_SIZE; 79d435d532SXin Yin unsigned int pgend = pgpos + folio_size(folio); 80d435d532SXin Yin bool pg_failed = false; 81d435d532SXin Yin 82d435d532SXin Yin for (;;) { 83d435d532SXin Yin if (!subreq) { 84d435d532SXin Yin pg_failed = true; 85d435d532SXin Yin break; 86d435d532SXin Yin } 87d435d532SXin Yin 88d435d532SXin Yin pg_failed |= subreq_failed; 89d435d532SXin Yin if (pgend < iopos + subreq->len) 90d435d532SXin Yin break; 91d435d532SXin Yin 92d435d532SXin Yin iopos += subreq->len; 93d435d532SXin Yin if (!list_is_last(&subreq->rreq_link, 94d435d532SXin Yin &rreq->subrequests)) { 95d435d532SXin Yin subreq = list_next_entry(subreq, rreq_link); 96d435d532SXin Yin subreq_failed = (subreq->error < 0); 97d435d532SXin Yin } else { 98d435d532SXin Yin subreq = NULL; 99d435d532SXin Yin subreq_failed = false; 100d435d532SXin Yin } 101d435d532SXin Yin if (pgend == iopos) 102d435d532SXin Yin break; 103d435d532SXin Yin } 104d435d532SXin Yin 105d435d532SXin Yin if (!pg_failed) 106d435d532SXin Yin folio_mark_uptodate(folio); 107d435d532SXin Yin 108d435d532SXin Yin folio_unlock(folio); 109d435d532SXin Yin } 110d435d532SXin Yin rcu_read_unlock(); 111d435d532SXin Yin } 112d435d532SXin Yin 113d435d532SXin Yin static void erofs_fscache_rreq_complete(struct netfs_io_request *rreq) 114d435d532SXin Yin { 115d435d532SXin Yin erofs_fscache_rreq_unlock_folios(rreq); 116d435d532SXin Yin erofs_fscache_clear_subrequests(rreq); 117d435d532SXin Yin erofs_fscache_put_request(rreq); 118d435d532SXin Yin } 119d435d532SXin Yin 120d435d532SXin Yin static void erofc_fscache_subreq_complete(void *priv, 121d435d532SXin Yin ssize_t transferred_or_error, bool was_async) 122d435d532SXin Yin { 123d435d532SXin Yin struct netfs_io_subrequest *subreq = priv; 124d435d532SXin Yin struct netfs_io_request *rreq = subreq->rreq; 125d435d532SXin Yin 126d435d532SXin Yin if (IS_ERR_VALUE(transferred_or_error)) 127d435d532SXin Yin subreq->error = transferred_or_error; 128d435d532SXin Yin 129d435d532SXin Yin if (atomic_dec_and_test(&rreq->nr_outstanding)) 130d435d532SXin Yin erofs_fscache_rreq_complete(rreq); 131d435d532SXin Yin 132d435d532SXin Yin erofs_fscache_put_subrequest(subreq); 133d435d532SXin Yin } 134d435d532SXin Yin 135ec00b5e2SJeffle Xu /* 136ec00b5e2SJeffle Xu * Read data from fscache and fill the read data into page cache described by 137d435d532SXin Yin * @rreq, which shall be both aligned with PAGE_SIZE. @pstart describes 138ec00b5e2SJeffle Xu * the start physical address in the cache file. 139ec00b5e2SJeffle Xu */ 140d435d532SXin Yin static int erofs_fscache_read_folios_async(struct fscache_cookie *cookie, 141d435d532SXin Yin struct netfs_io_request *rreq, loff_t pstart) 142ec00b5e2SJeffle Xu { 143ec00b5e2SJeffle Xu enum netfs_io_source source; 144d435d532SXin Yin struct super_block *sb = rreq->mapping->host->i_sb; 145d435d532SXin Yin struct netfs_io_subrequest *subreq; 146d435d532SXin Yin struct netfs_cache_resources *cres = &rreq->cache_resources; 147ec00b5e2SJeffle Xu struct iov_iter iter; 148d435d532SXin Yin loff_t start = rreq->start; 149d435d532SXin Yin size_t len = rreq->len; 150ec00b5e2SJeffle Xu size_t done = 0; 151ec00b5e2SJeffle Xu int ret; 152ec00b5e2SJeffle Xu 153d435d532SXin Yin atomic_set(&rreq->nr_outstanding, 1); 154d435d532SXin Yin 155ec00b5e2SJeffle Xu ret = fscache_begin_read_operation(cres, cookie); 156ec00b5e2SJeffle Xu if (ret) 157d435d532SXin Yin goto out; 158ec00b5e2SJeffle Xu 159ec00b5e2SJeffle Xu while (done < len) { 160d435d532SXin Yin subreq = kzalloc(sizeof(struct netfs_io_subrequest), 161d435d532SXin Yin GFP_KERNEL); 162d435d532SXin Yin if (subreq) { 163d435d532SXin Yin INIT_LIST_HEAD(&subreq->rreq_link); 164d435d532SXin Yin refcount_set(&subreq->ref, 2); 165d435d532SXin Yin subreq->rreq = rreq; 166d435d532SXin Yin refcount_inc(&rreq->ref); 167d435d532SXin Yin } else { 168d435d532SXin Yin ret = -ENOMEM; 169d435d532SXin Yin goto out; 170d435d532SXin Yin } 171ec00b5e2SJeffle Xu 172d435d532SXin Yin subreq->start = pstart + done; 173d435d532SXin Yin subreq->len = len - done; 174d435d532SXin Yin subreq->flags = 1 << NETFS_SREQ_ONDEMAND; 175d435d532SXin Yin 176d435d532SXin Yin list_add_tail(&subreq->rreq_link, &rreq->subrequests); 177d435d532SXin Yin 178d435d532SXin Yin source = cres->ops->prepare_read(subreq, LLONG_MAX); 179d435d532SXin Yin if (WARN_ON(subreq->len == 0)) 180ec00b5e2SJeffle Xu source = NETFS_INVALID_READ; 181ec00b5e2SJeffle Xu if (source != NETFS_READ_FROM_CACHE) { 182ec00b5e2SJeffle Xu erofs_err(sb, "failed to fscache prepare_read (source %d)", 183ec00b5e2SJeffle Xu source); 184ec00b5e2SJeffle Xu ret = -EIO; 185d435d532SXin Yin subreq->error = ret; 186d435d532SXin Yin erofs_fscache_put_subrequest(subreq); 187ec00b5e2SJeffle Xu goto out; 188ec00b5e2SJeffle Xu } 189ec00b5e2SJeffle Xu 190d435d532SXin Yin atomic_inc(&rreq->nr_outstanding); 191d435d532SXin Yin 192d435d532SXin Yin iov_iter_xarray(&iter, READ, &rreq->mapping->i_pages, 193d435d532SXin Yin start + done, subreq->len); 194d435d532SXin Yin 195d435d532SXin Yin ret = fscache_read(cres, subreq->start, &iter, 196d435d532SXin Yin NETFS_READ_HOLE_FAIL, 197d435d532SXin Yin erofc_fscache_subreq_complete, subreq); 198d435d532SXin Yin if (ret == -EIOCBQUEUED) 199d435d532SXin Yin ret = 0; 200ec00b5e2SJeffle Xu if (ret) { 201ec00b5e2SJeffle Xu erofs_err(sb, "failed to fscache_read (ret %d)", ret); 202ec00b5e2SJeffle Xu goto out; 203ec00b5e2SJeffle Xu } 204ec00b5e2SJeffle Xu 205d435d532SXin Yin done += subreq->len; 206ec00b5e2SJeffle Xu } 207ec00b5e2SJeffle Xu out: 208d435d532SXin Yin if (atomic_dec_and_test(&rreq->nr_outstanding)) 209d435d532SXin Yin erofs_fscache_rreq_complete(rreq); 210d435d532SXin Yin 211ec00b5e2SJeffle Xu return ret; 212ec00b5e2SJeffle Xu } 213ec00b5e2SJeffle Xu 214fdaf9a58SLinus Torvalds static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio) 2155375e7c8SJeffle Xu { 2165375e7c8SJeffle Xu int ret; 2175375e7c8SJeffle Xu struct super_block *sb = folio_mapping(folio)->host->i_sb; 218d435d532SXin Yin struct netfs_io_request *rreq; 2195375e7c8SJeffle Xu struct erofs_map_dev mdev = { 2205375e7c8SJeffle Xu .m_deviceid = 0, 2215375e7c8SJeffle Xu .m_pa = folio_pos(folio), 2225375e7c8SJeffle Xu }; 2235375e7c8SJeffle Xu 2245375e7c8SJeffle Xu ret = erofs_map_dev(sb, &mdev); 2255375e7c8SJeffle Xu if (ret) 2265375e7c8SJeffle Xu goto out; 2275375e7c8SJeffle Xu 228d435d532SXin Yin rreq = erofs_fscache_alloc_request(folio_mapping(folio), 229d435d532SXin Yin folio_pos(folio), folio_size(folio)); 2305bd9628bSSun Ke if (IS_ERR(rreq)) { 2315bd9628bSSun Ke ret = PTR_ERR(rreq); 232d435d532SXin Yin goto out; 2335bd9628bSSun Ke } 234d435d532SXin Yin 235d435d532SXin Yin return erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 236d435d532SXin Yin rreq, mdev.m_pa); 2375375e7c8SJeffle Xu out: 2385375e7c8SJeffle Xu folio_unlock(folio); 2395375e7c8SJeffle Xu return ret; 2405375e7c8SJeffle Xu } 2415375e7c8SJeffle Xu 242fdaf9a58SLinus Torvalds static int erofs_fscache_read_folio_inline(struct folio *folio, 243bd735bdaSJeffle Xu struct erofs_map_blocks *map) 244bd735bdaSJeffle Xu { 245bd735bdaSJeffle Xu struct super_block *sb = folio_mapping(folio)->host->i_sb; 246bd735bdaSJeffle Xu struct erofs_buf buf = __EROFS_BUF_INITIALIZER; 247bd735bdaSJeffle Xu erofs_blk_t blknr; 248bd735bdaSJeffle Xu size_t offset, len; 249bd735bdaSJeffle Xu void *src, *dst; 250bd735bdaSJeffle Xu 251bd735bdaSJeffle Xu /* For tail packing layout, the offset may be non-zero. */ 252bd735bdaSJeffle Xu offset = erofs_blkoff(map->m_pa); 253bd735bdaSJeffle Xu blknr = erofs_blknr(map->m_pa); 254bd735bdaSJeffle Xu len = map->m_llen; 255bd735bdaSJeffle Xu 256bd735bdaSJeffle Xu src = erofs_read_metabuf(&buf, sb, blknr, EROFS_KMAP); 257bd735bdaSJeffle Xu if (IS_ERR(src)) 258bd735bdaSJeffle Xu return PTR_ERR(src); 259bd735bdaSJeffle Xu 260bd735bdaSJeffle Xu dst = kmap_local_folio(folio, 0); 261bd735bdaSJeffle Xu memcpy(dst, src + offset, len); 262bd735bdaSJeffle Xu memset(dst + len, 0, PAGE_SIZE - len); 263bd735bdaSJeffle Xu kunmap_local(dst); 264bd735bdaSJeffle Xu 265bd735bdaSJeffle Xu erofs_put_metabuf(&buf); 266bd735bdaSJeffle Xu return 0; 267bd735bdaSJeffle Xu } 268bd735bdaSJeffle Xu 269fdaf9a58SLinus Torvalds static int erofs_fscache_read_folio(struct file *file, struct folio *folio) 2701442b02bSJeffle Xu { 2711442b02bSJeffle Xu struct inode *inode = folio_mapping(folio)->host; 2721442b02bSJeffle Xu struct super_block *sb = inode->i_sb; 2731442b02bSJeffle Xu struct erofs_map_blocks map; 2741442b02bSJeffle Xu struct erofs_map_dev mdev; 275d435d532SXin Yin struct netfs_io_request *rreq; 2761442b02bSJeffle Xu erofs_off_t pos; 2771442b02bSJeffle Xu loff_t pstart; 2781442b02bSJeffle Xu int ret; 2791442b02bSJeffle Xu 2801442b02bSJeffle Xu DBG_BUGON(folio_size(folio) != EROFS_BLKSIZ); 2811442b02bSJeffle Xu 2821442b02bSJeffle Xu pos = folio_pos(folio); 2831442b02bSJeffle Xu map.m_la = pos; 2841442b02bSJeffle Xu 2851442b02bSJeffle Xu ret = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW); 2861442b02bSJeffle Xu if (ret) 2871442b02bSJeffle Xu goto out_unlock; 2881442b02bSJeffle Xu 2891442b02bSJeffle Xu if (!(map.m_flags & EROFS_MAP_MAPPED)) { 2901442b02bSJeffle Xu folio_zero_range(folio, 0, folio_size(folio)); 2911442b02bSJeffle Xu goto out_uptodate; 2921442b02bSJeffle Xu } 2931442b02bSJeffle Xu 294bd735bdaSJeffle Xu if (map.m_flags & EROFS_MAP_META) { 295fdaf9a58SLinus Torvalds ret = erofs_fscache_read_folio_inline(folio, &map); 296bd735bdaSJeffle Xu goto out_uptodate; 297bd735bdaSJeffle Xu } 298bd735bdaSJeffle Xu 2991442b02bSJeffle Xu mdev = (struct erofs_map_dev) { 3001442b02bSJeffle Xu .m_deviceid = map.m_deviceid, 3011442b02bSJeffle Xu .m_pa = map.m_pa, 3021442b02bSJeffle Xu }; 3031442b02bSJeffle Xu 3041442b02bSJeffle Xu ret = erofs_map_dev(sb, &mdev); 3051442b02bSJeffle Xu if (ret) 3061442b02bSJeffle Xu goto out_unlock; 3071442b02bSJeffle Xu 308d435d532SXin Yin 309d435d532SXin Yin rreq = erofs_fscache_alloc_request(folio_mapping(folio), 310d435d532SXin Yin folio_pos(folio), folio_size(folio)); 3115bd9628bSSun Ke if (IS_ERR(rreq)) { 3125bd9628bSSun Ke ret = PTR_ERR(rreq); 313d435d532SXin Yin goto out_unlock; 3145bd9628bSSun Ke } 315d435d532SXin Yin 3161442b02bSJeffle Xu pstart = mdev.m_pa + (pos - map.m_la); 317d435d532SXin Yin return erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 318d435d532SXin Yin rreq, pstart); 3191442b02bSJeffle Xu 3201442b02bSJeffle Xu out_uptodate: 3211442b02bSJeffle Xu if (!ret) 3221442b02bSJeffle Xu folio_mark_uptodate(folio); 3231442b02bSJeffle Xu out_unlock: 3241442b02bSJeffle Xu folio_unlock(folio); 3251442b02bSJeffle Xu return ret; 3261442b02bSJeffle Xu } 3271442b02bSJeffle Xu 328d435d532SXin Yin static void erofs_fscache_advance_folios(struct readahead_control *rac, 329d435d532SXin Yin size_t len, bool unlock) 330c665b394SJeffle Xu { 331c665b394SJeffle Xu while (len) { 332c665b394SJeffle Xu struct folio *folio = readahead_folio(rac); 333c665b394SJeffle Xu len -= folio_size(folio); 334d435d532SXin Yin if (unlock) { 335c665b394SJeffle Xu folio_mark_uptodate(folio); 336c665b394SJeffle Xu folio_unlock(folio); 337c665b394SJeffle Xu } 338c665b394SJeffle Xu } 339d435d532SXin Yin } 340c665b394SJeffle Xu 341c665b394SJeffle Xu static void erofs_fscache_readahead(struct readahead_control *rac) 342c665b394SJeffle Xu { 343c665b394SJeffle Xu struct inode *inode = rac->mapping->host; 344c665b394SJeffle Xu struct super_block *sb = inode->i_sb; 345c665b394SJeffle Xu size_t len, count, done = 0; 346c665b394SJeffle Xu erofs_off_t pos; 347c665b394SJeffle Xu loff_t start, offset; 348c665b394SJeffle Xu int ret; 349c665b394SJeffle Xu 350c665b394SJeffle Xu if (!readahead_count(rac)) 351c665b394SJeffle Xu return; 352c665b394SJeffle Xu 353c665b394SJeffle Xu start = readahead_pos(rac); 354c665b394SJeffle Xu len = readahead_length(rac); 355c665b394SJeffle Xu 356c665b394SJeffle Xu do { 357c665b394SJeffle Xu struct erofs_map_blocks map; 358c665b394SJeffle Xu struct erofs_map_dev mdev; 359d435d532SXin Yin struct netfs_io_request *rreq; 360c665b394SJeffle Xu 361c665b394SJeffle Xu pos = start + done; 362c665b394SJeffle Xu map.m_la = pos; 363c665b394SJeffle Xu 364c665b394SJeffle Xu ret = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW); 365c665b394SJeffle Xu if (ret) 366c665b394SJeffle Xu return; 367c665b394SJeffle Xu 368c665b394SJeffle Xu offset = start + done; 369c665b394SJeffle Xu count = min_t(size_t, map.m_llen - (pos - map.m_la), 370c665b394SJeffle Xu len - done); 371c665b394SJeffle Xu 372c665b394SJeffle Xu if (!(map.m_flags & EROFS_MAP_MAPPED)) { 373c665b394SJeffle Xu struct iov_iter iter; 374c665b394SJeffle Xu 375c665b394SJeffle Xu iov_iter_xarray(&iter, READ, &rac->mapping->i_pages, 376c665b394SJeffle Xu offset, count); 377c665b394SJeffle Xu iov_iter_zero(count, &iter); 378c665b394SJeffle Xu 379d435d532SXin Yin erofs_fscache_advance_folios(rac, count, true); 380c665b394SJeffle Xu ret = count; 381c665b394SJeffle Xu continue; 382c665b394SJeffle Xu } 383c665b394SJeffle Xu 384c665b394SJeffle Xu if (map.m_flags & EROFS_MAP_META) { 385c665b394SJeffle Xu struct folio *folio = readahead_folio(rac); 386c665b394SJeffle Xu 387fdaf9a58SLinus Torvalds ret = erofs_fscache_read_folio_inline(folio, &map); 388c665b394SJeffle Xu if (!ret) { 389c665b394SJeffle Xu folio_mark_uptodate(folio); 390c665b394SJeffle Xu ret = folio_size(folio); 391c665b394SJeffle Xu } 392c665b394SJeffle Xu 393c665b394SJeffle Xu folio_unlock(folio); 394c665b394SJeffle Xu continue; 395c665b394SJeffle Xu } 396c665b394SJeffle Xu 397c665b394SJeffle Xu mdev = (struct erofs_map_dev) { 398c665b394SJeffle Xu .m_deviceid = map.m_deviceid, 399c665b394SJeffle Xu .m_pa = map.m_pa, 400c665b394SJeffle Xu }; 401c665b394SJeffle Xu ret = erofs_map_dev(sb, &mdev); 402c665b394SJeffle Xu if (ret) 403c665b394SJeffle Xu return; 404c665b394SJeffle Xu 405d435d532SXin Yin rreq = erofs_fscache_alloc_request(rac->mapping, offset, count); 406d435d532SXin Yin if (IS_ERR(rreq)) 407d435d532SXin Yin return; 408c665b394SJeffle Xu /* 409d435d532SXin Yin * Drop the ref of folios here. Unlock them in 410d435d532SXin Yin * rreq_unlock_folios() when rreq complete. 411c665b394SJeffle Xu */ 412d435d532SXin Yin erofs_fscache_advance_folios(rac, count, false); 413d435d532SXin Yin ret = erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 414d435d532SXin Yin rreq, mdev.m_pa + (pos - map.m_la)); 415d435d532SXin Yin if (!ret) 416c665b394SJeffle Xu ret = count; 417c665b394SJeffle Xu } while (ret > 0 && ((done += ret) < len)); 418c665b394SJeffle Xu } 419c665b394SJeffle Xu 4203c265d7dSJeffle Xu static const struct address_space_operations erofs_fscache_meta_aops = { 421fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_meta_read_folio, 4223c265d7dSJeffle Xu }; 4233c265d7dSJeffle Xu 4241442b02bSJeffle Xu const struct address_space_operations erofs_fscache_access_aops = { 425fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_read_folio, 426c665b394SJeffle Xu .readahead = erofs_fscache_readahead, 4271442b02bSJeffle Xu }; 4281442b02bSJeffle Xu 4298b7adf1dSJia Zhu static void erofs_fscache_domain_put(struct erofs_domain *domain) 4308b7adf1dSJia Zhu { 4318b7adf1dSJia Zhu if (!domain) 4328b7adf1dSJia Zhu return; 4338b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 4348b7adf1dSJia Zhu if (refcount_dec_and_test(&domain->ref)) { 4358b7adf1dSJia Zhu list_del(&domain->list); 436*a9849560SJia Zhu if (list_empty(&erofs_domain_list)) { 437*a9849560SJia Zhu kern_unmount(erofs_pseudo_mnt); 438*a9849560SJia Zhu erofs_pseudo_mnt = NULL; 439*a9849560SJia Zhu } 4408b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4418b7adf1dSJia Zhu fscache_relinquish_volume(domain->volume, NULL, false); 4428b7adf1dSJia Zhu kfree(domain->domain_id); 4438b7adf1dSJia Zhu kfree(domain); 4448b7adf1dSJia Zhu return; 4458b7adf1dSJia Zhu } 4468b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4478b7adf1dSJia Zhu } 4488b7adf1dSJia Zhu 4498b7adf1dSJia Zhu static int erofs_fscache_register_volume(struct super_block *sb) 4508b7adf1dSJia Zhu { 4518b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 4528b7adf1dSJia Zhu char *domain_id = sbi->opt.domain_id; 4538b7adf1dSJia Zhu struct fscache_volume *volume; 4548b7adf1dSJia Zhu char *name; 4558b7adf1dSJia Zhu int ret = 0; 4568b7adf1dSJia Zhu 4578b7adf1dSJia Zhu name = kasprintf(GFP_KERNEL, "erofs,%s", 4588b7adf1dSJia Zhu domain_id ? domain_id : sbi->opt.fsid); 4598b7adf1dSJia Zhu if (!name) 4608b7adf1dSJia Zhu return -ENOMEM; 4618b7adf1dSJia Zhu 4628b7adf1dSJia Zhu volume = fscache_acquire_volume(name, NULL, NULL, 0); 4638b7adf1dSJia Zhu if (IS_ERR_OR_NULL(volume)) { 4648b7adf1dSJia Zhu erofs_err(sb, "failed to register volume for %s", name); 4658b7adf1dSJia Zhu ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; 4668b7adf1dSJia Zhu volume = NULL; 4678b7adf1dSJia Zhu } 4688b7adf1dSJia Zhu 4698b7adf1dSJia Zhu sbi->volume = volume; 4708b7adf1dSJia Zhu kfree(name); 4718b7adf1dSJia Zhu return ret; 4728b7adf1dSJia Zhu } 4738b7adf1dSJia Zhu 4748b7adf1dSJia Zhu static int erofs_fscache_init_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 domain = kzalloc(sizeof(struct erofs_domain), GFP_KERNEL); 4818b7adf1dSJia Zhu if (!domain) 4828b7adf1dSJia Zhu return -ENOMEM; 4838b7adf1dSJia Zhu 4848b7adf1dSJia Zhu domain->domain_id = kstrdup(sbi->opt.domain_id, GFP_KERNEL); 4858b7adf1dSJia Zhu if (!domain->domain_id) { 4868b7adf1dSJia Zhu kfree(domain); 4878b7adf1dSJia Zhu return -ENOMEM; 4888b7adf1dSJia Zhu } 4898b7adf1dSJia Zhu 4908b7adf1dSJia Zhu err = erofs_fscache_register_volume(sb); 4918b7adf1dSJia Zhu if (err) 4928b7adf1dSJia Zhu goto out; 4938b7adf1dSJia Zhu 494*a9849560SJia Zhu if (!erofs_pseudo_mnt) { 495*a9849560SJia Zhu erofs_pseudo_mnt = kern_mount(&erofs_fs_type); 496*a9849560SJia Zhu if (IS_ERR(erofs_pseudo_mnt)) { 497*a9849560SJia Zhu err = PTR_ERR(erofs_pseudo_mnt); 498*a9849560SJia Zhu goto out; 499*a9849560SJia Zhu } 500*a9849560SJia Zhu } 501*a9849560SJia Zhu 5028b7adf1dSJia Zhu domain->volume = sbi->volume; 5038b7adf1dSJia Zhu refcount_set(&domain->ref, 1); 5048b7adf1dSJia Zhu list_add(&domain->list, &erofs_domain_list); 5058b7adf1dSJia Zhu sbi->domain = domain; 5068b7adf1dSJia Zhu return 0; 5078b7adf1dSJia Zhu out: 5088b7adf1dSJia Zhu kfree(domain->domain_id); 5098b7adf1dSJia Zhu kfree(domain); 5108b7adf1dSJia Zhu return err; 5118b7adf1dSJia Zhu } 5128b7adf1dSJia Zhu 5138b7adf1dSJia Zhu static int erofs_fscache_register_domain(struct super_block *sb) 5148b7adf1dSJia Zhu { 5158b7adf1dSJia Zhu int err; 5168b7adf1dSJia Zhu struct erofs_domain *domain; 5178b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 5188b7adf1dSJia Zhu 5198b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 5208b7adf1dSJia Zhu list_for_each_entry(domain, &erofs_domain_list, list) { 5218b7adf1dSJia Zhu if (!strcmp(domain->domain_id, sbi->opt.domain_id)) { 5228b7adf1dSJia Zhu sbi->domain = domain; 5238b7adf1dSJia Zhu sbi->volume = domain->volume; 5248b7adf1dSJia Zhu refcount_inc(&domain->ref); 5258b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 5268b7adf1dSJia Zhu return 0; 5278b7adf1dSJia Zhu } 5288b7adf1dSJia Zhu } 5298b7adf1dSJia Zhu err = erofs_fscache_init_domain(sb); 5308b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 5318b7adf1dSJia Zhu return err; 5328b7adf1dSJia Zhu } 5338b7adf1dSJia Zhu 534e1de2da0SJia Zhu struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, 5353c265d7dSJeffle Xu char *name, bool need_inode) 536b02c602fSJeffle Xu { 537b02c602fSJeffle Xu struct fscache_volume *volume = EROFS_SB(sb)->volume; 538b02c602fSJeffle Xu struct erofs_fscache *ctx; 539b02c602fSJeffle Xu struct fscache_cookie *cookie; 5403c265d7dSJeffle Xu int ret; 541b02c602fSJeffle Xu 542b02c602fSJeffle Xu ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 543b02c602fSJeffle Xu if (!ctx) 544e1de2da0SJia Zhu return ERR_PTR(-ENOMEM); 545b02c602fSJeffle Xu 546b02c602fSJeffle Xu cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE, 547b02c602fSJeffle Xu name, strlen(name), NULL, 0, 0); 548b02c602fSJeffle Xu if (!cookie) { 549b02c602fSJeffle Xu erofs_err(sb, "failed to get cookie for %s", name); 5503c265d7dSJeffle Xu ret = -EINVAL; 5513c265d7dSJeffle Xu goto err; 552b02c602fSJeffle Xu } 553b02c602fSJeffle Xu 554b02c602fSJeffle Xu fscache_use_cookie(cookie, false); 555b02c602fSJeffle Xu ctx->cookie = cookie; 556b02c602fSJeffle Xu 5573c265d7dSJeffle Xu if (need_inode) { 5583c265d7dSJeffle Xu struct inode *const inode = new_inode(sb); 5593c265d7dSJeffle Xu 5603c265d7dSJeffle Xu if (!inode) { 5613c265d7dSJeffle Xu erofs_err(sb, "failed to get anon inode for %s", name); 5623c265d7dSJeffle Xu ret = -ENOMEM; 5633c265d7dSJeffle Xu goto err_cookie; 5643c265d7dSJeffle Xu } 5653c265d7dSJeffle Xu 5663c265d7dSJeffle Xu set_nlink(inode, 1); 5673c265d7dSJeffle Xu inode->i_size = OFFSET_MAX; 5683c265d7dSJeffle Xu inode->i_mapping->a_ops = &erofs_fscache_meta_aops; 5693c265d7dSJeffle Xu mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); 5703c265d7dSJeffle Xu 5713c265d7dSJeffle Xu ctx->inode = inode; 5723c265d7dSJeffle Xu } 5733c265d7dSJeffle Xu 574e1de2da0SJia Zhu return ctx; 5753c265d7dSJeffle Xu 5763c265d7dSJeffle Xu err_cookie: 5773c265d7dSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 5783c265d7dSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 5793c265d7dSJeffle Xu err: 5803c265d7dSJeffle Xu kfree(ctx); 581e1de2da0SJia Zhu return ERR_PTR(ret); 582b02c602fSJeffle Xu } 583b02c602fSJeffle Xu 584e1de2da0SJia Zhu void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) 585b02c602fSJeffle Xu { 586b02c602fSJeffle Xu if (!ctx) 587b02c602fSJeffle Xu return; 588b02c602fSJeffle Xu 589b02c602fSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 590b02c602fSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 5913c265d7dSJeffle Xu iput(ctx->inode); 592b02c602fSJeffle Xu kfree(ctx); 593b02c602fSJeffle Xu } 594b02c602fSJeffle Xu 595c6be2bd0SJeffle Xu int erofs_fscache_register_fs(struct super_block *sb) 596c6be2bd0SJeffle Xu { 5978b7adf1dSJia Zhu int ret; 598c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 599e1de2da0SJia Zhu struct erofs_fscache *fscache; 600c6be2bd0SJeffle Xu 6018b7adf1dSJia Zhu if (sbi->opt.domain_id) 6028b7adf1dSJia Zhu ret = erofs_fscache_register_domain(sb); 6038b7adf1dSJia Zhu else 6048b7adf1dSJia Zhu ret = erofs_fscache_register_volume(sb); 6058b7adf1dSJia Zhu if (ret) 6068b7adf1dSJia Zhu return ret; 607c6be2bd0SJeffle Xu 6088b7adf1dSJia Zhu /* acquired domain/volume will be relinquished in kill_sb() on error */ 609e1de2da0SJia Zhu fscache = erofs_fscache_register_cookie(sb, sbi->opt.fsid, true); 610e1de2da0SJia Zhu if (IS_ERR(fscache)) 611e1de2da0SJia Zhu return PTR_ERR(fscache); 612e1de2da0SJia Zhu 613e1de2da0SJia Zhu sbi->s_fscache = fscache; 614e1de2da0SJia Zhu return 0; 615c6be2bd0SJeffle Xu } 616c6be2bd0SJeffle Xu 617c6be2bd0SJeffle Xu void erofs_fscache_unregister_fs(struct super_block *sb) 618c6be2bd0SJeffle Xu { 619c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 620c6be2bd0SJeffle Xu 621e1de2da0SJia Zhu erofs_fscache_unregister_cookie(sbi->s_fscache); 6228b7adf1dSJia Zhu 6238b7adf1dSJia Zhu if (sbi->domain) 6248b7adf1dSJia Zhu erofs_fscache_domain_put(sbi->domain); 6258b7adf1dSJia Zhu else 626c6be2bd0SJeffle Xu fscache_relinquish_volume(sbi->volume, NULL, false); 6278b7adf1dSJia Zhu 628e1de2da0SJia Zhu sbi->s_fscache = NULL; 629c6be2bd0SJeffle Xu sbi->volume = NULL; 6308b7adf1dSJia Zhu sbi->domain = NULL; 631c6be2bd0SJeffle Xu } 632