From: Mike Snitzer <snitzer@kernel.org>
To: Jeff Layton <jlayton@kernel.org>
Cc: linux-nfs@vger.kernel.org, Anna Schumaker <anna@kernel.org>,
Trond Myklebust <trondmy@hammerspace.com>,
Chuck Lever <chuck.lever@oracle.com>, NeilBrown <neilb@suse.de>
Subject: Re: [for-6.13 PATCH v2 11/15] nfs_common: track all open nfsd_files per LOCALIO nfs_client
Date: Thu, 14 Nov 2024 18:22:21 -0500 [thread overview]
Message-ID: <ZzaGLT2VThx09pOb@kernel.org> (raw)
In-Reply-To: <07c4b3f29094b4ce88da382586e822133eabdec9.camel@kernel.org>
On Thu, Nov 14, 2024 at 01:53:48PM -0500, Jeff Layton wrote:
> On Wed, 2024-11-13 at 22:59 -0500, Mike Snitzer wrote:
> > This tracking enables __nfsd_file_cache_purge() to call
> > nfs_localio_invalidate_clients(), upon shutdown or export change, to
> > nfs_close_local_fh() all open nfsd_files that are still cached by the
> > LOCALIO nfs clients associated with nfsd_net that is being shutdown.
> >
> > Now that the client must track all open nfsd_files there was more work
> > than necessary being done with the global nfs_uuids_lock contended.
> > This manifested in various RCU issues, e.g.:
> > hrtimer: interrupt took 47969440 ns
> > rcu: INFO: rcu_sched detected stalls on CPUs/tasks:
> >
> > Use nfs_uuid->lock to protect all nfs_uuid_t members, instead of
> > nfs_uuids_lock, once nfs_uuid_is_local() adds the client to
> > nn->local_clients.
> >
> > Also add 'local_clients_lock' to 'struct nfsd_net' to protect
> > nn->local_clients. And store a pointer to spinlock in the 'list_lock'
> > member of nfs_uuid_t so nfs_localio_disable_client() can use it to
> > avoid taking the global nfs_uuids_lock.
> >
> > In combination, these split out locks eliminate the use of the single
> > nfslocalio.c global nfs_uuids_lock in the IO paths (open and close).
> >
> > Also refactored associated fs/nfs_common/nfslocalio.c methods' locking
> > to reduce work performed with spinlocks held in general.
> >
> > Signed-off-by: Mike Snitzer <snitzer@kernel.org>
> > ---
> > fs/nfs_common/nfslocalio.c | 166 +++++++++++++++++++++++++++----------
> > fs/nfsd/filecache.c | 9 ++
> > fs/nfsd/localio.c | 1 +
> > fs/nfsd/netns.h | 1 +
> > fs/nfsd/nfsctl.c | 4 +-
> > include/linux/nfslocalio.h | 8 +-
> > 6 files changed, 143 insertions(+), 46 deletions(-)
> >
> > diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
> > index 5fa3f47b442e9..e5c0912b9a025 100644
> > --- a/fs/nfs_common/nfslocalio.c
> > +++ b/fs/nfs_common/nfslocalio.c
> > @@ -23,27 +23,49 @@ static DEFINE_SPINLOCK(nfs_uuids_lock);
> > */
> > static LIST_HEAD(nfs_uuids);
> >
> > +/*
> > + * Lock ordering:
> > + * 1: nfs_uuid->lock
> > + * 2: nfs_uuids_lock
> > + * 3: nfs_uuid->list_lock (aka nn->local_clients_lock)
> > + *
> > + * May skip locks in select cases, but never hold multiple
> > + * locks out of order.
> > + */
> > +
> > void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
> > {
> > nfs_uuid->net = NULL;
> > nfs_uuid->dom = NULL;
> > + nfs_uuid->list_lock = NULL;
> > INIT_LIST_HEAD(&nfs_uuid->list);
> > + INIT_LIST_HEAD(&nfs_uuid->files);
> > spin_lock_init(&nfs_uuid->lock);
> > }
> > EXPORT_SYMBOL_GPL(nfs_uuid_init);
> >
> > bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
> > {
> > + spin_lock(&nfs_uuid->lock);
> > + if (nfs_uuid->net) {
> > + /* This nfs_uuid is already in use */
> > + spin_unlock(&nfs_uuid->lock);
> > + return false;
> > + }
> > +
> > spin_lock(&nfs_uuids_lock);
> > - /* Is this nfs_uuid already in use? */
> > if (!list_empty(&nfs_uuid->list)) {
> > + /* This nfs_uuid is already in use */
> > spin_unlock(&nfs_uuids_lock);
> > + spin_unlock(&nfs_uuid->lock);
> > return false;
> > }
> > - uuid_gen(&nfs_uuid->uuid);
> > list_add_tail(&nfs_uuid->list, &nfs_uuids);
> > spin_unlock(&nfs_uuids_lock);
> >
> > + uuid_gen(&nfs_uuid->uuid);
> > + spin_unlock(&nfs_uuid->lock);
> > +
> > return true;
> > }
> > EXPORT_SYMBOL_GPL(nfs_uuid_begin);
> > @@ -51,11 +73,15 @@ EXPORT_SYMBOL_GPL(nfs_uuid_begin);
> > void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
> > {
> > if (nfs_uuid->net == NULL) {
> > - spin_lock(&nfs_uuids_lock);
> > - if (nfs_uuid->net == NULL)
> > + spin_lock(&nfs_uuid->lock);
> > + if (nfs_uuid->net == NULL) {
> > + /* Not local, remove from nfs_uuids */
> > + spin_lock(&nfs_uuids_lock);
> > list_del_init(&nfs_uuid->list);
> > - spin_unlock(&nfs_uuids_lock);
> > - }
> > + spin_unlock(&nfs_uuids_lock);
> > + }
> > + spin_unlock(&nfs_uuid->lock);
> > + }
> > }
> > EXPORT_SYMBOL_GPL(nfs_uuid_end);
> >
> > @@ -73,28 +99,39 @@ static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
> > static struct module *nfsd_mod;
> >
> > void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
> > - struct net *net, struct auth_domain *dom,
> > - struct module *mod)
> > + spinlock_t *list_lock, struct net *net,
> > + struct auth_domain *dom, struct module *mod)
> > {
> > nfs_uuid_t *nfs_uuid;
> >
> > spin_lock(&nfs_uuids_lock);
> > nfs_uuid = nfs_uuid_lookup_locked(uuid);
> > - if (nfs_uuid) {
> > - kref_get(&dom->ref);
> > - nfs_uuid->dom = dom;
> > - /*
> > - * We don't hold a ref on the net, but instead put
> > - * ourselves on a list so the net pointer can be
> > - * invalidated.
> > - */
> > - list_move(&nfs_uuid->list, list);
> > - rcu_assign_pointer(nfs_uuid->net, net);
> > -
> > - __module_get(mod);
> > - nfsd_mod = mod;
> > + if (!nfs_uuid) {
> > + spin_unlock(&nfs_uuids_lock);
> > + return;
> > }
> > +
> > + /*
> > + * We don't hold a ref on the net, but instead put
> > + * ourselves on @list (nn->local_clients) so the net
> > + * pointer can be invalidated.
> > + */
> > + spin_lock(list_lock); /* list_lock is nn->local_clients_lock */
> > + list_move(&nfs_uuid->list, list);
> > + spin_unlock(list_lock);
> > +
> > spin_unlock(&nfs_uuids_lock);
> > + /* Once nfs_uuid is parented to @list, avoid global nfs_uuids_lock */
> > + spin_lock(&nfs_uuid->lock);
> > +
> > + __module_get(mod);
> > + nfsd_mod = mod;
> > +
> > + nfs_uuid->list_lock = list_lock;
> > + kref_get(&dom->ref);
> > + nfs_uuid->dom = dom;
> > + rcu_assign_pointer(nfs_uuid->net, net);
> > + spin_unlock(&nfs_uuid->lock);
> > }
> > EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
> >
> > @@ -108,55 +145,96 @@ void nfs_localio_enable_client(struct nfs_client *clp)
> > }
> > EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
> >
> > -static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
> > +/*
> > + * Cleanup the nfs_uuid_t embedded in an nfs_client.
> > + * This is the long-form of nfs_uuid_init().
> > + */
> > +static void nfs_uuid_put(nfs_uuid_t *nfs_uuid)
> > {
> > - if (!nfs_uuid->net)
> > + LIST_HEAD(local_files);
> > + struct nfs_file_localio *nfl, *tmp;
> > +
> > + spin_lock(&nfs_uuid->lock);
> > + if (unlikely(!nfs_uuid->net)) {
> > + spin_unlock(&nfs_uuid->lock);
> > return;
> > - module_put(nfsd_mod);
> > + }
> > RCU_INIT_POINTER(nfs_uuid->net, NULL);
> >
> > if (nfs_uuid->dom) {
> > auth_domain_put(nfs_uuid->dom);
> > nfs_uuid->dom = NULL;
> > }
> > - list_del_init(&nfs_uuid->list);
> > +
> > + list_splice_init(&nfs_uuid->files, &local_files);
> > + spin_unlock(&nfs_uuid->lock);
> > +
> > + /* Walk list of files and ensure their last references dropped */
> > + list_for_each_entry_safe(nfl, tmp, &local_files, list) {
> > + nfs_close_local_fh(nfl);
> > + cond_resched();
> > + }
> > +
> > + spin_lock(&nfs_uuid->lock);
> > + BUG_ON(!list_empty(&nfs_uuid->files));
> > +
> > + /* Remove client from nn->local_clients */
> > + if (nfs_uuid->list_lock) {
> > + spin_lock(nfs_uuid->list_lock);
> > + BUG_ON(list_empty(&nfs_uuid->list));
> > + list_del_init(&nfs_uuid->list);
> > + spin_unlock(nfs_uuid->list_lock);
> > + nfs_uuid->list_lock = NULL;
> > + }
> > +
> > + module_put(nfsd_mod);
> > + spin_unlock(&nfs_uuid->lock);
> > }
> >
> > void nfs_localio_disable_client(struct nfs_client *clp)
> > {
> > - nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
> > + nfs_uuid_t *nfs_uuid = NULL;
> >
> > - spin_lock(&nfs_uuid->lock);
> > + spin_lock(&clp->cl_uuid.lock); /* aka &nfs_uuid->lock */
> > if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
> > - spin_lock(&nfs_uuids_lock);
> > - nfs_uuid_put_locked(nfs_uuid);
> > - spin_unlock(&nfs_uuids_lock);
> > + /* &clp->cl_uuid is always not NULL, using as bool here */
> > + nfs_uuid = &clp->cl_uuid;
> > }
> > - spin_unlock(&nfs_uuid->lock);
> > + spin_unlock(&clp->cl_uuid.lock);
> > +
> > + if (nfs_uuid)
> > + nfs_uuid_put(nfs_uuid);
> > }
> > EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
> >
> > -void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
> > +void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
> > + spinlock_t *nn_local_clients_lock)
> > {
> > + LIST_HEAD(local_clients);
> > nfs_uuid_t *nfs_uuid, *tmp;
> > + struct nfs_client *clp;
> >
> > - spin_lock(&nfs_uuids_lock);
> > - list_for_each_entry_safe(nfs_uuid, tmp, cl_uuid_list, list) {
> > - struct nfs_client *clp =
> > - container_of(nfs_uuid, struct nfs_client, cl_uuid);
> > -
> > + spin_lock(nn_local_clients_lock);
> > + list_splice_init(nn_local_clients, &local_clients);
> > + spin_unlock(nn_local_clients_lock);
> > + list_for_each_entry_safe(nfs_uuid, tmp, &local_clients, list) {
> > + if (WARN_ON(nfs_uuid->list_lock != nn_local_clients_lock))
> > + break;
> > + clp = container_of(nfs_uuid, struct nfs_client, cl_uuid);
> > nfs_localio_disable_client(clp);
> > }
> > - spin_unlock(&nfs_uuids_lock);
> > }
> > EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
> >
> > static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
> > {
> > - spin_lock(&nfs_uuids_lock);
> > - if (!nfl->nfs_uuid)
> > + /* Add nfl to nfs_uuid->files if it isn't already */
> > + spin_lock(&nfs_uuid->lock);
> > + if (list_empty(&nfl->list)) {
> > rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
> > - spin_unlock(&nfs_uuids_lock);
> > + list_add_tail(&nfl->list, &nfs_uuid->files);
> > + }
> > + spin_unlock(&nfs_uuid->lock);
> > }
> >
> > /*
> > @@ -217,14 +295,16 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
> > ro_nf = rcu_access_pointer(nfl->ro_file);
> > rw_nf = rcu_access_pointer(nfl->rw_file);
> > if (ro_nf || rw_nf) {
> > - spin_lock(&nfs_uuids_lock);
> > + spin_lock(&nfs_uuid->lock);
> > if (ro_nf)
> > ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
> > if (rw_nf)
> > rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
> >
> > + /* Remove nfl from nfs_uuid->files list */
> > rcu_assign_pointer(nfl->nfs_uuid, NULL);
> > - spin_unlock(&nfs_uuids_lock);
> > + list_del_init(&nfl->list);
> > + spin_unlock(&nfs_uuid->lock);
> > rcu_read_unlock();
> >
> > if (ro_nf)
> > diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
> > index ab9942e420543..c9ab64e3732ce 100644
> > --- a/fs/nfsd/filecache.c
> > +++ b/fs/nfsd/filecache.c
> > @@ -39,6 +39,7 @@
> > #include <linux/fsnotify.h>
> > #include <linux/seq_file.h>
> > #include <linux/rhashtable.h>
> > +#include <linux/nfslocalio.h>
> >
> > #include "vfs.h"
> > #include "nfsd.h"
> > @@ -836,6 +837,14 @@ __nfsd_file_cache_purge(struct net *net)
> > struct nfsd_file *nf;
> > LIST_HEAD(dispose);
> >
> > +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> > + if (net) {
> > + struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> > + nfs_localio_invalidate_clients(&nn->local_clients,
> > + &nn->local_clients_lock);
> > + }
> > +#endif
> > +
>
> If !net in this function, then nfsd is tearing down all the nfsd_files
> in the cache. You probably need to issue the above on every net
> namespace that is running nfsd in that case.
Right, I looked closely at this but really should have updated the
patch header to make it have more permanence.. IIRC there are 2
passes: Once per net and then the final shutdown that passes NULL.
1)
nfsd_destroy_serv(net) -> nfsd_shutdown_net ->
nfsd_file_cache_shutdown_net -> nfsd_file_cache_purge ->
__nfsd_file_cache_purge(net)
2)
nfsd_file_cache_shutdown -> __nfsd_file_cache_purge(NULL)
So I concluded no need to be concerned with the !net case needing to
handle all nets...
Mike
next prev parent reply other threads:[~2024-11-14 23:22 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 01/15] nfs_common: must not hold RCU while calling nfsd_file_put_local Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 02/15] nfs/localio: add direct IO enablement with sync and async IO support Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 03/15] nfsd: add nfsd_file_{get,put} to 'nfs_to' nfsd_localio_operations Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 04/15] nfs_common: rename functions that invalidate LOCALIO nfs_clients Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 05/15] nfs_common: move localio_lock to new lock member of nfs_uuid_t Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 06/15] nfs: cache all open LOCALIO nfsd_file(s) in client Mike Snitzer
2024-11-15 3:12 ` NeilBrown
2024-11-15 16:49 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 07/15] nfsd: update percpu_ref to manage references on nfsd_net Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 08/15] nfsd: rename nfsd_serv_ prefixed methods and variables with nfsd_net_ Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 09/15] nfsd: nfsd_file_acquire_local no longer returns GC'd nfsd_file Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 10/15] nfs_common: rename nfslocalio nfs_uuid_lock to nfs_uuids_lock Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 11/15] nfs_common: track all open nfsd_files per LOCALIO nfs_client Mike Snitzer
2024-11-14 18:53 ` Jeff Layton
2024-11-14 23:22 ` Mike Snitzer [this message]
2024-11-14 3:59 ` [for-6.13 PATCH v2 12/15] nfs_common: add nfs_localio trace events Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 13/15] nfs/localio: remove redundant code and simplify LOCALIO enablement Mike Snitzer
2024-11-14 15:31 ` [for-6.13 PATCH v2-fixed " Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 14/15] nfs: probe for LOCALIO when v4 client reconnects to server Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 15/15] nfs: probe for LOCALIO when v3 " Mike Snitzer
2024-11-15 3:55 ` NeilBrown
2024-11-14 16:14 ` (subset) [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO cel
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=ZzaGLT2VThx09pOb@kernel.org \
--to=snitzer@kernel.org \
--cc=anna@kernel.org \
--cc=chuck.lever@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neilb@suse.de \
--cc=trondmy@hammerspace.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 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.