sa: fix sa_add_projid lock ordering

sa_add_projid() currently acquires hdl->sa_lock before zp->z_lock.
Several same-znode update paths take zp->z_lock and then call
sa_update() or sa_bulk_update() on the same SA handle.

On Linux, FS_IOC_FSSETXATTR reaches zfs_setattr() through
zpl_ioctl_setxattr() without outer inode serialization. This makes
the reversed lock order a real ABBA deadlock rather than a lockdep
false positive when projid is added to an old-format inode while
another thread updates the same znode.

Acquire zp->z_lock before hdl->sa_lock in sa_add_projid() to match
the existing znode update ordering.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
Co-authored-by: gality369 <gality369@example.com>
Closes #18503
This commit is contained in:
Gality
2026-05-08 04:20:44 +08:00
committed by GitHub
parent 500b44eef2
commit 439b802e77
+2 -2
View File
@@ -1605,8 +1605,8 @@ sa_add_projid(sa_handle_t *hdl, dmu_tx_t *tx, uint64_t projid)
bulk = kmem_zalloc(sizeof (sa_bulk_attr_t) * ZPL_END, KM_SLEEP); bulk = kmem_zalloc(sizeof (sa_bulk_attr_t) * ZPL_END, KM_SLEEP);
attrs = kmem_zalloc(sizeof (sa_bulk_attr_t) * ZPL_END, KM_SLEEP); attrs = kmem_zalloc(sizeof (sa_bulk_attr_t) * ZPL_END, KM_SLEEP);
mutex_enter(&hdl->sa_lock);
mutex_enter(&zp->z_lock); mutex_enter(&zp->z_lock);
mutex_enter(&hdl->sa_lock);
err = sa_lookup_locked(hdl, SA_ZPL_PROJID(zfsvfs), &projid, err = sa_lookup_locked(hdl, SA_ZPL_PROJID(zfsvfs), &projid,
sizeof (uint64_t)); sizeof (uint64_t));
@@ -1750,8 +1750,8 @@ sa_add_projid(sa_handle_t *hdl, dmu_tx_t *tx, uint64_t projid)
zp->z_is_sa = B_TRUE; zp->z_is_sa = B_TRUE;
out: out:
mutex_exit(&zp->z_lock);
mutex_exit(&hdl->sa_lock); mutex_exit(&hdl->sa_lock);
mutex_exit(&zp->z_lock);
kmem_free(attrs, sizeof (sa_bulk_attr_t) * ZPL_END); kmem_free(attrs, sizeof (sa_bulk_attr_t) * ZPL_END);
kmem_free(bulk, sizeof (sa_bulk_attr_t) * ZPL_END); kmem_free(bulk, sizeof (sa_bulk_attr_t) * ZPL_END);
if (dxattr_obj) if (dxattr_obj)