xref: /openbmc/linux/fs/erofs/fscache.c (revision a9849560c55e9e4ab9c53d073363dd6e19ec06ef)
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);
108b7adf1dSJia Zhu static LIST_HEAD(erofs_domain_list);
11*a9849560SJia Zhu static struct vfsmount *erofs_pseudo_mnt;
128b7adf1dSJia Zhu 
13d435d532SXin Yin static struct netfs_io_request *erofs_fscache_alloc_request(struct address_space *mapping,
14d435d532SXin Yin 					     loff_t start, size_t len)
15d435d532SXin Yin {
16d435d532SXin Yin 	struct netfs_io_request *rreq;
17d435d532SXin Yin 
18d435d532SXin Yin 	rreq = kzalloc(sizeof(struct netfs_io_request), GFP_KERNEL);
19d435d532SXin Yin 	if (!rreq)
20d435d532SXin Yin 		return ERR_PTR(-ENOMEM);
21d435d532SXin Yin 
22d435d532SXin Yin 	rreq->start	= start;
23d435d532SXin Yin 	rreq->len	= len;
24d435d532SXin Yin 	rreq->mapping	= mapping;
25b5cb79dcSXin Yin 	rreq->inode	= mapping->host;
26d435d532SXin Yin 	INIT_LIST_HEAD(&rreq->subrequests);
27d435d532SXin Yin 	refcount_set(&rreq->ref, 1);
28d435d532SXin Yin 	return rreq;
29d435d532SXin Yin }
30d435d532SXin Yin 
31d435d532SXin Yin static void erofs_fscache_put_request(struct netfs_io_request *rreq)
32d435d532SXin Yin {
33d435d532SXin Yin 	if (!refcount_dec_and_test(&rreq->ref))
34d435d532SXin Yin 		return;
35d435d532SXin Yin 	if (rreq->cache_resources.ops)
36d435d532SXin Yin 		rreq->cache_resources.ops->end_operation(&rreq->cache_resources);
37d435d532SXin Yin 	kfree(rreq);
38d435d532SXin Yin }
39d435d532SXin Yin 
40d435d532SXin Yin static void erofs_fscache_put_subrequest(struct netfs_io_subrequest *subreq)
41d435d532SXin Yin {
42d435d532SXin Yin 	if (!refcount_dec_and_test(&subreq->ref))
43d435d532SXin Yin 		return;
44d435d532SXin Yin 	erofs_fscache_put_request(subreq->rreq);
45d435d532SXin Yin 	kfree(subreq);
46d435d532SXin Yin }
47d435d532SXin Yin 
48d435d532SXin Yin static void erofs_fscache_clear_subrequests(struct netfs_io_request *rreq)
49d435d532SXin Yin {
50d435d532SXin Yin 	struct netfs_io_subrequest *subreq;
51d435d532SXin Yin 
52d435d532SXin Yin 	while (!list_empty(&rreq->subrequests)) {
53d435d532SXin Yin 		subreq = list_first_entry(&rreq->subrequests,
54d435d532SXin Yin 				struct netfs_io_subrequest, rreq_link);
55d435d532SXin Yin 		list_del(&subreq->rreq_link);
56d435d532SXin Yin 		erofs_fscache_put_subrequest(subreq);
57d435d532SXin Yin 	}
58d435d532SXin Yin }
59d435d532SXin Yin 
60d435d532SXin Yin static void erofs_fscache_rreq_unlock_folios(struct netfs_io_request *rreq)
61d435d532SXin Yin {
62d435d532SXin Yin 	struct netfs_io_subrequest *subreq;
63d435d532SXin Yin 	struct folio *folio;
64d435d532SXin Yin 	unsigned int iopos = 0;
65d435d532SXin Yin 	pgoff_t start_page = rreq->start / PAGE_SIZE;
66d435d532SXin Yin 	pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1;
67d435d532SXin Yin 	bool subreq_failed = false;
68d435d532SXin Yin 
69d435d532SXin Yin 	XA_STATE(xas, &rreq->mapping->i_pages, start_page);
70d435d532SXin Yin 
71d435d532SXin Yin 	subreq = list_first_entry(&rreq->subrequests,
72d435d532SXin Yin 				  struct netfs_io_subrequest, rreq_link);
73d435d532SXin Yin 	subreq_failed = (subreq->error < 0);
74d435d532SXin Yin 
75d435d532SXin Yin 	rcu_read_lock();
76d435d532SXin Yin 	xas_for_each(&xas, folio, last_page) {
77d435d532SXin Yin 		unsigned int pgpos =
78d435d532SXin Yin 			(folio_index(folio) - start_page) * PAGE_SIZE;
79d435d532SXin Yin 		unsigned int pgend = pgpos + folio_size(folio);
80d435d532SXin Yin 		bool pg_failed = false;
81d435d532SXin Yin 
82d435d532SXin Yin 		for (;;) {
83d435d532SXin Yin 			if (!subreq) {
84d435d532SXin Yin 				pg_failed = true;
85d435d532SXin Yin 				break;
86d435d532SXin Yin 			}
87d435d532SXin Yin 
88d435d532SXin Yin 			pg_failed |= subreq_failed;
89d435d532SXin Yin 			if (pgend < iopos + subreq->len)
90d435d532SXin Yin 				break;
91d435d532SXin Yin 
92d435d532SXin Yin 			iopos += subreq->len;
93d435d532SXin Yin 			if (!list_is_last(&subreq->rreq_link,
94d435d532SXin Yin 					  &rreq->subrequests)) {
95d435d532SXin Yin 				subreq = list_next_entry(subreq, rreq_link);
96d435d532SXin Yin 				subreq_failed = (subreq->error < 0);
97d435d532SXin Yin 			} else {
98d435d532SXin Yin 				subreq = NULL;
99d435d532SXin Yin 				subreq_failed = false;
100d435d532SXin Yin 			}
101d435d532SXin Yin 			if (pgend == iopos)
102d435d532SXin Yin 				break;
103d435d532SXin Yin 		}
104d435d532SXin Yin 
105d435d532SXin Yin 		if (!pg_failed)
106d435d532SXin Yin 			folio_mark_uptodate(folio);
107d435d532SXin Yin 
108d435d532SXin Yin 		folio_unlock(folio);
109d435d532SXin Yin 	}
110d435d532SXin Yin 	rcu_read_unlock();
111d435d532SXin Yin }
112d435d532SXin Yin 
113d435d532SXin Yin static void erofs_fscache_rreq_complete(struct netfs_io_request *rreq)
114d435d532SXin Yin {
115d435d532SXin Yin 	erofs_fscache_rreq_unlock_folios(rreq);
116d435d532SXin Yin 	erofs_fscache_clear_subrequests(rreq);
117d435d532SXin Yin 	erofs_fscache_put_request(rreq);
118d435d532SXin Yin }
119d435d532SXin Yin 
120d435d532SXin Yin static void erofc_fscache_subreq_complete(void *priv,
121d435d532SXin Yin 		ssize_t transferred_or_error, bool was_async)
122d435d532SXin Yin {
123d435d532SXin Yin 	struct netfs_io_subrequest *subreq = priv;
124d435d532SXin Yin 	struct netfs_io_request *rreq = subreq->rreq;
125d435d532SXin Yin 
126d435d532SXin Yin 	if (IS_ERR_VALUE(transferred_or_error))
127d435d532SXin Yin 		subreq->error = transferred_or_error;
128d435d532SXin Yin 
129d435d532SXin Yin 	if (atomic_dec_and_test(&rreq->nr_outstanding))
130d435d532SXin Yin 		erofs_fscache_rreq_complete(rreq);
131d435d532SXin Yin 
132d435d532SXin Yin 	erofs_fscache_put_subrequest(subreq);
133d435d532SXin Yin }
134d435d532SXin Yin 
135ec00b5e2SJeffle Xu /*
136ec00b5e2SJeffle Xu  * Read data from fscache and fill the read data into page cache described by
137d435d532SXin Yin  * @rreq, which shall be both aligned with PAGE_SIZE. @pstart describes
138ec00b5e2SJeffle Xu  * the start physical address in the cache file.
139ec00b5e2SJeffle Xu  */
140d435d532SXin Yin static int erofs_fscache_read_folios_async(struct fscache_cookie *cookie,
141d435d532SXin Yin 				struct netfs_io_request *rreq, loff_t pstart)
142ec00b5e2SJeffle Xu {
143ec00b5e2SJeffle Xu 	enum netfs_io_source source;
144d435d532SXin Yin 	struct super_block *sb = rreq->mapping->host->i_sb;
145d435d532SXin Yin 	struct netfs_io_subrequest *subreq;
146d435d532SXin Yin 	struct netfs_cache_resources *cres = &rreq->cache_resources;
147ec00b5e2SJeffle Xu 	struct iov_iter iter;
148d435d532SXin Yin 	loff_t start = rreq->start;
149d435d532SXin Yin 	size_t len = rreq->len;
150ec00b5e2SJeffle Xu 	size_t done = 0;
151ec00b5e2SJeffle Xu 	int ret;
152ec00b5e2SJeffle Xu 
153d435d532SXin Yin 	atomic_set(&rreq->nr_outstanding, 1);
154d435d532SXin Yin 
155ec00b5e2SJeffle Xu 	ret = fscache_begin_read_operation(cres, cookie);
156ec00b5e2SJeffle Xu 	if (ret)
157d435d532SXin Yin 		goto out;
158ec00b5e2SJeffle Xu 
159ec00b5e2SJeffle Xu 	while (done < len) {
160d435d532SXin Yin 		subreq = kzalloc(sizeof(struct netfs_io_subrequest),
161d435d532SXin Yin 				 GFP_KERNEL);
162d435d532SXin Yin 		if (subreq) {
163d435d532SXin Yin 			INIT_LIST_HEAD(&subreq->rreq_link);
164d435d532SXin Yin 			refcount_set(&subreq->ref, 2);
165d435d532SXin Yin 			subreq->rreq = rreq;
166d435d532SXin Yin 			refcount_inc(&rreq->ref);
167d435d532SXin Yin 		} else {
168d435d532SXin Yin 			ret = -ENOMEM;
169d435d532SXin Yin 			goto out;
170d435d532SXin Yin 		}
171ec00b5e2SJeffle Xu 
172d435d532SXin Yin 		subreq->start = pstart + done;
173d435d532SXin Yin 		subreq->len	=  len - done;
174d435d532SXin Yin 		subreq->flags = 1 << NETFS_SREQ_ONDEMAND;
175d435d532SXin Yin 
176d435d532SXin Yin 		list_add_tail(&subreq->rreq_link, &rreq->subrequests);
177d435d532SXin Yin 
178d435d532SXin Yin 		source = cres->ops->prepare_read(subreq, LLONG_MAX);
179d435d532SXin Yin 		if (WARN_ON(subreq->len == 0))
180ec00b5e2SJeffle Xu 			source = NETFS_INVALID_READ;
181ec00b5e2SJeffle Xu 		if (source != NETFS_READ_FROM_CACHE) {
182ec00b5e2SJeffle Xu 			erofs_err(sb, "failed to fscache prepare_read (source %d)",
183ec00b5e2SJeffle Xu 				  source);
184ec00b5e2SJeffle Xu 			ret = -EIO;
185d435d532SXin Yin 			subreq->error = ret;
186d435d532SXin Yin 			erofs_fscache_put_subrequest(subreq);
187ec00b5e2SJeffle Xu 			goto out;
188ec00b5e2SJeffle Xu 		}
189ec00b5e2SJeffle Xu 
190d435d532SXin Yin 		atomic_inc(&rreq->nr_outstanding);
191d435d532SXin Yin 
192d435d532SXin Yin 		iov_iter_xarray(&iter, READ, &rreq->mapping->i_pages,
193d435d532SXin Yin 				start + done, subreq->len);
194d435d532SXin Yin 
195d435d532SXin Yin 		ret = fscache_read(cres, subreq->start, &iter,
196d435d532SXin Yin 				   NETFS_READ_HOLE_FAIL,
197d435d532SXin Yin 				   erofc_fscache_subreq_complete, subreq);
198d435d532SXin Yin 		if (ret == -EIOCBQUEUED)
199d435d532SXin Yin 			ret = 0;
200ec00b5e2SJeffle Xu 		if (ret) {
201ec00b5e2SJeffle Xu 			erofs_err(sb, "failed to fscache_read (ret %d)", ret);
202ec00b5e2SJeffle Xu 			goto out;
203ec00b5e2SJeffle Xu 		}
204ec00b5e2SJeffle Xu 
205d435d532SXin Yin 		done += subreq->len;
206ec00b5e2SJeffle Xu 	}
207ec00b5e2SJeffle Xu out:
208d435d532SXin Yin 	if (atomic_dec_and_test(&rreq->nr_outstanding))
209d435d532SXin Yin 		erofs_fscache_rreq_complete(rreq);
210d435d532SXin Yin 
211ec00b5e2SJeffle Xu 	return ret;
212ec00b5e2SJeffle Xu }
213ec00b5e2SJeffle Xu 
214fdaf9a58SLinus Torvalds static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio)
2155375e7c8SJeffle Xu {
2165375e7c8SJeffle Xu 	int ret;
2175375e7c8SJeffle Xu 	struct super_block *sb = folio_mapping(folio)->host->i_sb;
218d435d532SXin Yin 	struct netfs_io_request *rreq;
2195375e7c8SJeffle Xu 	struct erofs_map_dev mdev = {
2205375e7c8SJeffle Xu 		.m_deviceid = 0,
2215375e7c8SJeffle Xu 		.m_pa = folio_pos(folio),
2225375e7c8SJeffle Xu 	};
2235375e7c8SJeffle Xu 
2245375e7c8SJeffle Xu 	ret = erofs_map_dev(sb, &mdev);
2255375e7c8SJeffle Xu 	if (ret)
2265375e7c8SJeffle Xu 		goto out;
2275375e7c8SJeffle Xu 
228d435d532SXin Yin 	rreq = erofs_fscache_alloc_request(folio_mapping(folio),
229d435d532SXin Yin 				folio_pos(folio), folio_size(folio));
2305bd9628bSSun Ke 	if (IS_ERR(rreq)) {
2315bd9628bSSun Ke 		ret = PTR_ERR(rreq);
232d435d532SXin Yin 		goto out;
2335bd9628bSSun Ke 	}
234d435d532SXin Yin 
235d435d532SXin Yin 	return erofs_fscache_read_folios_async(mdev.m_fscache->cookie,
236d435d532SXin Yin 				rreq, mdev.m_pa);
2375375e7c8SJeffle Xu out:
2385375e7c8SJeffle Xu 	folio_unlock(folio);
2395375e7c8SJeffle Xu 	return ret;
2405375e7c8SJeffle Xu }
2415375e7c8SJeffle Xu 
242fdaf9a58SLinus Torvalds static int erofs_fscache_read_folio_inline(struct folio *folio,
243bd735bdaSJeffle Xu 					 struct erofs_map_blocks *map)
244bd735bdaSJeffle Xu {
245bd735bdaSJeffle Xu 	struct super_block *sb = folio_mapping(folio)->host->i_sb;
246bd735bdaSJeffle Xu 	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
247bd735bdaSJeffle Xu 	erofs_blk_t blknr;
248bd735bdaSJeffle Xu 	size_t offset, len;
249bd735bdaSJeffle Xu 	void *src, *dst;
250bd735bdaSJeffle Xu 
251bd735bdaSJeffle Xu 	/* For tail packing layout, the offset may be non-zero. */
252bd735bdaSJeffle Xu 	offset = erofs_blkoff(map->m_pa);
253bd735bdaSJeffle Xu 	blknr = erofs_blknr(map->m_pa);
254bd735bdaSJeffle Xu 	len = map->m_llen;
255bd735bdaSJeffle Xu 
256bd735bdaSJeffle Xu 	src = erofs_read_metabuf(&buf, sb, blknr, EROFS_KMAP);
257bd735bdaSJeffle Xu 	if (IS_ERR(src))
258bd735bdaSJeffle Xu 		return PTR_ERR(src);
259bd735bdaSJeffle Xu 
260bd735bdaSJeffle Xu 	dst = kmap_local_folio(folio, 0);
261bd735bdaSJeffle Xu 	memcpy(dst, src + offset, len);
262bd735bdaSJeffle Xu 	memset(dst + len, 0, PAGE_SIZE - len);
263bd735bdaSJeffle Xu 	kunmap_local(dst);
264bd735bdaSJeffle Xu 
265bd735bdaSJeffle Xu 	erofs_put_metabuf(&buf);
266bd735bdaSJeffle Xu 	return 0;
267bd735bdaSJeffle Xu }
268bd735bdaSJeffle Xu 
269fdaf9a58SLinus Torvalds static int erofs_fscache_read_folio(struct file *file, struct folio *folio)
2701442b02bSJeffle Xu {
2711442b02bSJeffle Xu 	struct inode *inode = folio_mapping(folio)->host;
2721442b02bSJeffle Xu 	struct super_block *sb = inode->i_sb;
2731442b02bSJeffle Xu 	struct erofs_map_blocks map;
2741442b02bSJeffle Xu 	struct erofs_map_dev mdev;
275d435d532SXin Yin 	struct netfs_io_request *rreq;
2761442b02bSJeffle Xu 	erofs_off_t pos;
2771442b02bSJeffle Xu 	loff_t pstart;
2781442b02bSJeffle Xu 	int ret;
2791442b02bSJeffle Xu 
2801442b02bSJeffle Xu 	DBG_BUGON(folio_size(folio) != EROFS_BLKSIZ);
2811442b02bSJeffle Xu 
2821442b02bSJeffle Xu 	pos = folio_pos(folio);
2831442b02bSJeffle Xu 	map.m_la = pos;
2841442b02bSJeffle Xu 
2851442b02bSJeffle Xu 	ret = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW);
2861442b02bSJeffle Xu 	if (ret)
2871442b02bSJeffle Xu 		goto out_unlock;
2881442b02bSJeffle Xu 
2891442b02bSJeffle Xu 	if (!(map.m_flags & EROFS_MAP_MAPPED)) {
2901442b02bSJeffle Xu 		folio_zero_range(folio, 0, folio_size(folio));
2911442b02bSJeffle Xu 		goto out_uptodate;
2921442b02bSJeffle Xu 	}
2931442b02bSJeffle Xu 
294bd735bdaSJeffle Xu 	if (map.m_flags & EROFS_MAP_META) {
295fdaf9a58SLinus Torvalds 		ret = erofs_fscache_read_folio_inline(folio, &map);
296bd735bdaSJeffle Xu 		goto out_uptodate;
297bd735bdaSJeffle Xu 	}
298bd735bdaSJeffle Xu 
2991442b02bSJeffle Xu 	mdev = (struct erofs_map_dev) {
3001442b02bSJeffle Xu 		.m_deviceid = map.m_deviceid,
3011442b02bSJeffle Xu 		.m_pa = map.m_pa,
3021442b02bSJeffle Xu 	};
3031442b02bSJeffle Xu 
3041442b02bSJeffle Xu 	ret = erofs_map_dev(sb, &mdev);
3051442b02bSJeffle Xu 	if (ret)
3061442b02bSJeffle Xu 		goto out_unlock;
3071442b02bSJeffle Xu 
308d435d532SXin Yin 
309d435d532SXin Yin 	rreq = erofs_fscache_alloc_request(folio_mapping(folio),
310d435d532SXin Yin 				folio_pos(folio), folio_size(folio));
3115bd9628bSSun Ke 	if (IS_ERR(rreq)) {
3125bd9628bSSun Ke 		ret = PTR_ERR(rreq);
313d435d532SXin Yin 		goto out_unlock;
3145bd9628bSSun Ke 	}
315d435d532SXin Yin 
3161442b02bSJeffle Xu 	pstart = mdev.m_pa + (pos - map.m_la);
317d435d532SXin Yin 	return erofs_fscache_read_folios_async(mdev.m_fscache->cookie,
318d435d532SXin Yin 				rreq, pstart);
3191442b02bSJeffle Xu 
3201442b02bSJeffle Xu out_uptodate:
3211442b02bSJeffle Xu 	if (!ret)
3221442b02bSJeffle Xu 		folio_mark_uptodate(folio);
3231442b02bSJeffle Xu out_unlock:
3241442b02bSJeffle Xu 	folio_unlock(folio);
3251442b02bSJeffle Xu 	return ret;
3261442b02bSJeffle Xu }
3271442b02bSJeffle Xu 
328d435d532SXin Yin static void erofs_fscache_advance_folios(struct readahead_control *rac,
329d435d532SXin Yin 					 size_t len, bool unlock)
330c665b394SJeffle Xu {
331c665b394SJeffle Xu 	while (len) {
332c665b394SJeffle Xu 		struct folio *folio = readahead_folio(rac);
333c665b394SJeffle Xu 		len -= folio_size(folio);
334d435d532SXin Yin 		if (unlock) {
335c665b394SJeffle Xu 			folio_mark_uptodate(folio);
336c665b394SJeffle Xu 			folio_unlock(folio);
337c665b394SJeffle Xu 		}
338c665b394SJeffle Xu 	}
339d435d532SXin Yin }
340c665b394SJeffle Xu 
341c665b394SJeffle Xu static void erofs_fscache_readahead(struct readahead_control *rac)
342c665b394SJeffle Xu {
343c665b394SJeffle Xu 	struct inode *inode = rac->mapping->host;
344c665b394SJeffle Xu 	struct super_block *sb = inode->i_sb;
345c665b394SJeffle Xu 	size_t len, count, done = 0;
346c665b394SJeffle Xu 	erofs_off_t pos;
347c665b394SJeffle Xu 	loff_t start, offset;
348c665b394SJeffle Xu 	int ret;
349c665b394SJeffle Xu 
350c665b394SJeffle Xu 	if (!readahead_count(rac))
351c665b394SJeffle Xu 		return;
352c665b394SJeffle Xu 
353c665b394SJeffle Xu 	start = readahead_pos(rac);
354c665b394SJeffle Xu 	len = readahead_length(rac);
355c665b394SJeffle Xu 
356c665b394SJeffle Xu 	do {
357c665b394SJeffle Xu 		struct erofs_map_blocks map;
358c665b394SJeffle Xu 		struct erofs_map_dev mdev;
359d435d532SXin Yin 		struct netfs_io_request *rreq;
360c665b394SJeffle Xu 
361c665b394SJeffle Xu 		pos = start + done;
362c665b394SJeffle Xu 		map.m_la = pos;
363c665b394SJeffle Xu 
364c665b394SJeffle Xu 		ret = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW);
365c665b394SJeffle Xu 		if (ret)
366c665b394SJeffle Xu 			return;
367c665b394SJeffle Xu 
368c665b394SJeffle Xu 		offset = start + done;
369c665b394SJeffle Xu 		count = min_t(size_t, map.m_llen - (pos - map.m_la),
370c665b394SJeffle Xu 			      len - done);
371c665b394SJeffle Xu 
372c665b394SJeffle Xu 		if (!(map.m_flags & EROFS_MAP_MAPPED)) {
373c665b394SJeffle Xu 			struct iov_iter iter;
374c665b394SJeffle Xu 
375c665b394SJeffle Xu 			iov_iter_xarray(&iter, READ, &rac->mapping->i_pages,
376c665b394SJeffle Xu 					offset, count);
377c665b394SJeffle Xu 			iov_iter_zero(count, &iter);
378c665b394SJeffle Xu 
379d435d532SXin Yin 			erofs_fscache_advance_folios(rac, count, true);
380c665b394SJeffle Xu 			ret = count;
381c665b394SJeffle Xu 			continue;
382c665b394SJeffle Xu 		}
383c665b394SJeffle Xu 
384c665b394SJeffle Xu 		if (map.m_flags & EROFS_MAP_META) {
385c665b394SJeffle Xu 			struct folio *folio = readahead_folio(rac);
386c665b394SJeffle Xu 
387fdaf9a58SLinus Torvalds 			ret = erofs_fscache_read_folio_inline(folio, &map);
388c665b394SJeffle Xu 			if (!ret) {
389c665b394SJeffle Xu 				folio_mark_uptodate(folio);
390c665b394SJeffle Xu 				ret = folio_size(folio);
391c665b394SJeffle Xu 			}
392c665b394SJeffle Xu 
393c665b394SJeffle Xu 			folio_unlock(folio);
394c665b394SJeffle Xu 			continue;
395c665b394SJeffle Xu 		}
396c665b394SJeffle Xu 
397c665b394SJeffle Xu 		mdev = (struct erofs_map_dev) {
398c665b394SJeffle Xu 			.m_deviceid = map.m_deviceid,
399c665b394SJeffle Xu 			.m_pa = map.m_pa,
400c665b394SJeffle Xu 		};
401c665b394SJeffle Xu 		ret = erofs_map_dev(sb, &mdev);
402c665b394SJeffle Xu 		if (ret)
403c665b394SJeffle Xu 			return;
404c665b394SJeffle Xu 
405d435d532SXin Yin 		rreq = erofs_fscache_alloc_request(rac->mapping, offset, count);
406d435d532SXin Yin 		if (IS_ERR(rreq))
407d435d532SXin Yin 			return;
408c665b394SJeffle Xu 		/*
409d435d532SXin Yin 		 * Drop the ref of folios here. Unlock them in
410d435d532SXin Yin 		 * rreq_unlock_folios() when rreq complete.
411c665b394SJeffle Xu 		 */
412d435d532SXin Yin 		erofs_fscache_advance_folios(rac, count, false);
413d435d532SXin Yin 		ret = erofs_fscache_read_folios_async(mdev.m_fscache->cookie,
414d435d532SXin Yin 					rreq, mdev.m_pa + (pos - map.m_la));
415d435d532SXin Yin 		if (!ret)
416c665b394SJeffle Xu 			ret = count;
417c665b394SJeffle Xu 	} while (ret > 0 && ((done += ret) < len));
418c665b394SJeffle Xu }
419c665b394SJeffle Xu 
4203c265d7dSJeffle Xu static const struct address_space_operations erofs_fscache_meta_aops = {
421fdaf9a58SLinus Torvalds 	.read_folio = erofs_fscache_meta_read_folio,
4223c265d7dSJeffle Xu };
4233c265d7dSJeffle Xu 
4241442b02bSJeffle Xu const struct address_space_operations erofs_fscache_access_aops = {
425fdaf9a58SLinus Torvalds 	.read_folio = erofs_fscache_read_folio,
426c665b394SJeffle Xu 	.readahead = erofs_fscache_readahead,
4271442b02bSJeffle Xu };
4281442b02bSJeffle Xu 
4298b7adf1dSJia Zhu static void erofs_fscache_domain_put(struct erofs_domain *domain)
4308b7adf1dSJia Zhu {
4318b7adf1dSJia Zhu 	if (!domain)
4328b7adf1dSJia Zhu 		return;
4338b7adf1dSJia Zhu 	mutex_lock(&erofs_domain_list_lock);
4348b7adf1dSJia Zhu 	if (refcount_dec_and_test(&domain->ref)) {
4358b7adf1dSJia Zhu 		list_del(&domain->list);
436*a9849560SJia Zhu 		if (list_empty(&erofs_domain_list)) {
437*a9849560SJia Zhu 			kern_unmount(erofs_pseudo_mnt);
438*a9849560SJia Zhu 			erofs_pseudo_mnt = NULL;
439*a9849560SJia Zhu 		}
4408b7adf1dSJia Zhu 		mutex_unlock(&erofs_domain_list_lock);
4418b7adf1dSJia Zhu 		fscache_relinquish_volume(domain->volume, NULL, false);
4428b7adf1dSJia Zhu 		kfree(domain->domain_id);
4438b7adf1dSJia Zhu 		kfree(domain);
4448b7adf1dSJia Zhu 		return;
4458b7adf1dSJia Zhu 	}
4468b7adf1dSJia Zhu 	mutex_unlock(&erofs_domain_list_lock);
4478b7adf1dSJia Zhu }
4488b7adf1dSJia Zhu 
4498b7adf1dSJia Zhu static int erofs_fscache_register_volume(struct super_block *sb)
4508b7adf1dSJia Zhu {
4518b7adf1dSJia Zhu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
4528b7adf1dSJia Zhu 	char *domain_id = sbi->opt.domain_id;
4538b7adf1dSJia Zhu 	struct fscache_volume *volume;
4548b7adf1dSJia Zhu 	char *name;
4558b7adf1dSJia Zhu 	int ret = 0;
4568b7adf1dSJia Zhu 
4578b7adf1dSJia Zhu 	name = kasprintf(GFP_KERNEL, "erofs,%s",
4588b7adf1dSJia Zhu 			 domain_id ? domain_id : sbi->opt.fsid);
4598b7adf1dSJia Zhu 	if (!name)
4608b7adf1dSJia Zhu 		return -ENOMEM;
4618b7adf1dSJia Zhu 
4628b7adf1dSJia Zhu 	volume = fscache_acquire_volume(name, NULL, NULL, 0);
4638b7adf1dSJia Zhu 	if (IS_ERR_OR_NULL(volume)) {
4648b7adf1dSJia Zhu 		erofs_err(sb, "failed to register volume for %s", name);
4658b7adf1dSJia Zhu 		ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP;
4668b7adf1dSJia Zhu 		volume = NULL;
4678b7adf1dSJia Zhu 	}
4688b7adf1dSJia Zhu 
4698b7adf1dSJia Zhu 	sbi->volume = volume;
4708b7adf1dSJia Zhu 	kfree(name);
4718b7adf1dSJia Zhu 	return ret;
4728b7adf1dSJia Zhu }
4738b7adf1dSJia Zhu 
4748b7adf1dSJia Zhu static int erofs_fscache_init_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 	domain = kzalloc(sizeof(struct erofs_domain), GFP_KERNEL);
4818b7adf1dSJia Zhu 	if (!domain)
4828b7adf1dSJia Zhu 		return -ENOMEM;
4838b7adf1dSJia Zhu 
4848b7adf1dSJia Zhu 	domain->domain_id = kstrdup(sbi->opt.domain_id, GFP_KERNEL);
4858b7adf1dSJia Zhu 	if (!domain->domain_id) {
4868b7adf1dSJia Zhu 		kfree(domain);
4878b7adf1dSJia Zhu 		return -ENOMEM;
4888b7adf1dSJia Zhu 	}
4898b7adf1dSJia Zhu 
4908b7adf1dSJia Zhu 	err = erofs_fscache_register_volume(sb);
4918b7adf1dSJia Zhu 	if (err)
4928b7adf1dSJia Zhu 		goto out;
4938b7adf1dSJia Zhu 
494*a9849560SJia Zhu 	if (!erofs_pseudo_mnt) {
495*a9849560SJia Zhu 		erofs_pseudo_mnt = kern_mount(&erofs_fs_type);
496*a9849560SJia Zhu 		if (IS_ERR(erofs_pseudo_mnt)) {
497*a9849560SJia Zhu 			err = PTR_ERR(erofs_pseudo_mnt);
498*a9849560SJia Zhu 			goto out;
499*a9849560SJia Zhu 		}
500*a9849560SJia Zhu 	}
501*a9849560SJia Zhu 
5028b7adf1dSJia Zhu 	domain->volume = sbi->volume;
5038b7adf1dSJia Zhu 	refcount_set(&domain->ref, 1);
5048b7adf1dSJia Zhu 	list_add(&domain->list, &erofs_domain_list);
5058b7adf1dSJia Zhu 	sbi->domain = domain;
5068b7adf1dSJia Zhu 	return 0;
5078b7adf1dSJia Zhu out:
5088b7adf1dSJia Zhu 	kfree(domain->domain_id);
5098b7adf1dSJia Zhu 	kfree(domain);
5108b7adf1dSJia Zhu 	return err;
5118b7adf1dSJia Zhu }
5128b7adf1dSJia Zhu 
5138b7adf1dSJia Zhu static int erofs_fscache_register_domain(struct super_block *sb)
5148b7adf1dSJia Zhu {
5158b7adf1dSJia Zhu 	int err;
5168b7adf1dSJia Zhu 	struct erofs_domain *domain;
5178b7adf1dSJia Zhu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
5188b7adf1dSJia Zhu 
5198b7adf1dSJia Zhu 	mutex_lock(&erofs_domain_list_lock);
5208b7adf1dSJia Zhu 	list_for_each_entry(domain, &erofs_domain_list, list) {
5218b7adf1dSJia Zhu 		if (!strcmp(domain->domain_id, sbi->opt.domain_id)) {
5228b7adf1dSJia Zhu 			sbi->domain = domain;
5238b7adf1dSJia Zhu 			sbi->volume = domain->volume;
5248b7adf1dSJia Zhu 			refcount_inc(&domain->ref);
5258b7adf1dSJia Zhu 			mutex_unlock(&erofs_domain_list_lock);
5268b7adf1dSJia Zhu 			return 0;
5278b7adf1dSJia Zhu 		}
5288b7adf1dSJia Zhu 	}
5298b7adf1dSJia Zhu 	err = erofs_fscache_init_domain(sb);
5308b7adf1dSJia Zhu 	mutex_unlock(&erofs_domain_list_lock);
5318b7adf1dSJia Zhu 	return err;
5328b7adf1dSJia Zhu }
5338b7adf1dSJia Zhu 
534e1de2da0SJia Zhu struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb,
5353c265d7dSJeffle Xu 						     char *name, bool need_inode)
536b02c602fSJeffle Xu {
537b02c602fSJeffle Xu 	struct fscache_volume *volume = EROFS_SB(sb)->volume;
538b02c602fSJeffle Xu 	struct erofs_fscache *ctx;
539b02c602fSJeffle Xu 	struct fscache_cookie *cookie;
5403c265d7dSJeffle Xu 	int ret;
541b02c602fSJeffle Xu 
542b02c602fSJeffle Xu 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
543b02c602fSJeffle Xu 	if (!ctx)
544e1de2da0SJia Zhu 		return ERR_PTR(-ENOMEM);
545b02c602fSJeffle Xu 
546b02c602fSJeffle Xu 	cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE,
547b02c602fSJeffle Xu 					name, strlen(name), NULL, 0, 0);
548b02c602fSJeffle Xu 	if (!cookie) {
549b02c602fSJeffle Xu 		erofs_err(sb, "failed to get cookie for %s", name);
5503c265d7dSJeffle Xu 		ret = -EINVAL;
5513c265d7dSJeffle Xu 		goto err;
552b02c602fSJeffle Xu 	}
553b02c602fSJeffle Xu 
554b02c602fSJeffle Xu 	fscache_use_cookie(cookie, false);
555b02c602fSJeffle Xu 	ctx->cookie = cookie;
556b02c602fSJeffle Xu 
5573c265d7dSJeffle Xu 	if (need_inode) {
5583c265d7dSJeffle Xu 		struct inode *const inode = new_inode(sb);
5593c265d7dSJeffle Xu 
5603c265d7dSJeffle Xu 		if (!inode) {
5613c265d7dSJeffle Xu 			erofs_err(sb, "failed to get anon inode for %s", name);
5623c265d7dSJeffle Xu 			ret = -ENOMEM;
5633c265d7dSJeffle Xu 			goto err_cookie;
5643c265d7dSJeffle Xu 		}
5653c265d7dSJeffle Xu 
5663c265d7dSJeffle Xu 		set_nlink(inode, 1);
5673c265d7dSJeffle Xu 		inode->i_size = OFFSET_MAX;
5683c265d7dSJeffle Xu 		inode->i_mapping->a_ops = &erofs_fscache_meta_aops;
5693c265d7dSJeffle Xu 		mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS);
5703c265d7dSJeffle Xu 
5713c265d7dSJeffle Xu 		ctx->inode = inode;
5723c265d7dSJeffle Xu 	}
5733c265d7dSJeffle Xu 
574e1de2da0SJia Zhu 	return ctx;
5753c265d7dSJeffle Xu 
5763c265d7dSJeffle Xu err_cookie:
5773c265d7dSJeffle Xu 	fscache_unuse_cookie(ctx->cookie, NULL, NULL);
5783c265d7dSJeffle Xu 	fscache_relinquish_cookie(ctx->cookie, false);
5793c265d7dSJeffle Xu err:
5803c265d7dSJeffle Xu 	kfree(ctx);
581e1de2da0SJia Zhu 	return ERR_PTR(ret);
582b02c602fSJeffle Xu }
583b02c602fSJeffle Xu 
584e1de2da0SJia Zhu void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx)
585b02c602fSJeffle Xu {
586b02c602fSJeffle Xu 	if (!ctx)
587b02c602fSJeffle Xu 		return;
588b02c602fSJeffle Xu 
589b02c602fSJeffle Xu 	fscache_unuse_cookie(ctx->cookie, NULL, NULL);
590b02c602fSJeffle Xu 	fscache_relinquish_cookie(ctx->cookie, false);
5913c265d7dSJeffle Xu 	iput(ctx->inode);
592b02c602fSJeffle Xu 	kfree(ctx);
593b02c602fSJeffle Xu }
594b02c602fSJeffle Xu 
595c6be2bd0SJeffle Xu int erofs_fscache_register_fs(struct super_block *sb)
596c6be2bd0SJeffle Xu {
5978b7adf1dSJia Zhu 	int ret;
598c6be2bd0SJeffle Xu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
599e1de2da0SJia Zhu 	struct erofs_fscache *fscache;
600c6be2bd0SJeffle Xu 
6018b7adf1dSJia Zhu 	if (sbi->opt.domain_id)
6028b7adf1dSJia Zhu 		ret = erofs_fscache_register_domain(sb);
6038b7adf1dSJia Zhu 	else
6048b7adf1dSJia Zhu 		ret = erofs_fscache_register_volume(sb);
6058b7adf1dSJia Zhu 	if (ret)
6068b7adf1dSJia Zhu 		return ret;
607c6be2bd0SJeffle Xu 
6088b7adf1dSJia Zhu 	/* acquired domain/volume will be relinquished in kill_sb() on error */
609e1de2da0SJia Zhu 	fscache = erofs_fscache_register_cookie(sb, sbi->opt.fsid, true);
610e1de2da0SJia Zhu 	if (IS_ERR(fscache))
611e1de2da0SJia Zhu 		return PTR_ERR(fscache);
612e1de2da0SJia Zhu 
613e1de2da0SJia Zhu 	sbi->s_fscache = fscache;
614e1de2da0SJia Zhu 	return 0;
615c6be2bd0SJeffle Xu }
616c6be2bd0SJeffle Xu 
617c6be2bd0SJeffle Xu void erofs_fscache_unregister_fs(struct super_block *sb)
618c6be2bd0SJeffle Xu {
619c6be2bd0SJeffle Xu 	struct erofs_sb_info *sbi = EROFS_SB(sb);
620c6be2bd0SJeffle Xu 
621e1de2da0SJia Zhu 	erofs_fscache_unregister_cookie(sbi->s_fscache);
6228b7adf1dSJia Zhu 
6238b7adf1dSJia Zhu 	if (sbi->domain)
6248b7adf1dSJia Zhu 		erofs_fscache_domain_put(sbi->domain);
6258b7adf1dSJia Zhu 	else
626c6be2bd0SJeffle Xu 		fscache_relinquish_volume(sbi->volume, NULL, false);
6278b7adf1dSJia Zhu 
628e1de2da0SJia Zhu 	sbi->s_fscache = NULL;
629c6be2bd0SJeffle Xu 	sbi->volume = NULL;
6308b7adf1dSJia Zhu 	sbi->domain = NULL;
631c6be2bd0SJeffle Xu }
632