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

Commit d583fb87 authored by Allison Henderson's avatar Allison Henderson Committed by Theodore Ts'o
Browse files

ext4: punch out extents



This patch modifies the truncate routines to support hole punching
Below is a brief summary of the patches changes:

- Added end param to ext_ext4_rm_leaf
        This function has been modified to accept an end parameter
        which enables it to punch holes in leafs instead of just
        truncating them.

- Implemented the "remove head" case in the ext_remove_blocks routine
        This routine is used by ext_ext4_rm_leaf to remove the tail
        of an extent during a truncate.  The new ext_ext4_rm_leaf
        routine will now also use it to remove the head of an extent in the
        case that the hole covers a region of blocks at the beginning
        of an extent.

- Added "end" param to ext4_ext_remove_space routine
        This function has been modified to accept a stop parameter, which
        is passed through to ext4_ext_rm_leaf.

[ext4 punch hole patch series 3/5 v6] 

Signed-off-by: default avatarAllison Henderson <achender@us.ibm.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent 30848851
Loading
Loading
Loading
Loading
+141 −19
Original line number Diff line number Diff line
@@ -46,6 +46,13 @@

#include <trace/events/ext4.h>

static int ext4_split_extent(handle_t *handle,
				struct inode *inode,
				struct ext4_ext_path *path,
				struct ext4_map_blocks *map,
				int split_flag,
				int flags);

static int ext4_ext_truncate_extend_restart(handle_t *handle,
					    struct inode *inode,
					    int needed)
@@ -2203,8 +2210,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
		ext4_free_blocks(handle, inode, NULL, start, num, flags);
	} else if (from == le32_to_cpu(ex->ee_block)
		   && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
		printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
			from, to, le32_to_cpu(ex->ee_block), ee_len);
		/* head removal */
		ext4_lblk_t num;
		ext4_fsblk_t start;

		num = to - from;
		start = ext4_ext_pblock(ex);

		ext_debug("free first %u blocks starting %llu\n", num, start);
		ext4_free_blocks(handle, inode, 0, start, num, flags);

	} else {
		printk(KERN_INFO "strange request: removal(2) "
				"%u-%u from %u:%u\n",
@@ -2213,9 +2228,22 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
	return 0;
}


/*
 * ext4_ext_rm_leaf() Removes the extents associated with the
 * blocks appearing between "start" and "end", and splits the extents
 * if "start" and "end" appear in the same extent
 *
 * @handle: The journal handle
 * @inode:  The files inode
 * @path:   The path to the leaf
 * @start:  The first block to remove
 * @end:   The last block to remove
 */
static int
ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
		struct ext4_ext_path *path, ext4_lblk_t start)
		struct ext4_ext_path *path, ext4_lblk_t start,
		ext4_lblk_t end)
{
	int err = 0, correct_index = 0;
	int depth = ext_depth(inode), credits;
@@ -2226,6 +2254,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
	unsigned short ex_ee_len;
	unsigned uninitialized = 0;
	struct ext4_extent *ex;
	struct ext4_map_blocks map;

	/* the header must be checked already in ext4_ext_remove_space() */
	ext_debug("truncate since %u in leaf\n", start);
@@ -2255,31 +2284,95 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
		path[depth].p_ext = ex;

		a = ex_ee_block > start ? ex_ee_block : start;
		b = ex_ee_block + ex_ee_len - 1 < EXT_MAX_BLOCK ?
			ex_ee_block + ex_ee_len - 1 : EXT_MAX_BLOCK;
		b = ex_ee_block+ex_ee_len - 1 < end ?
			ex_ee_block+ex_ee_len - 1 : end;

		ext_debug("  border %u:%u\n", a, b);

		if (a != ex_ee_block && b != ex_ee_block + ex_ee_len - 1) {
		/* If this extent is beyond the end of the hole, skip it */
		if (end <= ex_ee_block) {
			ex--;
			ex_ee_block = le32_to_cpu(ex->ee_block);
			ex_ee_len = ext4_ext_get_actual_len(ex);
			continue;
		} else if (a != ex_ee_block &&
			b != ex_ee_block + ex_ee_len - 1) {
			/*
			 * If this is a truncate, then this condition should
			 * never happen because at least one of the end points
			 * needs to be on the edge of the extent.
			 */
			if (end == EXT_MAX_BLOCK) {
				ext_debug("  bad truncate %u:%u\n",
						start, end);
				block = 0;
				num = 0;
			BUG();
				err = -EIO;
				goto out;
			}
			/*
			 * else this is a hole punch, so the extent needs to
			 * be split since neither edge of the hole is on the
			 * extent edge
			 */
			else{
				map.m_pblk = ext4_ext_pblock(ex);
				map.m_lblk = ex_ee_block;
				map.m_len = b - ex_ee_block;

				err = ext4_split_extent(handle,
					inode, path, &map, 0,
					EXT4_GET_BLOCKS_PUNCH_OUT_EXT |
					EXT4_GET_BLOCKS_PRE_IO);

				if (err < 0)
					goto out;

				ex_ee_len = ext4_ext_get_actual_len(ex);

				b = ex_ee_block+ex_ee_len - 1 < end ?
					ex_ee_block+ex_ee_len - 1 : end;

				/* Then remove tail of this extent */
				block = ex_ee_block;
				num = a - block;
			}
		} else if (a != ex_ee_block) {
			/* remove tail of the extent */
			block = ex_ee_block;
			num = a - block;
		} else if (b != ex_ee_block + ex_ee_len - 1) {
			/* remove head of the extent */
			block = a;
			num = b - a;
			/* there is no "make a hole" API yet */
			BUG();
			block = b;
			num =  ex_ee_block + ex_ee_len - b;

			/*
			 * If this is a truncate, this condition
			 * should never happen
			 */
			if (end == EXT_MAX_BLOCK) {
				ext_debug("  bad truncate %u:%u\n",
					start, end);
				err = -EIO;
				goto out;
			}
		} else {
			/* remove whole extent: excellent! */
			block = ex_ee_block;
			num = 0;
			BUG_ON(a != ex_ee_block);
			BUG_ON(b != ex_ee_block + ex_ee_len - 1);
			if (a != ex_ee_block) {
				ext_debug("  bad truncate %u:%u\n",
					start, end);
				err = -EIO;
				goto out;
			}

			if (b != ex_ee_block + ex_ee_len - 1) {
				ext_debug("  bad truncate %u:%u\n",
					start, end);
				err = -EIO;
				goto out;
			}
		}

		/*
@@ -2310,7 +2403,13 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
		if (num == 0) {
			/* this extent is removed; mark slot entirely unused */
			ext4_ext_store_pblock(ex, 0);
			le16_add_cpu(&eh->eh_entries, -1);
		} else if (block != ex_ee_block) {
			/*
			 * If this was a head removal, then we need to update
			 * the physical block since it is now at a different
			 * location
			 */
			ext4_ext_store_pblock(ex, ext4_ext_pblock(ex) + (b-a));
		}

		ex->ee_block = cpu_to_le32(block);
@@ -2326,6 +2425,27 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
		if (err)
			goto out;

		/*
		 * If the extent was completely released,
		 * we need to remove it from the leaf
		 */
		if (num == 0) {
			if (end != EXT_MAX_BLOCK) {
				/*
				 * For hole punching, we need to scoot all the
				 * extents up when an extent is removed so that
				 * we dont have blank extents in the middle
				 */
				memmove(ex, ex+1, (EXT_LAST_EXTENT(eh) - ex) *
					sizeof(struct ext4_extent));

				/* Now get rid of the one at the end */
				memset(EXT_LAST_EXTENT(eh), 0,
					sizeof(struct ext4_extent));
			}
			le16_add_cpu(&eh->eh_entries, -1);
		}

		ext_debug("new extent: %u:%u:%llu\n", block, num,
				ext4_ext_pblock(ex));
		ex--;
@@ -2366,7 +2486,8 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
	return 1;
}

static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
				ext4_lblk_t end)
{
	struct super_block *sb = inode->i_sb;
	int depth = ext_depth(inode);
@@ -2405,7 +2526,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
	while (i >= 0 && err == 0) {
		if (i == depth) {
			/* this is leaf block */
			err = ext4_ext_rm_leaf(handle, inode, path, start);
			err = ext4_ext_rm_leaf(handle, inode, path,
					start, end);
			/* root level has p_bh == NULL, brelse() eats this */
			brelse(path[i].p_bh);
			path[i].p_bh = NULL;
@@ -3451,7 +3573,7 @@ void ext4_ext_truncate(struct inode *inode)

	last_block = (inode->i_size + sb->s_blocksize - 1)
			>> EXT4_BLOCK_SIZE_BITS(sb);
	err = ext4_ext_remove_space(inode, last_block);
	err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCK);

	/* In a multi-transaction truncate, we only make the final
	 * transaction synchronous.