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