From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 34491DF76 for ; Thu, 6 Aug 2026 03:59:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.169 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785988800; cv=none; b=c1XfC2qwmh4lMNG1s2qiXEP0GDOAnncCKk8UjwF8Jz86yVibawkmklFhvIxoq7Vmk5lgUsajrXdXoQ4iiCzg3AFdGq32SmI15QjIh/EjJ7D9Yn+SPsOBod1GdbecNvrdJ0lmGKJ0V8D6/54+9dYF7iiFqGpUJuAONr5GiThXO/Y= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785988800; c=relaxed/simple; bh=f6VOjzmU/FxU0pVahdirIg5iehCeWXaXEEVHrptSSqY=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=lbGwQfK5NrrVX+geaL4Wgc3fAg9LDzbCKIzxv6KihmqvXKZBRXla/sLvdhuSMClp34r4AVy9AAIqRzN8TWAfpuTs/iWNQvycKWIjqieOUNpECwW0TSDStrendCeWDMUvSwy3wOVt6I0YsketIFM3x4XDbRkjafe7vRZwdKjbDME= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=tGxlIISE; arc=none smtp.client-ip=209.85.215.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="tGxlIISE" Received: by mail-pg1-f169.google.com with SMTP id 41be03b00d2f7-cbe827e3cb4so42585a12.3 for ; Wed, 05 Aug 2026 20:59:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785988798; x=1786593598; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=hS/qPjj+0KSIiFYCj9hbSyZbsQtcF1h6Wy0FklLZWy8=; b=tGxlIISEHa/ywSMHBCxFa7y2X6pTcLkVvbcNGA52GMKSgEXjwtv5VMI6g/HuX4dqPd 23lye4c//7cblSS3fa3vFGnkO2IKpW+RMG5CSMra22o1W/A6kwBu13i2LGax2mSH9kdB i7/Rde4wMC3AKsIYFoHV8gwKJQnkJsH9707IPy6qEF6NTwmbvZucZ26rd5YAX0/awwdW WAPX0RkZ66OLFE9JNfnGb6sRMitxCo8tGjjlZa3mhWaCl1r4m16ugE0GH2l/j3I+R/uI 3DLjgGLJuBiKSKRnWH+30bwW70Bl+Ob895bVg2J14x2bUaIvCe0lrkkOvH3O1qhBGiyD p7Vg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785988798; x=1786593598; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=hS/qPjj+0KSIiFYCj9hbSyZbsQtcF1h6Wy0FklLZWy8=; b=qBjlMMLEESHNMcQVg9sb6bEuv/dr05crq0bfW2K8r1MuvOyfXmrlmiO1FJbPBtyNtZ /Ej5cNK91g+Dce2I366t+42OESTJkGysRVJJ7XeNKdyA1X4gdPAQFFpjwoRsz42CeOlw XuC3m7MxG8ltwAS6pUEE08QQc2hcMvwLpajjpSrOz+8Zdf77wL7LryLWr2QYRMNsoi+t KgvtICfkBOEBGprLOQ7cIirQV9/E548Rg5Z1opyFr9VAf2sFNiyXPuLZc1IXNopnfFwC 713mfoiip93aN1XvI07etqAW7rgbUmTl1uUoNFpr1lsnfh/goJrJlMdlWkjqSqZzFcPO vklQ== X-Forwarded-Encrypted: i=1; AHgh+Rq8Aql78WeJFfFO0v82yzErWwcPbbdAQBSQcfsN19LyTur0JTBuhgkrKoN6sYs71+qcP9cUnP/BLi1/T88=@vger.kernel.org X-Gm-Message-State: AOJu0Yzm95NwpiCuxDsmfJ76va4cZyVg92n8iovegMTd9TRtLIEXce+5 i9A/RTl5JHKBm1LrFKiR9uKIRtGFAwSqOwMC6KwmwZGIaUD/4s6caIs8 X-Gm-Gg: AR+sD122W74BZt95urgAnYYkbHWBp25Iws47QZm0MVQuT+kWZzS9RCnjNg7syP+cgec 21MKp3Zks860xO/9eXeBlzJXxBWdtXP+o3EHHkMPgetUwt+UKVcGw/StC6RPE7SalYn7/FuJ+z8 Ufpy/+iA+JIskGtZvKi8JUS6GrJq6F6cMFwj23x4wC73QaderBB6w9Rl+tgdIRmHPJFxa4o5RaK ZrvmOAVoYHymYyhK9KQTp98gvHX7211vNCIKLiNLzy0/qGkqu3CCiKa3j536wskn+k5AmT7JHwa nu7G/M08X7uPFyrVdUyIUIvKkT1iGHgj8qy3fzoswlKOSvFa9Zf2g5GU0KzfNGgrHtsz5+LsFbo bLkWDkZLyJ/p7ixPVRkJ1yZB+65g1PHl1Ql7qWk+4e7g9dJ/UZH17bzfzdy+Ki0SXX21/VjMMOf FaURJ0dOL7kS/M6cUqYOALuCyURdSm+8LoP8mi9YeCGWh9QOQO8lKJBIs/3lzt+P5DqhAGP0ejA ZQhjCTo9MIX5HE46hq4uz/wpNk3ZQ== X-Received: by 2002:a05:6a00:2e18:b0:848:2c6c:dfe3 with SMTP id d2e1a72fcca58-84f2dffd75emr12304981b3a.17.1785988798184; Wed, 05 Aug 2026 20:59:58 -0700 (PDT) Received: from gmail.com ([138.199.21.246]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84f459ba36csm420171b3a.49.2026.08.05.20.59.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Aug 2026 20:59:57 -0700 (PDT) From: ZhengYuan Huang To: mark@fasheh.com, jlbec@evilplan.org, joseph.qi@linux.alibaba.com Cc: ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org, baijiaju1990@gmail.com, r33s3n6@gmail.com, zzzccc427@gmail.com, ZhengYuan Huang Subject: [PATCH v2] ocfs2: fix deadlock in inline-data truncate transactions Date: Thu, 6 Aug 2026 11:59:47 +0800 Message-ID: <20260806035947.2451857-1-gality369@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit [BUG] Updating an inode xattr can cause an ABBA deadlock with inline file truncation. ocfs2_truncate_file() down_write(&oi->ip_alloc_sem) ocfs2_truncate_inline() ocfs2_start_trans() ocfs2_xattr_set() ocfs2_start_trans() ocfs2_xattr_ibody_set() down_write(&oi->ip_alloc_sem) [CAUSE] The xattr set path starts its merged transaction before the helpers which modify inode-body xattrs acquire ip_alloc_sem. This creates the reverse of the ip_alloc_sem -> transaction order used by the allocation and truncate paths. The transaction merge in commit 85db90e77806 ("ocfs2/xattr: Merge xattr set transaction.") introduced this ordering for ordinary xattr updates. The create-time transaction -> ip_xattr_sem dependency is a separate case. The fix in commit c13024342c82 ("ocfs2: fix circular locking dependency in ocfs2_init_acl()") prepares the parent ACL before the create transaction and avoids the xattr semaphore for initial xattrs on an unpublished inode. That change does not cover ordinary ocfs2_xattr_set(). [FIX] Acquire ip_alloc_sem in ocfs2_xattr_set() before xattr preparation, allocation reservations, and transaction start. Tell the inode-body and indexed-block helpers when the semaphore is already held, while preserving their local locking for callers that do not provide outer protection. Keep the existing ip_alloc_sem -> transaction order in allocation and truncate paths; fixing the xattr side avoids changing that established ordering. Fixes: 85db90e77806 ("ocfs2/xattr: Merge xattr set transaction.") Signed-off-by: ZhengYuan Huang --- v2: - Fix the xattr side of the lock ordering based on Joseph's feedback. - Explain why the remaining fix belongs on the ordinary xattr side. --- fs/ocfs2/xattr.c | 49 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 35bcbb0ff607..27bea1f360a1 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -74,6 +74,7 @@ struct ocfs2_xattr_set_ctxt { struct ocfs2_alloc_context *data_ac; struct ocfs2_cached_dealloc_ctxt dealloc; int set_abort; + int alloc_sem_protected; }; #define OCFS2_XATTR_ROOT_SIZE (sizeof(struct ocfs2_xattr_def_value_root)) @@ -2916,7 +2917,8 @@ static int ocfs2_xattr_has_space_inline(struct inode *inode, static int ocfs2_xattr_ibody_find(struct inode *inode, int name_index, const char *name, - struct ocfs2_xattr_search *xs) + struct ocfs2_xattr_search *xs, + int lock_alloc_sem) { struct ocfs2_inode_info *oi = OCFS2_I(inode); struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; @@ -2927,9 +2929,11 @@ static int ocfs2_xattr_ibody_find(struct inode *inode, return 0; if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { - down_read(&oi->ip_alloc_sem); + if (lock_alloc_sem) + down_read(&oi->ip_alloc_sem); has_space = ocfs2_xattr_has_space_inline(inode, di); - up_read(&oi->ip_alloc_sem); + if (lock_alloc_sem) + up_read(&oi->ip_alloc_sem); if (!has_space) return 0; } @@ -3016,14 +3020,17 @@ static int ocfs2_xattr_ibody_set(struct inode *inode, struct ocfs2_xattr_search *xs, struct ocfs2_xattr_set_ctxt *ctxt) { - int ret; + int ret, took_alloc_sem = 0; struct ocfs2_inode_info *oi = OCFS2_I(inode); struct ocfs2_xa_loc loc; if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) return -ENOSPC; - down_write(&oi->ip_alloc_sem); + if (!ctxt->alloc_sem_protected) { + down_write(&oi->ip_alloc_sem); + took_alloc_sem = 1; + } if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { ret = ocfs2_xattr_ibody_init(inode, xs->inode_bh, ctxt); if (ret) { @@ -3044,7 +3051,8 @@ static int ocfs2_xattr_ibody_set(struct inode *inode, xs->here = loc.xl_entry; out: - up_write(&oi->ip_alloc_sem); + if (took_alloc_sem) + up_write(&oi->ip_alloc_sem); return ret; } @@ -3729,6 +3737,7 @@ int ocfs2_xattr_set_handle(handle_t *handle, .handle = handle, .meta_ac = meta_ac, .data_ac = data_ac, + .alloc_sem_protected = 1, }; if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb))) @@ -3750,7 +3759,7 @@ int ocfs2_xattr_set_handle(handle_t *handle, xis.inode_bh = xbs.inode_bh = di_bh; di = (struct ocfs2_dinode *)di_bh->b_data; - ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis); + ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis, 0); if (ret) goto cleanup; if (xis.not_found) { @@ -3834,7 +3843,7 @@ int ocfs2_xattr_set(struct inode *inode, * Scan inode and external block to find the same name * extended attribute and collect search information. */ - ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis); + ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis, 1); if (ret) goto cleanup; if (xis.not_found) { @@ -3856,6 +3865,8 @@ int ocfs2_xattr_set(struct inode *inode, goto cleanup; } + down_write(&OCFS2_I(inode)->ip_alloc_sem); + /* Check whether the value is refcounted and do some preparation. */ if (ocfs2_is_refcount_inode(inode) && (!xis.not_found || !xbs.not_found)) { @@ -3864,7 +3875,7 @@ int ocfs2_xattr_set(struct inode *inode, &ref_meta, &ref_credits); if (ret) { mlog_errno(ret); - goto cleanup; + goto out_unlock_alloc; } } @@ -3875,7 +3886,7 @@ int ocfs2_xattr_set(struct inode *inode, if (ret < 0) { inode_unlock(tl_inode); mlog_errno(ret); - goto cleanup; + goto out_unlock_alloc; } } inode_unlock(tl_inode); @@ -3884,8 +3895,9 @@ int ocfs2_xattr_set(struct inode *inode, &xbs, &ctxt, ref_meta, &credits); if (ret) { mlog_errno(ret); - goto cleanup; + goto out_unlock_alloc; } + ctxt.alloc_sem_protected = 1; /* we need to update inode's ctime field, so add credit for it. */ credits += OCFS2_INODE_UPDATE_CREDITS; @@ -3893,15 +3905,15 @@ int ocfs2_xattr_set(struct inode *inode, if (IS_ERR(ctxt.handle)) { ret = PTR_ERR(ctxt.handle); mlog_errno(ret); - goto out_free_ac; + goto out_unlock_alloc; } ret = __ocfs2_xattr_set_handle(inode, di, &xi, &xis, &xbs, &ctxt); ocfs2_update_inode_fsync_trans(ctxt.handle, inode, 0); ocfs2_commit_trans(osb, ctxt.handle); - -out_free_ac: +out_unlock_alloc: + up_write(&OCFS2_I(inode)->ip_alloc_sem); if (ctxt.data_ac) ocfs2_free_alloc_context(ctxt.data_ac); if (ctxt.meta_ac) @@ -4520,6 +4532,7 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, u64 blkno; handle_t *handle = ctxt->handle; struct ocfs2_inode_info *oi = OCFS2_I(inode); + int took_alloc_sem = 0; struct buffer_head *xb_bh = xs->xattr_bh; struct ocfs2_xattr_block *xb = (struct ocfs2_xattr_block *)xb_bh->b_data; @@ -4537,7 +4550,10 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, * We can use this lock for now, and maybe move to a dedicated mutex * if performance becomes a problem later. */ - down_write(&oi->ip_alloc_sem); + if (!ctxt->alloc_sem_protected) { + down_write(&oi->ip_alloc_sem); + took_alloc_sem = 1; + } ret = ocfs2_journal_access_xb(handle, INODE_CACHE(inode), xb_bh, OCFS2_JOURNAL_ACCESS_WRITE); @@ -4600,7 +4616,8 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, ocfs2_journal_dirty(handle, xb_bh); out: - up_write(&oi->ip_alloc_sem); + if (took_alloc_sem) + up_write(&oi->ip_alloc_sem); return ret; }