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

Commit d3c92626 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ext4 fixes from Ted Ts'o:
 "Fix a number of regression and other bugs in ext4, most of which were
  relatively obscure cornercases or races that were found using
  regression tests."

* tag 'ext4_for_linue' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (21 commits)
  ext4: fix data=journal fast mount/umount hang
  ext4: fix ext4_evict_inode() racing against workqueue processing code
  ext4: fix memory leakage in mext_check_coverage
  ext4: use s_extent_max_zeroout_kb value as number of kb
  ext4: use atomic64_t for the per-flexbg free_clusters count
  jbd2: fix use after free in jbd2_journal_dirty_metadata()
  ext4: reserve metadata block for every delayed write
  ext4: update reserved space after the 'correction'
  ext4: do not use yield()
  ext4: remove unused variable in ext4_free_blocks()
  ext4: fix WARN_ON from ext4_releasepage()
  ext4: fix the wrong number of the allocated blocks in ext4_split_extent()
  ext4: update extent status tree after an extent is zeroed out
  ext4: fix wrong m_len value after unwritten extent conversion
  ext4: add self-testing infrastructure to do a sanity check
  ext4: avoid a potential overflow in ext4_es_can_be_merged()
  ext4: invalidate extent status tree during extent migration
  ext4: remove unnecessary wait for extent conversion in ext4_fallocate()
  ext4: add warning to ext4_convert_unwritten_extents_endio
  ext4: disable merging of uninitialized extents
  ...
parents 0a7e4531 2b405bfa
Loading
Loading
Loading
Loading
+4 −4
Original line number Original line Diff line number Diff line
@@ -335,8 +335,8 @@ struct ext4_group_desc
 */
 */


struct flex_groups {
struct flex_groups {
	atomic64_t	free_clusters;
	atomic_t	free_inodes;
	atomic_t	free_inodes;
	atomic_t free_clusters;
	atomic_t	used_dirs;
	atomic_t	used_dirs;
};
};


@@ -2617,7 +2617,7 @@ extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
extern int __init ext4_init_pageio(void);
extern int __init ext4_init_pageio(void);
extern void ext4_add_complete_io(ext4_io_end_t *io_end);
extern void ext4_add_complete_io(ext4_io_end_t *io_end);
extern void ext4_exit_pageio(void);
extern void ext4_exit_pageio(void);
extern void ext4_ioend_wait(struct inode *);
extern void ext4_ioend_shutdown(struct inode *);
extern void ext4_free_io_end(ext4_io_end_t *io);
extern void ext4_free_io_end(ext4_io_end_t *io);
extern ext4_io_end_t *ext4_init_io_end(struct inode *inode, gfp_t flags);
extern ext4_io_end_t *ext4_init_io_end(struct inode *inode, gfp_t flags);
extern void ext4_end_io_work(struct work_struct *work);
extern void ext4_end_io_work(struct work_struct *work);
+84 −21
Original line number Original line Diff line number Diff line
@@ -1584,10 +1584,12 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1,
	unsigned short ext1_ee_len, ext2_ee_len, max_len;
	unsigned short ext1_ee_len, ext2_ee_len, max_len;


	/*
	/*
	 * Make sure that either both extents are uninitialized, or
	 * Make sure that both extents are initialized. We don't merge
	 * both are _not_.
	 * uninitialized extents so that we can be sure that end_io code has
	 * the extent that was written properly split out and conversion to
	 * initialized is trivial.
	 */
	 */
	if (ext4_ext_is_uninitialized(ex1) ^ ext4_ext_is_uninitialized(ex2))
	if (ext4_ext_is_uninitialized(ex1) || ext4_ext_is_uninitialized(ex2))
		return 0;
		return 0;


	if (ext4_ext_is_uninitialized(ex1))
	if (ext4_ext_is_uninitialized(ex1))
@@ -2923,7 +2925,7 @@ static int ext4_split_extent_at(handle_t *handle,
{
{
	ext4_fsblk_t newblock;
	ext4_fsblk_t newblock;
	ext4_lblk_t ee_block;
	ext4_lblk_t ee_block;
	struct ext4_extent *ex, newex, orig_ex;
	struct ext4_extent *ex, newex, orig_ex, zero_ex;
	struct ext4_extent *ex2 = NULL;
	struct ext4_extent *ex2 = NULL;
	unsigned int ee_len, depth;
	unsigned int ee_len, depth;
	int err = 0;
	int err = 0;
@@ -2943,6 +2945,10 @@ static int ext4_split_extent_at(handle_t *handle,
	newblock = split - ee_block + ext4_ext_pblock(ex);
	newblock = split - ee_block + ext4_ext_pblock(ex);


	BUG_ON(split < ee_block || split >= (ee_block + ee_len));
	BUG_ON(split < ee_block || split >= (ee_block + ee_len));
	BUG_ON(!ext4_ext_is_uninitialized(ex) &&
	       split_flag & (EXT4_EXT_MAY_ZEROOUT |
			     EXT4_EXT_MARK_UNINIT1 |
			     EXT4_EXT_MARK_UNINIT2));


	err = ext4_ext_get_access(handle, inode, path + depth);
	err = ext4_ext_get_access(handle, inode, path + depth);
	if (err)
	if (err)
@@ -2990,12 +2996,26 @@ static int ext4_split_extent_at(handle_t *handle,
	err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
	err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
	if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) {
	if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) {
		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
			if (split_flag & EXT4_EXT_DATA_VALID1)
			if (split_flag & EXT4_EXT_DATA_VALID1) {
				err = ext4_ext_zeroout(inode, ex2);
				err = ext4_ext_zeroout(inode, ex2);
			else
				zero_ex.ee_block = ex2->ee_block;
				zero_ex.ee_len = ext4_ext_get_actual_len(ex2);
				ext4_ext_store_pblock(&zero_ex,
						      ext4_ext_pblock(ex2));
			} else {
				err = ext4_ext_zeroout(inode, ex);
				err = ext4_ext_zeroout(inode, ex);
		} else
				zero_ex.ee_block = ex->ee_block;
				zero_ex.ee_len = ext4_ext_get_actual_len(ex);
				ext4_ext_store_pblock(&zero_ex,
						      ext4_ext_pblock(ex));
			}
		} else {
			err = ext4_ext_zeroout(inode, &orig_ex);
			err = ext4_ext_zeroout(inode, &orig_ex);
			zero_ex.ee_block = orig_ex.ee_block;
			zero_ex.ee_len = ext4_ext_get_actual_len(&orig_ex);
			ext4_ext_store_pblock(&zero_ex,
					      ext4_ext_pblock(&orig_ex));
		}


		if (err)
		if (err)
			goto fix_extent_len;
			goto fix_extent_len;
@@ -3003,6 +3023,12 @@ static int ext4_split_extent_at(handle_t *handle,
		ex->ee_len = cpu_to_le16(ee_len);
		ex->ee_len = cpu_to_le16(ee_len);
		ext4_ext_try_to_merge(handle, inode, path, ex);
		ext4_ext_try_to_merge(handle, inode, path, ex);
		err = ext4_ext_dirty(handle, inode, path + path->p_depth);
		err = ext4_ext_dirty(handle, inode, path + path->p_depth);
		if (err)
			goto fix_extent_len;

		/* update extent status tree */
		err = ext4_es_zeroout(inode, &zero_ex);

		goto out;
		goto out;
	} else if (err)
	} else if (err)
		goto fix_extent_len;
		goto fix_extent_len;
@@ -3041,6 +3067,7 @@ static int ext4_split_extent(handle_t *handle,
	int err = 0;
	int err = 0;
	int uninitialized;
	int uninitialized;
	int split_flag1, flags1;
	int split_flag1, flags1;
	int allocated = map->m_len;


	depth = ext_depth(inode);
	depth = ext_depth(inode);
	ex = path[depth].p_ext;
	ex = path[depth].p_ext;
@@ -3060,20 +3087,29 @@ static int ext4_split_extent(handle_t *handle,
				map->m_lblk + map->m_len, split_flag1, flags1);
				map->m_lblk + map->m_len, split_flag1, flags1);
		if (err)
		if (err)
			goto out;
			goto out;
	} else {
		allocated = ee_len - (map->m_lblk - ee_block);
	}
	}

	/*
	 * Update path is required because previous ext4_split_extent_at() may
	 * result in split of original leaf or extent zeroout.
	 */
	ext4_ext_drop_refs(path);
	ext4_ext_drop_refs(path);
	path = ext4_ext_find_extent(inode, map->m_lblk, path);
	path = ext4_ext_find_extent(inode, map->m_lblk, path);
	if (IS_ERR(path))
	if (IS_ERR(path))
		return PTR_ERR(path);
		return PTR_ERR(path);
	depth = ext_depth(inode);
	ex = path[depth].p_ext;
	uninitialized = ext4_ext_is_uninitialized(ex);
	split_flag1 = 0;


	if (map->m_lblk >= ee_block) {
	if (map->m_lblk >= ee_block) {
		split_flag1 = split_flag & (EXT4_EXT_MAY_ZEROOUT |
		split_flag1 = split_flag & EXT4_EXT_DATA_VALID2;
					    EXT4_EXT_DATA_VALID2);
		if (uninitialized) {
		if (uninitialized)
			split_flag1 |= EXT4_EXT_MARK_UNINIT1;
			split_flag1 |= EXT4_EXT_MARK_UNINIT1;
		if (split_flag & EXT4_EXT_MARK_UNINIT2)
			split_flag1 |= split_flag & (EXT4_EXT_MAY_ZEROOUT |
			split_flag1 |= EXT4_EXT_MARK_UNINIT2;
						     EXT4_EXT_MARK_UNINIT2);
		}
		err = ext4_split_extent_at(handle, inode, path,
		err = ext4_split_extent_at(handle, inode, path,
				map->m_lblk, split_flag1, flags);
				map->m_lblk, split_flag1, flags);
		if (err)
		if (err)
@@ -3082,7 +3118,7 @@ static int ext4_split_extent(handle_t *handle,


	ext4_ext_show_leaf(inode, path);
	ext4_ext_show_leaf(inode, path);
out:
out:
	return err ? err : map->m_len;
	return err ? err : allocated;
}
}


/*
/*
@@ -3137,6 +3173,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
	ee_block = le32_to_cpu(ex->ee_block);
	ee_block = le32_to_cpu(ex->ee_block);
	ee_len = ext4_ext_get_actual_len(ex);
	ee_len = ext4_ext_get_actual_len(ex);
	allocated = ee_len - (map->m_lblk - ee_block);
	allocated = ee_len - (map->m_lblk - ee_block);
	zero_ex.ee_len = 0;


	trace_ext4_ext_convert_to_initialized_enter(inode, map, ex);
	trace_ext4_ext_convert_to_initialized_enter(inode, map, ex);


@@ -3227,13 +3264,16 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,


	if (EXT4_EXT_MAY_ZEROOUT & split_flag)
	if (EXT4_EXT_MAY_ZEROOUT & split_flag)
		max_zeroout = sbi->s_extent_max_zeroout_kb >>
		max_zeroout = sbi->s_extent_max_zeroout_kb >>
			inode->i_sb->s_blocksize_bits;
			(inode->i_sb->s_blocksize_bits - 10);


	/* If extent is less than s_max_zeroout_kb, zeroout directly */
	/* If extent is less than s_max_zeroout_kb, zeroout directly */
	if (max_zeroout && (ee_len <= max_zeroout)) {
	if (max_zeroout && (ee_len <= max_zeroout)) {
		err = ext4_ext_zeroout(inode, ex);
		err = ext4_ext_zeroout(inode, ex);
		if (err)
		if (err)
			goto out;
			goto out;
		zero_ex.ee_block = ex->ee_block;
		zero_ex.ee_len = ext4_ext_get_actual_len(ex);
		ext4_ext_store_pblock(&zero_ex, ext4_ext_pblock(ex));


		err = ext4_ext_get_access(handle, inode, path + depth);
		err = ext4_ext_get_access(handle, inode, path + depth);
		if (err)
		if (err)
@@ -3292,6 +3332,9 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
		err = allocated;
		err = allocated;


out:
out:
	/* If we have gotten a failure, don't zero out status tree */
	if (!err)
		err = ext4_es_zeroout(inode, &zero_ex);
	return err ? err : allocated;
	return err ? err : allocated;
}
}


@@ -3374,8 +3417,19 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
		"block %llu, max_blocks %u\n", inode->i_ino,
		"block %llu, max_blocks %u\n", inode->i_ino,
		  (unsigned long long)ee_block, ee_len);
		  (unsigned long long)ee_block, ee_len);


	/* If extent is larger than requested then split is required */
	/* If extent is larger than requested it is a clear sign that we still
	 * have some extent state machine issues left. So extent_split is still
	 * required.
	 * TODO: Once all related issues will be fixed this situation should be
	 * illegal.
	 */
	if (ee_block != map->m_lblk || ee_len > map->m_len) {
	if (ee_block != map->m_lblk || ee_len > map->m_len) {
#ifdef EXT4_DEBUG
		ext4_warning("Inode (%ld) finished: extent logical block %llu,"
			     " len %u; IO logical block %llu, len %u\n",
			     inode->i_ino, (unsigned long long)ee_block, ee_len,
			     (unsigned long long)map->m_lblk, map->m_len);
#endif
		err = ext4_split_unwritten_extents(handle, inode, map, path,
		err = ext4_split_unwritten_extents(handle, inode, map, path,
						   EXT4_GET_BLOCKS_CONVERT);
						   EXT4_GET_BLOCKS_CONVERT);
		if (err < 0)
		if (err < 0)
@@ -3626,6 +3680,10 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
						 path, map->m_len);
						 path, map->m_len);
		} else
		} else
			err = ret;
			err = ret;
		map->m_flags |= EXT4_MAP_MAPPED;
		if (allocated > map->m_len)
			allocated = map->m_len;
		map->m_len = allocated;
		goto out2;
		goto out2;
	}
	}
	/* buffered IO case */
	/* buffered IO case */
@@ -3675,6 +3733,7 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
					allocated - map->m_len);
					allocated - map->m_len);
		allocated = map->m_len;
		allocated = map->m_len;
	}
	}
	map->m_len = allocated;


	/*
	/*
	 * If we have done fallocate with the offset that is already
	 * If we have done fallocate with the offset that is already
@@ -4106,9 +4165,6 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
			}
			}
		} else {
		} else {
			BUG_ON(allocated_clusters < reserved_clusters);
			BUG_ON(allocated_clusters < reserved_clusters);
			/* We will claim quota for all newly allocated blocks.*/
			ext4_da_update_reserve_space(inode, allocated_clusters,
							1);
			if (reserved_clusters < allocated_clusters) {
			if (reserved_clusters < allocated_clusters) {
				struct ext4_inode_info *ei = EXT4_I(inode);
				struct ext4_inode_info *ei = EXT4_I(inode);
				int reservation = allocated_clusters -
				int reservation = allocated_clusters -
@@ -4159,6 +4215,15 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
				ei->i_reserved_data_blocks += reservation;
				ei->i_reserved_data_blocks += reservation;
				spin_unlock(&ei->i_block_reservation_lock);
				spin_unlock(&ei->i_block_reservation_lock);
			}
			}
			/*
			 * We will claim quota for all newly allocated blocks.
			 * We're updating the reserved space *after* the
			 * correction above so we do not accidentally free
			 * all the metadata reservation because we might
			 * actually need it later on.
			 */
			ext4_da_update_reserve_space(inode, allocated_clusters,
							1);
		}
		}
	}
	}


@@ -4368,8 +4433,6 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
	if (len <= EXT_UNINIT_MAX_LEN << blkbits)
	if (len <= EXT_UNINIT_MAX_LEN << blkbits)
		flags |= EXT4_GET_BLOCKS_NO_NORMALIZE;
		flags |= EXT4_GET_BLOCKS_NO_NORMALIZE;


	/* Prevent race condition between unwritten */
	ext4_flush_unwritten_io(inode);
retry:
retry:
	while (ret >= 0 && ret < max_blocks) {
	while (ret >= 0 && ret < max_blocks) {
		map.m_lblk = map.m_lblk + ret;
		map.m_lblk = map.m_lblk + ret;
+207 −5
Original line number Original line Diff line number Diff line
@@ -333,17 +333,27 @@ static void ext4_es_free_extent(struct inode *inode, struct extent_status *es)
static int ext4_es_can_be_merged(struct extent_status *es1,
static int ext4_es_can_be_merged(struct extent_status *es1,
				 struct extent_status *es2)
				 struct extent_status *es2)
{
{
	if (es1->es_lblk + es1->es_len != es2->es_lblk)
	if (ext4_es_status(es1) != ext4_es_status(es2))
		return 0;
		return 0;


	if (ext4_es_status(es1) != ext4_es_status(es2))
	if (((__u64) es1->es_len) + es2->es_len > 0xFFFFFFFFULL)
		return 0;
		return 0;


	if ((ext4_es_is_written(es1) || ext4_es_is_unwritten(es1)) &&
	if (((__u64) es1->es_lblk) + es1->es_len != es2->es_lblk)
	    (ext4_es_pblock(es1) + es1->es_len != ext4_es_pblock(es2)))
		return 0;
		return 0;


	if ((ext4_es_is_written(es1) || ext4_es_is_unwritten(es1)) &&
	    (ext4_es_pblock(es1) + es1->es_len == ext4_es_pblock(es2)))
		return 1;

	if (ext4_es_is_hole(es1))
		return 1;
		return 1;

	/* we need to check delayed extent is without unwritten status */
	if (ext4_es_is_delayed(es1) && !ext4_es_is_unwritten(es1))
		return 1;

	return 0;
}
}


static struct extent_status *
static struct extent_status *
@@ -389,6 +399,179 @@ ext4_es_try_to_merge_right(struct inode *inode, struct extent_status *es)
	return es;
	return es;
}
}


#ifdef ES_AGGRESSIVE_TEST
static void ext4_es_insert_extent_ext_check(struct inode *inode,
					    struct extent_status *es)
{
	struct ext4_ext_path *path = NULL;
	struct ext4_extent *ex;
	ext4_lblk_t ee_block;
	ext4_fsblk_t ee_start;
	unsigned short ee_len;
	int depth, ee_status, es_status;

	path = ext4_ext_find_extent(inode, es->es_lblk, NULL);
	if (IS_ERR(path))
		return;

	depth = ext_depth(inode);
	ex = path[depth].p_ext;

	if (ex) {

		ee_block = le32_to_cpu(ex->ee_block);
		ee_start = ext4_ext_pblock(ex);
		ee_len = ext4_ext_get_actual_len(ex);

		ee_status = ext4_ext_is_uninitialized(ex) ? 1 : 0;
		es_status = ext4_es_is_unwritten(es) ? 1 : 0;

		/*
		 * Make sure ex and es are not overlap when we try to insert
		 * a delayed/hole extent.
		 */
		if (!ext4_es_is_written(es) && !ext4_es_is_unwritten(es)) {
			if (in_range(es->es_lblk, ee_block, ee_len)) {
				pr_warn("ES insert assertation failed for "
					"inode: %lu we can find an extent "
					"at block [%d/%d/%llu/%c], but we "
					"want to add an delayed/hole extent "
					"[%d/%d/%llu/%llx]\n",
					inode->i_ino, ee_block, ee_len,
					ee_start, ee_status ? 'u' : 'w',
					es->es_lblk, es->es_len,
					ext4_es_pblock(es), ext4_es_status(es));
			}
			goto out;
		}

		/*
		 * We don't check ee_block == es->es_lblk, etc. because es
		 * might be a part of whole extent, vice versa.
		 */
		if (es->es_lblk < ee_block ||
		    ext4_es_pblock(es) != ee_start + es->es_lblk - ee_block) {
			pr_warn("ES insert assertation failed for inode: %lu "
				"ex_status [%d/%d/%llu/%c] != "
				"es_status [%d/%d/%llu/%c]\n", inode->i_ino,
				ee_block, ee_len, ee_start,
				ee_status ? 'u' : 'w', es->es_lblk, es->es_len,
				ext4_es_pblock(es), es_status ? 'u' : 'w');
			goto out;
		}

		if (ee_status ^ es_status) {
			pr_warn("ES insert assertation failed for inode: %lu "
				"ex_status [%d/%d/%llu/%c] != "
				"es_status [%d/%d/%llu/%c]\n", inode->i_ino,
				ee_block, ee_len, ee_start,
				ee_status ? 'u' : 'w', es->es_lblk, es->es_len,
				ext4_es_pblock(es), es_status ? 'u' : 'w');
		}
	} else {
		/*
		 * We can't find an extent on disk.  So we need to make sure
		 * that we don't want to add an written/unwritten extent.
		 */
		if (!ext4_es_is_delayed(es) && !ext4_es_is_hole(es)) {
			pr_warn("ES insert assertation failed for inode: %lu "
				"can't find an extent at block %d but we want "
				"to add an written/unwritten extent "
				"[%d/%d/%llu/%llx]\n", inode->i_ino,
				es->es_lblk, es->es_lblk, es->es_len,
				ext4_es_pblock(es), ext4_es_status(es));
		}
	}
out:
	if (path) {
		ext4_ext_drop_refs(path);
		kfree(path);
	}
}

static void ext4_es_insert_extent_ind_check(struct inode *inode,
					    struct extent_status *es)
{
	struct ext4_map_blocks map;
	int retval;

	/*
	 * Here we call ext4_ind_map_blocks to lookup a block mapping because
	 * 'Indirect' structure is defined in indirect.c.  So we couldn't
	 * access direct/indirect tree from outside.  It is too dirty to define
	 * this function in indirect.c file.
	 */

	map.m_lblk = es->es_lblk;
	map.m_len = es->es_len;

	retval = ext4_ind_map_blocks(NULL, inode, &map, 0);
	if (retval > 0) {
		if (ext4_es_is_delayed(es) || ext4_es_is_hole(es)) {
			/*
			 * We want to add a delayed/hole extent but this
			 * block has been allocated.
			 */
			pr_warn("ES insert assertation failed for inode: %lu "
				"We can find blocks but we want to add a "
				"delayed/hole extent [%d/%d/%llu/%llx]\n",
				inode->i_ino, es->es_lblk, es->es_len,
				ext4_es_pblock(es), ext4_es_status(es));
			return;
		} else if (ext4_es_is_written(es)) {
			if (retval != es->es_len) {
				pr_warn("ES insert assertation failed for "
					"inode: %lu retval %d != es_len %d\n",
					inode->i_ino, retval, es->es_len);
				return;
			}
			if (map.m_pblk != ext4_es_pblock(es)) {
				pr_warn("ES insert assertation failed for "
					"inode: %lu m_pblk %llu != "
					"es_pblk %llu\n",
					inode->i_ino, map.m_pblk,
					ext4_es_pblock(es));
				return;
			}
		} else {
			/*
			 * We don't need to check unwritten extent because
			 * indirect-based file doesn't have it.
			 */
			BUG_ON(1);
		}
	} else if (retval == 0) {
		if (ext4_es_is_written(es)) {
			pr_warn("ES insert assertation failed for inode: %lu "
				"We can't find the block but we want to add "
				"an written extent [%d/%d/%llu/%llx]\n",
				inode->i_ino, es->es_lblk, es->es_len,
				ext4_es_pblock(es), ext4_es_status(es));
			return;
		}
	}
}

static inline void ext4_es_insert_extent_check(struct inode *inode,
					       struct extent_status *es)
{
	/*
	 * We don't need to worry about the race condition because
	 * caller takes i_data_sem locking.
	 */
	BUG_ON(!rwsem_is_locked(&EXT4_I(inode)->i_data_sem));
	if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
		ext4_es_insert_extent_ext_check(inode, es);
	else
		ext4_es_insert_extent_ind_check(inode, es);
}
#else
static inline void ext4_es_insert_extent_check(struct inode *inode,
					       struct extent_status *es)
{
}
#endif

static int __es_insert_extent(struct inode *inode, struct extent_status *newes)
static int __es_insert_extent(struct inode *inode, struct extent_status *newes)
{
{
	struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree;
	struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree;
@@ -471,6 +654,8 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
	ext4_es_store_status(&newes, status);
	ext4_es_store_status(&newes, status);
	trace_ext4_es_insert_extent(inode, &newes);
	trace_ext4_es_insert_extent(inode, &newes);


	ext4_es_insert_extent_check(inode, &newes);

	write_lock(&EXT4_I(inode)->i_es_lock);
	write_lock(&EXT4_I(inode)->i_es_lock);
	err = __es_remove_extent(inode, lblk, end);
	err = __es_remove_extent(inode, lblk, end);
	if (err != 0)
	if (err != 0)
@@ -669,6 +854,23 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
	return err;
	return err;
}
}


int ext4_es_zeroout(struct inode *inode, struct ext4_extent *ex)
{
	ext4_lblk_t  ee_block;
	ext4_fsblk_t ee_pblock;
	unsigned int ee_len;

	ee_block  = le32_to_cpu(ex->ee_block);
	ee_len    = ext4_ext_get_actual_len(ex);
	ee_pblock = ext4_ext_pblock(ex);

	if (ee_len == 0)
		return 0;

	return ext4_es_insert_extent(inode, ee_block, ee_len, ee_pblock,
				     EXTENT_STATUS_WRITTEN);
}

static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc)
static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc)
{
{
	struct ext4_sb_info *sbi = container_of(shrink,
	struct ext4_sb_info *sbi = container_of(shrink,
+9 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,12 @@
#define es_debug(fmt, ...)	no_printk(fmt, ##__VA_ARGS__)
#define es_debug(fmt, ...)	no_printk(fmt, ##__VA_ARGS__)
#endif
#endif


/*
 * With ES_AGGRESSIVE_TEST defined, the result of es caching will be
 * checked with old map_block's result.
 */
#define ES_AGGRESSIVE_TEST__

/*
/*
 * These flags live in the high bits of extent_status.es_pblk
 * These flags live in the high bits of extent_status.es_pblk
 */
 */
@@ -33,6 +39,8 @@
				 EXTENT_STATUS_DELAYED | \
				 EXTENT_STATUS_DELAYED | \
				 EXTENT_STATUS_HOLE)
				 EXTENT_STATUS_HOLE)


struct ext4_extent;

struct extent_status {
struct extent_status {
	struct rb_node rb_node;
	struct rb_node rb_node;
	ext4_lblk_t es_lblk;	/* first logical block extent covers */
	ext4_lblk_t es_lblk;	/* first logical block extent covers */
@@ -58,6 +66,7 @@ extern void ext4_es_find_delayed_extent(struct inode *inode, ext4_lblk_t lblk,
					struct extent_status *es);
					struct extent_status *es);
extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
				 struct extent_status *es);
				 struct extent_status *es);
extern int ext4_es_zeroout(struct inode *inode, struct ext4_extent *ex);


static inline int ext4_es_is_written(struct extent_status *es)
static inline int ext4_es_is_written(struct extent_status *es)
{
{
+2 −2
Original line number Original line Diff line number Diff line
@@ -324,8 +324,8 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
}
}


struct orlov_stats {
struct orlov_stats {
	__u64 free_clusters;
	__u32 free_inodes;
	__u32 free_inodes;
	__u32 free_clusters;
	__u32 used_dirs;
	__u32 used_dirs;
};
};


@@ -342,7 +342,7 @@ static void get_orlov_stats(struct super_block *sb, ext4_group_t g,


	if (flex_size > 1) {
	if (flex_size > 1) {
		stats->free_inodes = atomic_read(&flex_group[g].free_inodes);
		stats->free_inodes = atomic_read(&flex_group[g].free_inodes);
		stats->free_clusters = atomic_read(&flex_group[g].free_clusters);
		stats->free_clusters = atomic64_read(&flex_group[g].free_clusters);
		stats->used_dirs = atomic_read(&flex_group[g].used_dirs);
		stats->used_dirs = atomic_read(&flex_group[g].used_dirs);
		return;
		return;
	}
	}
Loading