* [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