From nobody Mon Sep 28 13:17:34 2026 Received: from azure-sdnproxy.icoremail.net (azure-sdnproxy.icoremail.net [52.237.72.81]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 969C21AB6F1 for ; Fri, 21 Aug 2026 08:08:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=52.237.72.81 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787299688; cv=none; b=RHoLw9CCRQJtDicj49seeKIELvfBGnrQBArcQhVvaO+sIKHRHtVCleAooLgaClVuaSOv/ZkdXJdIE6+ByMm+pCSiVzMeua+fYJI7QoJyb9/TCNuf0BRq2y2Wm3FfVqidCR680qX6HEVqZP8ulEB51DBL7JF0FxcnHt4baX7TmeY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787299688; c=relaxed/simple; bh=eq6wG2rxlnsCTXnhZfCUV1ycijioK5JyumnjJS2oXrk=; h=Date:From:To:Cc:Subject:Content-Type:MIME-Version:Message-ID; b=d/DmBDeL6bi9hTsgjmTq+G2svfW6shaxtcA/EbQA0vNjTmmhhiDOfTkb6Zup3Atq8oGT0J/yiIWyWNr7eE48EoxsuHuS/jyhjND25VceOB/ox3guqmQFUX4BFrxkNirRqQXUFPMLQf0FYHE/2WUoBUuXVb7Lg4AcBcRdnFMNVNE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=zju.edu.cn; spf=pass smtp.mailfrom=zju.edu.cn; arc=none smtp.client-ip=52.237.72.81 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=zju.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zju.edu.cn Received: from zju.edu.cn (unknown [10.190.129.65]) by mtasvr (Coremail) with SMTP id _____wDXoX1PB4hqummxAA--.12617S3; Fri, 21 Aug 2026 16:07:44 +0800 (CST) Received: from stitch$zju.edu.cn ( [10.190.129.65] ) by ajax-webmail-mail-app1 (Coremail) ; Fri, 21 Aug 2026 16:07:43 +0800 (GMT+08:00) Date: Fri, 21 Aug 2026 16:07:43 +0800 (GMT+08:00) X-CM-HeaderCharset: UTF-8 From: "Jiacheng Xu" To: "Andreas Gruenbacher" Cc: gfs2@lists.linux.dev, linux-kernel@vger.kernel.org Subject: [PATCH] gfs2: avoid sysfs freeze and unmount lock inversion X-Priority: 3 X-Mailer: Coremail Webmail Server Version 2025.3-cmXT6 build 20260617(6f868824) Copyright (c) 2002-2026 www.mailtech.cn zju.edu.cn Content-Transfer-Encoding: quoted-printable Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <3dd32003.17b68.1a0235c8d0f.Coremail.stitch@zju.edu.cn> X-Coremail-Locale: zh_CN X-CM-TRANSID: yy_KCgAnvKZPB4hqnywJBA--.50391W X-CM-SenderInfo: qtrxiiaqttimo62m3hxhgxhubq/1tbiAwMKC2qHsJcDeQAEs1 X-CM-DELIVERINFO: =?B?G28wFAXKKxbFmtjJiESix3B1w3tMoM3snk/k5SVzZVydjEqe1eefKHnLf95EnB4iem egWwPWW0XmXVmWsTmudtWHNpqP23dG6licKjAc4ZI2E3mBMcMfziBpZg0jn/H4WgasTOhb TndcuM18iuFEVqMkhulxDXDjttmajDhjC5tVH4a1P+yUmA3HsPTNm756h2Yasg== X-Coremail-Antispam: 1Uk129KBj93XoWxCr1DXryDWF1UurW7ZF17XFc_yoWrtFWkpF 4qy345Gr4kJr17WrsxCF48K343Kw4kZFyUG3yfW3Wav3W3twnaqas7AF1F9FWUArZ7Gw18 uw4UGFZIkrWUurXCm3ZEXasCq-sJn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUPCb4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r1j6r18M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_tr0E3s1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr1j6F4UJwA2z4x0Y4vEx4A2jsIE14v26rxl6s0DM28EF7xvwVC2z280aVCY1x0267AK xVW0oVCq3wAS0I0E0xvYzxvE52x082IY62kv0487Mc804VCY07AIYIkI8VC2zVCFFI0UMc 02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWUJVWUGwAv7VC2z280aVAF wI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI48JM4x0Y48IcxkI7V AKI48G6xCjnVAKz4kxM4xvF2IEb7IF0Fy264kE64k0F24lFcxC0VAYjxAxZF0Ex2IqxwCF 04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r 18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_Jrv_JF1lIxkGc2Ij64vI r41lIxAIcVC0I7IYx2IY67AKxVWUJVWUCwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Jr0_Gr 1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvE x4A2jsIEc7CjxVAFwI0_Gr0_Gr1UMVCEFcxC0VAYjxAxZFUvcSsGvfC2KfnxnUUI43ZEXa 7IU86OJ7UUUUU== Content-Type: text/plain; charset="utf-8" The gfs2 freeze sysfs callback runs with kernfs active protection held. freeze_super() and thaw_super() may acquire sb->s_umount. At the same time, the unmount path holds s_umount and calls gfs2_sys_fs_del(), which drains the same kernfs node. This creates an ABBA dependency: freeze_store() kernfs active -> s_umount gfs2_put_super() s_umount -> kernfs active Break the active protection before accessing s_umount. Since this allows unmount to proceed concurrently, acquire an active superblock reference while holding s_umount for reading. Release that reference from gfs2_freeze_wq so a final superblock reference cannot enter gfs2_put_super() from the sysfs callback itself. Return -EAGAIN if the superblock is being mounted, unmounted, or otherwise has s_umount held. Reject invalid freeze values before taking any references or changing sysfs active protection. Fixes: b3b94faa5fe5 ("[GFS2] The core of GFS2") Signed-off-by: Jiacheng Xu --- fs/gfs2/sys.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++----= --- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index ea2c7b9e4a77..78bc310be1fb 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -11,11 +11,14 @@ #include #include #include +#include #include #include +#include #include #include #include +#include #include "gfs2.h" #include "incore.h" @@ -33,6 +36,24 @@ struct gfs2_attr { ssize_t (*store)(struct gfs2_sbd *, const char *, size_t); }; +/* Forward declaration for freeze_store(), defined with the other attribut= es. */ +static struct gfs2_attr gfs2_attr_freeze; + +struct gfs2_sysfs_sb_ref { + struct work_struct work; + struct super_block *sb; +}; + +static void gfs2_sysfs_sb_ref_put(struct work_struct *work) +{ + struct gfs2_sysfs_sb_ref *ref =3D + container_of(work, struct gfs2_sysfs_sb_ref, work); + struct super_block *sb =3D ref->sb; + + deactivate_super(sb); + kfree(ref); +} + static ssize_t gfs2_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -153,6 +174,9 @@ static ssize_t freeze_show(struct gfs2_sbd *sdp, char *= buf) static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t l= en) { + struct super_block *sb =3D sdp->sd_vfs; + struct gfs2_sysfs_sb_ref *ref =3D NULL; + struct kernfs_node *kn; int error, n; error =3D kstrtoint(buf, 0, &n); @@ -162,23 +186,67 @@ static ssize_t freeze_store(struct gfs2_sbd *sdp, con= st char *buf, size_t len) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + if (n !=3D 0 && n !=3D 1) + return -EINVAL; + + /* + * The sysfs callback normally holds kn->active. Break that + * dependency before taking s_umount, otherwise unmount can wait for + * this callback while the callback waits for s_umount. + */ + kn =3D sysfs_break_active_protection(&sdp->sd_kobj, + &gfs2_attr_freeze.attr); + if (!kn) + return -ENODEV; + + ref =3D kmalloc_obj(*ref); + if (!ref) { + error =3D -ENOMEM; + goto out_unbreak; + } + INIT_WORK(&ref->work, gfs2_sysfs_sb_ref_put); + ref->sb =3D sb; + + /* + * Pin the superblock before running an operation which may wait for + * s_umount. Release the pin from a separate work item so a last + * reference cannot enter gfs2_put_super() on the sysfs call path. + */ + if (!down_read_trylock(&sb->s_umount)) { + error =3D -EAGAIN; + goto out_unbreak; + } + if (!(sb->s_flags & SB_ACTIVE)) { + up_read(&sb->s_umount); + error =3D -EAGAIN; + goto out_unbreak; + } + atomic_inc(&sb->s_active); + up_read(&sb->s_umount); + switch (n) { case 0: - error =3D thaw_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE, NU= LL); + error =3D thaw_super(sb, FREEZE_HOLDER_USERSPACE, NULL); break; case 1: - error =3D freeze_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE, = NULL); + error =3D freeze_super(sb, FREEZE_HOLDER_USERSPACE, NULL); break; - default: - return -EINVAL; } - if (error) { + if (error) fs_warn(sdp, "freeze %d error %d\n", n, error); - return error; - } - return len; + /* No sdp access is allowed after active protection is restored. */ + sysfs_unbreak_active_protection(kn); + /* gfs2 module teardown drains this queue before unloading the module= . */ + queue_work(gfs2_freeze_wq, &ref->work); + + return error ? error : len; + +out_unbreak: + sysfs_unbreak_active_protection(kn); + kfree(ref); + return error; } static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf)