diff options
Diffstat (limited to 'fs/erofs/data.c')
| -rw-r--r-- | fs/erofs/data.c | 149 | 
1 files changed, 97 insertions, 52 deletions
| diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 0e35ef3f9f3d..fa7ddb7ad980 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -9,37 +9,71 @@  #include <linux/dax.h>  #include <trace/events/erofs.h> -struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr) +void erofs_unmap_metabuf(struct erofs_buf *buf) +{ +	if (buf->kmap_type == EROFS_KMAP) +		kunmap(buf->page); +	else if (buf->kmap_type == EROFS_KMAP_ATOMIC) +		kunmap_atomic(buf->base); +	buf->base = NULL; +	buf->kmap_type = EROFS_NO_KMAP; +} + +void erofs_put_metabuf(struct erofs_buf *buf) +{ +	if (!buf->page) +		return; +	erofs_unmap_metabuf(buf); +	put_page(buf->page); +	buf->page = NULL; +} + +void *erofs_read_metabuf(struct erofs_buf *buf, struct super_block *sb, +			erofs_blk_t blkaddr, enum erofs_kmap_type type)  {  	struct address_space *const mapping = sb->s_bdev->bd_inode->i_mapping; -	struct page *page; - -	page = read_cache_page_gfp(mapping, blkaddr, -				   mapping_gfp_constraint(mapping, ~__GFP_FS)); -	/* should already be PageUptodate */ -	if (!IS_ERR(page)) -		lock_page(page); -	return page; +	erofs_off_t offset = blknr_to_addr(blkaddr); +	pgoff_t index = offset >> PAGE_SHIFT; +	struct page *page = buf->page; + +	if (!page || page->index != index) { +		erofs_put_metabuf(buf); +		page = read_cache_page_gfp(mapping, index, +				mapping_gfp_constraint(mapping, ~__GFP_FS)); +		if (IS_ERR(page)) +			return page; +		/* should already be PageUptodate, no need to lock page */ +		buf->page = page; +	} +	if (buf->kmap_type == EROFS_NO_KMAP) { +		if (type == EROFS_KMAP) +			buf->base = kmap(page); +		else if (type == EROFS_KMAP_ATOMIC) +			buf->base = kmap_atomic(page); +		buf->kmap_type = type; +	} else if (buf->kmap_type != type) { +		DBG_BUGON(1); +		return ERR_PTR(-EFAULT); +	} +	if (type == EROFS_NO_KMAP) +		return NULL; +	return buf->base + (offset & ~PAGE_MASK);  }  static int erofs_map_blocks_flatmode(struct inode *inode,  				     struct erofs_map_blocks *map,  				     int flags)  { -	int err = 0;  	erofs_blk_t nblocks, lastblk;  	u64 offset = map->m_la;  	struct erofs_inode *vi = EROFS_I(inode);  	bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE); -	trace_erofs_map_blocks_flatmode_enter(inode, map, flags); - -	nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE); +	nblocks = DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ);  	lastblk = nblocks - tailendpacking;  	/* there is no hole in flatmode */  	map->m_flags = EROFS_MAP_MAPPED; -  	if (offset < blknr_to_addr(lastblk)) {  		map->m_pa = blknr_to_addr(vi->raw_blkaddr) + map->m_la;  		map->m_plen = blknr_to_addr(lastblk) - offset; @@ -51,30 +85,23 @@ static int erofs_map_blocks_flatmode(struct inode *inode,  			vi->xattr_isize + erofs_blkoff(map->m_la);  		map->m_plen = inode->i_size - offset; -		/* inline data should be located in one meta block */ -		if (erofs_blkoff(map->m_pa) + map->m_plen > PAGE_SIZE) { +		/* inline data should be located in the same meta block */ +		if (erofs_blkoff(map->m_pa) + map->m_plen > EROFS_BLKSIZ) {  			erofs_err(inode->i_sb,  				  "inline data cross block boundary @ nid %llu",  				  vi->nid);  			DBG_BUGON(1); -			err = -EFSCORRUPTED; -			goto err_out; +			return -EFSCORRUPTED;  		} -  		map->m_flags |= EROFS_MAP_META;  	} else {  		erofs_err(inode->i_sb,  			  "internal error @ nid: %llu (size %llu), m_la 0x%llx",  			  vi->nid, inode->i_size, map->m_la);  		DBG_BUGON(1); -		err = -EIO; -		goto err_out; +		return -EIO;  	} - -	map->m_llen = map->m_plen; -err_out: -	trace_erofs_map_blocks_flatmode_exit(inode, map, flags, 0); -	return err; +	return 0;  }  static int erofs_map_blocks(struct inode *inode, @@ -83,12 +110,14 @@ static int erofs_map_blocks(struct inode *inode,  	struct super_block *sb = inode->i_sb;  	struct erofs_inode *vi = EROFS_I(inode);  	struct erofs_inode_chunk_index *idx; -	struct page *page; +	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;  	u64 chunknr;  	unsigned int unit;  	erofs_off_t pos; +	void *kaddr;  	int err = 0; +	trace_erofs_map_blocks_enter(inode, map, flags);  	map->m_deviceid = 0;  	if (map->m_la >= inode->i_size) {  		/* leave out-of-bound access unmapped */ @@ -97,8 +126,10 @@ static int erofs_map_blocks(struct inode *inode,  		goto out;  	} -	if (vi->datalayout != EROFS_INODE_CHUNK_BASED) -		return erofs_map_blocks_flatmode(inode, map, flags); +	if (vi->datalayout != EROFS_INODE_CHUNK_BASED) { +		err = erofs_map_blocks_flatmode(inode, map, flags); +		goto out; +	}  	if (vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES)  		unit = sizeof(*idx);			/* chunk index */ @@ -109,17 +140,18 @@ static int erofs_map_blocks(struct inode *inode,  	pos = ALIGN(iloc(EROFS_SB(sb), vi->nid) + vi->inode_isize +  		    vi->xattr_isize, unit) + unit * chunknr; -	page = erofs_get_meta_page(inode->i_sb, erofs_blknr(pos)); -	if (IS_ERR(page)) -		return PTR_ERR(page); - +	kaddr = erofs_read_metabuf(&buf, sb, erofs_blknr(pos), EROFS_KMAP); +	if (IS_ERR(kaddr)) { +		err = PTR_ERR(kaddr); +		goto out; +	}  	map->m_la = chunknr << vi->chunkbits;  	map->m_plen = min_t(erofs_off_t, 1UL << vi->chunkbits,  			    roundup(inode->i_size - map->m_la, EROFS_BLKSIZ));  	/* handle block map */  	if (!(vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) { -		__le32 *blkaddr = page_address(page) + erofs_blkoff(pos); +		__le32 *blkaddr = kaddr + erofs_blkoff(pos);  		if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) {  			map->m_flags = 0; @@ -130,7 +162,7 @@ static int erofs_map_blocks(struct inode *inode,  		goto out_unlock;  	}  	/* parse chunk indexes */ -	idx = page_address(page) + erofs_blkoff(pos); +	idx = kaddr + erofs_blkoff(pos);  	switch (le32_to_cpu(idx->blkaddr)) {  	case EROFS_NULL_ADDR:  		map->m_flags = 0; @@ -143,10 +175,11 @@ static int erofs_map_blocks(struct inode *inode,  		break;  	}  out_unlock: -	unlock_page(page); -	put_page(page); +	erofs_put_metabuf(&buf);  out: -	map->m_llen = map->m_plen; +	if (!err) +		map->m_llen = map->m_plen; +	trace_erofs_map_blocks_exit(inode, map, flags, 0);  	return err;  } @@ -159,6 +192,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)  	/* primary device by default */  	map->m_bdev = sb->s_bdev;  	map->m_daxdev = EROFS_SB(sb)->dax_dev; +	map->m_dax_part_off = EROFS_SB(sb)->dax_part_off;  	if (map->m_deviceid) {  		down_read(&devs->rwsem); @@ -169,6 +203,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)  		}  		map->m_bdev = dif->bdev;  		map->m_daxdev = dif->dax_dev; +		map->m_dax_part_off = dif->dax_part_off;  		up_read(&devs->rwsem);  	} else if (devs->extra_devices) {  		down_read(&devs->rwsem); @@ -185,6 +220,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)  				map->m_pa -= startoff;  				map->m_bdev = dif->bdev;  				map->m_daxdev = dif->dax_dev; +				map->m_dax_part_off = dif->dax_part_off;  				break;  			}  		} @@ -215,9 +251,13 @@ static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,  	if (ret)  		return ret; -	iomap->bdev = mdev.m_bdev; -	iomap->dax_dev = mdev.m_daxdev;  	iomap->offset = map.m_la; +	if (flags & IOMAP_DAX) { +		iomap->dax_dev = mdev.m_daxdev; +		iomap->offset += mdev.m_dax_part_off; +	} else { +		iomap->bdev = mdev.m_bdev; +	}  	iomap->length = map.m_llen;  	iomap->flags = 0;  	iomap->private = NULL; @@ -231,16 +271,16 @@ static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,  	}  	if (map.m_flags & EROFS_MAP_META) { -		struct page *ipage; +		void *ptr; +		struct erofs_buf buf = __EROFS_BUF_INITIALIZER;  		iomap->type = IOMAP_INLINE; -		ipage = erofs_get_meta_page(inode->i_sb, -					    erofs_blknr(mdev.m_pa)); -		if (IS_ERR(ipage)) -			return PTR_ERR(ipage); -		iomap->inline_data = page_address(ipage) + -					erofs_blkoff(mdev.m_pa); -		iomap->private = ipage; +		ptr = erofs_read_metabuf(&buf, inode->i_sb, +					 erofs_blknr(mdev.m_pa), EROFS_KMAP); +		if (IS_ERR(ptr)) +			return PTR_ERR(ptr); +		iomap->inline_data = ptr + erofs_blkoff(mdev.m_pa); +		iomap->private = buf.base;  	} else {  		iomap->type = IOMAP_MAPPED;  		iomap->addr = mdev.m_pa; @@ -251,12 +291,17 @@ static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,  static int erofs_iomap_end(struct inode *inode, loff_t pos, loff_t length,  		ssize_t written, unsigned int flags, struct iomap *iomap)  { -	struct page *ipage = iomap->private; +	void *ptr = iomap->private; + +	if (ptr) { +		struct erofs_buf buf = { +			.page = kmap_to_page(ptr), +			.base = ptr, +			.kmap_type = EROFS_KMAP, +		}; -	if (ipage) {  		DBG_BUGON(iomap->type != IOMAP_INLINE); -		unlock_page(ipage); -		put_page(ipage); +		erofs_put_metabuf(&buf);  	} else {  		DBG_BUGON(iomap->type == IOMAP_INLINE);  	} |