From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1B04A270545; Wed, 20 May 2026 18:36:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779302198; cv=none; b=XdIs0QdYJP0SCuD8pjH+lyo4+V5wfDBU9berzrKXKYxvsvFMX51tWR2J6gVG/3SyiUFpEgffNWxEh0bPN2uA1fnNyuEevCaG9V0T04LbVDXCekAEAL02uCjFJKdnCo6/tLt9QKLTcK1U0yHGzLxBpxgQlpS0Znn7O9vcbuvBuRA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779302198; c=relaxed/simple; bh=JvKUQcxn6O0bThSDm1zv8pdyyz7QXNf9jbwx0rPW4ZU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mxSjLxLianjtU32ZWaXHE2AIga89t7EomPucwut5ddaHrC8xfVAKxra8Tgm1geYciK4l8N3HNo7VAWLY4PBf3eXZ6HScSv4/iMnAA71klmKhOPrZeAAq73/EF1/aA4cdUAG7AE3uMAoyquW/EdB6p2V7daZWnzluLCG0mRFdH0c= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=MaGLNkFb; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="MaGLNkFb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 804511F000E9; Wed, 20 May 2026 18:36:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1779302196; bh=KQRqacFTA5Mk0PnJmSvMlrN/21xjOdfb1J19WlXO3R0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=MaGLNkFbp6MH/JC7b8xE3lHo6UX3i/3IiyPRd20OAwegeJBYHcGkRp4pDYL2uwEe+ fcomSmzrjMh34WhQAoRP2H6yJkgHiuSBwCr2xm/QAqAv+qtGXj0faqu1JoVVx8rHBV 8g2ONzFE0Z56Dy+1GzM7xKKbPurZrkBsgr8SdLMA= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Sam Sun , Jan Kara , Sasha Levin Subject: [PATCH 6.6 176/508] quota: Fix race of dquot_scan_active() with quota deactivation Date: Wed, 20 May 2026 18:19:59 +0200 Message-ID: <20260520162102.451802234@linuxfoundation.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260520162058.573354582@linuxfoundation.org> References: <20260520162058.573354582@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: stable@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.6-stable review patch. If anyone has any objections, please let me know. ------------------ From: Jan Kara [ Upstream commit e93ab401da4b2e2c1b8ef2424de2f238d51c8b2d ] dquot_scan_active() can race with quota deactivation in quota_release_workfn() like: CPU0 (quota_release_workfn) CPU1 (dquot_scan_active) ============================== ============================== spin_lock(&dq_list_lock); list_replace_init( &releasing_dquots, &rls_head); /* dquot X on rls_head, dq_count == 0, DQ_ACTIVE_B still set */ spin_unlock(&dq_list_lock); synchronize_srcu(&dquot_srcu); spin_lock(&dq_list_lock); list_for_each_entry(dquot, &inuse_list, dq_inuse) { /* finds dquot X */ dquot_active(X) -> true atomic_inc(&X->dq_count); } spin_unlock(&dq_list_lock); spin_lock(&dq_list_lock); dquot = list_first_entry(&rls_head); WARN_ON_ONCE(atomic_read(&dquot->dq_count)); The problem is not only a cosmetic one as under memory pressure the caller of dquot_scan_active() can end up working on freed dquot. Fix the problem by making sure the dquot is removed from releasing list when we acquire a reference to it. Fixes: 869b6ea1609f ("quota: Fix slow quotaoff") Reported-by: Sam Sun Link: https://lore.kernel.org/all/CAEkJfYPTt3uP1vAYnQ5V2ZWn5O9PLhhGi5HbOcAzyP9vbXyjeg@mail.gmail.com Signed-off-by: Jan Kara Signed-off-by: Sasha Levin --- fs/quota/dquot.c | 38 ++++++++++++++++++++++++++++++-------- include/linux/quotaops.h | 9 +-------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 42a7d0a71b22e..7562ee4ff2929 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -364,6 +364,31 @@ static inline int dquot_active(struct dquot *dquot) return test_bit(DQ_ACTIVE_B, &dquot->dq_flags); } +static struct dquot *__dqgrab(struct dquot *dquot) +{ + lockdep_assert_held(&dq_list_lock); + if (!atomic_read(&dquot->dq_count)) + remove_free_dquot(dquot); + atomic_inc(&dquot->dq_count); + return dquot; +} + +/* + * Get reference to dquot when we got pointer to it by some other means. The + * dquot has to be active and the caller has to make sure it cannot get + * deactivated under our hands. + */ +struct dquot *dqgrab(struct dquot *dquot) +{ + spin_lock(&dq_list_lock); + WARN_ON_ONCE(!dquot_active(dquot)); + dquot = __dqgrab(dquot); + spin_unlock(&dq_list_lock); + + return dquot; +} +EXPORT_SYMBOL_GPL(dqgrab); + static inline int dquot_dirty(struct dquot *dquot) { return test_bit(DQ_MOD_B, &dquot->dq_flags); @@ -642,15 +667,14 @@ int dquot_scan_active(struct super_block *sb, continue; if (dquot->dq_sb != sb) continue; - /* Now we have active dquot so we can just increase use count */ - atomic_inc(&dquot->dq_count); + __dqgrab(dquot); spin_unlock(&dq_list_lock); dqput(old_dquot); old_dquot = dquot; /* * ->release_dquot() can be racing with us. Our reference - * protects us from new calls to it so just wait for any - * outstanding call and recheck the DQ_ACTIVE_B after that. + * protects us from dquot_release() proceeding so just wait for + * any outstanding call and recheck the DQ_ACTIVE_B after that. */ wait_on_dquot(dquot); if (dquot_active(dquot)) { @@ -718,7 +742,7 @@ int dquot_writeback_dquots(struct super_block *sb, int type) /* Now we have active dquot from which someone is * holding reference so we can safely just increase * use count */ - dqgrab(dquot); + __dqgrab(dquot); spin_unlock(&dq_list_lock); err = dquot_write_dquot(dquot); if (err && !ret) @@ -973,9 +997,7 @@ struct dquot *dqget(struct super_block *sb, struct kqid qid) spin_unlock(&dq_list_lock); dqstats_inc(DQST_LOOKUPS); } else { - if (!atomic_read(&dquot->dq_count)) - remove_free_dquot(dquot); - atomic_inc(&dquot->dq_count); + __dqgrab(dquot); spin_unlock(&dq_list_lock); dqstats_inc(DQST_CACHE_HITS); dqstats_inc(DQST_LOOKUPS); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 4fa4ef0a173a3..a9be09f96b10e 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -44,14 +44,7 @@ int dquot_initialize(struct inode *inode); bool dquot_initialize_needed(struct inode *inode); void dquot_drop(struct inode *inode); struct dquot *dqget(struct super_block *sb, struct kqid qid); -static inline struct dquot *dqgrab(struct dquot *dquot) -{ - /* Make sure someone else has active reference to dquot */ - WARN_ON_ONCE(!atomic_read(&dquot->dq_count)); - WARN_ON_ONCE(!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)); - atomic_inc(&dquot->dq_count); - return dquot; -} +struct dquot *dqgrab(struct dquot *dquot); static inline bool dquot_is_busy(struct dquot *dquot) { -- 2.53.0