Skip to content

Commit 1e3577a

Browse files
neilbrownchucklever
authored andcommitted
SUNRPC: discard sv_refcnt, and svc_get/svc_put
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 simply not destroy the svc while there are any permanent sockets attached. 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 <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 7b207cc commit 1e3577a

File tree

7 files changed

+21
-88
lines changed

7 files changed

+21
-88
lines changed

fs/lockd/svc.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,10 @@ static int lockd_get(void)
345345

346346
serv->sv_maxconn = nlm_max_connections;
347347
error = svc_set_num_threads(serv, NULL, 1);
348-
/* The thread now holds the only reference */
349-
svc_put(serv);
350-
if (error < 0)
348+
if (error < 0) {
349+
svc_destroy(&serv);
351350
return error;
351+
}
352352

353353
nlmsvc_serv = serv;
354354
register_inetaddr_notifier(&lockd_inetaddr_notifier);
@@ -372,11 +372,9 @@ static void lockd_put(void)
372372
unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
373373
#endif
374374

375-
svc_get(nlmsvc_serv);
376375
svc_set_num_threads(nlmsvc_serv, NULL, 0);
377-
svc_put(nlmsvc_serv);
378376
timer_delete_sync(&nlmsvc_retry);
379-
nlmsvc_serv = NULL;
377+
svc_destroy(&nlmsvc_serv);
380378
dprintk("lockd_down: service destroyed\n");
381379
}
382380

fs/nfs/callback.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
187187
* Check whether we're already up and running.
188188
*/
189189
if (cb_info->serv)
190-
return svc_get(cb_info->serv);
190+
return cb_info->serv;
191191

192192
/*
193193
* Sanity check: if there's no task,
@@ -245,9 +245,10 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
245245

246246
cb_info->users++;
247247
err_net:
248-
if (!cb_info->users)
249-
cb_info->serv = NULL;
250-
svc_put(serv);
248+
if (!cb_info->users) {
249+
svc_set_num_threads(cb_info->serv, NULL, 0);
250+
svc_destroy(&cb_info->serv);
251+
}
251252
err_create:
252253
mutex_unlock(&nfs_callback_mutex);
253254
return ret;
@@ -271,11 +272,9 @@ void nfs_callback_down(int minorversion, struct net *net)
271272
nfs_callback_down_net(minorversion, serv, net);
272273
cb_info->users--;
273274
if (cb_info->users == 0) {
274-
svc_get(serv);
275275
svc_set_num_threads(serv, NULL, 0);
276-
svc_put(serv);
277276
dprintk("nfs_callback_down: service destroyed\n");
278-
cb_info->serv = NULL;
277+
svc_destroy(&cb_info->serv);
279278
}
280279
mutex_unlock(&nfs_callback_mutex);
281280
}

fs/nfsd/netns.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,6 @@ struct nfsd_net {
126126
struct svc_info nfsd_info;
127127
#define nfsd_serv nfsd_info.serv
128128

129-
/* When a listening socket is added to nfsd, keep_active is set
130-
* and this justifies a reference on nfsd_serv. This stops
131-
* nfsd_serv from being freed. When the number of threads is
132-
* set, keep_active is cleared and the reference is dropped. So
133-
* when the last thread exits, the service will be destroyed.
134-
*/
135-
int keep_active;
136129

137130
/*
138131
* clientid and stateid data for construction of net unique COPY

fs/nfsd/nfsctl.c

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -711,12 +711,8 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred
711711
serv = nn->nfsd_serv;
712712
err = svc_addsock(serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
713713

714-
if (err < 0 && !serv->sv_nrthreads && !nn->keep_active)
714+
if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks))
715715
nfsd_last_thread(net);
716-
else if (err >= 0 && !serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
717-
svc_get(serv);
718-
719-
svc_put(serv);
720716
return err;
721717
}
722718

@@ -754,10 +750,6 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
754750
if (err < 0 && err != -EAFNOSUPPORT)
755751
goto out_close;
756752

757-
if (!serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
758-
svc_get(serv);
759-
760-
svc_put(serv);
761753
return 0;
762754
out_close:
763755
xprt = svc_find_xprt(serv, transport, net, PF_INET, port);
@@ -766,10 +758,9 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
766758
svc_xprt_put(xprt);
767759
}
768760
out_err:
769-
if (!serv->sv_nrthreads && !nn->keep_active)
761+
if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks))
770762
nfsd_last_thread(net);
771763

772-
svc_put(serv);
773764
return err;
774765
}
775766

fs/nfsd/nfssvc.c

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,6 @@ static __be32 nfsd_init_request(struct svc_rqst *,
5959
* nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
6060
* of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
6161
*
62-
* If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a
63-
* properly initialised 'struct svc_serv' with ->sv_nrthreads > 0 (unless
64-
* nn->keep_active is set). That number of nfsd threads must
65-
* exist and each must be listed in ->sp_all_threads in some entry of
66-
* ->sv_pools[].
67-
*
68-
* Each active thread holds a counted reference on nn->nfsd_serv, as does
69-
* the nn->keep_active flag and various transient calls to svc_get().
70-
*
7162
* Finally, the nfsd_mutex also protects some of the global variables that are
7263
* accessed when nfsd starts and that are settable via the write_* routines in
7364
* nfsctl.c. In particular:
@@ -572,6 +563,7 @@ void nfsd_last_thread(struct net *net)
572563

573564
nfsd_shutdown_net(net);
574565
nfsd_export_flush(net);
566+
svc_destroy(&serv);
575567
}
576568

577569
void nfsd_reset_versions(struct nfsd_net *nn)
@@ -646,11 +638,9 @@ void nfsd_shutdown_threads(struct net *net)
646638
return;
647639
}
648640

649-
svc_get(serv);
650641
/* Kill outstanding nfsd threads */
651642
svc_set_num_threads(serv, NULL, 0);
652643
nfsd_last_thread(net);
653-
svc_put(serv);
654644
mutex_unlock(&nfsd_mutex);
655645
}
656646

@@ -666,10 +656,9 @@ int nfsd_create_serv(struct net *net)
666656
struct svc_serv *serv;
667657

668658
WARN_ON(!mutex_is_locked(&nfsd_mutex));
669-
if (nn->nfsd_serv) {
670-
svc_get(nn->nfsd_serv);
659+
if (nn->nfsd_serv)
671660
return 0;
672-
}
661+
673662
if (nfsd_max_blksize == 0)
674663
nfsd_max_blksize = nfsd_get_default_max_blksize();
675664
nfsd_reset_versions(nn);
@@ -680,7 +669,7 @@ int nfsd_create_serv(struct net *net)
680669
serv->sv_maxconn = nn->max_connections;
681670
error = svc_bind(serv, net);
682671
if (error < 0) {
683-
svc_put(serv);
672+
svc_destroy(&serv);
684673
return error;
685674
}
686675
spin_lock(&nfsd_notifier_lock);
@@ -764,15 +753,13 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
764753
nthreads[0] = 1;
765754

766755
/* apply the new numbers */
767-
svc_get(nn->nfsd_serv);
768756
for (i = 0; i < n; i++) {
769757
err = svc_set_num_threads(nn->nfsd_serv,
770758
&nn->nfsd_serv->sv_pools[i],
771759
nthreads[i]);
772760
if (err)
773761
break;
774762
}
775-
svc_put(nn->nfsd_serv);
776763
return err;
777764
}
778765

@@ -814,13 +801,8 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
814801
goto out_put;
815802
error = serv->sv_nrthreads;
816803
out_put:
817-
/* Threads now hold service active */
818-
if (xchg(&nn->keep_active, 0))
819-
svc_put(serv);
820-
821804
if (serv->sv_nrthreads == 0)
822805
nfsd_last_thread(net);
823-
svc_put(serv);
824806
out:
825807
mutex_unlock(&nfsd_mutex);
826808
return error;

include/linux/sunrpc/svc.h

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ struct svc_serv {
6969
struct svc_program * sv_program; /* RPC program */
7070
struct svc_stat * sv_stats; /* RPC statistics */
7171
spinlock_t sv_lock;
72-
struct kref sv_refcnt;
7372
unsigned int sv_nrthreads; /* # of server threads */
7473
unsigned int sv_maxconn; /* max connections allowed or
7574
* '0' causing max to be based
@@ -103,31 +102,7 @@ struct svc_info {
103102
struct mutex *mutex;
104103
};
105104

106-
/**
107-
* svc_get() - increment reference count on a SUNRPC serv
108-
* @serv: the svc_serv to have count incremented
109-
*
110-
* Returns: the svc_serv that was passed in.
111-
*/
112-
static inline struct svc_serv *svc_get(struct svc_serv *serv)
113-
{
114-
kref_get(&serv->sv_refcnt);
115-
return serv;
116-
}
117-
118-
void svc_destroy(struct kref *);
119-
120-
/**
121-
* svc_put - decrement reference count on a SUNRPC serv
122-
* @serv: the svc_serv to have count decremented
123-
*
124-
* When the reference count reaches zero, svc_destroy()
125-
* is called to clean up and free the serv.
126-
*/
127-
static inline void svc_put(struct svc_serv *serv)
128-
{
129-
kref_put(&serv->sv_refcnt, svc_destroy);
130-
}
105+
void svc_destroy(struct svc_serv **svcp);
131106

132107
/*
133108
* Maximum payload size supported by a kernel RPC server.

net/sunrpc/svc.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,6 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
463463
return NULL;
464464
serv->sv_name = prog->pg_name;
465465
serv->sv_program = prog;
466-
kref_init(&serv->sv_refcnt);
467466
serv->sv_stats = prog->pg_stats;
468467
if (bufsize > RPCSVC_MAXPAYLOAD)
469468
bufsize = RPCSVC_MAXPAYLOAD;
@@ -564,11 +563,13 @@ EXPORT_SYMBOL_GPL(svc_create_pooled);
564563
* protect sv_permsocks and sv_tempsocks.
565564
*/
566565
void
567-
svc_destroy(struct kref *ref)
566+
svc_destroy(struct svc_serv **servp)
568567
{
569-
struct svc_serv *serv = container_of(ref, struct svc_serv, sv_refcnt);
568+
struct svc_serv *serv = *servp;
570569
unsigned int i;
571570

571+
*servp = NULL;
572+
572573
dprintk("svc: svc_destroy(%s)\n", serv->sv_program->pg_name);
573574
timer_shutdown_sync(&serv->sv_temptimer);
574575

@@ -675,7 +676,6 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
675676
if (!rqstp)
676677
return ERR_PTR(-ENOMEM);
677678

678-
svc_get(serv);
679679
spin_lock_bh(&serv->sv_lock);
680680
serv->sv_nrthreads += 1;
681681
spin_unlock_bh(&serv->sv_lock);
@@ -935,11 +935,6 @@ svc_exit_thread(struct svc_rqst *rqstp)
935935

936936
svc_rqst_free(rqstp);
937937

938-
svc_put(serv);
939-
/* That svc_put() cannot be the last, because the thread
940-
* waiting for SP_VICTIM_REMAINS to clear must hold
941-
* a reference. So it is still safe to access pool.
942-
*/
943938
clear_and_wake_up_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
944939
}
945940
EXPORT_SYMBOL_GPL(svc_exit_thread);

0 commit comments

Comments
 (0)