xref: /openbmc/linux/fs/erofs/fscache.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
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 
erofs_anon_init_fs_context(struct fs_context * fc)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 
erofs_fscache_req_alloc(struct address_space * mapping,loff_t start,size_t len)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 
erofs_fscache_req_chain(struct erofs_fscache_request * primary,size_t len)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 
erofs_fscache_req_complete(struct erofs_fscache_request * req)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 
erofs_fscache_req_put(struct erofs_fscache_request * req)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 
erofs_fscache_subreq_complete(void * priv,ssize_t transferred_or_error,bool was_async)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  */
erofs_fscache_read_folios_async(struct fscache_cookie * cookie,struct erofs_fscache_request * req,loff_t pstart,size_t len)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 
erofs_fscache_meta_read_folio(struct file * data,struct folio * folio)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 
erofs_fscache_data_read_slice(struct erofs_fscache_request * primary)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 
erofs_fscache_data_read(struct erofs_fscache_request * req)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 
erofs_fscache_read_folio(struct file * file,struct folio * folio)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 
erofs_fscache_readahead(struct readahead_control * rac)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 
erofs_fscache_domain_put(struct erofs_domain * domain)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 
erofs_fscache_register_volume(struct super_block * sb)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 
erofs_fscache_init_domain(struct super_block * sb)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 
erofs_fscache_register_domain(struct super_block * sb)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 
erofs_fscache_acquire_cookie(struct super_block * sb,char * name,unsigned int flags)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 
erofs_fscache_relinquish_cookie(struct erofs_fscache * ctx)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 
erofs_domain_init_cookie(struct super_block * sb,char * name,unsigned int flags)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 
erofs_domain_register_cookie(struct super_block * sb,char * name,unsigned int flags)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 
erofs_fscache_register_cookie(struct super_block * sb,char * name,unsigned int flags)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 
erofs_fscache_unregister_cookie(struct erofs_fscache * ctx)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 
erofs_fscache_register_fs(struct super_block * sb)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 
erofs_fscache_unregister_fs(struct super_block * sb)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