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 */ 6*145cd3e3SBaokun Li #include <linux/pseudo_fs.h> 7c6be2bd0SJeffle Xu #include <linux/fscache.h> 8c6be2bd0SJeffle Xu #include "internal.h" 9c6be2bd0SJeffle Xu 108b7adf1dSJia Zhu static DEFINE_MUTEX(erofs_domain_list_lock); 117d419637SJia Zhu static DEFINE_MUTEX(erofs_domain_cookies_lock); 128b7adf1dSJia Zhu static LIST_HEAD(erofs_domain_list); 132dfb8c3bSJingbo Xu static LIST_HEAD(erofs_domain_cookies_list); 14a9849560SJia Zhu static struct vfsmount *erofs_pseudo_mnt; 158b7adf1dSJia Zhu 16*145cd3e3SBaokun Li static int erofs_anon_init_fs_context(struct fs_context *fc) 17*145cd3e3SBaokun Li { 18*145cd3e3SBaokun Li return init_pseudo(fc, EROFS_SUPER_MAGIC) ? 0 : -ENOMEM; 19*145cd3e3SBaokun Li } 20*145cd3e3SBaokun Li 21*145cd3e3SBaokun Li static struct file_system_type erofs_anon_fs_type = { 22*145cd3e3SBaokun Li .owner = THIS_MODULE, 23*145cd3e3SBaokun Li .name = "pseudo_erofs", 24*145cd3e3SBaokun Li .init_fs_context = erofs_anon_init_fs_context, 25*145cd3e3SBaokun Li .kill_sb = kill_anon_super, 26*145cd3e3SBaokun Li }; 27*145cd3e3SBaokun Li 28709fe09eSJingbo Xu struct erofs_fscache_request { 29be62c519SJingbo Xu struct erofs_fscache_request *primary; 30709fe09eSJingbo Xu struct netfs_cache_resources cache_resources; 31709fe09eSJingbo Xu struct address_space *mapping; /* The mapping being accessed */ 32709fe09eSJingbo Xu loff_t start; /* Start position */ 33709fe09eSJingbo Xu size_t len; /* Length of the request */ 34709fe09eSJingbo Xu size_t submitted; /* Length of submitted */ 35709fe09eSJingbo Xu short error; /* 0 or error that occurred */ 36709fe09eSJingbo Xu refcount_t ref; 37709fe09eSJingbo Xu }; 38709fe09eSJingbo Xu 39709fe09eSJingbo Xu static struct erofs_fscache_request *erofs_fscache_req_alloc(struct address_space *mapping, 40d435d532SXin Yin loff_t start, size_t len) 41d435d532SXin Yin { 42709fe09eSJingbo Xu struct erofs_fscache_request *req; 43d435d532SXin Yin 44709fe09eSJingbo Xu req = kzalloc(sizeof(struct erofs_fscache_request), GFP_KERNEL); 45709fe09eSJingbo Xu if (!req) 46d435d532SXin Yin return ERR_PTR(-ENOMEM); 47d435d532SXin Yin 48709fe09eSJingbo Xu req->mapping = mapping; 49709fe09eSJingbo Xu req->start = start; 50709fe09eSJingbo Xu req->len = len; 51709fe09eSJingbo Xu refcount_set(&req->ref, 1); 52709fe09eSJingbo Xu 53709fe09eSJingbo Xu return req; 54d435d532SXin Yin } 55d435d532SXin Yin 56be62c519SJingbo Xu static struct erofs_fscache_request *erofs_fscache_req_chain(struct erofs_fscache_request *primary, 57be62c519SJingbo Xu size_t len) 58d435d532SXin Yin { 59be62c519SJingbo Xu struct erofs_fscache_request *req; 60be62c519SJingbo Xu 61be62c519SJingbo Xu /* use primary request for the first submission */ 62be62c519SJingbo Xu if (!primary->submitted) { 63be62c519SJingbo Xu refcount_inc(&primary->ref); 64be62c519SJingbo Xu return primary; 65d435d532SXin Yin } 66d435d532SXin Yin 67be62c519SJingbo Xu req = erofs_fscache_req_alloc(primary->mapping, 68be62c519SJingbo Xu primary->start + primary->submitted, len); 69be62c519SJingbo Xu if (!IS_ERR(req)) { 70be62c519SJingbo Xu req->primary = primary; 71be62c519SJingbo Xu refcount_inc(&primary->ref); 72be62c519SJingbo Xu } 73be62c519SJingbo Xu return req; 74be62c519SJingbo Xu } 75be62c519SJingbo Xu 76709fe09eSJingbo Xu static void erofs_fscache_req_complete(struct erofs_fscache_request *req) 77d435d532SXin Yin { 78d435d532SXin Yin struct folio *folio; 79709fe09eSJingbo Xu bool failed = req->error; 80709fe09eSJingbo Xu pgoff_t start_page = req->start / PAGE_SIZE; 81709fe09eSJingbo Xu pgoff_t last_page = ((req->start + req->len) / PAGE_SIZE) - 1; 82d435d532SXin Yin 83709fe09eSJingbo Xu XA_STATE(xas, &req->mapping->i_pages, start_page); 84d435d532SXin Yin 85d435d532SXin Yin rcu_read_lock(); 86d435d532SXin Yin xas_for_each(&xas, folio, last_page) { 8737020bbbSJingbo Xu if (xas_retry(&xas, folio)) 8837020bbbSJingbo Xu continue; 89709fe09eSJingbo Xu if (!failed) 90d435d532SXin Yin folio_mark_uptodate(folio); 91d435d532SXin Yin folio_unlock(folio); 92d435d532SXin Yin } 93d435d532SXin Yin rcu_read_unlock(); 94d435d532SXin Yin } 95d435d532SXin Yin 96709fe09eSJingbo Xu static void erofs_fscache_req_put(struct erofs_fscache_request *req) 97d435d532SXin Yin { 98be62c519SJingbo Xu if (refcount_dec_and_test(&req->ref)) { 99be62c519SJingbo Xu if (req->cache_resources.ops) 100be62c519SJingbo Xu req->cache_resources.ops->end_operation(&req->cache_resources); 101be62c519SJingbo Xu if (!req->primary) 102709fe09eSJingbo Xu erofs_fscache_req_complete(req); 103be62c519SJingbo Xu else 104be62c519SJingbo Xu erofs_fscache_req_put(req->primary); 105be62c519SJingbo Xu kfree(req); 106be62c519SJingbo Xu } 107d435d532SXin Yin } 108d435d532SXin Yin 109709fe09eSJingbo Xu static void erofs_fscache_subreq_complete(void *priv, 110d435d532SXin Yin ssize_t transferred_or_error, bool was_async) 111d435d532SXin Yin { 112709fe09eSJingbo Xu struct erofs_fscache_request *req = priv; 113d435d532SXin Yin 114be62c519SJingbo Xu if (IS_ERR_VALUE(transferred_or_error)) { 115be62c519SJingbo Xu if (req->primary) 116be62c519SJingbo Xu req->primary->error = transferred_or_error; 117be62c519SJingbo Xu else 118709fe09eSJingbo Xu req->error = transferred_or_error; 119be62c519SJingbo Xu } 120709fe09eSJingbo Xu erofs_fscache_req_put(req); 121d435d532SXin Yin } 122d435d532SXin Yin 123ec00b5e2SJeffle Xu /* 124709fe09eSJingbo Xu * Read data from fscache (cookie, pstart, len), and fill the read data into 125709fe09eSJingbo Xu * page cache described by (req->mapping, lstart, len). @pstart describeis the 126709fe09eSJingbo Xu * start physical address in the cache file. 127ec00b5e2SJeffle Xu */ 128d435d532SXin Yin static int erofs_fscache_read_folios_async(struct fscache_cookie *cookie, 129709fe09eSJingbo Xu struct erofs_fscache_request *req, loff_t pstart, size_t len) 130ec00b5e2SJeffle Xu { 131ec00b5e2SJeffle Xu enum netfs_io_source source; 132709fe09eSJingbo Xu struct super_block *sb = req->mapping->host->i_sb; 133709fe09eSJingbo Xu struct netfs_cache_resources *cres = &req->cache_resources; 134ec00b5e2SJeffle Xu struct iov_iter iter; 135709fe09eSJingbo Xu loff_t lstart = req->start + req->submitted; 136ec00b5e2SJeffle Xu size_t done = 0; 137ec00b5e2SJeffle Xu int ret; 138ec00b5e2SJeffle Xu 139709fe09eSJingbo Xu DBG_BUGON(len > req->len - req->submitted); 140d435d532SXin Yin 141ec00b5e2SJeffle Xu ret = fscache_begin_read_operation(cres, cookie); 142ec00b5e2SJeffle Xu if (ret) 143709fe09eSJingbo Xu return ret; 144ec00b5e2SJeffle Xu 145ec00b5e2SJeffle Xu while (done < len) { 146709fe09eSJingbo Xu loff_t sstart = pstart + done; 147709fe09eSJingbo Xu size_t slen = len - done; 148709fe09eSJingbo Xu unsigned long flags = 1 << NETFS_SREQ_ONDEMAND; 149ec00b5e2SJeffle Xu 150709fe09eSJingbo Xu source = cres->ops->prepare_ondemand_read(cres, 151709fe09eSJingbo Xu sstart, &slen, LLONG_MAX, &flags, 0); 152709fe09eSJingbo Xu if (WARN_ON(slen == 0)) 153ec00b5e2SJeffle Xu source = NETFS_INVALID_READ; 154ec00b5e2SJeffle Xu if (source != NETFS_READ_FROM_CACHE) { 155709fe09eSJingbo Xu erofs_err(sb, "failed to fscache prepare_read (source %d)", source); 156709fe09eSJingbo Xu return -EIO; 157ec00b5e2SJeffle Xu } 158ec00b5e2SJeffle Xu 159709fe09eSJingbo Xu refcount_inc(&req->ref); 1604a6bff11SLinus Torvalds iov_iter_xarray(&iter, ITER_DEST, &req->mapping->i_pages, 161709fe09eSJingbo Xu lstart + done, slen); 162d435d532SXin Yin 163709fe09eSJingbo Xu ret = fscache_read(cres, sstart, &iter, NETFS_READ_HOLE_FAIL, 164709fe09eSJingbo Xu erofs_fscache_subreq_complete, req); 165d435d532SXin Yin if (ret == -EIOCBQUEUED) 166d435d532SXin Yin ret = 0; 167ec00b5e2SJeffle Xu if (ret) { 168ec00b5e2SJeffle Xu erofs_err(sb, "failed to fscache_read (ret %d)", ret); 169ec00b5e2SJeffle Xu return ret; 170ec00b5e2SJeffle Xu } 171ec00b5e2SJeffle Xu 172709fe09eSJingbo Xu done += slen; 173709fe09eSJingbo Xu } 174709fe09eSJingbo Xu DBG_BUGON(done != len); 175709fe09eSJingbo Xu return 0; 176709fe09eSJingbo Xu } 177709fe09eSJingbo Xu 178fdaf9a58SLinus Torvalds static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio) 1795375e7c8SJeffle Xu { 1805375e7c8SJeffle Xu int ret; 181bdfa9014SJingbo Xu struct erofs_fscache *ctx = folio_mapping(folio)->host->i_private; 182709fe09eSJingbo Xu struct erofs_fscache_request *req; 1835375e7c8SJeffle Xu 184709fe09eSJingbo Xu req = erofs_fscache_req_alloc(folio_mapping(folio), 185709fe09eSJingbo Xu folio_pos(folio), folio_size(folio)); 186709fe09eSJingbo Xu if (IS_ERR(req)) { 1873c265d7dSJeffle Xu folio_unlock(folio); 188709fe09eSJingbo Xu return PTR_ERR(req); 189709fe09eSJingbo Xu } 190709fe09eSJingbo Xu 191bdfa9014SJingbo Xu ret = erofs_fscache_read_folios_async(ctx->cookie, req, 192bdfa9014SJingbo Xu folio_pos(folio), folio_size(folio)); 193709fe09eSJingbo Xu if (ret) 194709fe09eSJingbo Xu req->error = ret; 195709fe09eSJingbo Xu 196709fe09eSJingbo Xu erofs_fscache_req_put(req); 1973c265d7dSJeffle Xu return ret; 1983c265d7dSJeffle Xu } 1993c265d7dSJeffle Xu 200be62c519SJingbo Xu static int erofs_fscache_data_read_slice(struct erofs_fscache_request *primary) 201bd735bdaSJeffle Xu { 202be62c519SJingbo Xu struct address_space *mapping = primary->mapping; 2031ae9470cSJingbo Xu struct inode *inode = mapping->host; 2041ae9470cSJingbo Xu struct super_block *sb = inode->i_sb; 205709fe09eSJingbo Xu struct erofs_fscache_request *req; 2061ae9470cSJingbo Xu struct erofs_map_blocks map; 2071ae9470cSJingbo Xu struct erofs_map_dev mdev; 2081ae9470cSJingbo Xu struct iov_iter iter; 209be62c519SJingbo Xu loff_t pos = primary->start + primary->submitted; 2101ae9470cSJingbo Xu size_t count; 2111ae9470cSJingbo Xu int ret; 2121ae9470cSJingbo Xu 2131ae9470cSJingbo Xu map.m_la = pos; 2148b58f9f0SJingbo Xu ret = erofs_map_blocks(inode, &map); 2151ae9470cSJingbo Xu if (ret) 2161ae9470cSJingbo Xu return ret; 2171ae9470cSJingbo Xu 2181ae9470cSJingbo Xu if (map.m_flags & EROFS_MAP_META) { 219bd735bdaSJeffle Xu struct erofs_buf buf = __EROFS_BUF_INITIALIZER; 220bd735bdaSJeffle Xu erofs_blk_t blknr; 2211ae9470cSJingbo Xu size_t offset, size; 2221ae9470cSJingbo Xu void *src; 223bd735bdaSJeffle Xu 224bd735bdaSJeffle Xu /* For tail packing layout, the offset may be non-zero. */ 2253acea5fcSJingbo Xu offset = erofs_blkoff(sb, map.m_pa); 2263acea5fcSJingbo Xu blknr = erofs_blknr(sb, map.m_pa); 2271ae9470cSJingbo Xu size = map.m_llen; 228bd735bdaSJeffle Xu 229bd735bdaSJeffle Xu src = erofs_read_metabuf(&buf, sb, blknr, EROFS_KMAP); 230bd735bdaSJeffle Xu if (IS_ERR(src)) 231bd735bdaSJeffle Xu return PTR_ERR(src); 232bd735bdaSJeffle Xu 233de4eda9dSAl Viro iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, PAGE_SIZE); 23475e43355SJingbo Xu if (copy_to_iter(src + offset, size, &iter) != size) { 23575e43355SJingbo Xu erofs_put_metabuf(&buf); 2361ae9470cSJingbo Xu return -EFAULT; 23775e43355SJingbo Xu } 2381ae9470cSJingbo Xu iov_iter_zero(PAGE_SIZE - size, &iter); 239bd735bdaSJeffle Xu erofs_put_metabuf(&buf); 240be62c519SJingbo Xu primary->submitted += PAGE_SIZE; 241be62c519SJingbo Xu return 0; 242bd735bdaSJeffle Xu } 243bd735bdaSJeffle Xu 244be62c519SJingbo Xu count = primary->len - primary->submitted; 2451442b02bSJeffle Xu if (!(map.m_flags & EROFS_MAP_MAPPED)) { 246de4eda9dSAl Viro iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, count); 2471ae9470cSJingbo Xu iov_iter_zero(count, &iter); 248be62c519SJingbo Xu primary->submitted += count; 249be62c519SJingbo Xu return 0; 250bd735bdaSJeffle Xu } 251bd735bdaSJeffle Xu 252be62c519SJingbo Xu count = min_t(size_t, map.m_llen - (pos - map.m_la), count); 253e6d9f9baSJingbo Xu DBG_BUGON(!count || count % PAGE_SIZE); 254e6d9f9baSJingbo Xu 2551442b02bSJeffle Xu mdev = (struct erofs_map_dev) { 2561442b02bSJeffle Xu .m_deviceid = map.m_deviceid, 2571442b02bSJeffle Xu .m_pa = map.m_pa, 2581442b02bSJeffle Xu }; 2591442b02bSJeffle Xu ret = erofs_map_dev(sb, &mdev); 2601442b02bSJeffle Xu if (ret) 2611442b02bSJeffle Xu return ret; 2621ae9470cSJingbo Xu 263be62c519SJingbo Xu req = erofs_fscache_req_chain(primary, count); 264709fe09eSJingbo Xu if (IS_ERR(req)) 265709fe09eSJingbo Xu return PTR_ERR(req); 2661ae9470cSJingbo Xu 267709fe09eSJingbo Xu ret = erofs_fscache_read_folios_async(mdev.m_fscache->cookie, 268709fe09eSJingbo Xu req, mdev.m_pa + (pos - map.m_la), count); 269be62c519SJingbo Xu erofs_fscache_req_put(req); 270be62c519SJingbo Xu primary->submitted += count; 271be62c519SJingbo Xu return ret; 272be62c519SJingbo Xu } 273be62c519SJingbo Xu 274be62c519SJingbo Xu static int erofs_fscache_data_read(struct erofs_fscache_request *req) 275be62c519SJingbo Xu { 276be62c519SJingbo Xu int ret; 277be62c519SJingbo Xu 278be62c519SJingbo Xu do { 279be62c519SJingbo Xu ret = erofs_fscache_data_read_slice(req); 280709fe09eSJingbo Xu if (ret) 281709fe09eSJingbo Xu req->error = ret; 282be62c519SJingbo Xu } while (!ret && req->submitted < req->len); 283709fe09eSJingbo Xu 284be62c519SJingbo Xu return ret; 2851442b02bSJeffle Xu } 2861442b02bSJeffle Xu 2871ae9470cSJingbo Xu static int erofs_fscache_read_folio(struct file *file, struct folio *folio) 288c665b394SJeffle Xu { 289be62c519SJingbo Xu struct erofs_fscache_request *req; 2901ae9470cSJingbo Xu int ret; 2911ae9470cSJingbo Xu 292be62c519SJingbo Xu req = erofs_fscache_req_alloc(folio_mapping(folio), 293be62c519SJingbo Xu folio_pos(folio), folio_size(folio)); 294be62c519SJingbo Xu if (IS_ERR(req)) { 295c665b394SJeffle Xu folio_unlock(folio); 296be62c519SJingbo Xu return PTR_ERR(req); 297c665b394SJeffle Xu } 298be62c519SJingbo Xu 299be62c519SJingbo Xu ret = erofs_fscache_data_read(req); 300be62c519SJingbo Xu erofs_fscache_req_put(req); 301be62c519SJingbo Xu return ret; 302d435d532SXin Yin } 303c665b394SJeffle Xu 304c665b394SJeffle Xu static void erofs_fscache_readahead(struct readahead_control *rac) 305c665b394SJeffle Xu { 306be62c519SJingbo Xu struct erofs_fscache_request *req; 307c665b394SJeffle Xu 308c665b394SJeffle Xu if (!readahead_count(rac)) 309c665b394SJeffle Xu return; 310c665b394SJeffle Xu 311be62c519SJingbo Xu req = erofs_fscache_req_alloc(rac->mapping, 312be62c519SJingbo Xu readahead_pos(rac), readahead_length(rac)); 313be62c519SJingbo Xu if (IS_ERR(req)) 314c665b394SJeffle Xu return; 315c665b394SJeffle Xu 316be62c519SJingbo Xu /* The request completion will drop refs on the folios. */ 317be62c519SJingbo Xu while (readahead_folio(rac)) 318be62c519SJingbo Xu ; 319be62c519SJingbo Xu 320be62c519SJingbo Xu erofs_fscache_data_read(req); 321be62c519SJingbo Xu erofs_fscache_req_put(req); 322c665b394SJeffle Xu } 323c665b394SJeffle Xu 324c6be2bd0SJeffle Xu static const struct address_space_operations erofs_fscache_meta_aops = { 325fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_meta_read_folio, 326c6be2bd0SJeffle Xu }; 327c6be2bd0SJeffle Xu 3281442b02bSJeffle Xu const struct address_space_operations erofs_fscache_access_aops = { 329fdaf9a58SLinus Torvalds .read_folio = erofs_fscache_read_folio, 330c665b394SJeffle Xu .readahead = erofs_fscache_readahead, 3311442b02bSJeffle Xu }; 3321442b02bSJeffle Xu 3338b7adf1dSJia Zhu static void erofs_fscache_domain_put(struct erofs_domain *domain) 3348b7adf1dSJia Zhu { 3358b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 3368b7adf1dSJia Zhu if (refcount_dec_and_test(&domain->ref)) { 3378b7adf1dSJia Zhu list_del(&domain->list); 338a9849560SJia Zhu if (list_empty(&erofs_domain_list)) { 339a9849560SJia Zhu kern_unmount(erofs_pseudo_mnt); 340a9849560SJia Zhu erofs_pseudo_mnt = NULL; 341a9849560SJia Zhu } 3428b7adf1dSJia Zhu fscache_relinquish_volume(domain->volume, NULL, false); 3437032809aSJingbo Xu mutex_unlock(&erofs_domain_list_lock); 3448b7adf1dSJia Zhu kfree(domain->domain_id); 3458b7adf1dSJia Zhu kfree(domain); 3468b7adf1dSJia Zhu return; 3478b7adf1dSJia Zhu } 3488b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 3498b7adf1dSJia Zhu } 3508b7adf1dSJia Zhu 3518b7adf1dSJia Zhu static int erofs_fscache_register_volume(struct super_block *sb) 3528b7adf1dSJia Zhu { 3538b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 35439bfcb81SJingbo Xu char *domain_id = sbi->domain_id; 3558b7adf1dSJia Zhu struct fscache_volume *volume; 3568b7adf1dSJia Zhu char *name; 3578b7adf1dSJia Zhu int ret = 0; 3588b7adf1dSJia Zhu 3598b7adf1dSJia Zhu name = kasprintf(GFP_KERNEL, "erofs,%s", 36039bfcb81SJingbo Xu domain_id ? domain_id : sbi->fsid); 3618b7adf1dSJia Zhu if (!name) 3628b7adf1dSJia Zhu return -ENOMEM; 3638b7adf1dSJia Zhu 3648b7adf1dSJia Zhu volume = fscache_acquire_volume(name, NULL, NULL, 0); 3658b7adf1dSJia Zhu if (IS_ERR_OR_NULL(volume)) { 3668b7adf1dSJia Zhu erofs_err(sb, "failed to register volume for %s", name); 3678b7adf1dSJia Zhu ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; 3688b7adf1dSJia Zhu volume = NULL; 3698b7adf1dSJia Zhu } 3708b7adf1dSJia Zhu 3718b7adf1dSJia Zhu sbi->volume = volume; 3728b7adf1dSJia Zhu kfree(name); 3738b7adf1dSJia Zhu return ret; 3748b7adf1dSJia Zhu } 3758b7adf1dSJia Zhu 3768b7adf1dSJia Zhu static int erofs_fscache_init_domain(struct super_block *sb) 3778b7adf1dSJia Zhu { 3788b7adf1dSJia Zhu int err; 3798b7adf1dSJia Zhu struct erofs_domain *domain; 3808b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 3818b7adf1dSJia Zhu 3828b7adf1dSJia Zhu domain = kzalloc(sizeof(struct erofs_domain), GFP_KERNEL); 3838b7adf1dSJia Zhu if (!domain) 3848b7adf1dSJia Zhu return -ENOMEM; 3858b7adf1dSJia Zhu 38639bfcb81SJingbo Xu domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL); 3878b7adf1dSJia Zhu if (!domain->domain_id) { 3888b7adf1dSJia Zhu kfree(domain); 3898b7adf1dSJia Zhu return -ENOMEM; 3908b7adf1dSJia Zhu } 3918b7adf1dSJia Zhu 3928b7adf1dSJia Zhu err = erofs_fscache_register_volume(sb); 3938b7adf1dSJia Zhu if (err) 3948b7adf1dSJia Zhu goto out; 3958b7adf1dSJia Zhu 396a9849560SJia Zhu if (!erofs_pseudo_mnt) { 397*145cd3e3SBaokun Li struct vfsmount *mnt = kern_mount(&erofs_anon_fs_type); 39851b27119SAl Viro if (IS_ERR(mnt)) { 39951b27119SAl Viro err = PTR_ERR(mnt); 400a9849560SJia Zhu goto out; 401a9849560SJia Zhu } 40251b27119SAl Viro erofs_pseudo_mnt = mnt; 403a9849560SJia Zhu } 404a9849560SJia Zhu 4058b7adf1dSJia Zhu domain->volume = sbi->volume; 4068b7adf1dSJia Zhu refcount_set(&domain->ref, 1); 4078b7adf1dSJia Zhu list_add(&domain->list, &erofs_domain_list); 4088b7adf1dSJia Zhu sbi->domain = domain; 4098b7adf1dSJia Zhu return 0; 4108b7adf1dSJia Zhu out: 4118b7adf1dSJia Zhu kfree(domain->domain_id); 4128b7adf1dSJia Zhu kfree(domain); 4138b7adf1dSJia Zhu return err; 4148b7adf1dSJia Zhu } 4158b7adf1dSJia Zhu 4168b7adf1dSJia Zhu static int erofs_fscache_register_domain(struct super_block *sb) 4178b7adf1dSJia Zhu { 4188b7adf1dSJia Zhu int err; 4198b7adf1dSJia Zhu struct erofs_domain *domain; 4208b7adf1dSJia Zhu struct erofs_sb_info *sbi = EROFS_SB(sb); 4218b7adf1dSJia Zhu 4228b7adf1dSJia Zhu mutex_lock(&erofs_domain_list_lock); 4238b7adf1dSJia Zhu list_for_each_entry(domain, &erofs_domain_list, list) { 42439bfcb81SJingbo Xu if (!strcmp(domain->domain_id, sbi->domain_id)) { 4258b7adf1dSJia Zhu sbi->domain = domain; 4268b7adf1dSJia Zhu sbi->volume = domain->volume; 4278b7adf1dSJia Zhu refcount_inc(&domain->ref); 4288b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4298b7adf1dSJia Zhu return 0; 4308b7adf1dSJia Zhu } 4318b7adf1dSJia Zhu } 4328b7adf1dSJia Zhu err = erofs_fscache_init_domain(sb); 4338b7adf1dSJia Zhu mutex_unlock(&erofs_domain_list_lock); 4348b7adf1dSJia Zhu return err; 4358b7adf1dSJia Zhu } 4368b7adf1dSJia Zhu 43761fef989SJingbo Xu static struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb, 43861fef989SJingbo Xu char *name, unsigned int flags) 439c6be2bd0SJeffle Xu { 440c6be2bd0SJeffle Xu struct fscache_volume *volume = EROFS_SB(sb)->volume; 441c6be2bd0SJeffle Xu struct erofs_fscache *ctx; 442c6be2bd0SJeffle Xu struct fscache_cookie *cookie; 44361fef989SJingbo Xu struct super_block *isb; 44461fef989SJingbo Xu struct inode *inode; 445c6be2bd0SJeffle Xu int ret; 446c6be2bd0SJeffle Xu 447c6be2bd0SJeffle Xu ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 448c6be2bd0SJeffle Xu if (!ctx) 449e1de2da0SJia Zhu return ERR_PTR(-ENOMEM); 4502dfb8c3bSJingbo Xu INIT_LIST_HEAD(&ctx->node); 4512dfb8c3bSJingbo Xu refcount_set(&ctx->ref, 1); 452c6be2bd0SJeffle Xu 453c6be2bd0SJeffle Xu cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE, 454c6be2bd0SJeffle Xu name, strlen(name), NULL, 0, 0); 455c6be2bd0SJeffle Xu if (!cookie) { 456c6be2bd0SJeffle Xu erofs_err(sb, "failed to get cookie for %s", name); 457c6be2bd0SJeffle Xu ret = -EINVAL; 458c6be2bd0SJeffle Xu goto err; 459c6be2bd0SJeffle Xu } 460c6be2bd0SJeffle Xu fscache_use_cookie(cookie, false); 461c6be2bd0SJeffle Xu 46261fef989SJingbo Xu /* 46361fef989SJingbo Xu * Allocate anonymous inode in global pseudo mount for shareable blobs, 46461fef989SJingbo Xu * so that they are accessible among erofs fs instances. 46561fef989SJingbo Xu */ 46661fef989SJingbo Xu isb = flags & EROFS_REG_COOKIE_SHARE ? erofs_pseudo_mnt->mnt_sb : sb; 46761fef989SJingbo Xu inode = new_inode(isb); 468b02c602fSJeffle Xu if (!inode) { 469b02c602fSJeffle Xu erofs_err(sb, "failed to get anon inode for %s", name); 470b02c602fSJeffle Xu ret = -ENOMEM; 471b02c602fSJeffle Xu goto err_cookie; 472b02c602fSJeffle Xu } 473b02c602fSJeffle Xu 474b02c602fSJeffle Xu inode->i_size = OFFSET_MAX; 475b02c602fSJeffle Xu inode->i_mapping->a_ops = &erofs_fscache_meta_aops; 476b02c602fSJeffle Xu mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); 4773acea5fcSJingbo Xu inode->i_blkbits = EROFS_SB(sb)->blkszbits; 478bdfa9014SJingbo Xu inode->i_private = ctx; 479b02c602fSJeffle Xu 48061fef989SJingbo Xu ctx->cookie = cookie; 481b02c602fSJeffle Xu ctx->inode = inode; 482e1de2da0SJia Zhu return ctx; 483b02c602fSJeffle Xu 484b02c602fSJeffle Xu err_cookie: 48561fef989SJingbo Xu fscache_unuse_cookie(cookie, NULL, NULL); 48661fef989SJingbo Xu fscache_relinquish_cookie(cookie, false); 487b02c602fSJeffle Xu err: 488b02c602fSJeffle Xu kfree(ctx); 489e1de2da0SJia Zhu return ERR_PTR(ret); 490b02c602fSJeffle Xu } 491b02c602fSJeffle Xu 4927d419637SJia Zhu static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx) 493b02c602fSJeffle Xu { 494b02c602fSJeffle Xu fscache_unuse_cookie(ctx->cookie, NULL, NULL); 495b02c602fSJeffle Xu fscache_relinquish_cookie(ctx->cookie, false); 4963c265d7dSJeffle Xu iput(ctx->inode); 4977d419637SJia Zhu kfree(ctx->name); 498b02c602fSJeffle Xu kfree(ctx); 499b02c602fSJeffle Xu } 500b02c602fSJeffle Xu 50161fef989SJingbo Xu static struct erofs_fscache *erofs_domain_init_cookie(struct super_block *sb, 50261fef989SJingbo Xu char *name, unsigned int flags) 5037d419637SJia Zhu { 5047d419637SJia Zhu struct erofs_fscache *ctx; 5057d419637SJia Zhu struct erofs_domain *domain = EROFS_SB(sb)->domain; 5067d419637SJia Zhu 50727f2a2dcSHou Tao ctx = erofs_fscache_acquire_cookie(sb, name, flags); 5087d419637SJia Zhu if (IS_ERR(ctx)) 5097d419637SJia Zhu return ctx; 5107d419637SJia Zhu 5117d419637SJia Zhu ctx->name = kstrdup(name, GFP_KERNEL); 5127d419637SJia Zhu if (!ctx->name) { 5137d419637SJia Zhu erofs_fscache_relinquish_cookie(ctx); 51461fef989SJingbo Xu return ERR_PTR(-ENOMEM); 5157d419637SJia Zhu } 5167d419637SJia Zhu 51761fef989SJingbo Xu refcount_inc(&domain->ref); 51861fef989SJingbo Xu ctx->domain = domain; 51961fef989SJingbo Xu list_add(&ctx->node, &erofs_domain_cookies_list); 52061fef989SJingbo Xu return ctx; 52161fef989SJingbo Xu } 52261fef989SJingbo Xu 52361fef989SJingbo Xu static struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, 52461fef989SJingbo Xu char *name, unsigned int flags) 5257d419637SJia Zhu { 5267d419637SJia Zhu struct erofs_fscache *ctx; 5277d419637SJia Zhu struct erofs_domain *domain = EROFS_SB(sb)->domain; 5287d419637SJia Zhu 52961fef989SJingbo Xu flags |= EROFS_REG_COOKIE_SHARE; 5307d419637SJia Zhu mutex_lock(&erofs_domain_cookies_lock); 5312dfb8c3bSJingbo Xu list_for_each_entry(ctx, &erofs_domain_cookies_list, node) { 5322dfb8c3bSJingbo Xu if (ctx->domain != domain || strcmp(ctx->name, name)) 5337d419637SJia Zhu continue; 53427f2a2dcSHou Tao if (!(flags & EROFS_REG_COOKIE_NEED_NOEXIST)) { 5352dfb8c3bSJingbo Xu refcount_inc(&ctx->ref); 53627f2a2dcSHou Tao } else { 53727f2a2dcSHou Tao erofs_err(sb, "%s already exists in domain %s", name, 53827f2a2dcSHou Tao domain->domain_id); 53927f2a2dcSHou Tao ctx = ERR_PTR(-EEXIST); 54027f2a2dcSHou Tao } 5417d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 5427d419637SJia Zhu return ctx; 5437d419637SJia Zhu } 54461fef989SJingbo Xu ctx = erofs_domain_init_cookie(sb, name, flags); 5457d419637SJia Zhu mutex_unlock(&erofs_domain_cookies_lock); 5467d419637SJia Zhu return ctx; 5477d419637SJia Zhu } 5487d419637SJia Zhu 5497d419637SJia Zhu struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, 55027f2a2dcSHou Tao char *name, 55127f2a2dcSHou Tao unsigned int flags) 5527d419637SJia Zhu { 55339bfcb81SJingbo Xu if (EROFS_SB(sb)->domain_id) 55427f2a2dcSHou Tao return erofs_domain_register_cookie(sb, name, flags); 55527f2a2dcSHou Tao return erofs_fscache_acquire_cookie(sb, name, flags); 5567d419637SJia Zhu } 5577d419637SJia Zhu 5587d419637SJia Zhu void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) 5597d419637SJia Zhu { 5602dfb8c3bSJingbo Xu struct erofs_domain *domain = NULL; 5617d419637SJia Zhu 5627d419637SJia Zhu if (!ctx) 5637d419637SJia Zhu return; 5642dfb8c3bSJingbo Xu if (!ctx->domain) 5652dfb8c3bSJingbo Xu return erofs_fscache_relinquish_cookie(ctx); 5667d419637SJia Zhu 5672dfb8c3bSJingbo Xu mutex_lock(&erofs_domain_cookies_lock); 5682dfb8c3bSJingbo Xu if (refcount_dec_and_test(&ctx->ref)) { 5692dfb8c3bSJingbo Xu domain = ctx->domain; 5702dfb8c3bSJingbo Xu list_del(&ctx->node); 5717d419637SJia Zhu erofs_fscache_relinquish_cookie(ctx); 5722dfb8c3bSJingbo Xu } 5732dfb8c3bSJingbo Xu mutex_unlock(&erofs_domain_cookies_lock); 5742dfb8c3bSJingbo Xu if (domain) 5757d419637SJia Zhu erofs_fscache_domain_put(domain); 5767d419637SJia Zhu } 5777d419637SJia Zhu 578c6be2bd0SJeffle Xu int erofs_fscache_register_fs(struct super_block *sb) 579c6be2bd0SJeffle Xu { 5808b7adf1dSJia Zhu int ret; 581c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 582e1de2da0SJia Zhu struct erofs_fscache *fscache; 58361fef989SJingbo Xu unsigned int flags = 0; 584c6be2bd0SJeffle Xu 58539bfcb81SJingbo Xu if (sbi->domain_id) 5868b7adf1dSJia Zhu ret = erofs_fscache_register_domain(sb); 5878b7adf1dSJia Zhu else 5888b7adf1dSJia Zhu ret = erofs_fscache_register_volume(sb); 5898b7adf1dSJia Zhu if (ret) 5908b7adf1dSJia Zhu return ret; 591c6be2bd0SJeffle Xu 59227f2a2dcSHou Tao /* 59327f2a2dcSHou Tao * When shared domain is enabled, using NEED_NOEXIST to guarantee 59427f2a2dcSHou Tao * the primary data blob (aka fsid) is unique in the shared domain. 59527f2a2dcSHou Tao * 59627f2a2dcSHou Tao * For non-shared-domain case, fscache_acquire_volume() invoked by 59727f2a2dcSHou Tao * erofs_fscache_register_volume() has already guaranteed 59827f2a2dcSHou Tao * the uniqueness of primary data blob. 59927f2a2dcSHou Tao * 60027f2a2dcSHou Tao * Acquired domain/volume will be relinquished in kill_sb() on error. 60127f2a2dcSHou Tao */ 60227f2a2dcSHou Tao if (sbi->domain_id) 60327f2a2dcSHou Tao flags |= EROFS_REG_COOKIE_NEED_NOEXIST; 60427f2a2dcSHou Tao fscache = erofs_fscache_register_cookie(sb, sbi->fsid, flags); 605e1de2da0SJia Zhu if (IS_ERR(fscache)) 606e1de2da0SJia Zhu return PTR_ERR(fscache); 607e1de2da0SJia Zhu 608e1de2da0SJia Zhu sbi->s_fscache = fscache; 609e1de2da0SJia Zhu return 0; 610c6be2bd0SJeffle Xu } 611c6be2bd0SJeffle Xu 612c6be2bd0SJeffle Xu void erofs_fscache_unregister_fs(struct super_block *sb) 613c6be2bd0SJeffle Xu { 614c6be2bd0SJeffle Xu struct erofs_sb_info *sbi = EROFS_SB(sb); 615c6be2bd0SJeffle Xu 616e1de2da0SJia Zhu erofs_fscache_unregister_cookie(sbi->s_fscache); 6178b7adf1dSJia Zhu 6188b7adf1dSJia Zhu if (sbi->domain) 6198b7adf1dSJia Zhu erofs_fscache_domain_put(sbi->domain); 6208b7adf1dSJia Zhu else 621c6be2bd0SJeffle Xu fscache_relinquish_volume(sbi->volume, NULL, false); 6228b7adf1dSJia Zhu 623e1de2da0SJia Zhu sbi->s_fscache = NULL; 624c6be2bd0SJeffle Xu sbi->volume = NULL; 6258b7adf1dSJia Zhu sbi->domain = NULL; 626c6be2bd0SJeffle Xu } 627