From: Jan Kara <jack@suse.cz>
To: Mateusz Guzik <mjguzik@gmail.com>
Cc: paulmck@kernel.org, brauner@kernel.org, viro@zeniv.linux.org.uk,
jack@suse.cz, linux-fsdevel@vger.kernel.org,
torvalds@linux-foundation.org, edumazet@google.com,
linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH] fs: elide the smp_rmb fence in fd_install()
Date: Thu, 5 Dec 2024 15:46:45 +0100 [thread overview]
Message-ID: <20241205144645.bv2q6nqua66sql3j@quack3> (raw)
In-Reply-To: <20241205120332.1578562-1-mjguzik@gmail.com>
On Thu 05-12-24 13:03:32, Mateusz Guzik wrote:
> See the added commentary for reasoning.
>
> ->resize_in_progress handling is moved inside of expand_fdtable() for
> clarity.
>
> Whacks an actual fence on arm64.
>
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
Hum, I don't think this works. What could happen now is:
CPU1 CPU2
expand_fdtable() fd_install()
files->resize_in_progress = true;
...
if (atomic_read(&files->count) > 1)
synchronize_rcu();
...
rcu_assign_pointer(files->fdt, new_fdt);
if (cur_fdt != &files->fdtab)
call_rcu(&cur_fdt->rcu, free_fdtable_rcu);
rcu_read_lock_sched()
fdt = rcu_dereference_sched(files->fdt);
/* Fetched old FD table - without
* smp_rmb() the read was reordered */
rcu_assign_pointer(files->fdt, new_fdt);
/*
* Publish everything before we unset ->resize_in_progress, see above
* for an explanation.
*/
smp_wmb();
out:
files->resize_in_progress = false;
if (unlikely(files->resize_in_progress)) {
- false
rcu_assign_pointer(fdt->fd[fd], file);
- store in the old table - boom.
Honza
> diff --git a/fs/file.c b/fs/file.c
> index 019fb9acf91b..d065a24980da 100644
> --- a/fs/file.c
> +++ b/fs/file.c
> @@ -233,28 +233,54 @@ static int expand_fdtable(struct files_struct *files, unsigned int nr)
> __acquires(files->file_lock)
> {
> struct fdtable *new_fdt, *cur_fdt;
> + int err = 0;
>
> + BUG_ON(files->resize_in_progress);
> + files->resize_in_progress = true;
> spin_unlock(&files->file_lock);
> new_fdt = alloc_fdtable(nr + 1);
>
> - /* make sure all fd_install() have seen resize_in_progress
> - * or have finished their rcu_read_lock_sched() section.
> + /*
> + * Synchronize against the lockless fd_install().
> + *
> + * All work in that routine is enclosed with RCU sched section.
> + *
> + * We published ->resize_in_progress = true with the unlock above,
> + * which makes new arrivals bail to locked operation.
> + *
> + * Now we only need to wait for CPUs which did not observe the flag to
> + * leave and make sure their store to the fd table got published.
> + *
> + * We do it with synchronize_rcu(), which both waits for all sections to
> + * finish (taking care of the first part) and guarantees all CPUs issued a
> + * full fence (taking care of the second part).
> + *
> + * Note we know there is nobody to wait for if we are dealing with a
> + * single-threaded process.
> */
> if (atomic_read(&files->count) > 1)
> synchronize_rcu();
>
> spin_lock(&files->file_lock);
> - if (IS_ERR(new_fdt))
> - return PTR_ERR(new_fdt);
> + if (IS_ERR(new_fdt)) {
> + err = PTR_ERR(new_fdt);
> + goto out;
> + }
> cur_fdt = files_fdtable(files);
> BUG_ON(nr < cur_fdt->max_fds);
> copy_fdtable(new_fdt, cur_fdt);
> rcu_assign_pointer(files->fdt, new_fdt);
> if (cur_fdt != &files->fdtab)
> call_rcu(&cur_fdt->rcu, free_fdtable_rcu);
> - /* coupled with smp_rmb() in fd_install() */
> +
> + /*
> + * Publish everything before we unset ->resize_in_progress, see above
> + * for an explanation.
> + */
> smp_wmb();
> - return 0;
> +out:
> + files->resize_in_progress = false;
> + return err;
> }
>
> /*
> @@ -290,9 +316,7 @@ static int expand_files(struct files_struct *files, unsigned int nr)
> return -EMFILE;
>
> /* All good, so we try */
> - files->resize_in_progress = true;
> error = expand_fdtable(files, nr);
> - files->resize_in_progress = false;
>
> wake_up_all(&files->resize_wait);
> return error;
> @@ -629,13 +653,18 @@ EXPORT_SYMBOL(put_unused_fd);
>
> void fd_install(unsigned int fd, struct file *file)
> {
> - struct files_struct *files = current->files;
> + struct files_struct *files;
> struct fdtable *fdt;
>
> if (WARN_ON_ONCE(unlikely(file->f_mode & FMODE_BACKING)))
> return;
>
> + /*
> + * Synchronized with expand_fdtable(), see that routine for an
> + * explanation.
> + */
> rcu_read_lock_sched();
> + files = READ_ONCE(current->files);
>
> if (unlikely(files->resize_in_progress)) {
> rcu_read_unlock_sched();
> @@ -646,8 +675,7 @@ void fd_install(unsigned int fd, struct file *file)
> spin_unlock(&files->file_lock);
> return;
> }
> - /* coupled with smp_wmb() in expand_fdtable() */
> - smp_rmb();
> +
> fdt = rcu_dereference_sched(files->fdt);
> BUG_ON(fdt->fd[fd] != NULL);
> rcu_assign_pointer(fdt->fd[fd], file);
> --
> 2.43.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
next prev parent reply other threads:[~2024-12-05 14:46 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-08-08 2:50 [RFC] why do we need smp_rmb/smp_wmb pair in fd_install()/expand_fdtable()? Al Viro
2024-08-08 3:06 ` Linus Torvalds
2024-08-08 3:35 ` Al Viro
2024-08-08 3:46 ` Al Viro
2024-08-08 6:08 ` Mateusz Guzik
2024-12-05 12:03 ` [RFC PATCH] fs: elide the smp_rmb fence in fd_install() Mateusz Guzik
2024-12-05 14:18 ` Al Viro
2024-12-05 14:43 ` Mateusz Guzik
2024-12-05 18:41 ` Paul E. McKenney
2024-12-05 19:03 ` Mateusz Guzik
2024-12-05 20:01 ` Paul E. McKenney
2024-12-05 20:15 ` Mateusz Guzik
2024-12-05 21:17 ` Paul E. McKenney
2024-12-05 19:26 ` Linus Torvalds
2024-12-05 19:47 ` Mateusz Guzik
2024-12-05 20:11 ` Paul E. McKenney
2024-12-06 12:11 ` Christian Brauner
2024-12-05 20:06 ` Paul E. McKenney
2024-12-05 14:46 ` Jan Kara [this message]
2024-12-05 15:01 ` Mateusz Guzik
2024-12-05 15:29 ` Jan Kara
2024-12-05 15:36 ` Mateusz Guzik
2024-12-06 15:32 ` Jan Kara
2024-12-05 14:58 ` Christian Brauner
2024-12-05 15:06 ` Mateusz Guzik
2024-08-08 13:20 ` [RFC] why do we need smp_rmb/smp_wmb pair in fd_install()/expand_fdtable()? Christian Brauner
2024-08-08 16:11 ` Linus Torvalds
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=20241205144645.bv2q6nqua66sql3j@quack3 \
--to=jack@suse.cz \
--cc=brauner@kernel.org \
--cc=edumazet@google.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mjguzik@gmail.com \
--cc=paulmck@kernel.org \
--cc=torvalds@linux-foundation.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.