LKML Archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path
@ 2024-07-10  4:06 libaokun
  2024-07-10  4:06 ` [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path' libaokun
                   ` (20 more replies)
  0 siblings, 21 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

Hi all!

This patch series is a hardening of ext4 extents path related code.
The following is a brief overview of the patches, see the patches for
more details.

Patch 1-2: Refactor ext4_ext_rm_idx() as suggested by Jan, and add
appropriate error handling branches to ext4_ext_rm_idx() and
ext4_ext_correct_indexes() to avoid inconsistent extents tree.
 PS: This comes from the previous work of my colleague zhanchengbin
 (see link), who is no longer in charge of these and I have taken over.
 Link: https://lore.kernel.org/r/20230213080514.535568-3-zhanchengbin1@huawei.com/

Patch 3-4: Fix an issue that caused p_bh to be released twice if it wasn't
set to NULL after path->p_bh was released. And add a helper function after
the quick fix to prevent this from happening again.

Patch 5-7: Quick fixes for use-after-free and double-free problems caused
by mixing path(pointer to an extent path) and ppath(pointer to an extent
path pointer).

Patch 8-19: Now the use of path and ppath is so confusing that we can
trigger use-after-free or double-free by accessing a stale pointer, or
we can get a memory leak by forgetting to update ppath. And it's very
difficult to read the code. So to make the code more readable, get rid
of ppath and pass path between functions uniformly to avoid these risks.

Patch 20: Reduces the consumption of unnecessary memory operations by
avoiding repetitive allocation and release paths.

"kvm-xfstests -c ext4/all -g auto" has been executed with no new failures.

Comments and questions are, as always, welcome.
Please let me know what you think.

Thanks,
Baokun

Baokun Li (20):
  ext4: refactor ext4_ext_rm_idx() to index 'path'
  ext4: prevent partial update of the extents path
  ext4: fix double brelse() the buffer of the extents path
  ext4: add new ext4_ext_path_brelse() helper
  ext4: fix slab-use-after-free in ext4_split_extent_at()
  ext4: avoid use-after-free in ext4_ext_show_leaf()
  ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free
  ext4: get rid of ppath in ext4_find_extent()
  ext4: get rid of ppath in get_ext_path()
  ext4: get rid of ppath in ext4_ext_create_new_leaf()
  ext4: get rid of ppath in ext4_ext_insert_extent()
  ext4: get rid of ppath in ext4_split_extent_at()
  ext4: get rid of ppath in ext4_force_split_extent_at()
  ext4: get rid of ppath in ext4_split_extent()
  ext4: get rid of ppath in ext4_split_convert_extents()
  ext4: get rid of ppath in ext4_convert_unwritten_extents_endio()
  ext4: get rid of ppath in ext4_ext_convert_to_initialized()
  ext4: get rid of ppath in ext4_ext_handle_unwritten_extents()
  ext4: get rid of ppath in convert_initialized_extent()
  ext4: avoid unnecessary extent path frees and allocations

 fs/ext4/ext4.h        |   9 +-
 fs/ext4/extents.c     | 746 +++++++++++++++++++++++-------------------
 fs/ext4/fast_commit.c |  17 +-
 fs/ext4/migrate.c     |   5 +-
 fs/ext4/move_extent.c |  36 +-
 5 files changed, 439 insertions(+), 374 deletions(-)

-- 
2.39.2


^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path'
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-24 18:44   ` Jan Kara
  2024-07-25  9:14   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 02/20] ext4: prevent partial update of the extents path libaokun
                   ` (19 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

As suggested by Honza in Link,modify ext4_ext_rm_idx() to leave 'path'
alone and just index it like ext4_ext_correct_indexes() does it. This
facilitates adding error handling later. No functional changes.

Suggested-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/all/20230216130305.nrbtd42tppxhbynn@quack3/
Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index e067f2dd0335..bff3666c891a 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2279,27 +2279,26 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
 {
 	int err;
 	ext4_fsblk_t leaf;
+	int k = depth - 1;
 
 	/* free index block */
-	depth--;
-	path = path + depth;
-	leaf = ext4_idx_pblock(path->p_idx);
-	if (unlikely(path->p_hdr->eh_entries == 0)) {
-		EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0");
+	leaf = ext4_idx_pblock(path[k].p_idx);
+	if (unlikely(path[k].p_hdr->eh_entries == 0)) {
+		EXT4_ERROR_INODE(inode, "path[%d].p_hdr->eh_entries == 0", k);
 		return -EFSCORRUPTED;
 	}
-	err = ext4_ext_get_access(handle, inode, path);
+	err = ext4_ext_get_access(handle, inode, path + k);
 	if (err)
 		return err;
 
-	if (path->p_idx != EXT_LAST_INDEX(path->p_hdr)) {
-		int len = EXT_LAST_INDEX(path->p_hdr) - path->p_idx;
+	if (path[k].p_idx != EXT_LAST_INDEX(path[k].p_hdr)) {
+		int len = EXT_LAST_INDEX(path[k].p_hdr) - path[k].p_idx;
 		len *= sizeof(struct ext4_extent_idx);
-		memmove(path->p_idx, path->p_idx + 1, len);
+		memmove(path[k].p_idx, path[k].p_idx + 1, len);
 	}
 
-	le16_add_cpu(&path->p_hdr->eh_entries, -1);
-	err = ext4_ext_dirty(handle, inode, path);
+	le16_add_cpu(&path[k].p_hdr->eh_entries, -1);
+	err = ext4_ext_dirty(handle, inode, path + k);
 	if (err)
 		return err;
 	ext_debug(inode, "index is empty, remove it, free block %llu\n", leaf);
@@ -2308,15 +2307,14 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
 	ext4_free_blocks(handle, inode, NULL, leaf, 1,
 			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
 
-	while (--depth >= 0) {
-		if (path->p_idx != EXT_FIRST_INDEX(path->p_hdr))
+	while (--k >= 0) {
+		if (path[k + 1].p_idx != EXT_FIRST_INDEX(path[k + 1].p_hdr))
 			break;
-		path--;
-		err = ext4_ext_get_access(handle, inode, path);
+		err = ext4_ext_get_access(handle, inode, path + k);
 		if (err)
 			break;
-		path->p_idx->ei_block = (path+1)->p_idx->ei_block;
-		err = ext4_ext_dirty(handle, inode, path);
+		path[k].p_idx->ei_block = path[k + 1].p_idx->ei_block;
+		err = ext4_ext_dirty(handle, inode, path + k);
 		if (err)
 			break;
 	}
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
  2024-07-10  4:06 ` [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path' libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-14 15:42   ` Ojaswin Mujoo
  2024-07-24 18:53   ` Jan Kara
  2024-07-10  4:06 ` [PATCH 03/20] ext4: fix double brelse() the buffer " libaokun
                   ` (18 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li, zhanchengbin

From: Baokun Li <libaokun1@huawei.com>

In ext4_ext_rm_idx() and ext4_ext_correct_indexes(), there is no proper
rollback of already executed updates when updating a level of the extents
path fails, so we may get an inconsistent extents tree, which may trigger
some bad things in errors=continue mode.

Hence clear the verified bit of modified extents buffers if the tree fails
to be updated in ext4_ext_rm_idx() or ext4_ext_correct_indexes(), which
forces the extents buffers to be checked in ext4_valid_extent_entries(),
ensuring that the extents tree is consistent.

Signed-off-by: zhanchengbin <zhanchengbin1@huawei.com>
Link: https://lore.kernel.org/r/20230213080514.535568-3-zhanchengbin1@huawei.com/
Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index bff3666c891a..4d589d34b30e 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1749,12 +1749,23 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
 			break;
 		err = ext4_ext_get_access(handle, inode, path + k);
 		if (err)
-			break;
+			goto clean;
 		path[k].p_idx->ei_block = border;
 		err = ext4_ext_dirty(handle, inode, path + k);
 		if (err)
-			break;
+			goto clean;
 	}
+	return 0;
+
+clean:
+	/*
+	 * The path[k].p_bh is either unmodified or with no verified bit
+	 * set (see ext4_ext_get_access()). So just clear the verified bit
+	 * of the successfully modified extents buffers, which will force
+	 * these extents to be checked to avoid using inconsistent data.
+	 */
+	while (++k < depth)
+		clear_buffer_verified(path[k].p_bh);
 
 	return err;
 }
@@ -2312,12 +2323,24 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
 			break;
 		err = ext4_ext_get_access(handle, inode, path + k);
 		if (err)
-			break;
+			goto clean;
 		path[k].p_idx->ei_block = path[k + 1].p_idx->ei_block;
 		err = ext4_ext_dirty(handle, inode, path + k);
 		if (err)
-			break;
+			goto clean;
 	}
+	return 0;
+
+clean:
+	/*
+	 * The path[k].p_bh is either unmodified or with no verified bit
+	 * set (see ext4_ext_get_access()). So just clear the verified bit
+	 * of the successfully modified extents buffers, which will force
+	 * these extents to be checked to avoid using inconsistent data.
+	 */
+	while (++k < depth)
+		clear_buffer_verified(path[k].p_bh);
+
 	return err;
 }
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 03/20] ext4: fix double brelse() the buffer of the extents path
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
  2024-07-10  4:06 ` [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path' libaokun
  2024-07-10  4:06 ` [PATCH 02/20] ext4: prevent partial update of the extents path libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-24 19:01   ` Jan Kara
  2024-07-26 11:45   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 04/20] ext4: add new ext4_ext_path_brelse() helper libaokun
                   ` (17 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li, stable

From: Baokun Li <libaokun1@huawei.com>

In ext4_ext_try_to_merge_up(), set path[1].p_bh to NULL after it has been
released, otherwise it may be released twice.

An example of what triggers this is as follows:

  split2    map    split1
|--------|-------|--------|

ext4_ext_map_blocks
 ext4_ext_handle_unwritten_extents
  ext4_split_convert_extents
   // path->p_depth == 0
   ext4_split_extent
     // 1. do split1
     ext4_split_extent_at
       ext4_ext_insert_extent
         ext4_ext_create_new_leaf
           ext4_ext_grow_indepth
             le16_add_cpu(&neh->eh_depth, 1)
           ext4_find_extent
             path->p_depth = 1
         ext4_ext_try_to_merge
           ext4_ext_try_to_merge_up
             path->p_depth = 0
             brelse(path[1].p_bh)  ---> not set to NULL here
     // 2. update path
     ext4_find_extent
     // 3. do split2
     ext4_split_extent_at
       ext4_ext_insert_extent
         ext4_ext_create_new_leaf
           ext4_ext_grow_indepth
             le16_add_cpu(&neh->eh_depth, 1)
           ext4_find_extent
             path[0].p_bh = NULL;
             path->p_depth = 1
             read_extent_tree_block  ---> return err
             // path[1].p_bh is still the old value
 ext4_free_ext_path
  ext4_ext_drop_refs
   // path->p_depth == 1
   brelse(path[1].p_bh)  ---> brelse a buffer twice

Finally got the following WARRNING when removing the buffer from lru:

============================================
VFS: brelse: Trying to free free buffer
WARNING: CPU: 2 PID: 72 at fs/buffer.c:1241 __brelse+0x58/0x90
CPU: 2 PID: 72 Comm: kworker/u19:1 Not tainted 6.9.0-dirty #716
RIP: 0010:__brelse+0x58/0x90
Call Trace:
 <TASK>
 __find_get_block+0x6e7/0x810
 bdev_getblk+0x2b/0x480
 __ext4_get_inode_loc+0x48a/0x1240
 ext4_get_inode_loc+0xb2/0x150
 ext4_reserve_inode_write+0xb7/0x230
 __ext4_mark_inode_dirty+0x144/0x6a0
 ext4_ext_insert_extent+0x9c8/0x3230
 ext4_ext_map_blocks+0xf45/0x2dc0
 ext4_map_blocks+0x724/0x1700
 ext4_do_writepages+0x12d6/0x2a70
[...]
============================================

Fixes: ecb94f5fdf4b ("ext4: collapse a single extent tree block into the inode if possible")
Cc: stable@kernel.org
Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 4d589d34b30e..657baf3991c1 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1888,6 +1888,7 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
 	path[0].p_hdr->eh_max = cpu_to_le16(max_root);
 
 	brelse(path[1].p_bh);
+	path[1].p_bh = NULL;
 	ext4_free_blocks(handle, inode, NULL, blk, 1,
 			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 04/20] ext4: add new ext4_ext_path_brelse() helper
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (2 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 03/20] ext4: fix double brelse() the buffer " libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-24 19:02   ` Jan Kara
  2024-07-26 11:53   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at() libaokun
                   ` (16 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

Add ext4_ext_path_brelse() helper function to reduce duplicate code
and ensure that path->p_bh is set to NULL after it is released.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 657baf3991c1..6e5b5baf3aa6 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -106,6 +106,12 @@ static int ext4_ext_trunc_restart_fn(struct inode *inode, int *dropped)
 	return 0;
 }
 
+static inline void ext4_ext_path_brelse(struct ext4_ext_path *path)
+{
+	brelse(path->p_bh);
+	path->p_bh = NULL;
+}
+
 static void ext4_ext_drop_refs(struct ext4_ext_path *path)
 {
 	int depth, i;
@@ -113,10 +119,8 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
 	if (!path)
 		return;
 	depth = path->p_depth;
-	for (i = 0; i <= depth; i++, path++) {
-		brelse(path->p_bh);
-		path->p_bh = NULL;
-	}
+	for (i = 0; i <= depth; i++, path++)
+		ext4_ext_path_brelse(path);
 }
 
 void ext4_free_ext_path(struct ext4_ext_path *path)
@@ -635,8 +639,7 @@ int ext4_ext_precache(struct inode *inode)
 		 */
 		if ((i == depth) ||
 		    path[i].p_idx > EXT_LAST_INDEX(path[i].p_hdr)) {
-			brelse(path[i].p_bh);
-			path[i].p_bh = NULL;
+			ext4_ext_path_brelse(path + i);
 			i--;
 			continue;
 		}
@@ -1887,8 +1890,7 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
 		(path[1].p_ext - EXT_FIRST_EXTENT(path[1].p_hdr));
 	path[0].p_hdr->eh_max = cpu_to_le16(max_root);
 
-	brelse(path[1].p_bh);
-	path[1].p_bh = NULL;
+	ext4_ext_path_brelse(path + 1);
 	ext4_free_blocks(handle, inode, NULL, blk, 1,
 			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
 }
@@ -2956,8 +2958,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 			err = ext4_ext_rm_leaf(handle, inode, path,
 					       &partial, start, end);
 			/* root level has p_bh == NULL, brelse() eats this */
-			brelse(path[i].p_bh);
-			path[i].p_bh = NULL;
+			ext4_ext_path_brelse(path + i);
 			i--;
 			continue;
 		}
@@ -3019,8 +3020,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 				err = ext4_ext_rm_idx(handle, inode, path, i);
 			}
 			/* root level has p_bh == NULL, brelse() eats this */
-			brelse(path[i].p_bh);
-			path[i].p_bh = NULL;
+			ext4_ext_path_brelse(path + i);
 			i--;
 			ext_debug(inode, "return to level %d\n", i);
 		}
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (3 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 04/20] ext4: add new ext4_ext_path_brelse() helper libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-24 19:13   ` Jan Kara
  2024-07-27 10:36   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf() libaokun
                   ` (15 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li, stable

From: Baokun Li <libaokun1@huawei.com>

We hit the following use-after-free:

==================================================================
BUG: KASAN: slab-use-after-free in ext4_split_extent_at+0xba8/0xcc0
Read of size 2 at addr ffff88810548ed08 by task kworker/u20:0/40
CPU: 0 PID: 40 Comm: kworker/u20:0 Not tainted 6.9.0-dirty #724
Call Trace:
 <TASK>
 kasan_report+0x93/0xc0
 ext4_split_extent_at+0xba8/0xcc0
 ext4_split_extent.isra.0+0x18f/0x500
 ext4_split_convert_extents+0x275/0x750
 ext4_ext_handle_unwritten_extents+0x73e/0x1580
 ext4_ext_map_blocks+0xe20/0x2dc0
 ext4_map_blocks+0x724/0x1700
 ext4_do_writepages+0x12d6/0x2a70
[...]

Allocated by task 40:
 __kmalloc_noprof+0x1ac/0x480
 ext4_find_extent+0xf3b/0x1e70
 ext4_ext_map_blocks+0x188/0x2dc0
 ext4_map_blocks+0x724/0x1700
 ext4_do_writepages+0x12d6/0x2a70
[...]

Freed by task 40:
 kfree+0xf1/0x2b0
 ext4_find_extent+0xa71/0x1e70
 ext4_ext_insert_extent+0xa22/0x3260
 ext4_split_extent_at+0x3ef/0xcc0
 ext4_split_extent.isra.0+0x18f/0x500
 ext4_split_convert_extents+0x275/0x750
 ext4_ext_handle_unwritten_extents+0x73e/0x1580
 ext4_ext_map_blocks+0xe20/0x2dc0
 ext4_map_blocks+0x724/0x1700
 ext4_do_writepages+0x12d6/0x2a70
[...]
==================================================================

The flow of issue triggering is as follows:

ext4_split_extent_at
  path = *ppath
  ext4_ext_insert_extent(ppath)
    ext4_ext_create_new_leaf(ppath)
      ext4_find_extent(orig_path)
        path = *orig_path
        read_extent_tree_block
          // return -ENOMEM or -EIO
        ext4_free_ext_path(path)
          kfree(path)
        *orig_path = NULL
  a. If err is -ENOMEM:
  ext4_ext_dirty(path + path->p_depth)
  // path use-after-free !!!
  b. If err is -EIO and we have EXT_DEBUG defined:
  ext4_ext_show_leaf(path)
    eh = path[depth].p_hdr
    // path also use-after-free !!!

So when trying to zeroout or fix the extent length, call ext4_find_extent()
to update the path.

In addition we use *ppath directly as an ext4_ext_show_leaf() input to
avoid possible use-after-free when EXT_DEBUG is defined, and to avoid
unnecessary path updates.

Fixes: dfe5080939ea ("ext4: drop EXT4_EX_NOFREE_ON_ERR from rest of extents handling code")
Cc: stable@kernel.org
Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 6e5b5baf3aa6..3a70a0739af8 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3252,6 +3252,25 @@ static int ext4_split_extent_at(handle_t *handle,
 	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
 		goto out;
 
+	/*
+	 * Update path is required because previous ext4_ext_insert_extent()
+	 * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
+	 * guarantees that ext4_find_extent() will not return -ENOMEM,
+	 * otherwise -ENOMEM will cause a retry in do_writepages(), and a
+	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
+	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
+	 */
+	path = ext4_find_extent(inode, ee_block, ppath,
+				flags | EXT4_EX_NOFAIL);
+	if (IS_ERR(path)) {
+		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
+				 split, PTR_ERR(path));
+		return PTR_ERR(path);
+	}
+	depth = ext_depth(inode);
+	ex = path[depth].p_ext;
+	*ppath = path;
+
 	if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
 		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
 			if (split_flag & EXT4_EXT_DATA_VALID1) {
@@ -3304,7 +3323,7 @@ static int ext4_split_extent_at(handle_t *handle,
 	ext4_ext_dirty(handle, inode, path + path->p_depth);
 	return err;
 out:
-	ext4_ext_show_leaf(inode, path);
+	ext4_ext_show_leaf(inode, *ppath);
 	return err;
 }
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (4 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-24 19:16   ` Jan Kara
  2024-07-27 10:43   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 07/20] ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free libaokun
                   ` (14 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

In ext4_find_extent(), path may be freed by error or be reallocated, so
using a previously saved *ppath may have been freed and thus may trigger
use-after-free, as follows:

ext4_split_extent
  path = *ppath;
  ext4_split_extent_at(ppath)
  path = ext4_find_extent(ppath)
  ext4_split_extent_at(ppath)
    // ext4_find_extent fails to free path
    // but zeroout succeeds
  ext4_ext_show_leaf(inode, path)
    eh = path[depth].p_hdr
    // path use-after-free !!!

Similar to ext4_split_extent_at(), we use *ppath directly as an input to
ext4_ext_show_leaf(). Fix a spelling error by the way.

Same problem in ext4_ext_handle_unwritten_extents(). Since 'path' is only
used in ext4_ext_show_leaf(), remove 'path' and use *ppath directly.

This issue is triggered only when EXT_DEBUG is defined and therefore does
not affect functionality.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 3a70a0739af8..1660434fbfc7 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3328,7 +3328,7 @@ static int ext4_split_extent_at(handle_t *handle,
 }
 
 /*
- * ext4_split_extents() splits an extent and mark extent which is covered
+ * ext4_split_extent() splits an extent and mark extent which is covered
  * by @map as split_flags indicates
  *
  * It may result in splitting the extent into multiple extents (up to three)
@@ -3404,7 +3404,7 @@ static int ext4_split_extent(handle_t *handle,
 			goto out;
 	}
 
-	ext4_ext_show_leaf(inode, path);
+	ext4_ext_show_leaf(inode, *ppath);
 out:
 	return err ? err : allocated;
 }
@@ -3869,14 +3869,13 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 			struct ext4_ext_path **ppath, int flags,
 			unsigned int allocated, ext4_fsblk_t newblock)
 {
-	struct ext4_ext_path __maybe_unused *path = *ppath;
 	int ret = 0;
 	int err = 0;
 
 	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
 		  (unsigned long long)map->m_lblk, map->m_len, flags,
 		  allocated);
-	ext4_ext_show_leaf(inode, path);
+	ext4_ext_show_leaf(inode, *ppath);
 
 	/*
 	 * When writing into unwritten space, we should not fail to
@@ -3973,7 +3972,7 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 	if (allocated > map->m_len)
 		allocated = map->m_len;
 	map->m_len = allocated;
-	ext4_ext_show_leaf(inode, path);
+	ext4_ext_show_leaf(inode, *ppath);
 out2:
 	return err ? err : allocated;
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 07/20] ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (5 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 10:31   ` Jan Kara
  2024-07-27 11:18   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent() libaokun
                   ` (13 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li, stable

From: Baokun Li <libaokun1@huawei.com>

When calling ext4_force_split_extent_at() in ext4_ext_replay_update_ex(),
the 'ppath' is updated but it is the 'path' that is freed, thus potentially
triggering a double-free in the following process:

ext4_ext_replay_update_ex
  ppath = path
  ext4_force_split_extent_at(&ppath)
    ext4_split_extent_at
      ext4_ext_insert_extent
        ext4_ext_create_new_leaf
          ext4_ext_grow_indepth
            ext4_find_extent
              if (depth > path[0].p_maxdepth)
                kfree(path)                 ---> path First freed
                *orig_path = path = NULL    ---> null ppath
  kfree(path)                               ---> path double-free !!!

So drop the unnecessary ppath and use path directly to avoid this problem.
And use ext4_find_extent() directly to update path, avoiding unnecessary
memory allocation and freeing. Also, propagate the error returned by
ext4_find_extent() instead of using strange error codes.

Fixes: 8016e29f4362 ("ext4: fast commit recovery path")
Cc: stable@kernel.org
Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 1660434fbfc7..b1cfce5b57d2 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -5920,7 +5920,7 @@ int ext4_clu_mapped(struct inode *inode, ext4_lblk_t lclu)
 int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
 			      int len, int unwritten, ext4_fsblk_t pblk)
 {
-	struct ext4_ext_path *path = NULL, *ppath;
+	struct ext4_ext_path *path;
 	struct ext4_extent *ex;
 	int ret;
 
@@ -5936,30 +5936,29 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
 	if (le32_to_cpu(ex->ee_block) != start ||
 		ext4_ext_get_actual_len(ex) != len) {
 		/* We need to split this extent to match our extent first */
-		ppath = path;
 		down_write(&EXT4_I(inode)->i_data_sem);
-		ret = ext4_force_split_extent_at(NULL, inode, &ppath, start, 1);
+		ret = ext4_force_split_extent_at(NULL, inode, &path, start, 1);
 		up_write(&EXT4_I(inode)->i_data_sem);
 		if (ret)
 			goto out;
-		kfree(path);
-		path = ext4_find_extent(inode, start, NULL, 0);
+
+		path = ext4_find_extent(inode, start, &path, 0);
 		if (IS_ERR(path))
-			return -1;
-		ppath = path;
+			return PTR_ERR(path);
 		ex = path[path->p_depth].p_ext;
 		WARN_ON(le32_to_cpu(ex->ee_block) != start);
+
 		if (ext4_ext_get_actual_len(ex) != len) {
 			down_write(&EXT4_I(inode)->i_data_sem);
-			ret = ext4_force_split_extent_at(NULL, inode, &ppath,
+			ret = ext4_force_split_extent_at(NULL, inode, &path,
 							 start + len, 1);
 			up_write(&EXT4_I(inode)->i_data_sem);
 			if (ret)
 				goto out;
-			kfree(path);
-			path = ext4_find_extent(inode, start, NULL, 0);
+
+			path = ext4_find_extent(inode, start, &path, 0);
 			if (IS_ERR(path))
-				return -EINVAL;
+				return PTR_ERR(path);
 			ex = path[path->p_depth].p_ext;
 		}
 	}
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (6 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 07/20] ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 10:38   ` Jan Kara
  2024-07-30 10:03   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 09/20] ext4: get rid of ppath in get_ext_path() libaokun
                   ` (12 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

Getting rid of ppath in ext4_find_extent() requires its caller to update
ppath. These ppaths will also be dropped later. No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/ext4.h        |  2 +-
 fs/ext4/extents.c     | 52 +++++++++++++++++++++++--------------------
 fs/ext4/move_extent.c |  6 ++---
 3 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 8007abd4972d..cbe8d6062c52 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3714,7 +3714,7 @@ extern int ext4_ext_insert_extent(handle_t *, struct inode *,
 				  struct ext4_ext_path **,
 				  struct ext4_extent *, int);
 extern struct ext4_ext_path *ext4_find_extent(struct inode *, ext4_lblk_t,
-					      struct ext4_ext_path **,
+					      struct ext4_ext_path *,
 					      int flags);
 extern void ext4_free_ext_path(struct ext4_ext_path *);
 extern int ext4_ext_check_inode(struct inode *inode);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index b1cfce5b57d2..5217e6f53467 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -884,11 +884,10 @@ void ext4_ext_tree_init(handle_t *handle, struct inode *inode)
 
 struct ext4_ext_path *
 ext4_find_extent(struct inode *inode, ext4_lblk_t block,
-		 struct ext4_ext_path **orig_path, int flags)
+		 struct ext4_ext_path *path, int flags)
 {
 	struct ext4_extent_header *eh;
 	struct buffer_head *bh;
-	struct ext4_ext_path *path = orig_path ? *orig_path : NULL;
 	short int depth, i, ppos = 0;
 	int ret;
 	gfp_t gfp_flags = GFP_NOFS;
@@ -909,7 +908,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
 		ext4_ext_drop_refs(path);
 		if (depth > path[0].p_maxdepth) {
 			kfree(path);
-			*orig_path = path = NULL;
+			path = NULL;
 		}
 	}
 	if (!path) {
@@ -964,8 +963,6 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
 
 err:
 	ext4_free_ext_path(path);
-	if (orig_path)
-		*orig_path = NULL;
 	return ERR_PTR(ret);
 }
 
@@ -1430,7 +1427,7 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
 		/* refill path */
 		path = ext4_find_extent(inode,
 				    (ext4_lblk_t)le32_to_cpu(newext->ee_block),
-				    ppath, gb_flags);
+				    path, gb_flags);
 		if (IS_ERR(path))
 			err = PTR_ERR(path);
 	} else {
@@ -1442,7 +1439,7 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
 		/* refill path */
 		path = ext4_find_extent(inode,
 				   (ext4_lblk_t)le32_to_cpu(newext->ee_block),
-				    ppath, gb_flags);
+				    path, gb_flags);
 		if (IS_ERR(path)) {
 			err = PTR_ERR(path);
 			goto out;
@@ -1458,8 +1455,8 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
 			goto repeat;
 		}
 	}
-
 out:
+	*ppath = IS_ERR(path) ? NULL : path;
 	return err;
 }
 
@@ -3260,11 +3257,12 @@ static int ext4_split_extent_at(handle_t *handle,
 	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
 	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
 	 */
-	path = ext4_find_extent(inode, ee_block, ppath,
+	path = ext4_find_extent(inode, ee_block, *ppath,
 				flags | EXT4_EX_NOFAIL);
 	if (IS_ERR(path)) {
 		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
 				 split, PTR_ERR(path));
+		*ppath = NULL;
 		return PTR_ERR(path);
 	}
 	depth = ext_depth(inode);
@@ -3379,9 +3377,12 @@ static int ext4_split_extent(handle_t *handle,
 	 * Update path is required because previous ext4_split_extent_at() may
 	 * result in split of original leaf or extent zeroout.
 	 */
-	path = ext4_find_extent(inode, map->m_lblk, ppath, flags);
-	if (IS_ERR(path))
+	path = ext4_find_extent(inode, map->m_lblk, *ppath, flags);
+	if (IS_ERR(path)) {
+		*ppath = NULL;
 		return PTR_ERR(path);
+	}
+	*ppath = path;
 	depth = ext_depth(inode);
 	ex = path[depth].p_ext;
 	if (!ex) {
@@ -3767,9 +3768,12 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
 						 EXT4_GET_BLOCKS_CONVERT);
 		if (err < 0)
 			return err;
-		path = ext4_find_extent(inode, map->m_lblk, ppath, 0);
-		if (IS_ERR(path))
+		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
+		if (IS_ERR(path)) {
+			*ppath = NULL;
 			return PTR_ERR(path);
+		}
+		*ppath = path;
 		depth = ext_depth(inode);
 		ex = path[depth].p_ext;
 	}
@@ -3825,9 +3829,12 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
 				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN);
 		if (err < 0)
 			return err;
-		path = ext4_find_extent(inode, map->m_lblk, ppath, 0);
-		if (IS_ERR(path))
+		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
+		if (IS_ERR(path)) {
+			*ppath = NULL;
 			return PTR_ERR(path);
+		}
+		*ppath = path;
 		depth = ext_depth(inode);
 		ex = path[depth].p_ext;
 		if (!ex) {
@@ -5224,7 +5231,7 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
 	* won't be shifted beyond EXT_MAX_BLOCKS.
 	*/
 	if (SHIFT == SHIFT_LEFT) {
-		path = ext4_find_extent(inode, start - 1, &path,
+		path = ext4_find_extent(inode, start - 1, path,
 					EXT4_EX_NOCACHE);
 		if (IS_ERR(path))
 			return PTR_ERR(path);
@@ -5273,7 +5280,7 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
 	 * becomes NULL to indicate the end of the loop.
 	 */
 	while (iterator && start <= stop) {
-		path = ext4_find_extent(inode, *iterator, &path,
+		path = ext4_find_extent(inode, *iterator, path,
 					EXT4_EX_NOCACHE);
 		if (IS_ERR(path))
 			return PTR_ERR(path);
@@ -5854,11 +5861,8 @@ int ext4_clu_mapped(struct inode *inode, ext4_lblk_t lclu)
 
 	/* search for the extent closest to the first block in the cluster */
 	path = ext4_find_extent(inode, EXT4_C2B(sbi, lclu), NULL, 0);
-	if (IS_ERR(path)) {
-		err = PTR_ERR(path);
-		path = NULL;
-		goto out;
-	}
+	if (IS_ERR(path))
+		return PTR_ERR(path);
 
 	depth = ext_depth(inode);
 
@@ -5942,7 +5946,7 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
 		if (ret)
 			goto out;
 
-		path = ext4_find_extent(inode, start, &path, 0);
+		path = ext4_find_extent(inode, start, path, 0);
 		if (IS_ERR(path))
 			return PTR_ERR(path);
 		ex = path[path->p_depth].p_ext;
@@ -5956,7 +5960,7 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
 			if (ret)
 				goto out;
 
-			path = ext4_find_extent(inode, start, &path, 0);
+			path = ext4_find_extent(inode, start, path, 0);
 			if (IS_ERR(path))
 				return PTR_ERR(path);
 			ex = path[path->p_depth].p_ext;
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 204f53b23622..b0ab19a913bf 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -26,14 +26,14 @@ static inline int
 get_ext_path(struct inode *inode, ext4_lblk_t lblock,
 		struct ext4_ext_path **ppath)
 {
-	struct ext4_ext_path *path;
+	struct ext4_ext_path *path = *ppath;
 
-	path = ext4_find_extent(inode, lblock, ppath, EXT4_EX_NOCACHE);
+	*ppath = NULL;
+	path = ext4_find_extent(inode, lblock, path, EXT4_EX_NOCACHE);
 	if (IS_ERR(path))
 		return PTR_ERR(path);
 	if (path[ext_depth(inode)].p_ext == NULL) {
 		ext4_free_ext_path(path);
-		*ppath = NULL;
 		return -ENODATA;
 	}
 	*ppath = path;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 09/20] ext4: get rid of ppath in get_ext_path()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (7 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 10:41   ` Jan Kara
  2024-08-01  7:16   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf() libaokun
                   ` (11 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

After getting rid of ppath in get_ext_path(), its caller may pass an error
pointer to ext4_free_ext_path(), so it needs to teach ext4_free_ext_path()
and ext4_ext_drop_refs() to skip the error pointer. No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c     |  5 +++--
 fs/ext4/move_extent.c | 34 +++++++++++++++++-----------------
 2 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 5217e6f53467..6dfb5d03e197 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -116,7 +116,7 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
 {
 	int depth, i;
 
-	if (!path)
+	if (IS_ERR_OR_NULL(path))
 		return;
 	depth = path->p_depth;
 	for (i = 0; i <= depth; i++, path++)
@@ -125,6 +125,8 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
 
 void ext4_free_ext_path(struct ext4_ext_path *path)
 {
+	if (IS_ERR_OR_NULL(path))
+		return;
 	ext4_ext_drop_refs(path);
 	kfree(path);
 }
@@ -4191,7 +4193,6 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 	path = ext4_find_extent(inode, map->m_lblk, NULL, 0);
 	if (IS_ERR(path)) {
 		err = PTR_ERR(path);
-		path = NULL;
 		goto out;
 	}
 
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index b0ab19a913bf..a7186d63725a 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -17,27 +17,23 @@
  * get_ext_path() - Find an extent path for designated logical block number.
  * @inode:	inode to be searched
  * @lblock:	logical block number to find an extent path
- * @ppath:	pointer to an extent path pointer (for output)
+ * @path:	pointer to an extent path
  *
- * ext4_find_extent wrapper. Return 0 on success, or a negative error value
- * on failure.
+ * ext4_find_extent wrapper. Return an extent path pointer on success,
+ * or an error pointer on failure.
  */
-static inline int
+static inline struct ext4_ext_path *
 get_ext_path(struct inode *inode, ext4_lblk_t lblock,
-		struct ext4_ext_path **ppath)
+	     struct ext4_ext_path *path)
 {
-	struct ext4_ext_path *path = *ppath;
-
-	*ppath = NULL;
 	path = ext4_find_extent(inode, lblock, path, EXT4_EX_NOCACHE);
 	if (IS_ERR(path))
-		return PTR_ERR(path);
+		return path;
 	if (path[ext_depth(inode)].p_ext == NULL) {
 		ext4_free_ext_path(path);
-		return -ENODATA;
+		return ERR_PTR(-ENODATA);
 	}
-	*ppath = path;
-	return 0;
+	return path;
 }
 
 /**
@@ -95,9 +91,11 @@ mext_check_coverage(struct inode *inode, ext4_lblk_t from, ext4_lblk_t count,
 	int ret = 0;
 	ext4_lblk_t last = from + count;
 	while (from < last) {
-		*err = get_ext_path(inode, from, &path);
-		if (*err)
-			goto out;
+		path = get_ext_path(inode, from, path);
+		if (IS_ERR(path)) {
+			*err = PTR_ERR(path);
+			return ret;
+		}
 		ext = path[ext_depth(inode)].p_ext;
 		if (unwritten != ext4_ext_is_unwritten(ext))
 			goto out;
@@ -624,9 +622,11 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
 		int offset_in_page;
 		int unwritten, cur_len;
 
-		ret = get_ext_path(orig_inode, o_start, &path);
-		if (ret)
+		path = get_ext_path(orig_inode, o_start, path);
+		if (IS_ERR(path)) {
+			ret = PTR_ERR(path);
 			goto out;
+		}
 		ex = path[path->p_depth].p_ext;
 		cur_blk = le32_to_cpu(ex->ee_block);
 		cur_len = ext4_ext_get_actual_len(ex);
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (8 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 09/20] ext4: get rid of ppath in get_ext_path() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 10:46   ` Jan Kara
  2024-08-02  7:34   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 11/20] ext4: get rid of ppath in ext4_ext_insert_extent() libaokun
                   ` (10 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
done here:

 * Free the extents path when an error is encountered.
 * Its caller needs to update ppath if it uses ppath.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 41 +++++++++++++++++++++--------------------
 1 file changed, 21 insertions(+), 20 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 6dfb5d03e197..0d6ce9e74b01 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1397,13 +1397,12 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
  * finds empty index and adds new leaf.
  * if no free index is found, then it requests in-depth growing.
  */
-static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
-				    unsigned int mb_flags,
-				    unsigned int gb_flags,
-				    struct ext4_ext_path **ppath,
-				    struct ext4_extent *newext)
+static struct ext4_ext_path *
+ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
+			 unsigned int mb_flags, unsigned int gb_flags,
+			 struct ext4_ext_path *path,
+			 struct ext4_extent *newext)
 {
-	struct ext4_ext_path *path = *ppath;
 	struct ext4_ext_path *curp;
 	int depth, i, err = 0;
 
@@ -1424,28 +1423,24 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
 		 * entry: create all needed subtree and add new leaf */
 		err = ext4_ext_split(handle, inode, mb_flags, path, newext, i);
 		if (err)
-			goto out;
+			goto errout;
 
 		/* refill path */
 		path = ext4_find_extent(inode,
 				    (ext4_lblk_t)le32_to_cpu(newext->ee_block),
 				    path, gb_flags);
-		if (IS_ERR(path))
-			err = PTR_ERR(path);
 	} else {
 		/* tree is full, time to grow in depth */
 		err = ext4_ext_grow_indepth(handle, inode, mb_flags);
 		if (err)
-			goto out;
+			goto errout;
 
 		/* refill path */
 		path = ext4_find_extent(inode,
 				   (ext4_lblk_t)le32_to_cpu(newext->ee_block),
 				    path, gb_flags);
-		if (IS_ERR(path)) {
-			err = PTR_ERR(path);
-			goto out;
-		}
+		if (IS_ERR(path))
+			return path;
 
 		/*
 		 * only first (depth 0 -> 1) produces free space;
@@ -1457,9 +1452,11 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
 			goto repeat;
 		}
 	}
-out:
-	*ppath = IS_ERR(path) ? NULL : path;
-	return err;
+	return path;
+
+errout:
+	ext4_free_ext_path(path);
+	return ERR_PTR(err);
 }
 
 /*
@@ -2112,10 +2109,14 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 	 */
 	if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
 		mb_flags |= EXT4_MB_USE_RESERVED;
-	err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
-				       ppath, newext);
-	if (err)
+	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
+					path, newext);
+	if (IS_ERR(path)) {
+		*ppath = NULL;
+		err = PTR_ERR(path);
 		goto cleanup;
+	}
+	*ppath = path;
 	depth = ext_depth(inode);
 	eh = path[depth].p_hdr;
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 11/20] ext4: get rid of ppath in ext4_ext_insert_extent()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (9 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 10:59   ` Jan Kara
  2024-08-02  8:01   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at() libaokun
                   ` (9 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_ext_insert_extent(), the following is done
here:

 * Free the extents path when an error is encountered.
 * Its caller needs to update ppath if it uses ppath.
 * Free path when npath is used, free npath when it is not used.
 * The got_allocated_blocks label in ext4_ext_map_blocks() does not
   update err now, so err is updated to 0 if the err returned by
   ext4_ext_search_right() is greater than 0 and is about to enter
   got_allocated_blocks.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/ext4.h        |  7 ++--
 fs/ext4/extents.c     | 88 ++++++++++++++++++++++++-------------------
 fs/ext4/fast_commit.c |  8 ++--
 fs/ext4/migrate.c     |  5 ++-
 4 files changed, 61 insertions(+), 47 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index cbe8d6062c52..53b4c1f454e6 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3710,9 +3710,10 @@ extern int ext4_map_blocks(handle_t *handle, struct inode *inode,
 extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode,
 						   int num,
 						   struct ext4_ext_path *path);
-extern int ext4_ext_insert_extent(handle_t *, struct inode *,
-				  struct ext4_ext_path **,
-				  struct ext4_extent *, int);
+extern struct ext4_ext_path *ext4_ext_insert_extent(
+				handle_t *handle, struct inode *inode,
+				struct ext4_ext_path *path,
+				struct ext4_extent *newext, int gb_flags);
 extern struct ext4_ext_path *ext4_find_extent(struct inode *, ext4_lblk_t,
 					      struct ext4_ext_path *,
 					      int flags);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 0d6ce9e74b01..fc75390d591a 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1974,16 +1974,15 @@ static unsigned int ext4_ext_check_overlap(struct ext4_sb_info *sbi,
  * inserts requested extent as new one into the tree,
  * creating new leaf in the no-space case.
  */
-int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
-				struct ext4_ext_path **ppath,
-				struct ext4_extent *newext, int gb_flags)
+struct ext4_ext_path *
+ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
+		       struct ext4_ext_path *path,
+		       struct ext4_extent *newext, int gb_flags)
 {
-	struct ext4_ext_path *path = *ppath;
 	struct ext4_extent_header *eh;
 	struct ext4_extent *ex, *fex;
 	struct ext4_extent *nearex; /* nearest extent */
-	struct ext4_ext_path *npath = NULL;
-	int depth, len, err;
+	int depth, len, err = 0;
 	ext4_lblk_t next;
 	int mb_flags = 0, unwritten;
 
@@ -1991,14 +1990,16 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 		mb_flags |= EXT4_MB_DELALLOC_RESERVED;
 	if (unlikely(ext4_ext_get_actual_len(newext) == 0)) {
 		EXT4_ERROR_INODE(inode, "ext4_ext_get_actual_len(newext) == 0");
-		return -EFSCORRUPTED;
+		err = -EFSCORRUPTED;
+		goto errout;
 	}
 	depth = ext_depth(inode);
 	ex = path[depth].p_ext;
 	eh = path[depth].p_hdr;
 	if (unlikely(path[depth].p_hdr == NULL)) {
 		EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
-		return -EFSCORRUPTED;
+		err = -EFSCORRUPTED;
+		goto errout;
 	}
 
 	/* try to insert block into found extent and return */
@@ -2036,7 +2037,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 			err = ext4_ext_get_access(handle, inode,
 						  path + depth);
 			if (err)
-				return err;
+				goto errout;
 			unwritten = ext4_ext_is_unwritten(ex);
 			ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
 					+ ext4_ext_get_actual_len(newext));
@@ -2061,7 +2062,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 			err = ext4_ext_get_access(handle, inode,
 						  path + depth);
 			if (err)
-				return err;
+				goto errout;
 
 			unwritten = ext4_ext_is_unwritten(ex);
 			ex->ee_block = newext->ee_block;
@@ -2086,21 +2087,26 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 	if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block))
 		next = ext4_ext_next_leaf_block(path);
 	if (next != EXT_MAX_BLOCKS) {
+		struct ext4_ext_path *npath;
+
 		ext_debug(inode, "next leaf block - %u\n", next);
-		BUG_ON(npath != NULL);
 		npath = ext4_find_extent(inode, next, NULL, gb_flags);
-		if (IS_ERR(npath))
-			return PTR_ERR(npath);
+		if (IS_ERR(npath)) {
+			err = PTR_ERR(npath);
+			goto errout;
+		}
 		BUG_ON(npath->p_depth != path->p_depth);
 		eh = npath[depth].p_hdr;
 		if (le16_to_cpu(eh->eh_entries) < le16_to_cpu(eh->eh_max)) {
 			ext_debug(inode, "next leaf isn't full(%d)\n",
 				  le16_to_cpu(eh->eh_entries));
+			ext4_free_ext_path(path);
 			path = npath;
 			goto has_space;
 		}
 		ext_debug(inode, "next leaf has no free space(%d,%d)\n",
 			  le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
+		ext4_free_ext_path(npath);
 	}
 
 	/*
@@ -2111,12 +2117,8 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 		mb_flags |= EXT4_MB_USE_RESERVED;
 	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
 					path, newext);
-	if (IS_ERR(path)) {
-		*ppath = NULL;
-		err = PTR_ERR(path);
-		goto cleanup;
-	}
-	*ppath = path;
+	if (IS_ERR(path))
+		return path;
 	depth = ext_depth(inode);
 	eh = path[depth].p_hdr;
 
@@ -2125,7 +2127,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 
 	err = ext4_ext_get_access(handle, inode, path + depth);
 	if (err)
-		goto cleanup;
+		goto errout;
 
 	if (!nearex) {
 		/* there is no extent in this leaf, create first one */
@@ -2183,17 +2185,20 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 	if (!(gb_flags & EXT4_GET_BLOCKS_PRE_IO))
 		ext4_ext_try_to_merge(handle, inode, path, nearex);
 
-
 	/* time to correct all indexes above */
 	err = ext4_ext_correct_indexes(handle, inode, path);
 	if (err)
-		goto cleanup;
+		goto errout;
 
 	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
+	if (err)
+		goto errout;
 
-cleanup:
-	ext4_free_ext_path(npath);
-	return err;
+	return path;
+
+errout:
+	ext4_free_ext_path(path);
+	return ERR_PTR(err);
 }
 
 static int ext4_fill_es_cache_info(struct inode *inode,
@@ -3248,24 +3253,29 @@ static int ext4_split_extent_at(handle_t *handle,
 	if (split_flag & EXT4_EXT_MARK_UNWRIT2)
 		ext4_ext_mark_unwritten(ex2);
 
-	err = ext4_ext_insert_extent(handle, inode, ppath, &newex, flags);
-	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
+	path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
+	if (!IS_ERR(path)) {
+		*ppath = path;
 		goto out;
+	}
+	*ppath = NULL;
+	err = PTR_ERR(path);
+	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
+		return err;
 
 	/*
-	 * Update path is required because previous ext4_ext_insert_extent()
-	 * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
-	 * guarantees that ext4_find_extent() will not return -ENOMEM,
-	 * otherwise -ENOMEM will cause a retry in do_writepages(), and a
-	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
-	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
+	 * Get a new path to try to zeroout or fix the extent length.
+	 * Using EXT4_EX_NOFAIL guarantees that ext4_find_extent()
+	 * will not return -ENOMEM, otherwise -ENOMEM will cause a
+	 * retry in do_writepages(), and a WARN_ON may be triggered
+	 * in ext4_da_update_reserve_space() due to an incorrect
+	 * ee_len causing the i_reserved_data_blocks exception.
 	 */
-	path = ext4_find_extent(inode, ee_block, *ppath,
+	path = ext4_find_extent(inode, ee_block, NULL,
 				flags | EXT4_EX_NOFAIL);
 	if (IS_ERR(path)) {
 		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
 				 split, PTR_ERR(path));
-		*ppath = NULL;
 		return PTR_ERR(path);
 	}
 	depth = ext_depth(inode);
@@ -3324,7 +3334,7 @@ static int ext4_split_extent_at(handle_t *handle,
 	ext4_ext_dirty(handle, inode, path + path->p_depth);
 	return err;
 out:
-	ext4_ext_show_leaf(inode, *ppath);
+	ext4_ext_show_leaf(inode, path);
 	return err;
 }
 
@@ -4313,6 +4323,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 	    get_implied_cluster_alloc(inode->i_sb, map, &ex2, path)) {
 		ar.len = allocated = map->m_len;
 		newblock = map->m_pblk;
+		err = 0;
 		goto got_allocated_blocks;
 	}
 
@@ -4385,8 +4396,9 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 		map->m_flags |= EXT4_MAP_UNWRITTEN;
 	}
 
-	err = ext4_ext_insert_extent(handle, inode, &path, &newex, flags);
-	if (err) {
+	path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
+	if (IS_ERR(path)) {
+		err = PTR_ERR(path);
 		if (allocated_clusters) {
 			int fb_flags = 0;
 
diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
index 87c009e0c59a..1dee40477727 100644
--- a/fs/ext4/fast_commit.c
+++ b/fs/ext4/fast_commit.c
@@ -1777,12 +1777,12 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
 			if (ext4_ext_is_unwritten(ex))
 				ext4_ext_mark_unwritten(&newex);
 			down_write(&EXT4_I(inode)->i_data_sem);
-			ret = ext4_ext_insert_extent(
-				NULL, inode, &path, &newex, 0);
+			path = ext4_ext_insert_extent(NULL, inode,
+						      path, &newex, 0);
 			up_write((&EXT4_I(inode)->i_data_sem));
-			ext4_free_ext_path(path);
-			if (ret)
+			if (IS_ERR(path))
 				goto out;
+			ext4_free_ext_path(path);
 			goto next;
 		}
 
diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
index d98ac2af8199..0f68b8a14560 100644
--- a/fs/ext4/migrate.c
+++ b/fs/ext4/migrate.c
@@ -37,7 +37,6 @@ static int finish_range(handle_t *handle, struct inode *inode,
 	path = ext4_find_extent(inode, lb->first_block, NULL, 0);
 	if (IS_ERR(path)) {
 		retval = PTR_ERR(path);
-		path = NULL;
 		goto err_out;
 	}
 
@@ -53,7 +52,9 @@ static int finish_range(handle_t *handle, struct inode *inode,
 	retval = ext4_datasem_ensure_credits(handle, inode, needed, needed, 0);
 	if (retval < 0)
 		goto err_out;
-	retval = ext4_ext_insert_extent(handle, inode, &path, &newext, 0);
+	path = ext4_ext_insert_extent(handle, inode, path, &newext, 0);
+	if (IS_ERR(path))
+		retval = PTR_ERR(path);
 err_out:
 	up_write((&EXT4_I(inode)->i_data_sem));
 	ext4_free_ext_path(path);
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (10 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 11/20] ext4: get rid of ppath in ext4_ext_insert_extent() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 11:07   ` Jan Kara
  2024-08-02  8:12   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at() libaokun
                   ` (8 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_split_extent_at(), the following is done
here:

 * Free the extents path when an error is encountered.
 * Its caller needs to update ppath if it uses ppath.
 * Teach ext4_ext_show_leaf() to skip error pointer.
 * Propagate ext4_find_extent() error return value in ext4_insert_range().

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 86 ++++++++++++++++++++++++++---------------------
 1 file changed, 48 insertions(+), 38 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index fc75390d591a..c86b1bb7720f 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -84,12 +84,11 @@ static void ext4_extent_block_csum_set(struct inode *inode,
 	et->et_checksum = ext4_extent_block_csum(inode, eh);
 }
 
-static int ext4_split_extent_at(handle_t *handle,
-			     struct inode *inode,
-			     struct ext4_ext_path **ppath,
-			     ext4_lblk_t split,
-			     int split_flag,
-			     int flags);
+static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
+						  struct inode *inode,
+						  struct ext4_ext_path *path,
+						  ext4_lblk_t split,
+						  int split_flag, int flags);
 
 static int ext4_ext_trunc_restart_fn(struct inode *inode, int *dropped)
 {
@@ -341,9 +340,15 @@ ext4_force_split_extent_at(handle_t *handle, struct inode *inode,
 	if (nofail)
 		flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL;
 
-	return ext4_split_extent_at(handle, inode, ppath, lblk, unwritten ?
+	path = ext4_split_extent_at(handle, inode, path, lblk, unwritten ?
 			EXT4_EXT_MARK_UNWRIT1|EXT4_EXT_MARK_UNWRIT2 : 0,
 			flags);
+	if (IS_ERR(path)) {
+		*ppath = NULL;
+		return PTR_ERR(path);
+	}
+	*ppath = path;
+	return 0;
 }
 
 static int
@@ -694,7 +699,7 @@ static void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path)
 	struct ext4_extent *ex;
 	int i;
 
-	if (!path)
+	if (IS_ERR_OR_NULL(path))
 		return;
 
 	eh = path[depth].p_hdr;
@@ -3174,16 +3179,14 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
  *  a> the extent are splitted into two extent.
  *  b> split is not needed, and just mark the extent.
  *
- * return 0 on success.
+ * Return an extent path pointer on success, or an error pointer on failure.
  */
-static int ext4_split_extent_at(handle_t *handle,
-			     struct inode *inode,
-			     struct ext4_ext_path **ppath,
-			     ext4_lblk_t split,
-			     int split_flag,
-			     int flags)
+static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
+						  struct inode *inode,
+						  struct ext4_ext_path *path,
+						  ext4_lblk_t split,
+						  int split_flag, int flags)
 {
-	struct ext4_ext_path *path = *ppath;
 	ext4_fsblk_t newblock;
 	ext4_lblk_t ee_block;
 	struct ext4_extent *ex, newex, orig_ex, zero_ex;
@@ -3254,14 +3257,12 @@ static int ext4_split_extent_at(handle_t *handle,
 		ext4_ext_mark_unwritten(ex2);
 
 	path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
-	if (!IS_ERR(path)) {
-		*ppath = path;
+	if (!IS_ERR(path))
 		goto out;
-	}
-	*ppath = NULL;
+
 	err = PTR_ERR(path);
 	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
-		return err;
+		return path;
 
 	/*
 	 * Get a new path to try to zeroout or fix the extent length.
@@ -3271,16 +3272,14 @@ static int ext4_split_extent_at(handle_t *handle,
 	 * in ext4_da_update_reserve_space() due to an incorrect
 	 * ee_len causing the i_reserved_data_blocks exception.
 	 */
-	path = ext4_find_extent(inode, ee_block, NULL,
-				flags | EXT4_EX_NOFAIL);
+	path = ext4_find_extent(inode, ee_block, NULL, flags | EXT4_EX_NOFAIL);
 	if (IS_ERR(path)) {
 		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
 				 split, PTR_ERR(path));
-		return PTR_ERR(path);
+		return path;
 	}
 	depth = ext_depth(inode);
 	ex = path[depth].p_ext;
-	*ppath = path;
 
 	if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
 		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
@@ -3332,10 +3331,13 @@ static int ext4_split_extent_at(handle_t *handle,
 	 * and err is a non-zero error code.
 	 */
 	ext4_ext_dirty(handle, inode, path + path->p_depth);
-	return err;
 out:
+	if (err) {
+		ext4_free_ext_path(path);
+		path = ERR_PTR(err);
+	}
 	ext4_ext_show_leaf(inode, path);
-	return err;
+	return path;
 }
 
 /*
@@ -3379,10 +3381,14 @@ static int ext4_split_extent(handle_t *handle,
 				       EXT4_EXT_MARK_UNWRIT2;
 		if (split_flag & EXT4_EXT_DATA_VALID2)
 			split_flag1 |= EXT4_EXT_DATA_VALID1;
-		err = ext4_split_extent_at(handle, inode, ppath,
+		path = ext4_split_extent_at(handle, inode, path,
 				map->m_lblk + map->m_len, split_flag1, flags1);
-		if (err)
+		if (IS_ERR(path)) {
+			err = PTR_ERR(path);
+			*ppath = NULL;
 			goto out;
+		}
+		*ppath = path;
 	} else {
 		allocated = ee_len - (map->m_lblk - ee_block);
 	}
@@ -3390,7 +3396,7 @@ static int ext4_split_extent(handle_t *handle,
 	 * Update path is required because previous ext4_split_extent_at() may
 	 * result in split of original leaf or extent zeroout.
 	 */
-	path = ext4_find_extent(inode, map->m_lblk, *ppath, flags);
+	path = ext4_find_extent(inode, map->m_lblk, path, flags);
 	if (IS_ERR(path)) {
 		*ppath = NULL;
 		return PTR_ERR(path);
@@ -3412,13 +3418,17 @@ static int ext4_split_extent(handle_t *handle,
 			split_flag1 |= split_flag & (EXT4_EXT_MAY_ZEROOUT |
 						     EXT4_EXT_MARK_UNWRIT2);
 		}
-		err = ext4_split_extent_at(handle, inode, ppath,
+		path = ext4_split_extent_at(handle, inode, path,
 				map->m_lblk, split_flag1, flags);
-		if (err)
+		if (IS_ERR(path)) {
+			err = PTR_ERR(path);
+			*ppath = NULL;
 			goto out;
+		}
+		*ppath = path;
 	}
 
-	ext4_ext_show_leaf(inode, *ppath);
+	ext4_ext_show_leaf(inode, path);
 out:
 	return err ? err : allocated;
 }
@@ -5596,6 +5606,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
 	path = ext4_find_extent(inode, offset_lblk, NULL, 0);
 	if (IS_ERR(path)) {
 		up_write(&EXT4_I(inode)->i_data_sem);
+		ret = PTR_ERR(path);
 		goto out_stop;
 	}
 
@@ -5614,22 +5625,21 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
 			if (ext4_ext_is_unwritten(extent))
 				split_flag = EXT4_EXT_MARK_UNWRIT1 |
 					EXT4_EXT_MARK_UNWRIT2;
-			ret = ext4_split_extent_at(handle, inode, &path,
+			path = ext4_split_extent_at(handle, inode, path,
 					offset_lblk, split_flag,
 					EXT4_EX_NOCACHE |
 					EXT4_GET_BLOCKS_PRE_IO |
 					EXT4_GET_BLOCKS_METADATA_NOFAIL);
 		}
 
-		ext4_free_ext_path(path);
-		if (ret < 0) {
+		if (IS_ERR(path)) {
 			up_write(&EXT4_I(inode)->i_data_sem);
+			ret = PTR_ERR(path);
 			goto out_stop;
 		}
-	} else {
-		ext4_free_ext_path(path);
 	}
 
+	ext4_free_ext_path(path);
 	ext4_es_remove_extent(inode, offset_lblk, EXT_MAX_BLOCKS - offset_lblk);
 
 	/*
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (11 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 11:14   ` Jan Kara
  2024-08-02 20:01   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 14/20] ext4: get rid of ppath in ext4_split_extent() libaokun
                   ` (7 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_force_split_extent_at(), the following is
done here:

 * The ext4_find_extent() can update the extent path so it doesn't have to
   allocate and free path repeatedly, thus reducing the consumption of
   memory allocation and freeing in ext4_swap_extents().

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 117 ++++++++++++++++++++++++----------------------
 1 file changed, 60 insertions(+), 57 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index c86b1bb7720f..0bd068ed324f 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -328,27 +328,20 @@ static inline int ext4_ext_space_root_idx(struct inode *inode, int check)
 	return size;
 }
 
-static inline int
+static inline struct ext4_ext_path *
 ext4_force_split_extent_at(handle_t *handle, struct inode *inode,
-			   struct ext4_ext_path **ppath, ext4_lblk_t lblk,
+			   struct ext4_ext_path *path, ext4_lblk_t lblk,
 			   int nofail)
 {
-	struct ext4_ext_path *path = *ppath;
 	int unwritten = ext4_ext_is_unwritten(path[path->p_depth].p_ext);
 	int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO;
 
 	if (nofail)
 		flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL;
 
-	path = ext4_split_extent_at(handle, inode, path, lblk, unwritten ?
+	return ext4_split_extent_at(handle, inode, path, lblk, unwritten ?
 			EXT4_EXT_MARK_UNWRIT1|EXT4_EXT_MARK_UNWRIT2 : 0,
 			flags);
-	if (IS_ERR(path)) {
-		*ppath = NULL;
-		return PTR_ERR(path);
-	}
-	*ppath = path;
-	return 0;
 }
 
 static int
@@ -2906,11 +2899,12 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 			 * fail removing space due to ENOSPC so try to use
 			 * reserved block if that happens.
 			 */
-			err = ext4_force_split_extent_at(handle, inode, &path,
-							 end + 1, 1);
-			if (err < 0)
+			path = ext4_force_split_extent_at(handle, inode, path,
+							  end + 1, 1);
+			if (IS_ERR(path)) {
+				err = PTR_ERR(path);
 				goto out;
-
+			}
 		} else if (sbi->s_cluster_ratio > 1 && end >= ex_end &&
 			   partial.state == initial) {
 			/*
@@ -5707,25 +5701,21 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
 		int e1_len, e2_len, len;
 		int split = 0;
 
-		path1 = ext4_find_extent(inode1, lblk1, NULL, EXT4_EX_NOCACHE);
+		path1 = ext4_find_extent(inode1, lblk1, path1, EXT4_EX_NOCACHE);
 		if (IS_ERR(path1)) {
 			*erp = PTR_ERR(path1);
-			path1 = NULL;
-		finish:
-			count = 0;
-			goto repeat;
+			goto errout;
 		}
-		path2 = ext4_find_extent(inode2, lblk2, NULL, EXT4_EX_NOCACHE);
+		path2 = ext4_find_extent(inode2, lblk2, path2, EXT4_EX_NOCACHE);
 		if (IS_ERR(path2)) {
 			*erp = PTR_ERR(path2);
-			path2 = NULL;
-			goto finish;
+			goto errout;
 		}
 		ex1 = path1[path1->p_depth].p_ext;
 		ex2 = path2[path2->p_depth].p_ext;
 		/* Do we have something to swap ? */
 		if (unlikely(!ex2 || !ex1))
-			goto finish;
+			goto errout;
 
 		e1_blk = le32_to_cpu(ex1->ee_block);
 		e2_blk = le32_to_cpu(ex2->ee_block);
@@ -5747,7 +5737,7 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
 				next2 = e2_blk;
 			/* Do we have something to swap */
 			if (next1 == EXT_MAX_BLOCKS || next2 == EXT_MAX_BLOCKS)
-				goto finish;
+				goto errout;
 			/* Move to the rightest boundary */
 			len = next1 - lblk1;
 			if (len < next2 - lblk2)
@@ -5757,28 +5747,32 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
 			lblk1 += len;
 			lblk2 += len;
 			count -= len;
-			goto repeat;
+			continue;
 		}
 
 		/* Prepare left boundary */
 		if (e1_blk < lblk1) {
 			split = 1;
-			*erp = ext4_force_split_extent_at(handle, inode1,
-						&path1, lblk1, 0);
-			if (unlikely(*erp))
-				goto finish;
+			path1 = ext4_force_split_extent_at(handle, inode1,
+							   path1, lblk1, 0);
+			if (IS_ERR(path1)) {
+				*erp = PTR_ERR(path1);
+				goto errout;
+			}
 		}
 		if (e2_blk < lblk2) {
 			split = 1;
-			*erp = ext4_force_split_extent_at(handle, inode2,
-						&path2,  lblk2, 0);
-			if (unlikely(*erp))
-				goto finish;
+			path2 = ext4_force_split_extent_at(handle, inode2,
+							   path2, lblk2, 0);
+			if (IS_ERR(path2)) {
+				*erp = PTR_ERR(path2);
+				goto errout;
+			}
 		}
 		/* ext4_split_extent_at() may result in leaf extent split,
 		 * path must to be revalidated. */
 		if (split)
-			goto repeat;
+			continue;
 
 		/* Prepare right boundary */
 		len = count;
@@ -5789,30 +5783,34 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
 
 		if (len != e1_len) {
 			split = 1;
-			*erp = ext4_force_split_extent_at(handle, inode1,
-						&path1, lblk1 + len, 0);
-			if (unlikely(*erp))
-				goto finish;
+			path1 = ext4_force_split_extent_at(handle, inode1,
+							path1, lblk1 + len, 0);
+			if (IS_ERR(path1)) {
+				*erp = PTR_ERR(path1);
+				goto errout;
+			}
 		}
 		if (len != e2_len) {
 			split = 1;
-			*erp = ext4_force_split_extent_at(handle, inode2,
-						&path2, lblk2 + len, 0);
-			if (*erp)
-				goto finish;
+			path2 = ext4_force_split_extent_at(handle, inode2,
+							path2, lblk2 + len, 0);
+			if (IS_ERR(path2)) {
+				*erp = PTR_ERR(path2);
+				goto errout;
+			}
 		}
 		/* ext4_split_extent_at() may result in leaf extent split,
 		 * path must to be revalidated. */
 		if (split)
-			goto repeat;
+			continue;
 
 		BUG_ON(e2_len != e1_len);
 		*erp = ext4_ext_get_access(handle, inode1, path1 + path1->p_depth);
 		if (unlikely(*erp))
-			goto finish;
+			goto errout;
 		*erp = ext4_ext_get_access(handle, inode2, path2 + path2->p_depth);
 		if (unlikely(*erp))
-			goto finish;
+			goto errout;
 
 		/* Both extents are fully inside boundaries. Swap it now */
 		tmp_ex = *ex1;
@@ -5830,7 +5828,7 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
 		*erp = ext4_ext_dirty(handle, inode2, path2 +
 				      path2->p_depth);
 		if (unlikely(*erp))
-			goto finish;
+			goto errout;
 		*erp = ext4_ext_dirty(handle, inode1, path1 +
 				      path1->p_depth);
 		/*
@@ -5840,17 +5838,17 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
 		 * aborted anyway.
 		 */
 		if (unlikely(*erp))
-			goto finish;
+			goto errout;
+
 		lblk1 += len;
 		lblk2 += len;
 		replaced_count += len;
 		count -= len;
-
-	repeat:
-		ext4_free_ext_path(path1);
-		ext4_free_ext_path(path2);
-		path1 = path2 = NULL;
 	}
+
+errout:
+	ext4_free_ext_path(path1);
+	ext4_free_ext_path(path2);
 	return replaced_count;
 }
 
@@ -5965,24 +5963,29 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
 		ext4_ext_get_actual_len(ex) != len) {
 		/* We need to split this extent to match our extent first */
 		down_write(&EXT4_I(inode)->i_data_sem);
-		ret = ext4_force_split_extent_at(NULL, inode, &path, start, 1);
+		path = ext4_force_split_extent_at(NULL, inode, path, start, 1);
 		up_write(&EXT4_I(inode)->i_data_sem);
-		if (ret)
+		if (IS_ERR(path)) {
+			ret = PTR_ERR(path);
 			goto out;
+		}
 
 		path = ext4_find_extent(inode, start, path, 0);
 		if (IS_ERR(path))
 			return PTR_ERR(path);
+
 		ex = path[path->p_depth].p_ext;
 		WARN_ON(le32_to_cpu(ex->ee_block) != start);
 
 		if (ext4_ext_get_actual_len(ex) != len) {
 			down_write(&EXT4_I(inode)->i_data_sem);
-			ret = ext4_force_split_extent_at(NULL, inode, &path,
-							 start + len, 1);
+			path = ext4_force_split_extent_at(NULL, inode, path,
+							  start + len, 1);
 			up_write(&EXT4_I(inode)->i_data_sem);
-			if (ret)
+			if (IS_ERR(path)) {
+				ret = PTR_ERR(path);
 				goto out;
+			}
 
 			path = ext4_find_extent(inode, start, path, 0);
 			if (IS_ERR(path))
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 14/20] ext4: get rid of ppath in ext4_split_extent()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (12 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 12:07   ` Jan Kara
  2024-08-02 20:17   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 15/20] ext4: get rid of ppath in ext4_split_convert_extents() libaokun
                   ` (6 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_split_extent(), the following is done here:

 * The 'allocated' is changed from passing a value to passing an address.
 * Its caller needs to update ppath if it uses ppath.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 97 ++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 47 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 0bd068ed324f..2a4f6c89858c 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3345,21 +3345,18 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
  *   c> Splits in three extents: Somone is splitting in middle of the extent
  *
  */
-static int ext4_split_extent(handle_t *handle,
-			      struct inode *inode,
-			      struct ext4_ext_path **ppath,
-			      struct ext4_map_blocks *map,
-			      int split_flag,
-			      int flags)
+static struct ext4_ext_path *ext4_split_extent(handle_t *handle,
+					       struct inode *inode,
+					       struct ext4_ext_path *path,
+					       struct ext4_map_blocks *map,
+					       int split_flag, int flags,
+					       unsigned int *allocated)
 {
-	struct ext4_ext_path *path = *ppath;
 	ext4_lblk_t ee_block;
 	struct ext4_extent *ex;
 	unsigned int ee_len, depth;
-	int err = 0;
 	int unwritten;
 	int split_flag1, flags1;
-	int allocated = map->m_len;
 
 	depth = ext_depth(inode);
 	ex = path[depth].p_ext;
@@ -3377,33 +3374,25 @@ static int ext4_split_extent(handle_t *handle,
 			split_flag1 |= EXT4_EXT_DATA_VALID1;
 		path = ext4_split_extent_at(handle, inode, path,
 				map->m_lblk + map->m_len, split_flag1, flags1);
-		if (IS_ERR(path)) {
-			err = PTR_ERR(path);
-			*ppath = NULL;
-			goto out;
+		if (IS_ERR(path))
+			return path;
+		/*
+		 * Update path is required because previous ext4_split_extent_at
+		 * may result in split of original leaf or extent zeroout.
+		 */
+		path = ext4_find_extent(inode, map->m_lblk, path, flags);
+		if (IS_ERR(path))
+			return path;
+		depth = ext_depth(inode);
+		ex = path[depth].p_ext;
+		if (!ex) {
+			EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
+					(unsigned long) map->m_lblk);
+			ext4_free_ext_path(path);
+			return ERR_PTR(-EFSCORRUPTED);
 		}
-		*ppath = path;
-	} 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.
-	 */
-	path = ext4_find_extent(inode, map->m_lblk, path, flags);
-	if (IS_ERR(path)) {
-		*ppath = NULL;
-		return PTR_ERR(path);
-	}
-	*ppath = path;
-	depth = ext_depth(inode);
-	ex = path[depth].p_ext;
-	if (!ex) {
-		EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
-				 (unsigned long) map->m_lblk);
-		return -EFSCORRUPTED;
+		unwritten = ext4_ext_is_unwritten(ex);
 	}
-	unwritten = ext4_ext_is_unwritten(ex);
 
 	if (map->m_lblk >= ee_block) {
 		split_flag1 = split_flag & EXT4_EXT_DATA_VALID2;
@@ -3414,17 +3403,18 @@ static int ext4_split_extent(handle_t *handle,
 		}
 		path = ext4_split_extent_at(handle, inode, path,
 				map->m_lblk, split_flag1, flags);
-		if (IS_ERR(path)) {
-			err = PTR_ERR(path);
-			*ppath = NULL;
-			goto out;
-		}
-		*ppath = path;
+		if (IS_ERR(path))
+			return path;
 	}
 
+	if (allocated) {
+		if (map->m_lblk + map->m_len > ee_block + ee_len)
+			*allocated = ee_len - (map->m_lblk - ee_block);
+		else
+			*allocated = map->m_len;
+	}
 	ext4_ext_show_leaf(inode, path);
-out:
-	return err ? err : allocated;
+	return path;
 }
 
 /*
@@ -3669,10 +3659,15 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 	}
 
 fallback:
-	err = ext4_split_extent(handle, inode, ppath, &split_map, split_flag,
-				flags);
-	if (err > 0)
-		err = 0;
+	path = ext4_split_extent(handle, inode, path, &split_map, split_flag,
+				 flags, NULL);
+	if (IS_ERR(path)) {
+		err = PTR_ERR(path);
+		*ppath = NULL;
+		goto out;
+	}
+	err = 0;
+	*ppath = path;
 out:
 	/* If we have gotten a failure, don't zero out status tree */
 	if (!err) {
@@ -3718,6 +3713,7 @@ static int ext4_split_convert_extents(handle_t *handle,
 	struct ext4_extent *ex;
 	unsigned int ee_len;
 	int split_flag = 0, depth;
+	unsigned int allocated = 0;
 
 	ext_debug(inode, "logical block %llu, max_blocks %u\n",
 		  (unsigned long long)map->m_lblk, map->m_len);
@@ -3745,7 +3741,14 @@ static int ext4_split_convert_extents(handle_t *handle,
 		split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2);
 	}
 	flags |= EXT4_GET_BLOCKS_PRE_IO;
-	return ext4_split_extent(handle, inode, ppath, map, split_flag, flags);
+	path = ext4_split_extent(handle, inode, path, map, split_flag, flags,
+				 &allocated);
+	if (IS_ERR(path)) {
+		*ppath = NULL;
+		return PTR_ERR(path);
+	}
+	*ppath = path;
+	return allocated;
 }
 
 static int ext4_convert_unwritten_extents_endio(handle_t *handle,
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 15/20] ext4: get rid of ppath in ext4_split_convert_extents()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (13 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 14/20] ext4: get rid of ppath in ext4_split_extent() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 12:12   ` Jan Kara
  2024-08-02 20:26   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 16/20] ext4: get rid of ppath in ext4_convert_unwritten_extents_endio() libaokun
                   ` (5 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_split_convert_extents(), the following is
done here:

 * Its caller needs to update ppath if it uses ppath.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 65 ++++++++++++++++++++++++-----------------------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 2a4f6c89858c..a41cbb8c4475 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3699,21 +3699,21 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
  * being filled will be convert to initialized by the end_io callback function
  * via ext4_convert_unwritten_extents().
  *
- * Returns the size of unwritten extent to be written on success.
+ * The size of unwritten extent to be written is passed to the caller via the
+ * allocated pointer. Return an extent path pointer on success, or an error
+ * pointer on failure.
  */
-static int ext4_split_convert_extents(handle_t *handle,
+static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle,
 					struct inode *inode,
 					struct ext4_map_blocks *map,
-					struct ext4_ext_path **ppath,
-					int flags)
+					struct ext4_ext_path *path,
+					int flags, unsigned int *allocated)
 {
-	struct ext4_ext_path *path = *ppath;
 	ext4_lblk_t eof_block;
 	ext4_lblk_t ee_block;
 	struct ext4_extent *ex;
 	unsigned int ee_len;
 	int split_flag = 0, depth;
-	unsigned int allocated = 0;
 
 	ext_debug(inode, "logical block %llu, max_blocks %u\n",
 		  (unsigned long long)map->m_lblk, map->m_len);
@@ -3741,14 +3741,8 @@ static int ext4_split_convert_extents(handle_t *handle,
 		split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2);
 	}
 	flags |= EXT4_GET_BLOCKS_PRE_IO;
-	path = ext4_split_extent(handle, inode, path, map, split_flag, flags,
-				 &allocated);
-	if (IS_ERR(path)) {
-		*ppath = NULL;
-		return PTR_ERR(path);
-	}
-	*ppath = path;
-	return allocated;
+	return ext4_split_extent(handle, inode, path, map, split_flag, flags,
+				 allocated);
 }
 
 static int ext4_convert_unwritten_extents_endio(handle_t *handle,
@@ -3784,11 +3778,14 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
 			     inode->i_ino, (unsigned long long)ee_block, ee_len,
 			     (unsigned long long)map->m_lblk, map->m_len);
 #endif
-		err = ext4_split_convert_extents(handle, inode, map, ppath,
-						 EXT4_GET_BLOCKS_CONVERT);
-		if (err < 0)
-			return err;
-		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
+		path = ext4_split_convert_extents(handle, inode, map, path,
+						EXT4_GET_BLOCKS_CONVERT, NULL);
+		if (IS_ERR(path)) {
+			*ppath = NULL;
+			return PTR_ERR(path);
+		}
+
+		path = ext4_find_extent(inode, map->m_lblk, path, 0);
 		if (IS_ERR(path)) {
 			*ppath = NULL;
 			return PTR_ERR(path);
@@ -3845,11 +3842,14 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
 		  (unsigned long long)ee_block, ee_len);
 
 	if (ee_block != map->m_lblk || ee_len > map->m_len) {
-		err = ext4_split_convert_extents(handle, inode, map, ppath,
-				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN);
-		if (err < 0)
-			return err;
-		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
+		path = ext4_split_convert_extents(handle, inode, map, path,
+				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, NULL);
+		if (IS_ERR(path)) {
+			*ppath = NULL;
+			return PTR_ERR(path);
+		}
+
+		path = ext4_find_extent(inode, map->m_lblk, path, 0);
 		if (IS_ERR(path)) {
 			*ppath = NULL;
 			return PTR_ERR(path);
@@ -3915,19 +3915,20 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 
 	/* get_block() before submitting IO, split the extent */
 	if (flags & EXT4_GET_BLOCKS_PRE_IO) {
-		ret = ext4_split_convert_extents(handle, inode, map, ppath,
-					 flags | EXT4_GET_BLOCKS_CONVERT);
-		if (ret < 0) {
-			err = ret;
+		*ppath = ext4_split_convert_extents(handle, inode, map, *ppath,
+				flags | EXT4_GET_BLOCKS_CONVERT, &allocated);
+		if (IS_ERR(*ppath)) {
+			err = PTR_ERR(*ppath);
+			*ppath = NULL;
 			goto out2;
 		}
 		/*
-		 * shouldn't get a 0 return when splitting an extent unless
+		 * shouldn't get a 0 allocated when splitting an extent unless
 		 * m_len is 0 (bug) or extent has been corrupted
 		 */
-		if (unlikely(ret == 0)) {
+		if (unlikely(allocated == 0)) {
 			EXT4_ERROR_INODE(inode,
-					 "unexpected ret == 0, m_len = %u",
+					 "unexpected allocated == 0, m_len = %u",
 					 map->m_len);
 			err = -EFSCORRUPTED;
 			goto out2;
@@ -3988,9 +3989,9 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 		err = -EFSCORRUPTED;
 		goto out2;
 	}
+	allocated = ret;
 
 out:
-	allocated = ret;
 	map->m_flags |= EXT4_MAP_NEW;
 map_out:
 	map->m_flags |= EXT4_MAP_MAPPED;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 16/20] ext4: get rid of ppath in ext4_convert_unwritten_extents_endio()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (14 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 15/20] ext4: get rid of ppath in ext4_split_convert_extents() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 12:14   ` Jan Kara
  2024-08-02 20:28   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 17/20] ext4: get rid of ppath in ext4_ext_convert_to_initialized() libaokun
                   ` (4 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_convert_unwritten_extents_endio(), the
following is done here:

 * Free the extents path when an error is encountered.
 * Its caller needs to update ppath if it uses ppath.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 43 +++++++++++++++++++++++--------------------
 1 file changed, 23 insertions(+), 20 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index a41cbb8c4475..b7f443f98e9d 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3745,12 +3745,11 @@ static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle,
 				 allocated);
 }
 
-static int ext4_convert_unwritten_extents_endio(handle_t *handle,
-						struct inode *inode,
-						struct ext4_map_blocks *map,
-						struct ext4_ext_path **ppath)
+static struct ext4_ext_path *
+ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
+				     struct ext4_map_blocks *map,
+				     struct ext4_ext_path *path)
 {
-	struct ext4_ext_path *path = *ppath;
 	struct ext4_extent *ex;
 	ext4_lblk_t ee_block;
 	unsigned int ee_len;
@@ -3780,24 +3779,19 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
 #endif
 		path = ext4_split_convert_extents(handle, inode, map, path,
 						EXT4_GET_BLOCKS_CONVERT, NULL);
-		if (IS_ERR(path)) {
-			*ppath = NULL;
-			return PTR_ERR(path);
-		}
+		if (IS_ERR(path))
+			return path;
 
 		path = ext4_find_extent(inode, map->m_lblk, path, 0);
-		if (IS_ERR(path)) {
-			*ppath = NULL;
-			return PTR_ERR(path);
-		}
-		*ppath = path;
+		if (IS_ERR(path))
+			return path;
 		depth = ext_depth(inode);
 		ex = path[depth].p_ext;
 	}
 
 	err = ext4_ext_get_access(handle, inode, path + depth);
 	if (err)
-		goto out;
+		goto errout;
 	/* first mark the extent as initialized */
 	ext4_ext_mark_initialized(ex);
 
@@ -3808,9 +3802,15 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
 
 	/* Mark modified extent as dirty */
 	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
-out:
+	if (err)
+		goto errout;
+
 	ext4_ext_show_leaf(inode, path);
-	return err;
+	return path;
+
+errout:
+	ext4_free_ext_path(path);
+	return ERR_PTR(err);
 }
 
 static int
@@ -3938,10 +3938,13 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 	}
 	/* IO end_io complete, convert the filled extent to written */
 	if (flags & EXT4_GET_BLOCKS_CONVERT) {
-		err = ext4_convert_unwritten_extents_endio(handle, inode, map,
-							   ppath);
-		if (err < 0)
+		*ppath = ext4_convert_unwritten_extents_endio(handle, inode,
+							      map, *ppath);
+		if (IS_ERR(*ppath)) {
+			err = PTR_ERR(*ppath);
+			*ppath = NULL;
 			goto out2;
+		}
 		ext4_update_inode_fsync_trans(handle, inode, 1);
 		goto map_out;
 	}
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 17/20] ext4: get rid of ppath in ext4_ext_convert_to_initialized()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (15 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 16/20] ext4: get rid of ppath in ext4_convert_unwritten_extents_endio() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 12:18   ` Jan Kara
  2024-08-02 20:38   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 18/20] ext4: get rid of ppath in ext4_ext_handle_unwritten_extents() libaokun
                   ` (3 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_ext_convert_to_initialized(), the following
is done here:

 * Free the extents path when an error is encountered.
 * Its caller needs to update ppath if it uses ppath.
 * The 'allocated' is changed from passing a value to passing an address.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 73 +++++++++++++++++++++++------------------------
 1 file changed, 35 insertions(+), 38 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index b7f443f98e9d..59e80926fe3a 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3437,13 +3437,11 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle,
  *    that are allocated and initialized.
  *    It is guaranteed to be >= map->m_len.
  */
-static int ext4_ext_convert_to_initialized(handle_t *handle,
-					   struct inode *inode,
-					   struct ext4_map_blocks *map,
-					   struct ext4_ext_path **ppath,
-					   int flags)
+static struct ext4_ext_path *
+ext4_ext_convert_to_initialized(handle_t *handle, struct inode *inode,
+			struct ext4_map_blocks *map, struct ext4_ext_path *path,
+			int flags, unsigned int *allocated)
 {
-	struct ext4_ext_path *path = *ppath;
 	struct ext4_sb_info *sbi;
 	struct ext4_extent_header *eh;
 	struct ext4_map_blocks split_map;
@@ -3453,7 +3451,6 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 	unsigned int ee_len, depth, map_len = map->m_len;
 	int err = 0;
 	int split_flag = EXT4_EXT_DATA_VALID2;
-	int allocated = 0;
 	unsigned int max_zeroout = 0;
 
 	ext_debug(inode, "logical block %llu, max_blocks %u\n",
@@ -3494,6 +3491,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 	 *  - L2: we only attempt to merge with an extent stored in the
 	 *    same extent tree node.
 	 */
+	*allocated = 0;
 	if ((map->m_lblk == ee_block) &&
 		/* See if we can merge left */
 		(map_len < ee_len) &&		/*L1*/
@@ -3523,7 +3521,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 			(prev_len < (EXT_INIT_MAX_LEN - map_len))) {	/*C4*/
 			err = ext4_ext_get_access(handle, inode, path + depth);
 			if (err)
-				goto out;
+				goto errout;
 
 			trace_ext4_ext_convert_to_initialized_fastpath(inode,
 				map, ex, abut_ex);
@@ -3538,7 +3536,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 			abut_ex->ee_len = cpu_to_le16(prev_len + map_len);
 
 			/* Result: number of initialized blocks past m_lblk */
-			allocated = map_len;
+			*allocated = map_len;
 		}
 	} else if (((map->m_lblk + map_len) == (ee_block + ee_len)) &&
 		   (map_len < ee_len) &&	/*L1*/
@@ -3569,7 +3567,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 		    (next_len < (EXT_INIT_MAX_LEN - map_len))) {	/*C4*/
 			err = ext4_ext_get_access(handle, inode, path + depth);
 			if (err)
-				goto out;
+				goto errout;
 
 			trace_ext4_ext_convert_to_initialized_fastpath(inode,
 				map, ex, abut_ex);
@@ -3584,18 +3582,20 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 			abut_ex->ee_len = cpu_to_le16(next_len + map_len);
 
 			/* Result: number of initialized blocks past m_lblk */
-			allocated = map_len;
+			*allocated = map_len;
 		}
 	}
-	if (allocated) {
+	if (*allocated) {
 		/* Mark the block containing both extents as dirty */
 		err = ext4_ext_dirty(handle, inode, path + depth);
 
 		/* Update path to point to the right extent */
 		path[depth].p_ext = abut_ex;
+		if (err)
+			goto errout;
 		goto out;
 	} else
-		allocated = ee_len - (map->m_lblk - ee_block);
+		*allocated = ee_len - (map->m_lblk - ee_block);
 
 	WARN_ON(map->m_lblk < ee_block);
 	/*
@@ -3622,21 +3622,21 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 	split_map.m_lblk = map->m_lblk;
 	split_map.m_len = map->m_len;
 
-	if (max_zeroout && (allocated > split_map.m_len)) {
-		if (allocated <= max_zeroout) {
+	if (max_zeroout && (*allocated > split_map.m_len)) {
+		if (*allocated <= max_zeroout) {
 			/* case 3 or 5 */
 			zero_ex1.ee_block =
 				 cpu_to_le32(split_map.m_lblk +
 					     split_map.m_len);
 			zero_ex1.ee_len =
-				cpu_to_le16(allocated - split_map.m_len);
+				cpu_to_le16(*allocated - split_map.m_len);
 			ext4_ext_store_pblock(&zero_ex1,
 				ext4_ext_pblock(ex) + split_map.m_lblk +
 				split_map.m_len - ee_block);
 			err = ext4_ext_zeroout(inode, &zero_ex1);
 			if (err)
 				goto fallback;
-			split_map.m_len = allocated;
+			split_map.m_len = *allocated;
 		}
 		if (split_map.m_lblk - ee_block + split_map.m_len <
 								max_zeroout) {
@@ -3654,27 +3654,24 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
 
 			split_map.m_len += split_map.m_lblk - ee_block;
 			split_map.m_lblk = ee_block;
-			allocated = map->m_len;
+			*allocated = map->m_len;
 		}
 	}
 
 fallback:
 	path = ext4_split_extent(handle, inode, path, &split_map, split_flag,
 				 flags, NULL);
-	if (IS_ERR(path)) {
-		err = PTR_ERR(path);
-		*ppath = NULL;
-		goto out;
-	}
-	err = 0;
-	*ppath = path;
+	if (IS_ERR(path))
+		return path;
 out:
 	/* If we have gotten a failure, don't zero out status tree */
-	if (!err) {
-		ext4_zeroout_es(inode, &zero_ex1);
-		ext4_zeroout_es(inode, &zero_ex2);
-	}
-	return err ? err : allocated;
+	ext4_zeroout_es(inode, &zero_ex1);
+	ext4_zeroout_es(inode, &zero_ex2);
+	return path;
+
+errout:
+	ext4_free_ext_path(path);
+	return ERR_PTR(err);
 }
 
 /*
@@ -3896,7 +3893,6 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 			struct ext4_ext_path **ppath, int flags,
 			unsigned int allocated, ext4_fsblk_t newblock)
 {
-	int ret = 0;
 	int err = 0;
 
 	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
@@ -3976,23 +3972,24 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 	 * For buffered writes, at writepage time, etc.  Convert a
 	 * discovered unwritten extent to written.
 	 */
-	ret = ext4_ext_convert_to_initialized(handle, inode, map, ppath, flags);
-	if (ret < 0) {
-		err = ret;
+	*ppath = ext4_ext_convert_to_initialized(handle, inode, map, *ppath,
+						 flags, &allocated);
+	if (IS_ERR(*ppath)) {
+		err = PTR_ERR(*ppath);
+		*ppath = NULL;
 		goto out2;
 	}
 	ext4_update_inode_fsync_trans(handle, inode, 1);
 	/*
-	 * shouldn't get a 0 return when converting an unwritten extent
+	 * shouldn't get a 0 allocated when converting an unwritten extent
 	 * unless m_len is 0 (bug) or extent has been corrupted
 	 */
-	if (unlikely(ret == 0)) {
-		EXT4_ERROR_INODE(inode, "unexpected ret == 0, m_len = %u",
+	if (unlikely(allocated == 0)) {
+		EXT4_ERROR_INODE(inode, "unexpected allocated == 0, m_len = %u",
 				 map->m_len);
 		err = -EFSCORRUPTED;
 		goto out2;
 	}
-	allocated = ret;
 
 out:
 	map->m_flags |= EXT4_MAP_NEW;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 18/20] ext4: get rid of ppath in ext4_ext_handle_unwritten_extents()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (16 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 17/20] ext4: get rid of ppath in ext4_ext_convert_to_initialized() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 12:22   ` Jan Kara
  2024-08-02 20:44   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 19/20] ext4: get rid of ppath in convert_initialized_extent() libaokun
                   ` (2 subsequent siblings)
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_ext_handle_unwritten_extents(), the
following is done here:

 * Free the extents path when an error is encountered.
 * The 'allocated' is changed from passing a value to passing an address.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 82 +++++++++++++++++++++--------------------------
 1 file changed, 37 insertions(+), 45 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 59e80926fe3a..badadcd64e66 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3887,18 +3887,18 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
 	return 0;
 }
 
-static int
+static struct ext4_ext_path *
 ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 			struct ext4_map_blocks *map,
-			struct ext4_ext_path **ppath, int flags,
-			unsigned int allocated, ext4_fsblk_t newblock)
+			struct ext4_ext_path *path, int flags,
+			unsigned int *allocated, ext4_fsblk_t newblock)
 {
 	int err = 0;
 
 	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
 		  (unsigned long long)map->m_lblk, map->m_len, flags,
-		  allocated);
-	ext4_ext_show_leaf(inode, *ppath);
+		  *allocated);
+	ext4_ext_show_leaf(inode, path);
 
 	/*
 	 * When writing into unwritten space, we should not fail to
@@ -3907,40 +3907,34 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 	flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL;
 
 	trace_ext4_ext_handle_unwritten_extents(inode, map, flags,
-						    allocated, newblock);
+						*allocated, newblock);
 
 	/* get_block() before submitting IO, split the extent */
 	if (flags & EXT4_GET_BLOCKS_PRE_IO) {
-		*ppath = ext4_split_convert_extents(handle, inode, map, *ppath,
-				flags | EXT4_GET_BLOCKS_CONVERT, &allocated);
-		if (IS_ERR(*ppath)) {
-			err = PTR_ERR(*ppath);
-			*ppath = NULL;
-			goto out2;
-		}
+		path = ext4_split_convert_extents(handle, inode, map, path,
+				flags | EXT4_GET_BLOCKS_CONVERT, allocated);
+		if (IS_ERR(path))
+			return path;
 		/*
 		 * shouldn't get a 0 allocated when splitting an extent unless
 		 * m_len is 0 (bug) or extent has been corrupted
 		 */
-		if (unlikely(allocated == 0)) {
+		if (unlikely(*allocated == 0)) {
 			EXT4_ERROR_INODE(inode,
 					 "unexpected allocated == 0, m_len = %u",
 					 map->m_len);
 			err = -EFSCORRUPTED;
-			goto out2;
+			goto errout;
 		}
 		map->m_flags |= EXT4_MAP_UNWRITTEN;
 		goto out;
 	}
 	/* IO end_io complete, convert the filled extent to written */
 	if (flags & EXT4_GET_BLOCKS_CONVERT) {
-		*ppath = ext4_convert_unwritten_extents_endio(handle, inode,
-							      map, *ppath);
-		if (IS_ERR(*ppath)) {
-			err = PTR_ERR(*ppath);
-			*ppath = NULL;
-			goto out2;
-		}
+		path = ext4_convert_unwritten_extents_endio(handle, inode,
+							    map, path);
+		if (IS_ERR(path))
+			return path;
 		ext4_update_inode_fsync_trans(handle, inode, 1);
 		goto map_out;
 	}
@@ -3972,23 +3966,20 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 	 * For buffered writes, at writepage time, etc.  Convert a
 	 * discovered unwritten extent to written.
 	 */
-	*ppath = ext4_ext_convert_to_initialized(handle, inode, map, *ppath,
-						 flags, &allocated);
-	if (IS_ERR(*ppath)) {
-		err = PTR_ERR(*ppath);
-		*ppath = NULL;
-		goto out2;
-	}
+	path = ext4_ext_convert_to_initialized(handle, inode, map, path,
+					       flags, allocated);
+	if (IS_ERR(path))
+		return path;
 	ext4_update_inode_fsync_trans(handle, inode, 1);
 	/*
 	 * shouldn't get a 0 allocated when converting an unwritten extent
 	 * unless m_len is 0 (bug) or extent has been corrupted
 	 */
-	if (unlikely(allocated == 0)) {
+	if (unlikely(*allocated == 0)) {
 		EXT4_ERROR_INODE(inode, "unexpected allocated == 0, m_len = %u",
 				 map->m_len);
 		err = -EFSCORRUPTED;
-		goto out2;
+		goto errout;
 	}
 
 out:
@@ -3997,12 +3988,15 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
 	map->m_flags |= EXT4_MAP_MAPPED;
 out1:
 	map->m_pblk = newblock;
-	if (allocated > map->m_len)
-		allocated = map->m_len;
-	map->m_len = allocated;
-	ext4_ext_show_leaf(inode, *ppath);
-out2:
-	return err ? err : allocated;
+	if (*allocated > map->m_len)
+		*allocated = map->m_len;
+	map->m_len = *allocated;
+	ext4_ext_show_leaf(inode, path);
+	return path;
+
+errout:
+	ext4_free_ext_path(path);
+	return ERR_PTR(err);
 }
 
 /*
@@ -4199,7 +4193,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 	struct ext4_extent newex, *ex, ex2;
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	ext4_fsblk_t newblock = 0, pblk;
-	int err = 0, depth, ret;
+	int err = 0, depth;
 	unsigned int allocated = 0, offset = 0;
 	unsigned int allocated_clusters = 0;
 	struct ext4_allocation_request ar;
@@ -4273,13 +4267,11 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 				goto out;
 			}
 
-			ret = ext4_ext_handle_unwritten_extents(
-				handle, inode, map, &path, flags,
-				allocated, newblock);
-			if (ret < 0)
-				err = ret;
-			else
-				allocated = ret;
+			path = ext4_ext_handle_unwritten_extents(
+				handle, inode, map, path, flags,
+				&allocated, newblock);
+			if (IS_ERR(path))
+				err = PTR_ERR(path);
 			goto out;
 		}
 	}
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 19/20] ext4: get rid of ppath in convert_initialized_extent()
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (17 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 18/20] ext4: get rid of ppath in ext4_ext_handle_unwritten_extents() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 12:23   ` Jan Kara
  2024-08-02 20:46   ` Ojaswin Mujoo
  2024-07-10  4:06 ` [PATCH 20/20] ext4: avoid unnecessary extent path frees and allocations libaokun
  2024-07-25  9:02 ` [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path Ojaswin Mujoo
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in convert_initialized_extent(), the following is
done here:

 * Free the extents path when an error is encountered.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 37 +++++++++++++++++++------------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index badadcd64e66..737432bb316e 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3810,13 +3810,12 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
 	return ERR_PTR(err);
 }
 
-static int
+static struct ext4_ext_path *
 convert_initialized_extent(handle_t *handle, struct inode *inode,
 			   struct ext4_map_blocks *map,
-			   struct ext4_ext_path **ppath,
+			   struct ext4_ext_path *path,
 			   unsigned int *allocated)
 {
-	struct ext4_ext_path *path = *ppath;
 	struct ext4_extent *ex;
 	ext4_lblk_t ee_block;
 	unsigned int ee_len;
@@ -3841,29 +3840,25 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
 	if (ee_block != map->m_lblk || ee_len > map->m_len) {
 		path = ext4_split_convert_extents(handle, inode, map, path,
 				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, NULL);
-		if (IS_ERR(path)) {
-			*ppath = NULL;
-			return PTR_ERR(path);
-		}
+		if (IS_ERR(path))
+			return path;
 
 		path = ext4_find_extent(inode, map->m_lblk, path, 0);
-		if (IS_ERR(path)) {
-			*ppath = NULL;
-			return PTR_ERR(path);
-		}
-		*ppath = path;
+		if (IS_ERR(path))
+			return path;
 		depth = ext_depth(inode);
 		ex = path[depth].p_ext;
 		if (!ex) {
 			EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
 					 (unsigned long) map->m_lblk);
-			return -EFSCORRUPTED;
+			err = -EFSCORRUPTED;
+			goto errout;
 		}
 	}
 
 	err = ext4_ext_get_access(handle, inode, path + depth);
 	if (err)
-		return err;
+		goto errout;
 	/* first mark the extent as unwritten */
 	ext4_ext_mark_unwritten(ex);
 
@@ -3875,7 +3870,7 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
 	/* Mark modified extent as dirty */
 	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
 	if (err)
-		return err;
+		goto errout;
 	ext4_ext_show_leaf(inode, path);
 
 	ext4_update_inode_fsync_trans(handle, inode, 1);
@@ -3884,7 +3879,11 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
 	if (*allocated > map->m_len)
 		*allocated = map->m_len;
 	map->m_len = *allocated;
-	return 0;
+	return path;
+
+errout:
+	ext4_free_ext_path(path);
+	return ERR_PTR(err);
 }
 
 static struct ext4_ext_path *
@@ -4254,8 +4253,10 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 			 */
 			if ((!ext4_ext_is_unwritten(ex)) &&
 			    (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN)) {
-				err = convert_initialized_extent(handle,
-					inode, map, &path, &allocated);
+				path = convert_initialized_extent(handle,
+					inode, map, path, &allocated);
+				if (IS_ERR(path))
+					err = PTR_ERR(path);
 				goto out;
 			} else if (!ext4_ext_is_unwritten(ex)) {
 				map->m_flags |= EXT4_MAP_MAPPED;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH 20/20] ext4: avoid unnecessary extent path frees and allocations
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (18 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 19/20] ext4: get rid of ppath in convert_initialized_extent() libaokun
@ 2024-07-10  4:06 ` libaokun
  2024-07-25 12:31   ` Jan Kara
  2024-08-02 20:58   ` Ojaswin Mujoo
  2024-07-25  9:02 ` [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path Ojaswin Mujoo
  20 siblings, 2 replies; 84+ messages in thread
From: libaokun @ 2024-07-10  4:06 UTC (permalink / raw)
  To: linux-ext4
  Cc: tytso, adilger.kernel, jack, ritesh.list, linux-kernel, yi.zhang,
	yangerkun, libaokun, Baokun Li

From: Baokun Li <libaokun1@huawei.com>

The ext4_find_extent() can update the extent path so that it does not have
to allocate and free the path repeatedly, thus reducing the consumption of
memory allocation and freeing in the following functions:

    ext4_ext_clear_bb
    ext4_ext_replay_set_iblocks
    ext4_fc_replay_add_range
    ext4_fc_set_bitmaps_and_counters

No functional changes. Note that ext4_find_extent() does not support error
pointers, so in this case set path to NULL first.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c     | 51 +++++++++++++++++++------------------------
 fs/ext4/fast_commit.c | 11 ++++++----
 2 files changed, 29 insertions(+), 33 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 737432bb316e..5ff92cd50dc8 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -6068,12 +6068,9 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
 	if (IS_ERR(path))
 		return PTR_ERR(path);
 	ex = path[path->p_depth].p_ext;
-	if (!ex) {
-		ext4_free_ext_path(path);
+	if (!ex)
 		goto out;
-	}
 	end = le32_to_cpu(ex->ee_block) + ext4_ext_get_actual_len(ex);
-	ext4_free_ext_path(path);
 
 	/* Count the number of data blocks */
 	cur = 0;
@@ -6099,32 +6096,28 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
 	ret = skip_hole(inode, &cur);
 	if (ret < 0)
 		goto out;
-	path = ext4_find_extent(inode, cur, NULL, 0);
+	path = ext4_find_extent(inode, cur, path, 0);
 	if (IS_ERR(path))
 		goto out;
 	numblks += path->p_depth;
-	ext4_free_ext_path(path);
 	while (cur < end) {
-		path = ext4_find_extent(inode, cur, NULL, 0);
+		path = ext4_find_extent(inode, cur, path, 0);
 		if (IS_ERR(path))
 			break;
 		ex = path[path->p_depth].p_ext;
-		if (!ex) {
-			ext4_free_ext_path(path);
-			return 0;
-		}
+		if (!ex)
+			goto cleanup;
+
 		cur = max(cur + 1, le32_to_cpu(ex->ee_block) +
 					ext4_ext_get_actual_len(ex));
 		ret = skip_hole(inode, &cur);
-		if (ret < 0) {
-			ext4_free_ext_path(path);
+		if (ret < 0)
 			break;
-		}
-		path2 = ext4_find_extent(inode, cur, NULL, 0);
-		if (IS_ERR(path2)) {
-			ext4_free_ext_path(path);
+
+		path2 = ext4_find_extent(inode, cur, path2, 0);
+		if (IS_ERR(path2))
 			break;
-		}
+
 		for (i = 0; i <= max(path->p_depth, path2->p_depth); i++) {
 			cmp1 = cmp2 = 0;
 			if (i <= path->p_depth)
@@ -6136,13 +6129,14 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
 			if (cmp1 != cmp2 && cmp2 != 0)
 				numblks++;
 		}
-		ext4_free_ext_path(path);
-		ext4_free_ext_path(path2);
 	}
 
 out:
 	inode->i_blocks = numblks << (inode->i_sb->s_blocksize_bits - 9);
 	ext4_mark_inode_dirty(NULL, inode);
+cleanup:
+	ext4_free_ext_path(path);
+	ext4_free_ext_path(path2);
 	return 0;
 }
 
@@ -6163,12 +6157,9 @@ int ext4_ext_clear_bb(struct inode *inode)
 	if (IS_ERR(path))
 		return PTR_ERR(path);
 	ex = path[path->p_depth].p_ext;
-	if (!ex) {
-		ext4_free_ext_path(path);
-		return 0;
-	}
+	if (!ex)
+		goto out;
 	end = le32_to_cpu(ex->ee_block) + ext4_ext_get_actual_len(ex);
-	ext4_free_ext_path(path);
 
 	cur = 0;
 	while (cur < end) {
@@ -6178,16 +6169,16 @@ int ext4_ext_clear_bb(struct inode *inode)
 		if (ret < 0)
 			break;
 		if (ret > 0) {
-			path = ext4_find_extent(inode, map.m_lblk, NULL, 0);
-			if (!IS_ERR_OR_NULL(path)) {
+			path = ext4_find_extent(inode, map.m_lblk, path, 0);
+			if (!IS_ERR(path)) {
 				for (j = 0; j < path->p_depth; j++) {
-
 					ext4_mb_mark_bb(inode->i_sb,
 							path[j].p_block, 1, false);
 					ext4_fc_record_regions(inode->i_sb, inode->i_ino,
 							0, path[j].p_block, 1, 1);
 				}
-				ext4_free_ext_path(path);
+			} else {
+				path = NULL;
 			}
 			ext4_mb_mark_bb(inode->i_sb, map.m_pblk, map.m_len, false);
 			ext4_fc_record_regions(inode->i_sb, inode->i_ino,
@@ -6196,5 +6187,7 @@ int ext4_ext_clear_bb(struct inode *inode)
 		cur = cur + map.m_len;
 	}
 
+out:
+	ext4_free_ext_path(path);
 	return 0;
 }
diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
index 1dee40477727..1426d595fab7 100644
--- a/fs/ext4/fast_commit.c
+++ b/fs/ext4/fast_commit.c
@@ -1766,7 +1766,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
 
 		if (ret == 0) {
 			/* Range is not mapped */
-			path = ext4_find_extent(inode, cur, NULL, 0);
+			path = ext4_find_extent(inode, cur, path, 0);
 			if (IS_ERR(path))
 				goto out;
 			memset(&newex, 0, sizeof(newex));
@@ -1782,7 +1782,6 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
 			up_write((&EXT4_I(inode)->i_data_sem));
 			if (IS_ERR(path))
 				goto out;
-			ext4_free_ext_path(path);
 			goto next;
 		}
 
@@ -1830,6 +1829,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
 	ext4_ext_replay_shrink_inode(inode, i_size_read(inode) >>
 					sb->s_blocksize_bits);
 out:
+	ext4_free_ext_path(path);
 	iput(inode);
 	return 0;
 }
@@ -1930,12 +1930,13 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
 				break;
 
 			if (ret > 0) {
-				path = ext4_find_extent(inode, map.m_lblk, NULL, 0);
+				path = ext4_find_extent(inode, map.m_lblk, path, 0);
 				if (!IS_ERR(path)) {
 					for (j = 0; j < path->p_depth; j++)
 						ext4_mb_mark_bb(inode->i_sb,
 							path[j].p_block, 1, true);
-					ext4_free_ext_path(path);
+				} else {
+					path = NULL;
 				}
 				cur += ret;
 				ext4_mb_mark_bb(inode->i_sb, map.m_pblk,
@@ -1946,6 +1947,8 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
 		}
 		iput(inode);
 	}
+
+	ext4_free_ext_path(path);
 }
 
 /*
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-10  4:06 ` [PATCH 02/20] ext4: prevent partial update of the extents path libaokun
@ 2024-07-14 15:42   ` Ojaswin Mujoo
  2024-07-15 12:33     ` Baokun Li
  2024-07-24 18:53   ` Jan Kara
  1 sibling, 1 reply; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-14 15:42 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin

On Wed, Jul 10, 2024 at 12:06:36PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> In ext4_ext_rm_idx() and ext4_ext_correct_indexes(), there is no proper
> rollback of already executed updates when updating a level of the extents
> path fails, so we may get an inconsistent extents tree, which may trigger
> some bad things in errors=continue mode.
> 
> Hence clear the verified bit of modified extents buffers if the tree fails
> to be updated in ext4_ext_rm_idx() or ext4_ext_correct_indexes(), which
> forces the extents buffers to be checked in ext4_valid_extent_entries(),
> ensuring that the extents tree is consistent.
> 
> Signed-off-by: zhanchengbin <zhanchengbin1@huawei.com>
> Link: https://lore.kernel.org/r/20230213080514.535568-3-zhanchengbin1@huawei.com/
> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> ---
>  fs/ext4/extents.c | 31 +++++++++++++++++++++++++++----
>  1 file changed, 27 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index bff3666c891a..4d589d34b30e 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -1749,12 +1749,23 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
>       break;
>     err = ext4_ext_get_access(handle, inode, path + k);
>     if (err)
> -     break;
> +     goto clean;
>     path[k].p_idx->ei_block = border;
>     err = ext4_ext_dirty(handle, inode, path + k);
>     if (err)
> -     break;
> +     goto clean;
>   }
> + return 0;
> +
> +clean:
> + /*
> +  * The path[k].p_bh is either unmodified or with no verified bit
> +  * set (see ext4_ext_get_access()). So just clear the verified bit
> +  * of the successfully modified extents buffers, which will force
> +  * these extents to be checked to avoid using inconsistent data.
> +  */
> + while (++k < depth)
> +   clear_buffer_verified(path[k].p_bh);
>  
>   return err;
>  }
> @@ -2312,12 +2323,24 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
>       break;
>     err = ext4_ext_get_access(handle, inode, path + k);
>     if (err)
> -     break;
> +     goto clean;
>     path[k].p_idx->ei_block = path[k + 1].p_idx->ei_block;
>     err = ext4_ext_dirty(handle, inode, path + k);
>     if (err)
> -     break;
> +     goto clean;
>   }
> + return 0;
> +
> +clean:
> + /*
> +  * The path[k].p_bh is either unmodified or with no verified bit
> +  * set (see ext4_ext_get_access()). So just clear the verified bit
> +  * of the successfully modified extents buffers, which will force
> +  * these extents to be checked to avoid using inconsistent data.
> +  */
> + while (++k < depth)
> +   clear_buffer_verified(path[k].p_bh);
> +
>   return err;
>  }

Hi Baokun,

So I wanted to understand the extent handling paths for a whil and thought this
patchset was a good chance to finally take sometime and do that.

I do have a question based on my understanding of this extent deletion code:

So IIUC, ext4_find_extent() will return a path where buffer of each node is
verified (via bh = read_extent_tree_block()). So imagine we have the following
path (d=depth, blk=idx.ei_block, v=verified, nv=not-verified):

+------+     +------+     +------+     +------+     +------+
|d=0   |     |d=1   |     |d=2   |     |d=3   |     |      |
|blk=1 | --> |blk=1 | --> |blk=1 | --> |blk=1 | --> |pblk=1|
|(v)   |     |(v)   |     |(v)   |     |(v)   |     |      |
+------+     +------+     +------+     +------+     +------+
                                       |d=3   |     +------+
                                       |blk=2 | --> |      |
                                       |(v)   |     |pblk=2|
                                       +------+     |      |
                                                    +------+

Here, the the last column are the leaf nodes with only 1 extent in them.  Now,
say we want to punch the leaf having pblk=1. We'll eventually call
ext4_ext_rm_leaf() -> ext4_ext_rm_idx() to remove the index at depth = 3 and
try fixin up the indices in path with ei_block = 2

Suppose we error out at depth == 1. After the cleanup (introduced in this
patch), we'll mark depth = 1 to 4 as non verified:

+------+     +------+     +------+     +------+     +------+
|d=0   |     |d=1   |     |d=2   |     |d=3   |     |      |
|blk=1 | --> |blk=1 | --> |blk=2 | --> |blk=2 | --> |pblk=2|
|(v)   |     |(nv)  |     |(nv)  |     |(nv)  |     |(nv)  |
+------+     +------+     +------+     +------+     +------+

And we return and we won't actually mark the FS corrupt until we try to check
the bh at depth = 1 above. In this case, the first node is still pointing to
wrong ei_block but is marked valid. Aren't we silently leaving the tree in an
inconsistent state which might lead to corrupted lookups until we actually
touch the affected bh and realise that there's a corruption.

Am I missing some codepath here? Should we maybe try to clear_valid for the
whole tree?

(I hope the formatting of diagram comes out correct :) )

Regards,
ojaswin

>  
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-14 15:42   ` Ojaswin Mujoo
@ 2024-07-15 12:33     ` Baokun Li
  2024-07-15 12:41       ` Baokun Li
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-15 12:33 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin,
	Baokun Li

Hi Ojaswin!

On 2024/7/14 23:42, Ojaswin Mujoo wrote:
> On Wed, Jul 10, 2024 at 12:06:36PM +0800, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> In ext4_ext_rm_idx() and ext4_ext_correct_indexes(), there is no proper
>> rollback of already executed updates when updating a level of the extents
>> path fails, so we may get an inconsistent extents tree, which may trigger
>> some bad things in errors=continue mode.
>>
>> Hence clear the verified bit of modified extents buffers if the tree fails
>> to be updated in ext4_ext_rm_idx() or ext4_ext_correct_indexes(), which
>> forces the extents buffers to be checked in ext4_valid_extent_entries(),
>> ensuring that the extents tree is consistent.
>>
>> Signed-off-by: zhanchengbin <zhanchengbin1@huawei.com>
>> Link: https://lore.kernel.org/r/20230213080514.535568-3-zhanchengbin1@huawei.com/
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
>> ---
>>   fs/ext4/extents.c | 31 +++++++++++++++++++++++++++----
>>   1 file changed, 27 insertions(+), 4 deletions(-)
>>
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index bff3666c891a..4d589d34b30e 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
>> @@ -1749,12 +1749,23 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
>>        break;
>>      err = ext4_ext_get_access(handle, inode, path + k);
>>      if (err)
>> -     break;
>> +     goto clean;
>>      path[k].p_idx->ei_block = border;
>>      err = ext4_ext_dirty(handle, inode, path + k);
>>      if (err)
>> -     break;
>> +     goto clean;
>>    }
>> + return 0;
>> +
>> +clean:
>> + /*
>> +  * The path[k].p_bh is either unmodified or with no verified bit
>> +  * set (see ext4_ext_get_access()). So just clear the verified bit
>> +  * of the successfully modified extents buffers, which will force
>> +  * these extents to be checked to avoid using inconsistent data.
>> +  */
>> + while (++k < depth)
>> +   clear_buffer_verified(path[k].p_bh);
>>   
>>    return err;
>>   }
>> @@ -2312,12 +2323,24 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
>>        break;
>>      err = ext4_ext_get_access(handle, inode, path + k);
>>      if (err)
>> -     break;
>> +     goto clean;
>>      path[k].p_idx->ei_block = path[k + 1].p_idx->ei_block;
>>      err = ext4_ext_dirty(handle, inode, path + k);
>>      if (err)
>> -     break;
>> +     goto clean;
>>    }
>> + return 0;
>> +
>> +clean:
>> + /*
>> +  * The path[k].p_bh is either unmodified or with no verified bit
>> +  * set (see ext4_ext_get_access()). So just clear the verified bit
>> +  * of the successfully modified extents buffers, which will force
>> +  * these extents to be checked to avoid using inconsistent data.
>> +  */
>> + while (++k < depth)
>> +   clear_buffer_verified(path[k].p_bh);
>> +
>>    return err;
>>   }
> Hi Baokun,
>
> So I wanted to understand the extent handling paths for a whil and thought this
> patchset was a good chance to finally take sometime and do that.
>
> I do have a question based on my understanding of this extent deletion code:
>
> So IIUC, ext4_find_extent() will return a path where buffer of each node is
> verified (via bh = read_extent_tree_block()). So imagine we have the following
> path (d=depth, blk=idx.ei_block, v=verified, nv=not-verified):
>
> +------+     +------+     +------+     +------+     +------+
> |d=0   |     |d=1   |     |d=2   |     |d=3   |     |      |
> |blk=1 | --> |blk=1 | --> |blk=1 | --> |blk=1 | --> |pblk=1|
> |(v)   |     |(v)   |     |(v)   |     |(v)   |     |      |
> +------+     +------+     +------+     +------+     +------+
>                                         |d=3   |     +------+
>                                         |blk=2 | --> |      |
>                                         |(v)   |     |pblk=2|
>                                         +------+     |      |
>                                                      +------+
>
> Here, the the last column are the leaf nodes with only 1 extent in them.  Now,
> say we want to punch the leaf having pblk=1. We'll eventually call
> ext4_ext_rm_leaf() -> ext4_ext_rm_idx() to remove the index at depth = 3 and
> try fixin up the indices in path with ei_block = 2
>
> Suppose we error out at depth == 1. After the cleanup (introduced in this
> patch), we'll mark depth = 1 to 4 as non verified:
>
> +------+     +------+     +------+     +------+     +------+
> |d=0   |     |d=1   |     |d=2   |     |d=3   |     |      |
> |blk=1 | --> |blk=1 | --> |blk=2 | --> |blk=2 | --> |pblk=2|
> |(v)   |     |(nv)  |     |(nv)  |     |(nv)  |     |(nv)  |
> +------+     +------+     +------+     +------+     +------+
Exactly right!
>
> And we return and we won't actually mark the FS corrupt until we try to check
> the bh at depth = 1 above. In this case, the first node is still pointing to
> wrong ei_block but is marked valid. Aren't we silently leaving the tree in an
> inconsistent state which might lead to corrupted lookups until we actually
> touch the affected bh and realise that there's a corruption.
>
> Am I missing some codepath here? Should we maybe try to clear_valid for the
> whole tree?
>
> (I hope the formatting of diagram comes out correct :) )
>
> Regards,
> ojaswin
But the journal will ensure the consistency of the extents path after
this patch.

When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
the extents tree to be inconsistent. But the inconsistency just
exists in memory and doesn't land on disk.

For ext4_ext_get_access(), the handle must have been aborted
when it returned an error, as follows:

ext4_ext_get_access
ext4_journal_get_write_access
__ext4_journal_get_write_access
err = jbd2_journal_get_write_access
if (err)
ext4_journal_abort_handle

For ext4_ext_dirty(), since path->p_bh must not be null and handle
must be valid, handle is aborted anyway when an error is returned:

ext4_ext_dirty __ext4_ext_dirty if (path->p_bh) 
__ext4_handle_dirty_metadata if (ext4_handle_valid(handle)) err = 
jbd2_journal_dirty_metadata if (!is_handle_aborted(handle) && 
WARN_ON_ONCE(err)) ext4_journal_abort_handle
Thus the extents tree will only be inconsistent in memory, so only
the verified bit of the modified buffer needs to be cleared to avoid
these inconsistent data being used in memory.

Regards,
Baokun


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-15 12:33     ` Baokun Li
@ 2024-07-15 12:41       ` Baokun Li
  2024-07-16  9:54         ` Ojaswin Mujoo
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-15 12:41 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin,
	Baokun Li

On 2024/7/15 20:33, Baokun Li wrote:
> Hi Ojaswin!
>
> On 2024/7/14 23:42, Ojaswin Mujoo wrote:
>> On Wed, Jul 10, 2024 at 12:06:36PM +0800, libaokun@huaweicloud.com 
>> wrote:
>>> From: Baokun Li <libaokun1@huawei.com>
>>>
>>> In ext4_ext_rm_idx() and ext4_ext_correct_indexes(), there is no proper
>>> rollback of already executed updates when updating a level of the 
>>> extents
>>> path fails, so we may get an inconsistent extents tree, which may 
>>> trigger
>>> some bad things in errors=continue mode.
>>>
>>> Hence clear the verified bit of modified extents buffers if the tree 
>>> fails
>>> to be updated in ext4_ext_rm_idx() or ext4_ext_correct_indexes(), which
>>> forces the extents buffers to be checked in 
>>> ext4_valid_extent_entries(),
>>> ensuring that the extents tree is consistent.
>>>
>>> Signed-off-by: zhanchengbin <zhanchengbin1@huawei.com>
>>> Link: 
>>> https://lore.kernel.org/r/20230213080514.535568-3-zhanchengbin1@huawei.com/
>>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
>>> ---
>>>   fs/ext4/extents.c | 31 +++++++++++++++++++++++++++----
>>>   1 file changed, 27 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>>> index bff3666c891a..4d589d34b30e 100644
>>> --- a/fs/ext4/extents.c
>>> +++ b/fs/ext4/extents.c
>>> @@ -1749,12 +1749,23 @@ static int ext4_ext_correct_indexes(handle_t 
>>> *handle, struct inode *inode,
>>>        break;
>>>      err = ext4_ext_get_access(handle, inode, path + k);
>>>      if (err)
>>> -     break;
>>> +     goto clean;
>>>      path[k].p_idx->ei_block = border;
>>>      err = ext4_ext_dirty(handle, inode, path + k);
>>>      if (err)
>>> -     break;
>>> +     goto clean;
>>>    }
>>> + return 0;
>>> +
>>> +clean:
>>> + /*
>>> +  * The path[k].p_bh is either unmodified or with no verified bit
>>> +  * set (see ext4_ext_get_access()). So just clear the verified bit
>>> +  * of the successfully modified extents buffers, which will force
>>> +  * these extents to be checked to avoid using inconsistent data.
>>> +  */
>>> + while (++k < depth)
>>> +   clear_buffer_verified(path[k].p_bh);
>>>      return err;
>>>   }
>>> @@ -2312,12 +2323,24 @@ static int ext4_ext_rm_idx(handle_t *handle, 
>>> struct inode *inode,
>>>        break;
>>>      err = ext4_ext_get_access(handle, inode, path + k);
>>>      if (err)
>>> -     break;
>>> +     goto clean;
>>>      path[k].p_idx->ei_block = path[k + 1].p_idx->ei_block;
>>>      err = ext4_ext_dirty(handle, inode, path + k);
>>>      if (err)
>>> -     break;
>>> +     goto clean;
>>>    }
>>> + return 0;
>>> +
>>> +clean:
>>> + /*
>>> +  * The path[k].p_bh is either unmodified or with no verified bit
>>> +  * set (see ext4_ext_get_access()). So just clear the verified bit
>>> +  * of the successfully modified extents buffers, which will force
>>> +  * these extents to be checked to avoid using inconsistent data.
>>> +  */
>>> + while (++k < depth)
>>> +   clear_buffer_verified(path[k].p_bh);
>>> +
>>>    return err;
>>>   }
>> Hi Baokun,
>>
>> So I wanted to understand the extent handling paths for a whil and 
>> thought this
>> patchset was a good chance to finally take sometime and do that.
>>
>> I do have a question based on my understanding of this extent 
>> deletion code:
>>
>> So IIUC, ext4_find_extent() will return a path where buffer of each 
>> node is
>> verified (via bh = read_extent_tree_block()). So imagine we have the 
>> following
>> path (d=depth, blk=idx.ei_block, v=verified, nv=not-verified):
>>
>> +------+     +------+     +------+     +------+     +------+
>> |d=0   |     |d=1   |     |d=2   |     |d=3   |     |      |
>> |blk=1 | --> |blk=1 | --> |blk=1 | --> |blk=1 | --> |pblk=1|
>> |(v)   |     |(v)   |     |(v)   |     |(v)   |     |      |
>> +------+     +------+     +------+     +------+     +------+
>>                                         |d=3   |     +------+
>>                                         |blk=2 | --> |      |
>>                                         |(v)   |     |pblk=2|
>>                                         +------+     |      |
>>                                                      +------+
>>
>> Here, the the last column are the leaf nodes with only 1 extent in 
>> them.  Now,
>> say we want to punch the leaf having pblk=1. We'll eventually call
>> ext4_ext_rm_leaf() -> ext4_ext_rm_idx() to remove the index at depth 
>> = 3 and
>> try fixin up the indices in path with ei_block = 2
>>
>> Suppose we error out at depth == 1. After the cleanup (introduced in 
>> this
>> patch), we'll mark depth = 1 to 4 as non verified:
>>
>> +------+     +------+     +------+     +------+     +------+
>> |d=0   |     |d=1   |     |d=2   |     |d=3   |     |      |
>> |blk=1 | --> |blk=1 | --> |blk=2 | --> |blk=2 | --> |pblk=2|
>> |(v)   |     |(nv)  |     |(nv)  |     |(nv)  |     |(nv)  |
>> +------+     +------+     +------+     +------+     +------+
> Exactly right!
>>
>> And we return and we won't actually mark the FS corrupt until we try 
>> to check
>> the bh at depth = 1 above. In this case, the first node is still 
>> pointing to
>> wrong ei_block but is marked valid. Aren't we silently leaving the 
>> tree in an
>> inconsistent state which might lead to corrupted lookups until we 
>> actually
>> touch the affected bh and realise that there's a corruption.
>>
>> Am I missing some codepath here? Should we maybe try to clear_valid 
>> for the
>> whole tree?
>>
>> (I hope the formatting of diagram comes out correct :) )
Uh, I'm sorry, my diagram is disordered. 😅
>>
>> Regards,
>> ojaswin
> But the journal will ensure the consistency of the extents path after
> this patch.
>
> When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
> ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
> the extents tree to be inconsistent. But the inconsistency just
> exists in memory and doesn't land on disk.
>
> For ext4_ext_get_access(), the handle must have been aborted
> when it returned an error, as follows:
ext4_ext_get_access
  ext4_journal_get_write_access
   __ext4_journal_get_write_access
    err = jbd2_journal_get_write_access
    if (err)
      ext4_journal_abort_handle
> For ext4_ext_dirty(), since path->p_bh must not be null and handle
> must be valid, handle is aborted anyway when an error is returned:
ext4_ext_dirty
  __ext4_ext_dirty
   if (path->p_bh)
     __ext4_handle_dirty_metadata
      if (ext4_handle_valid(handle))
        err = jbd2_journal_dirty_metadata
         if (!is_handle_aborted(handle) && WARN_ON_ONCE(err))
           ext4_journal_abort_handle
> Thus the extents tree will only be inconsistent in memory, so only
> the verified bit of the modified buffer needs to be cleared to avoid
> these inconsistent data being used in memory.
>
Regards,
Baokun


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-15 12:41       ` Baokun Li
@ 2024-07-16  9:54         ` Ojaswin Mujoo
  2024-07-16 11:54           ` Baokun Li
  0 siblings, 1 reply; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-16  9:54 UTC (permalink / raw)
  To: Baokun Li
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin


> > But the journal will ensure the consistency of the extents path after
> > this patch.
> > 
> > When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
> > ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
> > the extents tree to be inconsistent. But the inconsistency just
> > exists in memory and doesn't land on disk.
> > 
> > For ext4_ext_get_access(), the handle must have been aborted
> > when it returned an error, as follows:
> ext4_ext_get_access
>  ext4_journal_get_write_access
>   __ext4_journal_get_write_access
>    err = jbd2_journal_get_write_access
>    if (err)
>      ext4_journal_abort_handle
> > For ext4_ext_dirty(), since path->p_bh must not be null and handle
> > must be valid, handle is aborted anyway when an error is returned:
> ext4_ext_dirty
>  __ext4_ext_dirty
>   if (path->p_bh)
>     __ext4_handle_dirty_metadata
>      if (ext4_handle_valid(handle))
>        err = jbd2_journal_dirty_metadata
>         if (!is_handle_aborted(handle) && WARN_ON_ONCE(err))
>           ext4_journal_abort_handle
> > Thus the extents tree will only be inconsistent in memory, so only
> > the verified bit of the modified buffer needs to be cleared to avoid
> > these inconsistent data being used in memory.
> > 
> Regards,
> Baokun

Thanks for the explanation Baokun, so basically we only have the
inconsitency in the memory.

I do have a followup questions:

So in the above example, after we have the error, we'll have the buffer
for depth=0 marked as valid but pointing to the wrong ei_block.

In this case, can we have something like below:

-----------------
ext4_ext_remove_space
  err = ext4_ext_rm_idx (error, path[0].p_bh inconsistent but verified)
  /* 
   * we release buffers of the path but path[0].p_bh is not cleaned up
   * due to other references to it (possible?)
   */

... at a later point...:

ext4_find_extent
  bh = read_extent_tree_block() 
    /* 
     * we get the bh that was left inconsistent previously
     * since its verified, we dont check it again corrupting
     * the lookup
     */

-----------------

Is the above scenario possible? Or would the path[0].p_bh that was
corrupted previously always be reread during the subsequent
ext4_find_extent() lookup?

Thanks again,
Ojaswin

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-16  9:54         ` Ojaswin Mujoo
@ 2024-07-16 11:54           ` Baokun Li
  2024-07-17  5:29             ` Ojaswin Mujoo
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-16 11:54 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin,
	Baokun Li

Hi Ojaswin,

On 2024/7/16 17:54, Ojaswin Mujoo wrote:
>>> But the journal will ensure the consistency of the extents path after
>>> this patch.
>>>
>>> When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
>>> ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
>>> the extents tree to be inconsistent. But the inconsistency just
>>> exists in memory and doesn't land on disk.
>>>
>>> For ext4_ext_get_access(), the handle must have been aborted
>>> when it returned an error, as follows:
>> ext4_ext_get_access
>>   ext4_journal_get_write_access
>>    __ext4_journal_get_write_access
>>     err = jbd2_journal_get_write_access
>>     if (err)
>>       ext4_journal_abort_handle
>>> For ext4_ext_dirty(), since path->p_bh must not be null and handle
>>> must be valid, handle is aborted anyway when an error is returned:
>> ext4_ext_dirty
>>   __ext4_ext_dirty
>>    if (path->p_bh)
>>      __ext4_handle_dirty_metadata
>>       if (ext4_handle_valid(handle))
>>         err = jbd2_journal_dirty_metadata
>>          if (!is_handle_aborted(handle) && WARN_ON_ONCE(err))
>>            ext4_journal_abort_handle
>>> Thus the extents tree will only be inconsistent in memory, so only
>>> the verified bit of the modified buffer needs to be cleared to avoid
>>> these inconsistent data being used in memory.
>>>
>> Regards,
>> Baokun
> Thanks for the explanation Baokun, so basically we only have the
> inconsitency in the memory.
>
> I do have a followup questions:
>
> So in the above example, after we have the error, we'll have the buffer
> for depth=0 marked as valid but pointing to the wrong ei_block.
It looks wrong here. When there is an error, the ei_block of the
unmodified buffer with depth=0 is the correct one, it is indeed
'valid' and it is consistent with the disk. Only buffers that were
modified during the error process need to be checked.

Regards,
Baokun
>
> In this case, can we have something like below:
>
> -----------------
> ext4_ext_remove_space
>    err = ext4_ext_rm_idx (error, path[0].p_bh inconsistent but verified)
>    /*
>     * we release buffers of the path but path[0].p_bh is not cleaned up
>     * due to other references to it (possible?)
>     */
>
> ... at a later point...:
>
> ext4_find_extent
>    bh = read_extent_tree_block()
>      /*
>       * we get the bh that was left inconsistent previously
>       * since its verified, we dont check it again corrupting
>       * the lookup
>       */
>
> -----------------
>
> Is the above scenario possible? Or would the path[0].p_bh that was
> corrupted previously always be reread during the subsequent
> ext4_find_extent() lookup?
>
> Thanks again,
> Ojaswin


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-16 11:54           ` Baokun Li
@ 2024-07-17  5:29             ` Ojaswin Mujoo
  2024-07-17  6:11               ` Baokun Li
  0 siblings, 1 reply; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-17  5:29 UTC (permalink / raw)
  To: Baokun Li
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin

On Tue, Jul 16, 2024 at 07:54:43PM +0800, Baokun Li wrote:
> Hi Ojaswin,
> 
> On 2024/7/16 17:54, Ojaswin Mujoo wrote:
> > > > But the journal will ensure the consistency of the extents path after
> > > > this patch.
> > > > 
> > > > When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
> > > > ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
> > > > the extents tree to be inconsistent. But the inconsistency just
> > > > exists in memory and doesn't land on disk.
> > > > 
> > > > For ext4_ext_get_access(), the handle must have been aborted
> > > > when it returned an error, as follows:
> > > ext4_ext_get_access
> > >   ext4_journal_get_write_access
> > >    __ext4_journal_get_write_access
> > >     err = jbd2_journal_get_write_access
> > >     if (err)
> > >       ext4_journal_abort_handle
> > > > For ext4_ext_dirty(), since path->p_bh must not be null and handle
> > > > must be valid, handle is aborted anyway when an error is returned:
> > > ext4_ext_dirty
> > >   __ext4_ext_dirty
> > >    if (path->p_bh)
> > >      __ext4_handle_dirty_metadata
> > >       if (ext4_handle_valid(handle))
> > >         err = jbd2_journal_dirty_metadata
> > >          if (!is_handle_aborted(handle) && WARN_ON_ONCE(err))
> > >            ext4_journal_abort_handle
> > > > Thus the extents tree will only be inconsistent in memory, so only
> > > > the verified bit of the modified buffer needs to be cleared to avoid
> > > > these inconsistent data being used in memory.
> > > > 
> > > Regards,
> > > Baokun
> > Thanks for the explanation Baokun, so basically we only have the
> > inconsitency in the memory.
> > 
> > I do have a followup questions:
> > 
> > So in the above example, after we have the error, we'll have the buffer
> > for depth=0 marked as valid but pointing to the wrong ei_block.
> It looks wrong here. When there is an error, the ei_block of the
> unmodified buffer with depth=0 is the correct one, it is indeed
> 'valid' and it is consistent with the disk. Only buffers that were

Hey Baokun,

Ahh I see now, I was looking at it the wrong way. So basically since
depth 1 to 4 is inconsistent to the disk we mark then non verified so
then subsequent lookups can act accordingly.

Thanks for the explanation! I am in the middle of testing this patchset
with xfstests on a POWERPC system with 64k page size. I'll let you know
how that goes!

Regards,
Ojaswin
> modified during the error process need to be checked.
> 
> Regards,
> Baokun

> > 
> > In this case, can we have something like below:
> > 
> > -----------------
> > ext4_ext_remove_space
> >    err = ext4_ext_rm_idx (error, path[0].p_bh inconsistent but verified)
> >    /*
> >     * we release buffers of the path but path[0].p_bh is not cleaned up
> >     * due to other references to it (possible?)
> >     */
> > 
> > ... at a later point...:
> > 
> > ext4_find_extent
> >    bh = read_extent_tree_block()
> >      /*
> >       * we get the bh that was left inconsistent previously
> >       * since its verified, we dont check it again corrupting
> >       * the lookup
> >       */
> > 
> > -----------------
> > 
> > Is the above scenario possible? Or would the path[0].p_bh that was
> > corrupted previously always be reread during the subsequent
> > ext4_find_extent() lookup?
> > 
> > Thanks again,
> > Ojaswin
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-17  5:29             ` Ojaswin Mujoo
@ 2024-07-17  6:11               ` Baokun Li
  2024-07-24  6:23                 ` Ojaswin Mujoo
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-17  6:11 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin,
	Baokun Li

On 2024/7/17 13:29, Ojaswin Mujoo wrote:
> On Tue, Jul 16, 2024 at 07:54:43PM +0800, Baokun Li wrote:
>> Hi Ojaswin,
>>
>> On 2024/7/16 17:54, Ojaswin Mujoo wrote:
>>>>> But the journal will ensure the consistency of the extents path after
>>>>> this patch.
>>>>>
>>>>> When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
>>>>> ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
>>>>> the extents tree to be inconsistent. But the inconsistency just
>>>>> exists in memory and doesn't land on disk.
>>>>>
>>>>> For ext4_ext_get_access(), the handle must have been aborted
>>>>> when it returned an error, as follows:
>>>> ext4_ext_get_access
>>>>    ext4_journal_get_write_access
>>>>     __ext4_journal_get_write_access
>>>>      err = jbd2_journal_get_write_access
>>>>      if (err)
>>>>        ext4_journal_abort_handle
>>>>> For ext4_ext_dirty(), since path->p_bh must not be null and handle
>>>>> must be valid, handle is aborted anyway when an error is returned:
>>>> ext4_ext_dirty
>>>>    __ext4_ext_dirty
>>>>     if (path->p_bh)
>>>>       __ext4_handle_dirty_metadata
>>>>        if (ext4_handle_valid(handle))
>>>>          err = jbd2_journal_dirty_metadata
>>>>           if (!is_handle_aborted(handle) && WARN_ON_ONCE(err))
>>>>             ext4_journal_abort_handle
>>>>> Thus the extents tree will only be inconsistent in memory, so only
>>>>> the verified bit of the modified buffer needs to be cleared to avoid
>>>>> these inconsistent data being used in memory.
>>>>>
>>>> Regards,
>>>> Baokun
>>> Thanks for the explanation Baokun, so basically we only have the
>>> inconsitency in the memory.
>>>
>>> I do have a followup questions:
>>>
>>> So in the above example, after we have the error, we'll have the buffer
>>> for depth=0 marked as valid but pointing to the wrong ei_block.
>> It looks wrong here. When there is an error, the ei_block of the
>> unmodified buffer with depth=0 is the correct one, it is indeed
>> 'valid' and it is consistent with the disk. Only buffers that were
> Hey Baokun,
>
> Ahh I see now, I was looking at it the wrong way. So basically since
> depth 1 to 4 is inconsistent to the disk we mark then non verified so
> then subsequent lookups can act accordingly.
>
> Thanks for the explanation! I am in the middle of testing this patchset
> with xfstests on a POWERPC system with 64k page size. I'll let you know
> how that goes!
>
> Regards,
> Ojaswin

Hi Ojaswin,

Thank you for the test and feedback!

Cheers,
Baokun


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-17  6:11               ` Baokun Li
@ 2024-07-24  6:23                 ` Ojaswin Mujoo
  2024-07-25  5:35                   ` Baokun Li
  0 siblings, 1 reply; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-24  6:23 UTC (permalink / raw)
  To: Baokun Li
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin

On Wed, Jul 17, 2024 at 02:11:27PM +0800, Baokun Li wrote:
> On 2024/7/17 13:29, Ojaswin Mujoo wrote:
> > On Tue, Jul 16, 2024 at 07:54:43PM +0800, Baokun Li wrote:
> > > Hi Ojaswin,
> > > 
> > > On 2024/7/16 17:54, Ojaswin Mujoo wrote:
> > > > > > But the journal will ensure the consistency of the extents path after
> > > > > > this patch.
> > > > > > 
> > > > > > When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
> > > > > > ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
> > > > > > the extents tree to be inconsistent. But the inconsistency just
> > > > > > exists in memory and doesn't land on disk.
> > > > > > 
> > > > > > For ext4_ext_get_access(), the handle must have been aborted
> > > > > > when it returned an error, as follows:
> > > > > ext4_ext_get_access
> > > > >    ext4_journal_get_write_access
> > > > >     __ext4_journal_get_write_access
> > > > >      err = jbd2_journal_get_write_access
> > > > >      if (err)
> > > > >        ext4_journal_abort_handle
> > > > > > For ext4_ext_dirty(), since path->p_bh must not be null and handle
> > > > > > must be valid, handle is aborted anyway when an error is returned:
> > > > > ext4_ext_dirty
> > > > >    __ext4_ext_dirty
> > > > >     if (path->p_bh)
> > > > >       __ext4_handle_dirty_metadata
> > > > >        if (ext4_handle_valid(handle))
> > > > >          err = jbd2_journal_dirty_metadata
> > > > >           if (!is_handle_aborted(handle) && WARN_ON_ONCE(err))
> > > > >             ext4_journal_abort_handle
> > > > > > Thus the extents tree will only be inconsistent in memory, so only
> > > > > > the verified bit of the modified buffer needs to be cleared to avoid
> > > > > > these inconsistent data being used in memory.
> > > > > > 
> > > > > Regards,
> > > > > Baokun
> > > > Thanks for the explanation Baokun, so basically we only have the
> > > > inconsitency in the memory.
> > > > 
> > > > I do have a followup questions:
> > > > 
> > > > So in the above example, after we have the error, we'll have the buffer
> > > > for depth=0 marked as valid but pointing to the wrong ei_block.
> > > It looks wrong here. When there is an error, the ei_block of the
> > > unmodified buffer with depth=0 is the correct one, it is indeed
> > > 'valid' and it is consistent with the disk. Only buffers that were
> > Hey Baokun,
> > 
> > Ahh I see now, I was looking at it the wrong way. So basically since
> > depth 1 to 4 is inconsistent to the disk we mark then non verified so
> > then subsequent lookups can act accordingly.
> > 
> > Thanks for the explanation! I am in the middle of testing this patchset
> > with xfstests on a POWERPC system with 64k page size. I'll let you know
> > how that goes!
> > 
> > Regards,
> > Ojaswin
> 
> Hi Ojaswin,
> 
> Thank you for the test and feedback!
> 
> Cheers,
> Baokun

Hey Baokun,

The xfstests pass for sub page size as well as bs = page size for
POWERPC with no new regressions.

Although for this particular patch I doubt if we would be able to
exersice the error path using xfstests. We might need to artifically 
inject error in ext4_ext_get_access or ext4_ext_dirty.  Do you have any
other way of testing this? 

Also, just curious whether you came across this bug during code reading
or were you actually hitting it?

Regards,
Ojaswin
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path'
  2024-07-10  4:06 ` [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path' libaokun
@ 2024-07-24 18:44   ` Jan Kara
  2024-07-25  9:14   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-24 18:44 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:35, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> As suggested by Honza in Link,modify ext4_ext_rm_idx() to leave 'path'
> alone and just index it like ext4_ext_correct_indexes() does it. This
> facilitates adding error handling later. No functional changes.
> 
> Suggested-by: Jan Kara <jack@suse.cz>
> Link: https://lore.kernel.org/all/20230216130305.nrbtd42tppxhbynn@quack3/
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Yeah, nice. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 32 +++++++++++++++-----------------
>  1 file changed, 15 insertions(+), 17 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index e067f2dd0335..bff3666c891a 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -2279,27 +2279,26 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
>  {
>  	int err;
>  	ext4_fsblk_t leaf;
> +	int k = depth - 1;
>  
>  	/* free index block */
> -	depth--;
> -	path = path + depth;
> -	leaf = ext4_idx_pblock(path->p_idx);
> -	if (unlikely(path->p_hdr->eh_entries == 0)) {
> -		EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0");
> +	leaf = ext4_idx_pblock(path[k].p_idx);
> +	if (unlikely(path[k].p_hdr->eh_entries == 0)) {
> +		EXT4_ERROR_INODE(inode, "path[%d].p_hdr->eh_entries == 0", k);
>  		return -EFSCORRUPTED;
>  	}
> -	err = ext4_ext_get_access(handle, inode, path);
> +	err = ext4_ext_get_access(handle, inode, path + k);
>  	if (err)
>  		return err;
>  
> -	if (path->p_idx != EXT_LAST_INDEX(path->p_hdr)) {
> -		int len = EXT_LAST_INDEX(path->p_hdr) - path->p_idx;
> +	if (path[k].p_idx != EXT_LAST_INDEX(path[k].p_hdr)) {
> +		int len = EXT_LAST_INDEX(path[k].p_hdr) - path[k].p_idx;
>  		len *= sizeof(struct ext4_extent_idx);
> -		memmove(path->p_idx, path->p_idx + 1, len);
> +		memmove(path[k].p_idx, path[k].p_idx + 1, len);
>  	}
>  
> -	le16_add_cpu(&path->p_hdr->eh_entries, -1);
> -	err = ext4_ext_dirty(handle, inode, path);
> +	le16_add_cpu(&path[k].p_hdr->eh_entries, -1);
> +	err = ext4_ext_dirty(handle, inode, path + k);
>  	if (err)
>  		return err;
>  	ext_debug(inode, "index is empty, remove it, free block %llu\n", leaf);
> @@ -2308,15 +2307,14 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
>  	ext4_free_blocks(handle, inode, NULL, leaf, 1,
>  			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
>  
> -	while (--depth >= 0) {
> -		if (path->p_idx != EXT_FIRST_INDEX(path->p_hdr))
> +	while (--k >= 0) {
> +		if (path[k + 1].p_idx != EXT_FIRST_INDEX(path[k + 1].p_hdr))
>  			break;
> -		path--;
> -		err = ext4_ext_get_access(handle, inode, path);
> +		err = ext4_ext_get_access(handle, inode, path + k);
>  		if (err)
>  			break;
> -		path->p_idx->ei_block = (path+1)->p_idx->ei_block;
> -		err = ext4_ext_dirty(handle, inode, path);
> +		path[k].p_idx->ei_block = path[k + 1].p_idx->ei_block;
> +		err = ext4_ext_dirty(handle, inode, path + k);
>  		if (err)
>  			break;
>  	}
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-10  4:06 ` [PATCH 02/20] ext4: prevent partial update of the extents path libaokun
  2024-07-14 15:42   ` Ojaswin Mujoo
@ 2024-07-24 18:53   ` Jan Kara
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-24 18:53 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin

On Wed 10-07-24 12:06:36, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> In ext4_ext_rm_idx() and ext4_ext_correct_indexes(), there is no proper
> rollback of already executed updates when updating a level of the extents
> path fails, so we may get an inconsistent extents tree, which may trigger
> some bad things in errors=continue mode.
> 
> Hence clear the verified bit of modified extents buffers if the tree fails
> to be updated in ext4_ext_rm_idx() or ext4_ext_correct_indexes(), which
> forces the extents buffers to be checked in ext4_valid_extent_entries(),
> ensuring that the extents tree is consistent.
> 
> Signed-off-by: zhanchengbin <zhanchengbin1@huawei.com>
> Link: https://lore.kernel.org/r/20230213080514.535568-3-zhanchengbin1@huawei.com/
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 31 +++++++++++++++++++++++++++----
>  1 file changed, 27 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index bff3666c891a..4d589d34b30e 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -1749,12 +1749,23 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
>  			break;
>  		err = ext4_ext_get_access(handle, inode, path + k);
>  		if (err)
> -			break;
> +			goto clean;
>  		path[k].p_idx->ei_block = border;
>  		err = ext4_ext_dirty(handle, inode, path + k);
>  		if (err)
> -			break;
> +			goto clean;
>  	}
> +	return 0;
> +
> +clean:
> +	/*
> +	 * The path[k].p_bh is either unmodified or with no verified bit
> +	 * set (see ext4_ext_get_access()). So just clear the verified bit
> +	 * of the successfully modified extents buffers, which will force
> +	 * these extents to be checked to avoid using inconsistent data.
> +	 */
> +	while (++k < depth)
> +		clear_buffer_verified(path[k].p_bh);
>  
>  	return err;
>  }
> @@ -2312,12 +2323,24 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
>  			break;
>  		err = ext4_ext_get_access(handle, inode, path + k);
>  		if (err)
> -			break;
> +			goto clean;
>  		path[k].p_idx->ei_block = path[k + 1].p_idx->ei_block;
>  		err = ext4_ext_dirty(handle, inode, path + k);
>  		if (err)
> -			break;
> +			goto clean;
>  	}
> +	return 0;
> +
> +clean:
> +	/*
> +	 * The path[k].p_bh is either unmodified or with no verified bit
> +	 * set (see ext4_ext_get_access()). So just clear the verified bit
> +	 * of the successfully modified extents buffers, which will force
> +	 * these extents to be checked to avoid using inconsistent data.
> +	 */
> +	while (++k < depth)
> +		clear_buffer_verified(path[k].p_bh);
> +
>  	return err;
>  }
>  
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 03/20] ext4: fix double brelse() the buffer of the extents path
  2024-07-10  4:06 ` [PATCH 03/20] ext4: fix double brelse() the buffer " libaokun
@ 2024-07-24 19:01   ` Jan Kara
  2024-07-26 11:45   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-24 19:01 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable

On Wed 10-07-24 12:06:37, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> In ext4_ext_try_to_merge_up(), set path[1].p_bh to NULL after it has been
> released, otherwise it may be released twice.
> 
> An example of what triggers this is as follows:
> 
>   split2    map    split1
> |--------|-------|--------|
> 
> ext4_ext_map_blocks
>  ext4_ext_handle_unwritten_extents
>   ext4_split_convert_extents
>    // path->p_depth == 0
>    ext4_split_extent
>      // 1. do split1
>      ext4_split_extent_at
>        ext4_ext_insert_extent
>          ext4_ext_create_new_leaf
>            ext4_ext_grow_indepth
>              le16_add_cpu(&neh->eh_depth, 1)
>            ext4_find_extent
>              path->p_depth = 1
>          ext4_ext_try_to_merge
>            ext4_ext_try_to_merge_up
>              path->p_depth = 0
>              brelse(path[1].p_bh)  ---> not set to NULL here
>      // 2. update path
>      ext4_find_extent
>      // 3. do split2
>      ext4_split_extent_at
>        ext4_ext_insert_extent
>          ext4_ext_create_new_leaf
>            ext4_ext_grow_indepth
>              le16_add_cpu(&neh->eh_depth, 1)
>            ext4_find_extent
>              path[0].p_bh = NULL;
>              path->p_depth = 1
>              read_extent_tree_block  ---> return err
>              // path[1].p_bh is still the old value
>  ext4_free_ext_path
>   ext4_ext_drop_refs
>    // path->p_depth == 1
>    brelse(path[1].p_bh)  ---> brelse a buffer twice
> 
> Finally got the following WARRNING when removing the buffer from lru:
> 
> ============================================
> VFS: brelse: Trying to free free buffer
> WARNING: CPU: 2 PID: 72 at fs/buffer.c:1241 __brelse+0x58/0x90
> CPU: 2 PID: 72 Comm: kworker/u19:1 Not tainted 6.9.0-dirty #716
> RIP: 0010:__brelse+0x58/0x90
> Call Trace:
>  <TASK>
>  __find_get_block+0x6e7/0x810
>  bdev_getblk+0x2b/0x480
>  __ext4_get_inode_loc+0x48a/0x1240
>  ext4_get_inode_loc+0xb2/0x150
>  ext4_reserve_inode_write+0xb7/0x230
>  __ext4_mark_inode_dirty+0x144/0x6a0
>  ext4_ext_insert_extent+0x9c8/0x3230
>  ext4_ext_map_blocks+0xf45/0x2dc0
>  ext4_map_blocks+0x724/0x1700
>  ext4_do_writepages+0x12d6/0x2a70
> [...]
> ============================================
> 
> Fixes: ecb94f5fdf4b ("ext4: collapse a single extent tree block into the inode if possible")
> Cc: stable@kernel.org
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 4d589d34b30e..657baf3991c1 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -1888,6 +1888,7 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
>  	path[0].p_hdr->eh_max = cpu_to_le16(max_root);
>  
>  	brelse(path[1].p_bh);
> +	path[1].p_bh = NULL;
>  	ext4_free_blocks(handle, inode, NULL, blk, 1,
>  			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
>  }
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 04/20] ext4: add new ext4_ext_path_brelse() helper
  2024-07-10  4:06 ` [PATCH 04/20] ext4: add new ext4_ext_path_brelse() helper libaokun
@ 2024-07-24 19:02   ` Jan Kara
  2024-07-26 11:53   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-24 19:02 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:38, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> Add ext4_ext_path_brelse() helper function to reduce duplicate code
> and ensure that path->p_bh is set to NULL after it is released.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Why not. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 24 ++++++++++++------------
>  1 file changed, 12 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 657baf3991c1..6e5b5baf3aa6 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -106,6 +106,12 @@ static int ext4_ext_trunc_restart_fn(struct inode *inode, int *dropped)
>  	return 0;
>  }
>  
> +static inline void ext4_ext_path_brelse(struct ext4_ext_path *path)
> +{
> +	brelse(path->p_bh);
> +	path->p_bh = NULL;
> +}
> +
>  static void ext4_ext_drop_refs(struct ext4_ext_path *path)
>  {
>  	int depth, i;
> @@ -113,10 +119,8 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
>  	if (!path)
>  		return;
>  	depth = path->p_depth;
> -	for (i = 0; i <= depth; i++, path++) {
> -		brelse(path->p_bh);
> -		path->p_bh = NULL;
> -	}
> +	for (i = 0; i <= depth; i++, path++)
> +		ext4_ext_path_brelse(path);
>  }
>  
>  void ext4_free_ext_path(struct ext4_ext_path *path)
> @@ -635,8 +639,7 @@ int ext4_ext_precache(struct inode *inode)
>  		 */
>  		if ((i == depth) ||
>  		    path[i].p_idx > EXT_LAST_INDEX(path[i].p_hdr)) {
> -			brelse(path[i].p_bh);
> -			path[i].p_bh = NULL;
> +			ext4_ext_path_brelse(path + i);
>  			i--;
>  			continue;
>  		}
> @@ -1887,8 +1890,7 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
>  		(path[1].p_ext - EXT_FIRST_EXTENT(path[1].p_hdr));
>  	path[0].p_hdr->eh_max = cpu_to_le16(max_root);
>  
> -	brelse(path[1].p_bh);
> -	path[1].p_bh = NULL;
> +	ext4_ext_path_brelse(path + 1);
>  	ext4_free_blocks(handle, inode, NULL, blk, 1,
>  			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
>  }
> @@ -2956,8 +2958,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
>  			err = ext4_ext_rm_leaf(handle, inode, path,
>  					       &partial, start, end);
>  			/* root level has p_bh == NULL, brelse() eats this */
> -			brelse(path[i].p_bh);
> -			path[i].p_bh = NULL;
> +			ext4_ext_path_brelse(path + i);
>  			i--;
>  			continue;
>  		}
> @@ -3019,8 +3020,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
>  				err = ext4_ext_rm_idx(handle, inode, path, i);
>  			}
>  			/* root level has p_bh == NULL, brelse() eats this */
> -			brelse(path[i].p_bh);
> -			path[i].p_bh = NULL;
> +			ext4_ext_path_brelse(path + i);
>  			i--;
>  			ext_debug(inode, "return to level %d\n", i);
>  		}
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at()
  2024-07-10  4:06 ` [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at() libaokun
@ 2024-07-24 19:13   ` Jan Kara
  2024-07-27 10:36   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-24 19:13 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable

On Wed 10-07-24 12:06:39, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> We hit the following use-after-free:
> 
> ==================================================================
> BUG: KASAN: slab-use-after-free in ext4_split_extent_at+0xba8/0xcc0
> Read of size 2 at addr ffff88810548ed08 by task kworker/u20:0/40
> CPU: 0 PID: 40 Comm: kworker/u20:0 Not tainted 6.9.0-dirty #724
> Call Trace:
>  <TASK>
>  kasan_report+0x93/0xc0
>  ext4_split_extent_at+0xba8/0xcc0
>  ext4_split_extent.isra.0+0x18f/0x500
>  ext4_split_convert_extents+0x275/0x750
>  ext4_ext_handle_unwritten_extents+0x73e/0x1580
>  ext4_ext_map_blocks+0xe20/0x2dc0
>  ext4_map_blocks+0x724/0x1700
>  ext4_do_writepages+0x12d6/0x2a70
> [...]
> 
> Allocated by task 40:
>  __kmalloc_noprof+0x1ac/0x480
>  ext4_find_extent+0xf3b/0x1e70
>  ext4_ext_map_blocks+0x188/0x2dc0
>  ext4_map_blocks+0x724/0x1700
>  ext4_do_writepages+0x12d6/0x2a70
> [...]
> 
> Freed by task 40:
>  kfree+0xf1/0x2b0
>  ext4_find_extent+0xa71/0x1e70
>  ext4_ext_insert_extent+0xa22/0x3260
>  ext4_split_extent_at+0x3ef/0xcc0
>  ext4_split_extent.isra.0+0x18f/0x500
>  ext4_split_convert_extents+0x275/0x750
>  ext4_ext_handle_unwritten_extents+0x73e/0x1580
>  ext4_ext_map_blocks+0xe20/0x2dc0
>  ext4_map_blocks+0x724/0x1700
>  ext4_do_writepages+0x12d6/0x2a70
> [...]
> ==================================================================
> 
> The flow of issue triggering is as follows:
> 
> ext4_split_extent_at
>   path = *ppath
>   ext4_ext_insert_extent(ppath)
>     ext4_ext_create_new_leaf(ppath)
>       ext4_find_extent(orig_path)
>         path = *orig_path
>         read_extent_tree_block
>           // return -ENOMEM or -EIO
>         ext4_free_ext_path(path)
>           kfree(path)
>         *orig_path = NULL
>   a. If err is -ENOMEM:
>   ext4_ext_dirty(path + path->p_depth)
>   // path use-after-free !!!
>   b. If err is -EIO and we have EXT_DEBUG defined:
>   ext4_ext_show_leaf(path)
>     eh = path[depth].p_hdr
>     // path also use-after-free !!!
> 
> So when trying to zeroout or fix the extent length, call ext4_find_extent()
> to update the path.
> 
> In addition we use *ppath directly as an ext4_ext_show_leaf() input to
> avoid possible use-after-free when EXT_DEBUG is defined, and to avoid
> unnecessary path updates.
> 
> Fixes: dfe5080939ea ("ext4: drop EXT4_EX_NOFREE_ON_ERR from rest of extents handling code")
> Cc: stable@kernel.org
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

									Honza

> ---
>  fs/ext4/extents.c | 21 ++++++++++++++++++++-
>  1 file changed, 20 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 6e5b5baf3aa6..3a70a0739af8 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3252,6 +3252,25 @@ static int ext4_split_extent_at(handle_t *handle,
>  	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
>  		goto out;
>  
> +	/*
> +	 * Update path is required because previous ext4_ext_insert_extent()
> +	 * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
> +	 * guarantees that ext4_find_extent() will not return -ENOMEM,
> +	 * otherwise -ENOMEM will cause a retry in do_writepages(), and a
> +	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
> +	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
> +	 */
> +	path = ext4_find_extent(inode, ee_block, ppath,
> +				flags | EXT4_EX_NOFAIL);
> +	if (IS_ERR(path)) {
> +		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
> +				 split, PTR_ERR(path));
> +		return PTR_ERR(path);
> +	}
> +	depth = ext_depth(inode);
> +	ex = path[depth].p_ext;
> +	*ppath = path;
> +
>  	if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
>  		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
>  			if (split_flag & EXT4_EXT_DATA_VALID1) {
> @@ -3304,7 +3323,7 @@ static int ext4_split_extent_at(handle_t *handle,
>  	ext4_ext_dirty(handle, inode, path + path->p_depth);
>  	return err;
>  out:
> -	ext4_ext_show_leaf(inode, path);
> +	ext4_ext_show_leaf(inode, *ppath);
>  	return err;
>  }
>  
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf()
  2024-07-10  4:06 ` [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf() libaokun
@ 2024-07-24 19:16   ` Jan Kara
  2024-07-25  5:41     ` Baokun Li
  2024-07-27 10:43   ` Ojaswin Mujoo
  1 sibling, 1 reply; 84+ messages in thread
From: Jan Kara @ 2024-07-24 19:16 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:40, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> In ext4_find_extent(), path may be freed by error or be reallocated, so
> using a previously saved *ppath may have been freed and thus may trigger
> use-after-free, as follows:
> 
> ext4_split_extent
>   path = *ppath;
>   ext4_split_extent_at(ppath)
>   path = ext4_find_extent(ppath)
>   ext4_split_extent_at(ppath)
>     // ext4_find_extent fails to free path
>     // but zeroout succeeds
>   ext4_ext_show_leaf(inode, path)
>     eh = path[depth].p_hdr
>     // path use-after-free !!!
> 
> Similar to ext4_split_extent_at(), we use *ppath directly as an input to
> ext4_ext_show_leaf(). Fix a spelling error by the way.
> 
> Same problem in ext4_ext_handle_unwritten_extents(). Since 'path' is only
> used in ext4_ext_show_leaf(), remove 'path' and use *ppath directly.
> 
> This issue is triggered only when EXT_DEBUG is defined and therefore does
> not affect functionality.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

I'd just note that this shows that modifying ppath in the called function
was not a great idea as it makes possible use-after-free issues due to
cached values being used very hard to spot and very easy to introduce...

								Honza

> ---
>  fs/ext4/extents.c | 9 ++++-----
>  1 file changed, 4 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 3a70a0739af8..1660434fbfc7 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3328,7 +3328,7 @@ static int ext4_split_extent_at(handle_t *handle,
>  }
>  
>  /*
> - * ext4_split_extents() splits an extent and mark extent which is covered
> + * ext4_split_extent() splits an extent and mark extent which is covered
>   * by @map as split_flags indicates
>   *
>   * It may result in splitting the extent into multiple extents (up to three)
> @@ -3404,7 +3404,7 @@ static int ext4_split_extent(handle_t *handle,
>  			goto out;
>  	}
>  
> -	ext4_ext_show_leaf(inode, path);
> +	ext4_ext_show_leaf(inode, *ppath);
>  out:
>  	return err ? err : allocated;
>  }
> @@ -3869,14 +3869,13 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  			struct ext4_ext_path **ppath, int flags,
>  			unsigned int allocated, ext4_fsblk_t newblock)
>  {
> -	struct ext4_ext_path __maybe_unused *path = *ppath;
>  	int ret = 0;
>  	int err = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
>  		  (unsigned long long)map->m_lblk, map->m_len, flags,
>  		  allocated);
> -	ext4_ext_show_leaf(inode, path);
> +	ext4_ext_show_leaf(inode, *ppath);
>  
>  	/*
>  	 * When writing into unwritten space, we should not fail to
> @@ -3973,7 +3972,7 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	if (allocated > map->m_len)
>  		allocated = map->m_len;
>  	map->m_len = allocated;
> -	ext4_ext_show_leaf(inode, path);
> +	ext4_ext_show_leaf(inode, *ppath);
>  out2:
>  	return err ? err : allocated;
>  }
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-24  6:23                 ` Ojaswin Mujoo
@ 2024-07-25  5:35                   ` Baokun Li
  2024-07-25  8:48                     ` Ojaswin Mujoo
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-25  5:35 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin,
	Baokun Li

On 2024/7/24 14:23, Ojaswin Mujoo wrote:
> On Wed, Jul 17, 2024 at 02:11:27PM +0800, Baokun Li wrote:
>> On 2024/7/17 13:29, Ojaswin Mujoo wrote:
>>> On Tue, Jul 16, 2024 at 07:54:43PM +0800, Baokun Li wrote:
>>>> Hi Ojaswin,
>>>>
>>>> On 2024/7/16 17:54, Ojaswin Mujoo wrote:
>>>>>>> But the journal will ensure the consistency of the extents path after
>>>>>>> this patch.
>>>>>>>
>>>>>>> When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
>>>>>>> ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
>>>>>>> the extents tree to be inconsistent. But the inconsistency just
>>>>>>> exists in memory and doesn't land on disk.
>>>>>>>
>>>>>>> For ext4_ext_get_access(), the handle must have been aborted
>>>>>>> when it returned an error, as follows:
>>>>>> ext4_ext_get_access
>>>>>>     ext4_journal_get_write_access
>>>>>>      __ext4_journal_get_write_access
>>>>>>       err = jbd2_journal_get_write_access
>>>>>>       if (err)
>>>>>>         ext4_journal_abort_handle
>>>>>>> For ext4_ext_dirty(), since path->p_bh must not be null and handle
>>>>>>> must be valid, handle is aborted anyway when an error is returned:
>>>>>> ext4_ext_dirty
>>>>>>     __ext4_ext_dirty
>>>>>>      if (path->p_bh)
>>>>>>        __ext4_handle_dirty_metadata
>>>>>>         if (ext4_handle_valid(handle))
>>>>>>           err = jbd2_journal_dirty_metadata
>>>>>>            if (!is_handle_aborted(handle) && WARN_ON_ONCE(err))
>>>>>>              ext4_journal_abort_handle
>>>>>>> Thus the extents tree will only be inconsistent in memory, so only
>>>>>>> the verified bit of the modified buffer needs to be cleared to avoid
>>>>>>> these inconsistent data being used in memory.
>>>>>>>
>>>>>> Regards,
>>>>>> Baokun
>>>>> Thanks for the explanation Baokun, so basically we only have the
>>>>> inconsitency in the memory.
>>>>>
>>>>> I do have a followup questions:
>>>>>
>>>>> So in the above example, after we have the error, we'll have the buffer
>>>>> for depth=0 marked as valid but pointing to the wrong ei_block.
>>>> It looks wrong here. When there is an error, the ei_block of the
>>>> unmodified buffer with depth=0 is the correct one, it is indeed
>>>> 'valid' and it is consistent with the disk. Only buffers that were
>>> Hey Baokun,
>>>
>>> Ahh I see now, I was looking at it the wrong way. So basically since
>>> depth 1 to 4 is inconsistent to the disk we mark then non verified so
>>> then subsequent lookups can act accordingly.
>>>
>>> Thanks for the explanation! I am in the middle of testing this patchset
>>> with xfstests on a POWERPC system with 64k page size. I'll let you know
>>> how that goes!
>>>
>>> Regards,
>>> Ojaswin
>> Hi Ojaswin,
>>
>> Thank you for the test and feedback!
>>
>> Cheers,
>> Baokun
> Hey Baokun,

Hi Ojaswin,

Sorry for the slow reply, I'm currently on a business trip.

> The xfstests pass for sub page size as well as bs = page size for
> POWERPC with no new regressions.
Thank you very much for your test!
>
> Although for this particular patch I doubt if we would be able to
> exersice the error path using xfstests. We might need to artifically
> inject error in ext4_ext_get_access or ext4_ext_dirty.  Do you have any
> other way of testing this?
The issues in this patch set can all be triggered by injecting EIO or
ENOMEM into ext4_find_extent(). So not only did I test kvm-xftests
several times on x86 to make sure there weren't any regressions,
but I also tested that running kvm-xfstests while randomly injecting
faults into ext4_find_extent() didn't crash the system.
>
> Also, just curious whether you came across this bug during code reading
> or were you actually hitting it?
The initial issue was that e2fsck was always reporting some sort of
extents tree exception after testing, so the processes in question
were troubleshooting and hardening, i.e. the first two patches.
The other issues were discovered during fault injection testing of
the processes in question.


Regards,
Baokun


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf()
  2024-07-24 19:16   ` Jan Kara
@ 2024-07-25  5:41     ` Baokun Li
  0 siblings, 0 replies; 84+ messages in thread
From: Baokun Li @ 2024-07-25  5:41 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-ext4, tytso, adilger.kernel, ritesh.list, linux-kernel,
	yi.zhang, yangerkun, Baokun Li, Baokun Li

On 2024/7/25 3:16, Jan Kara wrote:
> On Wed 10-07-24 12:06:40, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> In ext4_find_extent(), path may be freed by error or be reallocated, so
>> using a previously saved *ppath may have been freed and thus may trigger
>> use-after-free, as follows:
>>
>> ext4_split_extent
>>    path = *ppath;
>>    ext4_split_extent_at(ppath)
>>    path = ext4_find_extent(ppath)
>>    ext4_split_extent_at(ppath)
>>      // ext4_find_extent fails to free path
>>      // but zeroout succeeds
>>    ext4_ext_show_leaf(inode, path)
>>      eh = path[depth].p_hdr
>>      // path use-after-free !!!
>>
>> Similar to ext4_split_extent_at(), we use *ppath directly as an input to
>> ext4_ext_show_leaf(). Fix a spelling error by the way.
>>
>> Same problem in ext4_ext_handle_unwritten_extents(). Since 'path' is only
>> used in ext4_ext_show_leaf(), remove 'path' and use *ppath directly.
>>
>> This issue is triggered only when EXT_DEBUG is defined and therefore does
>> not affect functionality.
>>
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> Looks good. Feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
>
> I'd just note that this shows that modifying ppath in the called function
> was not a great idea as it makes possible use-after-free issues due to
> cached values being used very hard to spot and very easy to introduce...
>
> 								Honza

Hi Honza,

Thank you very much for your review!

Yes, it was too confusing, which is why I dropped all ppaths after fixing
the problem. Judging and using the returned path every time would
make the code look a lot simpler.

Thanks,
Baokun
>> ---
>>   fs/ext4/extents.c | 9 ++++-----
>>   1 file changed, 4 insertions(+), 5 deletions(-)
>>
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index 3a70a0739af8..1660434fbfc7 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
>> @@ -3328,7 +3328,7 @@ static int ext4_split_extent_at(handle_t *handle,
>>   }
>>   
>>   /*
>> - * ext4_split_extents() splits an extent and mark extent which is covered
>> + * ext4_split_extent() splits an extent and mark extent which is covered
>>    * by @map as split_flags indicates
>>    *
>>    * It may result in splitting the extent into multiple extents (up to three)
>> @@ -3404,7 +3404,7 @@ static int ext4_split_extent(handle_t *handle,
>>   			goto out;
>>   	}
>>   
>> -	ext4_ext_show_leaf(inode, path);
>> +	ext4_ext_show_leaf(inode, *ppath);
>>   out:
>>   	return err ? err : allocated;
>>   }
>> @@ -3869,14 +3869,13 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>>   			struct ext4_ext_path **ppath, int flags,
>>   			unsigned int allocated, ext4_fsblk_t newblock)
>>   {
>> -	struct ext4_ext_path __maybe_unused *path = *ppath;
>>   	int ret = 0;
>>   	int err = 0;
>>   
>>   	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
>>   		  (unsigned long long)map->m_lblk, map->m_len, flags,
>>   		  allocated);
>> -	ext4_ext_show_leaf(inode, path);
>> +	ext4_ext_show_leaf(inode, *ppath);
>>   
>>   	/*
>>   	 * When writing into unwritten space, we should not fail to
>> @@ -3973,7 +3972,7 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>>   	if (allocated > map->m_len)
>>   		allocated = map->m_len;
>>   	map->m_len = allocated;
>> -	ext4_ext_show_leaf(inode, path);
>> +	ext4_ext_show_leaf(inode, *ppath);
>>   out2:
>>   	return err ? err : allocated;
>>   }
>> -- 
>> 2.39.2
>>
-- 
With Best Regards,
Baokun Li


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 02/20] ext4: prevent partial update of the extents path
  2024-07-25  5:35                   ` Baokun Li
@ 2024-07-25  8:48                     ` Ojaswin Mujoo
  0 siblings, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-25  8:48 UTC (permalink / raw)
  To: Baokun Li
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, zhanchengbin

On Thu, Jul 25, 2024 at 01:35:10PM +0800, Baokun Li wrote:
> On 2024/7/24 14:23, Ojaswin Mujoo wrote:
> > On Wed, Jul 17, 2024 at 02:11:27PM +0800, Baokun Li wrote:
> > > On 2024/7/17 13:29, Ojaswin Mujoo wrote:
> > > > On Tue, Jul 16, 2024 at 07:54:43PM +0800, Baokun Li wrote:
> > > > > Hi Ojaswin,
> > > > > 
> > > > > On 2024/7/16 17:54, Ojaswin Mujoo wrote:
> > > > > > > > But the journal will ensure the consistency of the extents path after
> > > > > > > > this patch.
> > > > > > > > 
> > > > > > > > When ext4_ext_get_access() or ext4_ext_dirty() returns an error in
> > > > > > > > ext4_ext_rm_idx() and ext4_ext_correct_indexes(), this may cause
> > > > > > > > the extents tree to be inconsistent. But the inconsistency just
> > > > > > > > exists in memory and doesn't land on disk.
> > > > > > > > 
> > > > > > > > For ext4_ext_get_access(), the handle must have been aborted
> > > > > > > > when it returned an error, as follows:
> > > > > > > ext4_ext_get_access
> > > > > > >     ext4_journal_get_write_access
> > > > > > >      __ext4_journal_get_write_access
> > > > > > >       err = jbd2_journal_get_write_access
> > > > > > >       if (err)
> > > > > > >         ext4_journal_abort_handle
> > > > > > > > For ext4_ext_dirty(), since path->p_bh must not be null and handle
> > > > > > > > must be valid, handle is aborted anyway when an error is returned:
> > > > > > > ext4_ext_dirty
> > > > > > >     __ext4_ext_dirty
> > > > > > >      if (path->p_bh)
> > > > > > >        __ext4_handle_dirty_metadata
> > > > > > >         if (ext4_handle_valid(handle))
> > > > > > >           err = jbd2_journal_dirty_metadata
> > > > > > >            if (!is_handle_aborted(handle) && WARN_ON_ONCE(err))
> > > > > > >              ext4_journal_abort_handle
> > > > > > > > Thus the extents tree will only be inconsistent in memory, so only
> > > > > > > > the verified bit of the modified buffer needs to be cleared to avoid
> > > > > > > > these inconsistent data being used in memory.
> > > > > > > > 
> > > > > > > Regards,
> > > > > > > Baokun
> > > > > > Thanks for the explanation Baokun, so basically we only have the
> > > > > > inconsitency in the memory.
> > > > > > 
> > > > > > I do have a followup questions:
> > > > > > 
> > > > > > So in the above example, after we have the error, we'll have the buffer
> > > > > > for depth=0 marked as valid but pointing to the wrong ei_block.
> > > > > It looks wrong here. When there is an error, the ei_block of the
> > > > > unmodified buffer with depth=0 is the correct one, it is indeed
> > > > > 'valid' and it is consistent with the disk. Only buffers that were
> > > > Hey Baokun,
> > > > 
> > > > Ahh I see now, I was looking at it the wrong way. So basically since
> > > > depth 1 to 4 is inconsistent to the disk we mark then non verified so
> > > > then subsequent lookups can act accordingly.
> > > > 
> > > > Thanks for the explanation! I am in the middle of testing this patchset
> > > > with xfstests on a POWERPC system with 64k page size. I'll let you know
> > > > how that goes!
> > > > 
> > > > Regards,
> > > > Ojaswin
> > > Hi Ojaswin,
> > > 
> > > Thank you for the test and feedback!
> > > 
> > > Cheers,
> > > Baokun
> > Hey Baokun,
> 
> Hi Ojaswin,
> 
> Sorry for the slow reply, I'm currently on a business trip.
> 
> > The xfstests pass for sub page size as well as bs = page size for
> > POWERPC with no new regressions.
> Thank you very much for your test!
> > 
> > Although for this particular patch I doubt if we would be able to
> > exersice the error path using xfstests. We might need to artifically
> > inject error in ext4_ext_get_access or ext4_ext_dirty.  Do you have any
> > other way of testing this?
> The issues in this patch set can all be triggered by injecting EIO or
> ENOMEM into ext4_find_extent(). So not only did I test kvm-xftests
> several times on x86 to make sure there weren't any regressions,
> but I also tested that running kvm-xfstests while randomly injecting
> faults into ext4_find_extent() didn't crash the system.

Ahh got it, thanks. I think I understand the changes well enough now and 
it makes sense to me to mark them non verified in case of errors.
Furthermore, the tests also look fine. Feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

> > 
> > Also, just curious whether you came across this bug during code reading
> > or were you actually hitting it?
> The initial issue was that e2fsck was always reporting some sort of
> extents tree exception after testing, so the processes in question
> were troubleshooting and hardening, i.e. the first two patches.
> The other issues were discovered during fault injection testing of
> the processes in question.
> 
> 
> Regards,
> Baokun
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path
  2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
                   ` (19 preceding siblings ...)
  2024-07-10  4:06 ` [PATCH 20/20] ext4: avoid unnecessary extent path frees and allocations libaokun
@ 2024-07-25  9:02 ` Ojaswin Mujoo
  20 siblings, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-25  9:02 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:34PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> Hi all!
> 
> This patch series is a hardening of ext4 extents path related code.
> The following is a brief overview of the patches, see the patches for
> more details.
> 
> Patch 1-2: Refactor ext4_ext_rm_idx() as suggested by Jan, and add
> appropriate error handling branches to ext4_ext_rm_idx() and
> ext4_ext_correct_indexes() to avoid inconsistent extents tree.
>  PS: This comes from the previous work of my colleague zhanchengbin
>  (see link), who is no longer in charge of these and I have taken over.
>  Link: https://lore.kernel.org/r/20230213080514.535568-3-zhanchengbin1@huawei.com/
> 
> Patch 3-4: Fix an issue that caused p_bh to be released twice if it wasn't
> set to NULL after path->p_bh was released. And add a helper function after
> the quick fix to prevent this from happening again.
> 
> Patch 5-7: Quick fixes for use-after-free and double-free problems caused
> by mixing path(pointer to an extent path) and ppath(pointer to an extent
> path pointer).
> 
> Patch 8-19: Now the use of path and ppath is so confusing that we can
> trigger use-after-free or double-free by accessing a stale pointer, or
> we can get a memory leak by forgetting to update ppath. And it's very
> difficult to read the code. So to make the code more readable, get rid
> of ppath and pass path between functions uniformly to avoid these risks.
> 
> Patch 20: Reduces the consumption of unnecessary memory operations by
> avoiding repetitive allocation and release paths.
> 
> "kvm-xfstests -c ext4/all -g auto" has been executed with no new failures.
> 
> Comments and questions are, as always, welcome.
> Please let me know what you think.
> 
> Thanks,
> Baokun
> 
> Baokun Li (20):
>   ext4: refactor ext4_ext_rm_idx() to index 'path'
>   ext4: prevent partial update of the extents path
>   ext4: fix double brelse() the buffer of the extents path
>   ext4: add new ext4_ext_path_brelse() helper
>   ext4: fix slab-use-after-free in ext4_split_extent_at()
>   ext4: avoid use-after-free in ext4_ext_show_leaf()
>   ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free
>   ext4: get rid of ppath in ext4_find_extent()
>   ext4: get rid of ppath in get_ext_path()
>   ext4: get rid of ppath in ext4_ext_create_new_leaf()
>   ext4: get rid of ppath in ext4_ext_insert_extent()
>   ext4: get rid of ppath in ext4_split_extent_at()
>   ext4: get rid of ppath in ext4_force_split_extent_at()
>   ext4: get rid of ppath in ext4_split_extent()
>   ext4: get rid of ppath in ext4_split_convert_extents()
>   ext4: get rid of ppath in ext4_convert_unwritten_extents_endio()
>   ext4: get rid of ppath in ext4_ext_convert_to_initialized()
>   ext4: get rid of ppath in ext4_ext_handle_unwritten_extents()
>   ext4: get rid of ppath in convert_initialized_extent()
>   ext4: avoid unnecessary extent path frees and allocations
> 
>  fs/ext4/ext4.h        |   9 +-
>  fs/ext4/extents.c     | 746 +++++++++++++++++++++++-------------------
>  fs/ext4/fast_commit.c |  17 +-
>  fs/ext4/migrate.c     |   5 +-
>  fs/ext4/move_extent.c |  36 +-
>  5 files changed, 439 insertions(+), 374 deletions(-)
> 
> -- 
> 2.39.2

Tested kvm-xfstests auto on Powerpc with 4k, 64k and adv and no
new regressions are seen.

Feel free to add:

Tested-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
Ojaswin

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path'
  2024-07-10  4:06 ` [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path' libaokun
  2024-07-24 18:44   ` Jan Kara
@ 2024-07-25  9:14   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-25  9:14 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:35PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> As suggested by Honza in Link,modify ext4_ext_rm_idx() to leave 'path'
> alone and just index it like ext4_ext_correct_indexes() does it. This
> facilitates adding error handling later. No functional changes.
> 
> Suggested-by: Jan Kara <jack@suse.cz>
> Link: https://lore.kernel.org/all/20230216130305.nrbtd42tppxhbynn@quack3/
> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> ---
>  fs/ext4/extents.c | 32 +++++++++++++++-----------------
>  1 file changed, 15 insertions(+), 17 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index e067f2dd0335..bff3666c891a 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -2279,27 +2279,26 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
>  {
>  	int err;
>  	ext4_fsblk_t leaf;
> +	int k = depth - 1;
>  
>  	/* free index block */
> -	depth--;
> -	path = path + depth;
> -	leaf = ext4_idx_pblock(path->p_idx);
> -	if (unlikely(path->p_hdr->eh_entries == 0)) {
> -		EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0");
> +	leaf = ext4_idx_pblock(path[k].p_idx);
> +	if (unlikely(path[k].p_hdr->eh_entries == 0)) {
> +		EXT4_ERROR_INODE(inode, "path[%d].p_hdr->eh_entries == 0", k);
>  		return -EFSCORRUPTED;
>  	}
> -	err = ext4_ext_get_access(handle, inode, path);
> +	err = ext4_ext_get_access(handle, inode, path + k);
>  	if (err)
>  		return err;
>  
> -	if (path->p_idx != EXT_LAST_INDEX(path->p_hdr)) {
> -		int len = EXT_LAST_INDEX(path->p_hdr) - path->p_idx;
> +	if (path[k].p_idx != EXT_LAST_INDEX(path[k].p_hdr)) {
> +		int len = EXT_LAST_INDEX(path[k].p_hdr) - path[k].p_idx;
>  		len *= sizeof(struct ext4_extent_idx);
> -		memmove(path->p_idx, path->p_idx + 1, len);
> +		memmove(path[k].p_idx, path[k].p_idx + 1, len);
>  	}
>  
> -	le16_add_cpu(&path->p_hdr->eh_entries, -1);
> -	err = ext4_ext_dirty(handle, inode, path);
> +	le16_add_cpu(&path[k].p_hdr->eh_entries, -1);
> +	err = ext4_ext_dirty(handle, inode, path + k);
>  	if (err)
>  		return err;
>  	ext_debug(inode, "index is empty, remove it, free block %llu\n", leaf);
> @@ -2308,15 +2307,14 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
>  	ext4_free_blocks(handle, inode, NULL, leaf, 1,
>  			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
>  
> -	while (--depth >= 0) {
> -		if (path->p_idx != EXT_FIRST_INDEX(path->p_hdr))
> +	while (--k >= 0) {
> +		if (path[k + 1].p_idx != EXT_FIRST_INDEX(path[k + 1].p_hdr))
>  			break;
> -		path--;
> -		err = ext4_ext_get_access(handle, inode, path);
> +		err = ext4_ext_get_access(handle, inode, path + k);
>  		if (err)
>  			break;
> -		path->p_idx->ei_block = (path+1)->p_idx->ei_block;
> -		err = ext4_ext_dirty(handle, inode, path);
> +		path[k].p_idx->ei_block = path[k + 1].p_idx->ei_block;
> +		err = ext4_ext_dirty(handle, inode, path + k);
>  		if (err)
>  			break;
>  	}

Look good to me as well. Feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 07/20] ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free
  2024-07-10  4:06 ` [PATCH 07/20] ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free libaokun
@ 2024-07-25 10:31   ` Jan Kara
  2024-07-27 11:18   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 10:31 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable

On Wed 10-07-24 12:06:41, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> When calling ext4_force_split_extent_at() in ext4_ext_replay_update_ex(),
> the 'ppath' is updated but it is the 'path' that is freed, thus potentially
> triggering a double-free in the following process:
> 
> ext4_ext_replay_update_ex
>   ppath = path
>   ext4_force_split_extent_at(&ppath)
>     ext4_split_extent_at
>       ext4_ext_insert_extent
>         ext4_ext_create_new_leaf
>           ext4_ext_grow_indepth
>             ext4_find_extent
>               if (depth > path[0].p_maxdepth)
>                 kfree(path)                 ---> path First freed
>                 *orig_path = path = NULL    ---> null ppath
>   kfree(path)                               ---> path double-free !!!
> 
> So drop the unnecessary ppath and use path directly to avoid this problem.
> And use ext4_find_extent() directly to update path, avoiding unnecessary
> memory allocation and freeing. Also, propagate the error returned by
> ext4_find_extent() instead of using strange error codes.
> 
> Fixes: 8016e29f4362 ("ext4: fast commit recovery path")
> Cc: stable@kernel.org
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Nice! Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 21 ++++++++++-----------
>  1 file changed, 10 insertions(+), 11 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 1660434fbfc7..b1cfce5b57d2 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -5920,7 +5920,7 @@ int ext4_clu_mapped(struct inode *inode, ext4_lblk_t lclu)
>  int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
>  			      int len, int unwritten, ext4_fsblk_t pblk)
>  {
> -	struct ext4_ext_path *path = NULL, *ppath;
> +	struct ext4_ext_path *path;
>  	struct ext4_extent *ex;
>  	int ret;
>  
> @@ -5936,30 +5936,29 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
>  	if (le32_to_cpu(ex->ee_block) != start ||
>  		ext4_ext_get_actual_len(ex) != len) {
>  		/* We need to split this extent to match our extent first */
> -		ppath = path;
>  		down_write(&EXT4_I(inode)->i_data_sem);
> -		ret = ext4_force_split_extent_at(NULL, inode, &ppath, start, 1);
> +		ret = ext4_force_split_extent_at(NULL, inode, &path, start, 1);
>  		up_write(&EXT4_I(inode)->i_data_sem);
>  		if (ret)
>  			goto out;
> -		kfree(path);
> -		path = ext4_find_extent(inode, start, NULL, 0);
> +
> +		path = ext4_find_extent(inode, start, &path, 0);
>  		if (IS_ERR(path))
> -			return -1;
> -		ppath = path;
> +			return PTR_ERR(path);
>  		ex = path[path->p_depth].p_ext;
>  		WARN_ON(le32_to_cpu(ex->ee_block) != start);
> +
>  		if (ext4_ext_get_actual_len(ex) != len) {
>  			down_write(&EXT4_I(inode)->i_data_sem);
> -			ret = ext4_force_split_extent_at(NULL, inode, &ppath,
> +			ret = ext4_force_split_extent_at(NULL, inode, &path,
>  							 start + len, 1);
>  			up_write(&EXT4_I(inode)->i_data_sem);
>  			if (ret)
>  				goto out;
> -			kfree(path);
> -			path = ext4_find_extent(inode, start, NULL, 0);
> +
> +			path = ext4_find_extent(inode, start, &path, 0);
>  			if (IS_ERR(path))
> -				return -EINVAL;
> +				return PTR_ERR(path);
>  			ex = path[path->p_depth].p_ext;
>  		}
>  	}
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent()
  2024-07-10  4:06 ` [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent() libaokun
@ 2024-07-25 10:38   ` Jan Kara
  2024-07-27  6:18     ` Baokun Li
  2024-07-30 10:03   ` Ojaswin Mujoo
  1 sibling, 1 reply; 84+ messages in thread
From: Jan Kara @ 2024-07-25 10:38 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:42, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> Getting rid of ppath in ext4_find_extent() requires its caller to update
> ppath. These ppaths will also be dropped later. No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

One nit below, otherwise feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

> @@ -3260,11 +3257,12 @@ static int ext4_split_extent_at(handle_t *handle,
>  	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
>  	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
>  	 */
> -	path = ext4_find_extent(inode, ee_block, ppath,
> +	path = ext4_find_extent(inode, ee_block, *ppath,
>  				flags | EXT4_EX_NOFAIL);
>  	if (IS_ERR(path)) {
>  		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
>  				 split, PTR_ERR(path));
> +		*ppath = NULL;
>  		return PTR_ERR(path);
>  	}

I think here you forgot to update ppath on success case. It will go away by
the end of the series but still it's good to keep thing consistent...

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 09/20] ext4: get rid of ppath in get_ext_path()
  2024-07-10  4:06 ` [PATCH 09/20] ext4: get rid of ppath in get_ext_path() libaokun
@ 2024-07-25 10:41   ` Jan Kara
  2024-08-01  7:16   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 10:41 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:43, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> After getting rid of ppath in get_ext_path(), its caller may pass an error
> pointer to ext4_free_ext_path(), so it needs to teach ext4_free_ext_path()
> and ext4_ext_drop_refs() to skip the error pointer. No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c     |  5 +++--
>  fs/ext4/move_extent.c | 34 +++++++++++++++++-----------------
>  2 files changed, 20 insertions(+), 19 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 5217e6f53467..6dfb5d03e197 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -116,7 +116,7 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
>  {
>  	int depth, i;
>  
> -	if (!path)
> +	if (IS_ERR_OR_NULL(path))
>  		return;
>  	depth = path->p_depth;
>  	for (i = 0; i <= depth; i++, path++)
> @@ -125,6 +125,8 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
>  
>  void ext4_free_ext_path(struct ext4_ext_path *path)
>  {
> +	if (IS_ERR_OR_NULL(path))
> +		return;
>  	ext4_ext_drop_refs(path);
>  	kfree(path);
>  }
> @@ -4191,7 +4193,6 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  	path = ext4_find_extent(inode, map->m_lblk, NULL, 0);
>  	if (IS_ERR(path)) {
>  		err = PTR_ERR(path);
> -		path = NULL;
>  		goto out;
>  	}
>  
> diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
> index b0ab19a913bf..a7186d63725a 100644
> --- a/fs/ext4/move_extent.c
> +++ b/fs/ext4/move_extent.c
> @@ -17,27 +17,23 @@
>   * get_ext_path() - Find an extent path for designated logical block number.
>   * @inode:	inode to be searched
>   * @lblock:	logical block number to find an extent path
> - * @ppath:	pointer to an extent path pointer (for output)
> + * @path:	pointer to an extent path
>   *
> - * ext4_find_extent wrapper. Return 0 on success, or a negative error value
> - * on failure.
> + * ext4_find_extent wrapper. Return an extent path pointer on success,
> + * or an error pointer on failure.
>   */
> -static inline int
> +static inline struct ext4_ext_path *
>  get_ext_path(struct inode *inode, ext4_lblk_t lblock,
> -		struct ext4_ext_path **ppath)
> +	     struct ext4_ext_path *path)
>  {
> -	struct ext4_ext_path *path = *ppath;
> -
> -	*ppath = NULL;
>  	path = ext4_find_extent(inode, lblock, path, EXT4_EX_NOCACHE);
>  	if (IS_ERR(path))
> -		return PTR_ERR(path);
> +		return path;
>  	if (path[ext_depth(inode)].p_ext == NULL) {
>  		ext4_free_ext_path(path);
> -		return -ENODATA;
> +		return ERR_PTR(-ENODATA);
>  	}
> -	*ppath = path;
> -	return 0;
> +	return path;
>  }
>  
>  /**
> @@ -95,9 +91,11 @@ mext_check_coverage(struct inode *inode, ext4_lblk_t from, ext4_lblk_t count,
>  	int ret = 0;
>  	ext4_lblk_t last = from + count;
>  	while (from < last) {
> -		*err = get_ext_path(inode, from, &path);
> -		if (*err)
> -			goto out;
> +		path = get_ext_path(inode, from, path);
> +		if (IS_ERR(path)) {
> +			*err = PTR_ERR(path);
> +			return ret;
> +		}
>  		ext = path[ext_depth(inode)].p_ext;
>  		if (unwritten != ext4_ext_is_unwritten(ext))
>  			goto out;
> @@ -624,9 +622,11 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
>  		int offset_in_page;
>  		int unwritten, cur_len;
>  
> -		ret = get_ext_path(orig_inode, o_start, &path);
> -		if (ret)
> +		path = get_ext_path(orig_inode, o_start, path);
> +		if (IS_ERR(path)) {
> +			ret = PTR_ERR(path);
>  			goto out;
> +		}
>  		ex = path[path->p_depth].p_ext;
>  		cur_blk = le32_to_cpu(ex->ee_block);
>  		cur_len = ext4_ext_get_actual_len(ex);
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf()
  2024-07-10  4:06 ` [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf() libaokun
@ 2024-07-25 10:46   ` Jan Kara
  2024-07-27  6:35     ` Baokun Li
  2024-08-02  7:34   ` Ojaswin Mujoo
  1 sibling, 1 reply; 84+ messages in thread
From: Jan Kara @ 2024-07-25 10:46 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:44, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
> done here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Just one nit below. Otherwise feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

> @@ -1424,28 +1423,24 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>  		 * entry: create all needed subtree and add new leaf */
>  		err = ext4_ext_split(handle, inode, mb_flags, path, newext, i);
>  		if (err)
> -			goto out;
> +			goto errout;
>  
>  		/* refill path */
>  		path = ext4_find_extent(inode,
>  				    (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>  				    path, gb_flags);
> -		if (IS_ERR(path))
> -			err = PTR_ERR(path);

So I'd rather have here:
		return path;

That way it's obvious we will not confuse some code below with error stored
in 'path' and we can also save one indentation level by removing 'else'
below (probably do reindenting in a separate patch).

								Honza

>  	} else {
>  		/* tree is full, time to grow in depth */
>  		err = ext4_ext_grow_indepth(handle, inode, mb_flags);
>  		if (err)
> -			goto out;
> +			goto errout;
>  
>  		/* refill path */
>  		path = ext4_find_extent(inode,
>  				   (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>  				    path, gb_flags);
> -		if (IS_ERR(path)) {
> -			err = PTR_ERR(path);
> -			goto out;
> -		}
> +		if (IS_ERR(path))
> +			return path;
>  
>  		/*
>  		 * only first (depth 0 -> 1) produces free space;
> @@ -1457,9 +1452,11 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>  			goto repeat;
>  		}
>  	}
> -out:
> -	*ppath = IS_ERR(path) ? NULL : path;
> -	return err;
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  /*
> @@ -2112,10 +2109,14 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  	 */
>  	if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
>  		mb_flags |= EXT4_MB_USE_RESERVED;
> -	err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
> -				       ppath, newext);
> -	if (err)
> +	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
> +					path, newext);
> +	if (IS_ERR(path)) {
> +		*ppath = NULL;
> +		err = PTR_ERR(path);
>  		goto cleanup;
> +	}
> +	*ppath = path;
>  	depth = ext_depth(inode);
>  	eh = path[depth].p_hdr;
>  
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 11/20] ext4: get rid of ppath in ext4_ext_insert_extent()
  2024-07-10  4:06 ` [PATCH 11/20] ext4: get rid of ppath in ext4_ext_insert_extent() libaokun
@ 2024-07-25 10:59   ` Jan Kara
  2024-08-02  8:01   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 10:59 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:45, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_insert_extent(), the following is done
> here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
>  * Free path when npath is used, free npath when it is not used.
>  * The got_allocated_blocks label in ext4_ext_map_blocks() does not
>    update err now, so err is updated to 0 if the err returned by
>    ext4_ext_search_right() is greater than 0 and is about to enter
>    got_allocated_blocks.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/ext4.h        |  7 ++--
>  fs/ext4/extents.c     | 88 ++++++++++++++++++++++++-------------------
>  fs/ext4/fast_commit.c |  8 ++--
>  fs/ext4/migrate.c     |  5 ++-
>  4 files changed, 61 insertions(+), 47 deletions(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index cbe8d6062c52..53b4c1f454e6 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -3710,9 +3710,10 @@ extern int ext4_map_blocks(handle_t *handle, struct inode *inode,
>  extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode,
>  						   int num,
>  						   struct ext4_ext_path *path);
> -extern int ext4_ext_insert_extent(handle_t *, struct inode *,
> -				  struct ext4_ext_path **,
> -				  struct ext4_extent *, int);
> +extern struct ext4_ext_path *ext4_ext_insert_extent(
> +				handle_t *handle, struct inode *inode,
> +				struct ext4_ext_path *path,
> +				struct ext4_extent *newext, int gb_flags);
>  extern struct ext4_ext_path *ext4_find_extent(struct inode *, ext4_lblk_t,
>  					      struct ext4_ext_path *,
>  					      int flags);
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 0d6ce9e74b01..fc75390d591a 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -1974,16 +1974,15 @@ static unsigned int ext4_ext_check_overlap(struct ext4_sb_info *sbi,
>   * inserts requested extent as new one into the tree,
>   * creating new leaf in the no-space case.
>   */
> -int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
> -				struct ext4_ext_path **ppath,
> -				struct ext4_extent *newext, int gb_flags)
> +struct ext4_ext_path *
> +ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
> +		       struct ext4_ext_path *path,
> +		       struct ext4_extent *newext, int gb_flags)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	struct ext4_extent_header *eh;
>  	struct ext4_extent *ex, *fex;
>  	struct ext4_extent *nearex; /* nearest extent */
> -	struct ext4_ext_path *npath = NULL;
> -	int depth, len, err;
> +	int depth, len, err = 0;
>  	ext4_lblk_t next;
>  	int mb_flags = 0, unwritten;
>  
> @@ -1991,14 +1990,16 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  		mb_flags |= EXT4_MB_DELALLOC_RESERVED;
>  	if (unlikely(ext4_ext_get_actual_len(newext) == 0)) {
>  		EXT4_ERROR_INODE(inode, "ext4_ext_get_actual_len(newext) == 0");
> -		return -EFSCORRUPTED;
> +		err = -EFSCORRUPTED;
> +		goto errout;
>  	}
>  	depth = ext_depth(inode);
>  	ex = path[depth].p_ext;
>  	eh = path[depth].p_hdr;
>  	if (unlikely(path[depth].p_hdr == NULL)) {
>  		EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
> -		return -EFSCORRUPTED;
> +		err = -EFSCORRUPTED;
> +		goto errout;
>  	}
>  
>  	/* try to insert block into found extent and return */
> @@ -2036,7 +2037,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  			err = ext4_ext_get_access(handle, inode,
>  						  path + depth);
>  			if (err)
> -				return err;
> +				goto errout;
>  			unwritten = ext4_ext_is_unwritten(ex);
>  			ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
>  					+ ext4_ext_get_actual_len(newext));
> @@ -2061,7 +2062,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  			err = ext4_ext_get_access(handle, inode,
>  						  path + depth);
>  			if (err)
> -				return err;
> +				goto errout;
>  
>  			unwritten = ext4_ext_is_unwritten(ex);
>  			ex->ee_block = newext->ee_block;
> @@ -2086,21 +2087,26 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  	if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block))
>  		next = ext4_ext_next_leaf_block(path);
>  	if (next != EXT_MAX_BLOCKS) {
> +		struct ext4_ext_path *npath;
> +
>  		ext_debug(inode, "next leaf block - %u\n", next);
> -		BUG_ON(npath != NULL);
>  		npath = ext4_find_extent(inode, next, NULL, gb_flags);
> -		if (IS_ERR(npath))
> -			return PTR_ERR(npath);
> +		if (IS_ERR(npath)) {
> +			err = PTR_ERR(npath);
> +			goto errout;
> +		}
>  		BUG_ON(npath->p_depth != path->p_depth);
>  		eh = npath[depth].p_hdr;
>  		if (le16_to_cpu(eh->eh_entries) < le16_to_cpu(eh->eh_max)) {
>  			ext_debug(inode, "next leaf isn't full(%d)\n",
>  				  le16_to_cpu(eh->eh_entries));
> +			ext4_free_ext_path(path);
>  			path = npath;
>  			goto has_space;
>  		}
>  		ext_debug(inode, "next leaf has no free space(%d,%d)\n",
>  			  le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
> +		ext4_free_ext_path(npath);
>  	}
>  
>  	/*
> @@ -2111,12 +2117,8 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  		mb_flags |= EXT4_MB_USE_RESERVED;
>  	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
>  					path, newext);
> -	if (IS_ERR(path)) {
> -		*ppath = NULL;
> -		err = PTR_ERR(path);
> -		goto cleanup;
> -	}
> -	*ppath = path;
> +	if (IS_ERR(path))
> +		return path;
>  	depth = ext_depth(inode);
>  	eh = path[depth].p_hdr;
>  
> @@ -2125,7 +2127,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  
>  	err = ext4_ext_get_access(handle, inode, path + depth);
>  	if (err)
> -		goto cleanup;
> +		goto errout;
>  
>  	if (!nearex) {
>  		/* there is no extent in this leaf, create first one */
> @@ -2183,17 +2185,20 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  	if (!(gb_flags & EXT4_GET_BLOCKS_PRE_IO))
>  		ext4_ext_try_to_merge(handle, inode, path, nearex);
>  
> -
>  	/* time to correct all indexes above */
>  	err = ext4_ext_correct_indexes(handle, inode, path);
>  	if (err)
> -		goto cleanup;
> +		goto errout;
>  
>  	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
> +	if (err)
> +		goto errout;
>  
> -cleanup:
> -	ext4_free_ext_path(npath);
> -	return err;
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  static int ext4_fill_es_cache_info(struct inode *inode,
> @@ -3248,24 +3253,29 @@ static int ext4_split_extent_at(handle_t *handle,
>  	if (split_flag & EXT4_EXT_MARK_UNWRIT2)
>  		ext4_ext_mark_unwritten(ex2);
>  
> -	err = ext4_ext_insert_extent(handle, inode, ppath, &newex, flags);
> -	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
> +	path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
> +	if (!IS_ERR(path)) {
> +		*ppath = path;
>  		goto out;
> +	}
> +	*ppath = NULL;
> +	err = PTR_ERR(path);
> +	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
> +		return err;
>  
>  	/*
> -	 * Update path is required because previous ext4_ext_insert_extent()
> -	 * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
> -	 * guarantees that ext4_find_extent() will not return -ENOMEM,
> -	 * otherwise -ENOMEM will cause a retry in do_writepages(), and a
> -	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
> -	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
> +	 * Get a new path to try to zeroout or fix the extent length.
> +	 * Using EXT4_EX_NOFAIL guarantees that ext4_find_extent()
> +	 * will not return -ENOMEM, otherwise -ENOMEM will cause a
> +	 * retry in do_writepages(), and a WARN_ON may be triggered
> +	 * in ext4_da_update_reserve_space() due to an incorrect
> +	 * ee_len causing the i_reserved_data_blocks exception.
>  	 */
> -	path = ext4_find_extent(inode, ee_block, *ppath,
> +	path = ext4_find_extent(inode, ee_block, NULL,
>  				flags | EXT4_EX_NOFAIL);
>  	if (IS_ERR(path)) {
>  		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
>  				 split, PTR_ERR(path));
> -		*ppath = NULL;
>  		return PTR_ERR(path);
>  	}
>  	depth = ext_depth(inode);
> @@ -3324,7 +3334,7 @@ static int ext4_split_extent_at(handle_t *handle,
>  	ext4_ext_dirty(handle, inode, path + path->p_depth);
>  	return err;
>  out:
> -	ext4_ext_show_leaf(inode, *ppath);
> +	ext4_ext_show_leaf(inode, path);
>  	return err;
>  }
>  
> @@ -4313,6 +4323,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  	    get_implied_cluster_alloc(inode->i_sb, map, &ex2, path)) {
>  		ar.len = allocated = map->m_len;
>  		newblock = map->m_pblk;
> +		err = 0;
>  		goto got_allocated_blocks;
>  	}
>  
> @@ -4385,8 +4396,9 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  		map->m_flags |= EXT4_MAP_UNWRITTEN;
>  	}
>  
> -	err = ext4_ext_insert_extent(handle, inode, &path, &newex, flags);
> -	if (err) {
> +	path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
> +	if (IS_ERR(path)) {
> +		err = PTR_ERR(path);
>  		if (allocated_clusters) {
>  			int fb_flags = 0;
>  
> diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
> index 87c009e0c59a..1dee40477727 100644
> --- a/fs/ext4/fast_commit.c
> +++ b/fs/ext4/fast_commit.c
> @@ -1777,12 +1777,12 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
>  			if (ext4_ext_is_unwritten(ex))
>  				ext4_ext_mark_unwritten(&newex);
>  			down_write(&EXT4_I(inode)->i_data_sem);
> -			ret = ext4_ext_insert_extent(
> -				NULL, inode, &path, &newex, 0);
> +			path = ext4_ext_insert_extent(NULL, inode,
> +						      path, &newex, 0);
>  			up_write((&EXT4_I(inode)->i_data_sem));
> -			ext4_free_ext_path(path);
> -			if (ret)
> +			if (IS_ERR(path))
>  				goto out;
> +			ext4_free_ext_path(path);
>  			goto next;
>  		}
>  
> diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
> index d98ac2af8199..0f68b8a14560 100644
> --- a/fs/ext4/migrate.c
> +++ b/fs/ext4/migrate.c
> @@ -37,7 +37,6 @@ static int finish_range(handle_t *handle, struct inode *inode,
>  	path = ext4_find_extent(inode, lb->first_block, NULL, 0);
>  	if (IS_ERR(path)) {
>  		retval = PTR_ERR(path);
> -		path = NULL;
>  		goto err_out;
>  	}
>  
> @@ -53,7 +52,9 @@ static int finish_range(handle_t *handle, struct inode *inode,
>  	retval = ext4_datasem_ensure_credits(handle, inode, needed, needed, 0);
>  	if (retval < 0)
>  		goto err_out;
> -	retval = ext4_ext_insert_extent(handle, inode, &path, &newext, 0);
> +	path = ext4_ext_insert_extent(handle, inode, path, &newext, 0);
> +	if (IS_ERR(path))
> +		retval = PTR_ERR(path);
>  err_out:
>  	up_write((&EXT4_I(inode)->i_data_sem));
>  	ext4_free_ext_path(path);
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at()
  2024-07-10  4:06 ` [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at() libaokun
@ 2024-07-25 11:07   ` Jan Kara
  2024-07-27  6:42     ` Baokun Li
  2024-08-02  8:12   ` Ojaswin Mujoo
  1 sibling, 1 reply; 84+ messages in thread
From: Jan Kara @ 2024-07-25 11:07 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:46, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_split_extent_at(), the following is done
> here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
>  * Teach ext4_ext_show_leaf() to skip error pointer.
>  * Propagate ext4_find_extent() error return value in ext4_insert_range().
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

One nit below:

> @@ -5596,6 +5606,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
>  	path = ext4_find_extent(inode, offset_lblk, NULL, 0);
>  	if (IS_ERR(path)) {
>  		up_write(&EXT4_I(inode)->i_data_sem);
> +		ret = PTR_ERR(path);
>  		goto out_stop;
>  	}

AFAICT this actually fixes a bug where we could have returned 0 although
ext4_find_extent() spotted an error? This would deserve a separate patch so
that it could be easily pulled into stable.

Otherwise looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at()
  2024-07-10  4:06 ` [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at() libaokun
@ 2024-07-25 11:14   ` Jan Kara
  2024-08-02 20:01   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 11:14 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:47, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_force_split_extent_at(), the following is
> done here:
> 
>  * The ext4_find_extent() can update the extent path so it doesn't have to
>    allocate and free path repeatedly, thus reducing the consumption of
>    memory allocation and freeing in ext4_swap_extents().
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 117 ++++++++++++++++++++++++----------------------
>  1 file changed, 60 insertions(+), 57 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index c86b1bb7720f..0bd068ed324f 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -328,27 +328,20 @@ static inline int ext4_ext_space_root_idx(struct inode *inode, int check)
>  	return size;
>  }
>  
> -static inline int
> +static inline struct ext4_ext_path *
>  ext4_force_split_extent_at(handle_t *handle, struct inode *inode,
> -			   struct ext4_ext_path **ppath, ext4_lblk_t lblk,
> +			   struct ext4_ext_path *path, ext4_lblk_t lblk,
>  			   int nofail)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	int unwritten = ext4_ext_is_unwritten(path[path->p_depth].p_ext);
>  	int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO;
>  
>  	if (nofail)
>  		flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL;
>  
> -	path = ext4_split_extent_at(handle, inode, path, lblk, unwritten ?
> +	return ext4_split_extent_at(handle, inode, path, lblk, unwritten ?
>  			EXT4_EXT_MARK_UNWRIT1|EXT4_EXT_MARK_UNWRIT2 : 0,
>  			flags);
> -	if (IS_ERR(path)) {
> -		*ppath = NULL;
> -		return PTR_ERR(path);
> -	}
> -	*ppath = path;
> -	return 0;
>  }
>  
>  static int
> @@ -2906,11 +2899,12 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
>  			 * fail removing space due to ENOSPC so try to use
>  			 * reserved block if that happens.
>  			 */
> -			err = ext4_force_split_extent_at(handle, inode, &path,
> -							 end + 1, 1);
> -			if (err < 0)
> +			path = ext4_force_split_extent_at(handle, inode, path,
> +							  end + 1, 1);
> +			if (IS_ERR(path)) {
> +				err = PTR_ERR(path);
>  				goto out;
> -
> +			}
>  		} else if (sbi->s_cluster_ratio > 1 && end >= ex_end &&
>  			   partial.state == initial) {
>  			/*
> @@ -5707,25 +5701,21 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  		int e1_len, e2_len, len;
>  		int split = 0;
>  
> -		path1 = ext4_find_extent(inode1, lblk1, NULL, EXT4_EX_NOCACHE);
> +		path1 = ext4_find_extent(inode1, lblk1, path1, EXT4_EX_NOCACHE);
>  		if (IS_ERR(path1)) {
>  			*erp = PTR_ERR(path1);
> -			path1 = NULL;
> -		finish:
> -			count = 0;
> -			goto repeat;
> +			goto errout;
>  		}
> -		path2 = ext4_find_extent(inode2, lblk2, NULL, EXT4_EX_NOCACHE);
> +		path2 = ext4_find_extent(inode2, lblk2, path2, EXT4_EX_NOCACHE);
>  		if (IS_ERR(path2)) {
>  			*erp = PTR_ERR(path2);
> -			path2 = NULL;
> -			goto finish;
> +			goto errout;
>  		}
>  		ex1 = path1[path1->p_depth].p_ext;
>  		ex2 = path2[path2->p_depth].p_ext;
>  		/* Do we have something to swap ? */
>  		if (unlikely(!ex2 || !ex1))
> -			goto finish;
> +			goto errout;
>  
>  		e1_blk = le32_to_cpu(ex1->ee_block);
>  		e2_blk = le32_to_cpu(ex2->ee_block);
> @@ -5747,7 +5737,7 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  				next2 = e2_blk;
>  			/* Do we have something to swap */
>  			if (next1 == EXT_MAX_BLOCKS || next2 == EXT_MAX_BLOCKS)
> -				goto finish;
> +				goto errout;
>  			/* Move to the rightest boundary */
>  			len = next1 - lblk1;
>  			if (len < next2 - lblk2)
> @@ -5757,28 +5747,32 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  			lblk1 += len;
>  			lblk2 += len;
>  			count -= len;
> -			goto repeat;
> +			continue;
>  		}
>  
>  		/* Prepare left boundary */
>  		if (e1_blk < lblk1) {
>  			split = 1;
> -			*erp = ext4_force_split_extent_at(handle, inode1,
> -						&path1, lblk1, 0);
> -			if (unlikely(*erp))
> -				goto finish;
> +			path1 = ext4_force_split_extent_at(handle, inode1,
> +							   path1, lblk1, 0);
> +			if (IS_ERR(path1)) {
> +				*erp = PTR_ERR(path1);
> +				goto errout;
> +			}
>  		}
>  		if (e2_blk < lblk2) {
>  			split = 1;
> -			*erp = ext4_force_split_extent_at(handle, inode2,
> -						&path2,  lblk2, 0);
> -			if (unlikely(*erp))
> -				goto finish;
> +			path2 = ext4_force_split_extent_at(handle, inode2,
> +							   path2, lblk2, 0);
> +			if (IS_ERR(path2)) {
> +				*erp = PTR_ERR(path2);
> +				goto errout;
> +			}
>  		}
>  		/* ext4_split_extent_at() may result in leaf extent split,
>  		 * path must to be revalidated. */
>  		if (split)
> -			goto repeat;
> +			continue;
>  
>  		/* Prepare right boundary */
>  		len = count;
> @@ -5789,30 +5783,34 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  
>  		if (len != e1_len) {
>  			split = 1;
> -			*erp = ext4_force_split_extent_at(handle, inode1,
> -						&path1, lblk1 + len, 0);
> -			if (unlikely(*erp))
> -				goto finish;
> +			path1 = ext4_force_split_extent_at(handle, inode1,
> +							path1, lblk1 + len, 0);
> +			if (IS_ERR(path1)) {
> +				*erp = PTR_ERR(path1);
> +				goto errout;
> +			}
>  		}
>  		if (len != e2_len) {
>  			split = 1;
> -			*erp = ext4_force_split_extent_at(handle, inode2,
> -						&path2, lblk2 + len, 0);
> -			if (*erp)
> -				goto finish;
> +			path2 = ext4_force_split_extent_at(handle, inode2,
> +							path2, lblk2 + len, 0);
> +			if (IS_ERR(path2)) {
> +				*erp = PTR_ERR(path2);
> +				goto errout;
> +			}
>  		}
>  		/* ext4_split_extent_at() may result in leaf extent split,
>  		 * path must to be revalidated. */
>  		if (split)
> -			goto repeat;
> +			continue;
>  
>  		BUG_ON(e2_len != e1_len);
>  		*erp = ext4_ext_get_access(handle, inode1, path1 + path1->p_depth);
>  		if (unlikely(*erp))
> -			goto finish;
> +			goto errout;
>  		*erp = ext4_ext_get_access(handle, inode2, path2 + path2->p_depth);
>  		if (unlikely(*erp))
> -			goto finish;
> +			goto errout;
>  
>  		/* Both extents are fully inside boundaries. Swap it now */
>  		tmp_ex = *ex1;
> @@ -5830,7 +5828,7 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  		*erp = ext4_ext_dirty(handle, inode2, path2 +
>  				      path2->p_depth);
>  		if (unlikely(*erp))
> -			goto finish;
> +			goto errout;
>  		*erp = ext4_ext_dirty(handle, inode1, path1 +
>  				      path1->p_depth);
>  		/*
> @@ -5840,17 +5838,17 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  		 * aborted anyway.
>  		 */
>  		if (unlikely(*erp))
> -			goto finish;
> +			goto errout;
> +
>  		lblk1 += len;
>  		lblk2 += len;
>  		replaced_count += len;
>  		count -= len;
> -
> -	repeat:
> -		ext4_free_ext_path(path1);
> -		ext4_free_ext_path(path2);
> -		path1 = path2 = NULL;
>  	}
> +
> +errout:
> +	ext4_free_ext_path(path1);
> +	ext4_free_ext_path(path2);
>  	return replaced_count;
>  }
>  
> @@ -5965,24 +5963,29 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
>  		ext4_ext_get_actual_len(ex) != len) {
>  		/* We need to split this extent to match our extent first */
>  		down_write(&EXT4_I(inode)->i_data_sem);
> -		ret = ext4_force_split_extent_at(NULL, inode, &path, start, 1);
> +		path = ext4_force_split_extent_at(NULL, inode, path, start, 1);
>  		up_write(&EXT4_I(inode)->i_data_sem);
> -		if (ret)
> +		if (IS_ERR(path)) {
> +			ret = PTR_ERR(path);
>  			goto out;
> +		}
>  
>  		path = ext4_find_extent(inode, start, path, 0);
>  		if (IS_ERR(path))
>  			return PTR_ERR(path);
> +
>  		ex = path[path->p_depth].p_ext;
>  		WARN_ON(le32_to_cpu(ex->ee_block) != start);
>  
>  		if (ext4_ext_get_actual_len(ex) != len) {
>  			down_write(&EXT4_I(inode)->i_data_sem);
> -			ret = ext4_force_split_extent_at(NULL, inode, &path,
> -							 start + len, 1);
> +			path = ext4_force_split_extent_at(NULL, inode, path,
> +							  start + len, 1);
>  			up_write(&EXT4_I(inode)->i_data_sem);
> -			if (ret)
> +			if (IS_ERR(path)) {
> +				ret = PTR_ERR(path);
>  				goto out;
> +			}
>  
>  			path = ext4_find_extent(inode, start, path, 0);
>  			if (IS_ERR(path))
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 14/20] ext4: get rid of ppath in ext4_split_extent()
  2024-07-10  4:06 ` [PATCH 14/20] ext4: get rid of ppath in ext4_split_extent() libaokun
@ 2024-07-25 12:07   ` Jan Kara
  2024-08-02 20:17   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 12:07 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:48, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_split_extent(), the following is done here:
> 
>  * The 'allocated' is changed from passing a value to passing an address.
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 97 ++++++++++++++++++++++++-----------------------
>  1 file changed, 50 insertions(+), 47 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 0bd068ed324f..2a4f6c89858c 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3345,21 +3345,18 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
>   *   c> Splits in three extents: Somone is splitting in middle of the extent
>   *
>   */
> -static int ext4_split_extent(handle_t *handle,
> -			      struct inode *inode,
> -			      struct ext4_ext_path **ppath,
> -			      struct ext4_map_blocks *map,
> -			      int split_flag,
> -			      int flags)
> +static struct ext4_ext_path *ext4_split_extent(handle_t *handle,
> +					       struct inode *inode,
> +					       struct ext4_ext_path *path,
> +					       struct ext4_map_blocks *map,
> +					       int split_flag, int flags,
> +					       unsigned int *allocated)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	ext4_lblk_t ee_block;
>  	struct ext4_extent *ex;
>  	unsigned int ee_len, depth;
> -	int err = 0;
>  	int unwritten;
>  	int split_flag1, flags1;
> -	int allocated = map->m_len;
>  
>  	depth = ext_depth(inode);
>  	ex = path[depth].p_ext;
> @@ -3377,33 +3374,25 @@ static int ext4_split_extent(handle_t *handle,
>  			split_flag1 |= EXT4_EXT_DATA_VALID1;
>  		path = ext4_split_extent_at(handle, inode, path,
>  				map->m_lblk + map->m_len, split_flag1, flags1);
> -		if (IS_ERR(path)) {
> -			err = PTR_ERR(path);
> -			*ppath = NULL;
> -			goto out;
> +		if (IS_ERR(path))
> +			return path;
> +		/*
> +		 * Update path is required because previous ext4_split_extent_at
> +		 * may result in split of original leaf or extent zeroout.
> +		 */
> +		path = ext4_find_extent(inode, map->m_lblk, path, flags);
> +		if (IS_ERR(path))
> +			return path;
> +		depth = ext_depth(inode);
> +		ex = path[depth].p_ext;
> +		if (!ex) {
> +			EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
> +					(unsigned long) map->m_lblk);
> +			ext4_free_ext_path(path);
> +			return ERR_PTR(-EFSCORRUPTED);
>  		}
> -		*ppath = path;
> -	} 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.
> -	 */
> -	path = ext4_find_extent(inode, map->m_lblk, path, flags);
> -	if (IS_ERR(path)) {
> -		*ppath = NULL;
> -		return PTR_ERR(path);
> -	}
> -	*ppath = path;
> -	depth = ext_depth(inode);
> -	ex = path[depth].p_ext;
> -	if (!ex) {
> -		EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
> -				 (unsigned long) map->m_lblk);
> -		return -EFSCORRUPTED;
> +		unwritten = ext4_ext_is_unwritten(ex);
>  	}
> -	unwritten = ext4_ext_is_unwritten(ex);
>  
>  	if (map->m_lblk >= ee_block) {
>  		split_flag1 = split_flag & EXT4_EXT_DATA_VALID2;
> @@ -3414,17 +3403,18 @@ static int ext4_split_extent(handle_t *handle,
>  		}
>  		path = ext4_split_extent_at(handle, inode, path,
>  				map->m_lblk, split_flag1, flags);
> -		if (IS_ERR(path)) {
> -			err = PTR_ERR(path);
> -			*ppath = NULL;
> -			goto out;
> -		}
> -		*ppath = path;
> +		if (IS_ERR(path))
> +			return path;
>  	}
>  
> +	if (allocated) {
> +		if (map->m_lblk + map->m_len > ee_block + ee_len)
> +			*allocated = ee_len - (map->m_lblk - ee_block);
> +		else
> +			*allocated = map->m_len;
> +	}
>  	ext4_ext_show_leaf(inode, path);
> -out:
> -	return err ? err : allocated;
> +	return path;
>  }
>  
>  /*
> @@ -3669,10 +3659,15 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  	}
>  
>  fallback:
> -	err = ext4_split_extent(handle, inode, ppath, &split_map, split_flag,
> -				flags);
> -	if (err > 0)
> -		err = 0;
> +	path = ext4_split_extent(handle, inode, path, &split_map, split_flag,
> +				 flags, NULL);
> +	if (IS_ERR(path)) {
> +		err = PTR_ERR(path);
> +		*ppath = NULL;
> +		goto out;
> +	}
> +	err = 0;
> +	*ppath = path;
>  out:
>  	/* If we have gotten a failure, don't zero out status tree */
>  	if (!err) {
> @@ -3718,6 +3713,7 @@ static int ext4_split_convert_extents(handle_t *handle,
>  	struct ext4_extent *ex;
>  	unsigned int ee_len;
>  	int split_flag = 0, depth;
> +	unsigned int allocated = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u\n",
>  		  (unsigned long long)map->m_lblk, map->m_len);
> @@ -3745,7 +3741,14 @@ static int ext4_split_convert_extents(handle_t *handle,
>  		split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2);
>  	}
>  	flags |= EXT4_GET_BLOCKS_PRE_IO;
> -	return ext4_split_extent(handle, inode, ppath, map, split_flag, flags);
> +	path = ext4_split_extent(handle, inode, path, map, split_flag, flags,
> +				 &allocated);
> +	if (IS_ERR(path)) {
> +		*ppath = NULL;
> +		return PTR_ERR(path);
> +	}
> +	*ppath = path;
> +	return allocated;
>  }
>  
>  static int ext4_convert_unwritten_extents_endio(handle_t *handle,
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 15/20] ext4: get rid of ppath in ext4_split_convert_extents()
  2024-07-10  4:06 ` [PATCH 15/20] ext4: get rid of ppath in ext4_split_convert_extents() libaokun
@ 2024-07-25 12:12   ` Jan Kara
  2024-08-02 20:26   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 12:12 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:49, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_split_convert_extents(), the following is
> done here:
> 
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza


> ---
>  fs/ext4/extents.c | 65 ++++++++++++++++++++++++-----------------------
>  1 file changed, 33 insertions(+), 32 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 2a4f6c89858c..a41cbb8c4475 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3699,21 +3699,21 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>   * being filled will be convert to initialized by the end_io callback function
>   * via ext4_convert_unwritten_extents().
>   *
> - * Returns the size of unwritten extent to be written on success.
> + * The size of unwritten extent to be written is passed to the caller via the
> + * allocated pointer. Return an extent path pointer on success, or an error
> + * pointer on failure.
>   */
> -static int ext4_split_convert_extents(handle_t *handle,
> +static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle,
>  					struct inode *inode,
>  					struct ext4_map_blocks *map,
> -					struct ext4_ext_path **ppath,
> -					int flags)
> +					struct ext4_ext_path *path,
> +					int flags, unsigned int *allocated)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	ext4_lblk_t eof_block;
>  	ext4_lblk_t ee_block;
>  	struct ext4_extent *ex;
>  	unsigned int ee_len;
>  	int split_flag = 0, depth;
> -	unsigned int allocated = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u\n",
>  		  (unsigned long long)map->m_lblk, map->m_len);
> @@ -3741,14 +3741,8 @@ static int ext4_split_convert_extents(handle_t *handle,
>  		split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2);
>  	}
>  	flags |= EXT4_GET_BLOCKS_PRE_IO;
> -	path = ext4_split_extent(handle, inode, path, map, split_flag, flags,
> -				 &allocated);
> -	if (IS_ERR(path)) {
> -		*ppath = NULL;
> -		return PTR_ERR(path);
> -	}
> -	*ppath = path;
> -	return allocated;
> +	return ext4_split_extent(handle, inode, path, map, split_flag, flags,
> +				 allocated);
>  }
>  
>  static int ext4_convert_unwritten_extents_endio(handle_t *handle,
> @@ -3784,11 +3778,14 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
>  			     inode->i_ino, (unsigned long long)ee_block, ee_len,
>  			     (unsigned long long)map->m_lblk, map->m_len);
>  #endif
> -		err = ext4_split_convert_extents(handle, inode, map, ppath,
> -						 EXT4_GET_BLOCKS_CONVERT);
> -		if (err < 0)
> -			return err;
> -		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
> +		path = ext4_split_convert_extents(handle, inode, map, path,
> +						EXT4_GET_BLOCKS_CONVERT, NULL);
> +		if (IS_ERR(path)) {
> +			*ppath = NULL;
> +			return PTR_ERR(path);
> +		}
> +
> +		path = ext4_find_extent(inode, map->m_lblk, path, 0);
>  		if (IS_ERR(path)) {
>  			*ppath = NULL;
>  			return PTR_ERR(path);
> @@ -3845,11 +3842,14 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  		  (unsigned long long)ee_block, ee_len);
>  
>  	if (ee_block != map->m_lblk || ee_len > map->m_len) {
> -		err = ext4_split_convert_extents(handle, inode, map, ppath,
> -				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN);
> -		if (err < 0)
> -			return err;
> -		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
> +		path = ext4_split_convert_extents(handle, inode, map, path,
> +				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, NULL);
> +		if (IS_ERR(path)) {
> +			*ppath = NULL;
> +			return PTR_ERR(path);
> +		}
> +
> +		path = ext4_find_extent(inode, map->m_lblk, path, 0);
>  		if (IS_ERR(path)) {
>  			*ppath = NULL;
>  			return PTR_ERR(path);
> @@ -3915,19 +3915,20 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  
>  	/* get_block() before submitting IO, split the extent */
>  	if (flags & EXT4_GET_BLOCKS_PRE_IO) {
> -		ret = ext4_split_convert_extents(handle, inode, map, ppath,
> -					 flags | EXT4_GET_BLOCKS_CONVERT);
> -		if (ret < 0) {
> -			err = ret;
> +		*ppath = ext4_split_convert_extents(handle, inode, map, *ppath,
> +				flags | EXT4_GET_BLOCKS_CONVERT, &allocated);
> +		if (IS_ERR(*ppath)) {
> +			err = PTR_ERR(*ppath);
> +			*ppath = NULL;
>  			goto out2;
>  		}
>  		/*
> -		 * shouldn't get a 0 return when splitting an extent unless
> +		 * shouldn't get a 0 allocated when splitting an extent unless
>  		 * m_len is 0 (bug) or extent has been corrupted
>  		 */
> -		if (unlikely(ret == 0)) {
> +		if (unlikely(allocated == 0)) {
>  			EXT4_ERROR_INODE(inode,
> -					 "unexpected ret == 0, m_len = %u",
> +					 "unexpected allocated == 0, m_len = %u",
>  					 map->m_len);
>  			err = -EFSCORRUPTED;
>  			goto out2;
> @@ -3988,9 +3989,9 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  		err = -EFSCORRUPTED;
>  		goto out2;
>  	}
> +	allocated = ret;
>  
>  out:
> -	allocated = ret;
>  	map->m_flags |= EXT4_MAP_NEW;
>  map_out:
>  	map->m_flags |= EXT4_MAP_MAPPED;
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 16/20] ext4: get rid of ppath in ext4_convert_unwritten_extents_endio()
  2024-07-10  4:06 ` [PATCH 16/20] ext4: get rid of ppath in ext4_convert_unwritten_extents_endio() libaokun
@ 2024-07-25 12:14   ` Jan Kara
  2024-08-02 20:28   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 12:14 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:50, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_convert_unwritten_extents_endio(), the
> following is done here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 43 +++++++++++++++++++++++--------------------
>  1 file changed, 23 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index a41cbb8c4475..b7f443f98e9d 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3745,12 +3745,11 @@ static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle,
>  				 allocated);
>  }
>  
> -static int ext4_convert_unwritten_extents_endio(handle_t *handle,
> -						struct inode *inode,
> -						struct ext4_map_blocks *map,
> -						struct ext4_ext_path **ppath)
> +static struct ext4_ext_path *
> +ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
> +				     struct ext4_map_blocks *map,
> +				     struct ext4_ext_path *path)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	struct ext4_extent *ex;
>  	ext4_lblk_t ee_block;
>  	unsigned int ee_len;
> @@ -3780,24 +3779,19 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
>  #endif
>  		path = ext4_split_convert_extents(handle, inode, map, path,
>  						EXT4_GET_BLOCKS_CONVERT, NULL);
> -		if (IS_ERR(path)) {
> -			*ppath = NULL;
> -			return PTR_ERR(path);
> -		}
> +		if (IS_ERR(path))
> +			return path;
>  
>  		path = ext4_find_extent(inode, map->m_lblk, path, 0);
> -		if (IS_ERR(path)) {
> -			*ppath = NULL;
> -			return PTR_ERR(path);
> -		}
> -		*ppath = path;
> +		if (IS_ERR(path))
> +			return path;
>  		depth = ext_depth(inode);
>  		ex = path[depth].p_ext;
>  	}
>  
>  	err = ext4_ext_get_access(handle, inode, path + depth);
>  	if (err)
> -		goto out;
> +		goto errout;
>  	/* first mark the extent as initialized */
>  	ext4_ext_mark_initialized(ex);
>  
> @@ -3808,9 +3802,15 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
>  
>  	/* Mark modified extent as dirty */
>  	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
> -out:
> +	if (err)
> +		goto errout;
> +
>  	ext4_ext_show_leaf(inode, path);
> -	return err;
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  static int
> @@ -3938,10 +3938,13 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	}
>  	/* IO end_io complete, convert the filled extent to written */
>  	if (flags & EXT4_GET_BLOCKS_CONVERT) {
> -		err = ext4_convert_unwritten_extents_endio(handle, inode, map,
> -							   ppath);
> -		if (err < 0)
> +		*ppath = ext4_convert_unwritten_extents_endio(handle, inode,
> +							      map, *ppath);
> +		if (IS_ERR(*ppath)) {
> +			err = PTR_ERR(*ppath);
> +			*ppath = NULL;
>  			goto out2;
> +		}
>  		ext4_update_inode_fsync_trans(handle, inode, 1);
>  		goto map_out;
>  	}
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 17/20] ext4: get rid of ppath in ext4_ext_convert_to_initialized()
  2024-07-10  4:06 ` [PATCH 17/20] ext4: get rid of ppath in ext4_ext_convert_to_initialized() libaokun
@ 2024-07-25 12:18   ` Jan Kara
  2024-08-02 20:38   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 12:18 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:51, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_convert_to_initialized(), the following
> is done here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
>  * The 'allocated' is changed from passing a value to passing an address.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good to me. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 73 +++++++++++++++++++++++------------------------
>  1 file changed, 35 insertions(+), 38 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index b7f443f98e9d..59e80926fe3a 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3437,13 +3437,11 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle,
>   *    that are allocated and initialized.
>   *    It is guaranteed to be >= map->m_len.
>   */
> -static int ext4_ext_convert_to_initialized(handle_t *handle,
> -					   struct inode *inode,
> -					   struct ext4_map_blocks *map,
> -					   struct ext4_ext_path **ppath,
> -					   int flags)
> +static struct ext4_ext_path *
> +ext4_ext_convert_to_initialized(handle_t *handle, struct inode *inode,
> +			struct ext4_map_blocks *map, struct ext4_ext_path *path,
> +			int flags, unsigned int *allocated)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	struct ext4_sb_info *sbi;
>  	struct ext4_extent_header *eh;
>  	struct ext4_map_blocks split_map;
> @@ -3453,7 +3451,6 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  	unsigned int ee_len, depth, map_len = map->m_len;
>  	int err = 0;
>  	int split_flag = EXT4_EXT_DATA_VALID2;
> -	int allocated = 0;
>  	unsigned int max_zeroout = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u\n",
> @@ -3494,6 +3491,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  	 *  - L2: we only attempt to merge with an extent stored in the
>  	 *    same extent tree node.
>  	 */
> +	*allocated = 0;
>  	if ((map->m_lblk == ee_block) &&
>  		/* See if we can merge left */
>  		(map_len < ee_len) &&		/*L1*/
> @@ -3523,7 +3521,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  			(prev_len < (EXT_INIT_MAX_LEN - map_len))) {	/*C4*/
>  			err = ext4_ext_get_access(handle, inode, path + depth);
>  			if (err)
> -				goto out;
> +				goto errout;
>  
>  			trace_ext4_ext_convert_to_initialized_fastpath(inode,
>  				map, ex, abut_ex);
> @@ -3538,7 +3536,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  			abut_ex->ee_len = cpu_to_le16(prev_len + map_len);
>  
>  			/* Result: number of initialized blocks past m_lblk */
> -			allocated = map_len;
> +			*allocated = map_len;
>  		}
>  	} else if (((map->m_lblk + map_len) == (ee_block + ee_len)) &&
>  		   (map_len < ee_len) &&	/*L1*/
> @@ -3569,7 +3567,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  		    (next_len < (EXT_INIT_MAX_LEN - map_len))) {	/*C4*/
>  			err = ext4_ext_get_access(handle, inode, path + depth);
>  			if (err)
> -				goto out;
> +				goto errout;
>  
>  			trace_ext4_ext_convert_to_initialized_fastpath(inode,
>  				map, ex, abut_ex);
> @@ -3584,18 +3582,20 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  			abut_ex->ee_len = cpu_to_le16(next_len + map_len);
>  
>  			/* Result: number of initialized blocks past m_lblk */
> -			allocated = map_len;
> +			*allocated = map_len;
>  		}
>  	}
> -	if (allocated) {
> +	if (*allocated) {
>  		/* Mark the block containing both extents as dirty */
>  		err = ext4_ext_dirty(handle, inode, path + depth);
>  
>  		/* Update path to point to the right extent */
>  		path[depth].p_ext = abut_ex;
> +		if (err)
> +			goto errout;
>  		goto out;
>  	} else
> -		allocated = ee_len - (map->m_lblk - ee_block);
> +		*allocated = ee_len - (map->m_lblk - ee_block);
>  
>  	WARN_ON(map->m_lblk < ee_block);
>  	/*
> @@ -3622,21 +3622,21 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  	split_map.m_lblk = map->m_lblk;
>  	split_map.m_len = map->m_len;
>  
> -	if (max_zeroout && (allocated > split_map.m_len)) {
> -		if (allocated <= max_zeroout) {
> +	if (max_zeroout && (*allocated > split_map.m_len)) {
> +		if (*allocated <= max_zeroout) {
>  			/* case 3 or 5 */
>  			zero_ex1.ee_block =
>  				 cpu_to_le32(split_map.m_lblk +
>  					     split_map.m_len);
>  			zero_ex1.ee_len =
> -				cpu_to_le16(allocated - split_map.m_len);
> +				cpu_to_le16(*allocated - split_map.m_len);
>  			ext4_ext_store_pblock(&zero_ex1,
>  				ext4_ext_pblock(ex) + split_map.m_lblk +
>  				split_map.m_len - ee_block);
>  			err = ext4_ext_zeroout(inode, &zero_ex1);
>  			if (err)
>  				goto fallback;
> -			split_map.m_len = allocated;
> +			split_map.m_len = *allocated;
>  		}
>  		if (split_map.m_lblk - ee_block + split_map.m_len <
>  								max_zeroout) {
> @@ -3654,27 +3654,24 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  
>  			split_map.m_len += split_map.m_lblk - ee_block;
>  			split_map.m_lblk = ee_block;
> -			allocated = map->m_len;
> +			*allocated = map->m_len;
>  		}
>  	}
>  
>  fallback:
>  	path = ext4_split_extent(handle, inode, path, &split_map, split_flag,
>  				 flags, NULL);
> -	if (IS_ERR(path)) {
> -		err = PTR_ERR(path);
> -		*ppath = NULL;
> -		goto out;
> -	}
> -	err = 0;
> -	*ppath = path;
> +	if (IS_ERR(path))
> +		return path;
>  out:
>  	/* If we have gotten a failure, don't zero out status tree */
> -	if (!err) {
> -		ext4_zeroout_es(inode, &zero_ex1);
> -		ext4_zeroout_es(inode, &zero_ex2);
> -	}
> -	return err ? err : allocated;
> +	ext4_zeroout_es(inode, &zero_ex1);
> +	ext4_zeroout_es(inode, &zero_ex2);
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  /*
> @@ -3896,7 +3893,6 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  			struct ext4_ext_path **ppath, int flags,
>  			unsigned int allocated, ext4_fsblk_t newblock)
>  {
> -	int ret = 0;
>  	int err = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
> @@ -3976,23 +3972,24 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	 * For buffered writes, at writepage time, etc.  Convert a
>  	 * discovered unwritten extent to written.
>  	 */
> -	ret = ext4_ext_convert_to_initialized(handle, inode, map, ppath, flags);
> -	if (ret < 0) {
> -		err = ret;
> +	*ppath = ext4_ext_convert_to_initialized(handle, inode, map, *ppath,
> +						 flags, &allocated);
> +	if (IS_ERR(*ppath)) {
> +		err = PTR_ERR(*ppath);
> +		*ppath = NULL;
>  		goto out2;
>  	}
>  	ext4_update_inode_fsync_trans(handle, inode, 1);
>  	/*
> -	 * shouldn't get a 0 return when converting an unwritten extent
> +	 * shouldn't get a 0 allocated when converting an unwritten extent
>  	 * unless m_len is 0 (bug) or extent has been corrupted
>  	 */
> -	if (unlikely(ret == 0)) {
> -		EXT4_ERROR_INODE(inode, "unexpected ret == 0, m_len = %u",
> +	if (unlikely(allocated == 0)) {
> +		EXT4_ERROR_INODE(inode, "unexpected allocated == 0, m_len = %u",
>  				 map->m_len);
>  		err = -EFSCORRUPTED;
>  		goto out2;
>  	}
> -	allocated = ret;
>  
>  out:
>  	map->m_flags |= EXT4_MAP_NEW;
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 18/20] ext4: get rid of ppath in ext4_ext_handle_unwritten_extents()
  2024-07-10  4:06 ` [PATCH 18/20] ext4: get rid of ppath in ext4_ext_handle_unwritten_extents() libaokun
@ 2024-07-25 12:22   ` Jan Kara
  2024-08-02 20:44   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 12:22 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:52, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_handle_unwritten_extents(), the
> following is done here:
> 
>  * Free the extents path when an error is encountered.
>  * The 'allocated' is changed from passing a value to passing an address.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 82 +++++++++++++++++++++--------------------------
>  1 file changed, 37 insertions(+), 45 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 59e80926fe3a..badadcd64e66 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3887,18 +3887,18 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  	return 0;
>  }
>  
> -static int
> +static struct ext4_ext_path *
>  ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  			struct ext4_map_blocks *map,
> -			struct ext4_ext_path **ppath, int flags,
> -			unsigned int allocated, ext4_fsblk_t newblock)
> +			struct ext4_ext_path *path, int flags,
> +			unsigned int *allocated, ext4_fsblk_t newblock)
>  {
>  	int err = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
>  		  (unsigned long long)map->m_lblk, map->m_len, flags,
> -		  allocated);
> -	ext4_ext_show_leaf(inode, *ppath);
> +		  *allocated);
> +	ext4_ext_show_leaf(inode, path);
>  
>  	/*
>  	 * When writing into unwritten space, we should not fail to
> @@ -3907,40 +3907,34 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL;
>  
>  	trace_ext4_ext_handle_unwritten_extents(inode, map, flags,
> -						    allocated, newblock);
> +						*allocated, newblock);
>  
>  	/* get_block() before submitting IO, split the extent */
>  	if (flags & EXT4_GET_BLOCKS_PRE_IO) {
> -		*ppath = ext4_split_convert_extents(handle, inode, map, *ppath,
> -				flags | EXT4_GET_BLOCKS_CONVERT, &allocated);
> -		if (IS_ERR(*ppath)) {
> -			err = PTR_ERR(*ppath);
> -			*ppath = NULL;
> -			goto out2;
> -		}
> +		path = ext4_split_convert_extents(handle, inode, map, path,
> +				flags | EXT4_GET_BLOCKS_CONVERT, allocated);
> +		if (IS_ERR(path))
> +			return path;
>  		/*
>  		 * shouldn't get a 0 allocated when splitting an extent unless
>  		 * m_len is 0 (bug) or extent has been corrupted
>  		 */
> -		if (unlikely(allocated == 0)) {
> +		if (unlikely(*allocated == 0)) {
>  			EXT4_ERROR_INODE(inode,
>  					 "unexpected allocated == 0, m_len = %u",
>  					 map->m_len);
>  			err = -EFSCORRUPTED;
> -			goto out2;
> +			goto errout;
>  		}
>  		map->m_flags |= EXT4_MAP_UNWRITTEN;
>  		goto out;
>  	}
>  	/* IO end_io complete, convert the filled extent to written */
>  	if (flags & EXT4_GET_BLOCKS_CONVERT) {
> -		*ppath = ext4_convert_unwritten_extents_endio(handle, inode,
> -							      map, *ppath);
> -		if (IS_ERR(*ppath)) {
> -			err = PTR_ERR(*ppath);
> -			*ppath = NULL;
> -			goto out2;
> -		}
> +		path = ext4_convert_unwritten_extents_endio(handle, inode,
> +							    map, path);
> +		if (IS_ERR(path))
> +			return path;
>  		ext4_update_inode_fsync_trans(handle, inode, 1);
>  		goto map_out;
>  	}
> @@ -3972,23 +3966,20 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	 * For buffered writes, at writepage time, etc.  Convert a
>  	 * discovered unwritten extent to written.
>  	 */
> -	*ppath = ext4_ext_convert_to_initialized(handle, inode, map, *ppath,
> -						 flags, &allocated);
> -	if (IS_ERR(*ppath)) {
> -		err = PTR_ERR(*ppath);
> -		*ppath = NULL;
> -		goto out2;
> -	}
> +	path = ext4_ext_convert_to_initialized(handle, inode, map, path,
> +					       flags, allocated);
> +	if (IS_ERR(path))
> +		return path;
>  	ext4_update_inode_fsync_trans(handle, inode, 1);
>  	/*
>  	 * shouldn't get a 0 allocated when converting an unwritten extent
>  	 * unless m_len is 0 (bug) or extent has been corrupted
>  	 */
> -	if (unlikely(allocated == 0)) {
> +	if (unlikely(*allocated == 0)) {
>  		EXT4_ERROR_INODE(inode, "unexpected allocated == 0, m_len = %u",
>  				 map->m_len);
>  		err = -EFSCORRUPTED;
> -		goto out2;
> +		goto errout;
>  	}
>  
>  out:
> @@ -3997,12 +3988,15 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	map->m_flags |= EXT4_MAP_MAPPED;
>  out1:
>  	map->m_pblk = newblock;
> -	if (allocated > map->m_len)
> -		allocated = map->m_len;
> -	map->m_len = allocated;
> -	ext4_ext_show_leaf(inode, *ppath);
> -out2:
> -	return err ? err : allocated;
> +	if (*allocated > map->m_len)
> +		*allocated = map->m_len;
> +	map->m_len = *allocated;
> +	ext4_ext_show_leaf(inode, path);
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  /*
> @@ -4199,7 +4193,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  	struct ext4_extent newex, *ex, ex2;
>  	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
>  	ext4_fsblk_t newblock = 0, pblk;
> -	int err = 0, depth, ret;
> +	int err = 0, depth;
>  	unsigned int allocated = 0, offset = 0;
>  	unsigned int allocated_clusters = 0;
>  	struct ext4_allocation_request ar;
> @@ -4273,13 +4267,11 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  				goto out;
>  			}
>  
> -			ret = ext4_ext_handle_unwritten_extents(
> -				handle, inode, map, &path, flags,
> -				allocated, newblock);
> -			if (ret < 0)
> -				err = ret;
> -			else
> -				allocated = ret;
> +			path = ext4_ext_handle_unwritten_extents(
> +				handle, inode, map, path, flags,
> +				&allocated, newblock);
> +			if (IS_ERR(path))
> +				err = PTR_ERR(path);
>  			goto out;
>  		}
>  	}
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 19/20] ext4: get rid of ppath in convert_initialized_extent()
  2024-07-10  4:06 ` [PATCH 19/20] ext4: get rid of ppath in convert_initialized_extent() libaokun
@ 2024-07-25 12:23   ` Jan Kara
  2024-08-02 20:46   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 12:23 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:53, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in convert_initialized_extent(), the following is
> done here:
> 
>  * Free the extents path when an error is encountered.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c | 37 +++++++++++++++++++------------------
>  1 file changed, 19 insertions(+), 18 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index badadcd64e66..737432bb316e 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3810,13 +3810,12 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
>  	return ERR_PTR(err);
>  }
>  
> -static int
> +static struct ext4_ext_path *
>  convert_initialized_extent(handle_t *handle, struct inode *inode,
>  			   struct ext4_map_blocks *map,
> -			   struct ext4_ext_path **ppath,
> +			   struct ext4_ext_path *path,
>  			   unsigned int *allocated)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	struct ext4_extent *ex;
>  	ext4_lblk_t ee_block;
>  	unsigned int ee_len;
> @@ -3841,29 +3840,25 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  	if (ee_block != map->m_lblk || ee_len > map->m_len) {
>  		path = ext4_split_convert_extents(handle, inode, map, path,
>  				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, NULL);
> -		if (IS_ERR(path)) {
> -			*ppath = NULL;
> -			return PTR_ERR(path);
> -		}
> +		if (IS_ERR(path))
> +			return path;
>  
>  		path = ext4_find_extent(inode, map->m_lblk, path, 0);
> -		if (IS_ERR(path)) {
> -			*ppath = NULL;
> -			return PTR_ERR(path);
> -		}
> -		*ppath = path;
> +		if (IS_ERR(path))
> +			return path;
>  		depth = ext_depth(inode);
>  		ex = path[depth].p_ext;
>  		if (!ex) {
>  			EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
>  					 (unsigned long) map->m_lblk);
> -			return -EFSCORRUPTED;
> +			err = -EFSCORRUPTED;
> +			goto errout;
>  		}
>  	}
>  
>  	err = ext4_ext_get_access(handle, inode, path + depth);
>  	if (err)
> -		return err;
> +		goto errout;
>  	/* first mark the extent as unwritten */
>  	ext4_ext_mark_unwritten(ex);
>  
> @@ -3875,7 +3870,7 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  	/* Mark modified extent as dirty */
>  	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
>  	if (err)
> -		return err;
> +		goto errout;
>  	ext4_ext_show_leaf(inode, path);
>  
>  	ext4_update_inode_fsync_trans(handle, inode, 1);
> @@ -3884,7 +3879,11 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  	if (*allocated > map->m_len)
>  		*allocated = map->m_len;
>  	map->m_len = *allocated;
> -	return 0;
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  static struct ext4_ext_path *
> @@ -4254,8 +4253,10 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  			 */
>  			if ((!ext4_ext_is_unwritten(ex)) &&
>  			    (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN)) {
> -				err = convert_initialized_extent(handle,
> -					inode, map, &path, &allocated);
> +				path = convert_initialized_extent(handle,
> +					inode, map, path, &allocated);
> +				if (IS_ERR(path))
> +					err = PTR_ERR(path);
>  				goto out;
>  			} else if (!ext4_ext_is_unwritten(ex)) {
>  				map->m_flags |= EXT4_MAP_MAPPED;
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 20/20] ext4: avoid unnecessary extent path frees and allocations
  2024-07-10  4:06 ` [PATCH 20/20] ext4: avoid unnecessary extent path frees and allocations libaokun
@ 2024-07-25 12:31   ` Jan Kara
  2024-08-02 20:58   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-25 12:31 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed 10-07-24 12:06:54, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The ext4_find_extent() can update the extent path so that it does not have
> to allocate and free the path repeatedly, thus reducing the consumption of
> memory allocation and freeing in the following functions:
> 
>     ext4_ext_clear_bb
>     ext4_ext_replay_set_iblocks
>     ext4_fc_replay_add_range
>     ext4_fc_set_bitmaps_and_counters
> 
> No functional changes. Note that ext4_find_extent() does not support error
> pointers, so in this case set path to NULL first.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good! Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/extents.c     | 51 +++++++++++++++++++------------------------
>  fs/ext4/fast_commit.c | 11 ++++++----
>  2 files changed, 29 insertions(+), 33 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 737432bb316e..5ff92cd50dc8 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -6068,12 +6068,9 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
>  	if (IS_ERR(path))
>  		return PTR_ERR(path);
>  	ex = path[path->p_depth].p_ext;
> -	if (!ex) {
> -		ext4_free_ext_path(path);
> +	if (!ex)
>  		goto out;
> -	}
>  	end = le32_to_cpu(ex->ee_block) + ext4_ext_get_actual_len(ex);
> -	ext4_free_ext_path(path);
>  
>  	/* Count the number of data blocks */
>  	cur = 0;
> @@ -6099,32 +6096,28 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
>  	ret = skip_hole(inode, &cur);
>  	if (ret < 0)
>  		goto out;
> -	path = ext4_find_extent(inode, cur, NULL, 0);
> +	path = ext4_find_extent(inode, cur, path, 0);
>  	if (IS_ERR(path))
>  		goto out;
>  	numblks += path->p_depth;
> -	ext4_free_ext_path(path);
>  	while (cur < end) {
> -		path = ext4_find_extent(inode, cur, NULL, 0);
> +		path = ext4_find_extent(inode, cur, path, 0);
>  		if (IS_ERR(path))
>  			break;
>  		ex = path[path->p_depth].p_ext;
> -		if (!ex) {
> -			ext4_free_ext_path(path);
> -			return 0;
> -		}
> +		if (!ex)
> +			goto cleanup;
> +
>  		cur = max(cur + 1, le32_to_cpu(ex->ee_block) +
>  					ext4_ext_get_actual_len(ex));
>  		ret = skip_hole(inode, &cur);
> -		if (ret < 0) {
> -			ext4_free_ext_path(path);
> +		if (ret < 0)
>  			break;
> -		}
> -		path2 = ext4_find_extent(inode, cur, NULL, 0);
> -		if (IS_ERR(path2)) {
> -			ext4_free_ext_path(path);
> +
> +		path2 = ext4_find_extent(inode, cur, path2, 0);
> +		if (IS_ERR(path2))
>  			break;
> -		}
> +
>  		for (i = 0; i <= max(path->p_depth, path2->p_depth); i++) {
>  			cmp1 = cmp2 = 0;
>  			if (i <= path->p_depth)
> @@ -6136,13 +6129,14 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
>  			if (cmp1 != cmp2 && cmp2 != 0)
>  				numblks++;
>  		}
> -		ext4_free_ext_path(path);
> -		ext4_free_ext_path(path2);
>  	}
>  
>  out:
>  	inode->i_blocks = numblks << (inode->i_sb->s_blocksize_bits - 9);
>  	ext4_mark_inode_dirty(NULL, inode);
> +cleanup:
> +	ext4_free_ext_path(path);
> +	ext4_free_ext_path(path2);
>  	return 0;
>  }
>  
> @@ -6163,12 +6157,9 @@ int ext4_ext_clear_bb(struct inode *inode)
>  	if (IS_ERR(path))
>  		return PTR_ERR(path);
>  	ex = path[path->p_depth].p_ext;
> -	if (!ex) {
> -		ext4_free_ext_path(path);
> -		return 0;
> -	}
> +	if (!ex)
> +		goto out;
>  	end = le32_to_cpu(ex->ee_block) + ext4_ext_get_actual_len(ex);
> -	ext4_free_ext_path(path);
>  
>  	cur = 0;
>  	while (cur < end) {
> @@ -6178,16 +6169,16 @@ int ext4_ext_clear_bb(struct inode *inode)
>  		if (ret < 0)
>  			break;
>  		if (ret > 0) {
> -			path = ext4_find_extent(inode, map.m_lblk, NULL, 0);
> -			if (!IS_ERR_OR_NULL(path)) {
> +			path = ext4_find_extent(inode, map.m_lblk, path, 0);
> +			if (!IS_ERR(path)) {
>  				for (j = 0; j < path->p_depth; j++) {
> -
>  					ext4_mb_mark_bb(inode->i_sb,
>  							path[j].p_block, 1, false);
>  					ext4_fc_record_regions(inode->i_sb, inode->i_ino,
>  							0, path[j].p_block, 1, 1);
>  				}
> -				ext4_free_ext_path(path);
> +			} else {
> +				path = NULL;
>  			}
>  			ext4_mb_mark_bb(inode->i_sb, map.m_pblk, map.m_len, false);
>  			ext4_fc_record_regions(inode->i_sb, inode->i_ino,
> @@ -6196,5 +6187,7 @@ int ext4_ext_clear_bb(struct inode *inode)
>  		cur = cur + map.m_len;
>  	}
>  
> +out:
> +	ext4_free_ext_path(path);
>  	return 0;
>  }
> diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
> index 1dee40477727..1426d595fab7 100644
> --- a/fs/ext4/fast_commit.c
> +++ b/fs/ext4/fast_commit.c
> @@ -1766,7 +1766,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
>  
>  		if (ret == 0) {
>  			/* Range is not mapped */
> -			path = ext4_find_extent(inode, cur, NULL, 0);
> +			path = ext4_find_extent(inode, cur, path, 0);
>  			if (IS_ERR(path))
>  				goto out;
>  			memset(&newex, 0, sizeof(newex));
> @@ -1782,7 +1782,6 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
>  			up_write((&EXT4_I(inode)->i_data_sem));
>  			if (IS_ERR(path))
>  				goto out;
> -			ext4_free_ext_path(path);
>  			goto next;
>  		}
>  
> @@ -1830,6 +1829,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
>  	ext4_ext_replay_shrink_inode(inode, i_size_read(inode) >>
>  					sb->s_blocksize_bits);
>  out:
> +	ext4_free_ext_path(path);
>  	iput(inode);
>  	return 0;
>  }
> @@ -1930,12 +1930,13 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
>  				break;
>  
>  			if (ret > 0) {
> -				path = ext4_find_extent(inode, map.m_lblk, NULL, 0);
> +				path = ext4_find_extent(inode, map.m_lblk, path, 0);
>  				if (!IS_ERR(path)) {
>  					for (j = 0; j < path->p_depth; j++)
>  						ext4_mb_mark_bb(inode->i_sb,
>  							path[j].p_block, 1, true);
> -					ext4_free_ext_path(path);
> +				} else {
> +					path = NULL;
>  				}
>  				cur += ret;
>  				ext4_mb_mark_bb(inode->i_sb, map.m_pblk,
> @@ -1946,6 +1947,8 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
>  		}
>  		iput(inode);
>  	}
> +
> +	ext4_free_ext_path(path);
>  }
>  
>  /*
> -- 
> 2.39.2
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 03/20] ext4: fix double brelse() the buffer of the extents path
  2024-07-10  4:06 ` [PATCH 03/20] ext4: fix double brelse() the buffer " libaokun
  2024-07-24 19:01   ` Jan Kara
@ 2024-07-26 11:45   ` Ojaswin Mujoo
  2024-07-30  8:47     ` Baokun Li
  1 sibling, 1 reply; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-26 11:45 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable

On Wed, Jul 10, 2024 at 12:06:37PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> In ext4_ext_try_to_merge_up(), set path[1].p_bh to NULL after it has been
> released, otherwise it may be released twice.
> 
> An example of what triggers this is as follows:
> 
>   split2    map    split1
> |--------|-------|--------|
> 
> ext4_ext_map_blocks
>  ext4_ext_handle_unwritten_extents
>   ext4_split_convert_extents
>    // path->p_depth == 0
>    ext4_split_extent
>      // 1. do split1
>      ext4_split_extent_at
>        ext4_ext_insert_extent
>          ext4_ext_create_new_leaf
>            ext4_ext_grow_indepth
>              le16_add_cpu(&neh->eh_depth, 1)
>            ext4_find_extent
>              path->p_depth = 1
>          ext4_ext_try_to_merge
>            ext4_ext_try_to_merge_up
>              path->p_depth = 0
>              brelse(path[1].p_bh)  ---> not set to NULL here
>      // 2. update path
>      ext4_find_extent
>      // 3. do split2
>      ext4_split_extent_at
>        ext4_ext_insert_extent
>          ext4_ext_create_new_leaf
>            ext4_ext_grow_indepth
>              le16_add_cpu(&neh->eh_depth, 1)
>            ext4_find_extent
>              path[0].p_bh = NULL;
>              path->p_depth = 1
>              read_extent_tree_block  ---> return err
>              // path[1].p_bh is still the old value
>  ext4_free_ext_path
>   ext4_ext_drop_refs
>    // path->p_depth == 1
>    brelse(path[1].p_bh)  ---> brelse a buffer twice

Hi Baokun,

If i'm not wrong, in this trace, we'll enter ext4_ext_insert_extent() with
gb_flags having EXT4_GET_BLOCKS_PRE_IO so we won't actually go for a
merge_up. 

That being said, there seems to be a few places where we follow the
split-insert pattern and it might be possible that one of those call
sites might not be passing EXT4_GET_BLOCKS_PRE_IO and we'll the double
free issue you mentioned. I'll check and update if I see anything.

On a separate note, isn't it a bit weird that we grow the tree indepth
(which includes allocation, marking buffer dirty etc) only to later
merge it up again and throwing all the changes we did while growing the
tree. Surely we could optimize this particular case somehow right?

> 
> Finally got the following WARRNING when removing the buffer from lru:
> 
> ============================================
> VFS: brelse: Trying to free free buffer
> WARNING: CPU: 2 PID: 72 at fs/buffer.c:1241 __brelse+0x58/0x90
> CPU: 2 PID: 72 Comm: kworker/u19:1 Not tainted 6.9.0-dirty #716
> RIP: 0010:__brelse+0x58/0x90
> Call Trace:
>  <TASK>
>  __find_get_block+0x6e7/0x810
>  bdev_getblk+0x2b/0x480
>  __ext4_get_inode_loc+0x48a/0x1240
>  ext4_get_inode_loc+0xb2/0x150
>  ext4_reserve_inode_write+0xb7/0x230
>  __ext4_mark_inode_dirty+0x144/0x6a0
>  ext4_ext_insert_extent+0x9c8/0x3230
>  ext4_ext_map_blocks+0xf45/0x2dc0
>  ext4_map_blocks+0x724/0x1700
>  ext4_do_writepages+0x12d6/0x2a70
> [...]
> ============================================
> 
> Fixes: ecb94f5fdf4b ("ext4: collapse a single extent tree block into the inode if possible")
> Cc: stable@kernel.org
> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> ---
>  fs/ext4/extents.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 4d589d34b30e..657baf3991c1 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -1888,6 +1888,7 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
>  	path[0].p_hdr->eh_max = cpu_to_le16(max_root);
>  
>  	brelse(path[1].p_bh);
> +	path[1].p_bh = NULL;

Anyways, I agree that adding this here is the right thing to do:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Thanks,
Ojaswin

>  	ext4_free_blocks(handle, inode, NULL, blk, 1,
>  			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
>  }
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 04/20] ext4: add new ext4_ext_path_brelse() helper
  2024-07-10  4:06 ` [PATCH 04/20] ext4: add new ext4_ext_path_brelse() helper libaokun
  2024-07-24 19:02   ` Jan Kara
@ 2024-07-26 11:53   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-26 11:53 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:38PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> Add ext4_ext_path_brelse() helper function to reduce duplicate code
> and ensure that path->p_bh is set to NULL after it is released.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Hi Baokun,

The helper looks good, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

> ---
>  fs/ext4/extents.c | 24 ++++++++++++------------
>  1 file changed, 12 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 657baf3991c1..6e5b5baf3aa6 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -106,6 +106,12 @@ static int ext4_ext_trunc_restart_fn(struct inode *inode, int *dropped)
>  	return 0;
>  }
>  
> +static inline void ext4_ext_path_brelse(struct ext4_ext_path *path)
> +{
> +	brelse(path->p_bh);
> +	path->p_bh = NULL;
> +}
> +
>  static void ext4_ext_drop_refs(struct ext4_ext_path *path)
>  {
>  	int depth, i;
> @@ -113,10 +119,8 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
>  	if (!path)
>  		return;
>  	depth = path->p_depth;
> -	for (i = 0; i <= depth; i++, path++) {
> -		brelse(path->p_bh);
> -		path->p_bh = NULL;
> -	}
> +	for (i = 0; i <= depth; i++, path++)
> +		ext4_ext_path_brelse(path);
>  }
>  
>  void ext4_free_ext_path(struct ext4_ext_path *path)
> @@ -635,8 +639,7 @@ int ext4_ext_precache(struct inode *inode)
>  		 */
>  		if ((i == depth) ||
>  		    path[i].p_idx > EXT_LAST_INDEX(path[i].p_hdr)) {
> -			brelse(path[i].p_bh);
> -			path[i].p_bh = NULL;
> +			ext4_ext_path_brelse(path + i);
>  			i--;
>  			continue;
>  		}
> @@ -1887,8 +1890,7 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
>  		(path[1].p_ext - EXT_FIRST_EXTENT(path[1].p_hdr));
>  	path[0].p_hdr->eh_max = cpu_to_le16(max_root);
>  
> -	brelse(path[1].p_bh);
> -	path[1].p_bh = NULL;
> +	ext4_ext_path_brelse(path + 1);
>  	ext4_free_blocks(handle, inode, NULL, blk, 1,
>  			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
>  }
> @@ -2956,8 +2958,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
>  			err = ext4_ext_rm_leaf(handle, inode, path,
>  					       &partial, start, end);
>  			/* root level has p_bh == NULL, brelse() eats this */
> -			brelse(path[i].p_bh);
> -			path[i].p_bh = NULL;
> +			ext4_ext_path_brelse(path + i);
>  			i--;
>  			continue;
>  		}
> @@ -3019,8 +3020,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
>  				err = ext4_ext_rm_idx(handle, inode, path, i);
>  			}
>  			/* root level has p_bh == NULL, brelse() eats this */
> -			brelse(path[i].p_bh);
> -			path[i].p_bh = NULL;
> +			ext4_ext_path_brelse(path + i);
>  			i--;
>  			ext_debug(inode, "return to level %d\n", i);
>  		}
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent()
  2024-07-25 10:38   ` Jan Kara
@ 2024-07-27  6:18     ` Baokun Li
  2024-07-29 11:28       ` Jan Kara
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-27  6:18 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-ext4, tytso, adilger.kernel, ritesh.list, linux-kernel,
	yi.zhang, yangerkun, Baokun Li, Baokun Li

On 2024/7/25 18:38, Jan Kara wrote:
> On Wed 10-07-24 12:06:42, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> The use of path and ppath is now very confusing, so to make the code more
>> readable, pass path between functions uniformly, and get rid of ppath.
>>
>> Getting rid of ppath in ext4_find_extent() requires its caller to update
>> ppath. These ppaths will also be dropped later. No functional changes.
>>
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> One nit below, otherwise feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
>
>> @@ -3260,11 +3257,12 @@ static int ext4_split_extent_at(handle_t *handle,
>>   	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
>>   	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
>>   	 */
>> -	path = ext4_find_extent(inode, ee_block, ppath,
>> +	path = ext4_find_extent(inode, ee_block, *ppath,
>>   				flags | EXT4_EX_NOFAIL);
>>   	if (IS_ERR(path)) {
>>   		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
>>   				 split, PTR_ERR(path));
>> +		*ppath = NULL;
>>   		return PTR_ERR(path);
>>   	}
> I think here you forgot to update ppath on success case. It will go away by
> the end of the series but still it's good to keep thing consistent...
>
> 								Honza

Hi Honza,

Thank you for your review!

In patch 5, the ppath is already updated in case of success, so there
is no need to add it here. This update was not shown when the patch
was made and it looks like this:

-       path = ext4_find_extent(inode, ee_block, ppath,
+       path = ext4_find_extent(inode, ee_block, *ppath,
                                 flags | EXT4_EX_NOFAIL);
         if (IS_ERR(path)) {
                 EXT4_ERROR_INODE(inode, "Failed split extent on %u, err 
%ld",
                                  split, PTR_ERR(path));
+               *ppath = NULL;
                 return PTR_ERR(path);
         }
         depth = ext_depth(inode);
         ex = path[depth].p_ext;
         *ppath = path;

-- 
With Best Regards,
Baokun Li


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf()
  2024-07-25 10:46   ` Jan Kara
@ 2024-07-27  6:35     ` Baokun Li
  0 siblings, 0 replies; 84+ messages in thread
From: Baokun Li @ 2024-07-27  6:35 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-ext4, tytso, adilger.kernel, ritesh.list, linux-kernel,
	yi.zhang, yangerkun, Baokun Li, Baokun Li

On 2024/7/25 18:46, Jan Kara wrote:
> On Wed 10-07-24 12:06:44, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> The use of path and ppath is now very confusing, so to make the code more
>> readable, pass path between functions uniformly, and get rid of ppath.
>>
>> To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
>> done here:
>>
>>   * Free the extents path when an error is encountered.
>>   * Its caller needs to update ppath if it uses ppath.
>>
>> No functional changes.
>>
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> Just one nit below. Otherwise feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
>
>> @@ -1424,28 +1423,24 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>>   		 * entry: create all needed subtree and add new leaf */
>>   		err = ext4_ext_split(handle, inode, mb_flags, path, newext, i);
>>   		if (err)
>> -			goto out;
>> +			goto errout;
>>   
>>   		/* refill path */
>>   		path = ext4_find_extent(inode,
>>   				    (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>>   				    path, gb_flags);
>> -		if (IS_ERR(path))
>> -			err = PTR_ERR(path);
> So I'd rather have here:
> 		return path;
>
> That way it's obvious we will not confuse some code below with error stored
> in 'path' and we can also save one indentation level by removing 'else'
> below (probably do reindenting in a separate patch).
>
> 								Honza
Yes, that looks clearer! I'll add 'return path' in the next version, and
add a cleanup patch after this one to save indentation.

Thanks,
Baokun
>>   	} else {
>>   		/* tree is full, time to grow in depth */
>>   		err = ext4_ext_grow_indepth(handle, inode, mb_flags);
>>   		if (err)
>> -			goto out;
>> +			goto errout;
>>   
>>   		/* refill path */
>>   		path = ext4_find_extent(inode,
>>   				   (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>>   				    path, gb_flags);
>> -		if (IS_ERR(path)) {
>> -			err = PTR_ERR(path);
>> -			goto out;
>> -		}
>> +		if (IS_ERR(path))
>> +			return path;
>>   
>>   		/*
>>   		 * only first (depth 0 -> 1) produces free space;
>> @@ -1457,9 +1452,11 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>>   			goto repeat;
>>   		}
>>   	}
>> -out:
>> -	*ppath = IS_ERR(path) ? NULL : path;
>> -	return err;
>> +	return path;
>> +
>> +errout:
>> +	ext4_free_ext_path(path);
>> +	return ERR_PTR(err);
>>   }
>>   
>>   /*
>> @@ -2112,10 +2109,14 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>>   	 */
>>   	if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
>>   		mb_flags |= EXT4_MB_USE_RESERVED;
>> -	err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
>> -				       ppath, newext);
>> -	if (err)
>> +	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
>> +					path, newext);
>> +	if (IS_ERR(path)) {
>> +		*ppath = NULL;
>> +		err = PTR_ERR(path);
>>   		goto cleanup;
>> +	}
>> +	*ppath = path;
>>   	depth = ext_depth(inode);
>>   	eh = path[depth].p_hdr;
>>   
>> -- 
>> 2.39.2
>>
-- 
With Best Regards,
Baokun Li


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at()
  2024-07-25 11:07   ` Jan Kara
@ 2024-07-27  6:42     ` Baokun Li
  2024-08-21  3:19       ` Theodore Ts'o
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-27  6:42 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-ext4, tytso, adilger.kernel, ritesh.list, linux-kernel,
	yi.zhang, yangerkun, Baokun Li, Baokun Li

On 2024/7/25 19:07, Jan Kara wrote:
> On Wed 10-07-24 12:06:46, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> The use of path and ppath is now very confusing, so to make the code more
>> readable, pass path between functions uniformly, and get rid of ppath.
>>
>> To get rid of the ppath in ext4_split_extent_at(), the following is done
>> here:
>>
>>   * Free the extents path when an error is encountered.
>>   * Its caller needs to update ppath if it uses ppath.
>>   * Teach ext4_ext_show_leaf() to skip error pointer.
>>   * Propagate ext4_find_extent() error return value in ext4_insert_range().
>>
>> No functional changes.
>>
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> One nit below:
>
>> @@ -5596,6 +5606,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
>>   	path = ext4_find_extent(inode, offset_lblk, NULL, 0);
>>   	if (IS_ERR(path)) {
>>   		up_write(&EXT4_I(inode)->i_data_sem);
>> +		ret = PTR_ERR(path);
>>   		goto out_stop;
>>   	}
> AFAICT this actually fixes a bug where we could have returned 0 although
> ext4_find_extent() spotted an error?
Yeah, exactly.
> This would deserve a separate patch so
> that it could be easily pulled into stable.
>
> Otherwise looks good. Feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
>
> 								Honza
Ok, I'll put this in a separate patch in the next version.

Thank you very much for your review!

-- 
With Best Regards,
Baokun Li


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at()
  2024-07-10  4:06 ` [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at() libaokun
  2024-07-24 19:13   ` Jan Kara
@ 2024-07-27 10:36   ` Ojaswin Mujoo
  2024-07-30  8:57     ` Baokun Li
  1 sibling, 1 reply; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-27 10:36 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable

On Wed, Jul 10, 2024 at 12:06:39PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> We hit the following use-after-free:
> 
> ==================================================================
> BUG: KASAN: slab-use-after-free in ext4_split_extent_at+0xba8/0xcc0
> Read of size 2 at addr ffff88810548ed08 by task kworker/u20:0/40
> CPU: 0 PID: 40 Comm: kworker/u20:0 Not tainted 6.9.0-dirty #724
> Call Trace:
>  <TASK>
>  kasan_report+0x93/0xc0
>  ext4_split_extent_at+0xba8/0xcc0
>  ext4_split_extent.isra.0+0x18f/0x500
>  ext4_split_convert_extents+0x275/0x750
>  ext4_ext_handle_unwritten_extents+0x73e/0x1580
>  ext4_ext_map_blocks+0xe20/0x2dc0
>  ext4_map_blocks+0x724/0x1700
>  ext4_do_writepages+0x12d6/0x2a70
> [...]
> 
> Allocated by task 40:
>  __kmalloc_noprof+0x1ac/0x480
>  ext4_find_extent+0xf3b/0x1e70
>  ext4_ext_map_blocks+0x188/0x2dc0
>  ext4_map_blocks+0x724/0x1700
>  ext4_do_writepages+0x12d6/0x2a70
> [...]
> 
> Freed by task 40:
>  kfree+0xf1/0x2b0
>  ext4_find_extent+0xa71/0x1e70
>  ext4_ext_insert_extent+0xa22/0x3260
>  ext4_split_extent_at+0x3ef/0xcc0
>  ext4_split_extent.isra.0+0x18f/0x500
>  ext4_split_convert_extents+0x275/0x750
>  ext4_ext_handle_unwritten_extents+0x73e/0x1580
>  ext4_ext_map_blocks+0xe20/0x2dc0
>  ext4_map_blocks+0x724/0x1700
>  ext4_do_writepages+0x12d6/0x2a70
> [...]
> ==================================================================
> 
> The flow of issue triggering is as follows:
> 
> ext4_split_extent_at
>   path = *ppath
>   ext4_ext_insert_extent(ppath)
>     ext4_ext_create_new_leaf(ppath)
>       ext4_find_extent(orig_path)
>         path = *orig_path
>         read_extent_tree_block
>           // return -ENOMEM or -EIO
>         ext4_free_ext_path(path)
>           kfree(path)
>         *orig_path = NULL
>   a. If err is -ENOMEM:
>   ext4_ext_dirty(path + path->p_depth)
>   // path use-after-free !!!
>   b. If err is -EIO and we have EXT_DEBUG defined:
>   ext4_ext_show_leaf(path)
>     eh = path[depth].p_hdr
>     // path also use-after-free !!!
> 
> So when trying to zeroout or fix the extent length, call ext4_find_extent()
> to update the path.
> 
> In addition we use *ppath directly as an ext4_ext_show_leaf() input to
> avoid possible use-after-free when EXT_DEBUG is defined, and to avoid
> unnecessary path updates.
> 
> Fixes: dfe5080939ea ("ext4: drop EXT4_EX_NOFREE_ON_ERR from rest of extents handling code")
> Cc: stable@kernel.org
> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> ---
>  fs/ext4/extents.c | 21 ++++++++++++++++++++-
>  1 file changed, 20 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 6e5b5baf3aa6..3a70a0739af8 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3252,6 +3252,25 @@ static int ext4_split_extent_at(handle_t *handle,
>   if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
>     goto out;
>  
> + /*
> +  * Update path is required because previous ext4_ext_insert_extent()
> +  * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
> +  * guarantees that ext4_find_extent() will not return -ENOMEM,
> +  * otherwise -ENOMEM will cause a retry in do_writepages(), and a
> +  * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
> +  * an incorrect ee_len causing the i_reserved_data_blocks exception.
> +  */
> + path = ext4_find_extent(inode, ee_block, ppath,
> +       flags | EXT4_EX_NOFAIL);
> + if (IS_ERR(path)) {
> +   EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
> +        split, PTR_ERR(path));
> +   return PTR_ERR(path);
> + }
> + depth = ext_depth(inode);
> + ex = path[depth].p_ext;
> + *ppath = path;
> +

Hi Baokun, nice catch! 

I was just wondering if it makes more sense to only update path if we
encounter an error:

  err = ext4_ext_insert_extent(handle, inode, ppath, &newex, flags);
  if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
    goto out;
  else if (err < 0) {
    ...
    path = ext4_find_extent(inode, ee_block, ppath, flags | EXT4_EX_NOFAIL);
  }

I believe that's the only time we'd end up with the use after free issue
and this way we can avoid the (maybe tiny) performance overhead of calling 
the extra find extent. What are your thoughts?

regards,
ojaswin

>   if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
>     if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
>       if (split_flag & EXT4_EXT_DATA_VALID1) {
> @@ -3304,7 +3323,7 @@ static int ext4_split_extent_at(handle_t *handle,
>   ext4_ext_dirty(handle, inode, path + path->p_depth);
>   return err;
>  out:
> - ext4_ext_show_leaf(inode, path);
> + ext4_ext_show_leaf(inode, *ppath);
>   return err;
>  }
>  
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf()
  2024-07-10  4:06 ` [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf() libaokun
  2024-07-24 19:16   ` Jan Kara
@ 2024-07-27 10:43   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-27 10:43 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:40PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> In ext4_find_extent(), path may be freed by error or be reallocated, so
> using a previously saved *ppath may have been freed and thus may trigger
> use-after-free, as follows:
> 
> ext4_split_extent
>   path = *ppath;
>   ext4_split_extent_at(ppath)
>   path = ext4_find_extent(ppath)
>   ext4_split_extent_at(ppath)
>     // ext4_find_extent fails to free path
>     // but zeroout succeeds
>   ext4_ext_show_leaf(inode, path)
>     eh = path[depth].p_hdr
>     // path use-after-free !!!
> 
> Similar to ext4_split_extent_at(), we use *ppath directly as an input to
> ext4_ext_show_leaf(). Fix a spelling error by the way.
> 
> Same problem in ext4_ext_handle_unwritten_extents(). Since 'path' is only
> used in ext4_ext_show_leaf(), remove 'path' and use *ppath directly.
> 
> This issue is triggered only when EXT_DEBUG is defined and therefore does
> not affect functionality.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin
> ---
>  fs/ext4/extents.c | 9 ++++-----
>  1 file changed, 4 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 3a70a0739af8..1660434fbfc7 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3328,7 +3328,7 @@ static int ext4_split_extent_at(handle_t *handle,
>  }
>  
>  /*
> - * ext4_split_extents() splits an extent and mark extent which is covered
> + * ext4_split_extent() splits an extent and mark extent which is covered
>   * by @map as split_flags indicates
>   *
>   * It may result in splitting the extent into multiple extents (up to three)
> @@ -3404,7 +3404,7 @@ static int ext4_split_extent(handle_t *handle,
>  			goto out;
>  	}
>  
> -	ext4_ext_show_leaf(inode, path);
> +	ext4_ext_show_leaf(inode, *ppath);
>  out:
>  	return err ? err : allocated;
>  }
> @@ -3869,14 +3869,13 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  			struct ext4_ext_path **ppath, int flags,
>  			unsigned int allocated, ext4_fsblk_t newblock)
>  {
> -	struct ext4_ext_path __maybe_unused *path = *ppath;
>  	int ret = 0;
>  	int err = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
>  		  (unsigned long long)map->m_lblk, map->m_len, flags,
>  		  allocated);
> -	ext4_ext_show_leaf(inode, path);
> +	ext4_ext_show_leaf(inode, *ppath);
>  
>  	/*
>  	 * When writing into unwritten space, we should not fail to
> @@ -3973,7 +3972,7 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	if (allocated > map->m_len)
>  		allocated = map->m_len;
>  	map->m_len = allocated;
> -	ext4_ext_show_leaf(inode, path);
> +	ext4_ext_show_leaf(inode, *ppath);
>  out2:
>  	return err ? err : allocated;
>  }
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 07/20] ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free
  2024-07-10  4:06 ` [PATCH 07/20] ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free libaokun
  2024-07-25 10:31   ` Jan Kara
@ 2024-07-27 11:18   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-27 11:18 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable

On Wed, Jul 10, 2024 at 12:06:41PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> When calling ext4_force_split_extent_at() in ext4_ext_replay_update_ex(),
> the 'ppath' is updated but it is the 'path' that is freed, thus potentially
> triggering a double-free in the following process:
> 
> ext4_ext_replay_update_ex
>   ppath = path
>   ext4_force_split_extent_at(&ppath)
>     ext4_split_extent_at
>       ext4_ext_insert_extent
>         ext4_ext_create_new_leaf
>           ext4_ext_grow_indepth
>             ext4_find_extent
>               if (depth > path[0].p_maxdepth)
>                 kfree(path)                 ---> path First freed
>                 *orig_path = path = NULL    ---> null ppath
>   kfree(path)                               ---> path double-free !!!
> 
> So drop the unnecessary ppath and use path directly to avoid this problem.
> And use ext4_find_extent() directly to update path, avoiding unnecessary
> memory allocation and freeing. Also, propagate the error returned by
> ext4_find_extent() instead of using strange error codes.
> 
> Fixes: 8016e29f4362 ("ext4: fast commit recovery path")
> Cc: stable@kernel.org
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Hey Baokun, 
These ppath bugs are really subtle indeed :)

Anyways, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>


> ---
>  fs/ext4/extents.c | 21 ++++++++++-----------
>  1 file changed, 10 insertions(+), 11 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 1660434fbfc7..b1cfce5b57d2 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -5920,7 +5920,7 @@ int ext4_clu_mapped(struct inode *inode, ext4_lblk_t lclu)
>  int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
>  			      int len, int unwritten, ext4_fsblk_t pblk)
>  {
> -	struct ext4_ext_path *path = NULL, *ppath;
> +	struct ext4_ext_path *path;
>  	struct ext4_extent *ex;
>  	int ret;
>  
> @@ -5936,30 +5936,29 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
>  	if (le32_to_cpu(ex->ee_block) != start ||
>  		ext4_ext_get_actual_len(ex) != len) {
>  		/* We need to split this extent to match our extent first */
> -		ppath = path;
>  		down_write(&EXT4_I(inode)->i_data_sem);
> -		ret = ext4_force_split_extent_at(NULL, inode, &ppath, start, 1);
> +		ret = ext4_force_split_extent_at(NULL, inode, &path, start, 1);
>  		up_write(&EXT4_I(inode)->i_data_sem);
>  		if (ret)
>  			goto out;
> -		kfree(path);
> -		path = ext4_find_extent(inode, start, NULL, 0);
> +
> +		path = ext4_find_extent(inode, start, &path, 0);
>  		if (IS_ERR(path))
> -			return -1;
> -		ppath = path;
> +			return PTR_ERR(path);
>  		ex = path[path->p_depth].p_ext;
>  		WARN_ON(le32_to_cpu(ex->ee_block) != start);
> +
>  		if (ext4_ext_get_actual_len(ex) != len) {
>  			down_write(&EXT4_I(inode)->i_data_sem);
> -			ret = ext4_force_split_extent_at(NULL, inode, &ppath,
> +			ret = ext4_force_split_extent_at(NULL, inode, &path,
>  							 start + len, 1);
>  			up_write(&EXT4_I(inode)->i_data_sem);
>  			if (ret)
>  				goto out;
> -			kfree(path);
> -			path = ext4_find_extent(inode, start, NULL, 0);
> +
> +			path = ext4_find_extent(inode, start, &path, 0);
>  			if (IS_ERR(path))
> -				return -EINVAL;
> +				return PTR_ERR(path);
>  			ex = path[path->p_depth].p_ext;
>  		}
>  	}
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent()
  2024-07-27  6:18     ` Baokun Li
@ 2024-07-29 11:28       ` Jan Kara
  0 siblings, 0 replies; 84+ messages in thread
From: Jan Kara @ 2024-07-29 11:28 UTC (permalink / raw)
  To: Baokun Li
  Cc: Jan Kara, linux-ext4, tytso, adilger.kernel, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Sat 27-07-24 14:18:33, Baokun Li wrote:
> On 2024/7/25 18:38, Jan Kara wrote:
> > On Wed 10-07-24 12:06:42, libaokun@huaweicloud.com wrote:
> > > From: Baokun Li <libaokun1@huawei.com>
> > > 
> > > The use of path and ppath is now very confusing, so to make the code more
> > > readable, pass path between functions uniformly, and get rid of ppath.
> > > 
> > > Getting rid of ppath in ext4_find_extent() requires its caller to update
> > > ppath. These ppaths will also be dropped later. No functional changes.
> > > 
> > > Signed-off-by: Baokun Li <libaokun1@huawei.com>
> > One nit below, otherwise feel free to add:
> > 
> > Reviewed-by: Jan Kara <jack@suse.cz>
> > 
> > > @@ -3260,11 +3257,12 @@ static int ext4_split_extent_at(handle_t *handle,
> > >   	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
> > >   	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
> > >   	 */
> > > -	path = ext4_find_extent(inode, ee_block, ppath,
> > > +	path = ext4_find_extent(inode, ee_block, *ppath,
> > >   				flags | EXT4_EX_NOFAIL);
> > >   	if (IS_ERR(path)) {
> > >   		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
> > >   				 split, PTR_ERR(path));
> > > +		*ppath = NULL;
> > >   		return PTR_ERR(path);
> > >   	}
> > I think here you forgot to update ppath on success case. It will go away by
> > the end of the series but still it's good to keep thing consistent...
> > 
> > 								Honza
> 
> Hi Honza,
> 
> Thank you for your review!
> 
> In patch 5, the ppath is already updated in case of success, so there
> is no need to add it here. This update was not shown when the patch
> was made and it looks like this:
> 
> -       path = ext4_find_extent(inode, ee_block, ppath,
> +       path = ext4_find_extent(inode, ee_block, *ppath,
>                                 flags | EXT4_EX_NOFAIL);
>         if (IS_ERR(path)) {
>                 EXT4_ERROR_INODE(inode, "Failed split extent on %u, err
> %ld",
>                                  split, PTR_ERR(path));
> +               *ppath = NULL;
>                 return PTR_ERR(path);
>         }
>         depth = ext_depth(inode);
>         ex = path[depth].p_ext;
>         *ppath = path;

Yes, you are right. I didn't realize the update was already there. So I
withdraw my comment :)

								Honza

-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 03/20] ext4: fix double brelse() the buffer of the extents path
  2024-07-26 11:45   ` Ojaswin Mujoo
@ 2024-07-30  8:47     ` Baokun Li
  2024-08-01  6:26       ` Ojaswin Mujoo
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-30  8:47 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable, Baokun Li

On 2024/7/26 19:45, Ojaswin Mujoo wrote:
> On Wed, Jul 10, 2024 at 12:06:37PM +0800, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> In ext4_ext_try_to_merge_up(), set path[1].p_bh to NULL after it has been
>> released, otherwise it may be released twice.
>>
>> An example of what triggers this is as follows:
>>
>>    split2    map    split1
>> |--------|-------|--------|
>>
>> ext4_ext_map_blocks
>>   ext4_ext_handle_unwritten_extents
>>    ext4_split_convert_extents
>>     // path->p_depth == 0
>>     ext4_split_extent
>>       // 1. do split1
>>       ext4_split_extent_at
>>         ext4_ext_insert_extent
>>           ext4_ext_create_new_leaf
>>             ext4_ext_grow_indepth
>>               le16_add_cpu(&neh->eh_depth, 1)
>>             ext4_find_extent
>>               path->p_depth = 1
>>           ext4_ext_try_to_merge
>>             ext4_ext_try_to_merge_up
>>               path->p_depth = 0
>>               brelse(path[1].p_bh)  ---> not set to NULL here
>>       // 2. update path
>>       ext4_find_extent
>>       // 3. do split2
>>       ext4_split_extent_at
>>         ext4_ext_insert_extent
>>           ext4_ext_create_new_leaf
>>             ext4_ext_grow_indepth
>>               le16_add_cpu(&neh->eh_depth, 1)
>>             ext4_find_extent
>>               path[0].p_bh = NULL;
>>               path->p_depth = 1
>>               read_extent_tree_block  ---> return err
>>               // path[1].p_bh is still the old value
>>   ext4_free_ext_path
>>    ext4_ext_drop_refs
>>     // path->p_depth == 1
>>     brelse(path[1].p_bh)  ---> brelse a buffer twice
> Hi Baokun,
>
> If i'm not wrong, in this trace, we'll enter ext4_ext_insert_extent() with
> gb_flags having EXT4_GET_BLOCKS_PRE_IO so we won't actually go for a
> merge_up.
>
> That being said, there seems to be a few places where we follow the
> split-insert pattern and it might be possible that one of those call
> sites might not be passing EXT4_GET_BLOCKS_PRE_IO and we'll the double
> free issue you mentioned. I'll check and update if I see anything.
Hi Ojaswin,

You're right. I am very sorry for the confusion.

The trace here is wrong, this patch should actually be placed after the two
UAF patches. Here ext4_ext_try_to_merge() is called when trying zeroout in
ext4_split_extent_at(). It is called when trying zeroout with or without
EXT4_GET_BLOCKS_PRE_IO.The correct trace is as follows:

   split2    map    split1
|--------|-------|--------|

ext4_ext_map_blocks
  ext4_ext_handle_unwritten_extents
   ext4_split_convert_extents
    // path->p_depth == 0
    ext4_split_extent
      // 1. do split1
      ext4_split_extent_at
        |ext4_ext_insert_extent
        |  ext4_ext_create_new_leaf
        |    ext4_ext_grow_indepth
        |      le16_add_cpu(&neh->eh_depth, 1)
        |    ext4_find_extent
        |      // return -ENOMEM
        |// get error and try zeroout
        |path = ext4_find_extent
        |  path->p_depth = 1
        |ext4_ext_try_to_merge
        |  ext4_ext_try_to_merge_up
        |    path->p_depth = 0
        |    brelse(path[1].p_bh)  ---> not set to NULL here
        |// zeroout success
      // 2. update path
      ext4_find_extent
      // 3. do split2
      ext4_split_extent_at
        ext4_ext_insert_extent
          ext4_ext_create_new_leaf
            ext4_ext_grow_indepth
              le16_add_cpu(&neh->eh_depth, 1)
            ext4_find_extent
              path[0].p_bh = NULL;
              path->p_depth = 1
              read_extent_tree_block  ---> return err
              // path[1].p_bh is still the old value
  ext4_free_ext_path
   ext4_ext_drop_refs
    // path->p_depth == 1
    brelse(path[1].p_bh)  ---> brelse a buffer twice

I'll adjust the order of the patches and correct the trace in the next 
version.
> On a separate note, isn't it a bit weird that we grow the tree indepth
> (which includes allocation, marking buffer dirty etc) only to later
> merge it up again and throwing all the changes we did while growing the
> tree. Surely we could optimize this particular case somehow right?
Sorry that my trace misled you. It seems reasonable to try to merge extent
in error handling.
>> Finally got the following WARRNING when removing the buffer from lru:
>>
>> ============================================
>> VFS: brelse: Trying to free free buffer
>> WARNING: CPU: 2 PID: 72 at fs/buffer.c:1241 __brelse+0x58/0x90
>> CPU: 2 PID: 72 Comm: kworker/u19:1 Not tainted 6.9.0-dirty #716
>> RIP: 0010:__brelse+0x58/0x90
>> Call Trace:
>>   <TASK>
>>   __find_get_block+0x6e7/0x810
>>   bdev_getblk+0x2b/0x480
>>   __ext4_get_inode_loc+0x48a/0x1240
>>   ext4_get_inode_loc+0xb2/0x150
>>   ext4_reserve_inode_write+0xb7/0x230
>>   __ext4_mark_inode_dirty+0x144/0x6a0
>>   ext4_ext_insert_extent+0x9c8/0x3230
>>   ext4_ext_map_blocks+0xf45/0x2dc0
>>   ext4_map_blocks+0x724/0x1700
>>   ext4_do_writepages+0x12d6/0x2a70
>> [...]
>> ============================================
>>
>> Fixes: ecb94f5fdf4b ("ext4: collapse a single extent tree block into the inode if possible")
>> Cc: stable@kernel.org
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
>> ---
>>   fs/ext4/extents.c | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index 4d589d34b30e..657baf3991c1 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
>> @@ -1888,6 +1888,7 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
>>   	path[0].p_hdr->eh_max = cpu_to_le16(max_root);
>>   
>>   	brelse(path[1].p_bh);
>> +	path[1].p_bh = NULL;
> Anyways, I agree that adding this here is the right thing to do:
>
> Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
>
> Thanks,
> Ojaswin
Thanks for the review!
>>   	ext4_free_blocks(handle, inode, NULL, blk, 1,
>>   			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
>>   }
>> -- 
>> 2.39.2
>>
-- 
With Best Regards,
Baokun Li


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at()
  2024-07-27 10:36   ` Ojaswin Mujoo
@ 2024-07-30  8:57     ` Baokun Li
  2024-07-30  9:26       ` Ojaswin Mujoo
  0 siblings, 1 reply; 84+ messages in thread
From: Baokun Li @ 2024-07-30  8:57 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable, Baokun Li

On 2024/7/27 18:36, Ojaswin Mujoo wrote:
> On Wed, Jul 10, 2024 at 12:06:39PM +0800, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> We hit the following use-after-free:
>>
>> ==================================================================
>> BUG: KASAN: slab-use-after-free in ext4_split_extent_at+0xba8/0xcc0
>> Read of size 2 at addr ffff88810548ed08 by task kworker/u20:0/40
>> CPU: 0 PID: 40 Comm: kworker/u20:0 Not tainted 6.9.0-dirty #724
>> Call Trace:
>>   <TASK>
>>   kasan_report+0x93/0xc0
>>   ext4_split_extent_at+0xba8/0xcc0
>>   ext4_split_extent.isra.0+0x18f/0x500
>>   ext4_split_convert_extents+0x275/0x750
>>   ext4_ext_handle_unwritten_extents+0x73e/0x1580
>>   ext4_ext_map_blocks+0xe20/0x2dc0
>>   ext4_map_blocks+0x724/0x1700
>>   ext4_do_writepages+0x12d6/0x2a70
>> [...]
>>
>> Allocated by task 40:
>>   __kmalloc_noprof+0x1ac/0x480
>>   ext4_find_extent+0xf3b/0x1e70
>>   ext4_ext_map_blocks+0x188/0x2dc0
>>   ext4_map_blocks+0x724/0x1700
>>   ext4_do_writepages+0x12d6/0x2a70
>> [...]
>>
>> Freed by task 40:
>>   kfree+0xf1/0x2b0
>>   ext4_find_extent+0xa71/0x1e70
>>   ext4_ext_insert_extent+0xa22/0x3260
>>   ext4_split_extent_at+0x3ef/0xcc0
>>   ext4_split_extent.isra.0+0x18f/0x500
>>   ext4_split_convert_extents+0x275/0x750
>>   ext4_ext_handle_unwritten_extents+0x73e/0x1580
>>   ext4_ext_map_blocks+0xe20/0x2dc0
>>   ext4_map_blocks+0x724/0x1700
>>   ext4_do_writepages+0x12d6/0x2a70
>> [...]
>> ==================================================================
>>
>> The flow of issue triggering is as follows:
>>
>> ext4_split_extent_at
>>    path = *ppath
>>    ext4_ext_insert_extent(ppath)
>>      ext4_ext_create_new_leaf(ppath)
>>        ext4_find_extent(orig_path)
>>          path = *orig_path
>>          read_extent_tree_block
>>            // return -ENOMEM or -EIO
>>          ext4_free_ext_path(path)
>>            kfree(path)
>>          *orig_path = NULL
>>    a. If err is -ENOMEM:
>>    ext4_ext_dirty(path + path->p_depth)
>>    // path use-after-free !!!
>>    b. If err is -EIO and we have EXT_DEBUG defined:
>>    ext4_ext_show_leaf(path)
>>      eh = path[depth].p_hdr
>>      // path also use-after-free !!!
>>
>> So when trying to zeroout or fix the extent length, call ext4_find_extent()
>> to update the path.
>>
>> In addition we use *ppath directly as an ext4_ext_show_leaf() input to
>> avoid possible use-after-free when EXT_DEBUG is defined, and to avoid
>> unnecessary path updates.
>>
>> Fixes: dfe5080939ea ("ext4: drop EXT4_EX_NOFREE_ON_ERR from rest of extents handling code")
>> Cc: stable@kernel.org
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
>> ---
>>   fs/ext4/extents.c | 21 ++++++++++++++++++++-
>>   1 file changed, 20 insertions(+), 1 deletion(-)
>>
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index 6e5b5baf3aa6..3a70a0739af8 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
>> @@ -3252,6 +3252,25 @@ static int ext4_split_extent_at(handle_t *handle,
>>    if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
>>      goto out;
>>   
>> + /*
>> +  * Update path is required because previous ext4_ext_insert_extent()
>> +  * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
>> +  * guarantees that ext4_find_extent() will not return -ENOMEM,
>> +  * otherwise -ENOMEM will cause a retry in do_writepages(), and a
>> +  * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
>> +  * an incorrect ee_len causing the i_reserved_data_blocks exception.
>> +  */
>> + path = ext4_find_extent(inode, ee_block, ppath,
>> +       flags | EXT4_EX_NOFAIL);
>> + if (IS_ERR(path)) {
>> +   EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
>> +        split, PTR_ERR(path));
>> +   return PTR_ERR(path);
>> + }
>> + depth = ext_depth(inode);
>> + ex = path[depth].p_ext;
>> + *ppath = path;
>> +
> Hi Baokun, nice catch!
>
> I was just wondering if it makes more sense to only update path if we
> encounter an error:
>
>    err = ext4_ext_insert_extent(handle, inode, ppath, &newex, flags);
>    if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
>      goto out;
>    else if (err < 0) {
>      ...
>      path = ext4_find_extent(inode, ee_block, ppath, flags | EXT4_EX_NOFAIL);
>    }
>
> I believe that's the only time we'd end up with the use after free issue
> and this way we can avoid the (maybe tiny) performance overhead of calling
> the extra find extent. What are your thoughts?
>
> regards,
> ojaswin
Hi Ojaswin,

We're already there now, updating the path only on recoverable,
non-critical errors (-ENOSPC, -EDQUOT, and -ENOMEM).

Thanks,
Baokun
>>    if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
>>      if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
>>        if (split_flag & EXT4_EXT_DATA_VALID1) {
>> @@ -3304,7 +3323,7 @@ static int ext4_split_extent_at(handle_t *handle,
>>    ext4_ext_dirty(handle, inode, path + path->p_depth);
>>    return err;
>>   out:
>> - ext4_ext_show_leaf(inode, path);
>> + ext4_ext_show_leaf(inode, *ppath);
>>    return err;
>>   }
>>   
>> -- 
>> 2.39.2
>>
-- 
With Best Regards,
Baokun Li


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at()
  2024-07-30  8:57     ` Baokun Li
@ 2024-07-30  9:26       ` Ojaswin Mujoo
  0 siblings, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-30  9:26 UTC (permalink / raw)
  To: Baokun Li
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable

On Tue, Jul 30, 2024 at 04:57:35PM +0800, Baokun Li wrote:
> On 2024/7/27 18:36, Ojaswin Mujoo wrote:
> > On Wed, Jul 10, 2024 at 12:06:39PM +0800, libaokun@huaweicloud.com wrote:
> > > From: Baokun Li <libaokun1@huawei.com>
> > > 
> > > We hit the following use-after-free:
> > > 
> > > ==================================================================
> > > BUG: KASAN: slab-use-after-free in ext4_split_extent_at+0xba8/0xcc0
> > > Read of size 2 at addr ffff88810548ed08 by task kworker/u20:0/40
> > > CPU: 0 PID: 40 Comm: kworker/u20:0 Not tainted 6.9.0-dirty #724
> > > Call Trace:
> > >   <TASK>
> > >   kasan_report+0x93/0xc0
> > >   ext4_split_extent_at+0xba8/0xcc0
> > >   ext4_split_extent.isra.0+0x18f/0x500
> > >   ext4_split_convert_extents+0x275/0x750
> > >   ext4_ext_handle_unwritten_extents+0x73e/0x1580
> > >   ext4_ext_map_blocks+0xe20/0x2dc0
> > >   ext4_map_blocks+0x724/0x1700
> > >   ext4_do_writepages+0x12d6/0x2a70
> > > [...]
> > > 
> > > Allocated by task 40:
> > >   __kmalloc_noprof+0x1ac/0x480
> > >   ext4_find_extent+0xf3b/0x1e70
> > >   ext4_ext_map_blocks+0x188/0x2dc0
> > >   ext4_map_blocks+0x724/0x1700
> > >   ext4_do_writepages+0x12d6/0x2a70
> > > [...]
> > > 
> > > Freed by task 40:
> > >   kfree+0xf1/0x2b0
> > >   ext4_find_extent+0xa71/0x1e70
> > >   ext4_ext_insert_extent+0xa22/0x3260
> > >   ext4_split_extent_at+0x3ef/0xcc0
> > >   ext4_split_extent.isra.0+0x18f/0x500
> > >   ext4_split_convert_extents+0x275/0x750
> > >   ext4_ext_handle_unwritten_extents+0x73e/0x1580
> > >   ext4_ext_map_blocks+0xe20/0x2dc0
> > >   ext4_map_blocks+0x724/0x1700
> > >   ext4_do_writepages+0x12d6/0x2a70
> > > [...]
> > > ==================================================================
> > > 
> > > The flow of issue triggering is as follows:
> > > 
> > > ext4_split_extent_at
> > >    path = *ppath
> > >    ext4_ext_insert_extent(ppath)
> > >      ext4_ext_create_new_leaf(ppath)
> > >        ext4_find_extent(orig_path)
> > >          path = *orig_path
> > >          read_extent_tree_block
> > >            // return -ENOMEM or -EIO
> > >          ext4_free_ext_path(path)
> > >            kfree(path)
> > >          *orig_path = NULL
> > >    a. If err is -ENOMEM:
> > >    ext4_ext_dirty(path + path->p_depth)
> > >    // path use-after-free !!!
> > >    b. If err is -EIO and we have EXT_DEBUG defined:
> > >    ext4_ext_show_leaf(path)
> > >      eh = path[depth].p_hdr
> > >      // path also use-after-free !!!
> > > 
> > > So when trying to zeroout or fix the extent length, call ext4_find_extent()
> > > to update the path.
> > > 
> > > In addition we use *ppath directly as an ext4_ext_show_leaf() input to
> > > avoid possible use-after-free when EXT_DEBUG is defined, and to avoid
> > > unnecessary path updates.
> > > 
> > > Fixes: dfe5080939ea ("ext4: drop EXT4_EX_NOFREE_ON_ERR from rest of extents handling code")
> > > Cc: stable@kernel.org
> > > Signed-off-by: Baokun Li <libaokun1@huawei.com>
> > > ---
> > >   fs/ext4/extents.c | 21 ++++++++++++++++++++-
> > >   1 file changed, 20 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> > > index 6e5b5baf3aa6..3a70a0739af8 100644
> > > --- a/fs/ext4/extents.c
> > > +++ b/fs/ext4/extents.c
> > > @@ -3252,6 +3252,25 @@ static int ext4_split_extent_at(handle_t *handle,
> > >    if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
> > >      goto out;
> > > + /*
> > > +  * Update path is required because previous ext4_ext_insert_extent()
> > > +  * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
> > > +  * guarantees that ext4_find_extent() will not return -ENOMEM,
> > > +  * otherwise -ENOMEM will cause a retry in do_writepages(), and a
> > > +  * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
> > > +  * an incorrect ee_len causing the i_reserved_data_blocks exception.
> > > +  */
> > > + path = ext4_find_extent(inode, ee_block, ppath,
> > > +       flags | EXT4_EX_NOFAIL);
> > > + if (IS_ERR(path)) {
> > > +   EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
> > > +        split, PTR_ERR(path));
> > > +   return PTR_ERR(path);
> > > + }
> > > + depth = ext_depth(inode);
> > > + ex = path[depth].p_ext;
> > > + *ppath = path;
> > > +
> > Hi Baokun, nice catch!
> > 
> > I was just wondering if it makes more sense to only update path if we
> > encounter an error:
> > 
> >    err = ext4_ext_insert_extent(handle, inode, ppath, &newex, flags);
> >    if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
> >      goto out;
> >    else if (err < 0) {
> >      ...
> >      path = ext4_find_extent(inode, ee_block, ppath, flags | EXT4_EX_NOFAIL);
> >    }
> > 
> > I believe that's the only time we'd end up with the use after free issue
> > and this way we can avoid the (maybe tiny) performance overhead of calling
> > the extra find extent. What are your thoughts?
> > 
> > regards,
> > ojaswin
> Hi Ojaswin,
> 
> We're already there now, updating the path only on recoverable,
> non-critical errors (-ENOSPC, -EDQUOT, and -ENOMEM).
> 
> Thanks,
> Baokun

Hey Baokun, yes you are right I somehow misread the code:

Feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin

> > >    if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
> > >      if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
> > >        if (split_flag & EXT4_EXT_DATA_VALID1) {
> > > @@ -3304,7 +3323,7 @@ static int ext4_split_extent_at(handle_t *handle,
> > >    ext4_ext_dirty(handle, inode, path + path->p_depth);
> > >    return err;
> > >   out:
> > > - ext4_ext_show_leaf(inode, path);
> > > + ext4_ext_show_leaf(inode, *ppath);
> > >    return err;
> > >   }
> > > -- 
> > > 2.39.2
> > > 
> -- 
> With Best Regards,
> Baokun Li
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent()
  2024-07-10  4:06 ` [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent() libaokun
  2024-07-25 10:38   ` Jan Kara
@ 2024-07-30 10:03   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-07-30 10:03 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:42PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> Getting rid of ppath in ext4_find_extent() requires its caller to update
> ppath. These ppaths will also be dropped later. No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> ---
>  fs/ext4/ext4.h        |  2 +-
>  fs/ext4/extents.c     | 52 +++++++++++++++++++++++--------------------
>  fs/ext4/move_extent.c |  6 ++---
>  3 files changed, 32 insertions(+), 28 deletions(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 8007abd4972d..cbe8d6062c52 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -3714,7 +3714,7 @@ extern int ext4_ext_insert_extent(handle_t *, struct inode *,
>  				  struct ext4_ext_path **,
>  				  struct ext4_extent *, int);
>  extern struct ext4_ext_path *ext4_find_extent(struct inode *, ext4_lblk_t,
> -					      struct ext4_ext_path **,
> +					      struct ext4_ext_path *,
>  					      int flags);
>  extern void ext4_free_ext_path(struct ext4_ext_path *);
>  extern int ext4_ext_check_inode(struct inode *inode);
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index b1cfce5b57d2..5217e6f53467 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -884,11 +884,10 @@ void ext4_ext_tree_init(handle_t *handle, struct inode *inode)
>  
>  struct ext4_ext_path *
>  ext4_find_extent(struct inode *inode, ext4_lblk_t block,
> -		 struct ext4_ext_path **orig_path, int flags)
> +		 struct ext4_ext_path *path, int flags)
>  {
>  	struct ext4_extent_header *eh;
>  	struct buffer_head *bh;
> -	struct ext4_ext_path *path = orig_path ? *orig_path : NULL;
>  	short int depth, i, ppos = 0;
>  	int ret;
>  	gfp_t gfp_flags = GFP_NOFS;
> @@ -909,7 +908,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
>  		ext4_ext_drop_refs(path);
>  		if (depth > path[0].p_maxdepth) {
>  			kfree(path);
> -			*orig_path = path = NULL;
> +			path = NULL;
>  		}
>  	}
>  	if (!path) {
> @@ -964,8 +963,6 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
>  
>  err:
>  	ext4_free_ext_path(path);
> -	if (orig_path)
> -		*orig_path = NULL;
>  	return ERR_PTR(ret);
>  }
>  
> @@ -1430,7 +1427,7 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>  		/* refill path */
>  		path = ext4_find_extent(inode,
>  				    (ext4_lblk_t)le32_to_cpu(newext->ee_block),
> -				    ppath, gb_flags);
> +				    path, gb_flags);
>  		if (IS_ERR(path))
>  			err = PTR_ERR(path);
>  	} else {
> @@ -1442,7 +1439,7 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>  		/* refill path */
>  		path = ext4_find_extent(inode,
>  				   (ext4_lblk_t)le32_to_cpu(newext->ee_block),
> -				    ppath, gb_flags);
> +				    path, gb_flags);
>  		if (IS_ERR(path)) {
>  			err = PTR_ERR(path);
>  			goto out;
> @@ -1458,8 +1455,8 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>  			goto repeat;
>  		}
>  	}
> -
>  out:
> +	*ppath = IS_ERR(path) ? NULL : path;
>  	return err;
>  }
>  
> @@ -3260,11 +3257,12 @@ static int ext4_split_extent_at(handle_t *handle,
>  	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
>  	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
>  	 */
> -	path = ext4_find_extent(inode, ee_block, ppath,
> +	path = ext4_find_extent(inode, ee_block, *ppath,
>  				flags | EXT4_EX_NOFAIL);
>  	if (IS_ERR(path)) {
>  		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
>  				 split, PTR_ERR(path));
> +		*ppath = NULL;
>  		return PTR_ERR(path);
>  	}
>  	depth = ext_depth(inode);
> @@ -3379,9 +3377,12 @@ static int ext4_split_extent(handle_t *handle,
>  	 * Update path is required because previous ext4_split_extent_at() may
>  	 * result in split of original leaf or extent zeroout.
>  	 */
> -	path = ext4_find_extent(inode, map->m_lblk, ppath, flags);
> -	if (IS_ERR(path))
> +	path = ext4_find_extent(inode, map->m_lblk, *ppath, flags);
> +	if (IS_ERR(path)) {
> +		*ppath = NULL;
>  		return PTR_ERR(path);
> +	}
> +	*ppath = path;
>  	depth = ext_depth(inode);
>  	ex = path[depth].p_ext;
>  	if (!ex) {
> @@ -3767,9 +3768,12 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
>  						 EXT4_GET_BLOCKS_CONVERT);
>  		if (err < 0)
>  			return err;
> -		path = ext4_find_extent(inode, map->m_lblk, ppath, 0);
> -		if (IS_ERR(path))
> +		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
> +		if (IS_ERR(path)) {
> +			*ppath = NULL;
>  			return PTR_ERR(path);
> +		}
> +		*ppath = path;
>  		depth = ext_depth(inode);
>  		ex = path[depth].p_ext;
>  	}
> @@ -3825,9 +3829,12 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN);
>  		if (err < 0)
>  			return err;
> -		path = ext4_find_extent(inode, map->m_lblk, ppath, 0);
> -		if (IS_ERR(path))
> +		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
> +		if (IS_ERR(path)) {
> +			*ppath = NULL;
>  			return PTR_ERR(path);
> +		}
> +		*ppath = path;
>  		depth = ext_depth(inode);
>  		ex = path[depth].p_ext;
>  		if (!ex) {
> @@ -5224,7 +5231,7 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
>  	* won't be shifted beyond EXT_MAX_BLOCKS.
>  	*/
>  	if (SHIFT == SHIFT_LEFT) {
> -		path = ext4_find_extent(inode, start - 1, &path,
> +		path = ext4_find_extent(inode, start - 1, path,
>  					EXT4_EX_NOCACHE);
>  		if (IS_ERR(path))
>  			return PTR_ERR(path);
> @@ -5273,7 +5280,7 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
>  	 * becomes NULL to indicate the end of the loop.
>  	 */
>  	while (iterator && start <= stop) {
> -		path = ext4_find_extent(inode, *iterator, &path,
> +		path = ext4_find_extent(inode, *iterator, path,
>  					EXT4_EX_NOCACHE);
>  		if (IS_ERR(path))
>  			return PTR_ERR(path);
> @@ -5854,11 +5861,8 @@ int ext4_clu_mapped(struct inode *inode, ext4_lblk_t lclu)
>  
>  	/* search for the extent closest to the first block in the cluster */
>  	path = ext4_find_extent(inode, EXT4_C2B(sbi, lclu), NULL, 0);
> -	if (IS_ERR(path)) {
> -		err = PTR_ERR(path);
> -		path = NULL;
> -		goto out;
> -	}
> +	if (IS_ERR(path))
> +		return PTR_ERR(path);
>  
>  	depth = ext_depth(inode);
>  
> @@ -5942,7 +5946,7 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
>  		if (ret)
>  			goto out;
>  
> -		path = ext4_find_extent(inode, start, &path, 0);
> +		path = ext4_find_extent(inode, start, path, 0);
>  		if (IS_ERR(path))
>  			return PTR_ERR(path);
>  		ex = path[path->p_depth].p_ext;
> @@ -5956,7 +5960,7 @@ int ext4_ext_replay_update_ex(struct inode *inode, ext4_lblk_t start,
>  			if (ret)
>  				goto out;
>  
> -			path = ext4_find_extent(inode, start, &path, 0);
> +			path = ext4_find_extent(inode, start, path, 0);
>  			if (IS_ERR(path))
>  				return PTR_ERR(path);
>  			ex = path[path->p_depth].p_ext;
> diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
> index 204f53b23622..b0ab19a913bf 100644
> --- a/fs/ext4/move_extent.c
> +++ b/fs/ext4/move_extent.c
> @@ -26,14 +26,14 @@ static inline int
>  get_ext_path(struct inode *inode, ext4_lblk_t lblock,
>  		struct ext4_ext_path **ppath)
>  {
> -	struct ext4_ext_path *path;
> +	struct ext4_ext_path *path = *ppath;
>  
> -	path = ext4_find_extent(inode, lblock, ppath, EXT4_EX_NOCACHE);
> +	*ppath = NULL;
> +	path = ext4_find_extent(inode, lblock, path, EXT4_EX_NOCACHE);
>  	if (IS_ERR(path))
>  		return PTR_ERR(path);
>  	if (path[ext_depth(inode)].p_ext == NULL) {
>  		ext4_free_ext_path(path);
> -		*ppath = NULL;
>  		return -ENODATA;
>  	}
>  	*ppath = path;
Hi Baokun,

The changes look good to me, afaict we've handled updating ppath in all
the places correctly.

Feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin
> -- 
> 2.39.2

> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 03/20] ext4: fix double brelse() the buffer of the extents path
  2024-07-30  8:47     ` Baokun Li
@ 2024-08-01  6:26       ` Ojaswin Mujoo
  0 siblings, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-01  6:26 UTC (permalink / raw)
  To: Baokun Li
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, stable

On Tue, Jul 30, 2024 at 04:47:56PM +0800, Baokun Li wrote:
> On 2024/7/26 19:45, Ojaswin Mujoo wrote:
> > On Wed, Jul 10, 2024 at 12:06:37PM +0800, libaokun@huaweicloud.com wrote:
> > > From: Baokun Li <libaokun1@huawei.com>
> > >  snip
> > Hi Baokun,
> > 
> > If i'm not wrong, in this trace, we'll enter ext4_ext_insert_extent() with
> > gb_flags having EXT4_GET_BLOCKS_PRE_IO so we won't actually go for a
> > merge_up.
> > 
> > That being said, there seems to be a few places where we follow the
> > split-insert pattern and it might be possible that one of those call
> > sites might not be passing EXT4_GET_BLOCKS_PRE_IO and we'll the double
> > free issue you mentioned. I'll check and update if I see anything.
> Hi Ojaswin,
> 
> You're right. I am very sorry for the confusion.
> 
> The trace here is wrong, this patch should actually be placed after the two
> UAF patches. Here ext4_ext_try_to_merge() is called when trying zeroout in
> ext4_split_extent_at(). It is called when trying zeroout with or without
> EXT4_GET_BLOCKS_PRE_IO.The correct trace is as follows:
> 
>   split2    map    split1
> |--------|-------|--------|
> 
> ext4_ext_map_blocks
>  ext4_ext_handle_unwritten_extents
>   ext4_split_convert_extents
>    // path->p_depth == 0
>    ext4_split_extent
>      // 1. do split1
>      ext4_split_extent_at
>        |ext4_ext_insert_extent
>        |  ext4_ext_create_new_leaf
>        |    ext4_ext_grow_indepth
>        |      le16_add_cpu(&neh->eh_depth, 1)
>        |    ext4_find_extent
>        |      // return -ENOMEM
>        |// get error and try zeroout
>        |path = ext4_find_extent
>        |  path->p_depth = 1
>        |ext4_ext_try_to_merge
>        |  ext4_ext_try_to_merge_up
>        |    path->p_depth = 0
>        |    brelse(path[1].p_bh)  ---> not set to NULL here
>        |// zeroout success
>      // 2. update path
>      ext4_find_extent
>      // 3. do split2
>      ext4_split_extent_at
>        ext4_ext_insert_extent
>          ext4_ext_create_new_leaf
>            ext4_ext_grow_indepth
>              le16_add_cpu(&neh->eh_depth, 1)
>            ext4_find_extent
>              path[0].p_bh = NULL;
>              path->p_depth = 1
>              read_extent_tree_block  ---> return err
>              // path[1].p_bh is still the old value
>  ext4_free_ext_path
>   ext4_ext_drop_refs
>    // path->p_depth == 1
>    brelse(path[1].p_bh)  ---> brelse a buffer twice
> 
> I'll adjust the order of the patches and correct the trace in the next
> version.

Hey Baokun,

The corrected trace looks good, thanks.

Regards,
Ojaswin

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 09/20] ext4: get rid of ppath in get_ext_path()
  2024-07-10  4:06 ` [PATCH 09/20] ext4: get rid of ppath in get_ext_path() libaokun
  2024-07-25 10:41   ` Jan Kara
@ 2024-08-01  7:16   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-01  7:16 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:43PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> After getting rid of ppath in get_ext_path(), its caller may pass an error
> pointer to ext4_free_ext_path(), so it needs to teach ext4_free_ext_path()
> and ext4_ext_drop_refs() to skip the error pointer. No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> ---
>  fs/ext4/extents.c     |  5 +++--
>  fs/ext4/move_extent.c | 34 +++++++++++++++++-----------------
>  2 files changed, 20 insertions(+), 19 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 5217e6f53467..6dfb5d03e197 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -116,7 +116,7 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
>  {
>  	int depth, i;
>  
> -	if (!path)
> +	if (IS_ERR_OR_NULL(path))
>  		return;
>  	depth = path->p_depth;
>  	for (i = 0; i <= depth; i++, path++)
> @@ -125,6 +125,8 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
>  
>  void ext4_free_ext_path(struct ext4_ext_path *path)
>  {
> +	if (IS_ERR_OR_NULL(path))
> +		return;
>  	ext4_ext_drop_refs(path);
>  	kfree(path);
>  }
> @@ -4191,7 +4193,6 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  	path = ext4_find_extent(inode, map->m_lblk, NULL, 0);
>  	if (IS_ERR(path)) {
>  		err = PTR_ERR(path);
> -		path = NULL;
>  		goto out;
>  	}
>  
> diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
> index b0ab19a913bf..a7186d63725a 100644
> --- a/fs/ext4/move_extent.c
> +++ b/fs/ext4/move_extent.c
> @@ -17,27 +17,23 @@
>   * get_ext_path() - Find an extent path for designated logical block number.
>   * @inode:	inode to be searched
>   * @lblock:	logical block number to find an extent path
> - * @ppath:	pointer to an extent path pointer (for output)
> + * @path:	pointer to an extent path
>   *
> - * ext4_find_extent wrapper. Return 0 on success, or a negative error value
> - * on failure.
> + * ext4_find_extent wrapper. Return an extent path pointer on success,
> + * or an error pointer on failure.
>   */
> -static inline int
> +static inline struct ext4_ext_path *
>  get_ext_path(struct inode *inode, ext4_lblk_t lblock,
> -		struct ext4_ext_path **ppath)
> +	     struct ext4_ext_path *path)
>  {
> -	struct ext4_ext_path *path = *ppath;
> -
> -	*ppath = NULL;
>  	path = ext4_find_extent(inode, lblock, path, EXT4_EX_NOCACHE);
>  	if (IS_ERR(path))
> -		return PTR_ERR(path);
> +		return path;
>  	if (path[ext_depth(inode)].p_ext == NULL) {
>  		ext4_free_ext_path(path);
> -		return -ENODATA;
> +		return ERR_PTR(-ENODATA);
>  	}
> -	*ppath = path;
> -	return 0;
> +	return path;
>  }
>  
>  /**
> @@ -95,9 +91,11 @@ mext_check_coverage(struct inode *inode, ext4_lblk_t from, ext4_lblk_t count,
>  	int ret = 0;
>  	ext4_lblk_t last = from + count;
>  	while (from < last) {
> -		*err = get_ext_path(inode, from, &path);
> -		if (*err)
> -			goto out;
> +		path = get_ext_path(inode, from, path);
> +		if (IS_ERR(path)) {
> +			*err = PTR_ERR(path);
> +			return ret;
> +		}
>  		ext = path[ext_depth(inode)].p_ext;
>  		if (unwritten != ext4_ext_is_unwritten(ext))
>  			goto out;
> @@ -624,9 +622,11 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
>  		int offset_in_page;
>  		int unwritten, cur_len;
>  
> -		ret = get_ext_path(orig_inode, o_start, &path);
> -		if (ret)
> +		path = get_ext_path(orig_inode, o_start, path);
> +		if (IS_ERR(path)) {
> +			ret = PTR_ERR(path);
>  			goto out;
> +		}
>  		ex = path[path->p_depth].p_ext;
>  		cur_blk = le32_to_cpu(ex->ee_block);
>  		cur_len = ext4_ext_get_actual_len(ex);
> -- 
> 2.39.2
Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf()
  2024-07-10  4:06 ` [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf() libaokun
  2024-07-25 10:46   ` Jan Kara
@ 2024-08-02  7:34   ` Ojaswin Mujoo
  2024-08-02 13:07     ` Baokun Li
  1 sibling, 1 reply; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02  7:34 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:44PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
> done here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Hi Baokun,

The changes look good to me, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

That being said, IIUC i think this patchset also fixes a potential UAF
bug. Below is a sample trace with dummy values:

ext4_ext_insert_extent
  path = *ppath = 2000
  ext4_ext_create_new_leaf(ppath)
    path = *ppath = 2000
    ext4_find_extent(path = 2000)
      if (depth > path[0].p_maxdepth)
            kfree(path = 2000);
            path = NULL;
      path = kcalloc() = 3000
      ...
      return path;
  path = 3000
  *ppath = 3000;
  return;
/* here path is still 2000 *, UAF! */
eh = path[depth].p_hdr 

I'm not completely sure if we can hit (depth > path[0].p_maxdepth) in the
above codepath but I think the flow is still a bit fragile. Maybe this
should be fixed in a separate patch first. What do you think?

Regards,
ojaswin

 ---
>  fs/ext4/extents.c | 41 +++++++++++++++++++++--------------------
>  1 file changed, 21 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 6dfb5d03e197..0d6ce9e74b01 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -1397,13 +1397,12 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
>   * finds empty index and adds new leaf.
>   * if no free index is found, then it requests in-depth growing.
>   */
> -static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
> -           unsigned int mb_flags,
> -           unsigned int gb_flags,
> -           struct ext4_ext_path **ppath,
> -           struct ext4_extent *newext)
> +static struct ext4_ext_path *
> +ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
> +      unsigned int mb_flags, unsigned int gb_flags,
> +      struct ext4_ext_path *path,
> +      struct ext4_extent *newext)
>  {
> - struct ext4_ext_path *path = *ppath;
>   struct ext4_ext_path *curp;
>   int depth, i, err = 0;
>  
> @@ -1424,28 +1423,24 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>      * entry: create all needed subtree and add new leaf */
>     err = ext4_ext_split(handle, inode, mb_flags, path, newext, i);
>     if (err)
> -     goto out;
> +     goto errout;
>  
>     /* refill path */
>     path = ext4_find_extent(inode,
>             (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>             path, gb_flags);
> -   if (IS_ERR(path))
> -     err = PTR_ERR(path);
>   } else {
>     /* tree is full, time to grow in depth */
>     err = ext4_ext_grow_indepth(handle, inode, mb_flags);
>     if (err)
> -     goto out;
> +     goto errout;
>  
>     /* refill path */
>     path = ext4_find_extent(inode,
>            (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>             path, gb_flags);
> -   if (IS_ERR(path)) {
> -     err = PTR_ERR(path);
> -     goto out;
> -   }
> +   if (IS_ERR(path))
> +     return path;
>  
>     /*
>      * only first (depth 0 -> 1) produces free space;
> @@ -1457,9 +1452,11 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>       goto repeat;
>     }
>   }
> -out:
> - *ppath = IS_ERR(path) ? NULL : path;
> - return err;
> + return path;
> +
> +errout:
> + ext4_free_ext_path(path);
> + return ERR_PTR(err);
>  }
>  
>  /*
> @@ -2112,10 +2109,14 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>    */
>   if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
>     mb_flags |= EXT4_MB_USE_RESERVED;
> - err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
> -              ppath, newext);
> - if (err)
> + path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
> +         path, newext);
> + if (IS_ERR(path)) {
> +   *ppath = NULL;
> +   err = PTR_ERR(path);
>     goto cleanup;
> + }
> + *ppath = path;
>   depth = ext_depth(inode);
>   eh = path[depth].p_hdr;
>  
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 11/20] ext4: get rid of ppath in ext4_ext_insert_extent()
  2024-07-10  4:06 ` [PATCH 11/20] ext4: get rid of ppath in ext4_ext_insert_extent() libaokun
  2024-07-25 10:59   ` Jan Kara
@ 2024-08-02  8:01   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02  8:01 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:45PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_insert_extent(), the following is done
> here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
>  * Free path when npath is used, free npath when it is not used.
>  * The got_allocated_blocks label in ext4_ext_map_blocks() does not
>    update err now, so err is updated to 0 if the err returned by
>    ext4_ext_search_right() is greater than 0 and is about to enter
>    got_allocated_blocks.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin
> ---
>  fs/ext4/ext4.h        |  7 ++--
>  fs/ext4/extents.c     | 88 ++++++++++++++++++++++++-------------------
>  fs/ext4/fast_commit.c |  8 ++--
>  fs/ext4/migrate.c     |  5 ++-
>  4 files changed, 61 insertions(+), 47 deletions(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index cbe8d6062c52..53b4c1f454e6 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -3710,9 +3710,10 @@ extern int ext4_map_blocks(handle_t *handle, struct inode *inode,
>  extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode,
>  						   int num,
>  						   struct ext4_ext_path *path);
> -extern int ext4_ext_insert_extent(handle_t *, struct inode *,
> -				  struct ext4_ext_path **,
> -				  struct ext4_extent *, int);
> +extern struct ext4_ext_path *ext4_ext_insert_extent(
> +				handle_t *handle, struct inode *inode,
> +				struct ext4_ext_path *path,
> +				struct ext4_extent *newext, int gb_flags);
>  extern struct ext4_ext_path *ext4_find_extent(struct inode *, ext4_lblk_t,
>  					      struct ext4_ext_path *,
>  					      int flags);
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 0d6ce9e74b01..fc75390d591a 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -1974,16 +1974,15 @@ static unsigned int ext4_ext_check_overlap(struct ext4_sb_info *sbi,
>   * inserts requested extent as new one into the tree,
>   * creating new leaf in the no-space case.
>   */
> -int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
> -				struct ext4_ext_path **ppath,
> -				struct ext4_extent *newext, int gb_flags)
> +struct ext4_ext_path *
> +ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
> +		       struct ext4_ext_path *path,
> +		       struct ext4_extent *newext, int gb_flags)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	struct ext4_extent_header *eh;
>  	struct ext4_extent *ex, *fex;
>  	struct ext4_extent *nearex; /* nearest extent */
> -	struct ext4_ext_path *npath = NULL;
> -	int depth, len, err;
> +	int depth, len, err = 0;
>  	ext4_lblk_t next;
>  	int mb_flags = 0, unwritten;
>  
> @@ -1991,14 +1990,16 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  		mb_flags |= EXT4_MB_DELALLOC_RESERVED;
>  	if (unlikely(ext4_ext_get_actual_len(newext) == 0)) {
>  		EXT4_ERROR_INODE(inode, "ext4_ext_get_actual_len(newext) == 0");
> -		return -EFSCORRUPTED;
> +		err = -EFSCORRUPTED;
> +		goto errout;
>  	}
>  	depth = ext_depth(inode);
>  	ex = path[depth].p_ext;
>  	eh = path[depth].p_hdr;
>  	if (unlikely(path[depth].p_hdr == NULL)) {
>  		EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
> -		return -EFSCORRUPTED;
> +		err = -EFSCORRUPTED;
> +		goto errout;
>  	}
>  
>  	/* try to insert block into found extent and return */
> @@ -2036,7 +2037,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  			err = ext4_ext_get_access(handle, inode,
>  						  path + depth);
>  			if (err)
> -				return err;
> +				goto errout;
>  			unwritten = ext4_ext_is_unwritten(ex);
>  			ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
>  					+ ext4_ext_get_actual_len(newext));
> @@ -2061,7 +2062,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  			err = ext4_ext_get_access(handle, inode,
>  						  path + depth);
>  			if (err)
> -				return err;
> +				goto errout;
>  
>  			unwritten = ext4_ext_is_unwritten(ex);
>  			ex->ee_block = newext->ee_block;
> @@ -2086,21 +2087,26 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  	if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block))
>  		next = ext4_ext_next_leaf_block(path);
>  	if (next != EXT_MAX_BLOCKS) {
> +		struct ext4_ext_path *npath;
> +
>  		ext_debug(inode, "next leaf block - %u\n", next);
> -		BUG_ON(npath != NULL);
>  		npath = ext4_find_extent(inode, next, NULL, gb_flags);
> -		if (IS_ERR(npath))
> -			return PTR_ERR(npath);
> +		if (IS_ERR(npath)) {
> +			err = PTR_ERR(npath);
> +			goto errout;
> +		}
>  		BUG_ON(npath->p_depth != path->p_depth);
>  		eh = npath[depth].p_hdr;
>  		if (le16_to_cpu(eh->eh_entries) < le16_to_cpu(eh->eh_max)) {
>  			ext_debug(inode, "next leaf isn't full(%d)\n",
>  				  le16_to_cpu(eh->eh_entries));
> +			ext4_free_ext_path(path);
>  			path = npath;
>  			goto has_space;
>  		}
>  		ext_debug(inode, "next leaf has no free space(%d,%d)\n",
>  			  le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
> +		ext4_free_ext_path(npath);
>  	}
>  
>  	/*
> @@ -2111,12 +2117,8 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  		mb_flags |= EXT4_MB_USE_RESERVED;
>  	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
>  					path, newext);
> -	if (IS_ERR(path)) {
> -		*ppath = NULL;
> -		err = PTR_ERR(path);
> -		goto cleanup;
> -	}
> -	*ppath = path;
> +	if (IS_ERR(path))
> +		return path;
>  	depth = ext_depth(inode);
>  	eh = path[depth].p_hdr;
>  
> @@ -2125,7 +2127,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  
>  	err = ext4_ext_get_access(handle, inode, path + depth);
>  	if (err)
> -		goto cleanup;
> +		goto errout;
>  
>  	if (!nearex) {
>  		/* there is no extent in this leaf, create first one */
> @@ -2183,17 +2185,20 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  	if (!(gb_flags & EXT4_GET_BLOCKS_PRE_IO))
>  		ext4_ext_try_to_merge(handle, inode, path, nearex);
>  
> -
>  	/* time to correct all indexes above */
>  	err = ext4_ext_correct_indexes(handle, inode, path);
>  	if (err)
> -		goto cleanup;
> +		goto errout;
>  
>  	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
> +	if (err)
> +		goto errout;
>  
> -cleanup:
> -	ext4_free_ext_path(npath);
> -	return err;
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  static int ext4_fill_es_cache_info(struct inode *inode,
> @@ -3248,24 +3253,29 @@ static int ext4_split_extent_at(handle_t *handle,
>  	if (split_flag & EXT4_EXT_MARK_UNWRIT2)
>  		ext4_ext_mark_unwritten(ex2);
>  
> -	err = ext4_ext_insert_extent(handle, inode, ppath, &newex, flags);
> -	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
> +	path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
> +	if (!IS_ERR(path)) {
> +		*ppath = path;
>  		goto out;
> +	}
> +	*ppath = NULL;
> +	err = PTR_ERR(path);
> +	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
> +		return err;
>  
>  	/*
> -	 * Update path is required because previous ext4_ext_insert_extent()
> -	 * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
> -	 * guarantees that ext4_find_extent() will not return -ENOMEM,
> -	 * otherwise -ENOMEM will cause a retry in do_writepages(), and a
> -	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
> -	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
> +	 * Get a new path to try to zeroout or fix the extent length.
> +	 * Using EXT4_EX_NOFAIL guarantees that ext4_find_extent()
> +	 * will not return -ENOMEM, otherwise -ENOMEM will cause a
> +	 * retry in do_writepages(), and a WARN_ON may be triggered
> +	 * in ext4_da_update_reserve_space() due to an incorrect
> +	 * ee_len causing the i_reserved_data_blocks exception.
>  	 */
> -	path = ext4_find_extent(inode, ee_block, *ppath,
> +	path = ext4_find_extent(inode, ee_block, NULL,
>  				flags | EXT4_EX_NOFAIL);
>  	if (IS_ERR(path)) {
>  		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
>  				 split, PTR_ERR(path));
> -		*ppath = NULL;
>  		return PTR_ERR(path);
>  	}
>  	depth = ext_depth(inode);
> @@ -3324,7 +3334,7 @@ static int ext4_split_extent_at(handle_t *handle,
>  	ext4_ext_dirty(handle, inode, path + path->p_depth);
>  	return err;
>  out:
> -	ext4_ext_show_leaf(inode, *ppath);
> +	ext4_ext_show_leaf(inode, path);
>  	return err;
>  }
>  
> @@ -4313,6 +4323,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  	    get_implied_cluster_alloc(inode->i_sb, map, &ex2, path)) {
>  		ar.len = allocated = map->m_len;
>  		newblock = map->m_pblk;
> +		err = 0;
>  		goto got_allocated_blocks;
>  	}
>  
> @@ -4385,8 +4396,9 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  		map->m_flags |= EXT4_MAP_UNWRITTEN;
>  	}
>  
> -	err = ext4_ext_insert_extent(handle, inode, &path, &newex, flags);
> -	if (err) {
> +	path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
> +	if (IS_ERR(path)) {
> +		err = PTR_ERR(path);
>  		if (allocated_clusters) {
>  			int fb_flags = 0;
>  
> diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
> index 87c009e0c59a..1dee40477727 100644
> --- a/fs/ext4/fast_commit.c
> +++ b/fs/ext4/fast_commit.c
> @@ -1777,12 +1777,12 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
>  			if (ext4_ext_is_unwritten(ex))
>  				ext4_ext_mark_unwritten(&newex);
>  			down_write(&EXT4_I(inode)->i_data_sem);
> -			ret = ext4_ext_insert_extent(
> -				NULL, inode, &path, &newex, 0);
> +			path = ext4_ext_insert_extent(NULL, inode,
> +						      path, &newex, 0);
>  			up_write((&EXT4_I(inode)->i_data_sem));
> -			ext4_free_ext_path(path);
> -			if (ret)
> +			if (IS_ERR(path))
>  				goto out;
> +			ext4_free_ext_path(path);
>  			goto next;
>  		}
>  
> diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
> index d98ac2af8199..0f68b8a14560 100644
> --- a/fs/ext4/migrate.c
> +++ b/fs/ext4/migrate.c
> @@ -37,7 +37,6 @@ static int finish_range(handle_t *handle, struct inode *inode,
>  	path = ext4_find_extent(inode, lb->first_block, NULL, 0);
>  	if (IS_ERR(path)) {
>  		retval = PTR_ERR(path);
> -		path = NULL;
>  		goto err_out;
>  	}
>  
> @@ -53,7 +52,9 @@ static int finish_range(handle_t *handle, struct inode *inode,
>  	retval = ext4_datasem_ensure_credits(handle, inode, needed, needed, 0);
>  	if (retval < 0)
>  		goto err_out;
> -	retval = ext4_ext_insert_extent(handle, inode, &path, &newext, 0);
> +	path = ext4_ext_insert_extent(handle, inode, path, &newext, 0);
> +	if (IS_ERR(path))
> +		retval = PTR_ERR(path);
>  err_out:
>  	up_write((&EXT4_I(inode)->i_data_sem));
>  	ext4_free_ext_path(path);
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at()
  2024-07-10  4:06 ` [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at() libaokun
  2024-07-25 11:07   ` Jan Kara
@ 2024-08-02  8:12   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02  8:12 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:46PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_split_extent_at(), the following is done
> here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
>  * Teach ext4_ext_show_leaf() to skip error pointer.
>  * Propagate ext4_find_extent() error return value in ext4_insert_range().
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Hi Baokun,

Change looks good, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin
> ---
>  fs/ext4/extents.c | 86 ++++++++++++++++++++++++++---------------------
>  1 file changed, 48 insertions(+), 38 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index fc75390d591a..c86b1bb7720f 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -84,12 +84,11 @@ static void ext4_extent_block_csum_set(struct inode *inode,
>  	et->et_checksum = ext4_extent_block_csum(inode, eh);
>  }
>  
> -static int ext4_split_extent_at(handle_t *handle,
> -			     struct inode *inode,
> -			     struct ext4_ext_path **ppath,
> -			     ext4_lblk_t split,
> -			     int split_flag,
> -			     int flags);
> +static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
> +						  struct inode *inode,
> +						  struct ext4_ext_path *path,
> +						  ext4_lblk_t split,
> +						  int split_flag, int flags);
>  
>  static int ext4_ext_trunc_restart_fn(struct inode *inode, int *dropped)
>  {
> @@ -341,9 +340,15 @@ ext4_force_split_extent_at(handle_t *handle, struct inode *inode,
>  	if (nofail)
>  		flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL;
>  
> -	return ext4_split_extent_at(handle, inode, ppath, lblk, unwritten ?
> +	path = ext4_split_extent_at(handle, inode, path, lblk, unwritten ?
>  			EXT4_EXT_MARK_UNWRIT1|EXT4_EXT_MARK_UNWRIT2 : 0,
>  			flags);
> +	if (IS_ERR(path)) {
> +		*ppath = NULL;
> +		return PTR_ERR(path);
> +	}
> +	*ppath = path;
> +	return 0;
>  }
>  
>  static int
> @@ -694,7 +699,7 @@ static void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path)
>  	struct ext4_extent *ex;
>  	int i;
>  
> -	if (!path)
> +	if (IS_ERR_OR_NULL(path))
>  		return;
>  
>  	eh = path[depth].p_hdr;
> @@ -3174,16 +3179,14 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
>   *  a> the extent are splitted into two extent.
>   *  b> split is not needed, and just mark the extent.
>   *
> - * return 0 on success.
> + * Return an extent path pointer on success, or an error pointer on failure.
>   */
> -static int ext4_split_extent_at(handle_t *handle,
> -			     struct inode *inode,
> -			     struct ext4_ext_path **ppath,
> -			     ext4_lblk_t split,
> -			     int split_flag,
> -			     int flags)
> +static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
> +						  struct inode *inode,
> +						  struct ext4_ext_path *path,
> +						  ext4_lblk_t split,
> +						  int split_flag, int flags)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	ext4_fsblk_t newblock;
>  	ext4_lblk_t ee_block;
>  	struct ext4_extent *ex, newex, orig_ex, zero_ex;
> @@ -3254,14 +3257,12 @@ static int ext4_split_extent_at(handle_t *handle,
>  		ext4_ext_mark_unwritten(ex2);
>  
>  	path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
> -	if (!IS_ERR(path)) {
> -		*ppath = path;
> +	if (!IS_ERR(path))
>  		goto out;
> -	}
> -	*ppath = NULL;
> +
>  	err = PTR_ERR(path);
>  	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
> -		return err;
> +		return path;
>  
>  	/*
>  	 * Get a new path to try to zeroout or fix the extent length.
> @@ -3271,16 +3272,14 @@ static int ext4_split_extent_at(handle_t *handle,
>  	 * in ext4_da_update_reserve_space() due to an incorrect
>  	 * ee_len causing the i_reserved_data_blocks exception.
>  	 */
> -	path = ext4_find_extent(inode, ee_block, NULL,
> -				flags | EXT4_EX_NOFAIL);
> +	path = ext4_find_extent(inode, ee_block, NULL, flags | EXT4_EX_NOFAIL);
>  	if (IS_ERR(path)) {
>  		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
>  				 split, PTR_ERR(path));
> -		return PTR_ERR(path);
> +		return path;
>  	}
>  	depth = ext_depth(inode);
>  	ex = path[depth].p_ext;
> -	*ppath = path;
>  
>  	if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
>  		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
> @@ -3332,10 +3331,13 @@ static int ext4_split_extent_at(handle_t *handle,
>  	 * and err is a non-zero error code.
>  	 */
>  	ext4_ext_dirty(handle, inode, path + path->p_depth);
> -	return err;
>  out:
> +	if (err) {
> +		ext4_free_ext_path(path);
> +		path = ERR_PTR(err);
> +	}
>  	ext4_ext_show_leaf(inode, path);
> -	return err;
> +	return path;
>  }
>  
>  /*
> @@ -3379,10 +3381,14 @@ static int ext4_split_extent(handle_t *handle,
>  				       EXT4_EXT_MARK_UNWRIT2;
>  		if (split_flag & EXT4_EXT_DATA_VALID2)
>  			split_flag1 |= EXT4_EXT_DATA_VALID1;
> -		err = ext4_split_extent_at(handle, inode, ppath,
> +		path = ext4_split_extent_at(handle, inode, path,
>  				map->m_lblk + map->m_len, split_flag1, flags1);
> -		if (err)
> +		if (IS_ERR(path)) {
> +			err = PTR_ERR(path);
> +			*ppath = NULL;
>  			goto out;
> +		}
> +		*ppath = path;
>  	} else {
>  		allocated = ee_len - (map->m_lblk - ee_block);
>  	}
> @@ -3390,7 +3396,7 @@ static int ext4_split_extent(handle_t *handle,
>  	 * Update path is required because previous ext4_split_extent_at() may
>  	 * result in split of original leaf or extent zeroout.
>  	 */
> -	path = ext4_find_extent(inode, map->m_lblk, *ppath, flags);
> +	path = ext4_find_extent(inode, map->m_lblk, path, flags);
>  	if (IS_ERR(path)) {
>  		*ppath = NULL;
>  		return PTR_ERR(path);
> @@ -3412,13 +3418,17 @@ static int ext4_split_extent(handle_t *handle,
>  			split_flag1 |= split_flag & (EXT4_EXT_MAY_ZEROOUT |
>  						     EXT4_EXT_MARK_UNWRIT2);
>  		}
> -		err = ext4_split_extent_at(handle, inode, ppath,
> +		path = ext4_split_extent_at(handle, inode, path,
>  				map->m_lblk, split_flag1, flags);
> -		if (err)
> +		if (IS_ERR(path)) {
> +			err = PTR_ERR(path);
> +			*ppath = NULL;
>  			goto out;
> +		}
> +		*ppath = path;
>  	}
>  
> -	ext4_ext_show_leaf(inode, *ppath);
> +	ext4_ext_show_leaf(inode, path);
>  out:
>  	return err ? err : allocated;
>  }
> @@ -5596,6 +5606,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
>  	path = ext4_find_extent(inode, offset_lblk, NULL, 0);
>  	if (IS_ERR(path)) {
>  		up_write(&EXT4_I(inode)->i_data_sem);
> +		ret = PTR_ERR(path);
>  		goto out_stop;
>  	}
>  
> @@ -5614,22 +5625,21 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
>  			if (ext4_ext_is_unwritten(extent))
>  				split_flag = EXT4_EXT_MARK_UNWRIT1 |
>  					EXT4_EXT_MARK_UNWRIT2;
> -			ret = ext4_split_extent_at(handle, inode, &path,
> +			path = ext4_split_extent_at(handle, inode, path,
>  					offset_lblk, split_flag,
>  					EXT4_EX_NOCACHE |
>  					EXT4_GET_BLOCKS_PRE_IO |
>  					EXT4_GET_BLOCKS_METADATA_NOFAIL);
>  		}
>  
> -		ext4_free_ext_path(path);
> -		if (ret < 0) {
> +		if (IS_ERR(path)) {
>  			up_write(&EXT4_I(inode)->i_data_sem);
> +			ret = PTR_ERR(path);
>  			goto out_stop;
>  		}
> -	} else {
> -		ext4_free_ext_path(path);
>  	}
>  
> +	ext4_free_ext_path(path);
>  	ext4_es_remove_extent(inode, offset_lblk, EXT_MAX_BLOCKS - offset_lblk);
>  
>  	/*
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf()
  2024-08-02  7:34   ` Ojaswin Mujoo
@ 2024-08-02 13:07     ` Baokun Li
  0 siblings, 0 replies; 84+ messages in thread
From: Baokun Li @ 2024-08-02 13:07 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, Baokun Li

On 2024/8/2 15:34, Ojaswin Mujoo wrote:
> On Wed, Jul 10, 2024 at 12:06:44PM +0800, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> The use of path and ppath is now very confusing, so to make the code more
>> readable, pass path between functions uniformly, and get rid of ppath.
>>
>> To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
>> done here:
>>
>>   * Free the extents path when an error is encountered.
>>   * Its caller needs to update ppath if it uses ppath.
>>
>> No functional changes.
>>
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> Hi Baokun,
Hey Ojaswin,
> The changes look good to me, feel free to add:
>
> Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Thank you very much for your review!
>
> That being said, IIUC i think this patchset also fixes a potential UAF
> bug. Below is a sample trace with dummy values:
>
> ext4_ext_insert_extent
>    path = *ppath = 2000
>    ext4_ext_create_new_leaf(ppath)
>      path = *ppath = 2000
>      ext4_find_extent(path = 2000)
>        if (depth > path[0].p_maxdepth)
>              kfree(path = 2000);
>              path = NULL;
>        path = kcalloc() = 3000
>        ...
>        return path;
>    path = 3000
>    *ppath = 3000;
>    return;
> /* here path is still 2000 *, UAF! */
> eh = path[depth].p_hdr
>
> I'm not completely sure if we can hit (depth > path[0].p_maxdepth) in the
> above codepath but I think the flow is still a bit fragile. Maybe this
> should be fixed in a separate patch first. What do you think?
>
> Regards,
> ojaswin
Nice catch!

This is indeed a potential UAF issue, and while it seems hard to
trigger (depth > path[0].p_maxdepth), it does deserve a separate
patch, and I'll be adding a separate quick fix for this in the next version.

Regards,
Baokun


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at()
  2024-07-10  4:06 ` [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at() libaokun
  2024-07-25 11:14   ` Jan Kara
@ 2024-08-02 20:01   ` Ojaswin Mujoo
  2024-08-03  2:23     ` Baokun Li
  1 sibling, 1 reply; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02 20:01 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:47PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_force_split_extent_at(), the following is
> done here:
> 
>  * The ext4_find_extent() can update the extent path so it doesn't have to
>    allocate and free path repeatedly, thus reducing the consumption of
>    memory allocation and freeing in ext4_swap_extents().
> 
> No functional changes.

Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

One small comment below..

> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> ---
>  fs/ext4/extents.c | 117 ++++++++++++++++++++++++----------------------
>  1 file changed, 60 insertions(+), 57 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index c86b1bb7720f..0bd068ed324f 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c

.. snip ..

> @@ -5707,25 +5701,21 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  		int e1_len, e2_len, len;
>  		int split = 0;
>  
> -		path1 = ext4_find_extent(inode1, lblk1, NULL, EXT4_EX_NOCACHE);
> +		path1 = ext4_find_extent(inode1, lblk1, path1, EXT4_EX_NOCACHE);
>  		if (IS_ERR(path1)) {
>  			*erp = PTR_ERR(path1);
> -			path1 = NULL;
> -		finish:
> -			count = 0;
> -			goto repeat;
> +			goto errout;
>  		}
> -		path2 = ext4_find_extent(inode2, lblk2, NULL, EXT4_EX_NOCACHE);
> +		path2 = ext4_find_extent(inode2, lblk2, path2, EXT4_EX_NOCACHE);
>  		if (IS_ERR(path2)) {
>  			*erp = PTR_ERR(path2);
> -			path2 = NULL;
> -			goto finish;
> +			goto errout;
>  		}
>  		ex1 = path1[path1->p_depth].p_ext;
>  		ex2 = path2[path2->p_depth].p_ext;
>  		/* Do we have something to swap ? */
>  		if (unlikely(!ex2 || !ex1))
> -			goto finish;
> +			goto errout;
>  
>  		e1_blk = le32_to_cpu(ex1->ee_block);
>  		e2_blk = le32_to_cpu(ex2->ee_block);
> @@ -5747,7 +5737,7 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  				next2 = e2_blk;
>  			/* Do we have something to swap */
>  			if (next1 == EXT_MAX_BLOCKS || next2 == EXT_MAX_BLOCKS)
> -				goto finish;
> +				goto errout;
>  			/* Move to the rightest boundary */
>  			len = next1 - lblk1;
>  			if (len < next2 - lblk2)
> @@ -5757,28 +5747,32 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  			lblk1 += len;
>  			lblk2 += len;
>  			count -= len;
> -			goto repeat;
> +			continue;
>  		}
>  
>  		/* Prepare left boundary */
>  		if (e1_blk < lblk1) {
>  			split = 1;
> -			*erp = ext4_force_split_extent_at(handle, inode1,
> -						&path1, lblk1, 0);
> -			if (unlikely(*erp))
> -				goto finish;
> +			path1 = ext4_force_split_extent_at(handle, inode1,
> +							   path1, lblk1, 0);
> +			if (IS_ERR(path1)) {
> +				*erp = PTR_ERR(path1);
> +				goto errout;
> +			}
>  		}
>  		if (e2_blk < lblk2) {
>  			split = 1;
> -			*erp = ext4_force_split_extent_at(handle, inode2,
> -						&path2,  lblk2, 0);
> -			if (unlikely(*erp))
> -				goto finish;
> +			path2 = ext4_force_split_extent_at(handle, inode2,
> +							   path2, lblk2, 0);
> +			if (IS_ERR(path2)) {
> +				*erp = PTR_ERR(path2);
> +				goto errout;
> +			}
>  		}
>  		/* ext4_split_extent_at() may result in leaf extent split,
>  		 * path must to be revalidated. */
>  		if (split)
> -			goto repeat;
> +			continue;
>  
>  		/* Prepare right boundary */
>  		len = count;
> @@ -5789,30 +5783,34 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  
>  		if (len != e1_len) {
>  			split = 1;
> -			*erp = ext4_force_split_extent_at(handle, inode1,
> -						&path1, lblk1 + len, 0);
> -			if (unlikely(*erp))
> -				goto finish;
> +			path1 = ext4_force_split_extent_at(handle, inode1,
> +							path1, lblk1 + len, 0);
> +			if (IS_ERR(path1)) {
> +				*erp = PTR_ERR(path1);
> +				goto errout;
> +			}
>  		}
>  		if (len != e2_len) {
>  			split = 1;
> -			*erp = ext4_force_split_extent_at(handle, inode2,
> -						&path2, lblk2 + len, 0);
> -			if (*erp)
> -				goto finish;
> +			path2 = ext4_force_split_extent_at(handle, inode2,
> +							path2, lblk2 + len, 0);
> +			if (IS_ERR(path2)) {
> +				*erp = PTR_ERR(path2);
> +				goto errout;
> +			}
>  		}
>  		/* ext4_split_extent_at() may result in leaf extent split,
>  		 * path must to be revalidated. */
>  		if (split)
> -			goto repeat;
> +			continue;
>  
>  		BUG_ON(e2_len != e1_len);
>  		*erp = ext4_ext_get_access(handle, inode1, path1 + path1->p_depth);
>  		if (unlikely(*erp))
> -			goto finish;
> +			goto errout;
>  		*erp = ext4_ext_get_access(handle, inode2, path2 + path2->p_depth);
>  		if (unlikely(*erp))
> -			goto finish;
> +			goto errout;
>  
>  		/* Both extents are fully inside boundaries. Swap it now */
>  		tmp_ex = *ex1;
> @@ -5830,7 +5828,7 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  		*erp = ext4_ext_dirty(handle, inode2, path2 +
>  				      path2->p_depth);
>  		if (unlikely(*erp))
> -			goto finish;
> +			goto errout;
>  		*erp = ext4_ext_dirty(handle, inode1, path1 +
>  				      path1->p_depth);
>  		/*
> @@ -5840,17 +5838,17 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
>  		 * aborted anyway.
>  		 */
>  		if (unlikely(*erp))
> -			goto finish;
> +			goto errout;
> +
>  		lblk1 += len;
>  		lblk2 += len;
>  		replaced_count += len;
>  		count -= len;
> -
> -	repeat:
> -		ext4_free_ext_path(path1);
> -		ext4_free_ext_path(path2);
> -		path1 = path2 = NULL;
>  	}
> +
> +errout:
> +	ext4_free_ext_path(path1);
> +	ext4_free_ext_path(path2);
>  	return replaced_count;
>  }
>  

In ext4_swap_extents, maybe we should keep the refactoring to a separate
patch than the changes needed to get rid of ppath in
ext4_force_split_extent_at(), the commits would look a bit cleaner and
easier to read that way. I don't feel too strongly about it tho and 
I'll let you take a call.

Regards,
ojaswin

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 14/20] ext4: get rid of ppath in ext4_split_extent()
  2024-07-10  4:06 ` [PATCH 14/20] ext4: get rid of ppath in ext4_split_extent() libaokun
  2024-07-25 12:07   ` Jan Kara
@ 2024-08-02 20:17   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02 20:17 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:48PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_split_extent(), the following is done here:
> 
>  * The 'allocated' is changed from passing a value to passing an address.
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin
> ---
>  fs/ext4/extents.c | 97 ++++++++++++++++++++++++-----------------------
>  1 file changed, 50 insertions(+), 47 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 0bd068ed324f..2a4f6c89858c 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3345,21 +3345,18 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
>   *   c> Splits in three extents: Somone is splitting in middle of the extent
>   *
>   */
> -static int ext4_split_extent(handle_t *handle,
> -			      struct inode *inode,
> -			      struct ext4_ext_path **ppath,
> -			      struct ext4_map_blocks *map,
> -			      int split_flag,
> -			      int flags)
> +static struct ext4_ext_path *ext4_split_extent(handle_t *handle,
> +					       struct inode *inode,
> +					       struct ext4_ext_path *path,
> +					       struct ext4_map_blocks *map,
> +					       int split_flag, int flags,
> +					       unsigned int *allocated)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	ext4_lblk_t ee_block;
>  	struct ext4_extent *ex;
>  	unsigned int ee_len, depth;
> -	int err = 0;
>  	int unwritten;
>  	int split_flag1, flags1;
> -	int allocated = map->m_len;
>  
>  	depth = ext_depth(inode);
>  	ex = path[depth].p_ext;
> @@ -3377,33 +3374,25 @@ static int ext4_split_extent(handle_t *handle,
>  			split_flag1 |= EXT4_EXT_DATA_VALID1;
>  		path = ext4_split_extent_at(handle, inode, path,
>  				map->m_lblk + map->m_len, split_flag1, flags1);
> -		if (IS_ERR(path)) {
> -			err = PTR_ERR(path);
> -			*ppath = NULL;
> -			goto out;
> +		if (IS_ERR(path))
> +			return path;
> +		/*
> +		 * Update path is required because previous ext4_split_extent_at
> +		 * may result in split of original leaf or extent zeroout.
> +		 */
> +		path = ext4_find_extent(inode, map->m_lblk, path, flags);
> +		if (IS_ERR(path))
> +			return path;
> +		depth = ext_depth(inode);
> +		ex = path[depth].p_ext;
> +		if (!ex) {
> +			EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
> +					(unsigned long) map->m_lblk);
> +			ext4_free_ext_path(path);
> +			return ERR_PTR(-EFSCORRUPTED);
>  		}
> -		*ppath = path;
> -	} 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.
> -	 */
> -	path = ext4_find_extent(inode, map->m_lblk, path, flags);
> -	if (IS_ERR(path)) {
> -		*ppath = NULL;
> -		return PTR_ERR(path);
> -	}
> -	*ppath = path;
> -	depth = ext_depth(inode);
> -	ex = path[depth].p_ext;
> -	if (!ex) {
> -		EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
> -				 (unsigned long) map->m_lblk);
> -		return -EFSCORRUPTED;
> +		unwritten = ext4_ext_is_unwritten(ex);
>  	}
> -	unwritten = ext4_ext_is_unwritten(ex);
>  
>  	if (map->m_lblk >= ee_block) {
>  		split_flag1 = split_flag & EXT4_EXT_DATA_VALID2;
> @@ -3414,17 +3403,18 @@ static int ext4_split_extent(handle_t *handle,
>  		}
>  		path = ext4_split_extent_at(handle, inode, path,
>  				map->m_lblk, split_flag1, flags);
> -		if (IS_ERR(path)) {
> -			err = PTR_ERR(path);
> -			*ppath = NULL;
> -			goto out;
> -		}
> -		*ppath = path;
> +		if (IS_ERR(path))
> +			return path;
>  	}
>  
> +	if (allocated) {
> +		if (map->m_lblk + map->m_len > ee_block + ee_len)
> +			*allocated = ee_len - (map->m_lblk - ee_block);
> +		else
> +			*allocated = map->m_len;
> +	}
>  	ext4_ext_show_leaf(inode, path);
> -out:
> -	return err ? err : allocated;
> +	return path;
>  }
>  
>  /*
> @@ -3669,10 +3659,15 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  	}
>  
>  fallback:
> -	err = ext4_split_extent(handle, inode, ppath, &split_map, split_flag,
> -				flags);
> -	if (err > 0)
> -		err = 0;
> +	path = ext4_split_extent(handle, inode, path, &split_map, split_flag,
> +				 flags, NULL);
> +	if (IS_ERR(path)) {
> +		err = PTR_ERR(path);
> +		*ppath = NULL;
> +		goto out;
> +	}
> +	err = 0;
> +	*ppath = path;
>  out:
>  	/* If we have gotten a failure, don't zero out status tree */
>  	if (!err) {
> @@ -3718,6 +3713,7 @@ static int ext4_split_convert_extents(handle_t *handle,
>  	struct ext4_extent *ex;
>  	unsigned int ee_len;
>  	int split_flag = 0, depth;
> +	unsigned int allocated = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u\n",
>  		  (unsigned long long)map->m_lblk, map->m_len);
> @@ -3745,7 +3741,14 @@ static int ext4_split_convert_extents(handle_t *handle,
>  		split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2);
>  	}
>  	flags |= EXT4_GET_BLOCKS_PRE_IO;
> -	return ext4_split_extent(handle, inode, ppath, map, split_flag, flags);
> +	path = ext4_split_extent(handle, inode, path, map, split_flag, flags,
> +				 &allocated);
> +	if (IS_ERR(path)) {
> +		*ppath = NULL;
> +		return PTR_ERR(path);
> +	}
> +	*ppath = path;
> +	return allocated;
>  }
>  
>  static int ext4_convert_unwritten_extents_endio(handle_t *handle,
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 15/20] ext4: get rid of ppath in ext4_split_convert_extents()
  2024-07-10  4:06 ` [PATCH 15/20] ext4: get rid of ppath in ext4_split_convert_extents() libaokun
  2024-07-25 12:12   ` Jan Kara
@ 2024-08-02 20:26   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02 20:26 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:49PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_split_convert_extents(), the following is
> done here:
> 
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin

> ---
>  fs/ext4/extents.c | 65 ++++++++++++++++++++++++-----------------------
>  1 file changed, 33 insertions(+), 32 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 2a4f6c89858c..a41cbb8c4475 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3699,21 +3699,21 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>   * being filled will be convert to initialized by the end_io callback function
>   * via ext4_convert_unwritten_extents().
>   *
> - * Returns the size of unwritten extent to be written on success.
> + * The size of unwritten extent to be written is passed to the caller via the
> + * allocated pointer. Return an extent path pointer on success, or an error
> + * pointer on failure.
>   */
> -static int ext4_split_convert_extents(handle_t *handle,
> +static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle,
>  					struct inode *inode,
>  					struct ext4_map_blocks *map,
> -					struct ext4_ext_path **ppath,
> -					int flags)
> +					struct ext4_ext_path *path,
> +					int flags, unsigned int *allocated)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	ext4_lblk_t eof_block;
>  	ext4_lblk_t ee_block;
>  	struct ext4_extent *ex;
>  	unsigned int ee_len;
>  	int split_flag = 0, depth;
> -	unsigned int allocated = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u\n",
>  		  (unsigned long long)map->m_lblk, map->m_len);
> @@ -3741,14 +3741,8 @@ static int ext4_split_convert_extents(handle_t *handle,
>  		split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2);
>  	}
>  	flags |= EXT4_GET_BLOCKS_PRE_IO;
> -	path = ext4_split_extent(handle, inode, path, map, split_flag, flags,
> -				 &allocated);
> -	if (IS_ERR(path)) {
> -		*ppath = NULL;
> -		return PTR_ERR(path);
> -	}
> -	*ppath = path;
> -	return allocated;
> +	return ext4_split_extent(handle, inode, path, map, split_flag, flags,
> +				 allocated);
>  }
>  
>  static int ext4_convert_unwritten_extents_endio(handle_t *handle,
> @@ -3784,11 +3778,14 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
>  			     inode->i_ino, (unsigned long long)ee_block, ee_len,
>  			     (unsigned long long)map->m_lblk, map->m_len);
>  #endif
> -		err = ext4_split_convert_extents(handle, inode, map, ppath,
> -						 EXT4_GET_BLOCKS_CONVERT);
> -		if (err < 0)
> -			return err;
> -		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
> +		path = ext4_split_convert_extents(handle, inode, map, path,
> +						EXT4_GET_BLOCKS_CONVERT, NULL);
> +		if (IS_ERR(path)) {
> +			*ppath = NULL;
> +			return PTR_ERR(path);
> +		}
> +
> +		path = ext4_find_extent(inode, map->m_lblk, path, 0);
>  		if (IS_ERR(path)) {
>  			*ppath = NULL;
>  			return PTR_ERR(path);
> @@ -3845,11 +3842,14 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  		  (unsigned long long)ee_block, ee_len);
>  
>  	if (ee_block != map->m_lblk || ee_len > map->m_len) {
> -		err = ext4_split_convert_extents(handle, inode, map, ppath,
> -				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN);
> -		if (err < 0)
> -			return err;
> -		path = ext4_find_extent(inode, map->m_lblk, *ppath, 0);
> +		path = ext4_split_convert_extents(handle, inode, map, path,
> +				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, NULL);
> +		if (IS_ERR(path)) {
> +			*ppath = NULL;
> +			return PTR_ERR(path);
> +		}
> +
> +		path = ext4_find_extent(inode, map->m_lblk, path, 0);
>  		if (IS_ERR(path)) {
>  			*ppath = NULL;
>  			return PTR_ERR(path);
> @@ -3915,19 +3915,20 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  
>  	/* get_block() before submitting IO, split the extent */
>  	if (flags & EXT4_GET_BLOCKS_PRE_IO) {
> -		ret = ext4_split_convert_extents(handle, inode, map, ppath,
> -					 flags | EXT4_GET_BLOCKS_CONVERT);
> -		if (ret < 0) {
> -			err = ret;
> +		*ppath = ext4_split_convert_extents(handle, inode, map, *ppath,
> +				flags | EXT4_GET_BLOCKS_CONVERT, &allocated);
> +		if (IS_ERR(*ppath)) {
> +			err = PTR_ERR(*ppath);
> +			*ppath = NULL;
>  			goto out2;
>  		}
>  		/*
> -		 * shouldn't get a 0 return when splitting an extent unless
> +		 * shouldn't get a 0 allocated when splitting an extent unless
>  		 * m_len is 0 (bug) or extent has been corrupted
>  		 */
> -		if (unlikely(ret == 0)) {
> +		if (unlikely(allocated == 0)) {
>  			EXT4_ERROR_INODE(inode,
> -					 "unexpected ret == 0, m_len = %u",
> +					 "unexpected allocated == 0, m_len = %u",
>  					 map->m_len);
>  			err = -EFSCORRUPTED;
>  			goto out2;
> @@ -3988,9 +3989,9 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  		err = -EFSCORRUPTED;
>  		goto out2;
>  	}
> +	allocated = ret;
>  
>  out:
> -	allocated = ret;
>  	map->m_flags |= EXT4_MAP_NEW;
>  map_out:
>  	map->m_flags |= EXT4_MAP_MAPPED;
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 16/20] ext4: get rid of ppath in ext4_convert_unwritten_extents_endio()
  2024-07-10  4:06 ` [PATCH 16/20] ext4: get rid of ppath in ext4_convert_unwritten_extents_endio() libaokun
  2024-07-25 12:14   ` Jan Kara
@ 2024-08-02 20:28   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02 20:28 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:50PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_convert_unwritten_extents_endio(), the
> following is done here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin

> ---
>  fs/ext4/extents.c | 43 +++++++++++++++++++++++--------------------
>  1 file changed, 23 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index a41cbb8c4475..b7f443f98e9d 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3745,12 +3745,11 @@ static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle,
>  				 allocated);
>  }
>  
> -static int ext4_convert_unwritten_extents_endio(handle_t *handle,
> -						struct inode *inode,
> -						struct ext4_map_blocks *map,
> -						struct ext4_ext_path **ppath)
> +static struct ext4_ext_path *
> +ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
> +				     struct ext4_map_blocks *map,
> +				     struct ext4_ext_path *path)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	struct ext4_extent *ex;
>  	ext4_lblk_t ee_block;
>  	unsigned int ee_len;
> @@ -3780,24 +3779,19 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
>  #endif
>  		path = ext4_split_convert_extents(handle, inode, map, path,
>  						EXT4_GET_BLOCKS_CONVERT, NULL);
> -		if (IS_ERR(path)) {
> -			*ppath = NULL;
> -			return PTR_ERR(path);
> -		}
> +		if (IS_ERR(path))
> +			return path;
>  
>  		path = ext4_find_extent(inode, map->m_lblk, path, 0);
> -		if (IS_ERR(path)) {
> -			*ppath = NULL;
> -			return PTR_ERR(path);
> -		}
> -		*ppath = path;
> +		if (IS_ERR(path))
> +			return path;
>  		depth = ext_depth(inode);
>  		ex = path[depth].p_ext;
>  	}
>  
>  	err = ext4_ext_get_access(handle, inode, path + depth);
>  	if (err)
> -		goto out;
> +		goto errout;
>  	/* first mark the extent as initialized */
>  	ext4_ext_mark_initialized(ex);
>  
> @@ -3808,9 +3802,15 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
>  
>  	/* Mark modified extent as dirty */
>  	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
> -out:
> +	if (err)
> +		goto errout;
> +
>  	ext4_ext_show_leaf(inode, path);
> -	return err;
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  static int
> @@ -3938,10 +3938,13 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	}
>  	/* IO end_io complete, convert the filled extent to written */
>  	if (flags & EXT4_GET_BLOCKS_CONVERT) {
> -		err = ext4_convert_unwritten_extents_endio(handle, inode, map,
> -							   ppath);
> -		if (err < 0)
> +		*ppath = ext4_convert_unwritten_extents_endio(handle, inode,
> +							      map, *ppath);
> +		if (IS_ERR(*ppath)) {
> +			err = PTR_ERR(*ppath);
> +			*ppath = NULL;
>  			goto out2;
> +		}
>  		ext4_update_inode_fsync_trans(handle, inode, 1);
>  		goto map_out;
>  	}
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 17/20] ext4: get rid of ppath in ext4_ext_convert_to_initialized()
  2024-07-10  4:06 ` [PATCH 17/20] ext4: get rid of ppath in ext4_ext_convert_to_initialized() libaokun
  2024-07-25 12:18   ` Jan Kara
@ 2024-08-02 20:38   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02 20:38 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:51PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_convert_to_initialized(), the following
> is done here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
>  * The 'allocated' is changed from passing a value to passing an address.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin

> ---
>  fs/ext4/extents.c | 73 +++++++++++++++++++++++------------------------
>  1 file changed, 35 insertions(+), 38 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index b7f443f98e9d..59e80926fe3a 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3437,13 +3437,11 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle,
>   *    that are allocated and initialized.
>   *    It is guaranteed to be >= map->m_len.
>   */
> -static int ext4_ext_convert_to_initialized(handle_t *handle,
> -					   struct inode *inode,
> -					   struct ext4_map_blocks *map,
> -					   struct ext4_ext_path **ppath,
> -					   int flags)
> +static struct ext4_ext_path *
> +ext4_ext_convert_to_initialized(handle_t *handle, struct inode *inode,
> +			struct ext4_map_blocks *map, struct ext4_ext_path *path,
> +			int flags, unsigned int *allocated)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	struct ext4_sb_info *sbi;
>  	struct ext4_extent_header *eh;
>  	struct ext4_map_blocks split_map;
> @@ -3453,7 +3451,6 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  	unsigned int ee_len, depth, map_len = map->m_len;
>  	int err = 0;
>  	int split_flag = EXT4_EXT_DATA_VALID2;
> -	int allocated = 0;
>  	unsigned int max_zeroout = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u\n",
> @@ -3494,6 +3491,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  	 *  - L2: we only attempt to merge with an extent stored in the
>  	 *    same extent tree node.
>  	 */
> +	*allocated = 0;
>  	if ((map->m_lblk == ee_block) &&
>  		/* See if we can merge left */
>  		(map_len < ee_len) &&		/*L1*/
> @@ -3523,7 +3521,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  			(prev_len < (EXT_INIT_MAX_LEN - map_len))) {	/*C4*/
>  			err = ext4_ext_get_access(handle, inode, path + depth);
>  			if (err)
> -				goto out;
> +				goto errout;
>  
>  			trace_ext4_ext_convert_to_initialized_fastpath(inode,
>  				map, ex, abut_ex);
> @@ -3538,7 +3536,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  			abut_ex->ee_len = cpu_to_le16(prev_len + map_len);
>  
>  			/* Result: number of initialized blocks past m_lblk */
> -			allocated = map_len;
> +			*allocated = map_len;
>  		}
>  	} else if (((map->m_lblk + map_len) == (ee_block + ee_len)) &&
>  		   (map_len < ee_len) &&	/*L1*/
> @@ -3569,7 +3567,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  		    (next_len < (EXT_INIT_MAX_LEN - map_len))) {	/*C4*/
>  			err = ext4_ext_get_access(handle, inode, path + depth);
>  			if (err)
> -				goto out;
> +				goto errout;
>  
>  			trace_ext4_ext_convert_to_initialized_fastpath(inode,
>  				map, ex, abut_ex);
> @@ -3584,18 +3582,20 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  			abut_ex->ee_len = cpu_to_le16(next_len + map_len);
>  
>  			/* Result: number of initialized blocks past m_lblk */
> -			allocated = map_len;
> +			*allocated = map_len;
>  		}
>  	}
> -	if (allocated) {
> +	if (*allocated) {
>  		/* Mark the block containing both extents as dirty */
>  		err = ext4_ext_dirty(handle, inode, path + depth);
>  
>  		/* Update path to point to the right extent */
>  		path[depth].p_ext = abut_ex;
> +		if (err)
> +			goto errout;
>  		goto out;
>  	} else
> -		allocated = ee_len - (map->m_lblk - ee_block);
> +		*allocated = ee_len - (map->m_lblk - ee_block);
>  
>  	WARN_ON(map->m_lblk < ee_block);
>  	/*
> @@ -3622,21 +3622,21 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  	split_map.m_lblk = map->m_lblk;
>  	split_map.m_len = map->m_len;
>  
> -	if (max_zeroout && (allocated > split_map.m_len)) {
> -		if (allocated <= max_zeroout) {
> +	if (max_zeroout && (*allocated > split_map.m_len)) {
> +		if (*allocated <= max_zeroout) {
>  			/* case 3 or 5 */
>  			zero_ex1.ee_block =
>  				 cpu_to_le32(split_map.m_lblk +
>  					     split_map.m_len);
>  			zero_ex1.ee_len =
> -				cpu_to_le16(allocated - split_map.m_len);
> +				cpu_to_le16(*allocated - split_map.m_len);
>  			ext4_ext_store_pblock(&zero_ex1,
>  				ext4_ext_pblock(ex) + split_map.m_lblk +
>  				split_map.m_len - ee_block);
>  			err = ext4_ext_zeroout(inode, &zero_ex1);
>  			if (err)
>  				goto fallback;
> -			split_map.m_len = allocated;
> +			split_map.m_len = *allocated;
>  		}
>  		if (split_map.m_lblk - ee_block + split_map.m_len <
>  								max_zeroout) {
> @@ -3654,27 +3654,24 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
>  
>  			split_map.m_len += split_map.m_lblk - ee_block;
>  			split_map.m_lblk = ee_block;
> -			allocated = map->m_len;
> +			*allocated = map->m_len;
>  		}
>  	}
>  
>  fallback:
>  	path = ext4_split_extent(handle, inode, path, &split_map, split_flag,
>  				 flags, NULL);
> -	if (IS_ERR(path)) {
> -		err = PTR_ERR(path);
> -		*ppath = NULL;
> -		goto out;
> -	}
> -	err = 0;
> -	*ppath = path;
> +	if (IS_ERR(path))
> +		return path;
>  out:
>  	/* If we have gotten a failure, don't zero out status tree */
> -	if (!err) {
> -		ext4_zeroout_es(inode, &zero_ex1);
> -		ext4_zeroout_es(inode, &zero_ex2);
> -	}
> -	return err ? err : allocated;
> +	ext4_zeroout_es(inode, &zero_ex1);
> +	ext4_zeroout_es(inode, &zero_ex2);
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  /*
> @@ -3896,7 +3893,6 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  			struct ext4_ext_path **ppath, int flags,
>  			unsigned int allocated, ext4_fsblk_t newblock)
>  {
> -	int ret = 0;
>  	int err = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
> @@ -3976,23 +3972,24 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	 * For buffered writes, at writepage time, etc.  Convert a
>  	 * discovered unwritten extent to written.
>  	 */
> -	ret = ext4_ext_convert_to_initialized(handle, inode, map, ppath, flags);
> -	if (ret < 0) {
> -		err = ret;
> +	*ppath = ext4_ext_convert_to_initialized(handle, inode, map, *ppath,
> +						 flags, &allocated);
> +	if (IS_ERR(*ppath)) {
> +		err = PTR_ERR(*ppath);
> +		*ppath = NULL;
>  		goto out2;
>  	}
>  	ext4_update_inode_fsync_trans(handle, inode, 1);
>  	/*
> -	 * shouldn't get a 0 return when converting an unwritten extent
> +	 * shouldn't get a 0 allocated when converting an unwritten extent
>  	 * unless m_len is 0 (bug) or extent has been corrupted
>  	 */
> -	if (unlikely(ret == 0)) {
> -		EXT4_ERROR_INODE(inode, "unexpected ret == 0, m_len = %u",
> +	if (unlikely(allocated == 0)) {
> +		EXT4_ERROR_INODE(inode, "unexpected allocated == 0, m_len = %u",
>  				 map->m_len);
>  		err = -EFSCORRUPTED;
>  		goto out2;
>  	}
> -	allocated = ret;
>  
>  out:
>  	map->m_flags |= EXT4_MAP_NEW;
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 18/20] ext4: get rid of ppath in ext4_ext_handle_unwritten_extents()
  2024-07-10  4:06 ` [PATCH 18/20] ext4: get rid of ppath in ext4_ext_handle_unwritten_extents() libaokun
  2024-07-25 12:22   ` Jan Kara
@ 2024-08-02 20:44   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02 20:44 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:52PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_handle_unwritten_extents(), the
> following is done here:
> 
>  * Free the extents path when an error is encountered.
>  * The 'allocated' is changed from passing a value to passing an address.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin

> ---
>  fs/ext4/extents.c | 82 +++++++++++++++++++++--------------------------
>  1 file changed, 37 insertions(+), 45 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 59e80926fe3a..badadcd64e66 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3887,18 +3887,18 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  	return 0;
>  }
>  
> -static int
> +static struct ext4_ext_path *
>  ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  			struct ext4_map_blocks *map,
> -			struct ext4_ext_path **ppath, int flags,
> -			unsigned int allocated, ext4_fsblk_t newblock)
> +			struct ext4_ext_path *path, int flags,
> +			unsigned int *allocated, ext4_fsblk_t newblock)
>  {
>  	int err = 0;
>  
>  	ext_debug(inode, "logical block %llu, max_blocks %u, flags 0x%x, allocated %u\n",
>  		  (unsigned long long)map->m_lblk, map->m_len, flags,
> -		  allocated);
> -	ext4_ext_show_leaf(inode, *ppath);
> +		  *allocated);
> +	ext4_ext_show_leaf(inode, path);
>  
>  	/*
>  	 * When writing into unwritten space, we should not fail to
> @@ -3907,40 +3907,34 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL;
>  
>  	trace_ext4_ext_handle_unwritten_extents(inode, map, flags,
> -						    allocated, newblock);
> +						*allocated, newblock);
>  
>  	/* get_block() before submitting IO, split the extent */
>  	if (flags & EXT4_GET_BLOCKS_PRE_IO) {
> -		*ppath = ext4_split_convert_extents(handle, inode, map, *ppath,
> -				flags | EXT4_GET_BLOCKS_CONVERT, &allocated);
> -		if (IS_ERR(*ppath)) {
> -			err = PTR_ERR(*ppath);
> -			*ppath = NULL;
> -			goto out2;
> -		}
> +		path = ext4_split_convert_extents(handle, inode, map, path,
> +				flags | EXT4_GET_BLOCKS_CONVERT, allocated);
> +		if (IS_ERR(path))
> +			return path;
>  		/*
>  		 * shouldn't get a 0 allocated when splitting an extent unless
>  		 * m_len is 0 (bug) or extent has been corrupted
>  		 */
> -		if (unlikely(allocated == 0)) {
> +		if (unlikely(*allocated == 0)) {
>  			EXT4_ERROR_INODE(inode,
>  					 "unexpected allocated == 0, m_len = %u",
>  					 map->m_len);
>  			err = -EFSCORRUPTED;
> -			goto out2;
> +			goto errout;
>  		}
>  		map->m_flags |= EXT4_MAP_UNWRITTEN;
>  		goto out;
>  	}
>  	/* IO end_io complete, convert the filled extent to written */
>  	if (flags & EXT4_GET_BLOCKS_CONVERT) {
> -		*ppath = ext4_convert_unwritten_extents_endio(handle, inode,
> -							      map, *ppath);
> -		if (IS_ERR(*ppath)) {
> -			err = PTR_ERR(*ppath);
> -			*ppath = NULL;
> -			goto out2;
> -		}
> +		path = ext4_convert_unwritten_extents_endio(handle, inode,
> +							    map, path);
> +		if (IS_ERR(path))
> +			return path;
>  		ext4_update_inode_fsync_trans(handle, inode, 1);
>  		goto map_out;
>  	}
> @@ -3972,23 +3966,20 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	 * For buffered writes, at writepage time, etc.  Convert a
>  	 * discovered unwritten extent to written.
>  	 */
> -	*ppath = ext4_ext_convert_to_initialized(handle, inode, map, *ppath,
> -						 flags, &allocated);
> -	if (IS_ERR(*ppath)) {
> -		err = PTR_ERR(*ppath);
> -		*ppath = NULL;
> -		goto out2;
> -	}
> +	path = ext4_ext_convert_to_initialized(handle, inode, map, path,
> +					       flags, allocated);
> +	if (IS_ERR(path))
> +		return path;
>  	ext4_update_inode_fsync_trans(handle, inode, 1);
>  	/*
>  	 * shouldn't get a 0 allocated when converting an unwritten extent
>  	 * unless m_len is 0 (bug) or extent has been corrupted
>  	 */
> -	if (unlikely(allocated == 0)) {
> +	if (unlikely(*allocated == 0)) {
>  		EXT4_ERROR_INODE(inode, "unexpected allocated == 0, m_len = %u",
>  				 map->m_len);
>  		err = -EFSCORRUPTED;
> -		goto out2;
> +		goto errout;
>  	}
>  
>  out:
> @@ -3997,12 +3988,15 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
>  	map->m_flags |= EXT4_MAP_MAPPED;
>  out1:
>  	map->m_pblk = newblock;
> -	if (allocated > map->m_len)
> -		allocated = map->m_len;
> -	map->m_len = allocated;
> -	ext4_ext_show_leaf(inode, *ppath);
> -out2:
> -	return err ? err : allocated;
> +	if (*allocated > map->m_len)
> +		*allocated = map->m_len;
> +	map->m_len = *allocated;
> +	ext4_ext_show_leaf(inode, path);
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  /*
> @@ -4199,7 +4193,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  	struct ext4_extent newex, *ex, ex2;
>  	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
>  	ext4_fsblk_t newblock = 0, pblk;
> -	int err = 0, depth, ret;
> +	int err = 0, depth;
>  	unsigned int allocated = 0, offset = 0;
>  	unsigned int allocated_clusters = 0;
>  	struct ext4_allocation_request ar;
> @@ -4273,13 +4267,11 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  				goto out;
>  			}
>  
> -			ret = ext4_ext_handle_unwritten_extents(
> -				handle, inode, map, &path, flags,
> -				allocated, newblock);
> -			if (ret < 0)
> -				err = ret;
> -			else
> -				allocated = ret;
> +			path = ext4_ext_handle_unwritten_extents(
> +				handle, inode, map, path, flags,
> +				&allocated, newblock);
> +			if (IS_ERR(path))
> +				err = PTR_ERR(path);
>  			goto out;
>  		}
>  	}
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 19/20] ext4: get rid of ppath in convert_initialized_extent()
  2024-07-10  4:06 ` [PATCH 19/20] ext4: get rid of ppath in convert_initialized_extent() libaokun
  2024-07-25 12:23   ` Jan Kara
@ 2024-08-02 20:46   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02 20:46 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:53PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in convert_initialized_extent(), the following is
> done here:
> 
>  * Free the extents path when an error is encountered.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin

> ---
>  fs/ext4/extents.c | 37 +++++++++++++++++++------------------
>  1 file changed, 19 insertions(+), 18 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index badadcd64e66..737432bb316e 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3810,13 +3810,12 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
>  	return ERR_PTR(err);
>  }
>  
> -static int
> +static struct ext4_ext_path *
>  convert_initialized_extent(handle_t *handle, struct inode *inode,
>  			   struct ext4_map_blocks *map,
> -			   struct ext4_ext_path **ppath,
> +			   struct ext4_ext_path *path,
>  			   unsigned int *allocated)
>  {
> -	struct ext4_ext_path *path = *ppath;
>  	struct ext4_extent *ex;
>  	ext4_lblk_t ee_block;
>  	unsigned int ee_len;
> @@ -3841,29 +3840,25 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  	if (ee_block != map->m_lblk || ee_len > map->m_len) {
>  		path = ext4_split_convert_extents(handle, inode, map, path,
>  				EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, NULL);
> -		if (IS_ERR(path)) {
> -			*ppath = NULL;
> -			return PTR_ERR(path);
> -		}
> +		if (IS_ERR(path))
> +			return path;
>  
>  		path = ext4_find_extent(inode, map->m_lblk, path, 0);
> -		if (IS_ERR(path)) {
> -			*ppath = NULL;
> -			return PTR_ERR(path);
> -		}
> -		*ppath = path;
> +		if (IS_ERR(path))
> +			return path;
>  		depth = ext_depth(inode);
>  		ex = path[depth].p_ext;
>  		if (!ex) {
>  			EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
>  					 (unsigned long) map->m_lblk);
> -			return -EFSCORRUPTED;
> +			err = -EFSCORRUPTED;
> +			goto errout;
>  		}
>  	}
>  
>  	err = ext4_ext_get_access(handle, inode, path + depth);
>  	if (err)
> -		return err;
> +		goto errout;
>  	/* first mark the extent as unwritten */
>  	ext4_ext_mark_unwritten(ex);
>  
> @@ -3875,7 +3870,7 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  	/* Mark modified extent as dirty */
>  	err = ext4_ext_dirty(handle, inode, path + path->p_depth);
>  	if (err)
> -		return err;
> +		goto errout;
>  	ext4_ext_show_leaf(inode, path);
>  
>  	ext4_update_inode_fsync_trans(handle, inode, 1);
> @@ -3884,7 +3879,11 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
>  	if (*allocated > map->m_len)
>  		*allocated = map->m_len;
>  	map->m_len = *allocated;
> -	return 0;
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  static struct ext4_ext_path *
> @@ -4254,8 +4253,10 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
>  			 */
>  			if ((!ext4_ext_is_unwritten(ex)) &&
>  			    (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN)) {
> -				err = convert_initialized_extent(handle,
> -					inode, map, &path, &allocated);
> +				path = convert_initialized_extent(handle,
> +					inode, map, path, &allocated);
> +				if (IS_ERR(path))
> +					err = PTR_ERR(path);
>  				goto out;
>  			} else if (!ext4_ext_is_unwritten(ex)) {
>  				map->m_flags |= EXT4_MAP_MAPPED;
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 20/20] ext4: avoid unnecessary extent path frees and allocations
  2024-07-10  4:06 ` [PATCH 20/20] ext4: avoid unnecessary extent path frees and allocations libaokun
  2024-07-25 12:31   ` Jan Kara
@ 2024-08-02 20:58   ` Ojaswin Mujoo
  1 sibling, 0 replies; 84+ messages in thread
From: Ojaswin Mujoo @ 2024-08-02 20:58 UTC (permalink / raw)
  To: libaokun
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li

On Wed, Jul 10, 2024 at 12:06:54PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The ext4_find_extent() can update the extent path so that it does not have
> to allocate and free the path repeatedly, thus reducing the consumption of
> memory allocation and freeing in the following functions:
> 
>     ext4_ext_clear_bb
>     ext4_ext_replay_set_iblocks
>     ext4_fc_replay_add_range
>     ext4_fc_set_bitmaps_and_counters
> 
> No functional changes. Note that ext4_find_extent() does not support error
> pointers, so in this case set path to NULL first.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Looks good Baokun, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

Regards,
ojaswin

> ---
>  fs/ext4/extents.c     | 51 +++++++++++++++++++------------------------
>  fs/ext4/fast_commit.c | 11 ++++++----
>  2 files changed, 29 insertions(+), 33 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 737432bb316e..5ff92cd50dc8 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -6068,12 +6068,9 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
>  	if (IS_ERR(path))
>  		return PTR_ERR(path);
>  	ex = path[path->p_depth].p_ext;
> -	if (!ex) {
> -		ext4_free_ext_path(path);
> +	if (!ex)
>  		goto out;
> -	}
>  	end = le32_to_cpu(ex->ee_block) + ext4_ext_get_actual_len(ex);
> -	ext4_free_ext_path(path);
>  
>  	/* Count the number of data blocks */
>  	cur = 0;
> @@ -6099,32 +6096,28 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
>  	ret = skip_hole(inode, &cur);
>  	if (ret < 0)
>  		goto out;
> -	path = ext4_find_extent(inode, cur, NULL, 0);
> +	path = ext4_find_extent(inode, cur, path, 0);
>  	if (IS_ERR(path))
>  		goto out;
>  	numblks += path->p_depth;
> -	ext4_free_ext_path(path);
>  	while (cur < end) {
> -		path = ext4_find_extent(inode, cur, NULL, 0);
> +		path = ext4_find_extent(inode, cur, path, 0);
>  		if (IS_ERR(path))
>  			break;
>  		ex = path[path->p_depth].p_ext;
> -		if (!ex) {
> -			ext4_free_ext_path(path);
> -			return 0;
> -		}
> +		if (!ex)
> +			goto cleanup;
> +
>  		cur = max(cur + 1, le32_to_cpu(ex->ee_block) +
>  					ext4_ext_get_actual_len(ex));
>  		ret = skip_hole(inode, &cur);
> -		if (ret < 0) {
> -			ext4_free_ext_path(path);
> +		if (ret < 0)
>  			break;
> -		}
> -		path2 = ext4_find_extent(inode, cur, NULL, 0);
> -		if (IS_ERR(path2)) {
> -			ext4_free_ext_path(path);
> +
> +		path2 = ext4_find_extent(inode, cur, path2, 0);
> +		if (IS_ERR(path2))
>  			break;
> -		}
> +
>  		for (i = 0; i <= max(path->p_depth, path2->p_depth); i++) {
>  			cmp1 = cmp2 = 0;
>  			if (i <= path->p_depth)
> @@ -6136,13 +6129,14 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
>  			if (cmp1 != cmp2 && cmp2 != 0)
>  				numblks++;
>  		}
> -		ext4_free_ext_path(path);
> -		ext4_free_ext_path(path2);
>  	}
>  
>  out:
>  	inode->i_blocks = numblks << (inode->i_sb->s_blocksize_bits - 9);
>  	ext4_mark_inode_dirty(NULL, inode);
> +cleanup:
> +	ext4_free_ext_path(path);
> +	ext4_free_ext_path(path2);
>  	return 0;
>  }
>  
> @@ -6163,12 +6157,9 @@ int ext4_ext_clear_bb(struct inode *inode)
>  	if (IS_ERR(path))
>  		return PTR_ERR(path);
>  	ex = path[path->p_depth].p_ext;
> -	if (!ex) {
> -		ext4_free_ext_path(path);
> -		return 0;
> -	}
> +	if (!ex)
> +		goto out;
>  	end = le32_to_cpu(ex->ee_block) + ext4_ext_get_actual_len(ex);
> -	ext4_free_ext_path(path);
>  
>  	cur = 0;
>  	while (cur < end) {
> @@ -6178,16 +6169,16 @@ int ext4_ext_clear_bb(struct inode *inode)
>  		if (ret < 0)
>  			break;
>  		if (ret > 0) {
> -			path = ext4_find_extent(inode, map.m_lblk, NULL, 0);
> -			if (!IS_ERR_OR_NULL(path)) {
> +			path = ext4_find_extent(inode, map.m_lblk, path, 0);
> +			if (!IS_ERR(path)) {
>  				for (j = 0; j < path->p_depth; j++) {
> -
>  					ext4_mb_mark_bb(inode->i_sb,
>  							path[j].p_block, 1, false);
>  					ext4_fc_record_regions(inode->i_sb, inode->i_ino,
>  							0, path[j].p_block, 1, 1);
>  				}
> -				ext4_free_ext_path(path);
> +			} else {
> +				path = NULL;
>  			}
>  			ext4_mb_mark_bb(inode->i_sb, map.m_pblk, map.m_len, false);
>  			ext4_fc_record_regions(inode->i_sb, inode->i_ino,
> @@ -6196,5 +6187,7 @@ int ext4_ext_clear_bb(struct inode *inode)
>  		cur = cur + map.m_len;
>  	}
>  
> +out:
> +	ext4_free_ext_path(path);
>  	return 0;
>  }
> diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
> index 1dee40477727..1426d595fab7 100644
> --- a/fs/ext4/fast_commit.c
> +++ b/fs/ext4/fast_commit.c
> @@ -1766,7 +1766,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
>  
>  		if (ret == 0) {
>  			/* Range is not mapped */
> -			path = ext4_find_extent(inode, cur, NULL, 0);
> +			path = ext4_find_extent(inode, cur, path, 0);
>  			if (IS_ERR(path))
>  				goto out;
>  			memset(&newex, 0, sizeof(newex));
> @@ -1782,7 +1782,6 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
>  			up_write((&EXT4_I(inode)->i_data_sem));
>  			if (IS_ERR(path))
>  				goto out;
> -			ext4_free_ext_path(path);
>  			goto next;
>  		}
>  
> @@ -1830,6 +1829,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
>  	ext4_ext_replay_shrink_inode(inode, i_size_read(inode) >>
>  					sb->s_blocksize_bits);
>  out:
> +	ext4_free_ext_path(path);
>  	iput(inode);
>  	return 0;
>  }
> @@ -1930,12 +1930,13 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
>  				break;
>  
>  			if (ret > 0) {
> -				path = ext4_find_extent(inode, map.m_lblk, NULL, 0);
> +				path = ext4_find_extent(inode, map.m_lblk, path, 0);
>  				if (!IS_ERR(path)) {
>  					for (j = 0; j < path->p_depth; j++)
>  						ext4_mb_mark_bb(inode->i_sb,
>  							path[j].p_block, 1, true);
> -					ext4_free_ext_path(path);
> +				} else {
> +					path = NULL;
>  				}
>  				cur += ret;
>  				ext4_mb_mark_bb(inode->i_sb, map.m_pblk,
> @@ -1946,6 +1947,8 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
>  		}
>  		iput(inode);
>  	}
> +
> +	ext4_free_ext_path(path);
>  }
>  
>  /*
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at()
  2024-08-02 20:01   ` Ojaswin Mujoo
@ 2024-08-03  2:23     ` Baokun Li
  0 siblings, 0 replies; 84+ messages in thread
From: Baokun Li @ 2024-08-03  2:23 UTC (permalink / raw)
  To: Ojaswin Mujoo
  Cc: linux-ext4, tytso, adilger.kernel, jack, ritesh.list,
	linux-kernel, yi.zhang, yangerkun, Baokun Li, Baokun Li

Hi Ojaswin,

On 2024/8/3 4:01, Ojaswin Mujoo wrote:
> On Wed, Jul 10, 2024 at 12:06:47PM +0800, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> The use of path and ppath is now very confusing, so to make the code more
>> readable, pass path between functions uniformly, and get rid of ppath.
>>
>> To get rid of the ppath in ext4_force_split_extent_at(), the following is
>> done here:
>>
>>   * The ext4_find_extent() can update the extent path so it doesn't have to
>>     allocate and free path repeatedly, thus reducing the consumption of
>>     memory allocation and freeing in ext4_swap_extents().
>>
>> No functional changes.
> Looks good Baokun, feel free to add:
>
> Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
>
> One small comment below..
Thanks for the review!
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
>> ---
>>   fs/ext4/extents.c | 117 ++++++++++++++++++++++++----------------------
>>   1 file changed, 60 insertions(+), 57 deletions(-)
>>
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index c86b1bb7720f..0bd068ed324f 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
> .. snip ..
>
>> @@ -5707,25 +5701,21 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
> In ext4_swap_extents, maybe we should keep the refactoring to a separate
> patch than the changes needed to get rid of ppath in
> ext4_force_split_extent_at(), the commits would look a bit cleaner and
> easier to read that way. I don't feel too strongly about it tho and
> I'll let you take a call.
>
> Regards,
> ojaswin
Totally agree! It does seem a bit unclear now to put the refactoring
of ext4_swap_extents() into a modification to get rid of the ppath.
In the next version I will put the logic for refactoring
ext4_swap_extents() into a separate patch.

Cheers,
Baokun


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at()
  2024-07-27  6:42     ` Baokun Li
@ 2024-08-21  3:19       ` Theodore Ts'o
  2024-08-21  3:29         ` Baokun Li
  0 siblings, 1 reply; 84+ messages in thread
From: Theodore Ts'o @ 2024-08-21  3:19 UTC (permalink / raw)
  To: Baokun Li
  Cc: Jan Kara, linux-ext4, adilger.kernel, ritesh.list, linux-kernel,
	yi.zhang, yangerkun, Baokun Li

On Sat, Jul 27, 2024 at 02:42:50PM +0800, Baokun Li wrote:
 > 								Honza
> Ok, I'll put this in a separate patch in the next version.
> 
> Thank you very much for your review!
>

Hi Baokun,

Did you send out a newer version of this patch series?  I can't seem
to find it in patchwork.

Thanks,

					- Ted

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at()
  2024-08-21  3:19       ` Theodore Ts'o
@ 2024-08-21  3:29         ` Baokun Li
  0 siblings, 0 replies; 84+ messages in thread
From: Baokun Li @ 2024-08-21  3:29 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Jan Kara, linux-ext4, adilger.kernel, ritesh.list, linux-kernel,
	yi.zhang, yangerkun, Baokun Li, Baokun Li

On 2024/8/21 11:19, Theodore Ts'o wrote:
> On Sat, Jul 27, 2024 at 02:42:50PM +0800, Baokun Li wrote:
>   > 								Honza
>> Ok, I'll put this in a separate patch in the next version.
>>
>> Thank you very much for your review!
>>
> Hi Baokun,
>
> Did you send out a newer version of this patch series?  I can't seem
> to find it in patchwork.
>
> Thanks,
>
> 					- Ted

Hi Ted.

I'm very sorry for the slow update, it's been very busy for a while now.

Last week I started preparing the new version and performing some tests.
Now that the tests are almost complete, I'll send the new version out 
tomorrow at the latest.

-- 
With Best Regards,
Baokun Li


^ permalink raw reply	[flat|nested] 84+ messages in thread

end of thread, other threads:[~2024-08-21  3:29 UTC | newest]

Thread overview: 84+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-10  4:06 [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path libaokun
2024-07-10  4:06 ` [PATCH 01/20] ext4: refactor ext4_ext_rm_idx() to index 'path' libaokun
2024-07-24 18:44   ` Jan Kara
2024-07-25  9:14   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 02/20] ext4: prevent partial update of the extents path libaokun
2024-07-14 15:42   ` Ojaswin Mujoo
2024-07-15 12:33     ` Baokun Li
2024-07-15 12:41       ` Baokun Li
2024-07-16  9:54         ` Ojaswin Mujoo
2024-07-16 11:54           ` Baokun Li
2024-07-17  5:29             ` Ojaswin Mujoo
2024-07-17  6:11               ` Baokun Li
2024-07-24  6:23                 ` Ojaswin Mujoo
2024-07-25  5:35                   ` Baokun Li
2024-07-25  8:48                     ` Ojaswin Mujoo
2024-07-24 18:53   ` Jan Kara
2024-07-10  4:06 ` [PATCH 03/20] ext4: fix double brelse() the buffer " libaokun
2024-07-24 19:01   ` Jan Kara
2024-07-26 11:45   ` Ojaswin Mujoo
2024-07-30  8:47     ` Baokun Li
2024-08-01  6:26       ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 04/20] ext4: add new ext4_ext_path_brelse() helper libaokun
2024-07-24 19:02   ` Jan Kara
2024-07-26 11:53   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 05/20] ext4: fix slab-use-after-free in ext4_split_extent_at() libaokun
2024-07-24 19:13   ` Jan Kara
2024-07-27 10:36   ` Ojaswin Mujoo
2024-07-30  8:57     ` Baokun Li
2024-07-30  9:26       ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 06/20] ext4: avoid use-after-free in ext4_ext_show_leaf() libaokun
2024-07-24 19:16   ` Jan Kara
2024-07-25  5:41     ` Baokun Li
2024-07-27 10:43   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 07/20] ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free libaokun
2024-07-25 10:31   ` Jan Kara
2024-07-27 11:18   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 08/20] ext4: get rid of ppath in ext4_find_extent() libaokun
2024-07-25 10:38   ` Jan Kara
2024-07-27  6:18     ` Baokun Li
2024-07-29 11:28       ` Jan Kara
2024-07-30 10:03   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 09/20] ext4: get rid of ppath in get_ext_path() libaokun
2024-07-25 10:41   ` Jan Kara
2024-08-01  7:16   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf() libaokun
2024-07-25 10:46   ` Jan Kara
2024-07-27  6:35     ` Baokun Li
2024-08-02  7:34   ` Ojaswin Mujoo
2024-08-02 13:07     ` Baokun Li
2024-07-10  4:06 ` [PATCH 11/20] ext4: get rid of ppath in ext4_ext_insert_extent() libaokun
2024-07-25 10:59   ` Jan Kara
2024-08-02  8:01   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 12/20] ext4: get rid of ppath in ext4_split_extent_at() libaokun
2024-07-25 11:07   ` Jan Kara
2024-07-27  6:42     ` Baokun Li
2024-08-21  3:19       ` Theodore Ts'o
2024-08-21  3:29         ` Baokun Li
2024-08-02  8:12   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 13/20] ext4: get rid of ppath in ext4_force_split_extent_at() libaokun
2024-07-25 11:14   ` Jan Kara
2024-08-02 20:01   ` Ojaswin Mujoo
2024-08-03  2:23     ` Baokun Li
2024-07-10  4:06 ` [PATCH 14/20] ext4: get rid of ppath in ext4_split_extent() libaokun
2024-07-25 12:07   ` Jan Kara
2024-08-02 20:17   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 15/20] ext4: get rid of ppath in ext4_split_convert_extents() libaokun
2024-07-25 12:12   ` Jan Kara
2024-08-02 20:26   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 16/20] ext4: get rid of ppath in ext4_convert_unwritten_extents_endio() libaokun
2024-07-25 12:14   ` Jan Kara
2024-08-02 20:28   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 17/20] ext4: get rid of ppath in ext4_ext_convert_to_initialized() libaokun
2024-07-25 12:18   ` Jan Kara
2024-08-02 20:38   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 18/20] ext4: get rid of ppath in ext4_ext_handle_unwritten_extents() libaokun
2024-07-25 12:22   ` Jan Kara
2024-08-02 20:44   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 19/20] ext4: get rid of ppath in convert_initialized_extent() libaokun
2024-07-25 12:23   ` Jan Kara
2024-08-02 20:46   ` Ojaswin Mujoo
2024-07-10  4:06 ` [PATCH 20/20] ext4: avoid unnecessary extent path frees and allocations libaokun
2024-07-25 12:31   ` Jan Kara
2024-08-02 20:58   ` Ojaswin Mujoo
2024-07-25  9:02 ` [PATCH 00/20] ext4: some bugfixes and cleanups for ext4 extents path Ojaswin Mujoo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).