AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/02 08:50 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "AFS_FS"
  ],
  "FocusSymbols": [
    "afs_d_revalidate",
    "afs_d_revalidate_rcu",
    "afs_unlink",
    "afs_rename",
    "afs_lookup"
  ],
  "Reasoning": "The patch modifies AFS directory operations to use `d_time` instead of `d_fsdata` for storing the data version, and uses `d_fsdata` to block lookups and revalidations during rename and unlink operations. This is a functional change in the core AFS filesystem logic that handles concurrency and VFS interactions, which is reachable and should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/07/02 08:50 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit becf549230f7c04e86b8df9f2a6da3b2aa34a161\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Jul 2 08:50:55 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/fs/afs/afs.h b/fs/afs/afs.h\nindex ec3db00bd0813c..019e77b08458e3 100644\n--- a/fs/afs/afs.h\n+++ b/fs/afs/afs.h\n@@ -26,6 +26,13 @@ typedef u64\t\t\tafs_volid_t;\n typedef u64\t\t\tafs_vnodeid_t;\n typedef u64\t\t\tafs_dataversion_t;\n \n+/* This is stored in -\u003ed_fsdata to stop d_revalidate looking at,\n+ * and possibly changing, -\u003ed_time on a dentry which is being moved\n+ * between directories, and to block lookup for dentry that is\n+ * being removed without silly-rename.\n+ */\n+#define AFS_FSDATA_BLOCKED ((void*)1)\n+\n typedef enum {\n \tAFSVL_RWVOL,\t\t\t/* read/write volume */\n \tAFSVL_ROVOL,\t\t\t/* read-only volume */\ndiff --git a/fs/afs/dir.c b/fs/afs/dir.c\nindex 498b99ccdf0e27..e9aaf66218ea6b 100644\n--- a/fs/afs/dir.c\n+++ b/fs/afs/dir.c\n@@ -795,7 +795,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry)\n \t\tafs_dir_iterate(dir, \u0026cookie-\u003ectx, NULL, \u0026data_version);\n \t}\n \n-\tdentry-\u003ed_fsdata = (void *)(unsigned long)data_version;\n+\tdentry-\u003ed_time = (unsigned long)data_version;\n \n \t/* Check to see if we already have an inode for the primary fid. */\n \tinode = ilookup5(dir-\u003ei_sb, cookie-\u003efids[1].vnode,\n@@ -882,9 +882,9 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry)\n \t}\n \n \tif (op-\u003efile[0].scb.have_status)\n-\t\tdentry-\u003ed_fsdata = (void *)(unsigned long)op-\u003efile[0].scb.status.data_version;\n+\t\tdentry-\u003ed_time = (unsigned long)op-\u003efile[0].scb.status.data_version;\n \telse\n-\t\tdentry-\u003ed_fsdata = (void *)(unsigned long)op-\u003efile[0].dv_before;\n+\t\tdentry-\u003ed_time = (unsigned long)op-\u003efile[0].dv_before;\n \tret = afs_put_operation(op);\n out:\n \tkfree(cookie);\n@@ -997,7 +997,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,\n \t_debug(\"splice %p\", dentry-\u003ed_inode);\n \td = d_splice_alias(inode, dentry);\n \tif (!IS_ERR_OR_NULL(d)) {\n-\t\td-\u003ed_fsdata = dentry-\u003ed_fsdata;\n+\t\td-\u003ed_time = dentry-\u003ed_time;\n \t\ttrace_afs_lookup(dvnode, \u0026d-\u003ed_name, \u0026fid);\n \t} else {\n \t\ttrace_afs_lookup(dvnode, \u0026dentry-\u003ed_name, \u0026fid);\n@@ -1021,13 +1021,17 @@ static int afs_d_revalidate_rcu(struct afs_vnode *dvnode, struct dentry *dentry)\n \tif (!afs_check_validity(dvnode))\n \t\treturn -ECHILD;\n \n+\t/* A rename/unlink is pending */\n+\tif (dentry-\u003ed_fsdata)\n+\t\treturn -ECHILD;\n+\n \t/* We only need to invalidate a dentry if the server's copy changed\n \t * behind our back.  If we made the change, it's no problem.  Note that\n \t * on a 32-bit system, we only have 32 bits in the dentry to store the\n \t * version.\n \t */\n \tdir_version = (long)READ_ONCE(dvnode-\u003estatus.data_version);\n-\tde_version = (long)READ_ONCE(dentry-\u003ed_fsdata);\n+\tde_version = (long)READ_ONCE(dentry-\u003ed_time);\n \tif (de_version != dir_version) {\n \t\tdir_version = (long)READ_ONCE(dvnode-\u003einvalid_before);\n \t\tif (de_version - dir_version \u003c 0)\n@@ -1056,6 +1060,10 @@ static int afs_d_revalidate(struct inode *parent_dir, const struct qstr *name,\n \tif (flags \u0026 LOOKUP_RCU)\n \t\treturn afs_d_revalidate_rcu(dir, dentry);\n \n+\t/* Wait for rename/unlink to complete */\n+wait_for_rename:\n+\twait_var_event(\u0026dentry-\u003ed_fsdata, dentry-\u003ed_fsdata == NULL);\n+\n \tif (d_really_is_positive(dentry)) {\n \t\tvnode = AFS_FS_I(d_inode(dentry));\n \t\t_enter(\"{v={%llx:%llu} n=%pd fl=%lx},\",\n@@ -1087,7 +1095,7 @@ static int afs_d_revalidate(struct inode *parent_dir, const struct qstr *name,\n \t * version.\n \t */\n \tdir_version = dir-\u003estatus.data_version;\n-\tde_version = (long)dentry-\u003ed_fsdata;\n+\tde_version = (long)dentry-\u003ed_time;\n \tif (de_version == (long)dir_version)\n \t\tgoto out_valid_noupdate;\n \n@@ -1148,7 +1156,13 @@ static int afs_d_revalidate(struct inode *parent_dir, const struct qstr *name,\n \t}\n \n out_valid:\n-\tdentry-\u003ed_fsdata = (void *)(unsigned long)dir_version;\n+\tspin_lock(\u0026dentry-\u003ed_lock);\n+\tif (dentry-\u003ed_fsdata) {\n+\t\tspin_unlock(\u0026dentry-\u003ed_lock);\n+\t\tgoto wait_for_rename;\n+\t}\n+\tdentry-\u003ed_time = (unsigned long)dir_version;\n+\tspin_unlock(\u0026dentry-\u003ed_lock);\n out_valid_noupdate:\n \tkey_put(key);\n \t_leave(\" = 1 [valid]\");\n@@ -1523,8 +1537,7 @@ static void afs_unlink_edit_dir(struct afs_operation *op)\n static void afs_unlink_put(struct afs_operation *op)\n {\n \t_enter(\"op=%08x\", op-\u003edebug_id);\n-\tif (op-\u003eunlink.need_rehash \u0026\u0026 afs_op_error(op) \u003c 0 \u0026\u0026 afs_op_error(op) != -ENOENT)\n-\t\td_rehash(op-\u003edentry);\n+\tstore_release_wake_up(\u0026op-\u003edentry-\u003ed_fsdata, NULL);\n }\n \n static const struct afs_operation_ops afs_unlink_operation = {\n@@ -1578,11 +1591,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)\n \t\tafs_op_set_error(op, afs_sillyrename(dvnode, vnode, dentry, op-\u003ekey));\n \t\tgoto error;\n \t}\n-\tif (!d_unhashed(dentry)) {\n-\t\t/* Prevent a race with RCU lookup. */\n-\t\t__d_drop(dentry);\n-\t\top-\u003eunlink.need_rehash = true;\n-\t}\n+\tdentry-\u003ed_fsdata = AFS_FSDATA_BLOCKED;\n \tspin_unlock(\u0026dentry-\u003ed_lock);\n \n \top-\u003efile[1].vnode = vnode;\n@@ -1889,9 +1898,10 @@ static void afs_rename_edit_dir(struct afs_operation *op)\n \n \t_enter(\"op=%08x\", op-\u003edebug_id);\n \n-\tif (op-\u003erename.rehash) {\n-\t\td_rehash(op-\u003erename.rehash);\n-\t\top-\u003erename.rehash = NULL;\n+\tif (op-\u003erename.unblock) {\n+\t\t/* Rename has finished, so unlocks lookups to target */\n+\t\tstore_release_wake_up(\u0026op-\u003erename.unblock-\u003ed_fsdata, NULL);\n+\t\top-\u003erename.unblock = NULL;\n \t}\n \n \tfscache_begin_write_operation(\u0026orig_cres, afs_vnode_cache(orig_dvnode));\n@@ -1935,7 +1945,7 @@ static void afs_rename_edit_dir(struct afs_operation *op)\n \t\tspin_unlock(\u0026new_inode-\u003ei_lock);\n \t}\n \n-\t/* Now we can update d_fsdata on the dentries to reflect their\n+\t/* Now we can update d_time on the dentries to reflect their\n \t * new parent's data_version.\n \t */\n \tafs_update_dentry_version(op, new_dvp, op-\u003edentry);\n@@ -1974,6 +1984,9 @@ static void afs_rename_exchange_edit_dir(struct afs_operation *op)\n \n \t\td_exchange(old_dentry, new_dentry);\n \t\tup_write(\u0026orig_dvnode-\u003evalidate_lock);\n+\t/* dentry has been moved, so d_validate can safely proceed */\n+\tstore_release_wake_up(\u0026old_dentry-\u003ed_fsdata, NULL);\n+\n \t} else {\n \t\tdown_write(\u0026orig_dvnode-\u003evalidate_lock);\n \t\tif (test_bit(AFS_VNODE_DIR_VALID, \u0026orig_dvnode-\u003eflags) \u0026\u0026\n@@ -2013,11 +2026,10 @@ static void afs_rename_exchange_edit_dir(struct afs_operation *op)\n static void afs_rename_put(struct afs_operation *op)\n {\n \t_enter(\"op=%08x\", op-\u003edebug_id);\n-\tif (op-\u003erename.rehash)\n-\t\td_rehash(op-\u003erename.rehash);\n+\tif (op-\u003erename.unblock)\n+\t\tstore_release_wake_up(\u0026op-\u003erename.unblock-\u003ed_fsdata, NULL);\n+\tstore_release_wake_up(\u0026op-\u003edentry-\u003ed_fsdata, NULL);\n \tdput(op-\u003erename.tmp);\n-\tif (afs_op_error(op))\n-\t\td_rehash(op-\u003edentry);\n }\n \n static const struct afs_operation_ops afs_rename_operation = {\n@@ -2125,7 +2137,6 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,\n \t\top-\u003eops\t\t= \u0026afs_rename_noreplace_operation;\n \t} else if (flags \u0026 RENAME_EXCHANGE) {\n \t\top-\u003eops\t\t= \u0026afs_rename_exchange_operation;\n-\t\td_drop(new_dentry);\n \t} else {\n \t\t/* If we might displace the target, we might need to do silly\n \t\t * rename.\n@@ -2139,14 +2150,12 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,\n \t\t */\n \t\tif (d_is_positive(new_dentry) \u0026\u0026 !d_is_dir(new_dentry)) {\n \t\t\t/* To prevent any new references to the target during\n-\t\t\t * the rename, we unhash the dentry in advance.\n+\t\t\t * the rename, we set d_fsdata which afs_d_revalidate will wait for.\n+\t\t\t * d_lock ensures d_count() and -\u003ed_fsdata are consistent.\n \t\t\t */\n-\t\t\tif (!d_unhashed(new_dentry)) {\n-\t\t\t\td_drop(new_dentry);\n-\t\t\t\top-\u003erename.rehash = new_dentry;\n-\t\t\t}\n-\n+\t\t\tspin_lock(\u0026new_dentry-\u003ed_lock);\n \t\t\tif (d_count(new_dentry) \u003e 2) {\n+\t\t\t\tspin_unlock(\u0026new_dentry-\u003ed_lock);\n \t\t\t\t/* copy the target dentry's name */\n \t\t\t\top-\u003erename.tmp = d_alloc(new_dentry-\u003ed_parent,\n \t\t\t\t\t\t\t \u0026new_dentry-\u003ed_name);\n@@ -2164,22 +2173,27 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,\n \t\t\t\t}\n \n \t\t\t\top-\u003edentry_2 = op-\u003erename.tmp;\n-\t\t\t\top-\u003erename.rehash = NULL;\n \t\t\t\top-\u003erename.new_negative = true;\n+\t\t\t} else {\n+\t\t\t\t/* Block any lookups to target until the rename completes */\n+\t\t\t\tnew_dentry-\u003ed_fsdata = AFS_FSDATA_BLOCKED;\n+\t\t\t\top-\u003erename.unblock = new_dentry;\n+\t\t\t\tspin_unlock(\u0026new_dentry-\u003ed_lock);\n \t\t\t}\n \t\t}\n \t}\n \n \t/* This bit is potentially nasty as there's a potential race with\n-\t * afs_d_revalidate{,_rcu}().  We have to change d_fsdata on the dentry\n+\t * afs_d_revalidate{,_rcu}().  We have to change d_time_ on the dentry\n \t * to reflect it's new parent's new data_version after the op, but\n \t * d_revalidate may see old_dentry between the op having taken place\n \t * and the version being updated.\n \t *\n-\t * So drop the old_dentry for now to make other threads go through\n-\t * lookup instead - which we hold a lock against.\n+\t * So block revalidate on the old_dentry until the rename completes.\n \t */\n-\td_drop(old_dentry);\n+\tspin_lock(\u0026old_dentry-\u003ed_lock);\n+\told_dentry-\u003ed_fsdata = AFS_FSDATA_BLOCKED;\n+\tspin_unlock(\u0026old_dentry-\u003ed_lock);\n \n \tret = afs_do_sync_operation(op);\n \tif (ret == -ENOTSUPP)\ndiff --git a/fs/afs/internal.h b/fs/afs/internal.h\nindex 0b72a856629992..a0afe552ae646b 100644\n--- a/fs/afs/internal.h\n+++ b/fs/afs/internal.h\n@@ -900,10 +900,7 @@ struct afs_operation {\n \t\t\tstruct afs_symlink *symlink;\n \t\t} create;\n \t\tstruct {\n-\t\t\tbool\tneed_rehash;\n-\t\t} unlink;\n-\t\tstruct {\n-\t\t\tstruct dentry\t*rehash;\n+\t\t\tstruct dentry\t*unblock;\n \t\t\tstruct dentry\t*tmp;\n \t\t\tunsigned int\trename_flags;\n \t\t\tbool\t\tnew_negative;\n@@ -1764,17 +1761,17 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)\n }\n \n /*\n- * Note that a dentry got changed.  We need to set d_fsdata to the data version\n+ * Note that a dentry got changed.  We need to set d_time to the data version\n  * number derived from the result of the operation.  It doesn't matter if\n- * d_fsdata goes backwards as we'll just revalidate.\n+ * d_time goes backwards as we'll just revalidate.\n  */\n static inline void afs_update_dentry_version(struct afs_operation *op,\n \t\t\t\t\t     struct afs_vnode_param *dir_vp,\n \t\t\t\t\t     struct dentry *dentry)\n {\n \tif (!op-\u003ecumul_error.error)\n-\t\tdentry-\u003ed_fsdata =\n-\t\t\t(void *)(unsigned long)dir_vp-\u003escb.status.data_version;\n+\t\tdentry-\u003ed_time =\n+\t\t\t(unsigned long)dir_vp-\u003escb.status.data_version;\n }\n \n /*\n"
}

2/1 2026/07/02 08:50 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "AFS_FS"
  ],
  "FocusSymbols": [
    "afs_d_revalidate",
    "afs_d_revalidate_rcu",
    "afs_unlink",
    "afs_rename",
    "afs_lookup"
  ],
  "Reasoning": "The patch modifies AFS directory operations to use `d_time` instead of `d_fsdata` for storing the data version, and uses `d_fsdata` to block lookups and revalidations during rename and unlink operations. This is a functional change in the core AFS filesystem logic that handles concurrency and VFS interactions, which is reachable and should be fuzzed.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit becf549230f7c04e86b8df9f2a6da3b2aa34a161
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Jul 2 08:50:55 2026 +0000

    syz-cluster: applied patch under review

diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index ec3db00bd0813c..019e77b08458e3 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -26,6 +26,13 @@ typedef u64			afs_volid_t;
 typedef u64			afs_vnodeid_t;
 typedef u64			afs_dataversion_t;
 
+/* This is stored in ->d_fsdata to stop d_revalidate looking at,
+ * and possibly changing, ->d_time on a dentry which is being moved
+ * between directories, and to block lookup for dentry that is
+ * being removed without silly-rename.
+ */
+#define AFS_FSDATA_BLOCKED ((void*)1)
+
 typedef enum {
 	AFSVL_RWVOL,			/* read/write volume */
 	AFSVL_ROVOL,			/* read-only volume */
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 498b99ccdf0e27..e9aaf66218ea6b 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -795,7 +795,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry)
 		afs_dir_iterate(dir, &cookie->ctx, NULL, &data_version);
 	}
 
-	dentry->d_fsdata = (void *)(unsigned long)data_version;
+	dentry->d_time = (unsigned long)data_version;
 
 	/* Check to see if we already have an inode for the primary fid. */
 	inode = ilookup5(dir->i_sb, cookie->fids[1].vnode,
@@ -882,9 +882,9 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry)
 	}
 
 	if (op->file[0].scb.have_status)
-		dentry->d_fsdata = (void *)(unsigned long)op->file[0].scb.status.data_version;
+		dentry->d_time = (unsigned long)op->file[0].scb.status.data_version;
 	else
-		dentry->d_fsdata = (void *)(unsigned long)op->file[0].dv_before;
+		dentry->d_time = (unsigned long)op->file[0].dv_before;
 	ret = afs_put_operation(op);
 out:
 	kfree(cookie);
@@ -997,7 +997,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
 	_debug("splice %p", dentry->d_inode);
 	d = d_splice_alias(inode, dentry);
 	if (!IS_ERR_OR_NULL(d)) {
-		d->d_fsdata = dentry->d_fsdata;
+		d->d_time = dentry->d_time;
 		trace_afs_lookup(dvnode, &d->d_name, &fid);
 	} else {
 		trace_afs_lookup(dvnode, &dentry->d_name, &fid);
@@ -1021,13 +1021,17 @@ static int afs_d_revalidate_rcu(struct afs_vnode *dvnode, struct dentry *dentry)
 	if (!afs_check_validity(dvnode))
 		return -ECHILD;
 
+	/* A rename/unlink is pending */
+	if (dentry->d_fsdata)
+		return -ECHILD;
+
 	/* We only need to invalidate a dentry if the server's copy changed
 	 * behind our back.  If we made the change, it's no problem.  Note that
 	 * on a 32-bit system, we only have 32 bits in the dentry to store the
 	 * version.
 	 */
 	dir_version = (long)READ_ONCE(dvnode->status.data_version);
-	de_version = (long)READ_ONCE(dentry->d_fsdata);
+	de_version = (long)READ_ONCE(dentry->d_time);
 	if (de_version != dir_version) {
 		dir_version = (long)READ_ONCE(dvnode->invalid_before);
 		if (de_version - dir_version < 0)
@@ -1056,6 +1060,10 @@ static int afs_d_revalidate(struct inode *parent_dir, const struct qstr *name,
 	if (flags & LOOKUP_RCU)
 		return afs_d_revalidate_rcu(dir, dentry);
 
+	/* Wait for rename/unlink to complete */
+wait_for_rename:
+	wait_var_event(&dentry->d_fsdata, dentry->d_fsdata == NULL);
+
 	if (d_really_is_positive(dentry)) {
 		vnode = AFS_FS_I(d_inode(dentry));
 		_enter("{v={%llx:%llu} n=%pd fl=%lx},",
@@ -1087,7 +1095,7 @@ static int afs_d_revalidate(struct inode *parent_dir, const struct qstr *name,
 	 * version.
 	 */
 	dir_version = dir->status.data_version;
-	de_version = (long)dentry->d_fsdata;
+	de_version = (long)dentry->d_time;
 	if (de_version == (long)dir_version)
 		goto out_valid_noupdate;
 
@@ -1148,7 +1156,13 @@ static int afs_d_revalidate(struct inode *parent_dir, const struct qstr *name,
 	}
 
 out_valid:
-	dentry->d_fsdata = (void *)(unsigned long)dir_version;
+	spin_lock(&dentry->d_lock);
+	if (dentry->d_fsdata) {
+		spin_unlock(&dentry->d_lock);
+		goto wait_for_rename;
+	}
+	dentry->d_time = (unsigned long)dir_version;
+	spin_unlock(&dentry->d_lock);
 out_valid_noupdate:
 	key_put(key);
 	_leave(" = 1 [valid]");
@@ -1523,8 +1537,7 @@ static void afs_unlink_edit_dir(struct afs_operation *op)
 static void afs_unlink_put(struct afs_operation *op)
 {
 	_enter("op=%08x", op->debug_id);
-	if (op->unlink.need_rehash && afs_op_error(op) < 0 && afs_op_error(op) != -ENOENT)
-		d_rehash(op->dentry);
+	store_release_wake_up(&op->dentry->d_fsdata, NULL);
 }
 
 static const struct afs_operation_ops afs_unlink_operation = {
@@ -1578,11 +1591,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
 		afs_op_set_error(op, afs_sillyrename(dvnode, vnode, dentry, op->key));
 		goto error;
 	}
-	if (!d_unhashed(dentry)) {
-		/* Prevent a race with RCU lookup. */
-		__d_drop(dentry);
-		op->unlink.need_rehash = true;
-	}
+	dentry->d_fsdata = AFS_FSDATA_BLOCKED;
 	spin_unlock(&dentry->d_lock);
 
 	op->file[1].vnode = vnode;
@@ -1889,9 +1898,10 @@ static void afs_rename_edit_dir(struct afs_operation *op)
 
 	_enter("op=%08x", op->debug_id);
 
-	if (op->rename.rehash) {
-		d_rehash(op->rename.rehash);
-		op->rename.rehash = NULL;
+	if (op->rename.unblock) {
+		/* Rename has finished, so unlocks lookups to target */
+		store_release_wake_up(&op->rename.unblock->d_fsdata, NULL);
+		op->rename.unblock = NULL;
 	}
 
 	fscache_begin_write_operation(&orig_cres, afs_vnode_cache(orig_dvnode));
@@ -1935,7 +1945,7 @@ static void afs_rename_edit_dir(struct afs_operation *op)
 		spin_unlock(&new_inode->i_lock);
 	}
 
-	/* Now we can update d_fsdata on the dentries to reflect their
+	/* Now we can update d_time on the dentries to reflect their
 	 * new parent's data_version.
 	 */
 	afs_update_dentry_version(op, new_dvp, op->dentry);
@@ -1974,6 +1984,9 @@ static void afs_rename_exchange_edit_dir(struct afs_operation *op)
 
 		d_exchange(old_dentry, new_dentry);
 		up_write(&orig_dvnode->validate_lock);
+	/* dentry has been moved, so d_validate can safely proceed */
+	store_release_wake_up(&old_dentry->d_fsdata, NULL);
+
 	} else {
 		down_write(&orig_dvnode->validate_lock);
 		if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags) &&
@@ -2013,11 +2026,10 @@ static void afs_rename_exchange_edit_dir(struct afs_operation *op)
 static void afs_rename_put(struct afs_operation *op)
 {
 	_enter("op=%08x", op->debug_id);
-	if (op->rename.rehash)
-		d_rehash(op->rename.rehash);
+	if (op->rename.unblock)
+		store_release_wake_up(&op->rename.unblock->d_fsdata, NULL);
+	store_release_wake_up(&op->dentry->d_fsdata, NULL);
 	dput(op->rename.tmp);
-	if (afs_op_error(op))
-		d_rehash(op->dentry);
 }
 
 static const struct afs_operation_ops afs_rename_operation = {
@@ -2125,7 +2137,6 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
 		op->ops		= &afs_rename_noreplace_operation;
 	} else if (flags & RENAME_EXCHANGE) {
 		op->ops		= &afs_rename_exchange_operation;
-		d_drop(new_dentry);
 	} else {
 		/* If we might displace the target, we might need to do silly
 		 * rename.
@@ -2139,14 +2150,12 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
 		 */
 		if (d_is_positive(new_dentry) && !d_is_dir(new_dentry)) {
 			/* To prevent any new references to the target during
-			 * the rename, we unhash the dentry in advance.
+			 * the rename, we set d_fsdata which afs_d_revalidate will wait for.
+			 * d_lock ensures d_count() and ->d_fsdata are consistent.
 			 */
-			if (!d_unhashed(new_dentry)) {
-				d_drop(new_dentry);
-				op->rename.rehash = new_dentry;
-			}
-
+			spin_lock(&new_dentry->d_lock);
 			if (d_count(new_dentry) > 2) {
+				spin_unlock(&new_dentry->d_lock);
 				/* copy the target dentry's name */
 				op->rename.tmp = d_alloc(new_dentry->d_parent,
 							 &new_dentry->d_name);
@@ -2164,22 +2173,27 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
 				}
 
 				op->dentry_2 = op->rename.tmp;
-				op->rename.rehash = NULL;
 				op->rename.new_negative = true;
+			} else {
+				/* Block any lookups to target until the rename completes */
+				new_dentry->d_fsdata = AFS_FSDATA_BLOCKED;
+				op->rename.unblock = new_dentry;
+				spin_unlock(&new_dentry->d_lock);
 			}
 		}
 	}
 
 	/* This bit is potentially nasty as there's a potential race with
-	 * afs_d_revalidate{,_rcu}().  We have to change d_fsdata on the dentry
+	 * afs_d_revalidate{,_rcu}().  We have to change d_time_ on the dentry
 	 * to reflect it's new parent's new data_version after the op, but
 	 * d_revalidate may see old_dentry between the op having taken place
 	 * and the version being updated.
 	 *
-	 * So drop the old_dentry for now to make other threads go through
-	 * lookup instead - which we hold a lock against.
+	 * So block revalidate on the old_dentry until the rename completes.
 	 */
-	d_drop(old_dentry);
+	spin_lock(&old_dentry->d_lock);
+	old_dentry->d_fsdata = AFS_FSDATA_BLOCKED;
+	spin_unlock(&old_dentry->d_lock);
 
 	ret = afs_do_sync_operation(op);
 	if (ret == -ENOTSUPP)
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 0b72a856629992..a0afe552ae646b 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -900,10 +900,7 @@ struct afs_operation {
 			struct afs_symlink *symlink;
 		} create;
 		struct {
-			bool	need_rehash;
-		} unlink;
-		struct {
-			struct dentry	*rehash;
+			struct dentry	*unblock;
 			struct dentry	*tmp;
 			unsigned int	rename_flags;
 			bool		new_negative;
@@ -1764,17 +1761,17 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
 }
 
 /*
- * Note that a dentry got changed.  We need to set d_fsdata to the data version
+ * Note that a dentry got changed.  We need to set d_time to the data version
  * number derived from the result of the operation.  It doesn't matter if
- * d_fsdata goes backwards as we'll just revalidate.
+ * d_time goes backwards as we'll just revalidate.
  */
 static inline void afs_update_dentry_version(struct afs_operation *op,
 					     struct afs_vnode_param *dir_vp,
 					     struct dentry *dentry)
 {
 	if (!op->cumul_error.error)
-		dentry->d_fsdata =
-			(void *)(unsigned long)dir_vp->scb.status.data_version;
+		dentry->d_time =
+			(unsigned long)dir_vp->scb.status.data_version;
 }
 
 /*

3/2 2026/07/02 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5773
							output: 31
							thoughts: 408
						

Thoughts:
**Analyzing the AFS Patch**

Okay, so I've got this patch for the Andrew File System in the Linux kernel. It's tweaking how `d_fsdata` and `d_time` are handled in `fs/afs/dir.c`. Basically, they're introducing `AFS_FSDATA_BLOCKED` to prevent `d_revalidate` from messing with the dentry's `d_time` when it's being moved between directories. The same flag will block lookups for dentries that are being removed the proper way. The patch swaps `d_time` for `d_fsdata` to store the data version and uses `d_fsdata` to block lookups/revalidates during rename/unlink operations.

My initial thought is, "Is this functional? Can we actually *reach* these changes?" AFS is a network filesystem, so we can definitely test this in a virtualized environment. Thinking about it, the modifications are buried deep in core directory operations – lookup, revalidate, unlink, rename. Those are definitely reachable and will be tested often.

Now, I need to pinpoint the functions that are going to be most susceptible to fuzzing. I can see the patch modifies several functions. I need to narrow it down to the most important targets. After reviewing the code, I think my focus should be on `afs_d_revalidate`, `afs_d_revalidate_rcu`, `afs_unlink`, and `afs_rename`.

Finally, before I dig in, I need to make sure I have the necessary configurations enabled. I know `CONFIG_AFS_FS` is the key one.



4/2 2026/07/02 08:51 tool grepper
0m Args:
{
  "Expression": "config AFS_FS",
  "PathPrefix": "fs/afs/Kconfig"
}

Results:
{
  "Output": "fs/afs/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/afs/Kconfig:2:config AFS_FS\nfs/afs/Kconfig-3-\ttristate \"Andrew File System support (AFS)\"\n--\nfs/afs/Kconfig=17=config AFS_DEBUG\n--\nfs/afs/Kconfig-26-\nfs/afs/Kconfig:27:config AFS_FSCACHE\nfs/afs/Kconfig-28-\tbool \"Provide AFS client caching support\"\n"
}

5/2 2026/07/02 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6705
							output: 65
							thoughts: 91
						

Thoughts:
**AFS Configuration & Code Review**

Okay, let's take a look at this patch. My initial thought is to see if there are any specific configurations I need to be aware of beyond the obvious, `CONFIG_AFS_FS`. That's the main flag, of course, but experience tells me it's always worth double-checking.

Alright, the patch touches `fs/afs/dir.c`, `fs/afs/afs.h`, and `fs/afs/internal.h`. That makes sense; it's right in the core of AFS functionality. No surprises there.

Now, let's shift gears and verify the functions within `fs/afs/dir.c`. I'll want to carefully examine those.



6/2 2026/07/02 08:51 tool grepper
0m Args:
{
  "Expression": "^(static )?(int|void|struct dentry \\*|struct inode \\*) afs_(d_revalidate|d_revalidate_rcu|unlink|rename)\\(",
  "PathPrefix": "fs/afs/dir.c"
}

Results:
{
  "Output": "fs/afs/dir.c=25=static int afs_readdir(struct file *file, struct dir_context *ctx);\nfs/afs/dir.c:26:static int afs_d_revalidate(struct inode *dir, const struct qstr *name,\nfs/afs/dir.c-27-\t\t\t    struct dentry *dentry, unsigned int flags);\n--\nfs/afs/dir.c=38=static int afs_rmdir(struct inode *dir, struct dentry *dentry);\nfs/afs/dir.c:39:static int afs_unlink(struct inode *dir, struct dentry *dentry);\nfs/afs/dir.c-40-static int afs_link(struct dentry *from, struct inode *dir,\n--\nfs/afs/dir.c=42=static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,\nfs/afs/dir.c-43-\t\t       struct dentry *dentry, const char *content);\nfs/afs/dir.c:44:static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,\nfs/afs/dir.c-45-\t\t      struct dentry *old_dentry, struct inode *new_dir,\n--\nfs/afs/dir.c=952=static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,\n--\nfs/afs/dir.c-1011- */\nfs/afs/dir.c:1012:static int afs_d_revalidate_rcu(struct afs_vnode *dvnode, struct dentry *dentry)\nfs/afs/dir.c-1013-{\n--\nfs/afs/dir.c-1048- */\nfs/afs/dir.c:1049:static int afs_d_revalidate(struct inode *parent_dir, const struct qstr *name,\nfs/afs/dir.c-1050-\t\t\t    struct dentry *dentry, unsigned int flags)\n--\nfs/afs/dir.c=1543=static const struct afs_operation_ops afs_unlink_operation = {\n--\nfs/afs/dir.c-1554- */\nfs/afs/dir.c:1555:static int afs_unlink(struct inode *dir, struct dentry *dentry)\nfs/afs/dir.c-1556-{\n--\nfs/afs/dir.c=2061=static const struct afs_operation_ops afs_rename_exchange_operation = {\n--\nfs/afs/dir.c-2071- */\nfs/afs/dir.c:2072:static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,\nfs/afs/dir.c-2073-\t\t      struct dentry *old_dentry, struct inode *new_dir,\n"
}

7/2 2026/07/02 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7757
							output: 154
							thoughts: 362
						

Thoughts:
**Fuzzing Strategy for AFS Directory Operations**

Okay, so I've been looking at this patch and my initial thought is that the functions modified here – `afs_d_revalidate`, `afs_d_revalidate_rcu`, `afs_unlink`, and `afs_rename` – are all prime targets for fuzzing. They represent key entry points within the AFS VFS operations, which this patch is directly altering. I need to make sure I don't miss anything, so I'll also include `afs_lookup`.

This patch is essentially changing how the AFS directory operations manage concurrency and interact with the VFS layer. Specifically, the data version is moving from `d_fsdata` to `d_time`, and `d_fsdata` is now being used to block lookups and revalidations during operations. This is a significant functional change in the core of AFS, so concurrency issues are a high risk. I expect fuzzing these functions will reveal some interesting edge cases related to these new locking conditions.

I've confirmed that the only configuration I need is `CONFIG_AFS_FS`, which is good. That simplifies the setup. I think I've got a solid set of targets and configs to move forward with fuzzing. Time to set the results and get started.



8/2 2026/07/02 08:51 tool set-results
0m Args:
{
  "EnableConfigs": [
    "AFS_FS"
  ],
  "FocusSymbols": [
    "afs_d_revalidate",
    "afs_d_revalidate_rcu",
    "afs_unlink",
    "afs_rename",
    "afs_lookup"
  ],
  "Reasoning": "The patch modifies AFS directory operations to use `d_time` instead of `d_fsdata` for storing the data version, and uses `d_fsdata` to block lookups and revalidations during rename and unlink operations. This is a functional change in the core AFS filesystem logic that handles concurrency and VFS interactions, which is reachable and should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "AFS_FS"
  ],
  "FocusSymbols": [
    "afs_d_revalidate",
    "afs_d_revalidate_rcu",
    "afs_unlink",
    "afs_rename",
    "afs_lookup"
  ],
  "Reasoning": "The patch modifies AFS directory operations to use `d_time` instead of `d_fsdata` for storing the data version, and uses `d_fsdata` to block lookups and revalidations during rename and unlink operations. This is a functional change in the core AFS filesystem logic that handles concurrency and VFS interactions, which is reachable and should be fuzzed.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)