xref: /openbmc/linux/fs/erofs/fscache.c (revision 39bfcb8138f6dc3375f23b1e62ccfc7c0d83295d)
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) {
78d435d532SXin Yin 		unsigned int pgpos =
79d435d532SXin Yin 			(folio_index(folio) - start_page) * PAGE_SIZE;
80d435d532SXin Yin 		unsigned int pgend = pgpos + folio_size(folio);
81d435d532SXin Yin 		bool pg_failed = false;
82d435d532SXin Yin 
83d435d532SXin Yin 		for (;;) {
84d435d532SXin Yin 			if (!subreq) {
85d435d532SXin Yin 				pg_failed = true;
86d435d532SXin Yin 				break;
87d435d532SXin Yin 			}
88d435d532SXin Yin 
89d435d532SXin Yin 			pg_failed |= subreq_failed;
90d435d532SXin Yin 			if (pgend < iopos + subreq->len)
91d435d532SXin Yin 				break;
92d435d532SXin Yin 
93d435d532SXin Yin 			iopos += subreq->len;
94d435d532SXin Yin 			if (!list_is_last(&subreq->rreq_link,
95d435d532SXin Yin 					  &rreq->subrequests)) {
96d435d532SXin Yin 				subreq = list_next_entry(subreq, rreq_link);
97d435d532SXin Yin 				subreq_failed = (subreq->error < 0);
98d435d532SXin Yin 			} else {
99d435d532SXin Yin 				subreq = NULL;
100d435d532SXin Yin 				subreq_failed = false;
101d435d532SXin Yin 			}
102d435d532SXin Yin 			if (pgend == iopos)
103d435d532SXin Yin 				break;
104d435d532SXin Yin 		}
105d435d532SXin Yin 
106d435d532SXin Yin 		if (!pg_failed)
107d435d532SXin Yin 			folio_mark_uptodate(folio);
108d435d532SXin Yin 
109d435d532SXin Yin 		folio_unlock(folio);
110d435d532SXin Yin 	}
111d435d532SXin Yin 	rcu_read_unlock();
112d435d532SXin Yin }
113d435d532SXin Yin 
114d435d532SXin Yin static void erofs_fscache_rreq_complete(struct netfs_io_request *rreq)
115d435d532SXin Yin {
116d435d532SXin Yin 	erofs_fscache_rreq_unlock_folios(rreq);
117d435d532SXin Yin 	erofs_fscache_clear_subrequests(rreq);
118d435d532SXin Yin 	erofs_fscache_put_request(rreq);
119d435d532SXin Yin }
120d435d532SXin Yin 
121d435d532SXin Yin static void erofc_fscache_subreq_complete(void *priv,
122d435d532SXin Yin 		ssize_t transferred_or_error, bool was_async)
123d435d532SXin Yin {
124d435d532SXin Yin 	struct netfs_io_subrequest *subreq = priv;
125d435d532SXin Yin 	struct netfs_io_request *rreq = subreq->rreq;
126d435d532SXin Yin 
127d435d532SXin Yin 	if (IS_ERR_VALUE(transferred_or_error))
128d435d532SXin Yin 		subreq->error = transferred_or_error;
129d435d532SXin Yin 
130d435d532SXin Yin 	if (atomic_dec_and_test(&rreq->nr_outstanding))
131d435d532SXin Yin 		erofs_fscache_rreq_complete(rreq);
132d435d532SXin Yin 
133d435d532SXin Yin 	erofs_fscache_put_subrequest(subreq);
134d435d532SXin Yin }
135d435d532SXin Yin 
136ec00b5e2SJeffle Xu /*
137ec00b5e2SJeffle Xu  * Read data from fscache and fill the read data into page cache described by
138d435d532SXin Yin  * @rreq, which shall be both aligned with PAGE_SIZE. @pstart describes
139ec00b5e2SJeffle Xu  * the start physical address in the cache file.
140ec00b5e2SJeffle Xu  */
141d435d532SXin Yin static int erofs_fscache_read_folios_async(struct fscache_cookie *cookie,
142d435d532SXin Yin 				struct netfs_io_request *rreq, loff_t pstart)
143ec00b5e2SJeffle Xu {
144ec00b5e2SJeffle Xu 	enum netfs_io_source source;
145d435d532SXin Yin 	struct super_block *sb = rreq->mapping->host->i_sb;
146d435d532SXin Yin 	struct netfs_io_subrequest *subreq;
147d435d532SXin Yin 	struct netfs_cache_resources *cres = &rreq->cache_resources;
148ec00b5e2SJeffle Xu 	struct iov_iter iter;
149d435d532SXin Yin 	loff_t start = rreq->start;
150d435d532SXin Yin 	size_t len = rreq->len;
151ec00b5e2SJeffle Xu 	size_t done = 0;
152ec00b5e2SJeffle Xu 	int ret;
153ec00b5e2SJeffle Xu 
154d435d532SXin Yin 	atomic_set(&rreq->nr_outstanding, 1);
155d435d532SXin Yin 
156ec00b5e2SJeffle Xu 	ret = fscache_begin_read_operation(cres, cookie);
157ec00b5e2SJeffle Xu 	if (ret)
158d435d532SXin Yin 		goto out;
159ec00b5e2SJeffle Xu 
160ec00b5e2SJeffle Xu 	while (done < len) {
161d435d532SXin Yin 		subreq = kzalloc(sizeof(struct netfs_io_subrequest),
162d435d532SXin Yin 				 GFP_KERNEL);
163d435d532SXin Yin 		if (subreq) {
164d435d532SXin Yin 			INIT_LIST_HEAD(&subreq->rreq_link);
165d435d532SXin Yin 			refcount_set(&subreq->ref, 2);
166d435d532SXin Yin 			subreq->rreq = rreq;
167d435d532SXin Yin 			refcount_inc(&rreq->ref);
168d435d532SXin Yin 		} else {
169d435d532SXin Yin 			ret = -ENOMEM;
170d435d532SXin Yin 			goto out;
171d435d532SXin Yin 		}
172ec00b5e2SJeffle Xu 
173d435d532SXin Yin 		subreq->start = pstart + done;
174d435d532SXin Yin 		subreq->len	=  len - done;
175d435d532SXin Yin 		subreq->flags = 1 << NETFS_SREQ_ONDEMAND;
176d435d532SXin Yin 
177d435d532SXin Yin 		list_add_tail(&subreq->rreq_link, &rreq->subrequests);
178d435d532SXin Yin 
179d435d532SXin Yin 		source = cres->ops->prepare_read(subreq, LLONG_MAX);
180d435d532SXin Yin 		if (WARN_ON(subreq->len == 0))
181ec00b5e2SJeffle Xu 			source = NETFS_INVALID_READ;
182ec00b5e2SJeffle Xu 		if (source != NETFS_READ_FROM_CACHE) {
183ec00b5e2SJeffle Xu 			erofs_err(sb, "failed to fscache prepare_read (source %d)",
184ec00b5e2SJeffle Xu 				  source);
185ec00b5e2SJeffle Xu 			ret = -EIO;
186d435d532SXin Yin 			subreq->error = ret;
187d435d532SXin Yin 			erofs_fscache_put_subrequest(subreq);
188ec00b5e2SJeffle Xu 			goto out;
189ec00b5e2SJeffle Xu 		}
190ec00b5e2SJeffle Xu 
191d435d532SXin Yin 		atomic_inc(&rreq->nr_outstanding);
192d435d532SXin Yin 
193d435d532SXin Yin 		iov_iter_xarray(&iter, READ, &rreq->mapping->i_pages,
194d435d532SXin Yin 				start + done, subreq->len);
195d435d532SXin Yin 
196d435d532SXin Yin 		ret = fscache_read(cres, subreq->start, &iter,
197d435d532SXin Yin 				   NETFS_READ_HOLE_FAIL,
198d435d532SXin Yin 				   erofc_fscache_subreq_complete, subreq);
199d435d532SXin Yin 		if (ret == -EIOCBQUEUED)
200d435d532SXin Yin 			ret = 0;
201ec00b5e2SJeffle Xu 		if (ret) {
202ec00b5e2SJeffle Xu 			erofs_err(sb, "failed to fscache_read (ret %d)", ret);
203ec00b5e2SJeffle Xu 			goto out;
204ec00b5e2SJeffle Xu 		}
205ec00b5e2SJeffle Xu 
206d435d532SXin Yin 		done += subreq->len;
207ec00b5e2SJeffle Xu 	}
208ec00b5e2SJeffle Xu out:
209d435d532SXin Yin 	if (atomic_dec_and_test(&rreq->nr_outstanding))
210d435d532SXin Yin 		erofs_fscache_rreq_complete(rreq);
211d435d532SXin Yin 
212ec00b5e2SJeffle Xu 	return ret;
213ec00b5e2SJeffle Xu }
214ec00b5e2SJeffle Xu 
215fdaf9a58SLinus Torvalds static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio)
2165375e7c8SJeffle Xu {
2175375e7c8SJeffle Xu 	int ret;
2185375e7c8SJeffle Xu 	struct super_block *sb = folio_mapping(folio)->host->i_sb;
219d435d532SXin Yin 	struct netfs_io_request *rreq;
2205375e7c8SJeffle Xu 	struct erofs_map_dev mdev = {
2215375e7c8SJeffle Xu 		.m_deviceid = 0,
2225375e7c8SJeffle Xu 		.m_pa = folio_pos(folio),
2235375e7c8SJeffle Xu 	};
2245375e7c8SJeffle Xu 
2255375e7c8SJeffle Xu 	ret = erofs_map_dev(sb, &mdev);
2265375e7c8SJeffle Xu 	if (ret)
2275375e7c8SJeffle Xu 		goto out;
2285375e7c8SJeffle Xu 
229d435d532SXin Yin 	rreq = erofs_fscache_alloc_request(folio_mapping(folio),
230d435d532SXin Yin 				folio_pos(folio), folio_size(folio));
2315bd9628bSSun Ke 	if (IS_ERR(rreq)) {
2325bd9628bSSun Ke 		ret = PTR_ERR(rreq);
233d435d532SXin Yin 		goto out;
2345bd9628bSSun Ke 	}
235d435d532SXin Yin 
236d435d532SXin Yin 	return erofs_fscache_read_folios_async(mdev.m_fscache->cookie,
237d435d532SXin Yin 				rreq, mdev.m_pa);
2385375e7c8SJeffle Xu out:
2395375e7c8SJeffle Xu 	folio_unlock(folio);
2405375e7c8SJeffle Xu 	return ret;
2415375e7c8SJeffle Xu }
2425375e7c8SJeffle Xu 
2431ae9470cSJingbo Xu /*
2441ae9470cSJingbo Xu  * Read into page cache in the range described by (@pos, @len).
2451ae9470cSJingbo Xu  *
2461ae9470cSJingbo Xu  * On return, the caller is responsible for page unlocking if the output @unlock
2471ae9470cSJingbo Xu  * is true, or the callee will take this responsibility through netfs_io_request
2481ae9470cSJingbo Xu  * interface.
2491ae9470cSJingbo Xu  *
2501ae9470cSJingbo Xu  * The return value is the number of bytes successfully handled, or negative
2511ae9470cSJingbo Xu  * error code on failure. The only exception is that, the length of the range
2521ae9470cSJingbo Xu  * instead of the error code is returned on failure after netfs_io_request is
2531ae9470cSJingbo Xu  * allocated, so that .readahead() could advance rac accordingly.
2541ae9470cSJingbo Xu  */
2551ae9470cSJingbo Xu static int erofs_fscache_data_read(struct address_space *mapping,
2561ae9470cSJingbo Xu 				   loff_t pos, size_t len, bool *unlock)
257bd735bdaSJeffle Xu {
2581ae9470cSJingbo Xu 	struct inode *inode = mapping->host;
2591ae9470cSJingbo Xu 	struct super_block *sb = inode->i_sb;
2601ae9470cSJingbo Xu 	struct netfs_io_request *rreq;
2611ae9470cSJingbo Xu 	struct erofs_map_blocks map;
2621ae9470cSJingbo Xu 	struct erofs_map_dev mdev;
2631ae9470cSJingbo Xu 	struct iov_iter iter;
2641ae9470cSJingbo Xu 	size_t count;
2651ae9470cSJingbo Xu 	int ret;
2661ae9470cSJingbo Xu 
2671ae9470cSJingbo Xu 	*unlock = true;
2681ae9470cSJingbo Xu 
2691ae9470cSJingbo Xu 	map.m_la = pos;
2701ae9470cSJingbo Xu 	ret = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW);
2711ae9470cSJingbo Xu 	if (ret)
2721ae9470cSJingbo Xu 		return ret;
2731ae9470cSJingbo Xu 
2741ae9470cSJingbo Xu 	if (map.m_flags & EROFS_MAP_META) {
275bd735bdaSJeffle Xu 		struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
276bd735bdaSJeffle Xu 		erofs_blk_t blknr;
2771ae9470cSJingbo Xu 		size_t offset, size;
2781ae9470cSJingbo Xu 		void *src;
279bd735bdaSJeffle Xu 
280bd735bdaSJeffle Xu 		/* For tail packing layout, the offset may be non-zero. */
2811ae9470cSJingbo Xu 		offset = erofs_blkoff(map.m_pa);
2821ae9470cSJingbo Xu 		blknr = erofs_blknr(map.m_pa);
2831ae9470cSJingbo Xu 		size = map.m_llen;
284bd735bdaSJeffle Xu 
285bd735bdaSJeffle Xu 		src = erofs_read_metabuf(&buf, sb, blknr, EROFS_KMAP);
286bd735bdaSJeffle Xu 		if (IS_ERR(src))
287bd735bdaSJeffle Xu 			return PTR_ERR(src);
288bd735bdaSJeffle Xu 
2891ae9470cSJingbo Xu 		iov_iter_xarray(&iter, READ, &mapping->i_pages, pos, PAGE_SIZE);
29075e43355SJingbo Xu 		if (copy_to_iter(src + offset, size, &iter) != size) {
29175e43355SJingbo Xu 			erofs_put_metabuf(&buf);
2921ae9470cSJingbo Xu 			return -EFAULT;
29375e43355SJingbo Xu 		}
2941ae9470cSJingbo Xu 		iov_iter_zero(PAGE_SIZE - size, &iter);
295bd735bdaSJeffle Xu 		erofs_put_metabuf(&buf);
2961ae9470cSJingbo Xu 		return PAGE_SIZE;
297bd735bdaSJeffle Xu 	}
298bd735bdaSJeffle Xu 
2991442b02bSJeffle Xu 	if (!(map.m_flags & EROFS_MAP_MAPPED)) {
300e6d9f9baSJingbo Xu 		count = len;
3011ae9470cSJingbo Xu 		iov_iter_xarray(&iter, READ, &mapping->i_pages, pos, count);
3021ae9470cSJingbo Xu 		iov_iter_zero(count, &iter);
3031ae9470cSJingbo Xu 		return count;
304bd735bdaSJeffle Xu 	}
305bd735bdaSJeffle Xu 
306e6d9f9baSJingbo Xu 	count = min_t(size_t, map.m_llen - (pos - map.m_la), len);
307e6d9f9baSJingbo Xu 	DBG_BUGON(!count || count % PAGE_SIZE);
308e6d9f9baSJingbo Xu 
3091442b02bSJeffle Xu 	mdev = (struct erofs_map_dev) {
3101442b02bSJeffle Xu 		.m_deviceid = map.m_deviceid,
3111442b02bSJeffle Xu 		.m_pa = map.m_pa,
3121442b02bSJeffle Xu 	};
3131442b02bSJeffle Xu 	ret = erofs_map_dev(sb, &mdev);
3141442b02bSJeffle Xu 	if (ret)
3151442b02bSJeffle Xu 		return ret;
3161ae9470cSJingbo Xu 
3171ae9470cSJingbo Xu 	rreq = erofs_fscache_alloc_request(mapping, pos, count);
3181ae9470cSJingbo Xu 	if (IS_ERR(rreq))
3191ae9470cSJingbo Xu 		return PTR_ERR(rreq);
3201ae9470cSJingbo Xu 
3211ae9470cSJingbo Xu 	*unlock = false;
3221ae9470cSJingbo Xu 	erofs_fscache_read_folios_async(mdev.m_fscache->cookie,
3231ae9470cSJingbo Xu 			rreq, mdev.m_pa + (pos - map.m_la));
3241ae9470cSJingbo Xu 	return count;
3251442b02bSJeffle Xu }
3261442b02bSJeffle Xu 
3271ae9470cSJingbo Xu static int erofs_fscache_read_folio(struct file *file, struct folio *folio)
328c665b394SJeffle Xu {
3291ae9470cSJingbo Xu 	bool unlock;
3301ae9470cSJingbo Xu 	int ret;
3311ae9470cSJingbo Xu 
3321ae9470cSJingbo Xu 	DBG_BUGON(folio_size(folio) != EROFS_BLKSIZ);
3331ae9470cSJingbo Xu 
3341ae9470cSJingbo Xu 	ret = erofs_fscache_data_read(folio_mapping(folio), folio_pos(folio),
3351ae9470cSJingbo Xu 				      folio_size(folio), &unlock);
336d435d532SXin Yin 	if (unlock) {
3371ae9470cSJingbo Xu 		if (ret > 0)
338c665b394SJeffle Xu 			folio_mark_uptodate(folio);
339c665b394SJeffle Xu 		folio_unlock(folio);
340c665b394SJeffle Xu 	}
3411ae9470cSJingbo Xu 	return ret < 0 ? ret : 0;
342d435d532SXin Yin }
343c665b394SJeffle Xu 
344c665b394SJeffle Xu static void erofs_fscache_readahead(struct readahead_control *rac)
345c665b394SJeffle Xu {
3461ae9470cSJingbo Xu 	struct folio *folio;
3471ae9470cSJingbo Xu 	size_t len, done = 0;
3481ae9470cSJingbo Xu 	loff_t start, pos;
3491ae9470cSJingbo Xu 	bool unlock;
3501ae9470cSJingbo Xu 	int ret, size;
351c665b394SJeffle Xu 
352c665b394SJeffle Xu 	if (!readahead_count(rac))
353c665b394SJeffle Xu 		return;
354c665b394SJeffle Xu 
355c665b394SJeffle Xu 	start = readahead_pos(rac);
356c665b394SJeffle Xu 	len = readahead_length(rac);
357c665b394SJeffle Xu 
358c665b394SJeffle Xu 	do {
359c665b394SJeffle Xu 		pos = start + done;
3601ae9470cSJingbo Xu 		ret = erofs_fscache_data_read(rac->mapping, pos,
3611ae9470cSJingbo Xu 					      len - done, &unlock);
3621ae9470cSJingbo Xu 		if (ret <= 0)
363c665b394SJeffle Xu 			return;
364c665b394SJeffle Xu 
3651ae9470cSJingbo Xu 		size = ret;
3661ae9470cSJingbo Xu 		while (size) {
3671ae9470cSJingbo Xu 			folio = readahead_folio(rac);
3681ae9470cSJingbo Xu 			size -= folio_size(folio);
3691ae9470cSJingbo Xu 			if (unlock) {
370c665b394SJeffle Xu 				folio_mark_uptodate(folio);
371c665b394SJeffle Xu 				folio_unlock(folio);
372c665b394SJeffle Xu 			}
3731ae9470cSJingbo Xu 		}
3741ae9470cSJingbo Xu 	} while ((done += ret) < len);
375c665b394SJeffle Xu }
376c665b394SJeffle Xu 
3773c265d7dSJeffle Xu static const struct address_space_operations erofs_fscache_meta_aops = {
378fdaf9a58SLinus Torvalds 	.read_folio = erofs_fscache_meta_read_folio,
3793c265d7dSJeffle Xu };
3803c265d7dSJeffle Xu 
3811442b02bSJeffle Xu const struct address_space_operations erofs_fscache_access_aops = {
382fdaf9a58SLinus Torvalds 	.read_folio = erofs_fscache_read_folio,
383c665b394SJeffle Xu 	.readahead = erofs_fscache_readahead,
3841442b02bSJeffle Xu };
3851442b02bSJeffle Xu 
3868b7adf1dSJia Zhu static void erofs_fscache_domain_put(struct erofs_domain *domain)
3878b7adf1dSJia Zhu {
3888b7adf1dSJia Zhu 	if (!domain)
3898b7adf1dSJia Zhu 		return;
3908b7adf1dSJia Zhu 	mutex_lock(&erofs_domain_list_lock);
3918b7adf1dSJia Zhu 	if (refcount_dec_and_test(&domain->ref)) {
3928b7adf1dSJia Zhu 		list_del(&domain->list);
393a9849560SJia Zhu 		if (list_empty(&erofs_domain_list)) {
394a9849560SJia Zhu 			kern_unmount(erofs_pseudo_mnt);
395a9849560SJia Zhu 			erofs_pseudo_mnt = NULL;
396a9849560SJia Zhu 		}
3978b7adf1dSJia Zhu 		mutex_unlock(&erofs_domain_list_lock);
3988b7adf1dSJia Zhu 		fscache_relinquish_volume(domain->volume, NULL, false);
3998b7adf1dSJia Zhu 		kfree(domain->domain_id);
4008b7adf1dSJia Zhu 		kfree(domain);
4018b7adf1dSJia Zhu 		return;
4028b7adf1dSJia Zhu 	}
4038b7adf1dSJia Zhu 	mutex_unlock(&erofs_domain_list_lock);
4048b7adf1dSJia Zhu }
4058b7adf1dSJia Zhu 
4068b7adf1dSJia Zhu static int erofs_fscache_register_volume(struct super_block *sb)
4078b7adf1dSJia Zhu {
4088b7adf1dSJia Zhu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
409*39bfcb81SJingbo Xu 	char *domain_id = sbi->domain_id;
4108b7adf1dSJia Zhu 	struct fscache_volume *volume;
4118b7adf1dSJia Zhu 	char *name;
4128b7adf1dSJia Zhu 	int ret = 0;
4138b7adf1dSJia Zhu 
4148b7adf1dSJia Zhu 	name = kasprintf(GFP_KERNEL, "erofs,%s",
415*39bfcb81SJingbo Xu 			 domain_id ? domain_id : sbi->fsid);
4168b7adf1dSJia Zhu 	if (!name)
4178b7adf1dSJia Zhu 		return -ENOMEM;
4188b7adf1dSJia Zhu 
4198b7adf1dSJia Zhu 	volume = fscache_acquire_volume(name, NULL, NULL, 0);
4208b7adf1dSJia Zhu 	if (IS_ERR_OR_NULL(volume)) {
4218b7adf1dSJia Zhu 		erofs_err(sb, "failed to register volume for %s", name);
4228b7adf1dSJia Zhu 		ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP;
4238b7adf1dSJia Zhu 		volume = NULL;
4248b7adf1dSJia Zhu 	}
4258b7adf1dSJia Zhu 
4268b7adf1dSJia Zhu 	sbi->volume = volume;
4278b7adf1dSJia Zhu 	kfree(name);
4288b7adf1dSJia Zhu 	return ret;
4298b7adf1dSJia Zhu }
4308b7adf1dSJia Zhu 
4318b7adf1dSJia Zhu static int erofs_fscache_init_domain(struct super_block *sb)
4328b7adf1dSJia Zhu {
4338b7adf1dSJia Zhu 	int err;
4348b7adf1dSJia Zhu 	struct erofs_domain *domain;
4358b7adf1dSJia Zhu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
4368b7adf1dSJia Zhu 
4378b7adf1dSJia Zhu 	domain = kzalloc(sizeof(struct erofs_domain), GFP_KERNEL);
4388b7adf1dSJia Zhu 	if (!domain)
4398b7adf1dSJia Zhu 		return -ENOMEM;
4408b7adf1dSJia Zhu 
441*39bfcb81SJingbo Xu 	domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL);
4428b7adf1dSJia Zhu 	if (!domain->domain_id) {
4438b7adf1dSJia Zhu 		kfree(domain);
4448b7adf1dSJia Zhu 		return -ENOMEM;
4458b7adf1dSJia Zhu 	}
4468b7adf1dSJia Zhu 
4478b7adf1dSJia Zhu 	err = erofs_fscache_register_volume(sb);
4488b7adf1dSJia Zhu 	if (err)
4498b7adf1dSJia Zhu 		goto out;
4508b7adf1dSJia Zhu 
451a9849560SJia Zhu 	if (!erofs_pseudo_mnt) {
452a9849560SJia Zhu 		erofs_pseudo_mnt = kern_mount(&erofs_fs_type);
453a9849560SJia Zhu 		if (IS_ERR(erofs_pseudo_mnt)) {
454a9849560SJia Zhu 			err = PTR_ERR(erofs_pseudo_mnt);
455a9849560SJia Zhu 			goto out;
456a9849560SJia Zhu 		}
457a9849560SJia Zhu 	}
458a9849560SJia Zhu 
4598b7adf1dSJia Zhu 	domain->volume = sbi->volume;
4608b7adf1dSJia Zhu 	refcount_set(&domain->ref, 1);
4618b7adf1dSJia Zhu 	list_add(&domain->list, &erofs_domain_list);
4628b7adf1dSJia Zhu 	sbi->domain = domain;
4638b7adf1dSJia Zhu 	return 0;
4648b7adf1dSJia Zhu out:
4658b7adf1dSJia Zhu 	kfree(domain->domain_id);
4668b7adf1dSJia Zhu 	kfree(domain);
4678b7adf1dSJia Zhu 	return err;
4688b7adf1dSJia Zhu }
4698b7adf1dSJia Zhu 
4708b7adf1dSJia Zhu static int erofs_fscache_register_domain(struct super_block *sb)
4718b7adf1dSJia Zhu {
4728b7adf1dSJia Zhu 	int err;
4738b7adf1dSJia Zhu 	struct erofs_domain *domain;
4748b7adf1dSJia Zhu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
4758b7adf1dSJia Zhu 
4768b7adf1dSJia Zhu 	mutex_lock(&erofs_domain_list_lock);
4778b7adf1dSJia Zhu 	list_for_each_entry(domain, &erofs_domain_list, list) {
478*39bfcb81SJingbo Xu 		if (!strcmp(domain->domain_id, sbi->domain_id)) {
4798b7adf1dSJia Zhu 			sbi->domain = domain;
4808b7adf1dSJia Zhu 			sbi->volume = domain->volume;
4818b7adf1dSJia Zhu 			refcount_inc(&domain->ref);
4828b7adf1dSJia Zhu 			mutex_unlock(&erofs_domain_list_lock);
4838b7adf1dSJia Zhu 			return 0;
4848b7adf1dSJia Zhu 		}
4858b7adf1dSJia Zhu 	}
4868b7adf1dSJia Zhu 	err = erofs_fscache_init_domain(sb);
4878b7adf1dSJia Zhu 	mutex_unlock(&erofs_domain_list_lock);
4888b7adf1dSJia Zhu 	return err;
4898b7adf1dSJia Zhu }
4908b7adf1dSJia Zhu 
4917d419637SJia Zhu static
4927d419637SJia Zhu struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb,
4933c265d7dSJeffle Xu 						    char *name, bool need_inode)
494b02c602fSJeffle Xu {
495b02c602fSJeffle Xu 	struct fscache_volume *volume = EROFS_SB(sb)->volume;
496b02c602fSJeffle Xu 	struct erofs_fscache *ctx;
497b02c602fSJeffle Xu 	struct fscache_cookie *cookie;
4983c265d7dSJeffle Xu 	int ret;
499b02c602fSJeffle Xu 
500b02c602fSJeffle Xu 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
501b02c602fSJeffle Xu 	if (!ctx)
502e1de2da0SJia Zhu 		return ERR_PTR(-ENOMEM);
503b02c602fSJeffle Xu 
504b02c602fSJeffle Xu 	cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE,
505b02c602fSJeffle Xu 					name, strlen(name), NULL, 0, 0);
506b02c602fSJeffle Xu 	if (!cookie) {
507b02c602fSJeffle Xu 		erofs_err(sb, "failed to get cookie for %s", name);
5083c265d7dSJeffle Xu 		ret = -EINVAL;
5093c265d7dSJeffle Xu 		goto err;
510b02c602fSJeffle Xu 	}
511b02c602fSJeffle Xu 
512b02c602fSJeffle Xu 	fscache_use_cookie(cookie, false);
513b02c602fSJeffle Xu 	ctx->cookie = cookie;
514b02c602fSJeffle Xu 
5153c265d7dSJeffle Xu 	if (need_inode) {
5163c265d7dSJeffle Xu 		struct inode *const inode = new_inode(sb);
5173c265d7dSJeffle Xu 
5183c265d7dSJeffle Xu 		if (!inode) {
5193c265d7dSJeffle Xu 			erofs_err(sb, "failed to get anon inode for %s", name);
5203c265d7dSJeffle Xu 			ret = -ENOMEM;
5213c265d7dSJeffle Xu 			goto err_cookie;
5223c265d7dSJeffle Xu 		}
5233c265d7dSJeffle Xu 
5243c265d7dSJeffle Xu 		set_nlink(inode, 1);
5253c265d7dSJeffle Xu 		inode->i_size = OFFSET_MAX;
5263c265d7dSJeffle Xu 		inode->i_mapping->a_ops = &erofs_fscache_meta_aops;
5273c265d7dSJeffle Xu 		mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS);
5283c265d7dSJeffle Xu 
5293c265d7dSJeffle Xu 		ctx->inode = inode;
5303c265d7dSJeffle Xu 	}
5313c265d7dSJeffle Xu 
532e1de2da0SJia Zhu 	return ctx;
5333c265d7dSJeffle Xu 
5343c265d7dSJeffle Xu err_cookie:
5353c265d7dSJeffle Xu 	fscache_unuse_cookie(ctx->cookie, NULL, NULL);
5363c265d7dSJeffle Xu 	fscache_relinquish_cookie(ctx->cookie, false);
5373c265d7dSJeffle Xu err:
5383c265d7dSJeffle Xu 	kfree(ctx);
539e1de2da0SJia Zhu 	return ERR_PTR(ret);
540b02c602fSJeffle Xu }
541b02c602fSJeffle Xu 
5427d419637SJia Zhu static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx)
543b02c602fSJeffle Xu {
544b02c602fSJeffle Xu 	fscache_unuse_cookie(ctx->cookie, NULL, NULL);
545b02c602fSJeffle Xu 	fscache_relinquish_cookie(ctx->cookie, false);
5463c265d7dSJeffle Xu 	iput(ctx->inode);
5477d419637SJia Zhu 	kfree(ctx->name);
548b02c602fSJeffle Xu 	kfree(ctx);
549b02c602fSJeffle Xu }
550b02c602fSJeffle Xu 
5517d419637SJia Zhu static
5527d419637SJia Zhu struct erofs_fscache *erofs_fscache_domain_init_cookie(struct super_block *sb,
5537d419637SJia Zhu 		char *name, bool need_inode)
5547d419637SJia Zhu {
5557d419637SJia Zhu 	int err;
5567d419637SJia Zhu 	struct inode *inode;
5577d419637SJia Zhu 	struct erofs_fscache *ctx;
5587d419637SJia Zhu 	struct erofs_domain *domain = EROFS_SB(sb)->domain;
5597d419637SJia Zhu 
5607d419637SJia Zhu 	ctx = erofs_fscache_acquire_cookie(sb, name, need_inode);
5617d419637SJia Zhu 	if (IS_ERR(ctx))
5627d419637SJia Zhu 		return ctx;
5637d419637SJia Zhu 
5647d419637SJia Zhu 	ctx->name = kstrdup(name, GFP_KERNEL);
5657d419637SJia Zhu 	if (!ctx->name) {
5667d419637SJia Zhu 		err = -ENOMEM;
5677d419637SJia Zhu 		goto out;
5687d419637SJia Zhu 	}
5697d419637SJia Zhu 
5707d419637SJia Zhu 	inode = new_inode(erofs_pseudo_mnt->mnt_sb);
5717d419637SJia Zhu 	if (!inode) {
5727d419637SJia Zhu 		err = -ENOMEM;
5737d419637SJia Zhu 		goto out;
5747d419637SJia Zhu 	}
5757d419637SJia Zhu 
5767d419637SJia Zhu 	ctx->domain = domain;
5777d419637SJia Zhu 	ctx->anon_inode = inode;
5787d419637SJia Zhu 	inode->i_private = ctx;
5797d419637SJia Zhu 	refcount_inc(&domain->ref);
5807d419637SJia Zhu 	return ctx;
5817d419637SJia Zhu out:
5827d419637SJia Zhu 	erofs_fscache_relinquish_cookie(ctx);
5837d419637SJia Zhu 	return ERR_PTR(err);
5847d419637SJia Zhu }
5857d419637SJia Zhu 
5867d419637SJia Zhu static
5877d419637SJia Zhu struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb,
5887d419637SJia Zhu 						   char *name, bool need_inode)
5897d419637SJia Zhu {
5907d419637SJia Zhu 	struct inode *inode;
5917d419637SJia Zhu 	struct erofs_fscache *ctx;
5927d419637SJia Zhu 	struct erofs_domain *domain = EROFS_SB(sb)->domain;
5937d419637SJia Zhu 	struct super_block *psb = erofs_pseudo_mnt->mnt_sb;
5947d419637SJia Zhu 
5957d419637SJia Zhu 	mutex_lock(&erofs_domain_cookies_lock);
596ce4b8156SDawei Li 	spin_lock(&psb->s_inode_list_lock);
5977d419637SJia Zhu 	list_for_each_entry(inode, &psb->s_inodes, i_sb_list) {
5987d419637SJia Zhu 		ctx = inode->i_private;
5997d419637SJia Zhu 		if (!ctx || ctx->domain != domain || strcmp(ctx->name, name))
6007d419637SJia Zhu 			continue;
6017d419637SJia Zhu 		igrab(inode);
602ce4b8156SDawei Li 		spin_unlock(&psb->s_inode_list_lock);
6037d419637SJia Zhu 		mutex_unlock(&erofs_domain_cookies_lock);
6047d419637SJia Zhu 		return ctx;
6057d419637SJia Zhu 	}
606ce4b8156SDawei Li 	spin_unlock(&psb->s_inode_list_lock);
6077d419637SJia Zhu 	ctx = erofs_fscache_domain_init_cookie(sb, name, need_inode);
6087d419637SJia Zhu 	mutex_unlock(&erofs_domain_cookies_lock);
6097d419637SJia Zhu 	return ctx;
6107d419637SJia Zhu }
6117d419637SJia Zhu 
6127d419637SJia Zhu struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb,
6137d419637SJia Zhu 						    char *name, bool need_inode)
6147d419637SJia Zhu {
615*39bfcb81SJingbo Xu 	if (EROFS_SB(sb)->domain_id)
6167d419637SJia Zhu 		return erofs_domain_register_cookie(sb, name, need_inode);
6177d419637SJia Zhu 	return erofs_fscache_acquire_cookie(sb, name, need_inode);
6187d419637SJia Zhu }
6197d419637SJia Zhu 
6207d419637SJia Zhu void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx)
6217d419637SJia Zhu {
6227d419637SJia Zhu 	bool drop;
6237d419637SJia Zhu 	struct erofs_domain *domain;
6247d419637SJia Zhu 
6257d419637SJia Zhu 	if (!ctx)
6267d419637SJia Zhu 		return;
6277d419637SJia Zhu 	domain = ctx->domain;
6287d419637SJia Zhu 	if (domain) {
6297d419637SJia Zhu 		mutex_lock(&erofs_domain_cookies_lock);
6307d419637SJia Zhu 		drop = atomic_read(&ctx->anon_inode->i_count) == 1;
6317d419637SJia Zhu 		iput(ctx->anon_inode);
6327d419637SJia Zhu 		mutex_unlock(&erofs_domain_cookies_lock);
6337d419637SJia Zhu 		if (!drop)
6347d419637SJia Zhu 			return;
6357d419637SJia Zhu 	}
6367d419637SJia Zhu 
6377d419637SJia Zhu 	erofs_fscache_relinquish_cookie(ctx);
6387d419637SJia Zhu 	erofs_fscache_domain_put(domain);
6397d419637SJia Zhu }
6407d419637SJia Zhu 
641c6be2bd0SJeffle Xu int erofs_fscache_register_fs(struct super_block *sb)
642c6be2bd0SJeffle Xu {
6438b7adf1dSJia Zhu 	int ret;
644c6be2bd0SJeffle Xu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
645e1de2da0SJia Zhu 	struct erofs_fscache *fscache;
646c6be2bd0SJeffle Xu 
647*39bfcb81SJingbo Xu 	if (sbi->domain_id)
6488b7adf1dSJia Zhu 		ret = erofs_fscache_register_domain(sb);
6498b7adf1dSJia Zhu 	else
6508b7adf1dSJia Zhu 		ret = erofs_fscache_register_volume(sb);
6518b7adf1dSJia Zhu 	if (ret)
6528b7adf1dSJia Zhu 		return ret;
653c6be2bd0SJeffle Xu 
6548b7adf1dSJia Zhu 	/* acquired domain/volume will be relinquished in kill_sb() on error */
655*39bfcb81SJingbo Xu 	fscache = erofs_fscache_register_cookie(sb, sbi->fsid, true);
656e1de2da0SJia Zhu 	if (IS_ERR(fscache))
657e1de2da0SJia Zhu 		return PTR_ERR(fscache);
658e1de2da0SJia Zhu 
659e1de2da0SJia Zhu 	sbi->s_fscache = fscache;
660e1de2da0SJia Zhu 	return 0;
661c6be2bd0SJeffle Xu }
662c6be2bd0SJeffle Xu 
663c6be2bd0SJeffle Xu void erofs_fscache_unregister_fs(struct super_block *sb)
664c6be2bd0SJeffle Xu {
665c6be2bd0SJeffle Xu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
666c6be2bd0SJeffle Xu 
667e1de2da0SJia Zhu 	erofs_fscache_unregister_cookie(sbi->s_fscache);
6688b7adf1dSJia Zhu 
6698b7adf1dSJia Zhu 	if (sbi->domain)
6708b7adf1dSJia Zhu 		erofs_fscache_domain_put(sbi->domain);
6718b7adf1dSJia Zhu 	else
672c6be2bd0SJeffle Xu 		fscache_relinquish_volume(sbi->volume, NULL, false);
6738b7adf1dSJia Zhu 
674e1de2da0SJia Zhu 	sbi->s_fscache = NULL;
675c6be2bd0SJeffle Xu 	sbi->volume = NULL;
6768b7adf1dSJia Zhu 	sbi->domain = NULL;
677c6be2bd0SJeffle Xu }
678