Commit f153fbe1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull erofs updates from Gao Xiang:
 "In this cycle, we introduce compressed inode support over fscache
  since a lot of native EROFS images are explicitly compressed so that
  EROFS over fscache can be more widely used even without Dragonfly
  Nydus [1].

  Apart from that, there are some folio conversions for compressed
  inodes available as well as a lockdep false positive fix.

  Summary:

   - Some folio conversions for compressed inodes;

   - Add compressed inode support over fscache;

   - Fix lockdep false positives of erofs_pseudo_mnt"

Link: https://nydus.dev [1]

* tag 'erofs-for-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs:
  erofs: support compressed inodes over fscache
  erofs: make iov_iter describe target buffers over fscache
  erofs: fix lockdep false positives on initializing erofs_pseudo_mnt
  erofs: refine managed cache operations to folios
  erofs: convert z_erofs_submissionqueue_endio() to folios
  erofs: convert z_erofs_fill_bio_vec() to folios
  erofs: get rid of `justfound` debugging tag
  erofs: convert z_erofs_do_read_page() to folios
  erofs: convert z_erofs_onlinepage_.* to folios
parents d453cc5a a1bafc31
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -81,13 +81,6 @@ static inline bool z_erofs_put_shortlivedpage(struct page **pagepool,
	return true;
}

#define MNGD_MAPPING(sbi)	((sbi)->managed_cache->i_mapping)
static inline bool erofs_page_is_managed(const struct erofs_sb_info *sbi,
					 struct page *page)
{
	return page->mapping == MNGD_MAPPING(sbi);
}

int z_erofs_fixup_insize(struct z_erofs_decompress_req *rq, const char *padbuf,
			 unsigned int padbufsize);
extern const struct z_erofs_decompressor erofs_decompressors[];
+0 −3
Original line number Diff line number Diff line
@@ -212,9 +212,6 @@ int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,

			if (rq->out[no] != rq->in[j])
				continue;

			DBG_BUGON(erofs_page_is_managed(EROFS_SB(sb),
							rq->in[j]));
			tmppage = erofs_allocpage(pgpl, rq->gfp);
			if (!tmppage) {
				err = -ENOMEM;
+0 −3
Original line number Diff line number Diff line
@@ -258,9 +258,6 @@ int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,

			if (rq->out[no] != rq->in[j])
				continue;

			DBG_BUGON(erofs_page_is_managed(EROFS_SB(rq->sb),
							rq->in[j]));
			tmppage = erofs_allocpage(pgpl, rq->gfp);
			if (!tmppage) {
				err = -ENOMEM;
+184 −113
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
 * Copyright (C) 2022, Alibaba Cloud
 * Copyright (C) 2022, Bytedance Inc. All rights reserved.
 */
#include <linux/pseudo_fs.h>
#include <linux/fscache.h>
#include "internal.h"

@@ -12,9 +13,27 @@ static LIST_HEAD(erofs_domain_list);
static LIST_HEAD(erofs_domain_cookies_list);
static struct vfsmount *erofs_pseudo_mnt;

struct erofs_fscache_request {
	struct erofs_fscache_request *primary;
	struct netfs_cache_resources cache_resources;
static int erofs_anon_init_fs_context(struct fs_context *fc)
{
	return init_pseudo(fc, EROFS_SUPER_MAGIC) ? 0 : -ENOMEM;
}

static struct file_system_type erofs_anon_fs_type = {
	.owner		= THIS_MODULE,
	.name           = "pseudo_erofs",
	.init_fs_context = erofs_anon_init_fs_context,
	.kill_sb        = kill_anon_super,
};

struct erofs_fscache_io {
	struct netfs_cache_resources cres;
	struct iov_iter		iter;
	netfs_io_terminated_t	end_io;
	void			*private;
	refcount_t		ref;
};

struct erofs_fscache_rq {
	struct address_space	*mapping;	/* The mapping being accessed */
	loff_t			start;		/* Start position */
	size_t			len;		/* Length of the request */
@@ -23,44 +42,17 @@ struct erofs_fscache_request {
	refcount_t		ref;
};

static struct erofs_fscache_request *erofs_fscache_req_alloc(struct address_space *mapping,
					     loff_t start, size_t len)
static bool erofs_fscache_io_put(struct erofs_fscache_io *io)
{
	struct erofs_fscache_request *req;

	req = kzalloc(sizeof(struct erofs_fscache_request), GFP_KERNEL);
	if (!req)
		return ERR_PTR(-ENOMEM);

	req->mapping = mapping;
	req->start   = start;
	req->len     = len;
	refcount_set(&req->ref, 1);

	return req;
	if (!refcount_dec_and_test(&io->ref))
		return false;
	if (io->cres.ops)
		io->cres.ops->end_operation(&io->cres);
	kfree(io);
	return true;
}

static struct erofs_fscache_request *erofs_fscache_req_chain(struct erofs_fscache_request *primary,
					     size_t len)
{
	struct erofs_fscache_request *req;

	/* use primary request for the first submission */
	if (!primary->submitted) {
		refcount_inc(&primary->ref);
		return primary;
	}

	req = erofs_fscache_req_alloc(primary->mapping,
			primary->start + primary->submitted, len);
	if (!IS_ERR(req)) {
		req->primary = primary;
		refcount_inc(&primary->ref);
	}
	return req;
}

static void erofs_fscache_req_complete(struct erofs_fscache_request *req)
static void erofs_fscache_req_complete(struct erofs_fscache_rq *req)
{
	struct folio *folio;
	bool failed = req->error;
@@ -80,120 +72,196 @@ static void erofs_fscache_req_complete(struct erofs_fscache_request *req)
	rcu_read_unlock();
}

static void erofs_fscache_req_put(struct erofs_fscache_request *req)
static void erofs_fscache_req_put(struct erofs_fscache_rq *req)
{
	if (refcount_dec_and_test(&req->ref)) {
		if (req->cache_resources.ops)
			req->cache_resources.ops->end_operation(&req->cache_resources);
		if (!req->primary)
	if (!refcount_dec_and_test(&req->ref))
		return;
	erofs_fscache_req_complete(req);
		else
			erofs_fscache_req_put(req->primary);
	kfree(req);
}

static struct erofs_fscache_rq *erofs_fscache_req_alloc(struct address_space *mapping,
						loff_t start, size_t len)
{
	struct erofs_fscache_rq *req = kzalloc(sizeof(*req), GFP_KERNEL);

	if (!req)
		return NULL;
	req->mapping = mapping;
	req->start = start;
	req->len = len;
	refcount_set(&req->ref, 1);
	return req;
}

static void erofs_fscache_req_io_put(struct erofs_fscache_io *io)
{
	struct erofs_fscache_rq *req = io->private;

	if (erofs_fscache_io_put(io))
		erofs_fscache_req_put(req);
}

static void erofs_fscache_subreq_complete(void *priv,
static void erofs_fscache_req_end_io(void *priv,
		ssize_t transferred_or_error, bool was_async)
{
	struct erofs_fscache_request *req = priv;
	struct erofs_fscache_io *io = priv;
	struct erofs_fscache_rq *req = io->private;

	if (IS_ERR_VALUE(transferred_or_error)) {
		if (req->primary)
			req->primary->error = transferred_or_error;
		else
	if (IS_ERR_VALUE(transferred_or_error))
		req->error = transferred_or_error;
	erofs_fscache_req_io_put(io);
}
	erofs_fscache_req_put(req);

static struct erofs_fscache_io *erofs_fscache_req_io_alloc(struct erofs_fscache_rq *req)
{
	struct erofs_fscache_io *io = kzalloc(sizeof(*io), GFP_KERNEL);

	if (!io)
		return NULL;
	io->end_io = erofs_fscache_req_end_io;
	io->private = req;
	refcount_inc(&req->ref);
	refcount_set(&io->ref, 1);
	return io;
}

/*
 * Read data from fscache (cookie, pstart, len), and fill the read data into
 * page cache described by (req->mapping, lstart, len). @pstart describeis the
 * start physical address in the cache file.
 * Read data from fscache described by cookie at pstart physical address
 * offset, and fill the read data into buffer described by io->iter.
 */
static int erofs_fscache_read_folios_async(struct fscache_cookie *cookie,
		struct erofs_fscache_request *req, loff_t pstart, size_t len)
static int erofs_fscache_read_io_async(struct fscache_cookie *cookie,
		loff_t pstart, struct erofs_fscache_io *io)
{
	enum netfs_io_source source;
	struct super_block *sb = req->mapping->host->i_sb;
	struct netfs_cache_resources *cres = &req->cache_resources;
	struct iov_iter iter;
	loff_t lstart = req->start + req->submitted;
	size_t done = 0;
	struct netfs_cache_resources *cres = &io->cres;
	struct iov_iter *iter = &io->iter;
	int ret;

	DBG_BUGON(len > req->len - req->submitted);

	ret = fscache_begin_read_operation(cres, cookie);
	if (ret)
		return ret;

	while (done < len) {
		loff_t sstart = pstart + done;
		size_t slen = len - done;
	while (iov_iter_count(iter)) {
		size_t orig_count = iov_iter_count(iter), len = orig_count;
		unsigned long flags = 1 << NETFS_SREQ_ONDEMAND;

		source = cres->ops->prepare_ondemand_read(cres,
				sstart, &slen, LLONG_MAX, &flags, 0);
		if (WARN_ON(slen == 0))
				pstart, &len, LLONG_MAX, &flags, 0);
		if (WARN_ON(len == 0))
			source = NETFS_INVALID_READ;
		if (source != NETFS_READ_FROM_CACHE) {
			erofs_err(sb, "failed to fscache prepare_read (source %d)", source);
			erofs_err(NULL, "prepare_read failed (source %d)", source);
			return -EIO;
		}

		refcount_inc(&req->ref);
		iov_iter_xarray(&iter, ITER_DEST, &req->mapping->i_pages,
				lstart + done, slen);

		ret = fscache_read(cres, sstart, &iter, NETFS_READ_HOLE_FAIL,
				   erofs_fscache_subreq_complete, req);
		iov_iter_truncate(iter, len);
		refcount_inc(&io->ref);
		ret = fscache_read(cres, pstart, iter, NETFS_READ_HOLE_FAIL,
				   io->end_io, io);
		if (ret == -EIOCBQUEUED)
			ret = 0;
		if (ret) {
			erofs_err(sb, "failed to fscache_read (ret %d)", ret);
			erofs_err(NULL, "fscache_read failed (ret %d)", ret);
			return ret;
		}
		if (WARN_ON(iov_iter_count(iter)))
			return -EIO;

		done += slen;
		iov_iter_reexpand(iter, orig_count - len);
		pstart += len;
	}
	DBG_BUGON(done != len);
	return 0;
}

static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio)
struct erofs_fscache_bio {
	struct erofs_fscache_io io;
	struct bio bio;		/* w/o bdev to share bio_add_page/endio() */
	struct bio_vec bvecs[BIO_MAX_VECS];
};

static void erofs_fscache_bio_endio(void *priv,
		ssize_t transferred_or_error, bool was_async)
{
	struct erofs_fscache_bio *io = priv;

	if (IS_ERR_VALUE(transferred_or_error))
		io->bio.bi_status = errno_to_blk_status(transferred_or_error);
	io->bio.bi_end_io(&io->bio);
	BUILD_BUG_ON(offsetof(struct erofs_fscache_bio, io) != 0);
	erofs_fscache_io_put(&io->io);
}

struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev)
{
	struct erofs_fscache_bio *io;

	io = kmalloc(sizeof(*io), GFP_KERNEL | __GFP_NOFAIL);
	bio_init(&io->bio, NULL, io->bvecs, BIO_MAX_VECS, REQ_OP_READ);
	io->io.private = mdev->m_fscache->cookie;
	io->io.end_io = erofs_fscache_bio_endio;
	refcount_set(&io->io.ref, 1);
	return &io->bio;
}

void erofs_fscache_submit_bio(struct bio *bio)
{
	struct erofs_fscache_bio *io = container_of(bio,
			struct erofs_fscache_bio, bio);
	int ret;

	iov_iter_bvec(&io->io.iter, ITER_DEST, io->bvecs, bio->bi_vcnt,
		      bio->bi_iter.bi_size);
	ret = erofs_fscache_read_io_async(io->io.private,
				bio->bi_iter.bi_sector << 9, &io->io);
	erofs_fscache_io_put(&io->io);
	if (!ret)
		return;
	bio->bi_status = errno_to_blk_status(ret);
	bio->bi_end_io(bio);
}

static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio)
{
	struct erofs_fscache *ctx = folio->mapping->host->i_private;
	struct erofs_fscache_request *req;
	int ret = -ENOMEM;
	struct erofs_fscache_rq *req;
	struct erofs_fscache_io *io;

	req = erofs_fscache_req_alloc(folio->mapping,
				folio_pos(folio), folio_size(folio));
	if (IS_ERR(req)) {
	if (!req) {
		folio_unlock(folio);
		return PTR_ERR(req);
		return ret;
	}

	ret = erofs_fscache_read_folios_async(ctx->cookie, req,
	io = erofs_fscache_req_io_alloc(req);
	if (!io) {
		req->error = ret;
		goto out;
	}
	iov_iter_xarray(&io->iter, ITER_DEST, &folio->mapping->i_pages,
			folio_pos(folio), folio_size(folio));

	ret = erofs_fscache_read_io_async(ctx->cookie, folio_pos(folio), io);
	if (ret)
		req->error = ret;

	erofs_fscache_req_io_put(io);
out:
	erofs_fscache_req_put(req);
	return ret;
}

static int erofs_fscache_data_read_slice(struct erofs_fscache_request *primary)
static int erofs_fscache_data_read_slice(struct erofs_fscache_rq *req)
{
	struct address_space *mapping = primary->mapping;
	struct address_space *mapping = req->mapping;
	struct inode *inode = mapping->host;
	struct super_block *sb = inode->i_sb;
	struct erofs_fscache_request *req;
	struct erofs_fscache_io *io;
	struct erofs_map_blocks map;
	struct erofs_map_dev mdev;
	struct iov_iter iter;
	loff_t pos = primary->start + primary->submitted;
	loff_t pos = req->start + req->submitted;
	size_t count;
	int ret;

@@ -204,6 +272,7 @@ static int erofs_fscache_data_read_slice(struct erofs_fscache_request *primary)

	if (map.m_flags & EROFS_MAP_META) {
		struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
		struct iov_iter iter;
		erofs_blk_t blknr;
		size_t offset, size;
		void *src;
@@ -224,15 +293,17 @@ static int erofs_fscache_data_read_slice(struct erofs_fscache_request *primary)
		}
		iov_iter_zero(PAGE_SIZE - size, &iter);
		erofs_put_metabuf(&buf);
		primary->submitted += PAGE_SIZE;
		req->submitted += PAGE_SIZE;
		return 0;
	}

	count = primary->len - primary->submitted;
	count = req->len - req->submitted;
	if (!(map.m_flags & EROFS_MAP_MAPPED)) {
		struct iov_iter iter;

		iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, count);
		iov_iter_zero(count, &iter);
		primary->submitted += count;
		req->submitted += count;
		return 0;
	}

@@ -247,18 +318,19 @@ static int erofs_fscache_data_read_slice(struct erofs_fscache_request *primary)
	if (ret)
		return ret;

	req = erofs_fscache_req_chain(primary, count);
	if (IS_ERR(req))
		return PTR_ERR(req);
	io = erofs_fscache_req_io_alloc(req);
	if (!io)
		return -ENOMEM;
	iov_iter_xarray(&io->iter, ITER_DEST, &mapping->i_pages, pos, count);
	ret = erofs_fscache_read_io_async(mdev.m_fscache->cookie,
			mdev.m_pa + (pos - map.m_la), io);
	erofs_fscache_req_io_put(io);

	ret = erofs_fscache_read_folios_async(mdev.m_fscache->cookie,
			req, mdev.m_pa + (pos - map.m_la), count);
	erofs_fscache_req_put(req);
	primary->submitted += count;
	req->submitted += count;
	return ret;
}

static int erofs_fscache_data_read(struct erofs_fscache_request *req)
static int erofs_fscache_data_read(struct erofs_fscache_rq *req)
{
	int ret;

@@ -267,20 +339,19 @@ static int erofs_fscache_data_read(struct erofs_fscache_request *req)
		if (ret)
			req->error = ret;
	} while (!ret && req->submitted < req->len);

	return ret;
}

static int erofs_fscache_read_folio(struct file *file, struct folio *folio)
{
	struct erofs_fscache_request *req;
	struct erofs_fscache_rq *req;
	int ret;

	req = erofs_fscache_req_alloc(folio->mapping,
			folio_pos(folio), folio_size(folio));
	if (IS_ERR(req)) {
	if (!req) {
		folio_unlock(folio);
		return PTR_ERR(req);
		return -ENOMEM;
	}

	ret = erofs_fscache_data_read(req);
@@ -290,14 +361,14 @@ static int erofs_fscache_read_folio(struct file *file, struct folio *folio)

static void erofs_fscache_readahead(struct readahead_control *rac)
{
	struct erofs_fscache_request *req;
	struct erofs_fscache_rq *req;

	if (!readahead_count(rac))
		return;

	req = erofs_fscache_req_alloc(rac->mapping,
			readahead_pos(rac), readahead_length(rac));
	if (IS_ERR(req))
	if (!req)
		return;

	/* The request completion will drop refs on the folios. */
@@ -381,7 +452,7 @@ static int erofs_fscache_init_domain(struct super_block *sb)
		goto out;

	if (!erofs_pseudo_mnt) {
		struct vfsmount *mnt = kern_mount(&erofs_fs_type);
		struct vfsmount *mnt = kern_mount(&erofs_anon_fs_type);
		if (IS_ERR(mnt)) {
			err = PTR_ERR(mnt);
			goto out;
+6 −8
Original line number Diff line number Diff line
@@ -259,14 +259,12 @@ static int erofs_fill_inode(struct inode *inode)

	if (erofs_inode_is_data_compressed(vi->datalayout)) {
#ifdef CONFIG_EROFS_FS_ZIP
		if (!erofs_is_fscache_mode(inode->i_sb)) {
			DO_ONCE_LITE_IF(inode->i_sb->s_blocksize != PAGE_SIZE,
		DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT,
			  erofs_info, inode->i_sb,
			  "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
		inode->i_mapping->a_ops = &z_erofs_aops;
		err = 0;
		goto out_unlock;
		}
#endif
		err = -EOPNOTSUPP;
		goto out_unlock;
Loading