From: Jeff Layton <jlayton@kernel.org>
To: NeilBrown <neilb@suse.de>, Chuck Lever <chuck.lever@oracle.com>
Cc: linux-nfs@vger.kernel.org, Olga Kornievskaia <kolga@netapp.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>
Subject: Re: [PATCH 4/5] SUNRPC: discard sv_refcnt, and svc_get/svc_put
Date: Mon, 30 Oct 2023 09:15:37 -0400 [thread overview]
Message-ID: <d8b02810ea9e52fbb478cd5db639db492f3cf260.camel@kernel.org> (raw)
In-Reply-To: <20231030011247.9794-5-neilb@suse.de>
On Mon, 2023-10-30 at 12:08 +1100, NeilBrown wrote:
> sv_refcnt is no longer useful.
> lockd and nfs-cb only ever have the svc active when there are a non-zero
> number of threads, so sv_refcnt mirrors sv_nrthreads.
>
> nfsd also keeps the svc active between when a socket is added and when
> the first thread is started, but we don't really need a refcount for
> that. We can simple not destory the svc after adding a socket.
>
> So remove sv_refcnt and the get/put functions.
> Instead of a final call to svc_put(), call svc_destroy() instead.
> This is changed to also store NULL in the passed-in pointer to make it
> easier to avoid use-after-free situations.
>
> Signed-off-by: NeilBrown <neilb@suse.de>
> ---
> fs/lockd/svc.c | 10 ++++------
> fs/nfs/callback.c | 13 ++++++-------
> fs/nfsd/netns.h | 7 -------
> fs/nfsd/nfsctl.c | 15 ++++-----------
> fs/nfsd/nfsd.h | 7 -------
> fs/nfsd/nfssvc.c | 26 ++++----------------------
> include/linux/sunrpc/svc.h | 27 +--------------------------
> net/sunrpc/svc.c | 13 ++++---------
> 8 files changed, 23 insertions(+), 95 deletions(-)
>
> diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
> index 81be07c1d3d1..0d6cb3fdc0e1 100644
> --- a/fs/lockd/svc.c
> +++ b/fs/lockd/svc.c
> @@ -345,10 +345,10 @@ static int lockd_get(void)
>
> serv->sv_maxconn = nlm_max_connections;
> error = svc_set_num_threads(serv, NULL, 1);
> - /* The thread now holds the only reference */
> - svc_put(serv);
> - if (error < 0)
> + if (error < 0) {
> + svc_destroy(&serv);
> return error;
> + }
>
> nlmsvc_serv = serv;
> register_inetaddr_notifier(&lockd_inetaddr_notifier);
> @@ -372,11 +372,9 @@ static void lockd_put(void)
> unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
> #endif
>
> - svc_get(nlmsvc_serv);
> svc_set_num_threads(nlmsvc_serv, NULL, 0);
> - svc_put(nlmsvc_serv);
> timer_delete_sync(&nlmsvc_retry);
> - nlmsvc_serv = NULL;
> + svc_destroy(&nlmsvc_serv);
> dprintk("lockd_down: service destroyed\n");
> }
>
> diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
> index 4ffa1f469e90..760d27dd7225 100644
> --- a/fs/nfs/callback.c
> +++ b/fs/nfs/callback.c
> @@ -187,7 +187,7 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
> * Check whether we're already up and running.
> */
> if (cb_info->serv)
> - return svc_get(cb_info->serv);
> + return cb_info->serv;
>
> /*
> * Sanity check: if there's no task,
> @@ -245,9 +245,10 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
>
> cb_info->users++;
> err_net:
> - if (!cb_info->users)
> - cb_info->serv = NULL;
> - svc_put(serv);
> + if (!cb_info->users) {
> + svc_set_num_threads(cb_info->serv, NULL, 0);
> + svc_destroy(&cb_info->serv);
> + }
> err_create:
> mutex_unlock(&nfs_callback_mutex);
> return ret;
> @@ -271,11 +272,9 @@ void nfs_callback_down(int minorversion, struct net *net)
> nfs_callback_down_net(minorversion, serv, net);
> cb_info->users--;
> if (cb_info->users == 0) {
> - svc_get(serv);
> svc_set_num_threads(serv, NULL, 0);
> - svc_put(serv);
> dprintk("nfs_callback_down: service destroyed\n");
> - cb_info->serv = NULL;
> + svc_destroy(&cb_info->serv);
> }
> mutex_unlock(&nfs_callback_mutex);
> }
> diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
> index ec49b200b797..7fc39aca5363 100644
> --- a/fs/nfsd/netns.h
> +++ b/fs/nfsd/netns.h
> @@ -124,13 +124,6 @@ struct nfsd_net {
> u32 clverifier_counter;
>
> struct svc_serv *nfsd_serv;
> - /* When a listening socket is added to nfsd, keep_active is set
> - * and this justifies a reference on nfsd_serv. This stops
> - * nfsd_serv from being freed. When the number of threads is
> - * set, keep_active is cleared and the reference is dropped. So
> - * when the last thread exits, the service will be destroyed.
> - */
> - int keep_active;
>
> /*
> * clientid and stateid data for construction of net unique COPY
> diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> index 8f644f1d157c..86cab5281fd2 100644
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@ -705,13 +705,10 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred
>
> err = svc_addsock(nn->nfsd_serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
>
> - if (err < 0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active)
> + if (!nn->nfsd_serv->sv_nrthreads &&
> + list_empty(&nn->nfsd_serv->sv_permsocks))
> nfsd_last_thread(net);
> - else if (err >= 0 &&
> - !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
> - svc_get(nn->nfsd_serv);
>
> - nfsd_put(net);
> return err;
> }
>
> @@ -747,10 +744,6 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
> if (err < 0 && err != -EAFNOSUPPORT)
> goto out_close;
>
> - if (!nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
> - svc_get(nn->nfsd_serv);
> -
> - nfsd_put(net);
> return 0;
> out_close:
> xprt = svc_find_xprt(nn->nfsd_serv, transport, net, PF_INET, port);
> @@ -759,10 +752,10 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
> svc_xprt_put(xprt);
> }
> out_err:
> - if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active)
> + if (!nn->nfsd_serv->sv_nrthreads &&
> + list_empty(&nn->nfsd_serv->sv_permsocks))
> nfsd_last_thread(net);
>
> - nfsd_put(net);
> return err;
> }
>
> diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> index 3286ffacbc56..9ed0e08d16c2 100644
> --- a/fs/nfsd/nfsd.h
> +++ b/fs/nfsd/nfsd.h
> @@ -113,13 +113,6 @@ int nfsd_pool_stats_open(struct inode *, struct file *);
> int nfsd_pool_stats_release(struct inode *, struct file *);
> void nfsd_shutdown_threads(struct net *net);
>
> -static inline void nfsd_put(struct net *net)
> -{
> - struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> -
> - svc_put(nn->nfsd_serv);
> -}
> -
> bool i_am_nfsd(void);
>
> struct nfsdfs_client {
> diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
> index 203e1cfc1cad..61a1d966ca48 100644
> --- a/fs/nfsd/nfssvc.c
> +++ b/fs/nfsd/nfssvc.c
> @@ -59,15 +59,6 @@ static __be32 nfsd_init_request(struct svc_rqst *,
> * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
> * of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
> *
> - * If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a
> - * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0 (unless
> - * nn->keep_active is set). That number of nfsd threads must
> - * exist and each must be listed in ->sp_all_threads in some entry of
> - * ->sv_pools[].
> - *
> - * Each active thread holds a counted reference on nn->nfsd_serv, as does
> - * the nn->keep_active flag and various transient calls to svc_get().
> - *
> * Finally, the nfsd_mutex also protects some of the global variables that are
> * accessed when nfsd starts and that are settable via the write_* routines in
> * nfsctl.c. In particular:
> @@ -573,6 +564,7 @@ void nfsd_last_thread(struct net *net)
>
> nfsd_shutdown_net(net);
> nfsd_export_flush(net);
> + svc_destroy(&serv);
> }
>
> void nfsd_reset_versions(struct nfsd_net *nn)
> @@ -647,11 +639,9 @@ void nfsd_shutdown_threads(struct net *net)
> return;
> }
>
> - svc_get(serv);
> /* Kill outstanding nfsd threads */
> svc_set_num_threads(serv, NULL, 0);
> nfsd_last_thread(net);
> - svc_put(serv);
> mutex_unlock(&nfsd_mutex);
> }
>
> @@ -667,10 +657,9 @@ int nfsd_create_serv(struct net *net)
> struct svc_serv *serv;
>
> WARN_ON(!mutex_is_locked(&nfsd_mutex));
> - if (nn->nfsd_serv) {
> - svc_get(nn->nfsd_serv);
> + if (nn->nfsd_serv)
> return 0;
> - }
> +
> if (nfsd_max_blksize == 0)
> nfsd_max_blksize = nfsd_get_default_max_blksize();
> nfsd_reset_versions(nn);
> @@ -681,7 +670,7 @@ int nfsd_create_serv(struct net *net)
> serv->sv_maxconn = nn->max_connections;
> error = svc_bind(serv, net);
> if (error < 0) {
> - svc_put(serv);
> + svc_destroy(&serv);
> return error;
> }
> spin_lock(&nfsd_notifier_lock);
> @@ -764,7 +753,6 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
> nthreads[0] = 1;
>
> /* apply the new numbers */
> - svc_get(nn->nfsd_serv);
> for (i = 0; i < n; i++) {
> err = svc_set_num_threads(nn->nfsd_serv,
> &nn->nfsd_serv->sv_pools[i],
> @@ -772,7 +760,6 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
> if (err)
> break;
> }
> - svc_put(nn->nfsd_serv);
> return err;
> }
>
> @@ -814,13 +801,8 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
> goto out_put;
> error = serv->sv_nrthreads;
> out_put:
> - /* Threads now hold service active */
> - if (xchg(&nn->keep_active, 0))
> - svc_put(serv);
> -
> if (serv->sv_nrthreads == 0)
> nfsd_last_thread(net);
> - svc_put(serv);
> out:
> mutex_unlock(&nfsd_mutex);
> return error;
> diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
> index 11acad6988a2..e50e12ed4d12 100644
> --- a/include/linux/sunrpc/svc.h
> +++ b/include/linux/sunrpc/svc.h
> @@ -69,7 +69,6 @@ struct svc_serv {
> struct svc_program * sv_program; /* RPC program */
> struct svc_stat * sv_stats; /* RPC statistics */
> spinlock_t sv_lock;
> - struct kref sv_refcnt;
> unsigned int sv_nrthreads; /* # of server threads */
> unsigned int sv_maxconn; /* max connections allowed or
> * '0' causing max to be based
> @@ -97,31 +96,7 @@ struct svc_serv {
> #endif /* CONFIG_SUNRPC_BACKCHANNEL */
> };
>
> -/**
> - * svc_get() - increment reference count on a SUNRPC serv
> - * @serv: the svc_serv to have count incremented
> - *
> - * Returns: the svc_serv that was passed in.
> - */
> -static inline struct svc_serv *svc_get(struct svc_serv *serv)
> -{
> - kref_get(&serv->sv_refcnt);
> - return serv;
> -}
> -
> -void svc_destroy(struct kref *);
> -
> -/**
> - * svc_put - decrement reference count on a SUNRPC serv
> - * @serv: the svc_serv to have count decremented
> - *
> - * When the reference count reaches zero, svc_destroy()
> - * is called to clean up and free the serv.
> - */
> -static inline void svc_put(struct svc_serv *serv)
> -{
> - kref_put(&serv->sv_refcnt, svc_destroy);
> -}
> +void svc_destroy(struct svc_serv **svcp);
>
> /*
> * Maximum payload size supported by a kernel RPC server.
> diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
> index 3f2ea7a0496f..0b5889e058c5 100644
> --- a/net/sunrpc/svc.c
> +++ b/net/sunrpc/svc.c
> @@ -463,7 +463,6 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
> return NULL;
> serv->sv_name = prog->pg_name;
> serv->sv_program = prog;
> - kref_init(&serv->sv_refcnt);
> serv->sv_stats = prog->pg_stats;
> if (bufsize > RPCSVC_MAXPAYLOAD)
> bufsize = RPCSVC_MAXPAYLOAD;
> @@ -564,11 +563,13 @@ EXPORT_SYMBOL_GPL(svc_create_pooled);
> * protect sv_permsocks and sv_tempsocks.
> */
> void
> -svc_destroy(struct kref *ref)
> +svc_destroy(struct svc_serv **servp)
> {
> - struct svc_serv *serv = container_of(ref, struct svc_serv, sv_refcnt);
> + struct svc_serv *serv = *servp;
> unsigned int i;
>
> + *servp = NULL;
> +
> dprintk("svc: svc_destroy(%s)\n", serv->sv_program->pg_name);
> timer_shutdown_sync(&serv->sv_temptimer);
>
> @@ -675,7 +676,6 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
> if (!rqstp)
> return ERR_PTR(-ENOMEM);
>
> - svc_get(serv);
> spin_lock_bh(&serv->sv_lock);
> serv->sv_nrthreads += 1;
> spin_unlock_bh(&serv->sv_lock);
> @@ -935,11 +935,6 @@ svc_exit_thread(struct svc_rqst *rqstp)
>
> svc_rqst_free(rqstp);
>
> - svc_put(serv);
> - /* That svc_put() cannot be the last, because the thread
> - * waiting for SP_VICTIM_REMAINS to clear must hold
> - * a reference. So it is still safe to access pool.
> - */
> clear_and_wake_up_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
> }
> EXPORT_SYMBOL_GPL(svc_exit_thread);
Ok, now I'm seeing the point of the change. This is a lot nicer than
dealing with a refcount.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
next prev parent reply other threads:[~2023-10-30 13:15 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-30 1:08 [PATCH 0/5] sunrpc: not refcounting svc_serv NeilBrown
2023-10-30 1:08 ` [PATCH 1/5] nfsd: call nfsd_last_thread() before final nfsd_put() NeilBrown
2023-10-30 13:15 ` Jeff Layton
2023-10-30 15:52 ` Chuck Lever
2023-10-30 16:41 ` Jeff Layton
2023-10-30 1:08 ` [PATCH 2/5] svc: don't hold reference for poolstats, only mutex NeilBrown
2023-10-30 13:01 ` Jeff Layton
2023-10-30 21:48 ` NeilBrown
2023-10-31 13:08 ` Jeff Layton
2023-10-30 1:08 ` [PATCH 3/5] nfsd: hold nfsd_mutex across entire netlink operation NeilBrown
2023-10-30 13:03 ` Jeff Layton
2023-10-30 13:27 ` Lorenzo Bianconi
2023-10-30 1:08 ` [PATCH 4/5] SUNRPC: discard sv_refcnt, and svc_get/svc_put NeilBrown
2023-10-30 13:15 ` Jeff Layton [this message]
2023-10-30 1:08 ` [PATCH 5/5] nfsd: rename nfsd_last_thread() to nfsd_destroy_serv() NeilBrown
2023-10-30 13:15 ` Jeff Layton
2023-10-31 15:39 ` [PATCH 0/5] sunrpc: not refcounting svc_serv Chuck Lever III
2023-10-31 20:40 ` NeilBrown
2023-10-31 20:42 ` Chuck Lever III
2023-10-31 20:46 ` NeilBrown
2023-10-31 20:50 ` Chuck Lever III
-- strict thread matches above, loose matches on Subject: below --
2023-12-15 0:56 [PATCH 0/5 v2] sunrpc: stop " NeilBrown
2023-12-15 0:56 ` [PATCH 4/5] SUNRPC: discard sv_refcnt, and svc_get/svc_put NeilBrown
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=d8b02810ea9e52fbb478cd5db639db492f3cf260.camel@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=chuck.lever@oracle.com \
--cc=kolga@netapp.com \
--cc=linux-nfs@vger.kernel.org \
--cc=neilb@suse.de \
--cc=tom@talpey.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