public inbox for linux-btrfs@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] btrfs: implement ref_tracker for delayed_nodes
@ 2025-07-09 22:04 Leo Martins
  2025-07-10 11:54 ` Filipe Manana
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Leo Martins @ 2025-07-09 22:04 UTC (permalink / raw)
  To: linux-btrfs, kernel-team; +Cc: jlayton

This patch tracks references to btrfs_delayed_nodes using ref_tracker.
This patch introduces some btrfs_delayed_node_ref_tracker_* wrappers
around ref_tracker structures and functions. The wrappers ensure
that when the Kconfig is disabled everything compiles down to noops.
I was hesitant to lump this with BTRFS_DEBUG because of how expensive
it is, so I introduced a new Kconfig "BTRFS_DELAYED_NODE_REF_TRACKER".
If this isn't an issue I am happy to use BTRFS_DEBUG instead.

- btrfs_delayed_node_ref_tracker_dir
- btrfs_delayed_node_ref_tracker_dir_init()
- btrfs_delayed_node_ref_tracker_dir_exit()

- btrfs_delayed_node_ref_tracker
- btrfs_delayed_node_ref_tracker_alloc()
- btrfs_delayed_node_ref_tracker_free()

Along with being useful for tracking delayed node reference leaks,
this patch helps "document" via code where each ref count increase is
decreased.

Signed-off-by: Leo Martins <loemra.dev@gmail.com>
---
 fs/btrfs/Kconfig         |  14 +++
 fs/btrfs/delayed-inode.c | 209 ++++++++++++++++++++++++++++-----------
 fs/btrfs/delayed-inode.h |  56 ++++++++++-
 3 files changed, 218 insertions(+), 61 deletions(-)

diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
index c352f3ae0385c..d1bff4fdc8c05 100644
--- a/fs/btrfs/Kconfig
+++ b/fs/btrfs/Kconfig
@@ -72,6 +72,20 @@ config BTRFS_DEBUG
 
 	  If unsure, say N.
 
+config BTRFS_DELAYED_NODE_REF_TRACKER
+	bool "Btrfs delayed node reference tracking"
+	depends on BTRFS_DEBUG
+	select REF_TRACKER
+	help
+	  Enable run-time reference tracking for delayed nodes in btrfs filesystem.
+
+	  This option tracks stack traces when references to delayed nodes are taken
+	  and released. It will alert if there are outstanding references when a
+	  delayed node is freed. Note that enabling this option may negatively impact
+	  performance and is primarily intended for debugging purposes.
+
+	  If unsure, say N.
+
 config BTRFS_ASSERT
 	bool "Btrfs assert support"
 	depends on BTRFS_FS
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 0f8d8e275143b..7e6e7532e70f5 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -4,6 +4,7 @@
  * Written by Miao Xie <miaox@cn.fujitsu.com>
  */
 
+#include "linux/gfp_types.h"
 #include <linux/slab.h>
 #include <linux/iversion.h>
 #include "ctree.h"
@@ -57,6 +58,8 @@ static inline void btrfs_init_delayed_node(
 	delayed_node->root = root;
 	delayed_node->inode_id = inode_id;
 	refcount_set(&delayed_node->refs, 0);
+	btrfs_delayed_node_ref_tracker_dir_init(&delayed_node->ref_dir, 16,
+						"delayed_node");
 	delayed_node->ins_root = RB_ROOT_CACHED;
 	delayed_node->del_root = RB_ROOT_CACHED;
 	mutex_init(&delayed_node->mutex);
@@ -64,8 +67,9 @@ static inline void btrfs_init_delayed_node(
 	INIT_LIST_HEAD(&delayed_node->p_list);
 }
 
-static struct btrfs_delayed_node *btrfs_get_delayed_node(
-		struct btrfs_inode *btrfs_inode)
+static struct btrfs_delayed_node *
+btrfs_get_delayed_node(struct btrfs_inode *btrfs_inode,
+		       btrfs_delayed_node_ref_tracker *tracker)
 {
 	struct btrfs_root *root = btrfs_inode->root;
 	u64 ino = btrfs_ino(btrfs_inode);
@@ -74,6 +78,8 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(
 	node = READ_ONCE(btrfs_inode->delayed_node);
 	if (node) {
 		refcount_inc(&node->refs);
+		btrfs_delayed_node_ref_tracker_alloc(&node->ref_dir, tracker,
+						     GFP_NOFS);
 		return node;
 	}
 
@@ -83,6 +89,8 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(
 	if (node) {
 		if (btrfs_inode->delayed_node) {
 			refcount_inc(&node->refs);	/* can be accessed */
+			btrfs_delayed_node_ref_tracker_alloc(
+				&node->ref_dir, tracker, GFP_ATOMIC);
 			BUG_ON(btrfs_inode->delayed_node != node);
 			xa_unlock(&root->delayed_nodes);
 			return node;
@@ -106,6 +114,11 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(
 		 */
 		if (refcount_inc_not_zero(&node->refs)) {
 			refcount_inc(&node->refs);
+			btrfs_delayed_node_ref_tracker_alloc(
+				&node->ref_dir, tracker, GFP_ATOMIC);
+			btrfs_delayed_node_ref_tracker_alloc(
+				&node->ref_dir, &node->inode_cache_tracker,
+				GFP_ATOMIC);
 			btrfs_inode->delayed_node = node;
 		} else {
 			node = NULL;
@@ -125,8 +138,9 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(
  *
  * Return the delayed node, or error pointer on failure.
  */
-static struct btrfs_delayed_node *btrfs_get_or_create_delayed_node(
-		struct btrfs_inode *btrfs_inode)
+static struct btrfs_delayed_node *
+btrfs_get_or_create_delayed_node(struct btrfs_inode *btrfs_inode,
+				 btrfs_delayed_node_ref_tracker *tracker)
 {
 	struct btrfs_delayed_node *node;
 	struct btrfs_root *root = btrfs_inode->root;
@@ -135,7 +149,7 @@ static struct btrfs_delayed_node *btrfs_get_or_create_delayed_node(
 	void *ptr;
 
 again:
-	node = btrfs_get_delayed_node(btrfs_inode);
+	node = btrfs_get_delayed_node(btrfs_inode, tracker);
 	if (node)
 		return node;
 
@@ -146,6 +160,9 @@ static struct btrfs_delayed_node *btrfs_get_or_create_delayed_node(
 
 	/* Cached in the inode and can be accessed. */
 	refcount_set(&node->refs, 2);
+	btrfs_delayed_node_ref_tracker_alloc(&node->ref_dir, tracker, GFP_NOFS);
+	btrfs_delayed_node_ref_tracker_alloc(
+		&node->ref_dir, &node->inode_cache_tracker, GFP_NOFS);
 
 	/* Allocate and reserve the slot, from now it can return a NULL from xa_load(). */
 	ret = xa_reserve(&root->delayed_nodes, ino, GFP_NOFS);
@@ -191,6 +208,8 @@ static void btrfs_queue_delayed_node(struct btrfs_delayed_root *root,
 		list_add_tail(&node->n_list, &root->node_list);
 		list_add_tail(&node->p_list, &root->prepare_list);
 		refcount_inc(&node->refs);	/* inserted into list */
+		btrfs_delayed_node_ref_tracker_alloc(
+			&node->ref_dir, &node->node_list_tracker, GFP_ATOMIC);
 		root->nodes++;
 		set_bit(BTRFS_DELAYED_NODE_IN_LIST, &node->flags);
 	}
@@ -204,6 +223,8 @@ static void btrfs_dequeue_delayed_node(struct btrfs_delayed_root *root,
 	spin_lock(&root->lock);
 	if (test_bit(BTRFS_DELAYED_NODE_IN_LIST, &node->flags)) {
 		root->nodes--;
+		btrfs_delayed_node_ref_tracker_free(&node->ref_dir,
+						    &node->node_list_tracker);
 		refcount_dec(&node->refs);	/* not in the list */
 		list_del_init(&node->n_list);
 		if (!list_empty(&node->p_list))
@@ -213,23 +234,28 @@ static void btrfs_dequeue_delayed_node(struct btrfs_delayed_root *root,
 	spin_unlock(&root->lock);
 }
 
-static struct btrfs_delayed_node *btrfs_first_delayed_node(
-			struct btrfs_delayed_root *delayed_root)
+static struct btrfs_delayed_node *
+btrfs_first_delayed_node(struct btrfs_delayed_root *delayed_root,
+			 btrfs_delayed_node_ref_tracker *tracker)
 {
 	struct btrfs_delayed_node *node;
 
 	spin_lock(&delayed_root->lock);
 	node = list_first_entry_or_null(&delayed_root->node_list,
 					struct btrfs_delayed_node, n_list);
-	if (node)
+	if (node) {
 		refcount_inc(&node->refs);
+		btrfs_delayed_node_ref_tracker_alloc(&node->ref_dir, tracker,
+						     GFP_ATOMIC);
+	}
 	spin_unlock(&delayed_root->lock);
 
 	return node;
 }
 
-static struct btrfs_delayed_node *btrfs_next_delayed_node(
-						struct btrfs_delayed_node *node)
+static struct btrfs_delayed_node *
+btrfs_next_delayed_node(struct btrfs_delayed_node *node,
+			btrfs_delayed_node_ref_tracker *tracker)
 {
 	struct btrfs_delayed_root *delayed_root;
 	struct list_head *p;
@@ -249,15 +275,17 @@ static struct btrfs_delayed_node *btrfs_next_delayed_node(
 
 	next = list_entry(p, struct btrfs_delayed_node, n_list);
 	refcount_inc(&next->refs);
+	btrfs_delayed_node_ref_tracker_alloc(&next->ref_dir, tracker,
+					     GFP_ATOMIC);
 out:
 	spin_unlock(&delayed_root->lock);
 
 	return next;
 }
 
-static void __btrfs_release_delayed_node(
-				struct btrfs_delayed_node *delayed_node,
-				int mod)
+static void
+__btrfs_release_delayed_node(struct btrfs_delayed_node *delayed_node, int mod,
+			     btrfs_delayed_node_ref_tracker *tracker)
 {
 	struct btrfs_delayed_root *delayed_root;
 
@@ -273,6 +301,7 @@ static void __btrfs_release_delayed_node(
 		btrfs_dequeue_delayed_node(delayed_root, delayed_node);
 	mutex_unlock(&delayed_node->mutex);
 
+	btrfs_delayed_node_ref_tracker_free(&delayed_node->ref_dir, tracker);
 	if (refcount_dec_and_test(&delayed_node->refs)) {
 		struct btrfs_root *root = delayed_node->root;
 
@@ -282,17 +311,21 @@ static void __btrfs_release_delayed_node(
 		 * back up.  We can delete it now.
 		 */
 		ASSERT(refcount_read(&delayed_node->refs) == 0);
+		btrfs_delayed_node_ref_tracker_dir_exit(&delayed_node->ref_dir);
 		kmem_cache_free(delayed_node_cache, delayed_node);
 	}
 }
 
-static inline void btrfs_release_delayed_node(struct btrfs_delayed_node *node)
+static inline void
+btrfs_release_delayed_node(struct btrfs_delayed_node *node,
+			   btrfs_delayed_node_ref_tracker *tracker)
 {
-	__btrfs_release_delayed_node(node, 0);
+	__btrfs_release_delayed_node(node, 0, tracker);
 }
 
-static struct btrfs_delayed_node *btrfs_first_prepared_delayed_node(
-					struct btrfs_delayed_root *delayed_root)
+static struct btrfs_delayed_node *
+btrfs_first_prepared_delayed_node(struct btrfs_delayed_root *delayed_root,
+				  btrfs_delayed_node_ref_tracker *tracker)
 {
 	struct btrfs_delayed_node *node;
 
@@ -302,16 +335,19 @@ static struct btrfs_delayed_node *btrfs_first_prepared_delayed_node(
 	if (node) {
 		list_del_init(&node->p_list);
 		refcount_inc(&node->refs);
+		btrfs_delayed_node_ref_tracker_alloc(&node->ref_dir, tracker,
+						     GFP_ATOMIC);
 	}
 	spin_unlock(&delayed_root->lock);
 
 	return node;
 }
 
-static inline void btrfs_release_prepared_delayed_node(
-					struct btrfs_delayed_node *node)
+static inline void
+btrfs_release_prepared_delayed_node(struct btrfs_delayed_node *node,
+				    btrfs_delayed_node_ref_tracker *tracker)
 {
-	__btrfs_release_delayed_node(node, 1);
+	__btrfs_release_delayed_node(node, 1, tracker);
 }
 
 static struct btrfs_delayed_item *btrfs_alloc_delayed_item(u16 data_len,
@@ -1126,6 +1162,8 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
 	struct btrfs_fs_info *fs_info = trans->fs_info;
 	struct btrfs_delayed_root *delayed_root;
 	struct btrfs_delayed_node *curr_node, *prev_node;
+	btrfs_delayed_node_ref_tracker curr_delayed_node_tracker,
+		prev_delayed_node_tracker;
 	struct btrfs_path *path;
 	struct btrfs_block_rsv *block_rsv;
 	int ret = 0;
@@ -1143,7 +1181,8 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
 
 	delayed_root = fs_info->delayed_root;
 
-	curr_node = btrfs_first_delayed_node(delayed_root);
+	curr_node = btrfs_first_delayed_node(delayed_root,
+					     &curr_delayed_node_tracker);
 	while (curr_node && (!count || nr--)) {
 		ret = __btrfs_commit_inode_delayed_items(trans, path,
 							 curr_node);
@@ -1153,7 +1192,9 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
 		}
 
 		prev_node = curr_node;
-		curr_node = btrfs_next_delayed_node(curr_node);
+		prev_delayed_node_tracker = curr_delayed_node_tracker;
+		curr_node = btrfs_next_delayed_node(curr_node,
+						    &curr_delayed_node_tracker);
 		/*
 		 * See the comment below about releasing path before releasing
 		 * node. If the commit of delayed items was successful the path
@@ -1161,7 +1202,8 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
 		 * point to locked extent buffers (a leaf at the very least).
 		 */
 		ASSERT(path->nodes[0] == NULL);
-		btrfs_release_delayed_node(prev_node);
+		btrfs_release_delayed_node(prev_node,
+					   &prev_delayed_node_tracker);
 	}
 
 	/*
@@ -1174,7 +1216,8 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
 	btrfs_free_path(path);
 
 	if (curr_node)
-		btrfs_release_delayed_node(curr_node);
+		btrfs_release_delayed_node(curr_node,
+					   &curr_delayed_node_tracker);
 	trans->block_rsv = block_rsv;
 
 	return ret;
@@ -1193,7 +1236,9 @@ int btrfs_run_delayed_items_nr(struct btrfs_trans_handle *trans, int nr)
 int btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
 				     struct btrfs_inode *inode)
 {
-	struct btrfs_delayed_node *delayed_node = btrfs_get_delayed_node(inode);
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
+	struct btrfs_delayed_node *delayed_node =
+		btrfs_get_delayed_node(inode, &delayed_node_tracker);
 	BTRFS_PATH_AUTO_FREE(path);
 	struct btrfs_block_rsv *block_rsv;
 	int ret;
@@ -1204,14 +1249,14 @@ int btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
 	mutex_lock(&delayed_node->mutex);
 	if (!delayed_node->count) {
 		mutex_unlock(&delayed_node->mutex);
-		btrfs_release_delayed_node(delayed_node);
+		btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 		return 0;
 	}
 	mutex_unlock(&delayed_node->mutex);
 
 	path = btrfs_alloc_path();
 	if (!path) {
-		btrfs_release_delayed_node(delayed_node);
+		btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 		return -ENOMEM;
 	}
 
@@ -1220,7 +1265,7 @@ int btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
 
 	ret = __btrfs_commit_inode_delayed_items(trans, path, delayed_node);
 
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 	trans->block_rsv = block_rsv;
 
 	return ret;
@@ -1230,7 +1275,9 @@ int btrfs_commit_inode_delayed_inode(struct btrfs_inode *inode)
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_delayed_node *delayed_node = btrfs_get_delayed_node(inode);
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
+	struct btrfs_delayed_node *delayed_node =
+		btrfs_get_delayed_node(inode, &delayed_node_tracker);
 	struct btrfs_path *path;
 	struct btrfs_block_rsv *block_rsv;
 	int ret;
@@ -1241,7 +1288,7 @@ int btrfs_commit_inode_delayed_inode(struct btrfs_inode *inode)
 	mutex_lock(&delayed_node->mutex);
 	if (!test_bit(BTRFS_DELAYED_NODE_INODE_DIRTY, &delayed_node->flags)) {
 		mutex_unlock(&delayed_node->mutex);
-		btrfs_release_delayed_node(delayed_node);
+		btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 		return 0;
 	}
 	mutex_unlock(&delayed_node->mutex);
@@ -1275,7 +1322,7 @@ int btrfs_commit_inode_delayed_inode(struct btrfs_inode *inode)
 	btrfs_end_transaction(trans);
 	btrfs_btree_balance_dirty(fs_info);
 out:
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 
 	return ret;
 }
@@ -1289,7 +1336,8 @@ void btrfs_remove_delayed_node(struct btrfs_inode *inode)
 		return;
 
 	inode->delayed_node = NULL;
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node,
+				   &delayed_node->inode_cache_tracker);
 }
 
 struct btrfs_async_delayed_work {
@@ -1305,6 +1353,7 @@ static void btrfs_async_run_delayed_root(struct btrfs_work *work)
 	struct btrfs_trans_handle *trans;
 	struct btrfs_path *path;
 	struct btrfs_delayed_node *delayed_node = NULL;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 	struct btrfs_root *root;
 	struct btrfs_block_rsv *block_rsv;
 	int total_done = 0;
@@ -1321,7 +1370,8 @@ static void btrfs_async_run_delayed_root(struct btrfs_work *work)
 		    BTRFS_DELAYED_BACKGROUND / 2)
 			break;
 
-		delayed_node = btrfs_first_prepared_delayed_node(delayed_root);
+		delayed_node = btrfs_first_prepared_delayed_node(
+			delayed_root, &delayed_node_tracker);
 		if (!delayed_node)
 			break;
 
@@ -1330,7 +1380,8 @@ static void btrfs_async_run_delayed_root(struct btrfs_work *work)
 		trans = btrfs_join_transaction(root);
 		if (IS_ERR(trans)) {
 			btrfs_release_path(path);
-			btrfs_release_prepared_delayed_node(delayed_node);
+			btrfs_release_prepared_delayed_node(
+				delayed_node, &delayed_node_tracker);
 			total_done++;
 			continue;
 		}
@@ -1345,7 +1396,8 @@ static void btrfs_async_run_delayed_root(struct btrfs_work *work)
 		btrfs_btree_balance_dirty_nodelay(root->fs_info);
 
 		btrfs_release_path(path);
-		btrfs_release_prepared_delayed_node(delayed_node);
+		btrfs_release_prepared_delayed_node(delayed_node,
+						    &delayed_node_tracker);
 		total_done++;
 
 	} while ((async_work->nr == 0 && total_done < BTRFS_DELAYED_WRITEBACK)
@@ -1377,10 +1429,15 @@ static int btrfs_wq_run_delayed_node(struct btrfs_delayed_root *delayed_root,
 
 void btrfs_assert_delayed_root_empty(struct btrfs_fs_info *fs_info)
 {
-	struct btrfs_delayed_node *node = btrfs_first_delayed_node(fs_info->delayed_root);
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
+	struct btrfs_delayed_node *node = btrfs_first_delayed_node(
+		fs_info->delayed_root, &delayed_node_tracker);
 
-	if (WARN_ON(node))
+	if (WARN_ON(node)) {
+		btrfs_delayed_node_ref_tracker_free(&node->ref_dir,
+						    &delayed_node_tracker);
 		refcount_dec(&node->refs);
+	}
 }
 
 static bool could_end_wait(struct btrfs_delayed_root *delayed_root, int seq)
@@ -1454,13 +1511,15 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
 	struct btrfs_fs_info *fs_info = trans->fs_info;
 	const unsigned int leaf_data_size = BTRFS_LEAF_DATA_SIZE(fs_info);
 	struct btrfs_delayed_node *delayed_node;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 	struct btrfs_delayed_item *delayed_item;
 	struct btrfs_dir_item *dir_item;
 	bool reserve_leaf_space;
 	u32 data_len;
 	int ret;
 
-	delayed_node = btrfs_get_or_create_delayed_node(dir);
+	delayed_node =
+		btrfs_get_or_create_delayed_node(dir, &delayed_node_tracker);
 	if (IS_ERR(delayed_node))
 		return PTR_ERR(delayed_node);
 
@@ -1536,7 +1595,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
 	mutex_unlock(&delayed_node->mutex);
 
 release_node:
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 	return ret;
 }
 
@@ -1591,10 +1650,11 @@ int btrfs_delete_delayed_dir_index(struct btrfs_trans_handle *trans,
 				   struct btrfs_inode *dir, u64 index)
 {
 	struct btrfs_delayed_node *node;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 	struct btrfs_delayed_item *item;
 	int ret;
 
-	node = btrfs_get_or_create_delayed_node(dir);
+	node = btrfs_get_or_create_delayed_node(dir, &delayed_node_tracker);
 	if (IS_ERR(node))
 		return PTR_ERR(node);
 
@@ -1635,13 +1695,15 @@ int btrfs_delete_delayed_dir_index(struct btrfs_trans_handle *trans,
 	}
 	mutex_unlock(&node->mutex);
 end:
-	btrfs_release_delayed_node(node);
+	btrfs_release_delayed_node(node, &delayed_node_tracker);
 	return ret;
 }
 
 int btrfs_inode_delayed_dir_index_count(struct btrfs_inode *inode)
 {
-	struct btrfs_delayed_node *delayed_node = btrfs_get_delayed_node(inode);
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
+	struct btrfs_delayed_node *delayed_node =
+		btrfs_get_delayed_node(inode, &delayed_node_tracker);
 
 	if (!delayed_node)
 		return -ENOENT;
@@ -1652,12 +1714,12 @@ int btrfs_inode_delayed_dir_index_count(struct btrfs_inode *inode)
 	 * is updated now. So we needn't lock the delayed node.
 	 */
 	if (!delayed_node->index_cnt) {
-		btrfs_release_delayed_node(delayed_node);
+		btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 		return -EINVAL;
 	}
 
 	inode->index_cnt = delayed_node->index_cnt;
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 	return 0;
 }
 
@@ -1668,8 +1730,9 @@ bool btrfs_readdir_get_delayed_items(struct btrfs_inode *inode,
 {
 	struct btrfs_delayed_node *delayed_node;
 	struct btrfs_delayed_item *item;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 
-	delayed_node = btrfs_get_delayed_node(inode);
+	delayed_node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
 	if (!delayed_node)
 		return false;
 
@@ -1704,6 +1767,8 @@ bool btrfs_readdir_get_delayed_items(struct btrfs_inode *inode,
 	 * insert/delete delayed items in this period. So we also needn't
 	 * requeue or dequeue this delayed node.
 	 */
+	btrfs_delayed_node_ref_tracker_free(&delayed_node->ref_dir,
+					    &delayed_node_tracker);
 	refcount_dec(&delayed_node->refs);
 
 	return true;
@@ -1845,17 +1910,18 @@ int btrfs_fill_inode(struct btrfs_inode *inode, u32 *rdev)
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 	struct btrfs_delayed_node *delayed_node;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 	struct btrfs_inode_item *inode_item;
 	struct inode *vfs_inode = &inode->vfs_inode;
 
-	delayed_node = btrfs_get_delayed_node(inode);
+	delayed_node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
 	if (!delayed_node)
 		return -ENOENT;
 
 	mutex_lock(&delayed_node->mutex);
 	if (!test_bit(BTRFS_DELAYED_NODE_INODE_DIRTY, &delayed_node->flags)) {
 		mutex_unlock(&delayed_node->mutex);
-		btrfs_release_delayed_node(delayed_node);
+		btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 		return -ENOENT;
 	}
 
@@ -1895,7 +1961,7 @@ int btrfs_fill_inode(struct btrfs_inode *inode, u32 *rdev)
 		inode->index_cnt = (u64)-1;
 
 	mutex_unlock(&delayed_node->mutex);
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 	return 0;
 }
 
@@ -1904,9 +1970,11 @@ int btrfs_delayed_update_inode(struct btrfs_trans_handle *trans,
 {
 	struct btrfs_root *root = inode->root;
 	struct btrfs_delayed_node *delayed_node;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 	int ret = 0;
 
-	delayed_node = btrfs_get_or_create_delayed_node(inode);
+	delayed_node =
+		btrfs_get_or_create_delayed_node(inode, &delayed_node_tracker);
 	if (IS_ERR(delayed_node))
 		return PTR_ERR(delayed_node);
 
@@ -1926,7 +1994,7 @@ int btrfs_delayed_update_inode(struct btrfs_trans_handle *trans,
 	atomic_inc(&root->fs_info->delayed_root->items);
 release_node:
 	mutex_unlock(&delayed_node->mutex);
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 	return ret;
 }
 
@@ -1934,6 +2002,7 @@ int btrfs_delayed_delete_inode_ref(struct btrfs_inode *inode)
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 	struct btrfs_delayed_node *delayed_node;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 
 	/*
 	 * we don't do delayed inode updates during log recovery because it
@@ -1943,7 +2012,8 @@ int btrfs_delayed_delete_inode_ref(struct btrfs_inode *inode)
 	if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags))
 		return -EAGAIN;
 
-	delayed_node = btrfs_get_or_create_delayed_node(inode);
+	delayed_node =
+		btrfs_get_or_create_delayed_node(inode, &delayed_node_tracker);
 	if (IS_ERR(delayed_node))
 		return PTR_ERR(delayed_node);
 
@@ -1970,7 +2040,7 @@ int btrfs_delayed_delete_inode_ref(struct btrfs_inode *inode)
 	atomic_inc(&fs_info->delayed_root->items);
 release_node:
 	mutex_unlock(&delayed_node->mutex);
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 	return 0;
 }
 
@@ -2014,19 +2084,21 @@ static void __btrfs_kill_delayed_node(struct btrfs_delayed_node *delayed_node)
 void btrfs_kill_delayed_inode_items(struct btrfs_inode *inode)
 {
 	struct btrfs_delayed_node *delayed_node;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 
-	delayed_node = btrfs_get_delayed_node(inode);
+	delayed_node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
 	if (!delayed_node)
 		return;
 
 	__btrfs_kill_delayed_node(delayed_node);
-	btrfs_release_delayed_node(delayed_node);
+	btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
 }
 
 void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
 {
 	unsigned long index = 0;
 	struct btrfs_delayed_node *delayed_nodes[8];
+	btrfs_delayed_node_ref_tracker delayed_node_trackers[8];
 
 	while (1) {
 		struct btrfs_delayed_node *node;
@@ -2045,6 +2117,10 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
 			 * about to be removed from the tree in the loop below
 			 */
 			if (refcount_inc_not_zero(&node->refs)) {
+				btrfs_delayed_node_ref_tracker_alloc(
+					&node->ref_dir,
+					&delayed_node_trackers[count],
+					GFP_ATOMIC);
 				delayed_nodes[count] = node;
 				count++;
 			}
@@ -2056,7 +2132,8 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
 
 		for (int i = 0; i < count; i++) {
 			__btrfs_kill_delayed_node(delayed_nodes[i]);
-			btrfs_release_delayed_node(delayed_nodes[i]);
+			btrfs_release_delayed_node(delayed_nodes[i],
+						   &delayed_node_trackers[i]);
 		}
 	}
 }
@@ -2064,14 +2141,20 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
 void btrfs_destroy_delayed_inodes(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_delayed_node *curr_node, *prev_node;
+	btrfs_delayed_node_ref_tracker curr_delayed_node_tracker,
+		prev_delayed_node_tracker;
 
-	curr_node = btrfs_first_delayed_node(fs_info->delayed_root);
+	curr_node = btrfs_first_delayed_node(fs_info->delayed_root,
+					     &curr_delayed_node_tracker);
 	while (curr_node) {
 		__btrfs_kill_delayed_node(curr_node);
 
 		prev_node = curr_node;
-		curr_node = btrfs_next_delayed_node(curr_node);
-		btrfs_release_delayed_node(prev_node);
+		prev_delayed_node_tracker = curr_delayed_node_tracker;
+		curr_node = btrfs_next_delayed_node(curr_node,
+						    &curr_delayed_node_tracker);
+		btrfs_release_delayed_node(prev_node,
+					   &prev_delayed_node_tracker);
 	}
 }
 
@@ -2081,8 +2164,9 @@ void btrfs_log_get_delayed_items(struct btrfs_inode *inode,
 {
 	struct btrfs_delayed_node *node;
 	struct btrfs_delayed_item *item;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 
-	node = btrfs_get_delayed_node(inode);
+	node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
 	if (!node)
 		return;
 
@@ -2140,6 +2224,8 @@ void btrfs_log_get_delayed_items(struct btrfs_inode *inode,
 	 * delete delayed items.
 	 */
 	ASSERT(refcount_read(&node->refs) > 1);
+	btrfs_delayed_node_ref_tracker_free(&node->ref_dir,
+					    &delayed_node_tracker);
 	refcount_dec(&node->refs);
 }
 
@@ -2150,8 +2236,9 @@ void btrfs_log_put_delayed_items(struct btrfs_inode *inode,
 	struct btrfs_delayed_node *node;
 	struct btrfs_delayed_item *item;
 	struct btrfs_delayed_item *next;
+	btrfs_delayed_node_ref_tracker delayed_node_tracker;
 
-	node = btrfs_get_delayed_node(inode);
+	node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
 	if (!node)
 		return;
 
@@ -2183,5 +2270,7 @@ void btrfs_log_put_delayed_items(struct btrfs_inode *inode,
 	 * delete delayed items.
 	 */
 	ASSERT(refcount_read(&node->refs) > 1);
+	btrfs_delayed_node_ref_tracker_free(&node->ref_dir,
+					    &delayed_node_tracker);
 	refcount_dec(&node->refs);
 }
diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h
index e6e763ad2d421..d7e0ec020c4d0 100644
--- a/fs/btrfs/delayed-inode.h
+++ b/fs/btrfs/delayed-inode.h
@@ -7,6 +7,7 @@
 #ifndef BTRFS_DELAYED_INODE_H
 #define BTRFS_DELAYED_INODE_H
 
+#include "linux/ref_tracker.h"
 #include <linux/types.h>
 #include <linux/rbtree.h>
 #include <linux/spinlock.h>
@@ -44,6 +45,51 @@ struct btrfs_delayed_root {
 	wait_queue_head_t wait;
 };
 
+#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
+typedef struct ref_tracker *btrfs_delayed_node_ref_tracker;
+typedef struct ref_tracker_dir btrfs_delayed_node_ref_tracker_dir;
+#else
+typedef struct {} btrfs_delayed_node_ref_tracker;
+typedef struct {} btrfs_delayed_node_ref_tracker_dir;
+#endif
+
+static inline void btrfs_delayed_node_ref_tracker_dir_init(btrfs_delayed_node_ref_tracker_dir *dir, 
+						       unsigned int quarantine_count,
+						       const char *name)
+{
+#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
+	ref_tracker_dir_init(dir, quarantine_count, name);
+#endif
+}
+
+static inline void btrfs_delayed_node_ref_tracker_dir_exit(btrfs_delayed_node_ref_tracker_dir *dir)
+{
+#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
+	ref_tracker_dir_exit(dir);
+#endif
+}
+
+static inline int btrfs_delayed_node_ref_tracker_alloc(btrfs_delayed_node_ref_tracker_dir *dir,
+						    btrfs_delayed_node_ref_tracker *tracker,
+						    gfp_t gfp)
+{
+#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
+	return ref_tracker_alloc(dir, tracker, gfp);
+#else
+	return 0;
+#endif
+}
+
+static inline int btrfs_delayed_node_ref_tracker_free(btrfs_delayed_node_ref_tracker_dir *dir,
+						   btrfs_delayed_node_ref_tracker *tracker)
+{
+#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
+	return ref_tracker_free(dir, tracker);
+#else
+	return 0;
+#endif
+}
+
 #define BTRFS_DELAYED_NODE_IN_LIST	0
 #define BTRFS_DELAYED_NODE_INODE_DIRTY	1
 #define BTRFS_DELAYED_NODE_DEL_IREF	2
@@ -63,10 +109,18 @@ struct btrfs_delayed_node {
 	struct rb_root_cached del_root;
 	struct mutex mutex;
 	struct btrfs_inode_item inode_item;
+
 	refcount_t refs;
-	int count;
+	/* Used to track all references to this delayed node. */
+	btrfs_delayed_node_ref_tracker_dir ref_dir;
+	/* Used to track delayed node reference stored in node list. */
+	btrfs_delayed_node_ref_tracker node_list_tracker;
+	/* Used to track delayed node reference stored in inode cache. */
+	btrfs_delayed_node_ref_tracker inode_cache_tracker;
+
 	u64 index_cnt;
 	unsigned long flags;
+	int count;
 	/*
 	 * The size of the next batch of dir index items to insert (if this
 	 * node is from a directory inode). Protected by @mutex.
-- 
2.47.1


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

* Re: [PATCH] btrfs: implement ref_tracker for delayed_nodes
  2025-07-09 22:04 [PATCH] btrfs: implement ref_tracker for delayed_nodes Leo Martins
@ 2025-07-10 11:54 ` Filipe Manana
  2025-07-11 10:38   ` David Sterba
  2025-07-10 23:49 ` kernel test robot
  2025-07-11  3:42 ` kernel test robot
  2 siblings, 1 reply; 7+ messages in thread
From: Filipe Manana @ 2025-07-10 11:54 UTC (permalink / raw)
  To: Leo Martins; +Cc: linux-btrfs, kernel-team, jlayton

On Wed, Jul 9, 2025 at 11:04 PM Leo Martins <loemra.dev@gmail.com> wrote:
>
> This patch tracks references to btrfs_delayed_nodes using ref_tracker.
> This patch introduces some btrfs_delayed_node_ref_tracker_* wrappers
> around ref_tracker structures and functions. The wrappers ensure
> that when the Kconfig is disabled everything compiles down to noops.
> I was hesitant to lump this with BTRFS_DEBUG because of how expensive
> it is, so I introduced a new Kconfig "BTRFS_DELAYED_NODE_REF_TRACKER".
> If this isn't an issue I am happy to use BTRFS_DEBUG instead.
>
> - btrfs_delayed_node_ref_tracker_dir
> - btrfs_delayed_node_ref_tracker_dir_init()
> - btrfs_delayed_node_ref_tracker_dir_exit()
>
> - btrfs_delayed_node_ref_tracker
> - btrfs_delayed_node_ref_tracker_alloc()
> - btrfs_delayed_node_ref_tracker_free()
>
> Along with being useful for tracking delayed node reference leaks,
> this patch helps "document" via code where each ref count increase is
> decreased.
>
> Signed-off-by: Leo Martins <loemra.dev@gmail.com>
> ---
>  fs/btrfs/Kconfig         |  14 +++
>  fs/btrfs/delayed-inode.c | 209 ++++++++++++++++++++++++++++-----------
>  fs/btrfs/delayed-inode.h |  56 ++++++++++-
>  3 files changed, 218 insertions(+), 61 deletions(-)
>
> diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
> index c352f3ae0385c..d1bff4fdc8c05 100644
> --- a/fs/btrfs/Kconfig
> +++ b/fs/btrfs/Kconfig
> @@ -72,6 +72,20 @@ config BTRFS_DEBUG
>
>           If unsure, say N.
>
> +config BTRFS_DELAYED_NODE_REF_TRACKER
> +       bool "Btrfs delayed node reference tracking"
> +       depends on BTRFS_DEBUG
> +       select REF_TRACKER
> +       help
> +         Enable run-time reference tracking for delayed nodes in btrfs filesystem.
> +
> +         This option tracks stack traces when references to delayed nodes are taken
> +         and released. It will alert if there are outstanding references when a
> +         delayed node is freed. Note that enabling this option may negatively impact
> +         performance and is primarily intended for debugging purposes.
> +
> +         If unsure, say N.
> +
>  config BTRFS_ASSERT
>         bool "Btrfs assert support"
>         depends on BTRFS_FS
> diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
> index 0f8d8e275143b..7e6e7532e70f5 100644
> --- a/fs/btrfs/delayed-inode.c
> +++ b/fs/btrfs/delayed-inode.c
> @@ -4,6 +4,7 @@
>   * Written by Miao Xie <miaox@cn.fujitsu.com>
>   */
>
> +#include "linux/gfp_types.h"
>  #include <linux/slab.h>
>  #include <linux/iversion.h>
>  #include "ctree.h"
> @@ -57,6 +58,8 @@ static inline void btrfs_init_delayed_node(
>         delayed_node->root = root;
>         delayed_node->inode_id = inode_id;
>         refcount_set(&delayed_node->refs, 0);
> +       btrfs_delayed_node_ref_tracker_dir_init(&delayed_node->ref_dir, 16,
> +                                               "delayed_node");
>         delayed_node->ins_root = RB_ROOT_CACHED;
>         delayed_node->del_root = RB_ROOT_CACHED;
>         mutex_init(&delayed_node->mutex);
> @@ -64,8 +67,9 @@ static inline void btrfs_init_delayed_node(
>         INIT_LIST_HEAD(&delayed_node->p_list);
>  }
>
> -static struct btrfs_delayed_node *btrfs_get_delayed_node(
> -               struct btrfs_inode *btrfs_inode)
> +static struct btrfs_delayed_node *
> +btrfs_get_delayed_node(struct btrfs_inode *btrfs_inode,
> +                      btrfs_delayed_node_ref_tracker *tracker)
>  {
>         struct btrfs_root *root = btrfs_inode->root;
>         u64 ino = btrfs_ino(btrfs_inode);
> @@ -74,6 +78,8 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(
>         node = READ_ONCE(btrfs_inode->delayed_node);
>         if (node) {
>                 refcount_inc(&node->refs);
> +               btrfs_delayed_node_ref_tracker_alloc(&node->ref_dir, tracker,
> +                                                    GFP_NOFS);
>                 return node;
>         }
>
> @@ -83,6 +89,8 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(
>         if (node) {
>                 if (btrfs_inode->delayed_node) {
>                         refcount_inc(&node->refs);      /* can be accessed */
> +                       btrfs_delayed_node_ref_tracker_alloc(
> +                               &node->ref_dir, tracker, GFP_ATOMIC);
>                         BUG_ON(btrfs_inode->delayed_node != node);
>                         xa_unlock(&root->delayed_nodes);
>                         return node;
> @@ -106,6 +114,11 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(
>                  */
>                 if (refcount_inc_not_zero(&node->refs)) {
>                         refcount_inc(&node->refs);
> +                       btrfs_delayed_node_ref_tracker_alloc(
> +                               &node->ref_dir, tracker, GFP_ATOMIC);
> +                       btrfs_delayed_node_ref_tracker_alloc(
> +                               &node->ref_dir, &node->inode_cache_tracker,
> +                               GFP_ATOMIC);
>                         btrfs_inode->delayed_node = node;
>                 } else {
>                         node = NULL;
> @@ -125,8 +138,9 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(
>   *
>   * Return the delayed node, or error pointer on failure.
>   */
> -static struct btrfs_delayed_node *btrfs_get_or_create_delayed_node(
> -               struct btrfs_inode *btrfs_inode)
> +static struct btrfs_delayed_node *
> +btrfs_get_or_create_delayed_node(struct btrfs_inode *btrfs_inode,
> +                                btrfs_delayed_node_ref_tracker *tracker)
>  {
>         struct btrfs_delayed_node *node;
>         struct btrfs_root *root = btrfs_inode->root;
> @@ -135,7 +149,7 @@ static struct btrfs_delayed_node *btrfs_get_or_create_delayed_node(
>         void *ptr;
>
>  again:
> -       node = btrfs_get_delayed_node(btrfs_inode);
> +       node = btrfs_get_delayed_node(btrfs_inode, tracker);
>         if (node)
>                 return node;
>
> @@ -146,6 +160,9 @@ static struct btrfs_delayed_node *btrfs_get_or_create_delayed_node(
>
>         /* Cached in the inode and can be accessed. */
>         refcount_set(&node->refs, 2);
> +       btrfs_delayed_node_ref_tracker_alloc(&node->ref_dir, tracker, GFP_NOFS);
> +       btrfs_delayed_node_ref_tracker_alloc(
> +               &node->ref_dir, &node->inode_cache_tracker, GFP_NOFS);
>
>         /* Allocate and reserve the slot, from now it can return a NULL from xa_load(). */
>         ret = xa_reserve(&root->delayed_nodes, ino, GFP_NOFS);
> @@ -191,6 +208,8 @@ static void btrfs_queue_delayed_node(struct btrfs_delayed_root *root,
>                 list_add_tail(&node->n_list, &root->node_list);
>                 list_add_tail(&node->p_list, &root->prepare_list);
>                 refcount_inc(&node->refs);      /* inserted into list */
> +               btrfs_delayed_node_ref_tracker_alloc(
> +                       &node->ref_dir, &node->node_list_tracker, GFP_ATOMIC);
>                 root->nodes++;
>                 set_bit(BTRFS_DELAYED_NODE_IN_LIST, &node->flags);
>         }
> @@ -204,6 +223,8 @@ static void btrfs_dequeue_delayed_node(struct btrfs_delayed_root *root,
>         spin_lock(&root->lock);
>         if (test_bit(BTRFS_DELAYED_NODE_IN_LIST, &node->flags)) {
>                 root->nodes--;
> +               btrfs_delayed_node_ref_tracker_free(&node->ref_dir,
> +                                                   &node->node_list_tracker);
>                 refcount_dec(&node->refs);      /* not in the list */
>                 list_del_init(&node->n_list);
>                 if (!list_empty(&node->p_list))
> @@ -213,23 +234,28 @@ static void btrfs_dequeue_delayed_node(struct btrfs_delayed_root *root,
>         spin_unlock(&root->lock);
>  }
>
> -static struct btrfs_delayed_node *btrfs_first_delayed_node(
> -                       struct btrfs_delayed_root *delayed_root)
> +static struct btrfs_delayed_node *
> +btrfs_first_delayed_node(struct btrfs_delayed_root *delayed_root,
> +                        btrfs_delayed_node_ref_tracker *tracker)
>  {
>         struct btrfs_delayed_node *node;
>
>         spin_lock(&delayed_root->lock);
>         node = list_first_entry_or_null(&delayed_root->node_list,
>                                         struct btrfs_delayed_node, n_list);
> -       if (node)
> +       if (node) {
>                 refcount_inc(&node->refs);
> +               btrfs_delayed_node_ref_tracker_alloc(&node->ref_dir, tracker,
> +                                                    GFP_ATOMIC);
> +       }
>         spin_unlock(&delayed_root->lock);
>
>         return node;
>  }
>
> -static struct btrfs_delayed_node *btrfs_next_delayed_node(
> -                                               struct btrfs_delayed_node *node)
> +static struct btrfs_delayed_node *
> +btrfs_next_delayed_node(struct btrfs_delayed_node *node,
> +                       btrfs_delayed_node_ref_tracker *tracker)
>  {
>         struct btrfs_delayed_root *delayed_root;
>         struct list_head *p;
> @@ -249,15 +275,17 @@ static struct btrfs_delayed_node *btrfs_next_delayed_node(
>
>         next = list_entry(p, struct btrfs_delayed_node, n_list);
>         refcount_inc(&next->refs);
> +       btrfs_delayed_node_ref_tracker_alloc(&next->ref_dir, tracker,
> +                                            GFP_ATOMIC);
>  out:
>         spin_unlock(&delayed_root->lock);
>
>         return next;
>  }
>
> -static void __btrfs_release_delayed_node(
> -                               struct btrfs_delayed_node *delayed_node,
> -                               int mod)
> +static void
> +__btrfs_release_delayed_node(struct btrfs_delayed_node *delayed_node, int mod,
> +                            btrfs_delayed_node_ref_tracker *tracker)
>  {
>         struct btrfs_delayed_root *delayed_root;
>
> @@ -273,6 +301,7 @@ static void __btrfs_release_delayed_node(
>                 btrfs_dequeue_delayed_node(delayed_root, delayed_node);
>         mutex_unlock(&delayed_node->mutex);
>
> +       btrfs_delayed_node_ref_tracker_free(&delayed_node->ref_dir, tracker);
>         if (refcount_dec_and_test(&delayed_node->refs)) {
>                 struct btrfs_root *root = delayed_node->root;
>
> @@ -282,17 +311,21 @@ static void __btrfs_release_delayed_node(
>                  * back up.  We can delete it now.
>                  */
>                 ASSERT(refcount_read(&delayed_node->refs) == 0);
> +               btrfs_delayed_node_ref_tracker_dir_exit(&delayed_node->ref_dir);
>                 kmem_cache_free(delayed_node_cache, delayed_node);
>         }
>  }
>
> -static inline void btrfs_release_delayed_node(struct btrfs_delayed_node *node)
> +static inline void
> +btrfs_release_delayed_node(struct btrfs_delayed_node *node,
> +                          btrfs_delayed_node_ref_tracker *tracker)
>  {
> -       __btrfs_release_delayed_node(node, 0);
> +       __btrfs_release_delayed_node(node, 0, tracker);
>  }
>
> -static struct btrfs_delayed_node *btrfs_first_prepared_delayed_node(
> -                                       struct btrfs_delayed_root *delayed_root)
> +static struct btrfs_delayed_node *
> +btrfs_first_prepared_delayed_node(struct btrfs_delayed_root *delayed_root,
> +                                 btrfs_delayed_node_ref_tracker *tracker)
>  {
>         struct btrfs_delayed_node *node;
>
> @@ -302,16 +335,19 @@ static struct btrfs_delayed_node *btrfs_first_prepared_delayed_node(
>         if (node) {
>                 list_del_init(&node->p_list);
>                 refcount_inc(&node->refs);
> +               btrfs_delayed_node_ref_tracker_alloc(&node->ref_dir, tracker,
> +                                                    GFP_ATOMIC);
>         }
>         spin_unlock(&delayed_root->lock);
>
>         return node;
>  }
>
> -static inline void btrfs_release_prepared_delayed_node(
> -                                       struct btrfs_delayed_node *node)
> +static inline void
> +btrfs_release_prepared_delayed_node(struct btrfs_delayed_node *node,
> +                                   btrfs_delayed_node_ref_tracker *tracker)
>  {
> -       __btrfs_release_delayed_node(node, 1);
> +       __btrfs_release_delayed_node(node, 1, tracker);
>  }
>
>  static struct btrfs_delayed_item *btrfs_alloc_delayed_item(u16 data_len,
> @@ -1126,6 +1162,8 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
>         struct btrfs_fs_info *fs_info = trans->fs_info;
>         struct btrfs_delayed_root *delayed_root;
>         struct btrfs_delayed_node *curr_node, *prev_node;
> +       btrfs_delayed_node_ref_tracker curr_delayed_node_tracker,
> +               prev_delayed_node_tracker;
>         struct btrfs_path *path;
>         struct btrfs_block_rsv *block_rsv;
>         int ret = 0;
> @@ -1143,7 +1181,8 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
>
>         delayed_root = fs_info->delayed_root;
>
> -       curr_node = btrfs_first_delayed_node(delayed_root);
> +       curr_node = btrfs_first_delayed_node(delayed_root,
> +                                            &curr_delayed_node_tracker);
>         while (curr_node && (!count || nr--)) {
>                 ret = __btrfs_commit_inode_delayed_items(trans, path,
>                                                          curr_node);
> @@ -1153,7 +1192,9 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
>                 }
>
>                 prev_node = curr_node;
> -               curr_node = btrfs_next_delayed_node(curr_node);
> +               prev_delayed_node_tracker = curr_delayed_node_tracker;
> +               curr_node = btrfs_next_delayed_node(curr_node,
> +                                                   &curr_delayed_node_tracker);
>                 /*
>                  * See the comment below about releasing path before releasing
>                  * node. If the commit of delayed items was successful the path
> @@ -1161,7 +1202,8 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
>                  * point to locked extent buffers (a leaf at the very least).
>                  */
>                 ASSERT(path->nodes[0] == NULL);
> -               btrfs_release_delayed_node(prev_node);
> +               btrfs_release_delayed_node(prev_node,
> +                                          &prev_delayed_node_tracker);
>         }
>
>         /*
> @@ -1174,7 +1216,8 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
>         btrfs_free_path(path);
>
>         if (curr_node)
> -               btrfs_release_delayed_node(curr_node);
> +               btrfs_release_delayed_node(curr_node,
> +                                          &curr_delayed_node_tracker);
>         trans->block_rsv = block_rsv;
>
>         return ret;
> @@ -1193,7 +1236,9 @@ int btrfs_run_delayed_items_nr(struct btrfs_trans_handle *trans, int nr)
>  int btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
>                                      struct btrfs_inode *inode)
>  {
> -       struct btrfs_delayed_node *delayed_node = btrfs_get_delayed_node(inode);
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
> +       struct btrfs_delayed_node *delayed_node =
> +               btrfs_get_delayed_node(inode, &delayed_node_tracker);
>         BTRFS_PATH_AUTO_FREE(path);
>         struct btrfs_block_rsv *block_rsv;
>         int ret;
> @@ -1204,14 +1249,14 @@ int btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
>         mutex_lock(&delayed_node->mutex);
>         if (!delayed_node->count) {
>                 mutex_unlock(&delayed_node->mutex);
> -               btrfs_release_delayed_node(delayed_node);
> +               btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>                 return 0;
>         }
>         mutex_unlock(&delayed_node->mutex);
>
>         path = btrfs_alloc_path();
>         if (!path) {
> -               btrfs_release_delayed_node(delayed_node);
> +               btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>                 return -ENOMEM;
>         }
>
> @@ -1220,7 +1265,7 @@ int btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
>
>         ret = __btrfs_commit_inode_delayed_items(trans, path, delayed_node);
>
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>         trans->block_rsv = block_rsv;
>
>         return ret;
> @@ -1230,7 +1275,9 @@ int btrfs_commit_inode_delayed_inode(struct btrfs_inode *inode)
>  {
>         struct btrfs_fs_info *fs_info = inode->root->fs_info;
>         struct btrfs_trans_handle *trans;
> -       struct btrfs_delayed_node *delayed_node = btrfs_get_delayed_node(inode);
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
> +       struct btrfs_delayed_node *delayed_node =
> +               btrfs_get_delayed_node(inode, &delayed_node_tracker);
>         struct btrfs_path *path;
>         struct btrfs_block_rsv *block_rsv;
>         int ret;
> @@ -1241,7 +1288,7 @@ int btrfs_commit_inode_delayed_inode(struct btrfs_inode *inode)
>         mutex_lock(&delayed_node->mutex);
>         if (!test_bit(BTRFS_DELAYED_NODE_INODE_DIRTY, &delayed_node->flags)) {
>                 mutex_unlock(&delayed_node->mutex);
> -               btrfs_release_delayed_node(delayed_node);
> +               btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>                 return 0;
>         }
>         mutex_unlock(&delayed_node->mutex);
> @@ -1275,7 +1322,7 @@ int btrfs_commit_inode_delayed_inode(struct btrfs_inode *inode)
>         btrfs_end_transaction(trans);
>         btrfs_btree_balance_dirty(fs_info);
>  out:
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>
>         return ret;
>  }
> @@ -1289,7 +1336,8 @@ void btrfs_remove_delayed_node(struct btrfs_inode *inode)
>                 return;
>
>         inode->delayed_node = NULL;
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node,
> +                                  &delayed_node->inode_cache_tracker);
>  }
>
>  struct btrfs_async_delayed_work {
> @@ -1305,6 +1353,7 @@ static void btrfs_async_run_delayed_root(struct btrfs_work *work)
>         struct btrfs_trans_handle *trans;
>         struct btrfs_path *path;
>         struct btrfs_delayed_node *delayed_node = NULL;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>         struct btrfs_root *root;
>         struct btrfs_block_rsv *block_rsv;
>         int total_done = 0;
> @@ -1321,7 +1370,8 @@ static void btrfs_async_run_delayed_root(struct btrfs_work *work)
>                     BTRFS_DELAYED_BACKGROUND / 2)
>                         break;
>
> -               delayed_node = btrfs_first_prepared_delayed_node(delayed_root);
> +               delayed_node = btrfs_first_prepared_delayed_node(
> +                       delayed_root, &delayed_node_tracker);
>                 if (!delayed_node)
>                         break;
>
> @@ -1330,7 +1380,8 @@ static void btrfs_async_run_delayed_root(struct btrfs_work *work)
>                 trans = btrfs_join_transaction(root);
>                 if (IS_ERR(trans)) {
>                         btrfs_release_path(path);
> -                       btrfs_release_prepared_delayed_node(delayed_node);
> +                       btrfs_release_prepared_delayed_node(
> +                               delayed_node, &delayed_node_tracker);
>                         total_done++;
>                         continue;
>                 }
> @@ -1345,7 +1396,8 @@ static void btrfs_async_run_delayed_root(struct btrfs_work *work)
>                 btrfs_btree_balance_dirty_nodelay(root->fs_info);
>
>                 btrfs_release_path(path);
> -               btrfs_release_prepared_delayed_node(delayed_node);
> +               btrfs_release_prepared_delayed_node(delayed_node,
> +                                                   &delayed_node_tracker);
>                 total_done++;
>
>         } while ((async_work->nr == 0 && total_done < BTRFS_DELAYED_WRITEBACK)
> @@ -1377,10 +1429,15 @@ static int btrfs_wq_run_delayed_node(struct btrfs_delayed_root *delayed_root,
>
>  void btrfs_assert_delayed_root_empty(struct btrfs_fs_info *fs_info)
>  {
> -       struct btrfs_delayed_node *node = btrfs_first_delayed_node(fs_info->delayed_root);
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
> +       struct btrfs_delayed_node *node = btrfs_first_delayed_node(
> +               fs_info->delayed_root, &delayed_node_tracker);
>
> -       if (WARN_ON(node))
> +       if (WARN_ON(node)) {
> +               btrfs_delayed_node_ref_tracker_free(&node->ref_dir,
> +                                                   &delayed_node_tracker);
>                 refcount_dec(&node->refs);
> +       }
>  }
>
>  static bool could_end_wait(struct btrfs_delayed_root *delayed_root, int seq)
> @@ -1454,13 +1511,15 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
>         struct btrfs_fs_info *fs_info = trans->fs_info;
>         const unsigned int leaf_data_size = BTRFS_LEAF_DATA_SIZE(fs_info);
>         struct btrfs_delayed_node *delayed_node;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>         struct btrfs_delayed_item *delayed_item;
>         struct btrfs_dir_item *dir_item;
>         bool reserve_leaf_space;
>         u32 data_len;
>         int ret;
>
> -       delayed_node = btrfs_get_or_create_delayed_node(dir);
> +       delayed_node =
> +               btrfs_get_or_create_delayed_node(dir, &delayed_node_tracker);
>         if (IS_ERR(delayed_node))
>                 return PTR_ERR(delayed_node);
>
> @@ -1536,7 +1595,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
>         mutex_unlock(&delayed_node->mutex);
>
>  release_node:
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>         return ret;
>  }
>
> @@ -1591,10 +1650,11 @@ int btrfs_delete_delayed_dir_index(struct btrfs_trans_handle *trans,
>                                    struct btrfs_inode *dir, u64 index)
>  {
>         struct btrfs_delayed_node *node;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>         struct btrfs_delayed_item *item;
>         int ret;
>
> -       node = btrfs_get_or_create_delayed_node(dir);
> +       node = btrfs_get_or_create_delayed_node(dir, &delayed_node_tracker);
>         if (IS_ERR(node))
>                 return PTR_ERR(node);
>
> @@ -1635,13 +1695,15 @@ int btrfs_delete_delayed_dir_index(struct btrfs_trans_handle *trans,
>         }
>         mutex_unlock(&node->mutex);
>  end:
> -       btrfs_release_delayed_node(node);
> +       btrfs_release_delayed_node(node, &delayed_node_tracker);
>         return ret;
>  }
>
>  int btrfs_inode_delayed_dir_index_count(struct btrfs_inode *inode)
>  {
> -       struct btrfs_delayed_node *delayed_node = btrfs_get_delayed_node(inode);
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
> +       struct btrfs_delayed_node *delayed_node =
> +               btrfs_get_delayed_node(inode, &delayed_node_tracker);
>
>         if (!delayed_node)
>                 return -ENOENT;
> @@ -1652,12 +1714,12 @@ int btrfs_inode_delayed_dir_index_count(struct btrfs_inode *inode)
>          * is updated now. So we needn't lock the delayed node.
>          */
>         if (!delayed_node->index_cnt) {
> -               btrfs_release_delayed_node(delayed_node);
> +               btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>                 return -EINVAL;
>         }
>
>         inode->index_cnt = delayed_node->index_cnt;
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>         return 0;
>  }
>
> @@ -1668,8 +1730,9 @@ bool btrfs_readdir_get_delayed_items(struct btrfs_inode *inode,
>  {
>         struct btrfs_delayed_node *delayed_node;
>         struct btrfs_delayed_item *item;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>
> -       delayed_node = btrfs_get_delayed_node(inode);
> +       delayed_node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
>         if (!delayed_node)
>                 return false;
>
> @@ -1704,6 +1767,8 @@ bool btrfs_readdir_get_delayed_items(struct btrfs_inode *inode,
>          * insert/delete delayed items in this period. So we also needn't
>          * requeue or dequeue this delayed node.
>          */
> +       btrfs_delayed_node_ref_tracker_free(&delayed_node->ref_dir,
> +                                           &delayed_node_tracker);
>         refcount_dec(&delayed_node->refs);
>
>         return true;
> @@ -1845,17 +1910,18 @@ int btrfs_fill_inode(struct btrfs_inode *inode, u32 *rdev)
>  {
>         struct btrfs_fs_info *fs_info = inode->root->fs_info;
>         struct btrfs_delayed_node *delayed_node;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>         struct btrfs_inode_item *inode_item;
>         struct inode *vfs_inode = &inode->vfs_inode;
>
> -       delayed_node = btrfs_get_delayed_node(inode);
> +       delayed_node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
>         if (!delayed_node)
>                 return -ENOENT;
>
>         mutex_lock(&delayed_node->mutex);
>         if (!test_bit(BTRFS_DELAYED_NODE_INODE_DIRTY, &delayed_node->flags)) {
>                 mutex_unlock(&delayed_node->mutex);
> -               btrfs_release_delayed_node(delayed_node);
> +               btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>                 return -ENOENT;
>         }
>
> @@ -1895,7 +1961,7 @@ int btrfs_fill_inode(struct btrfs_inode *inode, u32 *rdev)
>                 inode->index_cnt = (u64)-1;
>
>         mutex_unlock(&delayed_node->mutex);
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>         return 0;
>  }
>
> @@ -1904,9 +1970,11 @@ int btrfs_delayed_update_inode(struct btrfs_trans_handle *trans,
>  {
>         struct btrfs_root *root = inode->root;
>         struct btrfs_delayed_node *delayed_node;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>         int ret = 0;
>
> -       delayed_node = btrfs_get_or_create_delayed_node(inode);
> +       delayed_node =
> +               btrfs_get_or_create_delayed_node(inode, &delayed_node_tracker);
>         if (IS_ERR(delayed_node))
>                 return PTR_ERR(delayed_node);
>
> @@ -1926,7 +1994,7 @@ int btrfs_delayed_update_inode(struct btrfs_trans_handle *trans,
>         atomic_inc(&root->fs_info->delayed_root->items);
>  release_node:
>         mutex_unlock(&delayed_node->mutex);
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>         return ret;
>  }
>
> @@ -1934,6 +2002,7 @@ int btrfs_delayed_delete_inode_ref(struct btrfs_inode *inode)
>  {
>         struct btrfs_fs_info *fs_info = inode->root->fs_info;
>         struct btrfs_delayed_node *delayed_node;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>
>         /*
>          * we don't do delayed inode updates during log recovery because it
> @@ -1943,7 +2012,8 @@ int btrfs_delayed_delete_inode_ref(struct btrfs_inode *inode)
>         if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags))
>                 return -EAGAIN;
>
> -       delayed_node = btrfs_get_or_create_delayed_node(inode);
> +       delayed_node =
> +               btrfs_get_or_create_delayed_node(inode, &delayed_node_tracker);
>         if (IS_ERR(delayed_node))
>                 return PTR_ERR(delayed_node);
>
> @@ -1970,7 +2040,7 @@ int btrfs_delayed_delete_inode_ref(struct btrfs_inode *inode)
>         atomic_inc(&fs_info->delayed_root->items);
>  release_node:
>         mutex_unlock(&delayed_node->mutex);
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>         return 0;
>  }
>
> @@ -2014,19 +2084,21 @@ static void __btrfs_kill_delayed_node(struct btrfs_delayed_node *delayed_node)
>  void btrfs_kill_delayed_inode_items(struct btrfs_inode *inode)
>  {
>         struct btrfs_delayed_node *delayed_node;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>
> -       delayed_node = btrfs_get_delayed_node(inode);
> +       delayed_node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
>         if (!delayed_node)
>                 return;
>
>         __btrfs_kill_delayed_node(delayed_node);
> -       btrfs_release_delayed_node(delayed_node);
> +       btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
>  }
>
>  void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
>  {
>         unsigned long index = 0;
>         struct btrfs_delayed_node *delayed_nodes[8];
> +       btrfs_delayed_node_ref_tracker delayed_node_trackers[8];
>
>         while (1) {
>                 struct btrfs_delayed_node *node;
> @@ -2045,6 +2117,10 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
>                          * about to be removed from the tree in the loop below
>                          */
>                         if (refcount_inc_not_zero(&node->refs)) {
> +                               btrfs_delayed_node_ref_tracker_alloc(
> +                                       &node->ref_dir,
> +                                       &delayed_node_trackers[count],
> +                                       GFP_ATOMIC);
>                                 delayed_nodes[count] = node;
>                                 count++;
>                         }
> @@ -2056,7 +2132,8 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
>
>                 for (int i = 0; i < count; i++) {
>                         __btrfs_kill_delayed_node(delayed_nodes[i]);
> -                       btrfs_release_delayed_node(delayed_nodes[i]);
> +                       btrfs_release_delayed_node(delayed_nodes[i],
> +                                                  &delayed_node_trackers[i]);
>                 }
>         }
>  }
> @@ -2064,14 +2141,20 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
>  void btrfs_destroy_delayed_inodes(struct btrfs_fs_info *fs_info)
>  {
>         struct btrfs_delayed_node *curr_node, *prev_node;
> +       btrfs_delayed_node_ref_tracker curr_delayed_node_tracker,
> +               prev_delayed_node_tracker;
>
> -       curr_node = btrfs_first_delayed_node(fs_info->delayed_root);
> +       curr_node = btrfs_first_delayed_node(fs_info->delayed_root,
> +                                            &curr_delayed_node_tracker);
>         while (curr_node) {
>                 __btrfs_kill_delayed_node(curr_node);
>
>                 prev_node = curr_node;
> -               curr_node = btrfs_next_delayed_node(curr_node);
> -               btrfs_release_delayed_node(prev_node);
> +               prev_delayed_node_tracker = curr_delayed_node_tracker;
> +               curr_node = btrfs_next_delayed_node(curr_node,
> +                                                   &curr_delayed_node_tracker);
> +               btrfs_release_delayed_node(prev_node,
> +                                          &prev_delayed_node_tracker);
>         }
>  }
>
> @@ -2081,8 +2164,9 @@ void btrfs_log_get_delayed_items(struct btrfs_inode *inode,
>  {
>         struct btrfs_delayed_node *node;
>         struct btrfs_delayed_item *item;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>
> -       node = btrfs_get_delayed_node(inode);
> +       node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
>         if (!node)
>                 return;
>
> @@ -2140,6 +2224,8 @@ void btrfs_log_get_delayed_items(struct btrfs_inode *inode,
>          * delete delayed items.
>          */
>         ASSERT(refcount_read(&node->refs) > 1);
> +       btrfs_delayed_node_ref_tracker_free(&node->ref_dir,
> +                                           &delayed_node_tracker);
>         refcount_dec(&node->refs);
>  }
>
> @@ -2150,8 +2236,9 @@ void btrfs_log_put_delayed_items(struct btrfs_inode *inode,
>         struct btrfs_delayed_node *node;
>         struct btrfs_delayed_item *item;
>         struct btrfs_delayed_item *next;
> +       btrfs_delayed_node_ref_tracker delayed_node_tracker;
>
> -       node = btrfs_get_delayed_node(inode);
> +       node = btrfs_get_delayed_node(inode, &delayed_node_tracker);
>         if (!node)
>                 return;
>
> @@ -2183,5 +2270,7 @@ void btrfs_log_put_delayed_items(struct btrfs_inode *inode,
>          * delete delayed items.
>          */
>         ASSERT(refcount_read(&node->refs) > 1);
> +       btrfs_delayed_node_ref_tracker_free(&node->ref_dir,
> +                                           &delayed_node_tracker);
>         refcount_dec(&node->refs);
>  }
> diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h
> index e6e763ad2d421..d7e0ec020c4d0 100644
> --- a/fs/btrfs/delayed-inode.h
> +++ b/fs/btrfs/delayed-inode.h
> @@ -7,6 +7,7 @@
>  #ifndef BTRFS_DELAYED_INODE_H
>  #define BTRFS_DELAYED_INODE_H
>
> +#include "linux/ref_tracker.h"
>  #include <linux/types.h>
>  #include <linux/rbtree.h>
>  #include <linux/spinlock.h>
> @@ -44,6 +45,51 @@ struct btrfs_delayed_root {
>         wait_queue_head_t wait;
>  };
>
> +#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
> +typedef struct ref_tracker *btrfs_delayed_node_ref_tracker;
> +typedef struct ref_tracker_dir btrfs_delayed_node_ref_tracker_dir;
> +#else
> +typedef struct {} btrfs_delayed_node_ref_tracker;
> +typedef struct {} btrfs_delayed_node_ref_tracker_dir;
> +#endif
> +
> +static inline void btrfs_delayed_node_ref_tracker_dir_init(btrfs_delayed_node_ref_tracker_dir *dir,
> +                                                      unsigned int quarantine_count,
> +                                                      const char *name)
> +{
> +#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
> +       ref_tracker_dir_init(dir, quarantine_count, name);
> +#endif
> +}
> +
> +static inline void btrfs_delayed_node_ref_tracker_dir_exit(btrfs_delayed_node_ref_tracker_dir *dir)
> +{
> +#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
> +       ref_tracker_dir_exit(dir);
> +#endif
> +}
> +
> +static inline int btrfs_delayed_node_ref_tracker_alloc(btrfs_delayed_node_ref_tracker_dir *dir,
> +                                                   btrfs_delayed_node_ref_tracker *tracker,
> +                                                   gfp_t gfp)
> +{
> +#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
> +       return ref_tracker_alloc(dir, tracker, gfp);
> +#else
> +       return 0;
> +#endif
> +}
> +
> +static inline int btrfs_delayed_node_ref_tracker_free(btrfs_delayed_node_ref_tracker_dir *dir,
> +                                                  btrfs_delayed_node_ref_tracker *tracker)
> +{
> +#ifdef CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER
> +       return ref_tracker_free(dir, tracker);
> +#else
> +       return 0;
> +#endif
> +}
> +
>  #define BTRFS_DELAYED_NODE_IN_LIST     0
>  #define BTRFS_DELAYED_NODE_INODE_DIRTY 1
>  #define BTRFS_DELAYED_NODE_DEL_IREF    2
> @@ -63,10 +109,18 @@ struct btrfs_delayed_node {
>         struct rb_root_cached del_root;
>         struct mutex mutex;
>         struct btrfs_inode_item inode_item;
> +
>         refcount_t refs;
> -       int count;
> +       /* Used to track all references to this delayed node. */
> +       btrfs_delayed_node_ref_tracker_dir ref_dir;
> +       /* Used to track delayed node reference stored in node list. */
> +       btrfs_delayed_node_ref_tracker node_list_tracker;
> +       /* Used to track delayed node reference stored in inode cache. */
> +       btrfs_delayed_node_ref_tracker inode_cache_tracker;
> +
>         u64 index_cnt;
>         unsigned long flags;
> +       int count;

Why are you moving the count field?
This increases the size of the struct even without the new config
option enabled and as a result we get less objects per page.
You are essentially reverting this commit:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a20725e1e7014642fc8ba4c7dd2c4ef6d4ec56a9

Without any explanation about why.

I'm also not a big fan of the typedefs and would prefer to have
struct ref_tracker used directly in the structure surrounded by an
#ifdef block.
I also don't like adding a new Kconfig option just for this... How
much slower would a build with CONFIG_BTRFS_DEBUG=y be?

If that's really a lot slower, then perhaps a more generic config
option name in case we add similar tracking to other data structures
one day.

Thanks.

>         /*
>          * The size of the next batch of dir index items to insert (if this
>          * node is from a directory inode). Protected by @mutex.
> --
> 2.47.1
>
>

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

* Re: [PATCH] btrfs: implement ref_tracker for delayed_nodes
  2025-07-09 22:04 [PATCH] btrfs: implement ref_tracker for delayed_nodes Leo Martins
  2025-07-10 11:54 ` Filipe Manana
@ 2025-07-10 23:49 ` kernel test robot
  2025-07-11  3:42 ` kernel test robot
  2 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2025-07-10 23:49 UTC (permalink / raw)
  To: Leo Martins, linux-btrfs, kernel-team
  Cc: Paul Gazzillo, Necip Fazil Yildiran, oe-kbuild-all, jlayton

Hi Leo,

kernel test robot noticed the following build warnings:

[auto build test WARNING on kdave/for-next]
[also build test WARNING on linus/master v6.16-rc5 next-20250710]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Leo-Martins/btrfs-implement-ref_tracker-for-delayed_nodes/20250710-060640
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
patch link:    https://lore.kernel.org/r/fa19acf9dc38e93546183fc083c365cdb237e89b.1752098515.git.loemra.dev%40gmail.com
patch subject: [PATCH] btrfs: implement ref_tracker for delayed_nodes
config: alpha-kismet-CONFIG_REF_TRACKER-CONFIG_BTRFS_DELAYED_NODE_REF_TRACKER-0-0 (https://download.01.org/0day-ci/archive/20250711/202507110735.DbwDC17f-lkp@intel.com/config)
reproduce: (https://download.01.org/0day-ci/archive/20250711/202507110735.DbwDC17f-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507110735.DbwDC17f-lkp@intel.com/

kismet warnings: (new ones prefixed by >>)
>> kismet: WARNING: unmet direct dependencies detected for REF_TRACKER when selected by BTRFS_DELAYED_NODE_REF_TRACKER
   WARNING: unmet direct dependencies detected for REF_TRACKER
     Depends on [n]: STACKTRACE_SUPPORT
     Selected by [y]:
     - BTRFS_DELAYED_NODE_REF_TRACKER [=y] && BLOCK [=y] && BTRFS_DEBUG [=y]

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH] btrfs: implement ref_tracker for delayed_nodes
  2025-07-09 22:04 [PATCH] btrfs: implement ref_tracker for delayed_nodes Leo Martins
  2025-07-10 11:54 ` Filipe Manana
  2025-07-10 23:49 ` kernel test robot
@ 2025-07-11  3:42 ` kernel test robot
  2 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2025-07-11  3:42 UTC (permalink / raw)
  To: Leo Martins, linux-btrfs, kernel-team; +Cc: oe-kbuild-all, jlayton

Hi Leo,

kernel test robot noticed the following build errors:

[auto build test ERROR on kdave/for-next]
[also build test ERROR on linus/master v6.16-rc5 next-20250710]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Leo-Martins/btrfs-implement-ref_tracker-for-delayed_nodes/20250710-060640
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
patch link:    https://lore.kernel.org/r/fa19acf9dc38e93546183fc083c365cdb237e89b.1752098515.git.loemra.dev%40gmail.com
patch subject: [PATCH] btrfs: implement ref_tracker for delayed_nodes
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20250711/202507111111.Eh3BAP7i-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250711/202507111111.Eh3BAP7i-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507111111.Eh3BAP7i-lkp@intel.com/

All errors (new ones prefixed by >>):

   lib/ref_tracker.c: In function 'ref_tracker_alloc':
>> lib/ref_tracker.c:209:22: error: implicit declaration of function 'stack_trace_save'; did you mean 'stack_depot_save'? [-Wimplicit-function-declaration]
     209 |         nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
         |                      ^~~~~~~~~~~~~~~~
         |                      stack_depot_save

Kconfig warnings: (for reference only)
   WARNING: unmet direct dependencies detected for REF_TRACKER
   Depends on [n]: STACKTRACE_SUPPORT
   Selected by [y]:
   - BTRFS_DELAYED_NODE_REF_TRACKER [=y] && BLOCK [=y] && BTRFS_DEBUG [=y]


vim +209 lib/ref_tracker.c

4e66934eaadc83 Eric Dumazet  2021-12-04  184  
4e66934eaadc83 Eric Dumazet  2021-12-04  185  int ref_tracker_alloc(struct ref_tracker_dir *dir,
4e66934eaadc83 Eric Dumazet  2021-12-04  186  		      struct ref_tracker **trackerp,
4e66934eaadc83 Eric Dumazet  2021-12-04  187  		      gfp_t gfp)
4e66934eaadc83 Eric Dumazet  2021-12-04  188  {
4e66934eaadc83 Eric Dumazet  2021-12-04  189  	unsigned long entries[REF_TRACKER_STACK_ENTRIES];
4e66934eaadc83 Eric Dumazet  2021-12-04  190  	struct ref_tracker *tracker;
4e66934eaadc83 Eric Dumazet  2021-12-04  191  	unsigned int nr_entries;
acd8f0e5d72741 Andrzej Hajda 2023-06-02  192  	gfp_t gfp_mask = gfp | __GFP_NOWARN;
4e66934eaadc83 Eric Dumazet  2021-12-04  193  	unsigned long flags;
4e66934eaadc83 Eric Dumazet  2021-12-04  194  
e3ececfe668fac Eric Dumazet  2022-02-04  195  	WARN_ON_ONCE(dir->dead);
e3ececfe668fac Eric Dumazet  2022-02-04  196  
8fd5522f44dcd7 Eric Dumazet  2022-02-04  197  	if (!trackerp) {
8fd5522f44dcd7 Eric Dumazet  2022-02-04  198  		refcount_inc(&dir->no_tracker);
8fd5522f44dcd7 Eric Dumazet  2022-02-04  199  		return 0;
8fd5522f44dcd7 Eric Dumazet  2022-02-04  200  	}
c12837d1bb3103 Eric Dumazet  2022-01-12  201  	if (gfp & __GFP_DIRECT_RECLAIM)
c12837d1bb3103 Eric Dumazet  2022-01-12  202  		gfp_mask |= __GFP_NOFAIL;
c12837d1bb3103 Eric Dumazet  2022-01-12  203  	*trackerp = tracker = kzalloc(sizeof(*tracker), gfp_mask);
4e66934eaadc83 Eric Dumazet  2021-12-04  204  	if (unlikely(!tracker)) {
4e66934eaadc83 Eric Dumazet  2021-12-04  205  		pr_err_once("memory allocation failure, unreliable refcount tracker.\n");
4e66934eaadc83 Eric Dumazet  2021-12-04  206  		refcount_inc(&dir->untracked);
4e66934eaadc83 Eric Dumazet  2021-12-04  207  		return -ENOMEM;
4e66934eaadc83 Eric Dumazet  2021-12-04  208  	}
4e66934eaadc83 Eric Dumazet  2021-12-04 @209  	nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
4e66934eaadc83 Eric Dumazet  2021-12-04  210  	tracker->alloc_stack_handle = stack_depot_save(entries, nr_entries, gfp);
4e66934eaadc83 Eric Dumazet  2021-12-04  211  
4e66934eaadc83 Eric Dumazet  2021-12-04  212  	spin_lock_irqsave(&dir->lock, flags);
4e66934eaadc83 Eric Dumazet  2021-12-04  213  	list_add(&tracker->head, &dir->list);
4e66934eaadc83 Eric Dumazet  2021-12-04  214  	spin_unlock_irqrestore(&dir->lock, flags);
4e66934eaadc83 Eric Dumazet  2021-12-04  215  	return 0;
4e66934eaadc83 Eric Dumazet  2021-12-04  216  }
4e66934eaadc83 Eric Dumazet  2021-12-04  217  EXPORT_SYMBOL_GPL(ref_tracker_alloc);
4e66934eaadc83 Eric Dumazet  2021-12-04  218  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH] btrfs: implement ref_tracker for delayed_nodes
  2025-07-10 11:54 ` Filipe Manana
@ 2025-07-11 10:38   ` David Sterba
  2025-07-23 17:44     ` Leo Martins
  0 siblings, 1 reply; 7+ messages in thread
From: David Sterba @ 2025-07-11 10:38 UTC (permalink / raw)
  To: Filipe Manana; +Cc: Leo Martins, linux-btrfs, kernel-team, jlayton

On Thu, Jul 10, 2025 at 12:54:34PM +0100, Filipe Manana wrote:
> > @@ -63,10 +109,18 @@ struct btrfs_delayed_node {
> >         struct rb_root_cached del_root;
> >         struct mutex mutex;
> >         struct btrfs_inode_item inode_item;
> > +
> >         refcount_t refs;
> > -       int count;
> > +       /* Used to track all references to this delayed node. */
> > +       btrfs_delayed_node_ref_tracker_dir ref_dir;
> > +       /* Used to track delayed node reference stored in node list. */
> > +       btrfs_delayed_node_ref_tracker node_list_tracker;
> > +       /* Used to track delayed node reference stored in inode cache. */
> > +       btrfs_delayed_node_ref_tracker inode_cache_tracker;
> > +
> >         u64 index_cnt;
> >         unsigned long flags;
> > +       int count;
> 
> Why are you moving the count field?
> This increases the size of the struct even without the new config
> option enabled and as a result we get less objects per page.
> You are essentially reverting this commit:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a20725e1e7014642fc8ba4c7dd2c4ef6d4ec56a9
> 
> Without any explanation about why.
> 
> I'm also not a big fan of the typedefs and would prefer to have
> struct ref_tracker used directly in the structure surrounded by an
> #ifdef block.

Agreed, we use typedefs only for function callbacks, for variable types
not anymore.

> I also don't like adding a new Kconfig option just for this... How
> much slower would a build with CONFIG_BTRFS_DEBUG=y be?
> 
> If that's really a lot slower, then perhaps a more generic config
> option name in case we add similar tracking to other data structures
> one day.

Right, we want to keep the config options at minimum and for debugging
features it should be fine under BTRFS_DEBUG. If this needs to be
configured for performance reasons then it can be a mount option, same
as we have ref-verify or fragment already.

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

* Re: [PATCH] btrfs: implement ref_tracker for delayed_nodes
  2025-07-11 10:38   ` David Sterba
@ 2025-07-23 17:44     ` Leo Martins
  2025-07-23 18:04       ` Filipe Manana
  0 siblings, 1 reply; 7+ messages in thread
From: Leo Martins @ 2025-07-23 17:44 UTC (permalink / raw)
  To: David Sterba; +Cc: Filipe Manana, linux-btrfs, kernel-team, jlayton

On Fri, 11 Jul 2025 12:38:53 +0200 David Sterba <dsterba@suse.cz> wrote:

> On Thu, Jul 10, 2025 at 12:54:34PM +0100, Filipe Manana wrote:
> > > @@ -63,10 +109,18 @@ struct btrfs_delayed_node {
> > >         struct rb_root_cached del_root;
> > >         struct mutex mutex;
> > >         struct btrfs_inode_item inode_item;
> > > +
> > >         refcount_t refs;
> > > -       int count;
> > > +       /* Used to track all references to this delayed node. */
> > > +       btrfs_delayed_node_ref_tracker_dir ref_dir;
> > > +       /* Used to track delayed node reference stored in node list. */
> > > +       btrfs_delayed_node_ref_tracker node_list_tracker;
> > > +       /* Used to track delayed node reference stored in inode cache. */
> > > +       btrfs_delayed_node_ref_tracker inode_cache_tracker;
> > > +
> > >         u64 index_cnt;
> > >         unsigned long flags;
> > > +       int count;
> > 
> > Why are you moving the count field?
> > This increases the size of the struct even without the new config
> > option enabled and as a result we get less objects per page.
> > You are essentially reverting this commit:
> > 
> > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a20725e1e7014642fc8ba4c7dd2c4ef6d4ec56a9
> > 
> > Without any explanation about why.

Good catch, did not realize that moving the count field increased
struct size.

> > 
> > I'm also not a big fan of the typedefs and would prefer to have
> > struct ref_tracker used directly in the structure surrounded by an
> > #ifdef block.
> 
> Agreed, we use typedefs only for function callbacks, for variable types
> not anymore.

When I try and not use typedefs I end up using more conditional compilation
which I think hinders readability. Here's a snippet of my best attempt, let
me know if you see any improvements.

```C
struct btrfs_delayed_node {
	...
#ifdef CONFIG_BTRFS_DEBUG
	struct ref_tracker_dir ref_dir;
	struct ref_tracker *node_list_tracker;
	struct ref_tracker *inode_cache_tracker;
#endif
	...
}

#ifdef
static inline void
btrfs_release_delayed_node(struct btrfs_delayed_node *node,
			   struct ref_tracker **tracker)
{
	__btrfs_release_delayed_node(node, 0, tracker);
}

void btrfs_remove_delayed_node(struct btrfs_inode *inode)
{
	struct btrfs_delayed_node *delayed_node;

	delayed_node = READ_ONCE(inode->delayed_node);
	if (!delayed_node)
		return;

	inode->delayed_node = NULL;
#ifdef CONFIG_BTRFS_DEBUG
	btrfs_release_delayed_node(delayed_node,
				   &delayed_node->inode_cache_tracker);
#else
	btrfs_release_delayed_node(delayed_node, NULL);
#endif
}
```

With this approach I end up needing to add conditional compilation
anywhere I use node_list_tracker or inode_cache_tracker. This isn't
too big of an issue since their usage is relatively limited.
However, I believe this would be an appropriate place for typedefs.

Alternatively, I could have two versions of btrfs_release_delayed_node
one that takes a struct ref_tracker ** and one that takes a struct {}.
However, this would mean doing the same for all the functions that
take a tracker, btrfs_get_delayed_node, btrfs_get_or_create_delayed_node,
... But, I feel like that is the same as manually unwinding the typedef.

Any guidance would be greatly appreciated! Also curious about the aversion
to typedefs outside of function callbacks?

> 
> > I also don't like adding a new Kconfig option just for this... How
> > much slower would a build with CONFIG_BTRFS_DEBUG=y be?
> > 
> > If that's really a lot slower, then perhaps a more generic config
> > option name in case we add similar tracking to other data structures
> > one day.

I gathered some data here using fsperf and got some mixed results, most
tests performed slightly worse, and some got much worse
(dbench60, emptyfiles500k). I think it would be acceptible to keep in
CONFIG_BTRFS_DEBUG, but I think it could definitely benefit from
its own option, CONFIG_BTRFS_REF_TRACKER.

> 
> Right, we want to keep the config options at minimum and for debugging
> features it should be fine under BTRFS_DEBUG. If this needs to be
> configured for performance reasons then it can be a mount option, same
> as we have ref-verify or fragment already.

If we wanted to allow turning ref_tracker on via mount option we would
not be able to compile out the ref_tracker objects in delayed_node which
would cause a change in size from 304 bytes to 400 bytes.

baseline represents CONFIG_BTRFS_DEBUG=y without ref_tracker
current represents CONFIG_BTRFS_DEBUG=y with ref_tracker
** means 2*stdev worse
```
[root@localhost fsperf]# ./fsperf -p "baseline" -n 5
...
<enable ref_tracker>
[root@localhost fsperf]# ./fsperf -p "tracker" -n 5
...
[root@localhost fsperf]# ./fsperf-compare "baseline" "tracker"
['btrfs']
btrfs test results
bufferedappenddatasync results
      metric           baseline      current       stdev            diff
==============================================================================
avg_commit_ms               1.91          1.85         0.13    -3.23%
bg_count                    4.60          4.40         0.55    -4.35%
commits                       20         21.20         2.12     6.00%
dev_read_iops          262187.60     262190.40         8.32     0.00%
dev_read_kbytes          1020.80          1040        42.93     1.88%
dev_write_iops        3129696.80       3126160      2909.20    -0.11%
dev_write_kbytes        43783448   43726868.80     46497.53    -0.13%
elapsed                   301.60        300.60         9.29    -0.33%
end_state_mount_ns      46733159      44610208   3503084.15    -4.54%
end_state_umount_ns     2.77e+09      2.58e+09     2.17e+08    -6.65%
max_commit_ms               2.80          2.40         0.84   -14.29%
sys_cpu                    61.80         61.69         0.54    -0.17%
write_bw_bytes           3572197    3584941.20    106296.35     0.36%
write_clat_ns_mean      29694.09      29299.92      4510.28    -1.33%
write_clat_ns_p50       28262.40      27955.20      4380.52    -1.09%
write_clat_ns_p99       48793.60      48281.60      7226.28    -1.05%
write_io_kbytes          1048576       1048576            0     0.00%
write_iops                872.12        875.23        25.95     0.36%
write_lat_ns_max       764860.40        634311    354679.35   -17.07%
write_lat_ns_mean       29981.52      29539.17      4518.22    -1.48%
write_lat_ns_min        21404.80      21060.40       315.78    -1.61%

bufferedrandwrite16g results
      metric           baseline       current       stdev            diff
===============================================================================
avg_commit_ms              794.59        773.06        27.11    -2.71%
commits                     83.20         79.80         2.05    -4.09%
dev_read_iops              658.60        521.20       335.31   -20.86%
dev_read_kbytes           9478.40       7334.40      5341.58   -22.62%
dev_write_iops         5801247.80    5721033.20     53643.07    -1.38%
dev_write_kbytes      44303422.40   43038694.40    853851.59    -2.85%
elapsed                       360           347        21.94    -3.61%
end_state_mount_ns    58480927.20   59131105.80    873330.39     1.11%
end_state_umount_ns      9.10e+09      8.71e+09     3.41e+08    -4.33%
max_commit_ms                1624       1630.60        53.77     0.41%
sys_cpu                     35.52         35.88         1.25     1.00%
write_bw_bytes        47983181.20      49673467   2727170.05     3.52%
write_clat_ns_mean       83559.03      80805.49      5123.88    -3.30%
write_clat_ns_p50        27622.40      27417.60       879.39    -0.74%
write_clat_ns_p99       152166.40     171417.60      9864.47    12.65%
write_io_kbytes          16777216      16777216            0     0.00%
write_iops               11714.64      12127.31       665.81     3.52%
write_lat_ns_max         2.93e+09      2.21e+09     1.41e+09   -24.81%
write_lat_ns_mean        83674.85      80915.46      5128.53    -3.30%
write_lat_ns_min         16718.40      16552.40       455.01    -0.99%

dbench60 results
      metric           baseline     current       stdev            diff
=============================================================================
avg_commit_ms               31.72      33.63          1.24     6.04%
bg_count                        4          4             0     0.00%
close                        4.32       6.06          0.54    40.36%**
commits                        24         24             0     0.00%
deltree                    526.48     610.32         31.73    15.92%**
dev_read_iops            75597.20      65111       1808.47   -13.87%**
dev_read_kbytes              1040       1040             0     0.00%
dev_write_iops         1545476.80    1347859      52326.80   -12.79%**
dev_write_kbytes      18026305.60   15533764     691195.60   -13.83%**
end_state_mount_ns    48171843.60   45794087    2671959.83    -4.94%
end_state_umount_ns      1.85e+09   1.85e+09   41447992.49    -0.14%
find                        29.76      41.82          2.56    40.52%**
flush                      122.33     111.94         19.45    -8.49%
lockx                        0.95       1.21          0.45    27.55%
max_commit_ms               38.40      45.40          2.30    18.23%**
mkdir                        0.08       0.18          0.04   129.35%**
ntcreatex                   35.41      48.84          3.60    37.95%**
qfileinfo                    3.52       3.31          1.12    -6.11%
qfsinfo                      4.25       4.46          0.33     4.95%
qpathinfo                   25.30      32.56          3.72    28.68%
readx                        7.56       7.97          1.92     5.49%
rename                      50.33      50.86         12.36     1.06%
sfileinfo                    9.17       8.60          1.67    -6.22%
throughput                 315.59     282.74         10.32   -10.41%**
unlink                      44.87      54.50          6.19    21.44%
unlockx                      0.75       2.19          0.37   191.39%**
writex                      98.36      92.78         17.51    -5.68%

dio4kbs16threads results
      metric           baseline       current        stdev            diff
================================================================================
avg_commit_ms             2339.20       1071.20        975.30   -54.21%
bg_count                       20            20             0     0.00%
commits                         1             1             0     0.00%
dev_read_iops                  48            48             0     0.00%
dev_read_kbytes              1040          1040             0     0.00%
dev_write_iops         1105312.80       1070254      11754.77    -3.17%**
dev_write_kbytes       4919395.20    4815462.40      57354.39    -2.11%
elapsed                        61            61             0     0.00%
end_state_mount_ns    50869016.60   50826535.40    1496475.65    -0.08%
end_state_umount_ns      3.02e+09      2.99e+09   43691601.05    -0.97%
frag_pct_max                98.38         98.43          0.55     0.05%
frag_pct_mean               51.91         51.61          0.27    -0.59%
frag_pct_min                 5.45          4.79          0.29   -12.12%
frag_pct_p50                51.91         51.61          0.27    -0.59%
frag_pct_p95                98.38         98.43          0.55     0.05%
frag_pct_p99                98.38         98.43          0.55     0.05%
fragmented_bg_count             2             2             0     0.00%
max_commit_ms             2339.20       1071.20        975.30   -54.21%
sys_cpu                     18.11         17.24          0.06    -4.78%
write_bw_bytes           72547808   69915896.40     851270.12    -3.63%**
write_clat_ns_mean      900096.95     933821.96      10417.71     3.75%**
write_clat_ns_p50       400179.20     394444.80       4486.94    -1.43%
write_clat_ns_p99      2657484.80       2867200      37361.27     7.89%**
write_io_kbytes        4255195.20    4102859.20      48123.74    -3.58%**
write_iops               17711.87      17069.31        207.83    -3.63%**
write_lat_ns_max         9.19e+08      9.59e+08      1.02e+08     4.37%
write_lat_ns_mean       900449.84     934178.51      10411.01     3.75%**
write_lat_ns_min        148662.20     149014.40       3485.37     0.24%

diorandread results
      metric           baseline       current        stdev            diff
================================================================================
avg_commit_ms                5.31          5.49          0.08     3.35%**
bg_count                       18            18             0     0.00%
commits                        18            18             0     0.00%
dev_read_iops          2755803.60    2722414.60      23232.33    -1.21%
dev_read_kbytes       11022186.40   10888630.40      92929.32    -1.21%
dev_write_iops           41688.20      42010.80        312.31     0.77%
dev_write_kbytes         16897548      16897548             0     0.00%
elapsed                        61            61             0     0.00%
end_state_mount_ns    47309004.80   49367506.20    1371322.67     4.35%
end_state_umount_ns      1.07e+09      1.07e+09    6066020.04    -0.10%
max_commit_ms                6.60          6.60          0.55     0.00%
read_bw_bytes            1.88e+08      1.86e+08    1587441.01    -1.21%
read_clat_ns_mean       345830.30     350152.25       2831.51     1.25%
read_clat_ns_p50        327270.40     331366.40       2243.47     1.25%
read_clat_ns_p99        647987.20     656179.20       9340.32     1.26%
read_io_bytes            1.13e+10      1.11e+10   95159625.71    -1.21%
read_io_kbytes        11022186.40   10888630.40      92929.32    -1.21%
read_iops                45924.71      45368.09        387.56    -1.21%
read_lat_ns_max           4530461   13372441.20    2563283.41   195.17%**
read_lat_ns_mean        346044.45     350359.97       2846.68     1.25%
read_lat_ns_min         138696.20        134640       4341.30    -2.92%
sys_cpu                     24.22         24.16          0.21    -0.23%

emptyfiles500k results
      metric           baseline       current        stdev           diff
===============================================================================
avg_commit_ms               26.60         24.20          5.13   -9.02%
bg_count                        3             3             0    0.00%
commits                         1             1             0    0.00%
dev_read_iops                  48            48             0    0.00%
dev_read_kbytes              1040          1040             0    0.00%
dev_write_iops             842.80           842         15.27   -0.09%
dev_write_kbytes         13460.80         13448        244.33   -0.10%
elapsed                     42.20         42.20          0.84    0.00%
end_state_mount_ns    48019710.20   47157285.80    2091327.38   -1.80%
end_state_umount_ns      1.46e+09      1.46e+09   26640049.48    0.04%
max_commit_ms               26.60         24.20          5.13   -9.02%
sys_cpu                      5.47          7.76          0.18   41.97%**
write_bw_bytes         3077613.60    3073469.80      66469.32   -0.13%
write_clat_ns_mean       67202.18      98477.96       1091.86   46.54%**
write_clat_ns_p50        63027.20      93900.80       1061.71   48.98%**
write_clat_ns_p99       135270.40     169779.20       4165.81   25.51%**
write_io_kbytes            125000        125000             0    0.00%
write_iops                 751.37        750.36         16.23   -0.13%

randwrite2xram results
      metric           baseline       current       stdev           diff
==============================================================================
avg_commit_ms              872.44        847.24        13.74   -2.89%
commits                        60         71.40         4.85   19.00%**
dev_read_iops              630.40       1092.60       240.86   73.32%
dev_read_kbytes           9414.40      16627.20      3800.10   76.61%
dev_write_iops         5119929.40    5187028.80    123210.86    1.31%
dev_write_kbytes      37392001.60   39529271.20   1840547.09    5.72%
elapsed                    312.80        312.20         0.84   -0.19%
end_state_mount_ns    58923162.40   58027381.60   2021748.73   -1.52%
end_state_umount_ns      1.06e+10      1.07e+10     5.74e+08    0.87%
max_commit_ms             1608.60       1549.20       116.36   -3.69%
sys_cpu                      9.83          9.63         0.10   -2.02%
write_bw_bytes        48972662.80   47887587.20    400536.11   -2.22%**
write_clat_ns_mean      331763.74     339356.86      2837.07    2.29%**
write_clat_ns_p50        29721.60      30028.80       642.55    1.03%
write_clat_ns_p99       297164.80        329728     34486.16   10.96%
write_io_kbytes       14378941.60   14054288.80    129599.67   -2.26%**
write_iops               11956.22      11691.31        97.79   -2.22%**
write_lat_ns_max         2.24e+09      2.23e+09     1.17e+08   -0.41%
write_lat_ns_mean       331873.49     339472.07      2838.23    2.29%**
write_lat_ns_min         18096.40      17637.40       772.99   -2.54%

smallfiles100k results
      metric           baseline       current        stdev           diff
===============================================================================
avg_commit_ms               71.00         76.99          3.72    8.44%
bg_count                       19            19             0    0.00%
commits                     12.40         12.60          1.52    1.61%
dev_read_iops                 111        111.80          9.82    0.72%
dev_read_kbytes           1865.60       1875.20        166.89    0.51%
dev_write_iops         2183867.80    2183585.60      12589.22   -0.01%
dev_write_kbytes      21161021.60   21168306.40      71548.04    0.03%
elapsed                    390.40        391.40         47.79    0.26%
end_state_mount_ns    42532681.60   42288602.40    1686619.50   -0.57%
end_state_umount_ns      7.44e+09      8.06e+09      4.25e+08    8.30%
max_commit_ms              132.20           135          9.83    2.12%
sys_cpu                     39.03         39.36          4.34    0.86%
write_bw_bytes           5.46e+08      5.45e+08   58484454.42   -0.16%
write_clat_ns_mean       12794.53      12769.69       3097.83   -0.19%
write_clat_ns_p50        11315.20         11264       3334.89   -0.45%
write_clat_ns_p99        27545.60      27494.40       5067.25   -0.19%
write_io_kbytes          2.04e+08      2.04e+08             0    0.00%
write_iops              133271.81     133057.37      14278.43   -0.16%
write_lat_ns_max         34433966   34705517.60    4302608.84    0.79%
write_lat_ns_mean        12881.08      12859.97       3106.37   -0.16%
write_lat_ns_min             5946       5814.40        204.95   -2.21%
```

Sent using hkml (https://github.com/sjp38/hackermail)

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

* Re: [PATCH] btrfs: implement ref_tracker for delayed_nodes
  2025-07-23 17:44     ` Leo Martins
@ 2025-07-23 18:04       ` Filipe Manana
  0 siblings, 0 replies; 7+ messages in thread
From: Filipe Manana @ 2025-07-23 18:04 UTC (permalink / raw)
  To: Leo Martins; +Cc: David Sterba, linux-btrfs, kernel-team, jlayton

On Wed, Jul 23, 2025 at 6:44 PM Leo Martins <loemra.dev@gmail.com> wrote:
>
> On Fri, 11 Jul 2025 12:38:53 +0200 David Sterba <dsterba@suse.cz> wrote:
>
> > On Thu, Jul 10, 2025 at 12:54:34PM +0100, Filipe Manana wrote:
> > > > @@ -63,10 +109,18 @@ struct btrfs_delayed_node {
> > > >         struct rb_root_cached del_root;
> > > >         struct mutex mutex;
> > > >         struct btrfs_inode_item inode_item;
> > > > +
> > > >         refcount_t refs;
> > > > -       int count;
> > > > +       /* Used to track all references to this delayed node. */
> > > > +       btrfs_delayed_node_ref_tracker_dir ref_dir;
> > > > +       /* Used to track delayed node reference stored in node list. */
> > > > +       btrfs_delayed_node_ref_tracker node_list_tracker;
> > > > +       /* Used to track delayed node reference stored in inode cache. */
> > > > +       btrfs_delayed_node_ref_tracker inode_cache_tracker;
> > > > +
> > > >         u64 index_cnt;
> > > >         unsigned long flags;
> > > > +       int count;
> > >
> > > Why are you moving the count field?
> > > This increases the size of the struct even without the new config
> > > option enabled and as a result we get less objects per page.
> > > You are essentially reverting this commit:
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a20725e1e7014642fc8ba4c7dd2c4ef6d4ec56a9
> > >
> > > Without any explanation about why.
>
> Good catch, did not realize that moving the count field increased
> struct size.

For structures of things we can have many instances of, it's always
important to check if the structure size changes.
In this case it was puzzling why the field was moved when there's no
need to do it all for this patch and no mention in the changelog.

>
> > >
> > > I'm also not a big fan of the typedefs and would prefer to have
> > > struct ref_tracker used directly in the structure surrounded by an
> > > #ifdef block.
> >
> > Agreed, we use typedefs only for function callbacks, for variable types
> > not anymore.
>
> When I try and not use typedefs I end up using more conditional compilation
> which I think hinders readability. Here's a snippet of my best attempt, let
> me know if you see any improvements.
>
> ```C
> struct btrfs_delayed_node {
>         ...
> #ifdef CONFIG_BTRFS_DEBUG
>         struct ref_tracker_dir ref_dir;
>         struct ref_tracker *node_list_tracker;
>         struct ref_tracker *inode_cache_tracker;
> #endif
>         ...
> }
>
> #ifdef
> static inline void
> btrfs_release_delayed_node(struct btrfs_delayed_node *node,
>                            struct ref_tracker **tracker)
> {
>         __btrfs_release_delayed_node(node, 0, tracker);
> }
>
> void btrfs_remove_delayed_node(struct btrfs_inode *inode)
> {
>         struct btrfs_delayed_node *delayed_node;
>
>         delayed_node = READ_ONCE(inode->delayed_node);
>         if (!delayed_node)
>                 return;
>
>         inode->delayed_node = NULL;
> #ifdef CONFIG_BTRFS_DEBUG
>         btrfs_release_delayed_node(delayed_node,
>                                    &delayed_node->inode_cache_tracker);
> #else
>         btrfs_release_delayed_node(delayed_node, NULL);
> #endif
> }
> ```
>
> With this approach I end up needing to add conditional compilation
> anywhere I use node_list_tracker or inode_cache_tracker. This isn't
> too big of an issue since their usage is relatively limited.
> However, I believe this would be an appropriate place for typedefs.
>
> Alternatively, I could have two versions of btrfs_release_delayed_node
> one that takes a struct ref_tracker ** and one that takes a struct {}.
> However, this would mean doing the same for all the functions that
> take a tracker, btrfs_get_delayed_node, btrfs_get_or_create_delayed_node,
> ... But, I feel like that is the same as manually unwinding the typedef.
>
> Any guidance would be greatly appreciated! Also curious about the aversion
> to typedefs outside of function callbacks?

https://www.kernel.org/doc/html/v4.14/process/coding-style.html#typedefs

>
> >
> > > I also don't like adding a new Kconfig option just for this... How
> > > much slower would a build with CONFIG_BTRFS_DEBUG=y be?
> > >
> > > If that's really a lot slower, then perhaps a more generic config
> > > option name in case we add similar tracking to other data structures
> > > one day.
>
> I gathered some data here using fsperf and got some mixed results, most
> tests performed slightly worse, and some got much worse

Running fsperf and benchmarks tools is not the most important for
development IMO.
While developing we normally use CONFIG_BTRFS_DEBUG=y and the most
important thing is how much slower fstests become (or anything else
used for functional tests).

When we really want to test for performance, we do it so with
CONFIG_BTRFS_DEBUG disabled and then use fsperf and other tools -
users/distros don't tend to have a kernel config with
CONFIG_BTRFS_DEBUG=y.


> (dbench60, emptyfiles500k). I think it would be acceptible to keep in
> CONFIG_BTRFS_DEBUG, but I think it could definitely benefit from
> its own option, CONFIG_BTRFS_REF_TRACKER.
>
> >
> > Right, we want to keep the config options at minimum and for debugging
> > features it should be fine under BTRFS_DEBUG. If this needs to be
> > configured for performance reasons then it can be a mount option, same
> > as we have ref-verify or fragment already.
>
> If we wanted to allow turning ref_tracker on via mount option we would
> not be able to compile out the ref_tracker objects in delayed_node which
> would cause a change in size from 304 bytes to 400 bytes.
>
> baseline represents CONFIG_BTRFS_DEBUG=y without ref_tracker
> current represents CONFIG_BTRFS_DEBUG=y with ref_tracker
> ** means 2*stdev worse
> ```
> [root@localhost fsperf]# ./fsperf -p "baseline" -n 5
> ...
> <enable ref_tracker>
> [root@localhost fsperf]# ./fsperf -p "tracker" -n 5
> ...
> [root@localhost fsperf]# ./fsperf-compare "baseline" "tracker"
> ['btrfs']
> btrfs test results
> bufferedappenddatasync results
>       metric           baseline      current       stdev            diff
> ==============================================================================
> avg_commit_ms               1.91          1.85         0.13    -3.23%
> bg_count                    4.60          4.40         0.55    -4.35%
> commits                       20         21.20         2.12     6.00%
> dev_read_iops          262187.60     262190.40         8.32     0.00%
> dev_read_kbytes          1020.80          1040        42.93     1.88%
> dev_write_iops        3129696.80       3126160      2909.20    -0.11%
> dev_write_kbytes        43783448   43726868.80     46497.53    -0.13%
> elapsed                   301.60        300.60         9.29    -0.33%
> end_state_mount_ns      46733159      44610208   3503084.15    -4.54%
> end_state_umount_ns     2.77e+09      2.58e+09     2.17e+08    -6.65%
> max_commit_ms               2.80          2.40         0.84   -14.29%
> sys_cpu                    61.80         61.69         0.54    -0.17%
> write_bw_bytes           3572197    3584941.20    106296.35     0.36%
> write_clat_ns_mean      29694.09      29299.92      4510.28    -1.33%
> write_clat_ns_p50       28262.40      27955.20      4380.52    -1.09%
> write_clat_ns_p99       48793.60      48281.60      7226.28    -1.05%
> write_io_kbytes          1048576       1048576            0     0.00%
> write_iops                872.12        875.23        25.95     0.36%
> write_lat_ns_max       764860.40        634311    354679.35   -17.07%
> write_lat_ns_mean       29981.52      29539.17      4518.22    -1.48%
> write_lat_ns_min        21404.80      21060.40       315.78    -1.61%
>
> bufferedrandwrite16g results
>       metric           baseline       current       stdev            diff
> ===============================================================================
> avg_commit_ms              794.59        773.06        27.11    -2.71%
> commits                     83.20         79.80         2.05    -4.09%
> dev_read_iops              658.60        521.20       335.31   -20.86%
> dev_read_kbytes           9478.40       7334.40      5341.58   -22.62%
> dev_write_iops         5801247.80    5721033.20     53643.07    -1.38%
> dev_write_kbytes      44303422.40   43038694.40    853851.59    -2.85%
> elapsed                       360           347        21.94    -3.61%
> end_state_mount_ns    58480927.20   59131105.80    873330.39     1.11%
> end_state_umount_ns      9.10e+09      8.71e+09     3.41e+08    -4.33%
> max_commit_ms                1624       1630.60        53.77     0.41%
> sys_cpu                     35.52         35.88         1.25     1.00%
> write_bw_bytes        47983181.20      49673467   2727170.05     3.52%
> write_clat_ns_mean       83559.03      80805.49      5123.88    -3.30%
> write_clat_ns_p50        27622.40      27417.60       879.39    -0.74%
> write_clat_ns_p99       152166.40     171417.60      9864.47    12.65%
> write_io_kbytes          16777216      16777216            0     0.00%
> write_iops               11714.64      12127.31       665.81     3.52%
> write_lat_ns_max         2.93e+09      2.21e+09     1.41e+09   -24.81%
> write_lat_ns_mean        83674.85      80915.46      5128.53    -3.30%
> write_lat_ns_min         16718.40      16552.40       455.01    -0.99%
>
> dbench60 results
>       metric           baseline     current       stdev            diff
> =============================================================================
> avg_commit_ms               31.72      33.63          1.24     6.04%
> bg_count                        4          4             0     0.00%
> close                        4.32       6.06          0.54    40.36%**
> commits                        24         24             0     0.00%
> deltree                    526.48     610.32         31.73    15.92%**
> dev_read_iops            75597.20      65111       1808.47   -13.87%**
> dev_read_kbytes              1040       1040             0     0.00%
> dev_write_iops         1545476.80    1347859      52326.80   -12.79%**
> dev_write_kbytes      18026305.60   15533764     691195.60   -13.83%**
> end_state_mount_ns    48171843.60   45794087    2671959.83    -4.94%
> end_state_umount_ns      1.85e+09   1.85e+09   41447992.49    -0.14%
> find                        29.76      41.82          2.56    40.52%**
> flush                      122.33     111.94         19.45    -8.49%
> lockx                        0.95       1.21          0.45    27.55%
> max_commit_ms               38.40      45.40          2.30    18.23%**
> mkdir                        0.08       0.18          0.04   129.35%**
> ntcreatex                   35.41      48.84          3.60    37.95%**
> qfileinfo                    3.52       3.31          1.12    -6.11%
> qfsinfo                      4.25       4.46          0.33     4.95%
> qpathinfo                   25.30      32.56          3.72    28.68%
> readx                        7.56       7.97          1.92     5.49%
> rename                      50.33      50.86         12.36     1.06%
> sfileinfo                    9.17       8.60          1.67    -6.22%
> throughput                 315.59     282.74         10.32   -10.41%**
> unlink                      44.87      54.50          6.19    21.44%
> unlockx                      0.75       2.19          0.37   191.39%**
> writex                      98.36      92.78         17.51    -5.68%
>
> dio4kbs16threads results
>       metric           baseline       current        stdev            diff
> ================================================================================
> avg_commit_ms             2339.20       1071.20        975.30   -54.21%
> bg_count                       20            20             0     0.00%
> commits                         1             1             0     0.00%
> dev_read_iops                  48            48             0     0.00%
> dev_read_kbytes              1040          1040             0     0.00%
> dev_write_iops         1105312.80       1070254      11754.77    -3.17%**
> dev_write_kbytes       4919395.20    4815462.40      57354.39    -2.11%
> elapsed                        61            61             0     0.00%
> end_state_mount_ns    50869016.60   50826535.40    1496475.65    -0.08%
> end_state_umount_ns      3.02e+09      2.99e+09   43691601.05    -0.97%
> frag_pct_max                98.38         98.43          0.55     0.05%
> frag_pct_mean               51.91         51.61          0.27    -0.59%
> frag_pct_min                 5.45          4.79          0.29   -12.12%
> frag_pct_p50                51.91         51.61          0.27    -0.59%
> frag_pct_p95                98.38         98.43          0.55     0.05%
> frag_pct_p99                98.38         98.43          0.55     0.05%
> fragmented_bg_count             2             2             0     0.00%
> max_commit_ms             2339.20       1071.20        975.30   -54.21%
> sys_cpu                     18.11         17.24          0.06    -4.78%
> write_bw_bytes           72547808   69915896.40     851270.12    -3.63%**
> write_clat_ns_mean      900096.95     933821.96      10417.71     3.75%**
> write_clat_ns_p50       400179.20     394444.80       4486.94    -1.43%
> write_clat_ns_p99      2657484.80       2867200      37361.27     7.89%**
> write_io_kbytes        4255195.20    4102859.20      48123.74    -3.58%**
> write_iops               17711.87      17069.31        207.83    -3.63%**
> write_lat_ns_max         9.19e+08      9.59e+08      1.02e+08     4.37%
> write_lat_ns_mean       900449.84     934178.51      10411.01     3.75%**
> write_lat_ns_min        148662.20     149014.40       3485.37     0.24%
>
> diorandread results
>       metric           baseline       current        stdev            diff
> ================================================================================
> avg_commit_ms                5.31          5.49          0.08     3.35%**
> bg_count                       18            18             0     0.00%
> commits                        18            18             0     0.00%
> dev_read_iops          2755803.60    2722414.60      23232.33    -1.21%
> dev_read_kbytes       11022186.40   10888630.40      92929.32    -1.21%
> dev_write_iops           41688.20      42010.80        312.31     0.77%
> dev_write_kbytes         16897548      16897548             0     0.00%
> elapsed                        61            61             0     0.00%
> end_state_mount_ns    47309004.80   49367506.20    1371322.67     4.35%
> end_state_umount_ns      1.07e+09      1.07e+09    6066020.04    -0.10%
> max_commit_ms                6.60          6.60          0.55     0.00%
> read_bw_bytes            1.88e+08      1.86e+08    1587441.01    -1.21%
> read_clat_ns_mean       345830.30     350152.25       2831.51     1.25%
> read_clat_ns_p50        327270.40     331366.40       2243.47     1.25%
> read_clat_ns_p99        647987.20     656179.20       9340.32     1.26%
> read_io_bytes            1.13e+10      1.11e+10   95159625.71    -1.21%
> read_io_kbytes        11022186.40   10888630.40      92929.32    -1.21%
> read_iops                45924.71      45368.09        387.56    -1.21%
> read_lat_ns_max           4530461   13372441.20    2563283.41   195.17%**
> read_lat_ns_mean        346044.45     350359.97       2846.68     1.25%
> read_lat_ns_min         138696.20        134640       4341.30    -2.92%
> sys_cpu                     24.22         24.16          0.21    -0.23%
>
> emptyfiles500k results
>       metric           baseline       current        stdev           diff
> ===============================================================================
> avg_commit_ms               26.60         24.20          5.13   -9.02%
> bg_count                        3             3             0    0.00%
> commits                         1             1             0    0.00%
> dev_read_iops                  48            48             0    0.00%
> dev_read_kbytes              1040          1040             0    0.00%
> dev_write_iops             842.80           842         15.27   -0.09%
> dev_write_kbytes         13460.80         13448        244.33   -0.10%
> elapsed                     42.20         42.20          0.84    0.00%
> end_state_mount_ns    48019710.20   47157285.80    2091327.38   -1.80%
> end_state_umount_ns      1.46e+09      1.46e+09   26640049.48    0.04%
> max_commit_ms               26.60         24.20          5.13   -9.02%
> sys_cpu                      5.47          7.76          0.18   41.97%**
> write_bw_bytes         3077613.60    3073469.80      66469.32   -0.13%
> write_clat_ns_mean       67202.18      98477.96       1091.86   46.54%**
> write_clat_ns_p50        63027.20      93900.80       1061.71   48.98%**
> write_clat_ns_p99       135270.40     169779.20       4165.81   25.51%**
> write_io_kbytes            125000        125000             0    0.00%
> write_iops                 751.37        750.36         16.23   -0.13%
>
> randwrite2xram results
>       metric           baseline       current       stdev           diff
> ==============================================================================
> avg_commit_ms              872.44        847.24        13.74   -2.89%
> commits                        60         71.40         4.85   19.00%**
> dev_read_iops              630.40       1092.60       240.86   73.32%
> dev_read_kbytes           9414.40      16627.20      3800.10   76.61%
> dev_write_iops         5119929.40    5187028.80    123210.86    1.31%
> dev_write_kbytes      37392001.60   39529271.20   1840547.09    5.72%
> elapsed                    312.80        312.20         0.84   -0.19%
> end_state_mount_ns    58923162.40   58027381.60   2021748.73   -1.52%
> end_state_umount_ns      1.06e+10      1.07e+10     5.74e+08    0.87%
> max_commit_ms             1608.60       1549.20       116.36   -3.69%
> sys_cpu                      9.83          9.63         0.10   -2.02%
> write_bw_bytes        48972662.80   47887587.20    400536.11   -2.22%**
> write_clat_ns_mean      331763.74     339356.86      2837.07    2.29%**
> write_clat_ns_p50        29721.60      30028.80       642.55    1.03%
> write_clat_ns_p99       297164.80        329728     34486.16   10.96%
> write_io_kbytes       14378941.60   14054288.80    129599.67   -2.26%**
> write_iops               11956.22      11691.31        97.79   -2.22%**
> write_lat_ns_max         2.24e+09      2.23e+09     1.17e+08   -0.41%
> write_lat_ns_mean       331873.49     339472.07      2838.23    2.29%**
> write_lat_ns_min         18096.40      17637.40       772.99   -2.54%
>
> smallfiles100k results
>       metric           baseline       current        stdev           diff
> ===============================================================================
> avg_commit_ms               71.00         76.99          3.72    8.44%
> bg_count                       19            19             0    0.00%
> commits                     12.40         12.60          1.52    1.61%
> dev_read_iops                 111        111.80          9.82    0.72%
> dev_read_kbytes           1865.60       1875.20        166.89    0.51%
> dev_write_iops         2183867.80    2183585.60      12589.22   -0.01%
> dev_write_kbytes      21161021.60   21168306.40      71548.04    0.03%
> elapsed                    390.40        391.40         47.79    0.26%
> end_state_mount_ns    42532681.60   42288602.40    1686619.50   -0.57%
> end_state_umount_ns      7.44e+09      8.06e+09      4.25e+08    8.30%
> max_commit_ms              132.20           135          9.83    2.12%
> sys_cpu                     39.03         39.36          4.34    0.86%
> write_bw_bytes           5.46e+08      5.45e+08   58484454.42   -0.16%
> write_clat_ns_mean       12794.53      12769.69       3097.83   -0.19%
> write_clat_ns_p50        11315.20         11264       3334.89   -0.45%
> write_clat_ns_p99        27545.60      27494.40       5067.25   -0.19%
> write_io_kbytes          2.04e+08      2.04e+08             0    0.00%
> write_iops              133271.81     133057.37      14278.43   -0.16%
> write_lat_ns_max         34433966   34705517.60    4302608.84    0.79%
> write_lat_ns_mean        12881.08      12859.97       3106.37   -0.16%
> write_lat_ns_min             5946       5814.40        204.95   -2.21%
> ```
>
> Sent using hkml (https://github.com/sjp38/hackermail)

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

end of thread, other threads:[~2025-07-23 18:05 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-09 22:04 [PATCH] btrfs: implement ref_tracker for delayed_nodes Leo Martins
2025-07-10 11:54 ` Filipe Manana
2025-07-11 10:38   ` David Sterba
2025-07-23 17:44     ` Leo Martins
2025-07-23 18:04       ` Filipe Manana
2025-07-10 23:49 ` kernel test robot
2025-07-11  3:42 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox