162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017-2018 HUAWEI, Inc.
462306a36Sopenharmony_ci *             https://www.huawei.com/
562306a36Sopenharmony_ci * Copyright (C) 2021, Alibaba Cloud
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include "internal.h"
862306a36Sopenharmony_ci#include <linux/prefetch.h>
962306a36Sopenharmony_ci#include <linux/sched/mm.h>
1062306a36Sopenharmony_ci#include <linux/dax.h>
1162306a36Sopenharmony_ci#include <trace/events/erofs.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_civoid erofs_unmap_metabuf(struct erofs_buf *buf)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	if (buf->kmap_type == EROFS_KMAP)
1662306a36Sopenharmony_ci		kunmap_local(buf->base);
1762306a36Sopenharmony_ci	buf->base = NULL;
1862306a36Sopenharmony_ci	buf->kmap_type = EROFS_NO_KMAP;
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_civoid erofs_put_metabuf(struct erofs_buf *buf)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	if (!buf->page)
2462306a36Sopenharmony_ci		return;
2562306a36Sopenharmony_ci	erofs_unmap_metabuf(buf);
2662306a36Sopenharmony_ci	put_page(buf->page);
2762306a36Sopenharmony_ci	buf->page = NULL;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * Derive the block size from inode->i_blkbits to make compatible with
3262306a36Sopenharmony_ci * anonymous inode in fscache mode.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_civoid *erofs_bread(struct erofs_buf *buf, erofs_blk_t blkaddr,
3562306a36Sopenharmony_ci		  enum erofs_kmap_type type)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct inode *inode = buf->inode;
3862306a36Sopenharmony_ci	erofs_off_t offset = (erofs_off_t)blkaddr << inode->i_blkbits;
3962306a36Sopenharmony_ci	pgoff_t index = offset >> PAGE_SHIFT;
4062306a36Sopenharmony_ci	struct page *page = buf->page;
4162306a36Sopenharmony_ci	struct folio *folio;
4262306a36Sopenharmony_ci	unsigned int nofs_flag;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (!page || page->index != index) {
4562306a36Sopenharmony_ci		erofs_put_metabuf(buf);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		nofs_flag = memalloc_nofs_save();
4862306a36Sopenharmony_ci		folio = read_cache_folio(inode->i_mapping, index, NULL, NULL);
4962306a36Sopenharmony_ci		memalloc_nofs_restore(nofs_flag);
5062306a36Sopenharmony_ci		if (IS_ERR(folio))
5162306a36Sopenharmony_ci			return folio;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		/* should already be PageUptodate, no need to lock page */
5462306a36Sopenharmony_ci		page = folio_file_page(folio, index);
5562306a36Sopenharmony_ci		buf->page = page;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	if (buf->kmap_type == EROFS_NO_KMAP) {
5862306a36Sopenharmony_ci		if (type == EROFS_KMAP)
5962306a36Sopenharmony_ci			buf->base = kmap_local_page(page);
6062306a36Sopenharmony_ci		buf->kmap_type = type;
6162306a36Sopenharmony_ci	} else if (buf->kmap_type != type) {
6262306a36Sopenharmony_ci		DBG_BUGON(1);
6362306a36Sopenharmony_ci		return ERR_PTR(-EFAULT);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	if (type == EROFS_NO_KMAP)
6662306a36Sopenharmony_ci		return NULL;
6762306a36Sopenharmony_ci	return buf->base + (offset & ~PAGE_MASK);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_civoid erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	if (erofs_is_fscache_mode(sb))
7362306a36Sopenharmony_ci		buf->inode = EROFS_SB(sb)->s_fscache->inode;
7462306a36Sopenharmony_ci	else
7562306a36Sopenharmony_ci		buf->inode = sb->s_bdev->bd_inode;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid *erofs_read_metabuf(struct erofs_buf *buf, struct super_block *sb,
7962306a36Sopenharmony_ci			 erofs_blk_t blkaddr, enum erofs_kmap_type type)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	erofs_init_metabuf(buf, sb);
8262306a36Sopenharmony_ci	return erofs_bread(buf, blkaddr, type);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int erofs_map_blocks_flatmode(struct inode *inode,
8662306a36Sopenharmony_ci				     struct erofs_map_blocks *map)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	erofs_blk_t nblocks, lastblk;
8962306a36Sopenharmony_ci	u64 offset = map->m_la;
9062306a36Sopenharmony_ci	struct erofs_inode *vi = EROFS_I(inode);
9162306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
9262306a36Sopenharmony_ci	bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	nblocks = erofs_iblks(inode);
9562306a36Sopenharmony_ci	lastblk = nblocks - tailendpacking;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* there is no hole in flatmode */
9862306a36Sopenharmony_ci	map->m_flags = EROFS_MAP_MAPPED;
9962306a36Sopenharmony_ci	if (offset < erofs_pos(sb, lastblk)) {
10062306a36Sopenharmony_ci		map->m_pa = erofs_pos(sb, vi->raw_blkaddr) + map->m_la;
10162306a36Sopenharmony_ci		map->m_plen = erofs_pos(sb, lastblk) - offset;
10262306a36Sopenharmony_ci	} else if (tailendpacking) {
10362306a36Sopenharmony_ci		map->m_pa = erofs_iloc(inode) + vi->inode_isize +
10462306a36Sopenharmony_ci			vi->xattr_isize + erofs_blkoff(sb, offset);
10562306a36Sopenharmony_ci		map->m_plen = inode->i_size - offset;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		/* inline data should be located in the same meta block */
10862306a36Sopenharmony_ci		if (erofs_blkoff(sb, map->m_pa) + map->m_plen > sb->s_blocksize) {
10962306a36Sopenharmony_ci			erofs_err(sb, "inline data cross block boundary @ nid %llu",
11062306a36Sopenharmony_ci				  vi->nid);
11162306a36Sopenharmony_ci			DBG_BUGON(1);
11262306a36Sopenharmony_ci			return -EFSCORRUPTED;
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci		map->m_flags |= EROFS_MAP_META;
11562306a36Sopenharmony_ci	} else {
11662306a36Sopenharmony_ci		erofs_err(sb, "internal error @ nid: %llu (size %llu), m_la 0x%llx",
11762306a36Sopenharmony_ci			  vi->nid, inode->i_size, map->m_la);
11862306a36Sopenharmony_ci		DBG_BUGON(1);
11962306a36Sopenharmony_ci		return -EIO;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciint erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
12762306a36Sopenharmony_ci	struct erofs_inode *vi = EROFS_I(inode);
12862306a36Sopenharmony_ci	struct erofs_inode_chunk_index *idx;
12962306a36Sopenharmony_ci	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
13062306a36Sopenharmony_ci	u64 chunknr;
13162306a36Sopenharmony_ci	unsigned int unit;
13262306a36Sopenharmony_ci	erofs_off_t pos;
13362306a36Sopenharmony_ci	void *kaddr;
13462306a36Sopenharmony_ci	int err = 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	trace_erofs_map_blocks_enter(inode, map, 0);
13762306a36Sopenharmony_ci	map->m_deviceid = 0;
13862306a36Sopenharmony_ci	if (map->m_la >= inode->i_size) {
13962306a36Sopenharmony_ci		/* leave out-of-bound access unmapped */
14062306a36Sopenharmony_ci		map->m_flags = 0;
14162306a36Sopenharmony_ci		map->m_plen = 0;
14262306a36Sopenharmony_ci		goto out;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (vi->datalayout != EROFS_INODE_CHUNK_BASED) {
14662306a36Sopenharmony_ci		err = erofs_map_blocks_flatmode(inode, map);
14762306a36Sopenharmony_ci		goto out;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
15162306a36Sopenharmony_ci		unit = sizeof(*idx);			/* chunk index */
15262306a36Sopenharmony_ci	else
15362306a36Sopenharmony_ci		unit = EROFS_BLOCK_MAP_ENTRY_SIZE;	/* block map */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	chunknr = map->m_la >> vi->chunkbits;
15662306a36Sopenharmony_ci	pos = ALIGN(erofs_iloc(inode) + vi->inode_isize +
15762306a36Sopenharmony_ci		    vi->xattr_isize, unit) + unit * chunknr;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	kaddr = erofs_read_metabuf(&buf, sb, erofs_blknr(sb, pos), EROFS_KMAP);
16062306a36Sopenharmony_ci	if (IS_ERR(kaddr)) {
16162306a36Sopenharmony_ci		err = PTR_ERR(kaddr);
16262306a36Sopenharmony_ci		goto out;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci	map->m_la = chunknr << vi->chunkbits;
16562306a36Sopenharmony_ci	map->m_plen = min_t(erofs_off_t, 1UL << vi->chunkbits,
16662306a36Sopenharmony_ci			round_up(inode->i_size - map->m_la, sb->s_blocksize));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* handle block map */
16962306a36Sopenharmony_ci	if (!(vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) {
17062306a36Sopenharmony_ci		__le32 *blkaddr = kaddr + erofs_blkoff(sb, pos);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) {
17362306a36Sopenharmony_ci			map->m_flags = 0;
17462306a36Sopenharmony_ci		} else {
17562306a36Sopenharmony_ci			map->m_pa = erofs_pos(sb, le32_to_cpu(*blkaddr));
17662306a36Sopenharmony_ci			map->m_flags = EROFS_MAP_MAPPED;
17762306a36Sopenharmony_ci		}
17862306a36Sopenharmony_ci		goto out_unlock;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci	/* parse chunk indexes */
18162306a36Sopenharmony_ci	idx = kaddr + erofs_blkoff(sb, pos);
18262306a36Sopenharmony_ci	switch (le32_to_cpu(idx->blkaddr)) {
18362306a36Sopenharmony_ci	case EROFS_NULL_ADDR:
18462306a36Sopenharmony_ci		map->m_flags = 0;
18562306a36Sopenharmony_ci		break;
18662306a36Sopenharmony_ci	default:
18762306a36Sopenharmony_ci		map->m_deviceid = le16_to_cpu(idx->device_id) &
18862306a36Sopenharmony_ci			EROFS_SB(sb)->device_id_mask;
18962306a36Sopenharmony_ci		map->m_pa = erofs_pos(sb, le32_to_cpu(idx->blkaddr));
19062306a36Sopenharmony_ci		map->m_flags = EROFS_MAP_MAPPED;
19162306a36Sopenharmony_ci		break;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ciout_unlock:
19462306a36Sopenharmony_ci	erofs_put_metabuf(&buf);
19562306a36Sopenharmony_ciout:
19662306a36Sopenharmony_ci	if (!err)
19762306a36Sopenharmony_ci		map->m_llen = map->m_plen;
19862306a36Sopenharmony_ci	trace_erofs_map_blocks_exit(inode, map, 0, err);
19962306a36Sopenharmony_ci	return err;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciint erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct erofs_dev_context *devs = EROFS_SB(sb)->devs;
20562306a36Sopenharmony_ci	struct erofs_device_info *dif;
20662306a36Sopenharmony_ci	int id;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	map->m_bdev = sb->s_bdev;
20962306a36Sopenharmony_ci	map->m_daxdev = EROFS_SB(sb)->dax_dev;
21062306a36Sopenharmony_ci	map->m_dax_part_off = EROFS_SB(sb)->dax_part_off;
21162306a36Sopenharmony_ci	map->m_fscache = EROFS_SB(sb)->s_fscache;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (map->m_deviceid) {
21462306a36Sopenharmony_ci		down_read(&devs->rwsem);
21562306a36Sopenharmony_ci		dif = idr_find(&devs->tree, map->m_deviceid - 1);
21662306a36Sopenharmony_ci		if (!dif) {
21762306a36Sopenharmony_ci			up_read(&devs->rwsem);
21862306a36Sopenharmony_ci			return -ENODEV;
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci		if (devs->flatdev) {
22162306a36Sopenharmony_ci			map->m_pa += erofs_pos(sb, dif->mapped_blkaddr);
22262306a36Sopenharmony_ci			up_read(&devs->rwsem);
22362306a36Sopenharmony_ci			return 0;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci		map->m_bdev = dif->bdev_handle->bdev;
22662306a36Sopenharmony_ci		map->m_daxdev = dif->dax_dev;
22762306a36Sopenharmony_ci		map->m_dax_part_off = dif->dax_part_off;
22862306a36Sopenharmony_ci		map->m_fscache = dif->fscache;
22962306a36Sopenharmony_ci		up_read(&devs->rwsem);
23062306a36Sopenharmony_ci	} else if (devs->extra_devices && !devs->flatdev) {
23162306a36Sopenharmony_ci		down_read(&devs->rwsem);
23262306a36Sopenharmony_ci		idr_for_each_entry(&devs->tree, dif, id) {
23362306a36Sopenharmony_ci			erofs_off_t startoff, length;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci			if (!dif->mapped_blkaddr)
23662306a36Sopenharmony_ci				continue;
23762306a36Sopenharmony_ci			startoff = erofs_pos(sb, dif->mapped_blkaddr);
23862306a36Sopenharmony_ci			length = erofs_pos(sb, dif->blocks);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci			if (map->m_pa >= startoff &&
24162306a36Sopenharmony_ci			    map->m_pa < startoff + length) {
24262306a36Sopenharmony_ci				map->m_pa -= startoff;
24362306a36Sopenharmony_ci				map->m_bdev = dif->bdev_handle->bdev;
24462306a36Sopenharmony_ci				map->m_daxdev = dif->dax_dev;
24562306a36Sopenharmony_ci				map->m_dax_part_off = dif->dax_part_off;
24662306a36Sopenharmony_ci				map->m_fscache = dif->fscache;
24762306a36Sopenharmony_ci				break;
24862306a36Sopenharmony_ci			}
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci		up_read(&devs->rwsem);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	return 0;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
25662306a36Sopenharmony_ci		unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	int ret;
25962306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
26062306a36Sopenharmony_ci	struct erofs_map_blocks map;
26162306a36Sopenharmony_ci	struct erofs_map_dev mdev;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	map.m_la = offset;
26462306a36Sopenharmony_ci	map.m_llen = length;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ret = erofs_map_blocks(inode, &map);
26762306a36Sopenharmony_ci	if (ret < 0)
26862306a36Sopenharmony_ci		return ret;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	mdev = (struct erofs_map_dev) {
27162306a36Sopenharmony_ci		.m_deviceid = map.m_deviceid,
27262306a36Sopenharmony_ci		.m_pa = map.m_pa,
27362306a36Sopenharmony_ci	};
27462306a36Sopenharmony_ci	ret = erofs_map_dev(sb, &mdev);
27562306a36Sopenharmony_ci	if (ret)
27662306a36Sopenharmony_ci		return ret;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	iomap->offset = map.m_la;
27962306a36Sopenharmony_ci	if (flags & IOMAP_DAX)
28062306a36Sopenharmony_ci		iomap->dax_dev = mdev.m_daxdev;
28162306a36Sopenharmony_ci	else
28262306a36Sopenharmony_ci		iomap->bdev = mdev.m_bdev;
28362306a36Sopenharmony_ci	iomap->length = map.m_llen;
28462306a36Sopenharmony_ci	iomap->flags = 0;
28562306a36Sopenharmony_ci	iomap->private = NULL;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (!(map.m_flags & EROFS_MAP_MAPPED)) {
28862306a36Sopenharmony_ci		iomap->type = IOMAP_HOLE;
28962306a36Sopenharmony_ci		iomap->addr = IOMAP_NULL_ADDR;
29062306a36Sopenharmony_ci		if (!iomap->length)
29162306a36Sopenharmony_ci			iomap->length = length;
29262306a36Sopenharmony_ci		return 0;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (map.m_flags & EROFS_MAP_META) {
29662306a36Sopenharmony_ci		void *ptr;
29762306a36Sopenharmony_ci		struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		iomap->type = IOMAP_INLINE;
30062306a36Sopenharmony_ci		ptr = erofs_read_metabuf(&buf, sb,
30162306a36Sopenharmony_ci				erofs_blknr(sb, mdev.m_pa), EROFS_KMAP);
30262306a36Sopenharmony_ci		if (IS_ERR(ptr))
30362306a36Sopenharmony_ci			return PTR_ERR(ptr);
30462306a36Sopenharmony_ci		iomap->inline_data = ptr + erofs_blkoff(sb, mdev.m_pa);
30562306a36Sopenharmony_ci		iomap->private = buf.base;
30662306a36Sopenharmony_ci	} else {
30762306a36Sopenharmony_ci		iomap->type = IOMAP_MAPPED;
30862306a36Sopenharmony_ci		iomap->addr = mdev.m_pa;
30962306a36Sopenharmony_ci		if (flags & IOMAP_DAX)
31062306a36Sopenharmony_ci			iomap->addr += mdev.m_dax_part_off;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int erofs_iomap_end(struct inode *inode, loff_t pos, loff_t length,
31662306a36Sopenharmony_ci		ssize_t written, unsigned int flags, struct iomap *iomap)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	void *ptr = iomap->private;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (ptr) {
32162306a36Sopenharmony_ci		struct erofs_buf buf = {
32262306a36Sopenharmony_ci			.page = kmap_to_page(ptr),
32362306a36Sopenharmony_ci			.base = ptr,
32462306a36Sopenharmony_ci			.kmap_type = EROFS_KMAP,
32562306a36Sopenharmony_ci		};
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		DBG_BUGON(iomap->type != IOMAP_INLINE);
32862306a36Sopenharmony_ci		erofs_put_metabuf(&buf);
32962306a36Sopenharmony_ci	} else {
33062306a36Sopenharmony_ci		DBG_BUGON(iomap->type == IOMAP_INLINE);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	return written;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic const struct iomap_ops erofs_iomap_ops = {
33662306a36Sopenharmony_ci	.iomap_begin = erofs_iomap_begin,
33762306a36Sopenharmony_ci	.iomap_end = erofs_iomap_end,
33862306a36Sopenharmony_ci};
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ciint erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
34162306a36Sopenharmony_ci		 u64 start, u64 len)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	if (erofs_inode_is_data_compressed(EROFS_I(inode)->datalayout)) {
34462306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP
34562306a36Sopenharmony_ci		return iomap_fiemap(inode, fieinfo, start, len,
34662306a36Sopenharmony_ci				    &z_erofs_iomap_report_ops);
34762306a36Sopenharmony_ci#else
34862306a36Sopenharmony_ci		return -EOPNOTSUPP;
34962306a36Sopenharmony_ci#endif
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci	return iomap_fiemap(inode, fieinfo, start, len, &erofs_iomap_ops);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/*
35562306a36Sopenharmony_ci * since we dont have write or truncate flows, so no inode
35662306a36Sopenharmony_ci * locking needs to be held at the moment.
35762306a36Sopenharmony_ci */
35862306a36Sopenharmony_cistatic int erofs_read_folio(struct file *file, struct folio *folio)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	return iomap_read_folio(folio, &erofs_iomap_ops);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void erofs_readahead(struct readahead_control *rac)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	return iomap_readahead(rac, &erofs_iomap_ops);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic sector_t erofs_bmap(struct address_space *mapping, sector_t block)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	return iomap_bmap(mapping, block, &erofs_iomap_ops);
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct inode *inode = file_inode(iocb->ki_filp);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* no need taking (shared) inode lock since it's a ro filesystem */
37862306a36Sopenharmony_ci	if (!iov_iter_count(to))
37962306a36Sopenharmony_ci		return 0;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci#ifdef CONFIG_FS_DAX
38262306a36Sopenharmony_ci	if (IS_DAX(inode))
38362306a36Sopenharmony_ci		return dax_iomap_rw(iocb, to, &erofs_iomap_ops);
38462306a36Sopenharmony_ci#endif
38562306a36Sopenharmony_ci	if (iocb->ki_flags & IOCB_DIRECT) {
38662306a36Sopenharmony_ci		struct block_device *bdev = inode->i_sb->s_bdev;
38762306a36Sopenharmony_ci		unsigned int blksize_mask;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		if (bdev)
39062306a36Sopenharmony_ci			blksize_mask = bdev_logical_block_size(bdev) - 1;
39162306a36Sopenharmony_ci		else
39262306a36Sopenharmony_ci			blksize_mask = i_blocksize(inode) - 1;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		if ((iocb->ki_pos | iov_iter_count(to) |
39562306a36Sopenharmony_ci		     iov_iter_alignment(to)) & blksize_mask)
39662306a36Sopenharmony_ci			return -EINVAL;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		return iomap_dio_rw(iocb, to, &erofs_iomap_ops,
39962306a36Sopenharmony_ci				    NULL, 0, NULL, 0);
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	return filemap_read(iocb, to, 0);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/* for uncompressed (aligned) files and raw access for other files */
40562306a36Sopenharmony_ciconst struct address_space_operations erofs_raw_access_aops = {
40662306a36Sopenharmony_ci	.read_folio = erofs_read_folio,
40762306a36Sopenharmony_ci	.readahead = erofs_readahead,
40862306a36Sopenharmony_ci	.bmap = erofs_bmap,
40962306a36Sopenharmony_ci	.direct_IO = noop_direct_IO,
41062306a36Sopenharmony_ci	.release_folio = iomap_release_folio,
41162306a36Sopenharmony_ci	.invalidate_folio = iomap_invalidate_folio,
41262306a36Sopenharmony_ci};
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci#ifdef CONFIG_FS_DAX
41562306a36Sopenharmony_cistatic vm_fault_t erofs_dax_huge_fault(struct vm_fault *vmf,
41662306a36Sopenharmony_ci		unsigned int order)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	return dax_iomap_fault(vmf, order, NULL, NULL, &erofs_iomap_ops);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic vm_fault_t erofs_dax_fault(struct vm_fault *vmf)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	return erofs_dax_huge_fault(vmf, 0);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic const struct vm_operations_struct erofs_dax_vm_ops = {
42762306a36Sopenharmony_ci	.fault		= erofs_dax_fault,
42862306a36Sopenharmony_ci	.huge_fault	= erofs_dax_huge_fault,
42962306a36Sopenharmony_ci};
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int erofs_file_mmap(struct file *file, struct vm_area_struct *vma)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	if (!IS_DAX(file_inode(file)))
43462306a36Sopenharmony_ci		return generic_file_readonly_mmap(file, vma);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
43762306a36Sopenharmony_ci		return -EINVAL;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	vma->vm_ops = &erofs_dax_vm_ops;
44062306a36Sopenharmony_ci	vm_flags_set(vma, VM_HUGEPAGE);
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci#else
44462306a36Sopenharmony_ci#define erofs_file_mmap	generic_file_readonly_mmap
44562306a36Sopenharmony_ci#endif
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ciconst struct file_operations erofs_file_fops = {
44862306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
44962306a36Sopenharmony_ci	.read_iter	= erofs_file_read_iter,
45062306a36Sopenharmony_ci	.mmap		= erofs_file_mmap,
45162306a36Sopenharmony_ci	.get_unmapped_area = thp_get_unmapped_area,
45262306a36Sopenharmony_ci	.splice_read	= filemap_splice_read,
45362306a36Sopenharmony_ci};
454