From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org
Cc: Alexander Viro <viro@zeniv.linux.org.uk>, Jan Kara <jack@suse.cz>,
Peter Zijlstra <peterz@infradead.org>,
"Paul E. McKenney" <paulmck@kernel.org>,
"Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH 1/5] file: depend on the reloaded pointer in __fget_files_rcu()
Date: Thu, 05 Nov 2026 14:18:05 +0100 [thread overview]
Message-ID: <20261105-work-file_ref-ordering-v1-1-8713f4876b7b@kernel.org> (raw)
In-Reply-To: <20261105-work-file_ref-ordering-v1-0-8713f4876b7b@kernel.org>
__fget_files_rcu() loads the file pointer, takes a reference with
file_ref_get(), reloads the pointer and compares the two. If they match
it returns the pointer of the first load. The caller will read f_mode,
f_op and everything else through.
Becuase of SLAB_TYPESAFE_BY_RCU the file can be closed, freed and
reallocated between the first load and the increment. If another thread
of the same process reopens the same descriptor the reload can see the
same pointer again and the recheck passes.
While that's fine because the descriptor does point to that file now the
subsequent loads are only ordered after the increment.
The increment synchronizes with the store of file_ref_init() and
init_file(). But that happens before do_dentry_open() sets f_op and most
of the other fields.
They are published by the rcu_assign_pointer() in fd_install() meaning
the reload of the pointer.
But there's no dependency between the reload and later loads. So on
arm64 or power the caller can read the f_mode and the f_op of the
previous lifetime of the file.
So we do need to compare against a copy the compiler can't see through
and then use the reloaded pointer from there on just like we already do
in __get_file_rcu().
Nothing measurable changes because of this change.
Fixes: 253ca8678d30 ("Improve __fget_files_rcu() code generation (and thus __fget_light())")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/file.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/fs/file.c b/fs/file.c
index 4cd40dfa6129..052da9409874 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -1168,6 +1168,8 @@ static inline struct file *__fget_files_rcu(struct files_struct *files,
{
for (;;) {
struct file *file;
+ struct file *file_reloaded;
+ struct file *file_reloaded_cmp;
struct fdtable *fdt = rcu_dereference_raw(files->fdt);
struct file __rcu **fdentry;
unsigned long nospec_mask;
@@ -1216,11 +1218,32 @@ static inline struct file *__fget_files_rcu(struct files_struct *files,
*
* If so, we need to put our ref and try again.
*/
- if (unlikely(file != rcu_dereference_raw(*fdentry)) ||
+ file_reloaded = rcu_dereference_raw(*fdentry);
+
+ /*
+ * Every load needs to depend on the reload. If the
+ * file was freed and reallocated into this slot in the
+ * meantime, what open() wrote to it was published by the
+ * rcu_assign_pointer() in fd_install(), which is the
+ * store the reload saw.
+ *
+ * The increment above only orders against file_ref_init()
+ * and init_file(). That happens before do_dentry_open()
+ * sets f_op et al. So every load needs to depend on the
+ * reload otherwise we can see e.g., stale f_op on e.g.,
+ * arm64.
+ *
+ * So compare against a copy the compiler can't see through
+ * and use the reloaded pointer from here on.
+ */
+ file_reloaded_cmp = file_reloaded;
+ OPTIMIZER_HIDE_VAR(file_reloaded_cmp);
+ if (unlikely(file != file_reloaded_cmp) ||
unlikely(rcu_dereference_raw(files->fdt) != fdt)) {
fput(file);
continue;
}
+ file = file_reloaded;
/*
* This isn't the file we're looking for or we're not
--
2.53.0
next prev parent reply other threads:[~2026-11-05 13:18 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 13:18 [PATCH 0/5] file: fix and document ordering for lockless fd lookup wrt to file_ref Christian Brauner
2026-11-05 13:18 ` Christian Brauner [this message]
2026-11-05 13:18 ` [PATCH 2/5] file_ref: use acquire ordering in file_ref_get() Christian Brauner
2026-11-05 13:18 ` [PATCH 3/5] file_ref: document why file_ref_put() stays fully ordered Christian Brauner
2026-11-05 13:18 ` [PATCH 4/5] Documentation/litmus-tests: add the file reference count tests Christian Brauner
2026-11-05 13:18 ` [PATCH 5/5] Documentation: update the lock-free file lookup in files.rst 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=20261105-work-file_ref-ordering-v1-1-8713f4876b7b@kernel.org \
--to=brauner@kernel.org \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=paulmck@kernel.org \
--cc=peterz@infradead.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.