AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/06/23 09:26 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "EXT4_FS"
  ],
  "FocusSymbols": [
    "ext4_put_ea_inode",
    "ext4_ea_inode_work",
    "ext4_xattr_delete_inode",
    "ext4_xattr_inode_dec_ref_all",
    "ext4_xattr_release_block"
  ],
  "Reasoning": "The patch modifies how ext4 handles extended attribute (EA) inodes by deferring their iput to a workqueue to avoid lock ordering issues. This is a functional change to core ext4 logic that is reachable from userspace by creating and deleting files with large extended attributes.",
  "WorthFuzzing": true
}

1/1 2026/06/23 09:26 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 09d48ce5d20f51bac70d856ad53e473e19127ae0\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Jun 23 09:26:26 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h\nindex b37c136ea3ab33..b9b0ada7774bb4 100644\n--- a/fs/ext4/ext4.h\n+++ b/fs/ext4/ext4.h\n@@ -1070,8 +1070,14 @@ struct ext4_inode_info {\n \t * between readers of EAs and writers of regular file data, so\n \t * instead we synchronize on xattr_sem when reading or changing\n \t * EAs.\n+\t *\n+\t * EA inodes (EXT4_EA_INODE_FL) do not use xattr_sem; they reuse\n+\t * the space for deferred iput linkage.\n \t */\n-\tstruct rw_semaphore xattr_sem;\n+\tunion {\n+\t\tstruct rw_semaphore xattr_sem;\n+\t\tstruct llist_node i_ea_iput_node;\n+\t};\n \n \t/*\n \t * Inodes with EXT4_STATE_ORPHAN_FILE use i_orphan_idx. Otherwise\n@@ -1770,6 +1776,11 @@ struct ext4_sb_info {\n \tstruct ext4_es_stats s_es_stats;\n \tstruct mb_cache *s_ea_block_cache;\n \tstruct mb_cache *s_ea_inode_cache;\n+\n+\t/* Deferred iput for EA inodes to avoid lock ordering issues */\n+\tstruct llist_head s_ea_inode_to_free;\n+\tstruct delayed_work s_ea_inode_work;\n+\n \tspinlock_t s_es_lock ____cacheline_aligned_in_smp;\n \n \t/* Journal triggers for checksum computation */\ndiff --git a/fs/ext4/inode.c b/fs/ext4/inode.c\nindex ce99807c5f5b2c..747c52c232cc03 100644\n--- a/fs/ext4/inode.c\n+++ b/fs/ext4/inode.c\n@@ -176,7 +176,6 @@ void ext4_evict_inode(struct inode *inode)\n \t * (xattr block freeing), bitmap, group descriptor (inode freeing)\n \t */\n \tint extra_credits = 6;\n-\tstruct ext4_xattr_inode_array *ea_inode_array = NULL;\n \tbool freeze_protected = false;\n \n \ttrace_ext4_evict_inode(inode);\n@@ -281,8 +280,7 @@ void ext4_evict_inode(struct inode *inode)\n \t}\n \n \t/* Remove xattr references. */\n-\terr = ext4_xattr_delete_inode(handle, inode, \u0026ea_inode_array,\n-\t\t\t\t      extra_credits);\n+\terr = ext4_xattr_delete_inode(handle, inode, extra_credits);\n \tif (err) {\n \t\text4_warning(inode-\u003ei_sb, \"xattr delete (err %d)\", err);\n stop_handle:\n@@ -290,7 +288,6 @@ void ext4_evict_inode(struct inode *inode)\n \t\text4_orphan_del(NULL, inode);\n \t\tif (freeze_protected)\n \t\t\tsb_end_intwrite(inode-\u003ei_sb);\n-\t\text4_xattr_inode_array_free(ea_inode_array);\n \t\tgoto no_delete;\n \t}\n \n@@ -320,7 +317,6 @@ void ext4_evict_inode(struct inode *inode)\n \text4_journal_stop(handle);\n \tif (freeze_protected)\n \t\tsb_end_intwrite(inode-\u003ei_sb);\n-\text4_xattr_inode_array_free(ea_inode_array);\n \treturn;\n no_delete:\n \t/*\ndiff --git a/fs/ext4/super.c b/fs/ext4/super.c\nindex 245f67d10ded34..97f0e7c1b254f7 100644\n--- a/fs/ext4/super.c\n+++ b/fs/ext4/super.c\n@@ -1303,6 +1303,8 @@ static void ext4_put_super(struct super_block *sb)\n \t\t\t \u0026sb-\u003es_uuid);\n \n \text4_unregister_li_request(sb);\n+\t/* Drain deferred EA inode iputs while quota is still active. */\n+\text4_drain_ea_inode_work(sbi);\n \text4_quotas_off(sb, EXT4_MAXQUOTAS);\n \n \tdestroy_workqueue(sbi-\u003ersv_conversion_wq);\n@@ -1423,6 +1425,13 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)\n \tmemset(\u0026ei-\u003ei_dquot, 0, sizeof(ei-\u003ei_dquot));\n #endif\n \tei-\u003ejinode = NULL;\n+\t/*\n+\t * Reinitialize xattr_sem every allocation because EA inodes\n+\t * share this space with i_ea_iput_node (via union) which may\n+\t * have overwritten the semaphore when the slab object was\n+\t * previously used as an EA inode.\n+\t */\n+\tinit_rwsem(\u0026ei-\u003exattr_sem);\n \tINIT_LIST_HEAD(\u0026ei-\u003ei_rsv_conversion_list);\n \tspin_lock_init(\u0026ei-\u003ei_completed_io_lock);\n \tei-\u003ei_sync_tid = 0;\n@@ -1488,7 +1497,6 @@ static void init_once(void *foo)\n \tstruct ext4_inode_info *ei = foo;\n \n \tINIT_LIST_HEAD(\u0026ei-\u003ei_orphan);\n-\tinit_rwsem(\u0026ei-\u003exattr_sem);\n \tinit_rwsem(\u0026ei-\u003ei_data_sem);\n \tinode_init_once(\u0026ei-\u003evfs_inode);\n \text4_fc_init_inode(\u0026ei-\u003evfs_inode);\n@@ -5508,6 +5516,8 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)\n \t * The first inode we look at is the journal inode.  Don't try\n \t * root first: it may be modified in the journal!\n \t */\n+\text4_init_ea_inode_work(sbi);\n+\n \tif (!test_opt(sb, NOLOAD) \u0026\u0026 ext4_has_feature_journal(sb)) {\n \t\terr = ext4_load_and_init_journal(sb, es, ctx);\n \t\tif (err)\n@@ -5747,6 +5757,8 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)\n \treturn 0;\n \n failed_mount9:\n+\t/* Drain deferred EA inode iputs before quota shutdown */\n+\text4_drain_ea_inode_work(sbi);\n \text4_quotas_off(sb, EXT4_MAXQUOTAS);\n failed_mount8: __maybe_unused\n \text4_release_orphan_info(sb);\n@@ -5767,6 +5779,8 @@ failed_mount8: __maybe_unused\n \tif (EXT4_SB(sb)-\u003ersv_conversion_wq)\n \t\tdestroy_workqueue(EXT4_SB(sb)-\u003ersv_conversion_wq);\n failed_mount_wq:\n+\t/* Drain deferred EA inode iputs before freeing structures */\n+\text4_drain_ea_inode_work(sbi);\n \text4_xattr_destroy_cache(sbi-\u003es_ea_inode_cache);\n \tsbi-\u003es_ea_inode_cache = NULL;\n \n@@ -5777,6 +5791,8 @@ failed_mount8: __maybe_unused\n \t\text4_journal_destroy(sbi, sbi-\u003es_journal);\n \t}\n failed_mount3a:\n+\t/* Drain deferred EA inode iputs from journal replay */\n+\text4_drain_ea_inode_work(sbi);\n \text4_es_unregister_shrinker(sbi);\n failed_mount3:\n \t/* flush s_sb_upd_work before sbi destroy */\ndiff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c\nindex 982a1f831e2282..7f334349bd4f7d 100644\n--- a/fs/ext4/xattr.c\n+++ b/fs/ext4/xattr.c\n@@ -114,10 +114,6 @@ const struct xattr_handler * const ext4_xattr_handlers[] = {\n #define EA_INODE_CACHE(inode)\t(((struct ext4_sb_info *) \\\n \t\t\t\tinode-\u003ei_sb-\u003es_fs_info)-\u003es_ea_inode_cache)\n \n-static int\n-ext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array,\n-\t\t\tstruct inode *inode);\n-\n #ifdef CONFIG_LOCKDEP\n void ext4_xattr_inode_set_class(struct inode *ea_inode)\n {\n@@ -567,7 +563,7 @@ ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,\n \t\t\t\t\tea_inode-\u003ei_ino, true /* reusable */);\n \t}\n out:\n-\tiput(ea_inode);\n+\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \treturn err;\n }\n \n@@ -1104,10 +1100,10 @@ static int ext4_xattr_inode_inc_ref_all(handle_t *handle, struct inode *parent,\n \t\terr = ext4_xattr_inode_inc_ref(handle, ea_inode);\n \t\tif (err) {\n \t\t\text4_warning_inode(ea_inode, \"inc ref error %d\", err);\n-\t\t\tiput(ea_inode);\n+\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\n \t\t\tgoto cleanup;\n \t\t}\n-\t\tiput(ea_inode);\n+\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\n \t}\n \treturn 0;\n \n@@ -1133,7 +1129,7 @@ static int ext4_xattr_inode_inc_ref_all(handle_t *handle, struct inode *parent,\n \t\tif (err)\n \t\t\text4_warning_inode(ea_inode, \"cleanup dec ref error %d\",\n \t\t\t\t\t   err);\n-\t\tiput(ea_inode);\n+\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\n \t}\n \treturn saved_err;\n }\n@@ -1160,7 +1156,6 @@ static void\n ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n \t\t\t     struct buffer_head *bh,\n \t\t\t     struct ext4_xattr_entry *first, bool block_csum,\n-\t\t\t     struct ext4_xattr_inode_array **ea_inode_array,\n \t\t\t     int extra_credits, bool skip_quota)\n {\n \tstruct inode *ea_inode;\n@@ -1197,14 +1192,6 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n \t\tif (err)\n \t\t\tcontinue;\n \n-\t\terr = ext4_expand_inode_array(ea_inode_array, ea_inode);\n-\t\tif (err) {\n-\t\t\text4_warning_inode(ea_inode,\n-\t\t\t\t\t   \"Expand inode array err=%d\", err);\n-\t\t\tiput(ea_inode);\n-\t\t\tcontinue;\n-\t\t}\n-\n \t\terr = ext4_journal_ensure_credits_fn(handle, credits, credits,\n \t\t\text4_free_metadata_revoke_credits(parent-\u003ei_sb, 1),\n \t\t\text4_xattr_restart_fn(handle, parent, bh, block_csum,\n@@ -1212,6 +1199,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n \t\tif (err \u003c 0) {\n \t\t\text4_warning_inode(ea_inode, \"Ensure credits err=%d\",\n \t\t\t\t\t   err);\n+\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\n \t\t\tcontinue;\n \t\t}\n \t\tif (err \u003e 0) {\n@@ -1221,6 +1209,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n \t\t\t\text4_warning_inode(ea_inode,\n \t\t\t\t\t\t\"Re-get write access err=%d\",\n \t\t\t\t\t\terr);\n+\t\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t}\n@@ -1229,6 +1218,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n \t\tif (err) {\n \t\t\text4_warning_inode(ea_inode, \"ea_inode dec ref err=%d\",\n \t\t\t\t\t   err);\n+\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\n \t\t\tcontinue;\n \t\t}\n \n@@ -1245,6 +1235,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n \t\tentry-\u003ee_value_inum = 0;\n \t\tentry-\u003ee_value_size = 0;\n \n+\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\n \t\tdirty = true;\n \t}\n \n@@ -1271,7 +1262,6 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n static void\n ext4_xattr_release_block(handle_t *handle, struct inode *inode,\n \t\t\t struct buffer_head *bh,\n-\t\t\t struct ext4_xattr_inode_array **ea_inode_array,\n \t\t\t int extra_credits)\n {\n \tstruct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode);\n@@ -1313,7 +1303,6 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,\n \t\t\text4_xattr_inode_dec_ref_all(handle, inode, bh,\n \t\t\t\t\t\t     BFIRST(bh),\n \t\t\t\t\t\t     true /* block_csum */,\n-\t\t\t\t\t\t     ea_inode_array,\n \t\t\t\t\t\t     extra_credits,\n \t\t\t\t\t\t     true /* skip_quota */);\n \t\text4_free_blocks(handle, inode, bh, 0, 1,\n@@ -1505,7 +1494,7 @@ static struct inode *ext4_xattr_inode_create(handle_t *handle,\n \t\t\tif (ext4_xattr_inode_dec_ref(handle, ea_inode))\n \t\t\t\text4_warning_inode(ea_inode,\n \t\t\t\t\t\"cleanup dec ref error %d\", err);\n-\t\t\tiput(ea_inode);\n+\t\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \t\t\treturn ERR_PTR(err);\n \t\t}\n \n@@ -1564,7 +1553,7 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,\n \t\t\tkvfree(ea_data);\n \t\t\treturn ea_inode;\n \t\t}\n-\t\tiput(ea_inode);\n+\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \tnext_entry:\n \t\tce = mb_cache_entry_find_next(ea_inode_cache, ce);\n \t}\n@@ -1615,7 +1604,7 @@ static struct inode *ext4_xattr_inode_lookup_create(handle_t *handle,\n \t\t\t\t      ea_inode-\u003ei_ino, true /* reusable */);\n \treturn ea_inode;\n out_err:\n-\tiput(ea_inode);\n+\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \text4_xattr_inode_free_quota(inode, NULL, value_len);\n \treturn ERR_PTR(err);\n }\n@@ -1848,7 +1837,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,\n \n \tret = 0;\n out:\n-\tiput(old_ea_inode);\n+\text4_put_ea_inode(inode-\u003ei_sb, old_ea_inode);\n \treturn ret;\n }\n \n@@ -2010,7 +1999,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,\n \t\t\t\told_ea_inode_quota = le32_to_cpu(\n \t\t\t\t\t\ts-\u003ehere-\u003ee_value_size);\n \t\t\t}\n-\t\t\tiput(tmp_inode);\n+\t\t\text4_put_ea_inode(inode-\u003ei_sb, tmp_inode);\n \n \t\t\ts-\u003ehere-\u003ee_value_inum = 0;\n \t\t\ts-\u003ehere-\u003ee_value_size = 0;\n@@ -2150,7 +2139,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,\n \t\t\t\t\text4_warning_inode(ea_inode,\n \t\t\t\t\t\t\t   \"dec ref error=%d\",\n \t\t\t\t\t\t\t   error);\n-\t\t\t\tiput(ea_inode);\n+\t\t\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \t\t\t\tea_inode = NULL;\n \t\t\t}\n \n@@ -2182,12 +2171,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,\n \n \t/* Drop the previous xattr block. */\n \tif (bs-\u003ebh \u0026\u0026 bs-\u003ebh != new_bh) {\n-\t\tstruct ext4_xattr_inode_array *ea_inode_array = NULL;\n-\n \t\text4_xattr_release_block(handle, inode, bs-\u003ebh,\n-\t\t\t\t\t \u0026ea_inode_array,\n \t\t\t\t\t 0 /* extra_credits */);\n-\t\text4_xattr_inode_array_free(ea_inode_array);\n \t}\n \terror = 0;\n \n@@ -2203,7 +2188,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,\n \t\t\text4_xattr_inode_free_quota(inode, ea_inode,\n \t\t\t\t\t\t    i_size_read(ea_inode));\n \t\t}\n-\t\tiput(ea_inode);\n+\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \t}\n \tif (ce)\n \t\tmb_cache_entry_put(ea_block_cache, ce);\n@@ -2285,7 +2270,7 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,\n \n \t\t\text4_xattr_inode_free_quota(inode, ea_inode,\n \t\t\t\t\t\t    i_size_read(ea_inode));\n-\t\t\tiput(ea_inode);\n+\t\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \t\t}\n \t\treturn error;\n \t}\n@@ -2297,7 +2282,7 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,\n \t\theader-\u003eh_magic = cpu_to_le32(0);\n \t\text4_clear_inode_state(inode, EXT4_STATE_XATTR);\n \t}\n-\tiput(ea_inode);\n+\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \treturn 0;\n }\n \n@@ -2863,46 +2848,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,\n \treturn error;\n }\n \n-#define EIA_INCR 16 /* must be 2^n */\n-#define EIA_MASK (EIA_INCR - 1)\n-\n-/* Add the large xattr @inode into @ea_inode_array for deferred iput().\n- * If @ea_inode_array is new or full it will be grown and the old\n- * contents copied over.\n- */\n-static int\n-ext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array,\n-\t\t\tstruct inode *inode)\n-{\n-\tif (*ea_inode_array == NULL) {\n-\t\t/*\n-\t\t * Start with 15 inodes, so it fits into a power-of-two size.\n-\t\t */\n-\t\t(*ea_inode_array) = kmalloc_flex(**ea_inode_array, inodes,\n-\t\t\t\t\t\t EIA_MASK, GFP_NOFS);\n-\t\tif (*ea_inode_array == NULL)\n-\t\t\treturn -ENOMEM;\n-\t\t(*ea_inode_array)-\u003ecount = 0;\n-\t} else if (((*ea_inode_array)-\u003ecount \u0026 EIA_MASK) == EIA_MASK) {\n-\t\t/* expand the array once all 15 + n * 16 slots are full */\n-\t\tstruct ext4_xattr_inode_array *new_array = NULL;\n-\n-\t\tnew_array = kmalloc_flex(**ea_inode_array, inodes,\n-\t\t\t\t\t (*ea_inode_array)-\u003ecount + EIA_INCR,\n-\t\t\t\t\t GFP_NOFS);\n-\t\tif (new_array == NULL)\n-\t\t\treturn -ENOMEM;\n-\t\tmemcpy(new_array, *ea_inode_array,\n-\t\t       struct_size(*ea_inode_array, inodes,\n-\t\t\t\t   (*ea_inode_array)-\u003ecount));\n-\t\tkfree(*ea_inode_array);\n-\t\t*ea_inode_array = new_array;\n-\t}\n-\t(*ea_inode_array)-\u003ecount++;\n-\t(*ea_inode_array)-\u003einodes[(*ea_inode_array)-\u003ecount - 1] = inode;\n-\treturn 0;\n-}\n-\n /*\n  * ext4_xattr_delete_inode()\n  *\n@@ -2913,7 +2858,6 @@ ext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array,\n  * references on xattr block and xattr inodes.\n  */\n int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,\n-\t\t\t    struct ext4_xattr_inode_array **ea_inode_array,\n \t\t\t    int extra_credits)\n {\n \tstruct buffer_head *bh = NULL;\n@@ -2952,7 +2896,6 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,\n \t\t\text4_xattr_inode_dec_ref_all(handle, inode, iloc.bh,\n \t\t\t\t\t\t     IFIRST(header),\n \t\t\t\t\t\t     false /* block_csum */,\n-\t\t\t\t\t\t     ea_inode_array,\n \t\t\t\t\t\t     extra_credits,\n \t\t\t\t\t\t     false /* skip_quota */);\n \t}\n@@ -2986,12 +2929,12 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,\n \t\t\t\t\tcontinue;\n \t\t\t\text4_xattr_inode_free_quota(inode, ea_inode,\n \t\t\t\t\t      le32_to_cpu(entry-\u003ee_value_size));\n-\t\t\t\tiput(ea_inode);\n+\t\t\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\n \t\t\t}\n \n \t\t}\n \n-\t\text4_xattr_release_block(handle, inode, bh, ea_inode_array,\n+\t\text4_xattr_release_block(handle, inode, bh,\n \t\t\t\t\t extra_credits);\n \t\t/*\n \t\t * Update i_file_acl value in the same transaction that releases\n@@ -3013,16 +2956,59 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,\n \treturn error;\n }\n \n-void ext4_xattr_inode_array_free(struct ext4_xattr_inode_array *ea_inode_array)\n+/*\n+ * Worker function for deferred EA inode iput.  Processes all inodes queued\n+ * on s_ea_inode_to_free in a context free of xattr_sem/jbd2 handle locks.\n+ */\n+static void ext4_ea_inode_work(struct work_struct *work)\n {\n-\tint idx;\n+\tstruct ext4_sb_info *sbi = container_of(to_delayed_work(work),\n+\t\t\t\t\t\tstruct ext4_sb_info,\n+\t\t\t\t\t\ts_ea_inode_work);\n+\tstruct llist_node *node = llist_del_all(\u0026sbi-\u003es_ea_inode_to_free);\n+\tstruct llist_node *next;\n \n-\tif (ea_inode_array == NULL)\n+\twhile (node) {\n+\t\tstruct ext4_inode_info *ei = container_of(node,\n+\t\t\t\t\tstruct ext4_inode_info, i_ea_iput_node);\n+\t\tnext = node-\u003enext;\n+\t\tiput(\u0026ei-\u003evfs_inode);\n+\t\tnode = next;\n+\t}\n+}\n+\n+/*\n+ * Release a VFS reference on an EA inode.  Must be used instead of iput()\n+ * in any context where xattr_sem or a jbd2 handle is held.\n+ *\n+ * If this is not the last reference, drops it immediately via\n+ * iput_if_not_last() with no further action needed.\n+ *\n+ * If this is the last reference, the inode is linked onto a per-sb\n+ * llist via i_ea_iput_node (embedded in ext4_inode_info, sharing space\n+ * with the unused xattr_sem) and a delayed worker performs the final\n+ * iput() in a clean context.\n+ */\n+void ext4_put_ea_inode(struct super_block *sb, struct inode *inode)\n+{\n+\tif (!inode)\n+\t\treturn;\n+\tWARN_ON_ONCE(!(EXT4_I(inode)-\u003ei_flags \u0026 EXT4_EA_INODE_FL));\n+\tif (iput_if_not_last(inode))\n \t\treturn;\n+\tllist_add(\u0026EXT4_I(inode)-\u003ei_ea_iput_node,\n+\t\t  \u0026EXT4_SB(sb)-\u003es_ea_inode_to_free);\n+\t/*\n+\t * Use a short delay to allow multiple EA inodes to accumulate,\n+\t * reducing workqueue wakeups when several are released together.\n+\t */\n+\tschedule_delayed_work(\u0026EXT4_SB(sb)-\u003es_ea_inode_work, 1);\n+}\n \n-\tfor (idx = 0; idx \u003c ea_inode_array-\u003ecount; ++idx)\n-\t\tiput(ea_inode_array-\u003einodes[idx]);\n-\tkfree(ea_inode_array);\n+void ext4_init_ea_inode_work(struct ext4_sb_info *sbi)\n+{\n+\tinit_llist_head(\u0026sbi-\u003es_ea_inode_to_free);\n+\tINIT_DELAYED_WORK(\u0026sbi-\u003es_ea_inode_work, ext4_ea_inode_work);\n }\n \n /*\ndiff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h\nindex 1fedf44d4fb65e..8214a31fe0018d 100644\n--- a/fs/ext4/xattr.h\n+++ b/fs/ext4/xattr.h\n@@ -131,11 +131,6 @@ struct ext4_xattr_ibody_find {\n \tstruct ext4_iloc iloc;\n };\n \n-struct ext4_xattr_inode_array {\n-\tunsigned int count;\n-\tstruct inode *inodes[] __counted_by(count);\n-};\n-\n extern const struct xattr_handler ext4_xattr_user_handler;\n extern const struct xattr_handler ext4_xattr_trusted_handler;\n extern const struct xattr_handler ext4_xattr_security_handler;\n@@ -187,9 +182,21 @@ extern int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,\n \t\t\t\tbool is_create);\n \n extern int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,\n-\t\t\t\t   struct ext4_xattr_inode_array **array,\n \t\t\t\t   int extra_credits);\n-extern void ext4_xattr_inode_array_free(struct ext4_xattr_inode_array *array);\n+extern void ext4_init_ea_inode_work(struct ext4_sb_info *sbi);\n+extern void ext4_put_ea_inode(struct super_block *sb, struct inode *inode);\n+\n+/*\n+ * Drain all pending deferred EA inode iputs.  Must be called before\n+ * freeing resources that eviction depends on (quota, block allocator).\n+ * Loops because worker iput may trigger eviction that re-queues.\n+ */\n+static inline void ext4_drain_ea_inode_work(struct ext4_sb_info *sbi)\n+{\n+\twhile (flush_delayed_work(\u0026sbi-\u003es_ea_inode_work) ||\n+\t       !llist_empty(\u0026sbi-\u003es_ea_inode_to_free))\n+\t\t;\n+}\n \n extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,\n \t\t\t    struct ext4_inode *raw_inode, handle_t *handle);\ndiff --git a/include/linux/fs.h b/include/linux/fs.h\nindex 6da44573ce4508..4916a9d543470d 100644\n--- a/include/linux/fs.h\n+++ b/include/linux/fs.h\n@@ -2418,6 +2418,19 @@ static inline void super_set_sysfs_name_generic(struct super_block *sb, const ch\n extern void ihold(struct inode * inode);\n extern void iput(struct inode *);\n void iput_not_last(struct inode *);\n+\n+/**\n+ * iput_if_not_last - drop an inode reference only if it is not the last one\n+ * @inode: inode to put\n+ *\n+ * Returns true if the reference was dropped, false if this was the last\n+ * reference and the caller must arrange for final iput() in a safe context.\n+ */\n+static inline bool iput_if_not_last(struct inode *inode)\n+{\n+\treturn atomic_add_unless(\u0026inode-\u003ei_count, -1, 1);\n+}\n+\n int inode_update_time(struct inode *inode, enum fs_update_time type,\n \t\tunsigned int flags);\n int generic_update_time(struct inode *inode, enum fs_update_time type,\n"
}

2/1 2026/06/23 09:26 agent patch-evaluator
1m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "EXT4_FS"
  ],
  "FocusSymbols": [
    "ext4_put_ea_inode",
    "ext4_ea_inode_work",
    "ext4_xattr_delete_inode",
    "ext4_xattr_inode_dec_ref_all",
    "ext4_xattr_release_block"
  ],
  "Reasoning": "The patch modifies how ext4 handles extended attribute (EA) inodes by deferring their iput to a workqueue to avoid lock ordering issues. This is a functional change to core ext4 logic that is reachable from userspace by creating and deleting files with large extended attributes.",
  "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 09d48ce5d20f51bac70d856ad53e473e19127ae0
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Jun 23 09:26:26 2026 +0000

    syz-cluster: applied patch under review

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index b37c136ea3ab33..b9b0ada7774bb4 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1070,8 +1070,14 @@ struct ext4_inode_info {
 	 * between readers of EAs and writers of regular file data, so
 	 * instead we synchronize on xattr_sem when reading or changing
 	 * EAs.
+	 *
+	 * EA inodes (EXT4_EA_INODE_FL) do not use xattr_sem; they reuse
+	 * the space for deferred iput linkage.
 	 */
-	struct rw_semaphore xattr_sem;
+	union {
+		struct rw_semaphore xattr_sem;
+		struct llist_node i_ea_iput_node;
+	};
 
 	/*
 	 * Inodes with EXT4_STATE_ORPHAN_FILE use i_orphan_idx. Otherwise
@@ -1770,6 +1776,11 @@ struct ext4_sb_info {
 	struct ext4_es_stats s_es_stats;
 	struct mb_cache *s_ea_block_cache;
 	struct mb_cache *s_ea_inode_cache;
+
+	/* Deferred iput for EA inodes to avoid lock ordering issues */
+	struct llist_head s_ea_inode_to_free;
+	struct delayed_work s_ea_inode_work;
+
 	spinlock_t s_es_lock ____cacheline_aligned_in_smp;
 
 	/* Journal triggers for checksum computation */
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ce99807c5f5b2c..747c52c232cc03 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -176,7 +176,6 @@ void ext4_evict_inode(struct inode *inode)
 	 * (xattr block freeing), bitmap, group descriptor (inode freeing)
 	 */
 	int extra_credits = 6;
-	struct ext4_xattr_inode_array *ea_inode_array = NULL;
 	bool freeze_protected = false;
 
 	trace_ext4_evict_inode(inode);
@@ -281,8 +280,7 @@ void ext4_evict_inode(struct inode *inode)
 	}
 
 	/* Remove xattr references. */
-	err = ext4_xattr_delete_inode(handle, inode, &ea_inode_array,
-				      extra_credits);
+	err = ext4_xattr_delete_inode(handle, inode, extra_credits);
 	if (err) {
 		ext4_warning(inode->i_sb, "xattr delete (err %d)", err);
 stop_handle:
@@ -290,7 +288,6 @@ void ext4_evict_inode(struct inode *inode)
 		ext4_orphan_del(NULL, inode);
 		if (freeze_protected)
 			sb_end_intwrite(inode->i_sb);
-		ext4_xattr_inode_array_free(ea_inode_array);
 		goto no_delete;
 	}
 
@@ -320,7 +317,6 @@ void ext4_evict_inode(struct inode *inode)
 	ext4_journal_stop(handle);
 	if (freeze_protected)
 		sb_end_intwrite(inode->i_sb);
-	ext4_xattr_inode_array_free(ea_inode_array);
 	return;
 no_delete:
 	/*
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 245f67d10ded34..97f0e7c1b254f7 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1303,6 +1303,8 @@ static void ext4_put_super(struct super_block *sb)
 			 &sb->s_uuid);
 
 	ext4_unregister_li_request(sb);
+	/* Drain deferred EA inode iputs while quota is still active. */
+	ext4_drain_ea_inode_work(sbi);
 	ext4_quotas_off(sb, EXT4_MAXQUOTAS);
 
 	destroy_workqueue(sbi->rsv_conversion_wq);
@@ -1423,6 +1425,13 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
 	memset(&ei->i_dquot, 0, sizeof(ei->i_dquot));
 #endif
 	ei->jinode = NULL;
+	/*
+	 * Reinitialize xattr_sem every allocation because EA inodes
+	 * share this space with i_ea_iput_node (via union) which may
+	 * have overwritten the semaphore when the slab object was
+	 * previously used as an EA inode.
+	 */
+	init_rwsem(&ei->xattr_sem);
 	INIT_LIST_HEAD(&ei->i_rsv_conversion_list);
 	spin_lock_init(&ei->i_completed_io_lock);
 	ei->i_sync_tid = 0;
@@ -1488,7 +1497,6 @@ static void init_once(void *foo)
 	struct ext4_inode_info *ei = foo;
 
 	INIT_LIST_HEAD(&ei->i_orphan);
-	init_rwsem(&ei->xattr_sem);
 	init_rwsem(&ei->i_data_sem);
 	inode_init_once(&ei->vfs_inode);
 	ext4_fc_init_inode(&ei->vfs_inode);
@@ -5508,6 +5516,8 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
 	 * The first inode we look at is the journal inode.  Don't try
 	 * root first: it may be modified in the journal!
 	 */
+	ext4_init_ea_inode_work(sbi);
+
 	if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {
 		err = ext4_load_and_init_journal(sb, es, ctx);
 		if (err)
@@ -5747,6 +5757,8 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
 	return 0;
 
 failed_mount9:
+	/* Drain deferred EA inode iputs before quota shutdown */
+	ext4_drain_ea_inode_work(sbi);
 	ext4_quotas_off(sb, EXT4_MAXQUOTAS);
 failed_mount8: __maybe_unused
 	ext4_release_orphan_info(sb);
@@ -5767,6 +5779,8 @@ failed_mount8: __maybe_unused
 	if (EXT4_SB(sb)->rsv_conversion_wq)
 		destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq);
 failed_mount_wq:
+	/* Drain deferred EA inode iputs before freeing structures */
+	ext4_drain_ea_inode_work(sbi);
 	ext4_xattr_destroy_cache(sbi->s_ea_inode_cache);
 	sbi->s_ea_inode_cache = NULL;
 
@@ -5777,6 +5791,8 @@ failed_mount8: __maybe_unused
 		ext4_journal_destroy(sbi, sbi->s_journal);
 	}
 failed_mount3a:
+	/* Drain deferred EA inode iputs from journal replay */
+	ext4_drain_ea_inode_work(sbi);
 	ext4_es_unregister_shrinker(sbi);
 failed_mount3:
 	/* flush s_sb_upd_work before sbi destroy */
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 982a1f831e2282..7f334349bd4f7d 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -114,10 +114,6 @@ const struct xattr_handler * const ext4_xattr_handlers[] = {
 #define EA_INODE_CACHE(inode)	(((struct ext4_sb_info *) \
 				inode->i_sb->s_fs_info)->s_ea_inode_cache)
 
-static int
-ext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array,
-			struct inode *inode);
-
 #ifdef CONFIG_LOCKDEP
 void ext4_xattr_inode_set_class(struct inode *ea_inode)
 {
@@ -567,7 +563,7 @@ ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,
 					ea_inode->i_ino, true /* reusable */);
 	}
 out:
-	iput(ea_inode);
+	ext4_put_ea_inode(inode->i_sb, ea_inode);
 	return err;
 }
 
@@ -1104,10 +1100,10 @@ static int ext4_xattr_inode_inc_ref_all(handle_t *handle, struct inode *parent,
 		err = ext4_xattr_inode_inc_ref(handle, ea_inode);
 		if (err) {
 			ext4_warning_inode(ea_inode, "inc ref error %d", err);
-			iput(ea_inode);
+			ext4_put_ea_inode(parent->i_sb, ea_inode);
 			goto cleanup;
 		}
-		iput(ea_inode);
+		ext4_put_ea_inode(parent->i_sb, ea_inode);
 	}
 	return 0;
 
@@ -1133,7 +1129,7 @@ static int ext4_xattr_inode_inc_ref_all(handle_t *handle, struct inode *parent,
 		if (err)
 			ext4_warning_inode(ea_inode, "cleanup dec ref error %d",
 					   err);
-		iput(ea_inode);
+		ext4_put_ea_inode(parent->i_sb, ea_inode);
 	}
 	return saved_err;
 }
@@ -1160,7 +1156,6 @@ static void
 ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
 			     struct buffer_head *bh,
 			     struct ext4_xattr_entry *first, bool block_csum,
-			     struct ext4_xattr_inode_array **ea_inode_array,
 			     int extra_credits, bool skip_quota)
 {
 	struct inode *ea_inode;
@@ -1197,14 +1192,6 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
 		if (err)
 			continue;
 
-		err = ext4_expand_inode_array(ea_inode_array, ea_inode);
-		if (err) {
-			ext4_warning_inode(ea_inode,
-					   "Expand inode array err=%d", err);
-			iput(ea_inode);
-			continue;
-		}
-
 		err = ext4_journal_ensure_credits_fn(handle, credits, credits,
 			ext4_free_metadata_revoke_credits(parent->i_sb, 1),
 			ext4_xattr_restart_fn(handle, parent, bh, block_csum,
@@ -1212,6 +1199,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
 		if (err < 0) {
 			ext4_warning_inode(ea_inode, "Ensure credits err=%d",
 					   err);
+			ext4_put_ea_inode(parent->i_sb, ea_inode);
 			continue;
 		}
 		if (err > 0) {
@@ -1221,6 +1209,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
 				ext4_warning_inode(ea_inode,
 						"Re-get write access err=%d",
 						err);
+				ext4_put_ea_inode(parent->i_sb, ea_inode);
 				continue;
 			}
 		}
@@ -1229,6 +1218,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
 		if (err) {
 			ext4_warning_inode(ea_inode, "ea_inode dec ref err=%d",
 					   err);
+			ext4_put_ea_inode(parent->i_sb, ea_inode);
 			continue;
 		}
 
@@ -1245,6 +1235,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
 		entry->e_value_inum = 0;
 		entry->e_value_size = 0;
 
+		ext4_put_ea_inode(parent->i_sb, ea_inode);
 		dirty = true;
 	}
 
@@ -1271,7 +1262,6 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
 static void
 ext4_xattr_release_block(handle_t *handle, struct inode *inode,
 			 struct buffer_head *bh,
-			 struct ext4_xattr_inode_array **ea_inode_array,
 			 int extra_credits)
 {
 	struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode);
@@ -1313,7 +1303,6 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
 			ext4_xattr_inode_dec_ref_all(handle, inode, bh,
 						     BFIRST(bh),
 						     true /* block_csum */,
-						     ea_inode_array,
 						     extra_credits,
 						     true /* skip_quota */);
 		ext4_free_blocks(handle, inode, bh, 0, 1,
@@ -1505,7 +1494,7 @@ static struct inode *ext4_xattr_inode_create(handle_t *handle,
 			if (ext4_xattr_inode_dec_ref(handle, ea_inode))
 				ext4_warning_inode(ea_inode,
 					"cleanup dec ref error %d", err);
-			iput(ea_inode);
+			ext4_put_ea_inode(inode->i_sb, ea_inode);
 			return ERR_PTR(err);
 		}
 
@@ -1564,7 +1553,7 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,
 			kvfree(ea_data);
 			return ea_inode;
 		}
-		iput(ea_inode);
+		ext4_put_ea_inode(inode->i_sb, ea_inode);
 	next_entry:
 		ce = mb_cache_entry_find_next(ea_inode_cache, ce);
 	}
@@ -1615,7 +1604,7 @@ static struct inode *ext4_xattr_inode_lookup_create(handle_t *handle,
 				      ea_inode->i_ino, true /* reusable */);
 	return ea_inode;
 out_err:
-	iput(ea_inode);
+	ext4_put_ea_inode(inode->i_sb, ea_inode);
 	ext4_xattr_inode_free_quota(inode, NULL, value_len);
 	return ERR_PTR(err);
 }
@@ -1848,7 +1837,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
 
 	ret = 0;
 out:
-	iput(old_ea_inode);
+	ext4_put_ea_inode(inode->i_sb, old_ea_inode);
 	return ret;
 }
 
@@ -2010,7 +1999,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
 				old_ea_inode_quota = le32_to_cpu(
 						s->here->e_value_size);
 			}
-			iput(tmp_inode);
+			ext4_put_ea_inode(inode->i_sb, tmp_inode);
 
 			s->here->e_value_inum = 0;
 			s->here->e_value_size = 0;
@@ -2150,7 +2139,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
 					ext4_warning_inode(ea_inode,
 							   "dec ref error=%d",
 							   error);
-				iput(ea_inode);
+				ext4_put_ea_inode(inode->i_sb, ea_inode);
 				ea_inode = NULL;
 			}
 
@@ -2182,12 +2171,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
 
 	/* Drop the previous xattr block. */
 	if (bs->bh && bs->bh != new_bh) {
-		struct ext4_xattr_inode_array *ea_inode_array = NULL;
-
 		ext4_xattr_release_block(handle, inode, bs->bh,
-					 &ea_inode_array,
 					 0 /* extra_credits */);
-		ext4_xattr_inode_array_free(ea_inode_array);
 	}
 	error = 0;
 
@@ -2203,7 +2188,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
 			ext4_xattr_inode_free_quota(inode, ea_inode,
 						    i_size_read(ea_inode));
 		}
-		iput(ea_inode);
+		ext4_put_ea_inode(inode->i_sb, ea_inode);
 	}
 	if (ce)
 		mb_cache_entry_put(ea_block_cache, ce);
@@ -2285,7 +2270,7 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
 
 			ext4_xattr_inode_free_quota(inode, ea_inode,
 						    i_size_read(ea_inode));
-			iput(ea_inode);
+			ext4_put_ea_inode(inode->i_sb, ea_inode);
 		}
 		return error;
 	}
@@ -2297,7 +2282,7 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
 		header->h_magic = cpu_to_le32(0);
 		ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
 	}
-	iput(ea_inode);
+	ext4_put_ea_inode(inode->i_sb, ea_inode);
 	return 0;
 }
 
@@ -2863,46 +2848,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
 	return error;
 }
 
-#define EIA_INCR 16 /* must be 2^n */
-#define EIA_MASK (EIA_INCR - 1)
-
-/* Add the large xattr @inode into @ea_inode_array for deferred iput().
- * If @ea_inode_array is new or full it will be grown and the old
- * contents copied over.
- */
-static int
-ext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array,
-			struct inode *inode)
-{
-	if (*ea_inode_array == NULL) {
-		/*
-		 * Start with 15 inodes, so it fits into a power-of-two size.
-		 */
-		(*ea_inode_array) = kmalloc_flex(**ea_inode_array, inodes,
-						 EIA_MASK, GFP_NOFS);
-		if (*ea_inode_array == NULL)
-			return -ENOMEM;
-		(*ea_inode_array)->count = 0;
-	} else if (((*ea_inode_array)->count & EIA_MASK) == EIA_MASK) {
-		/* expand the array once all 15 + n * 16 slots are full */
-		struct ext4_xattr_inode_array *new_array = NULL;
-
-		new_array = kmalloc_flex(**ea_inode_array, inodes,
-					 (*ea_inode_array)->count + EIA_INCR,
-					 GFP_NOFS);
-		if (new_array == NULL)
-			return -ENOMEM;
-		memcpy(new_array, *ea_inode_array,
-		       struct_size(*ea_inode_array, inodes,
-				   (*ea_inode_array)->count));
-		kfree(*ea_inode_array);
-		*ea_inode_array = new_array;
-	}
-	(*ea_inode_array)->count++;
-	(*ea_inode_array)->inodes[(*ea_inode_array)->count - 1] = inode;
-	return 0;
-}
-
 /*
  * ext4_xattr_delete_inode()
  *
@@ -2913,7 +2858,6 @@ ext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array,
  * references on xattr block and xattr inodes.
  */
 int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
-			    struct ext4_xattr_inode_array **ea_inode_array,
 			    int extra_credits)
 {
 	struct buffer_head *bh = NULL;
@@ -2952,7 +2896,6 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
 			ext4_xattr_inode_dec_ref_all(handle, inode, iloc.bh,
 						     IFIRST(header),
 						     false /* block_csum */,
-						     ea_inode_array,
 						     extra_credits,
 						     false /* skip_quota */);
 	}
@@ -2986,12 +2929,12 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
 					continue;
 				ext4_xattr_inode_free_quota(inode, ea_inode,
 					      le32_to_cpu(entry->e_value_size));
-				iput(ea_inode);
+				ext4_put_ea_inode(inode->i_sb, ea_inode);
 			}
 
 		}
 
-		ext4_xattr_release_block(handle, inode, bh, ea_inode_array,
+		ext4_xattr_release_block(handle, inode, bh,
 					 extra_credits);
 		/*
 		 * Update i_file_acl value in the same transaction that releases
@@ -3013,16 +2956,59 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
 	return error;
 }
 
-void ext4_xattr_inode_array_free(struct ext4_xattr_inode_array *ea_inode_array)
+/*
+ * Worker function for deferred EA inode iput.  Processes all inodes queued
+ * on s_ea_inode_to_free in a context free of xattr_sem/jbd2 handle locks.
+ */
+static void ext4_ea_inode_work(struct work_struct *work)
 {
-	int idx;
+	struct ext4_sb_info *sbi = container_of(to_delayed_work(work),
+						struct ext4_sb_info,
+						s_ea_inode_work);
+	struct llist_node *node = llist_del_all(&sbi->s_ea_inode_to_free);
+	struct llist_node *next;
 
-	if (ea_inode_array == NULL)
+	while (node) {
+		struct ext4_inode_info *ei = container_of(node,
+					struct ext4_inode_info, i_ea_iput_node);
+		next = node->next;
+		iput(&ei->vfs_inode);
+		node = next;
+	}
+}
+
+/*
+ * Release a VFS reference on an EA inode.  Must be used instead of iput()
+ * in any context where xattr_sem or a jbd2 handle is held.
+ *
+ * If this is not the last reference, drops it immediately via
+ * iput_if_not_last() with no further action needed.
+ *
+ * If this is the last reference, the inode is linked onto a per-sb
+ * llist via i_ea_iput_node (embedded in ext4_inode_info, sharing space
+ * with the unused xattr_sem) and a delayed worker performs the final
+ * iput() in a clean context.
+ */
+void ext4_put_ea_inode(struct super_block *sb, struct inode *inode)
+{
+	if (!inode)
+		return;
+	WARN_ON_ONCE(!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL));
+	if (iput_if_not_last(inode))
 		return;
+	llist_add(&EXT4_I(inode)->i_ea_iput_node,
+		  &EXT4_SB(sb)->s_ea_inode_to_free);
+	/*
+	 * Use a short delay to allow multiple EA inodes to accumulate,
+	 * reducing workqueue wakeups when several are released together.
+	 */
+	schedule_delayed_work(&EXT4_SB(sb)->s_ea_inode_work, 1);
+}
 
-	for (idx = 0; idx < ea_inode_array->count; ++idx)
-		iput(ea_inode_array->inodes[idx]);
-	kfree(ea_inode_array);
+void ext4_init_ea_inode_work(struct ext4_sb_info *sbi)
+{
+	init_llist_head(&sbi->s_ea_inode_to_free);
+	INIT_DELAYED_WORK(&sbi->s_ea_inode_work, ext4_ea_inode_work);
 }
 
 /*
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 1fedf44d4fb65e..8214a31fe0018d 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -131,11 +131,6 @@ struct ext4_xattr_ibody_find {
 	struct ext4_iloc iloc;
 };
 
-struct ext4_xattr_inode_array {
-	unsigned int count;
-	struct inode *inodes[] __counted_by(count);
-};
-
 extern const struct xattr_handler ext4_xattr_user_handler;
 extern const struct xattr_handler ext4_xattr_trusted_handler;
 extern const struct xattr_handler ext4_xattr_security_handler;
@@ -187,9 +182,21 @@ extern int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
 				bool is_create);
 
 extern int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
-				   struct ext4_xattr_inode_array **array,
 				   int extra_credits);
-extern void ext4_xattr_inode_array_free(struct ext4_xattr_inode_array *array);
+extern void ext4_init_ea_inode_work(struct ext4_sb_info *sbi);
+extern void ext4_put_ea_inode(struct super_block *sb, struct inode *inode);
+
+/*
+ * Drain all pending deferred EA inode iputs.  Must be called before
+ * freeing resources that eviction depends on (quota, block allocator).
+ * Loops because worker iput may trigger eviction that re-queues.
+ */
+static inline void ext4_drain_ea_inode_work(struct ext4_sb_info *sbi)
+{
+	while (flush_delayed_work(&sbi->s_ea_inode_work) ||
+	       !llist_empty(&sbi->s_ea_inode_to_free))
+		;
+}
 
 extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
 			    struct ext4_inode *raw_inode, handle_t *handle);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6da44573ce4508..4916a9d543470d 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2418,6 +2418,19 @@ static inline void super_set_sysfs_name_generic(struct super_block *sb, const ch
 extern void ihold(struct inode * inode);
 extern void iput(struct inode *);
 void iput_not_last(struct inode *);
+
+/**
+ * iput_if_not_last - drop an inode reference only if it is not the last one
+ * @inode: inode to put
+ *
+ * Returns true if the reference was dropped, false if this was the last
+ * reference and the caller must arrange for final iput() in a safe context.
+ */
+static inline bool iput_if_not_last(struct inode *inode)
+{
+	return atomic_add_unless(&inode->i_count, -1, 1);
+}
+
 int inode_update_time(struct inode *inode, enum fs_update_time type,
 		unsigned int flags);
 int generic_update_time(struct inode *inode, enum fs_update_time type,

3/2 2026/06/23 09:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9548
							output: 35
							thoughts: 315
						

Thoughts:
**Analyzing the Ext4 EA Inode Patch**

Okay, so I'm looking at this patch for ext4, and it's making some pretty significant changes to how extended attribute (EA) inodes are handled. It's replacing the old `ext4_xattr_inode_array` approach, which I remember could be problematic with deferred `iput` operations, with a per-superblock list (`s_ea_inode_to_free`) and a delayed workqueue (`s_ea_inode_work`). Smart move, potentially addressing those nasty lock ordering issues. I see they've also introduced `iput_if_not_last` in `include/linux/fs.h`, which is probably tied to the deferred release.

This is definitely a functional change – impacting the core ext4 filesystem logic. Essentially, it delays the `iput` operation to a workqueue to avoid locking problems, particularly with concurrent access. This is going to be triggered from userspace by manipulating files with large extended attributes. Those large attributes, when the `ea_inode` feature is enabled, end up being stored in their own dedicated inodes, so it's a good place to test.

I need to confirm the configuration and features. Let's make sure the dependencies are in place. The filesystem in question is ext4, so we need `EXT4_FS`. Now for the feature itself – that would be `ea_inode`. Next, I should check for any specific configurations related to ext4 extended attributes. Let's start with `CONFIG_EXT4_FS_XATTR`... okay, time to dive into `fs/ext4/Kconfig` and see what else we can find. This is where the devil is in the details.



4/2 2026/06/23 09:26 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config EXT4_FS_XATTR",
  "PathPrefix": "fs/ext4/Kconfig"
}

Results:
{
  "error": "no matches"
}

5/2 2026/06/23 09:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10279
							output: 29
							thoughts: 56
						

Thoughts:
Okay, let's see...

**Investigating a Configuration Issue**

Hmm, I suspect there's a problem with how the extended attributes are being handled in the ext4 filesystem configuration. Perhaps the symbol `CONFIG_EXT4_FS_XATTR` is no longer valid, or maybe it's been renamed to just `EXT4_FS_XATTR`. To be certain, I'll need to check the Kconfig file for ext4, specifically the one located in `fs/ext4/Kconfig`. I'll scan that file for anything related to `XATTR` to clarify the current configuration options.



6/2 2026/06/23 09:26 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "XATTR",
  "PathPrefix": "fs/ext4/Kconfig"
}

Results:
{
  "error": "no matches"
}

7/2 2026/06/23 09:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10509
							output: 28
							thoughts: 12
						

Thoughts:
Here's a summary of my thought process, as if I were considering that particular Makefile:

**Analyzing the ext4 Makefile**

Alright, let's dive into the `fs/ext4/Makefile`. I need to understand what's going on in the build process for the ext4 filesystem. As an expert, I'm already familiar with the general kernel build system, so I'm looking for the specific ext4-related targets, dependencies, and any custom build rules or flags that stand out. I'll be focused on how it handles object file compilation, linking, module creation (if any), and any specific optimizations or configurations tailored to ext4. I'm also interested in how it interacts with the broader kernel build system – are there any unusual include directives or dependencies on other parts of the kernel? I'll quickly look for things like kernel version checks, architecture-specific code inclusions, or any conditional compilation that might affect the behavior. I also want to see if there are any specific options being set for debugging or performance tuning. Overall, I want to form a mental model of how the ext4 components are built and integrated into the kernel, with an eye towards potential areas for modification, debugging, or optimization.



8/2 2026/06/23 09:26 tool grepper
0m Args:
{
  "Expression": "xattr",
  "PathPrefix": "fs/ext4/Makefile"
}

Results:
{
  "Output": "fs/ext4/Makefile=8=ext4-y\t:= balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \\\n--\nfs/ext4/Makefile-11-\t\tmmp.o move_extent.o namei.o page-io.o readpage.o resize.o \\\nfs/ext4/Makefile:12:\t\tsuper.o symlink.o sysfs.o xattr.o xattr_hurd.o xattr_trusted.o \\\nfs/ext4/Makefile:13:\t\txattr_user.o fast_commit.o orphan.o\nfs/ext4/Makefile-14-\nfs/ext4/Makefile=15=ext4-$(CONFIG_EXT4_FS_POSIX_ACL)\t+= acl.o\nfs/ext4/Makefile:16:ext4-$(CONFIG_EXT4_FS_SECURITY)\t\t+= xattr_security.o\nfs/ext4/Makefile-17-ext4-test-objs\t\t\t\t+= inode-test.o mballoc-test.o \\\n"
}

9/2 2026/06/23 09:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11073
							output: 28
							thoughts: 35
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own:

**Investigating Extended Attribute Compilation in EXT4**

Alright, so `xattr.o` is being compiled unconditionally when `EXT4_FS` is enabled. That's a given, but I'm curious if we have any more granular control over extended attribute inode features. I need to dig deeper into the configuration options to see if there are any specific settings that influence the inclusion or behavior of EA inodes. Maybe there's a more nuanced control than just a blanket enable/disable. This is important for optimization and understanding how flexible this part of the file system can be configured. Let me see what I can find...



10/2 2026/06/23 09:26 tool grepper
0m Args:
{
  "Expression": "ea_inode",
  "PathPrefix": "fs/ext4/"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 816 lines.\nUse more precise expression if possible.\n\nfs/ext4/ext4.h=963=do {\t\t\t\t\t\t\t\t\t\t\\\n--\nfs/ext4/ext4.h-1016- *  I_DATA_SEM_QUOTA  - Used for quota inodes only\nfs/ext4/ext4.h:1017: *  I_DATA_SEM_EA     - Used for ea_inodes only\nfs/ext4/ext4.h-1018- *  I_DATA_SEM_JOURNAL - Used for journal inode only\n--\nfs/ext4/ext4.h=1592=struct ext4_sb_info {\n--\nfs/ext4/ext4.h-1777-\tstruct mb_cache *s_ea_block_cache;\nfs/ext4/ext4.h:1778:\tstruct mb_cache *s_ea_inode_cache;\nfs/ext4/ext4.h-1779-\nfs/ext4/ext4.h-1780-\t/* Deferred iput for EA inodes to avoid lock ordering issues */\nfs/ext4/ext4.h:1781:\tstruct llist_head s_ea_inode_to_free;\nfs/ext4/ext4.h:1782:\tstruct delayed_work s_ea_inode_work;\nfs/ext4/ext4.h-1783-\n--\nfs/ext4/ext4.h=2037=enum {\n--\nfs/ext4/ext4.h-2045-\tEXT4_STATE_EXT_PRECACHED,\t/* extents have been precached */\nfs/ext4/ext4.h:2046:\tEXT4_STATE_LUSTRE_EA_INODE,\t/* Lustre-style ea_inode */\nfs/ext4/ext4.h-2047-\tEXT4_STATE_VERITY_IN_PROGRESS,\t/* building fs-verity Merkle tree */\n--\nfs/ext4/ext4.h=2314=EXT4_FEATURE_INCOMPAT_FUNCS(flex_bg,\t\tFLEX_BG)\nfs/ext4/ext4.h:2315:EXT4_FEATURE_INCOMPAT_FUNCS(ea_inode,\t\tEA_INODE)\nfs/ext4/ext4.h-2316-EXT4_FEATURE_INCOMPAT_FUNCS(dirdata,\t\tDIRDATA)\n--\nfs/ext4/inode.c=151=int ext4_inode_is_fast_symlink(struct inode *inode)\nfs/ext4/inode.c-152-{\nfs/ext4/inode.c:153:\tif (!ext4_has_feature_ea_inode(inode-\u003ei_sb)) {\nfs/ext4/inode.c-154-\t\tint ea_blocks = EXT4_I(inode)-\u003ei_file_acl ?\n--\nfs/ext4/inode.c=169=void ext4_evict_inode(struct inode *inode)\n--\nfs/ext4/inode.c-185-\tif (EXT4_I(inode)-\u003ei_flags \u0026 EXT4_EA_INODE_FL)\nfs/ext4/inode.c:186:\t\text4_evict_ea_inode(inode);\nfs/ext4/inode.c-187-\tif (inode-\u003ei_nlink) {\n--\nfs/ext4/inode.c=5211=static int check_igot_inode(struct inode *inode, ext4_iget_flags flags,\n--\nfs/ext4/inode.c-5222-\t\t    EXT4_I(inode)-\u003ei_file_acl) {\nfs/ext4/inode.c:5223:\t\t\terr_str = \"ea_inode with extended attributes\";\nfs/ext4/inode.c-5224-\t\t\tgoto error;\n--\nfs/ext4/inode.c-5229-\t\t\t * open_by_handle_at() could provide an old inode number\nfs/ext4/inode.c:5230:\t\t\t * that has since been reused for an ea_inode; this does\nfs/ext4/inode.c-5231-\t\t\t * not indicate filesystem corruption\n--\nfs/ext4/super.c=1282=static void ext4_put_super(struct super_block *sb)\n--\nfs/ext4/super.c-1306-\t/* Drain deferred EA inode iputs while quota is still active. */\nfs/ext4/super.c:1307:\text4_drain_ea_inode_work(sbi);\nfs/ext4/super.c-1308-\text4_quotas_off(sb, EXT4_MAXQUOTAS);\n--\nfs/ext4/super.c-1367-\nfs/ext4/super.c:1368:\text4_xattr_destroy_cache(sbi-\u003es_ea_inode_cache);\nfs/ext4/super.c:1369:\tsbi-\u003es_ea_inode_cache = NULL;\nfs/ext4/super.c-1370-\n--\nfs/ext4/super.c=4685=static int ext4_init_metadata_csum(struct super_block *sb, struct ext4_super_block *es)\n--\nfs/ext4/super.c-4714-\telse if (ext4_has_feature_metadata_csum(sb) ||\nfs/ext4/super.c:4715:\t\t ext4_has_feature_ea_inode(sb))\nfs/ext4/super.c-4716-\t\tsbi-\u003es_csum_seed = ext4_chksum(~0, es-\u003es_uuid,\n--\nfs/ext4/super.c=4721=static int ext4_check_feature_compatibility(struct super_block *sb,\n--\nfs/ext4/super.c-4743-\t\t/*\nfs/ext4/super.c:4744:\t\t * ea_inode feature uses l_i_version field which is not\nfs/ext4/super.c-4745-\t\t * available in HURD_COMPAT mode.\nfs/ext4/super.c-4746-\t\t */\nfs/ext4/super.c:4747:\t\tif (ext4_has_feature_ea_inode(sb)) {\nfs/ext4/super.c-4748-\t\t\text4_msg(sb, KERN_ERR,\nfs/ext4/super.c:4749:\t\t\t\t \"ea_inode feature is not supported for Hurd\");\nfs/ext4/super.c-4750-\t\t\treturn -EINVAL;\n--\nfs/ext4/super.c=5346=static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)\n--\nfs/ext4/super.c-5518-\t */\nfs/ext4/super.c:5519:\text4_init_ea_inode_work(sbi);\nfs/ext4/super.c-5520-\n--\nfs/ext4/super.c-5560-\nfs/ext4/super.c:5561:\t\tif (ext4_has_feature_ea_inode(sb)) {\nfs/ext4/super.c:5562:\t\t\tsbi-\u003es_ea_inode_cache = ext4_xattr_create_cache();\nfs/ext4/super.c:5563:\t\t\tif (!sbi-\u003es_ea_inode_cache) {\nfs/ext4/super.c-5564-\t\t\t\text4_msg(sb, KERN_ERR,\nfs/ext4/super.c:5565:\t\t\t\t\t \"Failed to create ea_inode_cache\");\nfs/ext4/super.c-5566-\t\t\t\terr = -EINVAL;\n--\nfs/ext4/super.c-5760-\t/* Drain deferred EA inode iputs before quota shutdown */\nfs/ext4/super.c:5761:\text4_drain_ea_inode_work(sbi);\nfs/ext4/super.c-5762-\text4_quotas_off(sb, EXT4_MAXQUOTAS);\nfs/ext4/super.c=5763=failed_mount8: __maybe_unused\n--\nfs/ext4/super.c-5782-\t/* Drain deferred EA inode iputs before freeing structures */\nfs/ext4/super.c:5783:\text4_drain_ea_inode_work(sbi);\nfs/ext4/super.c:5784:\text4_xattr_destroy_cache(sbi-\u003es_ea_inode_cache);\nfs/ext4/super.c:5785:\tsbi-\u003es_ea_inode_cache = NULL;\nfs/ext4/super.c-5786-\n--\nfs/ext4/super.c-5794-\t/* Drain deferred EA inode iputs from journal replay */\nfs/ext4/super.c:5795:\text4_drain_ea_inode_work(sbi);\nfs/ext4/super.c-5796-\text4_es_unregister_shrinker(sbi);\n--\nfs/ext4/xattr.c=101=const struct xattr_handler * const ext4_xattr_handlers[] = {\n--\nfs/ext4/xattr.c-114-#define EA_INODE_CACHE(inode)\t(((struct ext4_sb_info *) \\\nfs/ext4/xattr.c:115:\t\t\t\tinode-\u003ei_sb-\u003es_fs_info)-\u003es_ea_inode_cache)\nfs/ext4/xattr.c-116-\nfs/ext4/xattr.c-117-#ifdef CONFIG_LOCKDEP\nfs/ext4/xattr.c:118:void ext4_xattr_inode_set_class(struct inode *ea_inode)\nfs/ext4/xattr.c-119-{\nfs/ext4/xattr.c:120:\tstruct ext4_inode_info *ei = EXT4_I(ea_inode);\nfs/ext4/xattr.c-121-\nfs/ext4/xattr.c:122:\tlockdep_set_subclass(\u0026ea_inode-\u003ei_rwsem, 1);\nfs/ext4/xattr.c-123-\t(void) ei;\t/* shut up clang warning if !CONFIG_LOCKDEP */\n--\nfs/ext4/xattr.c=187=check_xattrs(struct inode *inode, struct buffer_head *bh,\n--\nfs/ext4/xattr.c-240-\nfs/ext4/xattr.c:241:\t\tif (!ext4_has_feature_ea_inode(inode-\u003ei_sb) \u0026\u0026 ea_ino) {\nfs/ext4/xattr.c:242:\t\t\terr_str = \"ea_inode specified without ea_inode feature enabled\";\nfs/ext4/xattr.c-243-\t\t\tgoto errout;\n--\nfs/ext4/xattr.c=349=ext4_xattr_inode_hash(struct ext4_sb_info *sbi, const void *buffer, size_t size)\n--\nfs/ext4/xattr.c-353-\nfs/ext4/xattr.c:354:static u64 ext4_xattr_inode_get_ref(struct inode *ea_inode)\nfs/ext4/xattr.c-355-{\nfs/ext4/xattr.c:356:\treturn ((u64) inode_get_ctime_sec(ea_inode) \u003c\u003c 32) |\nfs/ext4/xattr.c:357:\t\t(u32) inode_peek_iversion_raw(ea_inode);\nfs/ext4/xattr.c-358-}\nfs/ext4/xattr.c-359-\nfs/ext4/xattr.c:360:static void ext4_xattr_inode_set_ref(struct inode *ea_inode, u64 ref_count)\nfs/ext4/xattr.c-361-{\nfs/ext4/xattr.c:362:\tinode_set_ctime(ea_inode, (u32)(ref_count \u003e\u003e 32), 0);\nfs/ext4/xattr.c:363:\tinode_set_iversion_raw(ea_inode, ref_count \u0026 0xffffffff);\nfs/ext4/xattr.c-364-}\nfs/ext4/xattr.c-365-\nfs/ext4/xattr.c:366:static u32 ext4_xattr_inode_get_hash(struct inode *ea_inode)\nfs/ext4/xattr.c-367-{\nfs/ext4/xattr.c:368:\treturn (u32) inode_get_atime_sec(ea_inode);\nfs/ext4/xattr.c-369-}\nfs/ext4/xattr.c-370-\nfs/ext4/xattr.c:371:static void ext4_xattr_inode_set_hash(struct inode *ea_inode, u32 hash)\nfs/ext4/xattr.c-372-{\nfs/ext4/xattr.c:373:\tinode_set_atime(ea_inode, hash, 0);\nfs/ext4/xattr.c-374-}\n--\nfs/ext4/xattr.c-378- */\nfs/ext4/xattr.c:379:static int ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t size)\nfs/ext4/xattr.c-380-{\nfs/ext4/xattr.c:381:\tint blocksize = 1 \u003c\u003c ea_inode-\u003ei_blkbits;\nfs/ext4/xattr.c:382:\tint bh_count = (size + blocksize - 1) \u003e\u003e ea_inode-\u003ei_blkbits;\nfs/ext4/xattr.c-383-\tint tail_size = (size % blocksize) ?: blocksize;\n--\nfs/ext4/xattr.c-393-\nfs/ext4/xattr.c:394:\tret = ext4_bread_batch(ea_inode, 0 /* block */, bh_count,\nfs/ext4/xattr.c-395-\t\t\t       true /* wait */, bhs);\n--\nfs/ext4/xattr.c-399-\tfor (i = 0; i \u003c bh_count; i++) {\nfs/ext4/xattr.c:400:\t\t/* There shouldn't be any holes in ea_inode. */\nfs/ext4/xattr.c-401-\t\tif (!bhs[i]) {\n--\nfs/ext4/xattr.c=420=static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,\nfs/ext4/xattr.c:421:\t\t\t\t u32 ea_inode_hash, struct inode **ea_inode)\nfs/ext4/xattr.c-422-{\n--\nfs/ext4/xattr.c-449-\t * implementation does not have hash validation, rather it has a\nfs/ext4/xattr.c:450:\t * backpointer from ea_inode to the parent inode.\nfs/ext4/xattr.c-451-\t */\nfs/ext4/xattr.c:452:\tif (ea_inode_hash != ext4_xattr_inode_get_hash(inode) \u0026\u0026\nfs/ext4/xattr.c-453-\t    EXT4_XATTR_INODE_GET_PARENT(inode) == parent-\u003ei_ino \u0026\u0026\n--\nfs/ext4/xattr.c-462-\nfs/ext4/xattr.c:463:\t*ea_inode = inode;\nfs/ext4/xattr.c-464-\treturn 0;\n--\nfs/ext4/xattr.c-467-/* Remove entry from mbcache when EA inode is getting evicted */\nfs/ext4/xattr.c:468:void ext4_evict_ea_inode(struct inode *inode)\nfs/ext4/xattr.c-469-{\n--\nfs/ext4/xattr.c=482=static int\nfs/ext4/xattr.c:483:ext4_xattr_inode_verify_hashes(struct inode *ea_inode,\nfs/ext4/xattr.c-484-\t\t\t       struct ext4_xattr_entry *entry, void *buffer,\n--\nfs/ext4/xattr.c-489-\t/* Verify stored hash matches calculated hash. */\nfs/ext4/xattr.c:490:\thash = ext4_xattr_inode_hash(EXT4_SB(ea_inode-\u003ei_sb), buffer, size);\nfs/ext4/xattr.c:491:\tif (hash != ext4_xattr_inode_get_hash(ea_inode))\nfs/ext4/xattr.c-492-\t\treturn -EFSCORRUPTED;\n--\nfs/ext4/xattr.c=525=ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,\n--\nfs/ext4/xattr.c-527-{\nfs/ext4/xattr.c:528:\tstruct mb_cache *ea_inode_cache = EA_INODE_CACHE(inode);\nfs/ext4/xattr.c:529:\tstruct inode *ea_inode;\nfs/ext4/xattr.c-530-\tint err;\n--\nfs/ext4/xattr.c-532-\terr = ext4_xattr_inode_iget(inode, le32_to_cpu(entry-\u003ee_value_inum),\nfs/ext4/xattr.c:533:\t\t\t\t    le32_to_cpu(entry-\u003ee_hash), \u0026ea_inode);\nfs/ext4/xattr.c-534-\tif (err) {\nfs/ext4/xattr.c:535:\t\tea_inode = NULL;\nfs/ext4/xattr.c-536-\t\tgoto out;\n--\nfs/ext4/xattr.c-538-\nfs/ext4/xattr.c:539:\tif (i_size_read(ea_inode) != size) {\nfs/ext4/xattr.c:540:\t\text4_warning_inode(ea_inode,\nfs/ext4/xattr.c:541:\t\t\t\t   \"ea_inode file size=%llu entry size=%zu\",\nfs/ext4/xattr.c:542:\t\t\t\t   i_size_read(ea_inode), size);\nfs/ext4/xattr.c-543-\t\terr = -EFSCORRUPTED;\n--\nfs/ext4/xattr.c-546-\nfs/ext4/xattr.c:547:\terr = ext4_xattr_inode_read(ea_inode, buffer, size);\nfs/ext4/xattr.c-548-\tif (err)\n--\nfs/ext4/xattr.c-550-\nfs/ext4/xattr.c:551:\tif (!ext4_test_inode_state(ea_inode, EXT4_STATE_LUSTRE_EA_INODE)) {\nfs/ext4/xattr.c:552:\t\terr = ext4_xattr_inode_verify_hashes(ea_inode, entry, buffer,\nfs/ext4/xattr.c-553-\t\t\t\t\t\t     size);\nfs/ext4/xattr.c-554-\t\tif (err) {\nfs/ext4/xattr.c:555:\t\t\text4_warning_inode(ea_inode,\nfs/ext4/xattr.c-556-\t\t\t\t\t   \"EA inode hash validation failed\");\n--\nfs/ext4/xattr.c-559-\nfs/ext4/xattr.c:560:\t\tif (ea_inode_cache)\nfs/ext4/xattr.c:561:\t\t\tmb_cache_entry_create(ea_inode_cache, GFP_NOFS,\nfs/ext4/xattr.c:562:\t\t\t\t\text4_xattr_inode_get_hash(ea_inode),\nfs/ext4/xattr.c:563:\t\t\t\t\tea_inode-\u003ei_ino, true /* reusable */);\nfs/ext4/xattr.c-564-\t}\nfs/ext4/xattr.c-565-out:\nfs/ext4/xattr.c:566:\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-567-\treturn err;\n--\nfs/ext4/xattr.c=851=int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)\n--\nfs/ext4/xattr.c-857-\tstruct ext4_xattr_entry *entry;\nfs/ext4/xattr.c:858:\tqsize_t ea_inode_refs = 0;\nfs/ext4/xattr.c-859-\tint ret;\n--\nfs/ext4/xattr.c-872-\t\t\tif (entry-\u003ee_value_inum)\nfs/ext4/xattr.c:873:\t\t\t\tea_inode_refs++;\nfs/ext4/xattr.c-874-\t}\n--\nfs/ext4/xattr.c-890-\t\t\tif (entry-\u003ee_value_inum)\nfs/ext4/xattr.c:891:\t\t\t\tea_inode_refs++;\nfs/ext4/xattr.c-892-\t}\nfs/ext4/xattr.c:893:\t*usage = ea_inode_refs + 1;\nfs/ext4/xattr.c-894-\tret = 0;\n--\nfs/ext4/xattr.c=924=static void ext4_xattr_inode_free_quota(struct inode *parent,\nfs/ext4/xattr.c:925:\t\t\t\t\tstruct inode *ea_inode,\nfs/ext4/xattr.c-926-\t\t\t\t\tsize_t len)\nfs/ext4/xattr.c-927-{\nfs/ext4/xattr.c:928:\tif (ea_inode \u0026\u0026\nfs/ext4/xattr.c:929:\t    ext4_test_inode_state(ea_inode, EXT4_STATE_LUSTRE_EA_INODE))\nfs/ext4/xattr.c-930-\t\treturn;\n--\nfs/ext4/xattr.c=935=int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,\n--\nfs/ext4/xattr.c-966-\nfs/ext4/xattr.c:967:\t/* We are done if ea_inode feature is not enabled. */\nfs/ext4/xattr.c:968:\tif (!ext4_has_feature_ea_inode(sb))\nfs/ext4/xattr.c-969-\t\treturn credits;\nfs/ext4/xattr.c-970-\nfs/ext4/xattr.c:971:\t/* New ea_inode, inode map, block bitmap, group descriptor. */\nfs/ext4/xattr.c-972-\tcredits += 4;\n--\nfs/ext4/xattr.c-986-\tif (!is_create) {\nfs/ext4/xattr.c:987:\t\t/* Dereference ea_inode holding old xattr value.\nfs/ext4/xattr.c:988:\t\t * Old ea_inode, inode map, block bitmap, group descriptor.\nfs/ext4/xattr.c-989-\t\t */\n--\nfs/ext4/xattr.c-991-\nfs/ext4/xattr.c:992:\t\t/* Data blocks for old ea_inode. */\nfs/ext4/xattr.c-993-\t\tblocks = XATTR_SIZE_MAX \u003e\u003e sb-\u003es_blocksize_bits;\n--\nfs/ext4/xattr.c-995-\t\t/* Indirection block or one level of extent tree for old\nfs/ext4/xattr.c:996:\t\t * ea_inode.\nfs/ext4/xattr.c-997-\t\t */\n--\nfs/ext4/xattr.c-1004-\t/* We may need to clone the existing xattr block in which case we need\nfs/ext4/xattr.c:1005:\t * to increment ref counts for existing ea_inodes referenced by it.\nfs/ext4/xattr.c-1006-\t */\n--\nfs/ext4/xattr.c-1011-\t\t\tif (entry-\u003ee_value_inum)\nfs/ext4/xattr.c:1012:\t\t\t\t/* Ref count update on ea_inode. */\nfs/ext4/xattr.c-1013-\t\t\t\tcredits += 1;\n--\nfs/ext4/xattr.c-1017-\nfs/ext4/xattr.c:1018:static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode,\nfs/ext4/xattr.c-1019-\t\t\t\t       int ref_change)\n--\nfs/ext4/xattr.c-1024-\nfs/ext4/xattr.c:1025:\tinode_lock_nested(ea_inode, I_MUTEX_XATTR);\nfs/ext4/xattr.c-1026-\nfs/ext4/xattr.c:1027:\tret = ext4_reserve_inode_write(handle, ea_inode, \u0026iloc);\nfs/ext4/xattr.c-1028-\tif (ret)\n--\nfs/ext4/xattr.c-1030-\nfs/ext4/xattr.c:1031:\tref_count = ext4_xattr_inode_get_ref(ea_inode);\nfs/ext4/xattr.c-1032-\tif ((ref_count == 0 \u0026\u0026 ref_change \u003c 0) || (ref_count == U64_MAX \u0026\u0026 ref_change \u003e 0)) {\nfs/ext4/xattr.c:1033:\t\text4_error_inode(ea_inode, __func__, __LINE__, 0,\nfs/ext4/xattr.c-1034-\t\t\t\"EA inode %llu ref wraparound: ref_count=%lld ref_change=%d\",\nfs/ext4/xattr.c:1035:\t\t\tea_inode-\u003ei_ino, ref_count, ref_change);\nfs/ext4/xattr.c-1036-\t\tbrelse(iloc.bh);\n--\nfs/ext4/xattr.c-1040-\tref_count += ref_change;\nfs/ext4/xattr.c:1041:\text4_xattr_inode_set_ref(ea_inode, ref_count);\nfs/ext4/xattr.c-1042-\n--\nfs/ext4/xattr.c-1044-\t\tif (ref_count == 1) {\nfs/ext4/xattr.c:1045:\t\t\tWARN_ONCE(ea_inode-\u003ei_nlink, \"EA inode %llu i_nlink=%u\",\nfs/ext4/xattr.c:1046:\t\t\t\t  ea_inode-\u003ei_ino, ea_inode-\u003ei_nlink);\nfs/ext4/xattr.c-1047-\nfs/ext4/xattr.c:1048:\t\t\tset_nlink(ea_inode, 1);\nfs/ext4/xattr.c:1049:\t\t\text4_orphan_del(handle, ea_inode);\nfs/ext4/xattr.c-1050-\t\t}\n--\nfs/ext4/xattr.c-1052-\t\tif (ref_count == 0) {\nfs/ext4/xattr.c:1053:\t\t\tWARN_ONCE(ea_inode-\u003ei_nlink != 1,\nfs/ext4/xattr.c-1054-\t\t\t\t  \"EA inode %llu i_nlink=%u\",\nfs/ext4/xattr.c:1055:\t\t\t\t  ea_inode-\u003ei_ino, ea_inode-\u003ei_nlink);\nfs/ext4/xattr.c-1056-\nfs/ext4/xattr.c:1057:\t\t\tclear_nlink(ea_inode);\nfs/ext4/xattr.c:1058:\t\t\text4_orphan_add(handle, ea_inode);\nfs/ext4/xattr.c-1059-\t\t}\n--\nfs/ext4/xattr.c-1061-\nfs/ext4/xattr.c:1062:\tret = ext4_mark_iloc_dirty(handle, ea_inode, \u0026iloc);\nfs/ext4/xattr.c-1063-\tif (ret)\nfs/ext4/xattr.c:1064:\t\text4_warning_inode(ea_inode,\nfs/ext4/xattr.c-1065-\t\t\t\t   \"ext4_mark_iloc_dirty() failed ret=%d\", ret);\nfs/ext4/xattr.c-1066-out:\nfs/ext4/xattr.c:1067:\tinode_unlock(ea_inode);\nfs/ext4/xattr.c-1068-\treturn ret;\n--\nfs/ext4/xattr.c-1070-\nfs/ext4/xattr.c:1071:static int ext4_xattr_inode_inc_ref(handle_t *handle, struct inode *ea_inode)\nfs/ext4/xattr.c-1072-{\nfs/ext4/xattr.c:1073:\treturn ext4_xattr_inode_update_ref(handle, ea_inode, 1);\nfs/ext4/xattr.c-1074-}\nfs/ext4/xattr.c-1075-\nfs/ext4/xattr.c:1076:static int ext4_xattr_inode_dec_ref(handle_t *handle, struct inode *ea_inode)\nfs/ext4/xattr.c-1077-{\nfs/ext4/xattr.c:1078:\treturn ext4_xattr_inode_update_ref(handle, ea_inode, -1);\nfs/ext4/xattr.c-1079-}\n--\nfs/ext4/xattr.c=1081=static int ext4_xattr_inode_inc_ref_all(handle_t *handle, struct inode *parent,\n--\nfs/ext4/xattr.c-1083-{\nfs/ext4/xattr.c:1084:\tstruct inode *ea_inode;\nfs/ext4/xattr.c-1085-\tstruct ext4_xattr_entry *entry;\n--\nfs/ext4/xattr.c-1096-\t\t\t\t\t    le32_to_cpu(entry-\u003ee_hash),\nfs/ext4/xattr.c:1097:\t\t\t\t\t    \u0026ea_inode);\nfs/ext4/xattr.c-1098-\t\tif (err)\nfs/ext4/xattr.c-1099-\t\t\tgoto cleanup;\nfs/ext4/xattr.c:1100:\t\terr = ext4_xattr_inode_inc_ref(handle, ea_inode);\nfs/ext4/xattr.c-1101-\t\tif (err) {\nfs/ext4/xattr.c:1102:\t\t\text4_warning_inode(ea_inode, \"inc ref error %d\", err);\nfs/ext4/xattr.c:1103:\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1104-\t\t\tgoto cleanup;\nfs/ext4/xattr.c-1105-\t\t}\nfs/ext4/xattr.c:1106:\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1107-\t}\n--\nfs/ext4/xattr.c-1120-\t\t\t\t\t    le32_to_cpu(entry-\u003ee_hash),\nfs/ext4/xattr.c:1121:\t\t\t\t\t    \u0026ea_inode);\nfs/ext4/xattr.c-1122-\t\tif (err) {\n--\nfs/ext4/xattr.c-1127-\t\t}\nfs/ext4/xattr.c:1128:\t\terr = ext4_xattr_inode_dec_ref(handle, ea_inode);\nfs/ext4/xattr.c-1129-\t\tif (err)\nfs/ext4/xattr.c:1130:\t\t\text4_warning_inode(ea_inode, \"cleanup dec ref error %d\",\nfs/ext4/xattr.c-1131-\t\t\t\t\t   err);\nfs/ext4/xattr.c:1132:\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1133-\t}\n--\nfs/ext4/xattr.c=1156=ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n--\nfs/ext4/xattr.c-1160-{\nfs/ext4/xattr.c:1161:\tstruct inode *ea_inode;\nfs/ext4/xattr.c-1162-\tstruct ext4_xattr_entry *entry;\n--\nfs/ext4/xattr.c-1180-\nfs/ext4/xattr.c:1181:\t/* One credit for dec ref on ea_inode, one for orphan list addition, */\nfs/ext4/xattr.c-1182-\tcredits = 2 + extra_credits;\n--\nfs/ext4/xattr.c-1190-\t\t\t\t\t    le32_to_cpu(entry-\u003ee_hash),\nfs/ext4/xattr.c:1191:\t\t\t\t\t    \u0026ea_inode);\nfs/ext4/xattr.c-1192-\t\tif (err)\n--\nfs/ext4/xattr.c-1199-\t\tif (err \u003c 0) {\nfs/ext4/xattr.c:1200:\t\t\text4_warning_inode(ea_inode, \"Ensure credits err=%d\",\nfs/ext4/xattr.c-1201-\t\t\t\t\t   err);\nfs/ext4/xattr.c:1202:\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1203-\t\t\tcontinue;\n--\nfs/ext4/xattr.c-1208-\t\t\tif (err) {\nfs/ext4/xattr.c:1209:\t\t\t\text4_warning_inode(ea_inode,\nfs/ext4/xattr.c-1210-\t\t\t\t\t\t\"Re-get write access err=%d\",\nfs/ext4/xattr.c-1211-\t\t\t\t\t\terr);\nfs/ext4/xattr.c:1212:\t\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1213-\t\t\t\tcontinue;\n--\nfs/ext4/xattr.c-1216-\nfs/ext4/xattr.c:1217:\t\terr = ext4_xattr_inode_dec_ref(handle, ea_inode);\nfs/ext4/xattr.c-1218-\t\tif (err) {\nfs/ext4/xattr.c:1219:\t\t\text4_warning_inode(ea_inode, \"ea_inode dec ref err=%d\",\nfs/ext4/xattr.c-1220-\t\t\t\t\t   err);\nfs/ext4/xattr.c:1221:\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1222-\t\t\tcontinue;\n--\nfs/ext4/xattr.c-1225-\t\tif (!skip_quota)\nfs/ext4/xattr.c:1226:\t\t\text4_xattr_inode_free_quota(parent, ea_inode,\nfs/ext4/xattr.c-1227-\t\t\t\t\t      le32_to_cpu(entry-\u003ee_value_size));\n--\nfs/ext4/xattr.c-1229-\t\t/*\nfs/ext4/xattr.c:1230:\t\t * Forget about ea_inode within the same transaction that\nfs/ext4/xattr.c-1231-\t\t * decrements the ref count. This avoids duplicate decrements in\n--\nfs/ext4/xattr.c-1237-\nfs/ext4/xattr.c:1238:\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1239-\t\tdirty = true;\n--\nfs/ext4/xattr.c=1263=ext4_xattr_release_block(handle_t *handle, struct inode *inode,\n--\nfs/ext4/xattr.c-1301-\nfs/ext4/xattr.c:1302:\t\tif (ext4_has_feature_ea_inode(inode-\u003ei_sb))\nfs/ext4/xattr.c-1303-\t\t\text4_xattr_inode_dec_ref_all(handle, inode, bh,\n--\nfs/ext4/xattr.c=1358=static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,\n--\nfs/ext4/xattr.c-1375- */\nfs/ext4/xattr.c:1376:static int ext4_xattr_inode_write(handle_t *handle, struct inode *ea_inode,\nfs/ext4/xattr.c-1377-\t\t\t\t  const void *buf, int bufsize)\n--\nfs/ext4/xattr.c-1380-\tunsigned long block = 0;\nfs/ext4/xattr.c:1381:\tint blocksize = ea_inode-\u003ei_sb-\u003es_blocksize;\nfs/ext4/xattr.c:1382:\tint max_blocks = (bufsize + blocksize - 1) \u003e\u003e ea_inode-\u003ei_blkbits;\nfs/ext4/xattr.c-1383-\tint csize, wsize = 0;\n--\nfs/ext4/xattr.c-1392-\nfs/ext4/xattr.c:1393:\t\tret = ext4_map_blocks(handle, ea_inode, \u0026map,\nfs/ext4/xattr.c-1394-\t\t\t\t      EXT4_GET_BLOCKS_CREATE);\nfs/ext4/xattr.c-1395-\t\tif (ret \u003c= 0) {\nfs/ext4/xattr.c:1396:\t\t\text4_mark_inode_dirty(handle, ea_inode);\nfs/ext4/xattr.c-1397-\t\t\tif (ret == -ENOSPC \u0026\u0026\nfs/ext4/xattr.c:1398:\t\t\t    ext4_should_retry_alloc(ea_inode-\u003ei_sb, \u0026retries)) {\nfs/ext4/xattr.c-1399-\t\t\t\tret = 0;\n--\nfs/ext4/xattr.c-1413-\t\t\t\t\t\t\t\tbufsize - wsize;\nfs/ext4/xattr.c:1414:\t\tbh = ext4_getblk(handle, ea_inode, block, 0);\nfs/ext4/xattr.c-1415-\t\tif (IS_ERR(bh))\n--\nfs/ext4/xattr.c-1418-\t\t\tWARN_ON_ONCE(1);\nfs/ext4/xattr.c:1419:\t\t\tEXT4_ERROR_INODE(ea_inode,\nfs/ext4/xattr.c-1420-\t\t\t\t\t \"ext4_getblk() return bh = NULL\");\n--\nfs/ext4/xattr.c-1422-\t\t}\nfs/ext4/xattr.c:1423:\t\tret = ext4_journal_get_write_access(handle, ea_inode-\u003ei_sb, bh,\nfs/ext4/xattr.c-1424-\t\t\t\t\t\t   EXT4_JTR_NONE);\n--\nfs/ext4/xattr.c-1435-\t\tset_buffer_uptodate(bh);\nfs/ext4/xattr.c:1436:\t\text4_handle_dirty_metadata(handle, ea_inode, bh);\nfs/ext4/xattr.c-1437-\n--\nfs/ext4/xattr.c-1442-\nfs/ext4/xattr.c:1443:\tinode_lock(ea_inode);\nfs/ext4/xattr.c:1444:\ti_size_write(ea_inode, wsize);\nfs/ext4/xattr.c:1445:\text4_update_i_disksize(ea_inode, wsize);\nfs/ext4/xattr.c:1446:\tinode_unlock(ea_inode);\nfs/ext4/xattr.c-1447-\nfs/ext4/xattr.c:1448:\tret2 = ext4_mark_inode_dirty(handle, ea_inode);\nfs/ext4/xattr.c-1449-\tif (unlikely(ret2 \u0026\u0026 !ret))\n--\nfs/ext4/xattr.c=1461=static struct inode *ext4_xattr_inode_create(handle_t *handle,\n--\nfs/ext4/xattr.c-1463-{\nfs/ext4/xattr.c:1464:\tstruct inode *ea_inode = NULL;\nfs/ext4/xattr.c-1465-\tuid_t owner[2] = { i_uid_read(inode), i_gid_read(inode) };\n--\nfs/ext4/xattr.c-1478-\t */\nfs/ext4/xattr.c:1479:\tea_inode = ext4_new_inode(handle, inode-\u003ei_sb-\u003es_root-\u003ed_inode,\nfs/ext4/xattr.c-1480-\t\t\t\t  S_IFREG | 0600, NULL, inode-\u003ei_ino + 1, owner,\nfs/ext4/xattr.c-1481-\t\t\t\t  EXT4_EA_INODE_FL);\nfs/ext4/xattr.c:1482:\tif (!IS_ERR(ea_inode)) {\nfs/ext4/xattr.c:1483:\t\tea_inode-\u003ei_op = \u0026ext4_file_inode_operations;\nfs/ext4/xattr.c:1484:\t\tea_inode-\u003ei_fop = \u0026ext4_file_operations;\nfs/ext4/xattr.c:1485:\t\text4_set_aops(ea_inode);\nfs/ext4/xattr.c:1486:\t\text4_xattr_inode_set_class(ea_inode);\nfs/ext4/xattr.c:1487:\t\tunlock_new_inode(ea_inode);\nfs/ext4/xattr.c:1488:\t\text4_xattr_inode_set_ref(ea_inode, 1);\nfs/ext4/xattr.c:1489:\t\text4_xattr_inode_set_hash(ea_inode, hash);\nfs/ext4/xattr.c:1490:\t\terr = ext4_mark_inode_dirty(handle, ea_inode);\nfs/ext4/xattr.c-1491-\t\tif (!err)\nfs/ext4/xattr.c:1492:\t\t\terr = ext4_inode_attach_jinode(ea_inode);\nfs/ext4/xattr.c-1493-\t\tif (err) {\nfs/ext4/xattr.c:1494:\t\t\tif (ext4_xattr_inode_dec_ref(handle, ea_inode))\nfs/ext4/xattr.c:1495:\t\t\t\text4_warning_inode(ea_inode,\n\n"
}

11/2 2026/06/23 09:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22613
							output: 30
							thoughts: 31
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own mental process:

**Investigating the `ea_inode` Feature**

Right, so I'm looking at this `ea_inode` thing. Is it a configurable option? I feel like I've encountered this before. Let me quickly double-check the Ext4 Kconfig file again, just to be sure. I seem to recall that's where I would find the configuration details. Let me see what I can find.



12/2 2026/06/23 09:27 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "ea_inode",
  "PathPrefix": "fs/ext4/Kconfig"
}

Results:
{
  "error": "no matches"
}

13/2 2026/06/23 09:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22794
							output: 42
							thoughts: 59
						

Thoughts:
Okay, let's break this down.

**Understanding ea_inode and EXT4**

Right, so *ea_inode* isn't something I need to wrangle with at compile time. It's a filesystem feature, which is much more manageable, in my experience. I was initially worried I'd have to tweak some obscure configuration flag, but that's not the case. The crucial thing, the *only* necessary config option, is `EXT4_FS`. That's good news, it simplifies things.

Now, let's dig into the nitty-gritty. I should really check out `ext4_put_ea_inode` and `ext4_ea_inode_work`. Those functions are where the real logic of this feature is implemented, so understanding those will give me a better grasp of how `ea_inode` actually *works* within EXT4. I'll need to see how they interact with the overall filesystem operations. I'll focus on the data structures and control flow. That should give me the insights I need.



14/2 2026/06/23 09:27 tool grepper
0m Args:
{
  "Expression": "ext4_put_ea_inode|ext4_ea_inode_work",
  "PathPrefix": "fs/ext4/"
}

Results:
{
  "Output": "fs/ext4/xattr.c=525=ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,\n--\nfs/ext4/xattr.c-565-out:\nfs/ext4/xattr.c:566:\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-567-\treturn err;\n--\nfs/ext4/xattr.c=1081=static int ext4_xattr_inode_inc_ref_all(handle_t *handle, struct inode *parent,\n--\nfs/ext4/xattr.c-1102-\t\t\text4_warning_inode(ea_inode, \"inc ref error %d\", err);\nfs/ext4/xattr.c:1103:\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1104-\t\t\tgoto cleanup;\nfs/ext4/xattr.c-1105-\t\t}\nfs/ext4/xattr.c:1106:\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1107-\t}\n--\nfs/ext4/xattr.c-1131-\t\t\t\t\t   err);\nfs/ext4/xattr.c:1132:\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1133-\t}\n--\nfs/ext4/xattr.c=1156=ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\n--\nfs/ext4/xattr.c-1201-\t\t\t\t\t   err);\nfs/ext4/xattr.c:1202:\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1203-\t\t\tcontinue;\n--\nfs/ext4/xattr.c-1211-\t\t\t\t\t\terr);\nfs/ext4/xattr.c:1212:\t\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1213-\t\t\t\tcontinue;\n--\nfs/ext4/xattr.c-1220-\t\t\t\t\t   err);\nfs/ext4/xattr.c:1221:\t\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1222-\t\t\tcontinue;\n--\nfs/ext4/xattr.c-1237-\nfs/ext4/xattr.c:1238:\t\text4_put_ea_inode(parent-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1239-\t\tdirty = true;\n--\nfs/ext4/xattr.c=1461=static struct inode *ext4_xattr_inode_create(handle_t *handle,\n--\nfs/ext4/xattr.c-1496-\t\t\t\t\t\"cleanup dec ref error %d\", err);\nfs/ext4/xattr.c:1497:\t\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1498-\t\t\treturn ERR_PTR(err);\n--\nfs/ext4/xattr.c=1516=ext4_xattr_inode_cache_find(struct inode *inode, const void *value,\n--\nfs/ext4/xattr.c-1555-\t\t}\nfs/ext4/xattr.c:1556:\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1557-\tnext_entry:\n--\nfs/ext4/xattr.c=1567=static struct inode *ext4_xattr_inode_lookup_create(handle_t *handle,\n--\nfs/ext4/xattr.c-1606-out_err:\nfs/ext4/xattr.c:1607:\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-1608-\text4_xattr_inode_free_quota(inode, NULL, value_len);\n--\nfs/ext4/xattr.c=1618=static int ext4_xattr_set_entry(struct ext4_xattr_info *i,\n--\nfs/ext4/xattr.c-1839-out:\nfs/ext4/xattr.c:1840:\text4_put_ea_inode(inode-\u003ei_sb, old_ea_inode);\nfs/ext4/xattr.c-1841-\treturn ret;\n--\nfs/ext4/xattr.c=1888=ext4_xattr_block_set(handle_t *handle, struct inode *inode,\n--\nfs/ext4/xattr.c-2001-\t\t\t}\nfs/ext4/xattr.c:2002:\t\t\text4_put_ea_inode(inode-\u003ei_sb, tmp_inode);\nfs/ext4/xattr.c-2003-\n--\nfs/ext4/xattr.c-2141-\t\t\t\t\t\t\t   error);\nfs/ext4/xattr.c:2142:\t\t\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-2143-\t\t\t\tea_inode = NULL;\n--\nfs/ext4/xattr.c-2190-\t\t}\nfs/ext4/xattr.c:2191:\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-2192-\t}\n--\nfs/ext4/xattr.c=2239=int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,\n--\nfs/ext4/xattr.c-2272-\t\t\t\t\t\t    i_size_read(ea_inode));\nfs/ext4/xattr.c:2273:\t\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-2274-\t\t}\n--\nfs/ext4/xattr.c-2284-\t}\nfs/ext4/xattr.c:2285:\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-2286-\treturn 0;\n--\nfs/ext4/xattr.c=2860=int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,\n--\nfs/ext4/xattr.c-2931-\t\t\t\t\t      le32_to_cpu(entry-\u003ee_value_size));\nfs/ext4/xattr.c:2932:\t\t\t\text4_put_ea_inode(inode-\u003ei_sb, ea_inode);\nfs/ext4/xattr.c-2933-\t\t\t}\n--\nfs/ext4/xattr.c-2962- */\nfs/ext4/xattr.c:2963:static void ext4_ea_inode_work(struct work_struct *work)\nfs/ext4/xattr.c-2964-{\n--\nfs/ext4/xattr.c-2991- */\nfs/ext4/xattr.c:2992:void ext4_put_ea_inode(struct super_block *sb, struct inode *inode)\nfs/ext4/xattr.c-2993-{\n--\nfs/ext4/xattr.c=3008=void ext4_init_ea_inode_work(struct ext4_sb_info *sbi)\n--\nfs/ext4/xattr.c-3010-\tinit_llist_head(\u0026sbi-\u003es_ea_inode_to_free);\nfs/ext4/xattr.c:3011:\tINIT_DELAYED_WORK(\u0026sbi-\u003es_ea_inode_work, ext4_ea_inode_work);\nfs/ext4/xattr.c-3012-}\n--\nfs/ext4/xattr.h=186=extern void ext4_init_ea_inode_work(struct ext4_sb_info *sbi);\nfs/ext4/xattr.h:187:extern void ext4_put_ea_inode(struct super_block *sb, struct inode *inode);\nfs/ext4/xattr.h-188-\n"
}

15/2 2026/06/23 09:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25355
							output: 32
							thoughts: 363
						

Thoughts:
**Fuzzing Target Identification: Extended Attributes in ext4**

Okay, so this patch is obviously adding some new functionality related to extended attributes (EAs) within the ext4 filesystem. Let's see, the core functions introduced appear to be `ext4_put_ea_inode` and `ext4_ea_inode_work`. My initial thought is to target these for fuzzing.

But wait, looking closer, `ext4_drain_ea_inode_work` is also relevant, even though it's declared `static inline` in `fs/ext4/xattr.h`.  That means we can't directly fuzz it as a symbol, but we should consider fuzzing its callers, or at the very least, keep it in mind. Beyond that I should also target `ext4_xattr_delete_inode`, and `ext4_xattr_inode_dec_ref_all`, and `ext4_xattr_release_block` as potential areas of interest.

Okay, let me check the declaration of `ext4_ea_inode_work`. Yes, it's declared as static. This means we can't fuzz `ext4_ea_inode_work` directly either.  So, we can probably summarize our fuzzing targets as:

- `ext4_put_ea_inode` (non-static)
- `ext4_ea_inode_work` (static, consider callers)
- `ext4_xattr_delete_inode` (non-static)
- `ext4_xattr_inode_dec_ref_all` (non-static)

Right, now, what about configuration options? I'll need to make sure I have the right settings enabled to even test these functions. Let's start with `EXT4_FS`. Time to double check `fs/ext4/Kconfig` to be sure I'm not missing anything else.



16/2 2026/06/23 09:27 tool grepper
0m Args:
{
  "Expression": "config EXT4_FS",
  "PathPrefix": "fs/ext4/Kconfig"
}

Results:
{
  "Output": "fs/ext4/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/ext4/Kconfig:2:config EXT4_FS\nfs/ext4/Kconfig-3-\ttristate \"The Extended 4 (ext4) filesystem\"\n--\nfs/ext4/Kconfig=35=config EXT4_USE_FOR_EXT2\n--\nfs/ext4/Kconfig-45-\nfs/ext4/Kconfig:46:config EXT4_FS_POSIX_ACL\nfs/ext4/Kconfig-47-\tbool \"Ext4 POSIX Access Control Lists\"\n--\nfs/ext4/Kconfig-55-\nfs/ext4/Kconfig:56:config EXT4_FS_SECURITY\nfs/ext4/Kconfig-57-\tbool \"Ext4 Security Labels\"\n"
}

17/2 2026/06/23 09:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26377
							output: 52
							thoughts: 192
						

Thoughts:
**Analyzing EXT4 Extended Attributes**

Okay, so I'm focusing on extended attributes (XATTRs) within the EXT4 filesystem. `EXT4_FS` seems to be the core, but let's quickly confirm that `EXT4_FS_XATTR` isn't defined on its own. Nope, doesn't look like it. Are there any other relevant configurations? No, it's not looking like it.

Now, let's take a look at `iput_if_not_last` in `include/linux/fs.h`. That's a static inline function. That tells me what to expect.

Given all of this, the key symbols I need to focus on are:
*   `ext4_put_ea_inode`
*   `ext4_ea_inode_work`
*   `ext4_xattr_delete_inode`
*   `ext4_xattr_inode_dec_ref_all`
*   `ext4_xattr_release_block`

I'm making a mental note to verify that `ext4_xattr_inode_dec_ref_all` and `ext4_xattr_release_block` are static, though. That's always important to double-check.



18/2 2026/06/23 09:27 tool grepper
0m Args:
{
  "Expression": "ext4_xattr_inode_dec_ref_all|ext4_xattr_release_block",
  "PathPrefix": "fs/ext4/xattr.c"
}

Results:
{
  "Output": "fs/ext4/xattr.c=1155=static void\nfs/ext4/xattr.c:1156:ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,\nfs/ext4/xattr.c-1157-\t\t\t     struct buffer_head *bh,\n--\nfs/ext4/xattr.c=1262=static void\nfs/ext4/xattr.c:1263:ext4_xattr_release_block(handle_t *handle, struct inode *inode,\nfs/ext4/xattr.c-1264-\t\t\t struct buffer_head *bh,\n--\nfs/ext4/xattr.c-1302-\t\tif (ext4_has_feature_ea_inode(inode-\u003ei_sb))\nfs/ext4/xattr.c:1303:\t\t\text4_xattr_inode_dec_ref_all(handle, inode, bh,\nfs/ext4/xattr.c-1304-\t\t\t\t\t\t     BFIRST(bh),\n--\nfs/ext4/xattr.c=1888=ext4_xattr_block_set(handle_t *handle, struct inode *inode,\n--\nfs/ext4/xattr.c-2173-\tif (bs-\u003ebh \u0026\u0026 bs-\u003ebh != new_bh) {\nfs/ext4/xattr.c:2174:\t\text4_xattr_release_block(handle, inode, bs-\u003ebh,\nfs/ext4/xattr.c-2175-\t\t\t\t\t 0 /* extra_credits */);\n--\nfs/ext4/xattr.c=2860=int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,\n--\nfs/ext4/xattr.c-2895-\t\tif (header-\u003eh_magic == cpu_to_le32(EXT4_XATTR_MAGIC))\nfs/ext4/xattr.c:2896:\t\t\text4_xattr_inode_dec_ref_all(handle, inode, iloc.bh,\nfs/ext4/xattr.c-2897-\t\t\t\t\t\t     IFIRST(header),\n--\nfs/ext4/xattr.c-2936-\nfs/ext4/xattr.c:2937:\t\text4_xattr_release_block(handle, inode, bh,\nfs/ext4/xattr.c-2938-\t\t\t\t\t extra_credits);\n"
}

19/2 2026/06/23 09:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 27525
							output: 153
							thoughts: 223
						

Thoughts:
**My Assessment of the Patch's Fuzzing Potential**

Okay, so I've got this patch here, and it's making some pretty fundamental changes, but the core thoughts remain static. The critical thing is the patch is *removing* the `ea_inode_array` argument, which really catches my eye. That alone makes them strong candidates for deeper scrutiny.

Let's break down the rationale: this patch is modifying how ext4 handles extended attribute (EA) inodes. It's deferring the `iput` operation – which is a pretty crucial release – to a workqueue to dodge potential lock ordering issues. Functionally, this is a modification to core ext4 logic, and crucially, it's something that a user can trigger from userspace just by creating and deleting files with large extended attributes. That’s a good sign, from a fuzzing perspective, as it allows us to easily interact with the logic.

So, here's the summary of my findings:

*   **WorthFuzzing**: Absolutely. This warrants attention.
*   **FocusSymbols**: My immediate targets are `ext4_put_ea_inode`, `ext4_ea_inode_work`, `ext4_xattr_delete_inode`, `ext4_xattr_inode_dec_ref_all`, and `ext4_xattr_release_block`. These functions are central to the EA inode handling and are likely entry points or critical components of the modified logic.
*   **EnableConfigs**: I've confirmed that enabling `EXT4_FS` is sufficient for this patch to have an effect. No need to worry about other config flags at this time.

Alright, I'm ready to move on. Let's call `set-results` and get the fuzzing process rolling.



20/2 2026/06/23 09:27 tool set-results
0m Args:
{
  "EnableConfigs": [
    "EXT4_FS"
  ],
  "FocusSymbols": [
    "ext4_put_ea_inode",
    "ext4_ea_inode_work",
    "ext4_xattr_delete_inode",
    "ext4_xattr_inode_dec_ref_all",
    "ext4_xattr_release_block"
  ],
  "Reasoning": "The patch modifies how ext4 handles extended attribute (EA) inodes by deferring their iput to a workqueue to avoid lock ordering issues. This is a functional change to core ext4 logic that is reachable from userspace by creating and deleting files with large extended attributes.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "EXT4_FS"
  ],
  "FocusSymbols": [
    "ext4_put_ea_inode",
    "ext4_ea_inode_work",
    "ext4_xattr_delete_inode",
    "ext4_xattr_inode_dec_ref_all",
    "ext4_xattr_release_block"
  ],
  "Reasoning": "The patch modifies how ext4 handles extended attribute (EA) inodes by deferring their iput to a workqueue to avoid lock ordering issues. This is a functional change to core ext4 logic that is reachable from userspace by creating and deleting files with large extended attributes.",
  "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)