Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb5835ed authored by Theodore Ts'o's avatar Theodore Ts'o
Browse files

ext4: add new ioctl EXT4_IOC_GET_ES_CACHE



For debugging reasons, it's useful to know the contents of the extent
cache.  Since the extent cache contains much of what is in the fiemap
ioctl, use an fiemap-style interface to return this information.

Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 1ad3ea6e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -652,6 +652,7 @@ enum {
/* ioctl codes 19--39 are reserved for fscrypt */
#define EXT4_IOC_CLEAR_ES_CACHE		_IO('f', 40)
#define EXT4_IOC_GETSTATE		_IOW('f', 41, __u32)
#define EXT4_IOC_GET_ES_CACHE		_IOWR('f', 42, struct fiemap)

#define EXT4_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
#define EXT4_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
@@ -692,6 +693,12 @@ enum {
#define EXT4_IOC32_SETVERSION_OLD	FS_IOC32_SETVERSION
#endif

/*
 * Returned by EXT4_IOC_GET_ES_CACHE as an additional possible flag.
 * It indicates that the entry in extent status cache is for a hole.
 */
#define EXT4_FIEMAP_EXTENT_HOLE		0x08000000

/* Max physical block we can address w/o extents */
#define EXT4_MAX_BLOCK_FILE_PHYS	0xFFFFFFFF

@@ -3258,6 +3265,9 @@ extern int ext4_ext_check_inode(struct inode *inode);
extern ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_ext_path *path);
extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
			__u64 start, __u64 len);
extern int ext4_get_es_cache(struct inode *inode,
			     struct fiemap_extent_info *fieinfo,
			     __u64 start, __u64 len);
extern int ext4_ext_precache(struct inode *inode);
extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len);
extern int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len);
+86 −8
Original line number Diff line number Diff line
@@ -2315,6 +2315,52 @@ static int ext4_fill_fiemap_extents(struct inode *inode,
	return err;
}

static int ext4_fill_es_cache_info(struct inode *inode,
				   ext4_lblk_t block, ext4_lblk_t num,
				   struct fiemap_extent_info *fieinfo)
{
	ext4_lblk_t next, end = block + num - 1;
	struct extent_status es;
	unsigned char blksize_bits = inode->i_sb->s_blocksize_bits;
	unsigned int flags;
	int err;

	while (block <= end) {
		next = 0;
		flags = 0;
		if (!ext4_es_lookup_extent(inode, block, &next, &es))
			break;
		if (ext4_es_is_unwritten(&es))
			flags |= FIEMAP_EXTENT_UNWRITTEN;
		if (ext4_es_is_delayed(&es))
			flags |= (FIEMAP_EXTENT_DELALLOC |
				  FIEMAP_EXTENT_UNKNOWN);
		if (ext4_es_is_hole(&es))
			flags |= EXT4_FIEMAP_EXTENT_HOLE;
		if (next == 0)
			flags |= FIEMAP_EXTENT_LAST;
		if (flags & (FIEMAP_EXTENT_DELALLOC|
			     EXT4_FIEMAP_EXTENT_HOLE))
			es.es_pblk = 0;
		else
			es.es_pblk = ext4_es_pblock(&es);
		err = fiemap_fill_next_extent(fieinfo,
				(__u64)es.es_lblk << blksize_bits,
				(__u64)es.es_pblk << blksize_bits,
				(__u64)es.es_len << blksize_bits,
				flags);
		if (next == 0)
			break;
		block = next;
		if (err < 0)
			return err;
		if (err == 1)
			return 0;
	}
	return 0;
}


/*
 * ext4_ext_determine_hole - determine hole around given block
 * @inode:	inode we lookup in
@@ -5017,8 +5063,6 @@ static int ext4_find_delayed_extent(struct inode *inode,

	return next_del;
}
/* fiemap flags we can handle specified here */
#define EXT4_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)

static int ext4_xattr_fiemap(struct inode *inode,
				struct fiemap_extent_info *fieinfo)
@@ -5055,10 +5099,16 @@ static int ext4_xattr_fiemap(struct inode *inode,
	return (error < 0 ? error : 0);
}

int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
		__u64 start, __u64 len)
static int _ext4_fiemap(struct inode *inode,
			struct fiemap_extent_info *fieinfo,
			__u64 start, __u64 len,
			int (*fill)(struct inode *, ext4_lblk_t,
				    ext4_lblk_t,
				    struct fiemap_extent_info *))
{
	ext4_lblk_t start_blk;
	u32 ext4_fiemap_flags = FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR;

	int error = 0;

	if (ext4_has_inline_data(inode)) {
@@ -5075,14 +5125,18 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
		error = ext4_ext_precache(inode);
		if (error)
			return error;
		fieinfo->fi_flags &= ~FIEMAP_FLAG_CACHE;
	}

	/* fallback to generic here if not in extents fmt */
	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) &&
	    fill == ext4_fill_fiemap_extents)
		return generic_block_fiemap(inode, fieinfo, start, len,
			ext4_get_block);

	if (fiemap_check_flags(fieinfo, EXT4_FIEMAP_FLAGS))
	if (fill == ext4_fill_es_cache_info)
		ext4_fiemap_flags &= FIEMAP_FLAG_XATTR;
	if (fiemap_check_flags(fieinfo, ext4_fiemap_flags))
		return -EBADR;

	if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
@@ -5101,12 +5155,36 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
		 * Walk the extent tree gathering extent information
		 * and pushing extents back to the user.
		 */
		error = ext4_fill_fiemap_extents(inode, start_blk,
						 len_blks, fieinfo);
		error = fill(inode, start_blk, len_blks, fieinfo);
	}
	return error;
}

int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
		__u64 start, __u64 len)
{
	return _ext4_fiemap(inode, fieinfo, start, len,
			    ext4_fill_fiemap_extents);
}

int ext4_get_es_cache(struct inode *inode, struct fiemap_extent_info *fieinfo,
		      __u64 start, __u64 len)
{
	if (ext4_has_inline_data(inode)) {
		int has_inline;

		down_read(&EXT4_I(inode)->xattr_sem);
		has_inline = ext4_has_inline_data(inode);
		up_read(&EXT4_I(inode)->xattr_sem);
		if (has_inline)
			return 0;
	}

	return _ext4_fiemap(inode, fieinfo, start, len,
			    ext4_fill_es_cache_info);
}


/*
 * ext4_access_path:
 * Function to access the path buffer for marking it dirty.
+10 −0
Original line number Diff line number Diff line
@@ -899,6 +899,7 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk,
 * Return: 1 on found, 0 on not
 */
int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
			  ext4_lblk_t *next_lblk,
			  struct extent_status *es)
{
	struct ext4_es_tree *tree;
@@ -948,6 +949,15 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
		if (!ext4_es_is_referenced(es1))
			ext4_es_set_referenced(es1);
		stats->es_stats_cache_hits++;
		if (next_lblk) {
			node = rb_next(&es1->rb_node);
			if (node) {
				es1 = rb_entry(node, struct extent_status,
					       rb_node);
				*next_lblk = es1->es_lblk;
			} else
				*next_lblk = 0;
		}
	} else {
		stats->es_stats_cache_misses++;
	}
+1 −0
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ extern void ext4_es_find_extent_range(struct inode *inode,
				      ext4_lblk_t lblk, ext4_lblk_t end,
				      struct extent_status *es);
extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
				 ext4_lblk_t *next_lblk,
				 struct extent_status *es);
extern bool ext4_es_scan_range(struct inode *inode,
			       int (*matching_fn)(struct extent_status *es),
+3 −3
Original line number Diff line number Diff line
@@ -527,7 +527,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
		return -EFSCORRUPTED;

	/* Lookup extent status tree firstly */
	if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) {
	if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
		if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) {
			map->m_pblk = ext4_es_pblock(&es) +
					map->m_lblk - es.es_lblk;
@@ -695,7 +695,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
		 * extent status tree.
		 */
		if ((flags & EXT4_GET_BLOCKS_PRE_IO) &&
		    ext4_es_lookup_extent(inode, map->m_lblk, &es)) {
		    ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
			if (ext4_es_is_written(&es))
				goto out_sem;
		}
@@ -1868,7 +1868,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
		  (unsigned long) map->m_lblk);

	/* Lookup extent status tree firstly */
	if (ext4_es_lookup_extent(inode, iblock, &es)) {
	if (ext4_es_lookup_extent(inode, iblock, NULL, &es)) {
		if (ext4_es_is_hole(&es)) {
			retval = 0;
			down_read(&EXT4_I(inode)->i_data_sem);
Loading