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 14709fe09eSJingbo Xu struct erofs_fscache_request { 15be62c519SJingbo Xu struct erofs_fscache_request *primary; 16709fe09eSJingbo Xu struct netfs_cache_resources cache_resources; 17709fe09eSJingbo Xu struct address_space *mapping; /* The mapping being accessed */ 18709fe09eSJingbo Xu loff_t start; /* Start position */ 19709fe09eSJingbo Xu size_t len; /* Length of the request */ 20709fe09eSJingbo Xu size_t submitted; /* Length of submitted */ 21709fe09eSJingbo Xu short error; /* 0 or error that occurred */ 22709fe09eSJingbo Xu refcount_t ref; 23709fe09eSJingbo Xu }; 24709fe09eSJingbo Xu 25709fe09eSJingbo Xu static struct erofs_fscache_request *erofs_fscache_req_alloc(struct address_space *mapping, 26d435d532SXin Yin loff_t start, size_t len) 27d435d532SXin Yin { 28709fe09eSJingbo Xu struct erofs_fscache_request *req; 29d435d532SXin Yin 30709fe09eSJingbo Xu req = kzalloc(sizeof(struct erofs_fscache_request), GFP_KERNEL); 31709fe09eSJingbo Xu if (!req) 32d435d532SXin Yin return ERR_PTR(-ENOMEM); 33d435d532SXin Yin 34709fe09eSJingbo Xu req->mapping = mapping; 35709fe09eSJingbo Xu req->start = start; 36709fe09eSJingbo Xu req->len = len; 37709fe09eSJingbo Xu refcount_set(&req->ref, 1); 38709fe09eSJingbo Xu 39709fe09eSJingbo Xu return req; 40d435d532SXin Yin } 41d435d532SXin Yin 42be62c519SJingbo Xu static struct erofs_fscache_request *erofs_fscache_req_chain(struct erofs_fscache_request *primary, 43be62c519SJingbo Xu size_t len) 44d435d532SXin Yin { 45be62c519SJingbo Xu struct erofs_fscache_request *req; 46be62c519SJingbo Xu 47be62c519SJingbo Xu /* use primary request for the first submission */ 48be62c519SJingbo Xu if (!primary->submitted) { 49be62c519SJingbo Xu refcount_inc(&primary->ref); 50be62c519SJingbo Xu return primary; 51d435d532SXin Yin } 52d435d532SXin Yin 53be62c519SJingbo Xu req = erofs_fscache_req_alloc(primary->mapping, 54be62c519SJingbo Xu primary->start + primary->submitted, len); 55be62c519SJingbo Xu if (!IS_ERR(req)) { 56be62c519SJingbo Xu req->primary = primary; 57be62c519SJingbo Xu refcount_inc(&primary->ref); 58be62c519SJingbo Xu } 59be62c519SJingbo Xu return req; 60be62c519SJingbo Xu } 61be62c519SJingbo Xu 62709fe09eSJingbo Xu static void erofs_fscache_req_complete(struct erofs_fscache_request *req) 63d435d532SXin Yin { 64d435d532SXin Yin struct folio *folio; 65709fe09eSJingbo Xu bool failed = req->error; 66709fe09eSJingbo Xu pgoff_t start_page = req->start / PAGE_SIZE; 67709fe09eSJingbo Xu pgoff_t last_page = ((req->start + req->len) / PAGE_SIZE) - 1; 68d435d532SXin Yin 69709fe09eSJingbo Xu XA_STATE(xas, &req->mapping->i_pages, start_page); 70d435d532SXin Yin 71d435d532SXin Yin rcu_read_lock(); 72d435d532SXin Yin xas_for_each(&xas, folio, last_page) { 7337020bbbSJingbo Xu if (xas_retry(&xas, folio)) 7437020bbbSJingbo Xu continue; 75709fe09eSJingbo Xu if (!failed) 76d435d532SXin Yin folio_mark_uptodate(folio); 77d435d532SXin Yin folio_unlock(folio); 78d435d532SXin Yin } 79d435d532SXin Yin rcu_read_unlock(); 80d435d532SXin Yin } 81d435d532SXin Yin 82709fe09eSJingbo Xu static void erofs_fscache_req_put(struct erofs_fscache_request *req) 83d435d532SXin Yin { 84be62c519SJingbo Xu if (refcount_dec_and_test(&req->ref)) { 85be62c519SJingbo Xu if (req->cache_resources.ops) 86be62c519SJingbo Xu req->cache_resources.ops->end_operation(&req->cache_resources); 87be62c519SJingbo Xu if (!req->primary) 88709fe09eSJingbo Xu erofs_fscache_req_complete(req); 89be62c519SJingbo Xu else 90be62c519SJingbo Xu erofs_fscache_req_put(req->primary); 91be62c519SJingbo Xu kfree(req); 92be62c519SJingbo Xu } 93d435d532SXin Yin } 94d435d532SXin Yin 95709fe09eSJingbo Xu static void erofs_fscache_subreq_complete(void *priv, 96d435d532SXin Yin ssize_t transferred_or_error, bool was_async) 97d435d532SXin Yin { 98709fe09eSJingbo Xu struct erofs_fscache_request *req = priv; 99d435d532SXin Yin 100be62c519SJingbo Xu if (IS_ERR_VALUE(transferred_or_error)) { 101be62c519SJingbo Xu if (req->primary) 102be62c519SJingbo Xu req->primary->error = transferred_or_error; 103be62c519SJingbo Xu else 104709fe09eSJingbo Xu req->error = transferred_or_error; 105be62c519SJingbo Xu } 106709fe09eSJingbo Xu erofs_fscache_req_put(req); 107d435d532SXin Yin } 108d435d532SXin Yin 109ec00b5e2SJeffle Xu /* 110709fe09eSJingbo Xu * Read data from fscache (cookie, pstart, len), and fill the read data into 111709fe09eSJingbo Xu * page cache described by (req->mapping, lstart, len). @pstart describeis the 112709fe09eSJingbo Xu * start physical address in the cache file. 113ec00b5e2SJeffle Xu */ 114d435d532SXin Yin static int erofs_fscache_read_folios_async(struct fscache_cookie *cookie, 115709fe09eSJingbo Xu struct erofs_fscache_request *req, loff_t pstart, size_t len) 116ec00b5e2SJeffle Xu { 117ec00b5e2SJeffle Xu enum netfs_io_source source; 118709fe09eSJingbo Xu struct super_block *sb = req->mapping->host->i_sb; 119709fe09eSJingbo Xu struct netfs_cache_resources *cres = &req->cache_resources; 120ec00b5e2SJeffle Xu struct iov_iter iter; 121709fe09eSJingbo Xu loff_t lstart = req->start + req->submitted; 122ec00b5e2SJeffle Xu size_t done = 0; 123ec00b5e2SJeffle Xu int ret; 124ec00b5e2SJeffle Xu 125709fe09eSJingbo Xu DBG_BUGON(len > req->len - req->submitted); 126d435d532SXin Yin 127ec00b5e2SJeffle Xu ret = fscache_begin_read_operation(cres, cookie); 128ec00b5e2SJeffle Xu if (ret) 129709fe09eSJingbo Xu return ret; 130ec00b5e2SJeffle Xu 131ec00b5e2SJeffle Xu while (done < len) { 132709fe09eSJingbo Xu loff_t sstart = pstart + done; 133709fe09eSJingbo Xu size_t slen = len - done; 134709fe09eSJingbo Xu unsigned long flags = 1 << NETFS_SREQ_ONDEMAND; 135ec00b5e2SJeffle Xu 136709fe09eSJingbo Xu source = cres->ops->prepare_ondemand_read(cres, 137709fe09eSJingbo Xu sstart, &slen, LLONG_MAX, &flags, 0); 138709fe09eSJingbo Xu if (WARN_ON(slen == 0)) 139ec00b5e2SJeffle Xu source = NETFS_INVALID_READ; 140ec00b5e2SJeffle Xu if (source != NETFS_READ_FROM_CACHE) { 141709fe09eSJingbo Xu erofs_err(sb, "failed to fscache prepare_read (source %d)", source); 142709fe09eSJingbo Xu return -EIO; 143ec00b5e2SJeffle Xu } 144ec00b5e2SJeffle Xu 145709fe09eSJingbo Xu refcount_inc(&req->ref); 1464a6bff11SLinus Torvalds iov_iter_xarray(&iter, ITER_DEST, &req->mapping->i_pages, 147709fe09eSJingbo Xu lstart + done, slen); 148d435d532SXin Yin 149709fe09eSJingbo Xu ret = fscache_read(cres, sstart, &iter, NETFS_READ_HOLE_FAIL, 150709fe09eSJingbo Xu erofs_fscache_subreq_complete, req); 151d435d532SXin Yin if (ret == -EIOCBQUEUED) 152d435d532SXin Yin ret = 0; 153ec00b5e2SJeffle Xu if (ret) { 154ec00b5e2SJeffle Xu erofs_err(sb, "failed to fscache_read (ret %d)", ret); 155ec00b5e2SJeffle Xu return ret; 156ec00b5e2SJeffle Xu } 157ec00b5e2SJeffle Xu 158709fe09eSJingbo Xu done += slen; 159709fe09eSJingbo Xu } 160709fe09eSJingbo Xu DBG_BUGON(done != len); 161709fe09eSJingbo Xu return 0; 162709fe09eSJingbo Xu } 163709fe09eSJingbo Xu 164fdaf9a58SLinus Torvalds static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio) 1655375e7c8SJeffle Xu { 1665375e7c8SJeffle Xu int ret; 167*bdfa9014SJingbo Xu struct erofs_fscache *ctx = folio_mapping(folio)->host->i_private; 168709fe09eSJingbo Xu struct erofs_fscache_request *req; 1695375e7c8SJeffle Xu 170709fe09eSJingbo Xu req = erofs_fscache_req_alloc(folio_mapping(folio), 171709fe09eSJingbo Xu folio_pos(folio), folio_size(folio)); 172709fe09eSJingbo Xu if (IS_ERR(req)) { 1733c265d7dSJeffle Xu folio_unlock(folio); 174709fe09eSJingbo Xu return PTR_ERR(req); 175709fe09eSJingbo Xu } 176709fe09eSJingbo Xu 177*bdfa9014SJingbo Xu ret = erofs_fscache_read_folios_async(ctx->cookie, req, 178*bdfa9014SJingbo Xu folio_pos(folio), folio_size(folio)); 179709fe09eSJingbo Xu if (ret) 180709fe09eSJingbo Xu req->error = ret; 181709fe09eSJingbo Xu 182709fe09eSJingbo Xu erofs_fscache_req_put(req); 1833c265d7dSJeffle Xu return ret; 1843c265d7dSJeffle Xu } 1853c265d7dSJeffle Xu 186be62c519SJingbo Xu static int erofs_fscache_data_read_slice(struct erofs_fscache_request *primary) 187bd735bdaSJeffle Xu { 188be62c519SJingbo Xu struct address_space *mapping = primary->mapping; 1891ae9470cSJingbo Xu struct inode *inode = mapping->host; 1901ae9470cSJingbo Xu struct super_block *sb = inode->i_sb; 191709fe09eSJingbo Xu struct erofs_fscache_request *req; 1921ae9470cSJingbo Xu struct erofs_map_blocks map; 1931ae9470cSJingbo Xu struct erofs_map_dev mdev; 1941ae9470cSJingbo Xu struct iov_iter iter; 195be62c519SJingbo Xu loff_t pos = primary->start + primary->submitted; 1961ae9470cSJingbo Xu size_t count; 1971ae9470cSJingbo Xu int ret; 1981ae9470cSJingbo Xu 1991ae9470cSJingbo Xu map.m_la = pos; 2008b58f9f0SJingbo Xu ret = erofs_map_blocks(inode, &map); 2011ae9470cSJingbo Xu if (ret) 2021ae9470cSJingbo Xu return ret; 2031ae9470cSJingbo Xu 2041ae9470cSJingbo Xu if (map.m_flags & EROFS_MAP_META) { 205bd735bdaSJeffle Xu struct erofs_buf buf = __EROFS_BUF_INITIALIZER; 206bd735bdaSJeffle Xu erofs_blk_t blknr; 2071ae9470cSJingbo Xu size_t offset, size; 2081ae9470cSJingbo Xu void *src; 209bd735bdaSJeffle Xu 210bd735bdaSJeffle Xu /* For tail packing layout, the offset may be non-zero. */ 2111ae9470cSJingbo Xu offset = erofs_blkoff(map.m_pa); 2121ae9470cSJingbo Xu blknr = erofs_blknr(map.m_pa); 2131ae9470cSJingbo Xu size = map.m_llen; 214bd735bdaSJeffle Xu 215bd735bdaSJeffle Xu src = erofs_read_metabuf(&buf, sb, blknr, EROFS_KMAP); 216bd735bdaSJeffle Xu if (IS_ERR(src)) 217bd735bdaSJeffle Xu return PTR_ERR(src); 218bd735bdaSJeffle Xu 219de4eda9dSAl Viro iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, PAGE_SIZE); 22075e43355SJingbo Xu if (copy_to_iter(src + offset, size, &iter) != size) { 22175e43355SJingbo Xu erofs_put_metabuf(&buf); 2221ae9470cSJingbo Xu return -EFAULT; 22375e43355SJingbo Xu } 2241ae9470cSJingbo Xu iov_iter_zero(PAGE_SIZE - size, &iter); 225bd735bdaSJeffle Xu erofs_put_metabuf(&buf); 226be62c519SJingbo Xu primary->submitted += PAGE_SIZE; 227be62c519SJingbo Xu return 0; 228bd735bdaSJeffle Xu } 229bd735bdaSJeffle Xu 230be62c519SJingbo Xu count = primary->len - primary->submitted; 2311442b02bSJeffle Xu if (!(map.m_flags & EROFS_MAP_MAPPED)) { 232de4eda9dSAl Viro iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, count); 2331ae9470cSJingbo Xu iov_iter_zero(count, &iter); 234be62c519SJingbo Xu primary->submitted += count; 235be62c519SJingbo Xu return 0; 236bd735bdaSJeffle Xu } 237bd735bdaSJeffle Xu 238be62c519SJingbo Xu count = min_t(size_t, map.m_llen - (pos - map.m_la), count); 239e6d9f9baSJingbo Xu DBG_BUGON(!count || count % PAGE_SIZE); 240e6d9f9baSJingbo Xu 2411442b02bSJeffle Xu mdev = (struct erofs_map_dev) { 2421442b02bSJeffle Xu .m_deviceid = map.m_deviceid, 2431442b02bSJeffle Xu .m_pa = map.m_pa, 2441442b02bSJeffle Xu }; 2451442b02bSJeffle Xu ret = erofs_map_dev(sb, &mdev); 2461442b02bSJeffle Xu if (ret) 2471442b02bSJeffle Xu return ret; 2481ae9470cSJingbo Xu 249be62c519SJingbo Xu req = erofs_fscache_req_chain(primary, count); 250709fe09eSJingbo Xu if (IS_ERR(req)) 251709fe09eSJingbo Xu return PTR_ERR(req); 2521ae9470cSJingbo Xu 253709fe09eSJingbo Xu ret = erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 254709fe09eSJingbo Xu req, mdev.m_pa + (pos - map.m_la), count); 255be62c519SJingbo Xu erofs_fscache_req_put(req); 256be62c519SJingbo Xu primary->submitted += count; 257be62c519SJingbo Xu return ret; 258be62c519SJingbo Xu } 259be62c519SJingbo Xu 260be62c519SJingbo Xu static int erofs_fscache_data_read(struct erofs_fscache_request *req) 261be62c519SJingbo Xu { 262be62c519SJingbo Xu int ret; 263be62c519SJingbo Xu 264be62c519SJingbo Xu do { 265be62c519SJingbo Xu ret = erofs_fscache_data_read_slice(req); 266709fe09eSJingbo Xu if (ret) 267709fe09eSJingbo Xu req->error = ret; 268be62c519SJingbo Xu } while (!ret && req->submitted < req->len); 269709fe09eSJingbo Xu 270be62c519SJingbo Xu return ret; 2711442b02bSJeffle Xu } 2721442b02bSJeffle Xu 2731ae9470cSJingbo Xu static int erofs_fscache_read_folio(struct file *file, struct folio *folio) 274c665b394SJeffle Xu { 275be62c519SJingbo Xu struct erofs_fscache_request *req; 2761ae9470cSJingbo Xu int ret; 2771ae9470cSJingbo Xu 278be62c519SJingbo Xu req = erofs_fscache_req_alloc(folio_mapping(folio), 279be62c519SJingbo Xu folio_pos(folio), folio_size(folio)); 280be62c519SJingbo Xu if (IS_ERR(req)) { 281c665b394SJeffle Xu folio_unlock(folio); 282be62c519SJingbo Xu return PTR_ERR(req); 283c665b394SJeffle Xu } 284be62c519SJingbo Xu 285be62c519SJingbo Xu ret = erofs_fscache_data_read(req); 286be62c519SJingbo Xu erofs_fscache_req_put(req); 287be62c519SJingbo Xu return ret; 288d435d532SXin Yin } 289c665b394SJeffle Xu 290c665b394SJeffle Xu static void erofs_fscache_readahead(struct readahead_control *rac) 291c665b394SJeffle Xu { 292be62c519SJingbo Xu struct erofs_fscache_request *req; 293c665b394SJeffle Xu 294c665b394SJeffle Xu if (!readahead_count(rac)) 295c665b394SJeffle Xu return; 296c665b394SJeffle Xu 297be62c519SJingbo Xu req = erofs_fscache_req_alloc(rac->mapping, 298be62c519SJingbo Xu readahead_pos(rac), readahead_length(rac)); 299be62c519SJingbo Xu if (IS_ERR(req)) 300c665b394SJeffle Xu return; 301c665b394SJeffle Xu 302be62c519SJingbo Xu /* The request completion will drop refs on the folios. */ 303be62c519SJingbo Xu while (readahead_folio(rac)) 304be62c519SJingbo Xu ; 305be62c519SJingbo Xu 306be62c519SJingbo Xu erofs_fscache_data_read(req); 307be62c519SJingbo Xu erofs_fscache_req_put(req); 308c665b394SJeffle Xu } 309c665b394SJeffle Xu 310c6be2bd0SJeffle Xu static const struct address_space_operations erofs_fscache_meta_aops = { 311fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_meta_read_folio, 312c6be2bd0SJeffle Xu }; 313c6be2bd0SJeffle Xu 3141442b02bSJeffle Xu const struct address_space_operations erofs_fscache_access_aops = { 315fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_read_folio, 316c665b394SJeffle Xu .readahead = erofs_fscache_readahead, 3171442b02bSJeffle Xu }; 3181442b02bSJeffle Xu 3198b7adf1dSJia Zhu static void erofs_fscache_domain_put(struct erofs_domain *domain) 3208b7adf1dSJia Zhu { 3218b7adf1dSJia Zhu if (!domain) 3228b7adf1dSJia Zhu return; 3238b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 3248b7adf1dSJia Zhu if (refcount_dec_and_test(&domain->ref)) { 3258b7adf1dSJia Zhu list_del(&domain->list); 326a9849560SJia Zhu if (list_empty(&erofs_domain_list)) { 327a9849560SJia Zhu kern_unmount(erofs_pseudo_mnt); 328a9849560SJia Zhu erofs_pseudo_mnt = NULL; 329a9849560SJia Zhu } 3308b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 3318b7adf1dSJia Zhu fscache_relinquish_volume(domain->volume, NULL, false); 3328b7adf1dSJia Zhu kfree(domain->domain_id); 3338b7adf1dSJia Zhu kfree(domain); 3348b7adf1dSJia Zhu return; 3358b7adf1dSJia Zhu } 3368b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 3378b7adf1dSJia Zhu } 3388b7adf1dSJia Zhu 3398b7adf1dSJia Zhu static int erofs_fscache_register_volume(struct super_block *sb) 3408b7adf1dSJia Zhu { 3418b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 34239bfcb81SJingbo Xu char *domain_id = sbi->domain_id; 3438b7adf1dSJia Zhu struct fscache_volume *volume; 3448b7adf1dSJia Zhu char *name; 3458b7adf1dSJia Zhu int ret = 0; 3468b7adf1dSJia Zhu 3478b7adf1dSJia Zhu name = kasprintf(GFP_KERNEL, "erofs,%s", 34839bfcb81SJingbo Xu domain_id ? domain_id : sbi->fsid); 3498b7adf1dSJia Zhu if (!name) 3508b7adf1dSJia Zhu return -ENOMEM; 3518b7adf1dSJia Zhu 3528b7adf1dSJia Zhu volume = fscache_acquire_volume(name, NULL, NULL, 0); 3538b7adf1dSJia Zhu if (IS_ERR_OR_NULL(volume)) { 3548b7adf1dSJia Zhu erofs_err(sb, "failed to register volume for %s", name); 3558b7adf1dSJia Zhu ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; 3568b7adf1dSJia Zhu volume = NULL; 3578b7adf1dSJia Zhu } 3588b7adf1dSJia Zhu 3598b7adf1dSJia Zhu sbi->volume = volume; 3608b7adf1dSJia Zhu kfree(name); 3618b7adf1dSJia Zhu return ret; 3628b7adf1dSJia Zhu } 3638b7adf1dSJia Zhu 3648b7adf1dSJia Zhu static int erofs_fscache_init_domain(struct super_block *sb) 3658b7adf1dSJia Zhu { 3668b7adf1dSJia Zhu int err; 3678b7adf1dSJia Zhu struct erofs_domain *domain; 3688b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 3698b7adf1dSJia Zhu 3708b7adf1dSJia Zhu domain = kzalloc(sizeof(struct erofs_domain), GFP_KERNEL); 3718b7adf1dSJia Zhu if (!domain) 3728b7adf1dSJia Zhu return -ENOMEM; 3738b7adf1dSJia Zhu 37439bfcb81SJingbo Xu domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL); 3758b7adf1dSJia Zhu if (!domain->domain_id) { 3768b7adf1dSJia Zhu kfree(domain); 3778b7adf1dSJia Zhu return -ENOMEM; 3788b7adf1dSJia Zhu } 3798b7adf1dSJia Zhu 3808b7adf1dSJia Zhu err = erofs_fscache_register_volume(sb); 3818b7adf1dSJia Zhu if (err) 3828b7adf1dSJia Zhu goto out; 3838b7adf1dSJia Zhu 384a9849560SJia Zhu if (!erofs_pseudo_mnt) { 385a9849560SJia Zhu erofs_pseudo_mnt = kern_mount(&erofs_fs_type); 386a9849560SJia Zhu if (IS_ERR(erofs_pseudo_mnt)) { 387a9849560SJia Zhu err = PTR_ERR(erofs_pseudo_mnt); 388a9849560SJia Zhu goto out; 389a9849560SJia Zhu } 390a9849560SJia Zhu } 391a9849560SJia Zhu 3928b7adf1dSJia Zhu domain->volume = sbi->volume; 3938b7adf1dSJia Zhu refcount_set(&domain->ref, 1); 3948b7adf1dSJia Zhu list_add(&domain->list, &erofs_domain_list); 3958b7adf1dSJia Zhu sbi->domain = domain; 3968b7adf1dSJia Zhu return 0; 3978b7adf1dSJia Zhu out: 3988b7adf1dSJia Zhu kfree(domain->domain_id); 3998b7adf1dSJia Zhu kfree(domain); 4008b7adf1dSJia Zhu return err; 4018b7adf1dSJia Zhu } 4028b7adf1dSJia Zhu 4038b7adf1dSJia Zhu static int erofs_fscache_register_domain(struct super_block *sb) 4048b7adf1dSJia Zhu { 4058b7adf1dSJia Zhu int err; 4068b7adf1dSJia Zhu struct erofs_domain *domain; 4078b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 4088b7adf1dSJia Zhu 4098b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 4108b7adf1dSJia Zhu list_for_each_entry(domain, &erofs_domain_list, list) { 41139bfcb81SJingbo Xu if (!strcmp(domain->domain_id, sbi->domain_id)) { 4128b7adf1dSJia Zhu sbi->domain = domain; 4138b7adf1dSJia Zhu sbi->volume = domain->volume; 4148b7adf1dSJia Zhu refcount_inc(&domain->ref); 4158b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4168b7adf1dSJia Zhu return 0; 4178b7adf1dSJia Zhu } 4188b7adf1dSJia Zhu } 4198b7adf1dSJia Zhu err = erofs_fscache_init_domain(sb); 4208b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4218b7adf1dSJia Zhu return err; 4228b7adf1dSJia Zhu } 4238b7adf1dSJia Zhu 4247d419637SJia Zhu static 4257d419637SJia Zhu struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb, 42627f2a2dcSHou Tao char *name, 42727f2a2dcSHou Tao unsigned int flags) 428c6be2bd0SJeffle Xu { 429c6be2bd0SJeffle Xu struct fscache_volume *volume = EROFS_SB(sb)->volume; 430c6be2bd0SJeffle Xu struct erofs_fscache *ctx; 431c6be2bd0SJeffle Xu struct fscache_cookie *cookie; 432c6be2bd0SJeffle Xu int ret; 433c6be2bd0SJeffle Xu 434c6be2bd0SJeffle Xu ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 435c6be2bd0SJeffle Xu if (!ctx) 436e1de2da0SJia Zhu return ERR_PTR(-ENOMEM); 437c6be2bd0SJeffle Xu 438c6be2bd0SJeffle Xu cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE, 439c6be2bd0SJeffle Xu name, strlen(name), NULL, 0, 0); 440c6be2bd0SJeffle Xu if (!cookie) { 441c6be2bd0SJeffle Xu erofs_err(sb, "failed to get cookie for %s", name); 442c6be2bd0SJeffle Xu ret = -EINVAL; 443c6be2bd0SJeffle Xu goto err; 444c6be2bd0SJeffle Xu } 445c6be2bd0SJeffle Xu 446c6be2bd0SJeffle Xu fscache_use_cookie(cookie, false); 447c6be2bd0SJeffle Xu ctx->cookie = cookie; 448c6be2bd0SJeffle Xu 44927f2a2dcSHou Tao if (flags & EROFS_REG_COOKIE_NEED_INODE) { 450c6be2bd0SJeffle Xu struct inode *const inode = new_inode(sb); 451c6be2bd0SJeffle Xu 452b02c602fSJeffle Xu if (!inode) { 453b02c602fSJeffle Xu erofs_err(sb, "failed to get anon inode for %s", name); 454b02c602fSJeffle Xu ret = -ENOMEM; 455b02c602fSJeffle Xu goto err_cookie; 456b02c602fSJeffle Xu } 457b02c602fSJeffle Xu 458b02c602fSJeffle Xu set_nlink(inode, 1); 459b02c602fSJeffle Xu inode->i_size = OFFSET_MAX; 460b02c602fSJeffle Xu inode->i_mapping->a_ops = &erofs_fscache_meta_aops; 461b02c602fSJeffle Xu mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); 462*bdfa9014SJingbo Xu inode->i_private = ctx; 463b02c602fSJeffle Xu 464b02c602fSJeffle Xu ctx->inode = inode; 465b02c602fSJeffle Xu } 466b02c602fSJeffle Xu 467e1de2da0SJia Zhu return ctx; 468b02c602fSJeffle Xu 469b02c602fSJeffle Xu err_cookie: 470b02c602fSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 471b02c602fSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 472b02c602fSJeffle Xu err: 473b02c602fSJeffle Xu kfree(ctx); 474e1de2da0SJia Zhu return ERR_PTR(ret); 475b02c602fSJeffle Xu } 476b02c602fSJeffle Xu 4777d419637SJia Zhu static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx) 478b02c602fSJeffle Xu { 479b02c602fSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 480b02c602fSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 4813c265d7dSJeffle Xu iput(ctx->inode); 4827d419637SJia Zhu kfree(ctx->name); 483b02c602fSJeffle Xu kfree(ctx); 484b02c602fSJeffle Xu } 485b02c602fSJeffle Xu 4867d419637SJia Zhu static 4877d419637SJia Zhu struct erofs_fscache *erofs_fscache_domain_init_cookie(struct super_block *sb, 48827f2a2dcSHou Tao char *name, 48927f2a2dcSHou Tao unsigned int flags) 4907d419637SJia Zhu { 4917d419637SJia Zhu int err; 4927d419637SJia Zhu struct inode *inode; 4937d419637SJia Zhu struct erofs_fscache *ctx; 4947d419637SJia Zhu struct erofs_domain *domain = EROFS_SB(sb)->domain; 4957d419637SJia Zhu 49627f2a2dcSHou Tao ctx = erofs_fscache_acquire_cookie(sb, name, flags); 4977d419637SJia Zhu if (IS_ERR(ctx)) 4987d419637SJia Zhu return ctx; 4997d419637SJia Zhu 5007d419637SJia Zhu ctx->name = kstrdup(name, GFP_KERNEL); 5017d419637SJia Zhu if (!ctx->name) { 5027d419637SJia Zhu err = -ENOMEM; 5037d419637SJia Zhu goto out; 5047d419637SJia Zhu } 5057d419637SJia Zhu 5067d419637SJia Zhu inode = new_inode(erofs_pseudo_mnt->mnt_sb); 5077d419637SJia Zhu if (!inode) { 5087d419637SJia Zhu err = -ENOMEM; 5097d419637SJia Zhu goto out; 5107d419637SJia Zhu } 5117d419637SJia Zhu 5127d419637SJia Zhu ctx->domain = domain; 5137d419637SJia Zhu ctx->anon_inode = inode; 5147d419637SJia Zhu inode->i_private = ctx; 5157d419637SJia Zhu refcount_inc(&domain->ref); 5167d419637SJia Zhu return ctx; 5177d419637SJia Zhu out: 5187d419637SJia Zhu erofs_fscache_relinquish_cookie(ctx); 5197d419637SJia Zhu return ERR_PTR(err); 5207d419637SJia Zhu } 5217d419637SJia Zhu 5227d419637SJia Zhu static 5237d419637SJia Zhu struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, 52427f2a2dcSHou Tao char *name, 52527f2a2dcSHou Tao unsigned int flags) 5267d419637SJia Zhu { 5277d419637SJia Zhu struct inode *inode; 5287d419637SJia Zhu struct erofs_fscache *ctx; 5297d419637SJia Zhu struct erofs_domain *domain = EROFS_SB(sb)->domain; 5307d419637SJia Zhu struct super_block *psb = erofs_pseudo_mnt->mnt_sb; 5317d419637SJia Zhu 5327d419637SJia Zhu mutex_lock(&erofs_domain_cookies_lock); 533ce4b8156SDawei Li spin_lock(&psb->s_inode_list_lock); 5347d419637SJia Zhu list_for_each_entry(inode, &psb->s_inodes, i_sb_list) { 5357d419637SJia Zhu ctx = inode->i_private; 5367d419637SJia Zhu if (!ctx || ctx->domain != domain || strcmp(ctx->name, name)) 5377d419637SJia Zhu continue; 53827f2a2dcSHou Tao if (!(flags & EROFS_REG_COOKIE_NEED_NOEXIST)) { 5397d419637SJia Zhu igrab(inode); 54027f2a2dcSHou Tao } else { 54127f2a2dcSHou Tao erofs_err(sb, "%s already exists in domain %s", name, 54227f2a2dcSHou Tao domain->domain_id); 54327f2a2dcSHou Tao ctx = ERR_PTR(-EEXIST); 54427f2a2dcSHou Tao } 545ce4b8156SDawei Li spin_unlock(&psb->s_inode_list_lock); 5467d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 5477d419637SJia Zhu return ctx; 5487d419637SJia Zhu } 549ce4b8156SDawei Li spin_unlock(&psb->s_inode_list_lock); 55027f2a2dcSHou Tao ctx = erofs_fscache_domain_init_cookie(sb, name, flags); 5517d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 5527d419637SJia Zhu return ctx; 5537d419637SJia Zhu } 5547d419637SJia Zhu 5557d419637SJia Zhu struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, 55627f2a2dcSHou Tao char *name, 55727f2a2dcSHou Tao unsigned int flags) 5587d419637SJia Zhu { 55939bfcb81SJingbo Xu if (EROFS_SB(sb)->domain_id) 56027f2a2dcSHou Tao return erofs_domain_register_cookie(sb, name, flags); 56127f2a2dcSHou Tao return erofs_fscache_acquire_cookie(sb, name, flags); 5627d419637SJia Zhu } 5637d419637SJia Zhu 5647d419637SJia Zhu void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) 5657d419637SJia Zhu { 5667d419637SJia Zhu bool drop; 5677d419637SJia Zhu struct erofs_domain *domain; 5687d419637SJia Zhu 5697d419637SJia Zhu if (!ctx) 5707d419637SJia Zhu return; 5717d419637SJia Zhu domain = ctx->domain; 5727d419637SJia Zhu if (domain) { 5737d419637SJia Zhu mutex_lock(&erofs_domain_cookies_lock); 5747d419637SJia Zhu drop = atomic_read(&ctx->anon_inode->i_count) == 1; 5757d419637SJia Zhu iput(ctx->anon_inode); 5767d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 5777d419637SJia Zhu if (!drop) 5787d419637SJia Zhu return; 5797d419637SJia Zhu } 5807d419637SJia Zhu 5817d419637SJia Zhu erofs_fscache_relinquish_cookie(ctx); 5827d419637SJia Zhu erofs_fscache_domain_put(domain); 5837d419637SJia Zhu } 5847d419637SJia Zhu 585c6be2bd0SJeffle Xu int erofs_fscache_register_fs(struct super_block *sb) 586c6be2bd0SJeffle Xu { 5878b7adf1dSJia Zhu int ret; 588c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 589e1de2da0SJia Zhu struct erofs_fscache *fscache; 59027f2a2dcSHou Tao unsigned int flags; 591c6be2bd0SJeffle Xu 59239bfcb81SJingbo Xu if (sbi->domain_id) 5938b7adf1dSJia Zhu ret = erofs_fscache_register_domain(sb); 5948b7adf1dSJia Zhu else 5958b7adf1dSJia Zhu ret = erofs_fscache_register_volume(sb); 5968b7adf1dSJia Zhu if (ret) 5978b7adf1dSJia Zhu return ret; 598c6be2bd0SJeffle Xu 59927f2a2dcSHou Tao /* 60027f2a2dcSHou Tao * When shared domain is enabled, using NEED_NOEXIST to guarantee 60127f2a2dcSHou Tao * the primary data blob (aka fsid) is unique in the shared domain. 60227f2a2dcSHou Tao * 60327f2a2dcSHou Tao * For non-shared-domain case, fscache_acquire_volume() invoked by 60427f2a2dcSHou Tao * erofs_fscache_register_volume() has already guaranteed 60527f2a2dcSHou Tao * the uniqueness of primary data blob. 60627f2a2dcSHou Tao * 60727f2a2dcSHou Tao * Acquired domain/volume will be relinquished in kill_sb() on error. 60827f2a2dcSHou Tao */ 60927f2a2dcSHou Tao flags = EROFS_REG_COOKIE_NEED_INODE; 61027f2a2dcSHou Tao if (sbi->domain_id) 61127f2a2dcSHou Tao flags |= EROFS_REG_COOKIE_NEED_NOEXIST; 61227f2a2dcSHou Tao fscache = erofs_fscache_register_cookie(sb, sbi->fsid, flags); 613e1de2da0SJia Zhu if (IS_ERR(fscache)) 614e1de2da0SJia Zhu return PTR_ERR(fscache); 615e1de2da0SJia Zhu 616e1de2da0SJia Zhu sbi->s_fscache = fscache; 617e1de2da0SJia Zhu return 0; 618c6be2bd0SJeffle Xu } 619c6be2bd0SJeffle Xu 620c6be2bd0SJeffle Xu void erofs_fscache_unregister_fs(struct super_block *sb) 621c6be2bd0SJeffle Xu { 622c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 623c6be2bd0SJeffle Xu 624e1de2da0SJia Zhu erofs_fscache_unregister_cookie(sbi->s_fscache); 6258b7adf1dSJia Zhu 6268b7adf1dSJia Zhu if (sbi->domain) 6278b7adf1dSJia Zhu erofs_fscache_domain_put(sbi->domain); 6288b7adf1dSJia Zhu else 629c6be2bd0SJeffle Xu fscache_relinquish_volume(sbi->volume, NULL, false); 6308b7adf1dSJia Zhu 631e1de2da0SJia Zhu sbi->s_fscache = NULL; 632c6be2bd0SJeffle Xu sbi->volume = NULL; 6338b7adf1dSJia Zhu sbi->domain = NULL; 634c6be2bd0SJeffle Xu } 635