From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org
Cc: Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>,
linux-mm@kvack.org, Farid Zakaria <farid.m.zakaria@gmail.com>,
jannh@google.com
Subject: [PATCH 1/3] binfmt_misc: convert entry list to an hlist
Date: Wed, 08 Jul 2026 18:51:16 +0200 [thread overview]
Message-ID: <20260708-work-binfmt_misc-locking-v1-1-a009dd5b56db@kernel.org> (raw)
In-Reply-To: <20260708-work-binfmt_misc-locking-v1-0-a009dd5b56db@kernel.org>
The upcoming conversion of the handler lookup to RCU walks cannot use
list_del_init(): reinitializing the forward pointer of a removed entry
would make a concurrent lockless walker standing on that entry loop
back onto it indefinitely. The removal paths do rely on
reinitialization though because bm_{entry,status}_write() and
bm_evict_inode() need to detect whether an entry has already been
unlinked.
hlists support exactly this pattern: hlist_del_init_rcu() keeps the
forward pointer of the removed entry intact for concurrent walkers and
only zeroes ->pprev with hlist_unhashed() serving as the linked test.
Convert the entry list to an hlist now while keeping the rwlock so the
subsequent RCU conversion is a pure locking change. hlist_add_head()
inserts at the head just as list_add() did so lookup precedence
between registered handlers is unchanged.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/binfmt_misc.c | 25 +++++++++++++------------
include/linux/binfmts.h | 2 +-
kernel/user.c | 2 +-
3 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 84349fcb93f1..34de1b01e9c2 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -48,7 +48,7 @@ enum {Enabled, Magic};
#define MISC_FMT_OPEN_FILE (1UL << 28)
typedef struct {
- struct list_head list;
+ struct hlist_node node;
unsigned long flags; /* type, status, etc. */
int offset; /* offset of magic */
int size; /* size of magic/mask */
@@ -95,7 +95,7 @@ static Node *search_binfmt_handler(struct binfmt_misc *misc,
Node *e;
/* Walk all the registered handlers. */
- list_for_each_entry(e, &misc->entries, list) {
+ hlist_for_each_entry(e, &misc->entries, node) {
char *s;
int j;
@@ -654,8 +654,8 @@ static struct binfmt_misc *i_binfmt_misc(struct inode *inode)
*
* If the ->evict call was not caused by a super block shutdown but by a write
* to remove the entry or all entries via bm_{entry,status}_write() the entry
- * will have already been removed from the list. We keep the list_empty() check
- * to make that explicit.
+ * will have already been removed from the list. We keep the hlist_unhashed()
+ * check to make that explicit.
*/
static void bm_evict_inode(struct inode *inode)
{
@@ -668,8 +668,8 @@ static void bm_evict_inode(struct inode *inode)
misc = i_binfmt_misc(inode);
write_lock(&misc->entries_lock);
- if (!list_empty(&e->list))
- list_del_init(&e->list);
+ if (!hlist_unhashed(&e->node))
+ hlist_del_init(&e->node);
write_unlock(&misc->entries_lock);
put_binfmt_handler(e);
}
@@ -690,7 +690,7 @@ static void bm_evict_inode(struct inode *inode)
static void remove_binfmt_handler(struct binfmt_misc *misc, Node *e)
{
write_lock(&misc->entries_lock);
- list_del_init(&e->list);
+ hlist_del_init(&e->node);
write_unlock(&misc->entries_lock);
locked_recursive_removal(e->dentry, NULL);
}
@@ -746,7 +746,7 @@ static ssize_t bm_entry_write(struct file *file, const char __user *buffer,
* read-only. So we only need to take the write lock when we
* actually remove the entry from the list.
*/
- if (!list_empty(&e->list))
+ if (!hlist_unhashed(&e->node))
remove_binfmt_handler(i_binfmt_misc(inode), e);
inode_unlock(inode);
@@ -790,7 +790,7 @@ static int add_entry(Node *e, struct super_block *sb)
d_make_persistent(dentry, inode);
misc = i_binfmt_misc(inode);
write_lock(&misc->entries_lock);
- list_add(&e->list, &misc->entries);
+ hlist_add_head(&e->node, &misc->entries);
write_unlock(&misc->entries_lock);
simple_done_creating(dentry);
return 0;
@@ -863,8 +863,9 @@ static ssize_t bm_status_write(struct file *file, const char __user *buffer,
{
struct binfmt_misc *misc;
int res = parse_command(buffer, count);
- Node *e, *next;
+ struct hlist_node *next;
struct inode *inode;
+ Node *e;
misc = i_binfmt_misc(file_inode(file));
switch (res) {
@@ -890,7 +891,7 @@ static ssize_t bm_status_write(struct file *file, const char __user *buffer,
* read-only. So we only need to take the write lock when we
* actually remove the entry from the list.
*/
- list_for_each_entry_safe(e, next, &misc->entries, list)
+ hlist_for_each_entry_safe(e, next, &misc->entries, node)
remove_binfmt_handler(misc, e);
inode_unlock(inode);
@@ -965,7 +966,7 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
if (!misc)
return -ENOMEM;
- INIT_LIST_HEAD(&misc->entries);
+ INIT_HLIST_HEAD(&misc->entries);
rwlock_init(&misc->entries_lock);
/* Pairs with smp_load_acquire() in load_binfmt_misc(). */
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 2c77e383e737..071da63f2b48 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -101,7 +101,7 @@ struct linux_binfmt {
#if IS_ENABLED(CONFIG_BINFMT_MISC)
struct binfmt_misc {
- struct list_head entries;
+ struct hlist_head entries;
rwlock_t entries_lock;
bool enabled;
} __randomize_layout;
diff --git a/kernel/user.c b/kernel/user.c
index 7aef4e679a6a..c6a2bfb4d918 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -23,7 +23,7 @@
#if IS_ENABLED(CONFIG_BINFMT_MISC)
struct binfmt_misc init_binfmt_misc = {
- .entries = LIST_HEAD_INIT(init_binfmt_misc.entries),
+ .entries = HLIST_HEAD_INIT,
.enabled = true,
.entries_lock = __RW_LOCK_UNLOCKED(init_binfmt_misc.entries_lock),
};
--
2.53.0
next prev parent reply other threads:[~2026-07-08 16:51 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-08 16:51 [PATCH 0/3] binfmt_misc: use RCU for the handler lookup Christian Brauner
2026-07-08 16:51 ` Christian Brauner [this message]
2026-07-09 10:46 ` [PATCH 1/3] binfmt_misc: convert entry list to an hlist Jori Koolstra
2026-07-08 16:51 ` [PATCH 2/3] binfmt_misc: use RCU for the handler lookup Christian Brauner
2026-07-08 16:51 ` [PATCH 3/3] binfmt_misc: annotate racy accesses to ->enabled Christian Brauner
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=20260708-work-binfmt_misc-locking-v1-1-a009dd5b56db@kernel.org \
--to=brauner@kernel.org \
--cc=farid.m.zakaria@gmail.com \
--cc=jack@suse.cz \
--cc=jannh@google.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=viro@zeniv.linux.org.uk \
/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