sunrpc: fix up the special handling of sv_nrpools == 1

Only pooled services take a reference to the svc_pool_map. The sunrpc
code has always used the sv_nrpools value to detect whether the service
is pooled.

The problem there is that nfsd is a pooled service, but when it's
running in "global" pool_mode, it doesn't take a reference to the pool
map because it has a sv_nrpools value of 1. This means that we have
two separate codepaths for starting the server, depending on whether
it's pooled or not.

Fix this by adding a new flag to the svc_serv, that indicates whether
the serv is pooled. With this we can have the nfsd service
unconditionally take a reference, regardless of pool_mode.

Note that this is a behavior change for
/sys/module/sunrpc/parameters/pool_mode. Usually this file does not
allow you to change the pool-mode while there are nfsd threads running,
but if the pool-mode is "global" it's allowed. My assumption is that
this is a bug, since it probably should never have worked this way.

This patch changes the behavior such that you get back EBUSY even
when nfsd is running in global mode. I think this is more reasonable
behavior, and given that most people set this today using the module
parameter, it's doubtful anyone will notice.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

authored by Jeff Layton and committed by Chuck Lever 8e0c8d23 3a6adfca

+8 -19
+1
include/linux/sunrpc/svc.h
··· 85 85 char * sv_name; /* service name */ 86 86 87 87 unsigned int sv_nrpools; /* number of thread pools */ 88 + bool sv_is_pooled; /* is this a pooled service? */ 88 89 struct svc_pool * sv_pools; /* array of thread pools */ 89 90 int (*sv_threadfn)(void *data); 90 91
+7 -19
net/sunrpc/svc.c
··· 250 250 int npools = -1; 251 251 252 252 mutex_lock(&svc_pool_map_mutex); 253 - 254 253 if (m->count++) { 255 254 mutex_unlock(&svc_pool_map_mutex); 256 - WARN_ON_ONCE(m->npools <= 1); 257 255 return m->npools; 258 256 } 259 257 ··· 273 275 m->mode = SVC_POOL_GLOBAL; 274 276 } 275 277 m->npools = npools; 276 - 277 - if (npools == 1) 278 - /* service is unpooled, so doesn't hold a reference */ 279 - m->count--; 280 - 281 278 mutex_unlock(&svc_pool_map_mutex); 282 279 return npools; 283 280 } 284 281 285 282 /* 286 - * Drop a reference to the global map of cpus to pools, if 287 - * pools were in use, i.e. if npools > 1. 283 + * Drop a reference to the global map of cpus to pools. 288 284 * When the last reference is dropped, the map data is 289 - * freed; this allows the sysadmin to change the pool 290 - * mode using the pool_mode module option without 291 - * rebooting or re-loading sunrpc.ko. 285 + * freed; this allows the sysadmin to change the pool. 292 286 */ 293 287 static void 294 - svc_pool_map_put(int npools) 288 + svc_pool_map_put(void) 295 289 { 296 290 struct svc_pool_map *m = &svc_pool_map; 297 291 298 - if (npools <= 1) 299 - return; 300 292 mutex_lock(&svc_pool_map_mutex); 301 - 302 293 if (!--m->count) { 303 294 kfree(m->to_pool); 304 295 m->to_pool = NULL; ··· 295 308 m->pool_to = NULL; 296 309 m->npools = 0; 297 310 } 298 - 299 311 mutex_unlock(&svc_pool_map_mutex); 300 312 } 301 313 ··· 539 553 serv = __svc_create(prog, stats, bufsize, npools, threadfn); 540 554 if (!serv) 541 555 goto out_err; 556 + serv->sv_is_pooled = true; 542 557 return serv; 543 558 out_err: 544 - svc_pool_map_put(npools); 559 + svc_pool_map_put(); 545 560 return NULL; 546 561 } 547 562 EXPORT_SYMBOL_GPL(svc_create_pooled); ··· 572 585 573 586 cache_clean_deferred(serv); 574 587 575 - svc_pool_map_put(serv->sv_nrpools); 588 + if (serv->sv_is_pooled) 589 + svc_pool_map_put(); 576 590 577 591 for (i = 0; i < serv->sv_nrpools; i++) { 578 592 struct svc_pool *pool = &serv->sv_pools[i];