From: Kevin Mitchell <kevmitch@arista.com>
To: Tejun Heo <tj@kernel.org>
Cc: Kevin Mitchell <kevmitch@arista.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Chengming Zhou <zhouchengming@bytedance.com>,
linux-kernel@vger.kernel.org
Subject: [PATCH] kernfs: use atomics for open_node refcounts
Date: Wed, 19 Nov 2025 11:17:57 -0800 [thread overview]
Message-ID: <20251119191758.612694-2-kevmitch@arista.com> (raw)
Counts of files to be released and mmapped files were added to
kernfs_open_node in commit bdb2fd7fc56e ("kernfs: Skip
kernfs_drain_open_files() more aggressively") to optimize
kernfs_drain_open_files(). A WARN_ON_ONCE sanity check was also added in
kernfs_drain_open_files to ensure that these counters were brought to
zero once all files had been drained.
This WARNING was found to trigger on occasion during pci hotplug link
down when mmapped pci resource files were removed in
pci_remove_resource_files. This was caused by a race condition during
the initial mmappings where incrementing nr_mmapped from multiple CPUs
such that 2 (or possibly more) simultaneous increments overwrote each
other thus appearing as only a single increment. The correct number of
kernfs_open_files with mmapped == true were nevertheless still present
in the kernfs_open_node's files list. Consequently, the iteration in
kernfs_drain_open_files would underflow nr_mmapped and the WARNING would
fire.
Since both nr_mmapped and nr_to_release may be incremented and
decremented from multiple CPUs simultaneously without acquiring the
kernfs_open_file_mutex_lock, make these members atomic. This should
ensure that simultaneous increments and decrements don't overwrite each
other.
Fixes: bdb2fd7fc56e ("kernfs: Skip kernfs_drain_open_files() more aggressively")
Signed-off-by: Kevin Mitchell <kevmitch@arista.com>
---
fs/kernfs/file.c | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 9adf36e6364b..a9d0bf82ceba 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -23,8 +23,8 @@ struct kernfs_open_node {
atomic_t event;
wait_queue_head_t poll;
struct list_head files; /* goes through kernfs_open_file.list */
- unsigned int nr_mmapped;
- unsigned int nr_to_release;
+ atomic_t nr_mmapped;
+ atomic_t nr_to_release;
};
/*
@@ -503,7 +503,7 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma)
rc = 0;
if (!of->mmapped) {
of->mmapped = true;
- of_on(of)->nr_mmapped++;
+ atomic_inc(&of_on(of)->nr_mmapped);
of->vm_ops = vma->vm_ops;
}
vma->vm_ops = &kernfs_vm_ops;
@@ -553,7 +553,7 @@ static int kernfs_get_open_node(struct kernfs_node *kn,
list_add_tail(&of->list, &on->files);
if (kn->flags & KERNFS_HAS_RELEASE)
- on->nr_to_release++;
+ atomic_inc(&on->nr_to_release);
mutex_unlock(mutex);
return 0;
@@ -592,10 +592,10 @@ static void kernfs_unlink_open_file(struct kernfs_node *kn,
if (kn->flags & KERNFS_HAS_RELEASE) {
WARN_ON_ONCE(of->released == open_failed);
if (open_failed)
- on->nr_to_release--;
+ atomic_dec(&on->nr_to_release);
}
if (of->mmapped)
- on->nr_mmapped--;
+ atomic_dec(&on->nr_mmapped);
list_del(&of->list);
}
@@ -763,7 +763,7 @@ static void kernfs_release_file(struct kernfs_node *kn,
*/
kn->attr.ops->release(of);
of->released = true;
- of_on(of)->nr_to_release--;
+ atomic_dec(&of_on(of)->nr_to_release);
}
}
@@ -802,7 +802,8 @@ bool kernfs_should_drain_open_files(struct kernfs_node *kn)
rcu_read_lock();
on = rcu_dereference(kn->attr.open);
- ret = on && (on->nr_mmapped || on->nr_to_release);
+ ret = on && (atomic_read(&on->nr_mmapped) ||
+ atomic_read(&on->nr_to_release));
rcu_read_unlock();
return ret;
@@ -827,14 +828,15 @@ void kernfs_drain_open_files(struct kernfs_node *kn)
if (of->mmapped) {
unmap_mapping_range(inode->i_mapping, 0, 0, 1);
of->mmapped = false;
- on->nr_mmapped--;
+ atomic_dec(&on->nr_mmapped);
}
if (kn->flags & KERNFS_HAS_RELEASE)
kernfs_release_file(kn, of);
}
- WARN_ON_ONCE(on->nr_mmapped || on->nr_to_release);
+ WARN_ON_ONCE(atomic_read(&on->nr_mmapped) ||
+ atomic_read(&on->nr_to_release));
mutex_unlock(mutex);
}
--
2.51.0
next reply other threads:[~2025-11-19 19:19 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-19 19:17 Kevin Mitchell [this message]
2025-11-20 7:20 ` Greg Kroah-Hartman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251119191758.612694-2-kevmitch@arista.com \
--to=kevmitch@arista.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=tj@kernel.org \
--cc=zhouchengming@bytedance.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®