11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/net/sunrpc/svc.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * High-level RPC service routines 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 7*bfd24160SGreg Banks * 8*bfd24160SGreg Banks * Multiple threads pools and NUMAisation 9*bfd24160SGreg Banks * Copyright (c) 2006 Silicon Graphics, Inc. 10*bfd24160SGreg Banks * by Greg Banks <gnb@melbourne.sgi.com> 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds 131da177e4SLinus Torvalds #include <linux/linkage.h> 141da177e4SLinus Torvalds #include <linux/sched.h> 151da177e4SLinus Torvalds #include <linux/errno.h> 161da177e4SLinus Torvalds #include <linux/net.h> 171da177e4SLinus Torvalds #include <linux/in.h> 181da177e4SLinus Torvalds #include <linux/mm.h> 19a7455442SGreg Banks #include <linux/interrupt.h> 20a7455442SGreg Banks #include <linux/module.h> 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/sunrpc/types.h> 231da177e4SLinus Torvalds #include <linux/sunrpc/xdr.h> 241da177e4SLinus Torvalds #include <linux/sunrpc/stats.h> 251da177e4SLinus Torvalds #include <linux/sunrpc/svcsock.h> 261da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h> 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #define RPCDBG_FACILITY RPCDBG_SVCDSP 291da177e4SLinus Torvalds #define RPC_PARANOIA 1 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds /* 32*bfd24160SGreg Banks * Mode for mapping cpus to pools. 33*bfd24160SGreg Banks */ 34*bfd24160SGreg Banks enum { 35*bfd24160SGreg Banks SVC_POOL_NONE = -1, /* uninitialised, choose one of the others */ 36*bfd24160SGreg Banks SVC_POOL_GLOBAL, /* no mapping, just a single global pool 37*bfd24160SGreg Banks * (legacy & UP mode) */ 38*bfd24160SGreg Banks SVC_POOL_PERCPU, /* one pool per cpu */ 39*bfd24160SGreg Banks SVC_POOL_PERNODE /* one pool per numa node */ 40*bfd24160SGreg Banks }; 41*bfd24160SGreg Banks 42*bfd24160SGreg Banks /* 43*bfd24160SGreg Banks * Structure for mapping cpus to pools and vice versa. 44*bfd24160SGreg Banks * Setup once during sunrpc initialisation. 45*bfd24160SGreg Banks */ 46*bfd24160SGreg Banks static struct svc_pool_map { 47*bfd24160SGreg Banks int mode; /* Note: int not enum to avoid 48*bfd24160SGreg Banks * warnings about "enumeration value 49*bfd24160SGreg Banks * not handled in switch" */ 50*bfd24160SGreg Banks unsigned int npools; 51*bfd24160SGreg Banks unsigned int *pool_to; /* maps pool id to cpu or node */ 52*bfd24160SGreg Banks unsigned int *to_pool; /* maps cpu or node to pool id */ 53*bfd24160SGreg Banks } svc_pool_map = { 54*bfd24160SGreg Banks .mode = SVC_POOL_NONE 55*bfd24160SGreg Banks }; 56*bfd24160SGreg Banks 57*bfd24160SGreg Banks 58*bfd24160SGreg Banks /* 59*bfd24160SGreg Banks * Detect best pool mapping mode heuristically, 60*bfd24160SGreg Banks * according to the machine's topology. 61*bfd24160SGreg Banks */ 62*bfd24160SGreg Banks static int 63*bfd24160SGreg Banks svc_pool_map_choose_mode(void) 64*bfd24160SGreg Banks { 65*bfd24160SGreg Banks unsigned int node; 66*bfd24160SGreg Banks 67*bfd24160SGreg Banks if (num_online_nodes() > 1) { 68*bfd24160SGreg Banks /* 69*bfd24160SGreg Banks * Actually have multiple NUMA nodes, 70*bfd24160SGreg Banks * so split pools on NUMA node boundaries 71*bfd24160SGreg Banks */ 72*bfd24160SGreg Banks return SVC_POOL_PERNODE; 73*bfd24160SGreg Banks } 74*bfd24160SGreg Banks 75*bfd24160SGreg Banks node = any_online_node(node_online_map); 76*bfd24160SGreg Banks if (nr_cpus_node(node) > 2) { 77*bfd24160SGreg Banks /* 78*bfd24160SGreg Banks * Non-trivial SMP, or CONFIG_NUMA on 79*bfd24160SGreg Banks * non-NUMA hardware, e.g. with a generic 80*bfd24160SGreg Banks * x86_64 kernel on Xeons. In this case we 81*bfd24160SGreg Banks * want to divide the pools on cpu boundaries. 82*bfd24160SGreg Banks */ 83*bfd24160SGreg Banks return SVC_POOL_PERCPU; 84*bfd24160SGreg Banks } 85*bfd24160SGreg Banks 86*bfd24160SGreg Banks /* default: one global pool */ 87*bfd24160SGreg Banks return SVC_POOL_GLOBAL; 88*bfd24160SGreg Banks } 89*bfd24160SGreg Banks 90*bfd24160SGreg Banks /* 91*bfd24160SGreg Banks * Allocate the to_pool[] and pool_to[] arrays. 92*bfd24160SGreg Banks * Returns 0 on success or an errno. 93*bfd24160SGreg Banks */ 94*bfd24160SGreg Banks static int 95*bfd24160SGreg Banks svc_pool_map_alloc_arrays(struct svc_pool_map *m, unsigned int maxpools) 96*bfd24160SGreg Banks { 97*bfd24160SGreg Banks m->to_pool = kcalloc(maxpools, sizeof(unsigned int), GFP_KERNEL); 98*bfd24160SGreg Banks if (!m->to_pool) 99*bfd24160SGreg Banks goto fail; 100*bfd24160SGreg Banks m->pool_to = kcalloc(maxpools, sizeof(unsigned int), GFP_KERNEL); 101*bfd24160SGreg Banks if (!m->pool_to) 102*bfd24160SGreg Banks goto fail_free; 103*bfd24160SGreg Banks 104*bfd24160SGreg Banks return 0; 105*bfd24160SGreg Banks 106*bfd24160SGreg Banks fail_free: 107*bfd24160SGreg Banks kfree(m->to_pool); 108*bfd24160SGreg Banks fail: 109*bfd24160SGreg Banks return -ENOMEM; 110*bfd24160SGreg Banks } 111*bfd24160SGreg Banks 112*bfd24160SGreg Banks /* 113*bfd24160SGreg Banks * Initialise the pool map for SVC_POOL_PERCPU mode. 114*bfd24160SGreg Banks * Returns number of pools or <0 on error. 115*bfd24160SGreg Banks */ 116*bfd24160SGreg Banks static int 117*bfd24160SGreg Banks svc_pool_map_init_percpu(struct svc_pool_map *m) 118*bfd24160SGreg Banks { 119*bfd24160SGreg Banks unsigned int maxpools = highest_possible_processor_id()+1; 120*bfd24160SGreg Banks unsigned int pidx = 0; 121*bfd24160SGreg Banks unsigned int cpu; 122*bfd24160SGreg Banks int err; 123*bfd24160SGreg Banks 124*bfd24160SGreg Banks err = svc_pool_map_alloc_arrays(m, maxpools); 125*bfd24160SGreg Banks if (err) 126*bfd24160SGreg Banks return err; 127*bfd24160SGreg Banks 128*bfd24160SGreg Banks for_each_online_cpu(cpu) { 129*bfd24160SGreg Banks BUG_ON(pidx > maxpools); 130*bfd24160SGreg Banks m->to_pool[cpu] = pidx; 131*bfd24160SGreg Banks m->pool_to[pidx] = cpu; 132*bfd24160SGreg Banks pidx++; 133*bfd24160SGreg Banks } 134*bfd24160SGreg Banks /* cpus brought online later all get mapped to pool0, sorry */ 135*bfd24160SGreg Banks 136*bfd24160SGreg Banks return pidx; 137*bfd24160SGreg Banks }; 138*bfd24160SGreg Banks 139*bfd24160SGreg Banks 140*bfd24160SGreg Banks /* 141*bfd24160SGreg Banks * Initialise the pool map for SVC_POOL_PERNODE mode. 142*bfd24160SGreg Banks * Returns number of pools or <0 on error. 143*bfd24160SGreg Banks */ 144*bfd24160SGreg Banks static int 145*bfd24160SGreg Banks svc_pool_map_init_pernode(struct svc_pool_map *m) 146*bfd24160SGreg Banks { 147*bfd24160SGreg Banks unsigned int maxpools = highest_possible_node_id()+1; 148*bfd24160SGreg Banks unsigned int pidx = 0; 149*bfd24160SGreg Banks unsigned int node; 150*bfd24160SGreg Banks int err; 151*bfd24160SGreg Banks 152*bfd24160SGreg Banks err = svc_pool_map_alloc_arrays(m, maxpools); 153*bfd24160SGreg Banks if (err) 154*bfd24160SGreg Banks return err; 155*bfd24160SGreg Banks 156*bfd24160SGreg Banks for_each_node_with_cpus(node) { 157*bfd24160SGreg Banks /* some architectures (e.g. SN2) have cpuless nodes */ 158*bfd24160SGreg Banks BUG_ON(pidx > maxpools); 159*bfd24160SGreg Banks m->to_pool[node] = pidx; 160*bfd24160SGreg Banks m->pool_to[pidx] = node; 161*bfd24160SGreg Banks pidx++; 162*bfd24160SGreg Banks } 163*bfd24160SGreg Banks /* nodes brought online later all get mapped to pool0, sorry */ 164*bfd24160SGreg Banks 165*bfd24160SGreg Banks return pidx; 166*bfd24160SGreg Banks } 167*bfd24160SGreg Banks 168*bfd24160SGreg Banks 169*bfd24160SGreg Banks /* 170*bfd24160SGreg Banks * Build the global map of cpus to pools and vice versa. 171*bfd24160SGreg Banks */ 172*bfd24160SGreg Banks static unsigned int 173*bfd24160SGreg Banks svc_pool_map_init(void) 174*bfd24160SGreg Banks { 175*bfd24160SGreg Banks struct svc_pool_map *m = &svc_pool_map; 176*bfd24160SGreg Banks int npools = -1; 177*bfd24160SGreg Banks 178*bfd24160SGreg Banks if (m->mode != SVC_POOL_NONE) 179*bfd24160SGreg Banks return m->npools; 180*bfd24160SGreg Banks 181*bfd24160SGreg Banks m->mode = svc_pool_map_choose_mode(); 182*bfd24160SGreg Banks 183*bfd24160SGreg Banks switch (m->mode) { 184*bfd24160SGreg Banks case SVC_POOL_PERCPU: 185*bfd24160SGreg Banks npools = svc_pool_map_init_percpu(m); 186*bfd24160SGreg Banks break; 187*bfd24160SGreg Banks case SVC_POOL_PERNODE: 188*bfd24160SGreg Banks npools = svc_pool_map_init_pernode(m); 189*bfd24160SGreg Banks break; 190*bfd24160SGreg Banks } 191*bfd24160SGreg Banks 192*bfd24160SGreg Banks if (npools < 0) { 193*bfd24160SGreg Banks /* default, or memory allocation failure */ 194*bfd24160SGreg Banks npools = 1; 195*bfd24160SGreg Banks m->mode = SVC_POOL_GLOBAL; 196*bfd24160SGreg Banks } 197*bfd24160SGreg Banks m->npools = npools; 198*bfd24160SGreg Banks 199*bfd24160SGreg Banks return m->npools; 200*bfd24160SGreg Banks } 201*bfd24160SGreg Banks 202*bfd24160SGreg Banks /* 203*bfd24160SGreg Banks * Set the current thread's cpus_allowed mask so that it 204*bfd24160SGreg Banks * will only run on cpus in the given pool. 205*bfd24160SGreg Banks * 206*bfd24160SGreg Banks * Returns 1 and fills in oldmask iff a cpumask was applied. 207*bfd24160SGreg Banks */ 208*bfd24160SGreg Banks static inline int 209*bfd24160SGreg Banks svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask) 210*bfd24160SGreg Banks { 211*bfd24160SGreg Banks struct svc_pool_map *m = &svc_pool_map; 212*bfd24160SGreg Banks unsigned int node; /* or cpu */ 213*bfd24160SGreg Banks 214*bfd24160SGreg Banks /* 215*bfd24160SGreg Banks * The caller checks for sv_nrpools > 1, which 216*bfd24160SGreg Banks * implies that we've been initialized and the 217*bfd24160SGreg Banks * map mode is not NONE. 218*bfd24160SGreg Banks */ 219*bfd24160SGreg Banks BUG_ON(m->mode == SVC_POOL_NONE); 220*bfd24160SGreg Banks 221*bfd24160SGreg Banks switch (m->mode) 222*bfd24160SGreg Banks { 223*bfd24160SGreg Banks default: 224*bfd24160SGreg Banks return 0; 225*bfd24160SGreg Banks case SVC_POOL_PERCPU: 226*bfd24160SGreg Banks node = m->pool_to[pidx]; 227*bfd24160SGreg Banks *oldmask = current->cpus_allowed; 228*bfd24160SGreg Banks set_cpus_allowed(current, cpumask_of_cpu(node)); 229*bfd24160SGreg Banks return 1; 230*bfd24160SGreg Banks case SVC_POOL_PERNODE: 231*bfd24160SGreg Banks node = m->pool_to[pidx]; 232*bfd24160SGreg Banks *oldmask = current->cpus_allowed; 233*bfd24160SGreg Banks set_cpus_allowed(current, node_to_cpumask(node)); 234*bfd24160SGreg Banks return 1; 235*bfd24160SGreg Banks } 236*bfd24160SGreg Banks } 237*bfd24160SGreg Banks 238*bfd24160SGreg Banks /* 239*bfd24160SGreg Banks * Use the mapping mode to choose a pool for a given CPU. 240*bfd24160SGreg Banks * Used when enqueueing an incoming RPC. Always returns 241*bfd24160SGreg Banks * a non-NULL pool pointer. 242*bfd24160SGreg Banks */ 243*bfd24160SGreg Banks struct svc_pool * 244*bfd24160SGreg Banks svc_pool_for_cpu(struct svc_serv *serv, int cpu) 245*bfd24160SGreg Banks { 246*bfd24160SGreg Banks struct svc_pool_map *m = &svc_pool_map; 247*bfd24160SGreg Banks unsigned int pidx = 0; 248*bfd24160SGreg Banks 249*bfd24160SGreg Banks /* 250*bfd24160SGreg Banks * SVC_POOL_NONE happens in a pure client when 251*bfd24160SGreg Banks * lockd is brought up, so silently treat it the 252*bfd24160SGreg Banks * same as SVC_POOL_GLOBAL. 253*bfd24160SGreg Banks */ 254*bfd24160SGreg Banks 255*bfd24160SGreg Banks switch (m->mode) { 256*bfd24160SGreg Banks case SVC_POOL_PERCPU: 257*bfd24160SGreg Banks pidx = m->to_pool[cpu]; 258*bfd24160SGreg Banks break; 259*bfd24160SGreg Banks case SVC_POOL_PERNODE: 260*bfd24160SGreg Banks pidx = m->to_pool[cpu_to_node(cpu)]; 261*bfd24160SGreg Banks break; 262*bfd24160SGreg Banks } 263*bfd24160SGreg Banks return &serv->sv_pools[pidx % serv->sv_nrpools]; 264*bfd24160SGreg Banks } 265*bfd24160SGreg Banks 266*bfd24160SGreg Banks 267*bfd24160SGreg Banks /* 2681da177e4SLinus Torvalds * Create an RPC service 2691da177e4SLinus Torvalds */ 270a7455442SGreg Banks static struct svc_serv * 271a7455442SGreg Banks __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, 272bc591ccfSNeilBrown void (*shutdown)(struct svc_serv *serv)) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds struct svc_serv *serv; 2751da177e4SLinus Torvalds int vers; 2761da177e4SLinus Torvalds unsigned int xdrsize; 2773262c816SGreg Banks unsigned int i; 2781da177e4SLinus Torvalds 2790da974f4SPanagiotis Issaris if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL))) 2801da177e4SLinus Torvalds return NULL; 2819ba02638SAndreas Gruenbacher serv->sv_name = prog->pg_name; 2821da177e4SLinus Torvalds serv->sv_program = prog; 2831da177e4SLinus Torvalds serv->sv_nrthreads = 1; 2841da177e4SLinus Torvalds serv->sv_stats = prog->pg_stats; 2851da177e4SLinus Torvalds serv->sv_bufsz = bufsize? bufsize : 4096; 286bc591ccfSNeilBrown serv->sv_shutdown = shutdown; 2871da177e4SLinus Torvalds xdrsize = 0; 2889ba02638SAndreas Gruenbacher while (prog) { 2899ba02638SAndreas Gruenbacher prog->pg_lovers = prog->pg_nvers-1; 2901da177e4SLinus Torvalds for (vers=0; vers<prog->pg_nvers ; vers++) 2911da177e4SLinus Torvalds if (prog->pg_vers[vers]) { 2921da177e4SLinus Torvalds prog->pg_hivers = vers; 2931da177e4SLinus Torvalds if (prog->pg_lovers > vers) 2941da177e4SLinus Torvalds prog->pg_lovers = vers; 2951da177e4SLinus Torvalds if (prog->pg_vers[vers]->vs_xdrsize > xdrsize) 2961da177e4SLinus Torvalds xdrsize = prog->pg_vers[vers]->vs_xdrsize; 2971da177e4SLinus Torvalds } 2989ba02638SAndreas Gruenbacher prog = prog->pg_next; 2999ba02638SAndreas Gruenbacher } 3001da177e4SLinus Torvalds serv->sv_xdrsize = xdrsize; 3011da177e4SLinus Torvalds INIT_LIST_HEAD(&serv->sv_tempsocks); 3021da177e4SLinus Torvalds INIT_LIST_HEAD(&serv->sv_permsocks); 30336bdfc8bSGreg Banks init_timer(&serv->sv_temptimer); 3041da177e4SLinus Torvalds spin_lock_init(&serv->sv_lock); 3051da177e4SLinus Torvalds 306a7455442SGreg Banks serv->sv_nrpools = npools; 3073262c816SGreg Banks serv->sv_pools = 3083262c816SGreg Banks kcalloc(sizeof(struct svc_pool), serv->sv_nrpools, 3093262c816SGreg Banks GFP_KERNEL); 3103262c816SGreg Banks if (!serv->sv_pools) { 3113262c816SGreg Banks kfree(serv); 3123262c816SGreg Banks return NULL; 3133262c816SGreg Banks } 3143262c816SGreg Banks 3153262c816SGreg Banks for (i = 0; i < serv->sv_nrpools; i++) { 3163262c816SGreg Banks struct svc_pool *pool = &serv->sv_pools[i]; 3173262c816SGreg Banks 3183262c816SGreg Banks dprintk("initialising pool %u for %s\n", 3193262c816SGreg Banks i, serv->sv_name); 3203262c816SGreg Banks 3213262c816SGreg Banks pool->sp_id = i; 3223262c816SGreg Banks INIT_LIST_HEAD(&pool->sp_threads); 3233262c816SGreg Banks INIT_LIST_HEAD(&pool->sp_sockets); 324a7455442SGreg Banks INIT_LIST_HEAD(&pool->sp_all_threads); 3253262c816SGreg Banks spin_lock_init(&pool->sp_lock); 3263262c816SGreg Banks } 3273262c816SGreg Banks 3283262c816SGreg Banks 3291da177e4SLinus Torvalds /* Remove any stale portmap registrations */ 3301da177e4SLinus Torvalds svc_register(serv, 0, 0); 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds return serv; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds 335a7455442SGreg Banks struct svc_serv * 336a7455442SGreg Banks svc_create(struct svc_program *prog, unsigned int bufsize, 337a7455442SGreg Banks void (*shutdown)(struct svc_serv *serv)) 338a7455442SGreg Banks { 339a7455442SGreg Banks return __svc_create(prog, bufsize, /*npools*/1, shutdown); 340a7455442SGreg Banks } 341a7455442SGreg Banks 342a7455442SGreg Banks struct svc_serv * 343a7455442SGreg Banks svc_create_pooled(struct svc_program *prog, unsigned int bufsize, 344a7455442SGreg Banks void (*shutdown)(struct svc_serv *serv), 345a7455442SGreg Banks svc_thread_fn func, int sig, struct module *mod) 346a7455442SGreg Banks { 347a7455442SGreg Banks struct svc_serv *serv; 348*bfd24160SGreg Banks unsigned int npools = svc_pool_map_init(); 349a7455442SGreg Banks 350*bfd24160SGreg Banks serv = __svc_create(prog, bufsize, npools, shutdown); 351a7455442SGreg Banks 352a7455442SGreg Banks if (serv != NULL) { 353a7455442SGreg Banks serv->sv_function = func; 354a7455442SGreg Banks serv->sv_kill_signal = sig; 355a7455442SGreg Banks serv->sv_module = mod; 356a7455442SGreg Banks } 357a7455442SGreg Banks 358a7455442SGreg Banks return serv; 359a7455442SGreg Banks } 360a7455442SGreg Banks 3611da177e4SLinus Torvalds /* 3623262c816SGreg Banks * Destroy an RPC service. Should be called with the BKL held 3631da177e4SLinus Torvalds */ 3641da177e4SLinus Torvalds void 3651da177e4SLinus Torvalds svc_destroy(struct svc_serv *serv) 3661da177e4SLinus Torvalds { 3671da177e4SLinus Torvalds struct svc_sock *svsk; 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds dprintk("RPC: svc_destroy(%s, %d)\n", 3701da177e4SLinus Torvalds serv->sv_program->pg_name, 3711da177e4SLinus Torvalds serv->sv_nrthreads); 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds if (serv->sv_nrthreads) { 3741da177e4SLinus Torvalds if (--(serv->sv_nrthreads) != 0) { 3751da177e4SLinus Torvalds svc_sock_update_bufs(serv); 3761da177e4SLinus Torvalds return; 3771da177e4SLinus Torvalds } 3781da177e4SLinus Torvalds } else 3791da177e4SLinus Torvalds printk("svc_destroy: no threads for serv=%p!\n", serv); 3801da177e4SLinus Torvalds 38136bdfc8bSGreg Banks del_timer_sync(&serv->sv_temptimer); 38236bdfc8bSGreg Banks 3831da177e4SLinus Torvalds while (!list_empty(&serv->sv_tempsocks)) { 3841da177e4SLinus Torvalds svsk = list_entry(serv->sv_tempsocks.next, 3851da177e4SLinus Torvalds struct svc_sock, 3861da177e4SLinus Torvalds sk_list); 3871da177e4SLinus Torvalds svc_delete_socket(svsk); 3881da177e4SLinus Torvalds } 389bc591ccfSNeilBrown if (serv->sv_shutdown) 390bc591ccfSNeilBrown serv->sv_shutdown(serv); 391bc591ccfSNeilBrown 3921da177e4SLinus Torvalds while (!list_empty(&serv->sv_permsocks)) { 3931da177e4SLinus Torvalds svsk = list_entry(serv->sv_permsocks.next, 3941da177e4SLinus Torvalds struct svc_sock, 3951da177e4SLinus Torvalds sk_list); 3961da177e4SLinus Torvalds svc_delete_socket(svsk); 3971da177e4SLinus Torvalds } 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds cache_clean_deferred(serv); 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds /* Unregister service with the portmapper */ 4021da177e4SLinus Torvalds svc_register(serv, 0, 0); 4033262c816SGreg Banks kfree(serv->sv_pools); 4041da177e4SLinus Torvalds kfree(serv); 4051da177e4SLinus Torvalds } 4061da177e4SLinus Torvalds 4071da177e4SLinus Torvalds /* 4081da177e4SLinus Torvalds * Allocate an RPC server's buffer space. 4091da177e4SLinus Torvalds * We allocate pages and place them in rq_argpages. 4101da177e4SLinus Torvalds */ 4111da177e4SLinus Torvalds static int 4121da177e4SLinus Torvalds svc_init_buffer(struct svc_rqst *rqstp, unsigned int size) 4131da177e4SLinus Torvalds { 4141da177e4SLinus Torvalds int pages; 4151da177e4SLinus Torvalds int arghi; 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds if (size > RPCSVC_MAXPAYLOAD) 4181da177e4SLinus Torvalds size = RPCSVC_MAXPAYLOAD; 4191da177e4SLinus Torvalds pages = 2 + (size+ PAGE_SIZE -1) / PAGE_SIZE; 4201da177e4SLinus Torvalds rqstp->rq_argused = 0; 4211da177e4SLinus Torvalds rqstp->rq_resused = 0; 4221da177e4SLinus Torvalds arghi = 0; 42309a62660SKris Katterjohn BUG_ON(pages > RPCSVC_MAXPAGES); 4241da177e4SLinus Torvalds while (pages) { 4251da177e4SLinus Torvalds struct page *p = alloc_page(GFP_KERNEL); 4261da177e4SLinus Torvalds if (!p) 4271da177e4SLinus Torvalds break; 4281da177e4SLinus Torvalds rqstp->rq_argpages[arghi++] = p; 4291da177e4SLinus Torvalds pages--; 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds rqstp->rq_arghi = arghi; 4321da177e4SLinus Torvalds return ! pages; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds 4351da177e4SLinus Torvalds /* 4361da177e4SLinus Torvalds * Release an RPC server buffer 4371da177e4SLinus Torvalds */ 4381da177e4SLinus Torvalds static void 4391da177e4SLinus Torvalds svc_release_buffer(struct svc_rqst *rqstp) 4401da177e4SLinus Torvalds { 4411da177e4SLinus Torvalds while (rqstp->rq_arghi) 4421da177e4SLinus Torvalds put_page(rqstp->rq_argpages[--rqstp->rq_arghi]); 4431da177e4SLinus Torvalds while (rqstp->rq_resused) { 4441da177e4SLinus Torvalds if (rqstp->rq_respages[--rqstp->rq_resused] == NULL) 4451da177e4SLinus Torvalds continue; 4461da177e4SLinus Torvalds put_page(rqstp->rq_respages[rqstp->rq_resused]); 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds rqstp->rq_argused = 0; 4491da177e4SLinus Torvalds } 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds /* 4523262c816SGreg Banks * Create a thread in the given pool. Caller must hold BKL. 453*bfd24160SGreg Banks * On a NUMA or SMP machine, with a multi-pool serv, the thread 454*bfd24160SGreg Banks * will be restricted to run on the cpus belonging to the pool. 4551da177e4SLinus Torvalds */ 4563262c816SGreg Banks static int 4573262c816SGreg Banks __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, 4583262c816SGreg Banks struct svc_pool *pool) 4591da177e4SLinus Torvalds { 4601da177e4SLinus Torvalds struct svc_rqst *rqstp; 4611da177e4SLinus Torvalds int error = -ENOMEM; 462*bfd24160SGreg Banks int have_oldmask = 0; 463*bfd24160SGreg Banks cpumask_t oldmask; 4641da177e4SLinus Torvalds 4650da974f4SPanagiotis Issaris rqstp = kzalloc(sizeof(*rqstp), GFP_KERNEL); 4661da177e4SLinus Torvalds if (!rqstp) 4671da177e4SLinus Torvalds goto out; 4681da177e4SLinus Torvalds 4691da177e4SLinus Torvalds init_waitqueue_head(&rqstp->rq_wait); 4701da177e4SLinus Torvalds 47112fe2c58SJesper Juhl if (!(rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL)) 47212fe2c58SJesper Juhl || !(rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL)) 4731da177e4SLinus Torvalds || !svc_init_buffer(rqstp, serv->sv_bufsz)) 4741da177e4SLinus Torvalds goto out_thread; 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds serv->sv_nrthreads++; 4773262c816SGreg Banks spin_lock_bh(&pool->sp_lock); 4783262c816SGreg Banks pool->sp_nrthreads++; 479a7455442SGreg Banks list_add(&rqstp->rq_all, &pool->sp_all_threads); 4803262c816SGreg Banks spin_unlock_bh(&pool->sp_lock); 4811da177e4SLinus Torvalds rqstp->rq_server = serv; 4823262c816SGreg Banks rqstp->rq_pool = pool; 483*bfd24160SGreg Banks 484*bfd24160SGreg Banks if (serv->sv_nrpools > 1) 485*bfd24160SGreg Banks have_oldmask = svc_pool_map_set_cpumask(pool->sp_id, &oldmask); 486*bfd24160SGreg Banks 4871da177e4SLinus Torvalds error = kernel_thread((int (*)(void *)) func, rqstp, 0); 488*bfd24160SGreg Banks 489*bfd24160SGreg Banks if (have_oldmask) 490*bfd24160SGreg Banks set_cpus_allowed(current, oldmask); 491*bfd24160SGreg Banks 4921da177e4SLinus Torvalds if (error < 0) 4931da177e4SLinus Torvalds goto out_thread; 4941da177e4SLinus Torvalds svc_sock_update_bufs(serv); 4951da177e4SLinus Torvalds error = 0; 4961da177e4SLinus Torvalds out: 4971da177e4SLinus Torvalds return error; 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds out_thread: 5001da177e4SLinus Torvalds svc_exit_thread(rqstp); 5011da177e4SLinus Torvalds goto out; 5021da177e4SLinus Torvalds } 5031da177e4SLinus Torvalds 5041da177e4SLinus Torvalds /* 5053262c816SGreg Banks * Create a thread in the default pool. Caller must hold BKL. 5063262c816SGreg Banks */ 5073262c816SGreg Banks int 5083262c816SGreg Banks svc_create_thread(svc_thread_fn func, struct svc_serv *serv) 5093262c816SGreg Banks { 5103262c816SGreg Banks return __svc_create_thread(func, serv, &serv->sv_pools[0]); 5113262c816SGreg Banks } 5123262c816SGreg Banks 5133262c816SGreg Banks /* 514a7455442SGreg Banks * Choose a pool in which to create a new thread, for svc_set_num_threads 515a7455442SGreg Banks */ 516a7455442SGreg Banks static inline struct svc_pool * 517a7455442SGreg Banks choose_pool(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) 518a7455442SGreg Banks { 519a7455442SGreg Banks if (pool != NULL) 520a7455442SGreg Banks return pool; 521a7455442SGreg Banks 522a7455442SGreg Banks return &serv->sv_pools[(*state)++ % serv->sv_nrpools]; 523a7455442SGreg Banks } 524a7455442SGreg Banks 525a7455442SGreg Banks /* 526a7455442SGreg Banks * Choose a thread to kill, for svc_set_num_threads 527a7455442SGreg Banks */ 528a7455442SGreg Banks static inline struct task_struct * 529a7455442SGreg Banks choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) 530a7455442SGreg Banks { 531a7455442SGreg Banks unsigned int i; 532a7455442SGreg Banks struct task_struct *task = NULL; 533a7455442SGreg Banks 534a7455442SGreg Banks if (pool != NULL) { 535a7455442SGreg Banks spin_lock_bh(&pool->sp_lock); 536a7455442SGreg Banks } else { 537a7455442SGreg Banks /* choose a pool in round-robin fashion */ 538a7455442SGreg Banks for (i = 0; i < serv->sv_nrpools; i++) { 539a7455442SGreg Banks pool = &serv->sv_pools[--(*state) % serv->sv_nrpools]; 540a7455442SGreg Banks spin_lock_bh(&pool->sp_lock); 541a7455442SGreg Banks if (!list_empty(&pool->sp_all_threads)) 542a7455442SGreg Banks goto found_pool; 543a7455442SGreg Banks spin_unlock_bh(&pool->sp_lock); 544a7455442SGreg Banks } 545a7455442SGreg Banks return NULL; 546a7455442SGreg Banks } 547a7455442SGreg Banks 548a7455442SGreg Banks found_pool: 549a7455442SGreg Banks if (!list_empty(&pool->sp_all_threads)) { 550a7455442SGreg Banks struct svc_rqst *rqstp; 551a7455442SGreg Banks 552a7455442SGreg Banks /* 553a7455442SGreg Banks * Remove from the pool->sp_all_threads list 554a7455442SGreg Banks * so we don't try to kill it again. 555a7455442SGreg Banks */ 556a7455442SGreg Banks rqstp = list_entry(pool->sp_all_threads.next, struct svc_rqst, rq_all); 557a7455442SGreg Banks list_del_init(&rqstp->rq_all); 558a7455442SGreg Banks task = rqstp->rq_task; 559a7455442SGreg Banks } 560a7455442SGreg Banks spin_unlock_bh(&pool->sp_lock); 561a7455442SGreg Banks 562a7455442SGreg Banks return task; 563a7455442SGreg Banks } 564a7455442SGreg Banks 565a7455442SGreg Banks /* 566a7455442SGreg Banks * Create or destroy enough new threads to make the number 567a7455442SGreg Banks * of threads the given number. If `pool' is non-NULL, applies 568a7455442SGreg Banks * only to threads in that pool, otherwise round-robins between 569a7455442SGreg Banks * all pools. Must be called with a svc_get() reference and 570a7455442SGreg Banks * the BKL held. 571a7455442SGreg Banks * 572a7455442SGreg Banks * Destroying threads relies on the service threads filling in 573a7455442SGreg Banks * rqstp->rq_task, which only the nfs ones do. Assumes the serv 574a7455442SGreg Banks * has been created using svc_create_pooled(). 575a7455442SGreg Banks * 576a7455442SGreg Banks * Based on code that used to be in nfsd_svc() but tweaked 577a7455442SGreg Banks * to be pool-aware. 578a7455442SGreg Banks */ 579a7455442SGreg Banks int 580a7455442SGreg Banks svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) 581a7455442SGreg Banks { 582a7455442SGreg Banks struct task_struct *victim; 583a7455442SGreg Banks int error = 0; 584a7455442SGreg Banks unsigned int state = serv->sv_nrthreads-1; 585a7455442SGreg Banks 586a7455442SGreg Banks if (pool == NULL) { 587a7455442SGreg Banks /* The -1 assumes caller has done a svc_get() */ 588a7455442SGreg Banks nrservs -= (serv->sv_nrthreads-1); 589a7455442SGreg Banks } else { 590a7455442SGreg Banks spin_lock_bh(&pool->sp_lock); 591a7455442SGreg Banks nrservs -= pool->sp_nrthreads; 592a7455442SGreg Banks spin_unlock_bh(&pool->sp_lock); 593a7455442SGreg Banks } 594a7455442SGreg Banks 595a7455442SGreg Banks /* create new threads */ 596a7455442SGreg Banks while (nrservs > 0) { 597a7455442SGreg Banks nrservs--; 598a7455442SGreg Banks __module_get(serv->sv_module); 599a7455442SGreg Banks error = __svc_create_thread(serv->sv_function, serv, 600a7455442SGreg Banks choose_pool(serv, pool, &state)); 601a7455442SGreg Banks if (error < 0) { 602a7455442SGreg Banks module_put(serv->sv_module); 603a7455442SGreg Banks break; 604a7455442SGreg Banks } 605a7455442SGreg Banks } 606a7455442SGreg Banks /* destroy old threads */ 607a7455442SGreg Banks while (nrservs < 0 && 608a7455442SGreg Banks (victim = choose_victim(serv, pool, &state)) != NULL) { 609a7455442SGreg Banks send_sig(serv->sv_kill_signal, victim, 1); 610a7455442SGreg Banks nrservs++; 611a7455442SGreg Banks } 612a7455442SGreg Banks 613a7455442SGreg Banks return error; 614a7455442SGreg Banks } 615a7455442SGreg Banks 616a7455442SGreg Banks /* 6173262c816SGreg Banks * Called from a server thread as it's exiting. Caller must hold BKL. 6181da177e4SLinus Torvalds */ 6191da177e4SLinus Torvalds void 6201da177e4SLinus Torvalds svc_exit_thread(struct svc_rqst *rqstp) 6211da177e4SLinus Torvalds { 6221da177e4SLinus Torvalds struct svc_serv *serv = rqstp->rq_server; 6233262c816SGreg Banks struct svc_pool *pool = rqstp->rq_pool; 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds svc_release_buffer(rqstp); 6261da177e4SLinus Torvalds kfree(rqstp->rq_resp); 6271da177e4SLinus Torvalds kfree(rqstp->rq_argp); 6281da177e4SLinus Torvalds kfree(rqstp->rq_auth_data); 6293262c816SGreg Banks 6303262c816SGreg Banks spin_lock_bh(&pool->sp_lock); 6313262c816SGreg Banks pool->sp_nrthreads--; 632a7455442SGreg Banks list_del(&rqstp->rq_all); 6333262c816SGreg Banks spin_unlock_bh(&pool->sp_lock); 6343262c816SGreg Banks 6351da177e4SLinus Torvalds kfree(rqstp); 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds /* Release the server */ 6381da177e4SLinus Torvalds if (serv) 6391da177e4SLinus Torvalds svc_destroy(serv); 6401da177e4SLinus Torvalds } 6411da177e4SLinus Torvalds 6421da177e4SLinus Torvalds /* 6431da177e4SLinus Torvalds * Register an RPC service with the local portmapper. 6441da177e4SLinus Torvalds * To unregister a service, call this routine with 6451da177e4SLinus Torvalds * proto and port == 0. 6461da177e4SLinus Torvalds */ 6471da177e4SLinus Torvalds int 6481da177e4SLinus Torvalds svc_register(struct svc_serv *serv, int proto, unsigned short port) 6491da177e4SLinus Torvalds { 6501da177e4SLinus Torvalds struct svc_program *progp; 6511da177e4SLinus Torvalds unsigned long flags; 6521da177e4SLinus Torvalds int i, error = 0, dummy; 6531da177e4SLinus Torvalds 6541da177e4SLinus Torvalds progp = serv->sv_program; 6551da177e4SLinus Torvalds 6561da177e4SLinus Torvalds dprintk("RPC: svc_register(%s, %s, %d)\n", 6571da177e4SLinus Torvalds progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port); 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds if (!port) 6601da177e4SLinus Torvalds clear_thread_flag(TIF_SIGPENDING); 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds for (i = 0; i < progp->pg_nvers; i++) { 6631da177e4SLinus Torvalds if (progp->pg_vers[i] == NULL) 6641da177e4SLinus Torvalds continue; 6651da177e4SLinus Torvalds error = rpc_register(progp->pg_prog, i, proto, port, &dummy); 6661da177e4SLinus Torvalds if (error < 0) 6671da177e4SLinus Torvalds break; 6681da177e4SLinus Torvalds if (port && !dummy) { 6691da177e4SLinus Torvalds error = -EACCES; 6701da177e4SLinus Torvalds break; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds } 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds if (!port) { 6751da177e4SLinus Torvalds spin_lock_irqsave(¤t->sighand->siglock, flags); 6761da177e4SLinus Torvalds recalc_sigpending(); 6771da177e4SLinus Torvalds spin_unlock_irqrestore(¤t->sighand->siglock, flags); 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds return error; 6811da177e4SLinus Torvalds } 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds /* 6841da177e4SLinus Torvalds * Process the RPC request. 6851da177e4SLinus Torvalds */ 6861da177e4SLinus Torvalds int 6876fb2b47fSNeilBrown svc_process(struct svc_rqst *rqstp) 6881da177e4SLinus Torvalds { 6891da177e4SLinus Torvalds struct svc_program *progp; 6901da177e4SLinus Torvalds struct svc_version *versp = NULL; /* compiler food */ 6911da177e4SLinus Torvalds struct svc_procedure *procp = NULL; 6921da177e4SLinus Torvalds struct kvec * argv = &rqstp->rq_arg.head[0]; 6931da177e4SLinus Torvalds struct kvec * resv = &rqstp->rq_res.head[0]; 6946fb2b47fSNeilBrown struct svc_serv *serv = rqstp->rq_server; 6951da177e4SLinus Torvalds kxdrproc_t xdr; 696d8ed029dSAlexey Dobriyan __be32 *statp; 697d8ed029dSAlexey Dobriyan u32 dir, prog, vers, proc; 698d8ed029dSAlexey Dobriyan __be32 auth_stat, rpc_stat; 6991da177e4SLinus Torvalds int auth_res; 700d8ed029dSAlexey Dobriyan __be32 *accept_statp; 7011da177e4SLinus Torvalds 7021da177e4SLinus Torvalds rpc_stat = rpc_success; 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds if (argv->iov_len < 6*4) 7051da177e4SLinus Torvalds goto err_short_len; 7061da177e4SLinus Torvalds 7071da177e4SLinus Torvalds /* setup response xdr_buf. 7081da177e4SLinus Torvalds * Initially it has just one page 7091da177e4SLinus Torvalds */ 7101da177e4SLinus Torvalds svc_take_page(rqstp); /* must succeed */ 7111da177e4SLinus Torvalds resv->iov_base = page_address(rqstp->rq_respages[0]); 7121da177e4SLinus Torvalds resv->iov_len = 0; 7131da177e4SLinus Torvalds rqstp->rq_res.pages = rqstp->rq_respages+1; 7141da177e4SLinus Torvalds rqstp->rq_res.len = 0; 7151da177e4SLinus Torvalds rqstp->rq_res.page_base = 0; 7161da177e4SLinus Torvalds rqstp->rq_res.page_len = 0; 717334ccfd5STrond Myklebust rqstp->rq_res.buflen = PAGE_SIZE; 7187c9fdcfbSJ. Bruce Fields rqstp->rq_res.tail[0].iov_base = NULL; 7191da177e4SLinus Torvalds rqstp->rq_res.tail[0].iov_len = 0; 7205c04c46aSJ. Bruce Fields /* Will be turned off only in gss privacy case: */ 7215c04c46aSJ. Bruce Fields rqstp->rq_sendfile_ok = 1; 7221da177e4SLinus Torvalds /* tcp needs a space for the record length... */ 7231da177e4SLinus Torvalds if (rqstp->rq_prot == IPPROTO_TCP) 72476994313SAlexey Dobriyan svc_putnl(resv, 0); 7251da177e4SLinus Torvalds 7261da177e4SLinus Torvalds rqstp->rq_xid = svc_getu32(argv); 7271da177e4SLinus Torvalds svc_putu32(resv, rqstp->rq_xid); 7281da177e4SLinus Torvalds 72976994313SAlexey Dobriyan dir = svc_getnl(argv); 73076994313SAlexey Dobriyan vers = svc_getnl(argv); 7311da177e4SLinus Torvalds 7321da177e4SLinus Torvalds /* First words of reply: */ 73376994313SAlexey Dobriyan svc_putnl(resv, 1); /* REPLY */ 7341da177e4SLinus Torvalds 7351da177e4SLinus Torvalds if (dir != 0) /* direction != CALL */ 7361da177e4SLinus Torvalds goto err_bad_dir; 7371da177e4SLinus Torvalds if (vers != 2) /* RPC version number */ 7381da177e4SLinus Torvalds goto err_bad_rpc; 7391da177e4SLinus Torvalds 7401da177e4SLinus Torvalds /* Save position in case we later decide to reject: */ 7411da177e4SLinus Torvalds accept_statp = resv->iov_base + resv->iov_len; 7421da177e4SLinus Torvalds 74376994313SAlexey Dobriyan svc_putnl(resv, 0); /* ACCEPT */ 7441da177e4SLinus Torvalds 74576994313SAlexey Dobriyan rqstp->rq_prog = prog = svc_getnl(argv); /* program number */ 74676994313SAlexey Dobriyan rqstp->rq_vers = vers = svc_getnl(argv); /* version number */ 74776994313SAlexey Dobriyan rqstp->rq_proc = proc = svc_getnl(argv); /* procedure number */ 7481da177e4SLinus Torvalds 7491da177e4SLinus Torvalds progp = serv->sv_program; 75080d188a6SNeilBrown 75180d188a6SNeilBrown for (progp = serv->sv_program; progp; progp = progp->pg_next) 75280d188a6SNeilBrown if (prog == progp->pg_prog) 75380d188a6SNeilBrown break; 75480d188a6SNeilBrown 7551da177e4SLinus Torvalds /* 7561da177e4SLinus Torvalds * Decode auth data, and add verifier to reply buffer. 7571da177e4SLinus Torvalds * We do this before anything else in order to get a decent 7581da177e4SLinus Torvalds * auth verifier. 7591da177e4SLinus Torvalds */ 7601da177e4SLinus Torvalds auth_res = svc_authenticate(rqstp, &auth_stat); 7611da177e4SLinus Torvalds /* Also give the program a chance to reject this call: */ 76280d188a6SNeilBrown if (auth_res == SVC_OK && progp) { 7631da177e4SLinus Torvalds auth_stat = rpc_autherr_badcred; 7641da177e4SLinus Torvalds auth_res = progp->pg_authenticate(rqstp); 7651da177e4SLinus Torvalds } 7661da177e4SLinus Torvalds switch (auth_res) { 7671da177e4SLinus Torvalds case SVC_OK: 7681da177e4SLinus Torvalds break; 7691da177e4SLinus Torvalds case SVC_GARBAGE: 7701da177e4SLinus Torvalds rpc_stat = rpc_garbage_args; 7711da177e4SLinus Torvalds goto err_bad; 7721da177e4SLinus Torvalds case SVC_SYSERR: 7731da177e4SLinus Torvalds rpc_stat = rpc_system_err; 7741da177e4SLinus Torvalds goto err_bad; 7751da177e4SLinus Torvalds case SVC_DENIED: 7761da177e4SLinus Torvalds goto err_bad_auth; 7771da177e4SLinus Torvalds case SVC_DROP: 7781da177e4SLinus Torvalds goto dropit; 7791da177e4SLinus Torvalds case SVC_COMPLETE: 7801da177e4SLinus Torvalds goto sendit; 7811da177e4SLinus Torvalds } 7821da177e4SLinus Torvalds 7839ba02638SAndreas Gruenbacher if (progp == NULL) 7841da177e4SLinus Torvalds goto err_bad_prog; 7851da177e4SLinus Torvalds 7861da177e4SLinus Torvalds if (vers >= progp->pg_nvers || 7871da177e4SLinus Torvalds !(versp = progp->pg_vers[vers])) 7881da177e4SLinus Torvalds goto err_bad_vers; 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds procp = versp->vs_proc + proc; 7911da177e4SLinus Torvalds if (proc >= versp->vs_nproc || !procp->pc_func) 7921da177e4SLinus Torvalds goto err_bad_proc; 7931da177e4SLinus Torvalds rqstp->rq_server = serv; 7941da177e4SLinus Torvalds rqstp->rq_procinfo = procp; 7951da177e4SLinus Torvalds 7961da177e4SLinus Torvalds /* Syntactic check complete */ 7971da177e4SLinus Torvalds serv->sv_stats->rpccnt++; 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds /* Build the reply header. */ 8001da177e4SLinus Torvalds statp = resv->iov_base +resv->iov_len; 80176994313SAlexey Dobriyan svc_putnl(resv, RPC_SUCCESS); 8021da177e4SLinus Torvalds 8031da177e4SLinus Torvalds /* Bump per-procedure stats counter */ 8041da177e4SLinus Torvalds procp->pc_count++; 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds /* Initialize storage for argp and resp */ 8071da177e4SLinus Torvalds memset(rqstp->rq_argp, 0, procp->pc_argsize); 8081da177e4SLinus Torvalds memset(rqstp->rq_resp, 0, procp->pc_ressize); 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds /* un-reserve some of the out-queue now that we have a 8111da177e4SLinus Torvalds * better idea of reply size 8121da177e4SLinus Torvalds */ 8131da177e4SLinus Torvalds if (procp->pc_xdrressize) 8141da177e4SLinus Torvalds svc_reserve(rqstp, procp->pc_xdrressize<<2); 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds /* Call the function that processes the request. */ 8171da177e4SLinus Torvalds if (!versp->vs_dispatch) { 8181da177e4SLinus Torvalds /* Decode arguments */ 8191da177e4SLinus Torvalds xdr = procp->pc_decode; 8201da177e4SLinus Torvalds if (xdr && !xdr(rqstp, argv->iov_base, rqstp->rq_argp)) 8211da177e4SLinus Torvalds goto err_garbage; 8221da177e4SLinus Torvalds 8231da177e4SLinus Torvalds *statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds /* Encode reply */ 8261da177e4SLinus Torvalds if (*statp == rpc_success && (xdr = procp->pc_encode) 8271da177e4SLinus Torvalds && !xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) { 8281da177e4SLinus Torvalds dprintk("svc: failed to encode reply\n"); 8291da177e4SLinus Torvalds /* serv->sv_stats->rpcsystemerr++; */ 8301da177e4SLinus Torvalds *statp = rpc_system_err; 8311da177e4SLinus Torvalds } 8321da177e4SLinus Torvalds } else { 8331da177e4SLinus Torvalds dprintk("svc: calling dispatcher\n"); 8341da177e4SLinus Torvalds if (!versp->vs_dispatch(rqstp, statp)) { 8351da177e4SLinus Torvalds /* Release reply info */ 8361da177e4SLinus Torvalds if (procp->pc_release) 8371da177e4SLinus Torvalds procp->pc_release(rqstp, NULL, rqstp->rq_resp); 8381da177e4SLinus Torvalds goto dropit; 8391da177e4SLinus Torvalds } 8401da177e4SLinus Torvalds } 8411da177e4SLinus Torvalds 8421da177e4SLinus Torvalds /* Check RPC status result */ 8431da177e4SLinus Torvalds if (*statp != rpc_success) 8441da177e4SLinus Torvalds resv->iov_len = ((void*)statp) - resv->iov_base + 4; 8451da177e4SLinus Torvalds 8461da177e4SLinus Torvalds /* Release reply info */ 8471da177e4SLinus Torvalds if (procp->pc_release) 8481da177e4SLinus Torvalds procp->pc_release(rqstp, NULL, rqstp->rq_resp); 8491da177e4SLinus Torvalds 8501da177e4SLinus Torvalds if (procp->pc_encode == NULL) 8511da177e4SLinus Torvalds goto dropit; 8521da177e4SLinus Torvalds 8531da177e4SLinus Torvalds sendit: 8541da177e4SLinus Torvalds if (svc_authorise(rqstp)) 8551da177e4SLinus Torvalds goto dropit; 8561da177e4SLinus Torvalds return svc_send(rqstp); 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds dropit: 8591da177e4SLinus Torvalds svc_authorise(rqstp); /* doesn't hurt to call this twice */ 8601da177e4SLinus Torvalds dprintk("svc: svc_process dropit\n"); 8611da177e4SLinus Torvalds svc_drop(rqstp); 8621da177e4SLinus Torvalds return 0; 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds err_short_len: 8651da177e4SLinus Torvalds #ifdef RPC_PARANOIA 8661da177e4SLinus Torvalds printk("svc: short len %Zd, dropping request\n", argv->iov_len); 8671da177e4SLinus Torvalds #endif 8681da177e4SLinus Torvalds goto dropit; /* drop request */ 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds err_bad_dir: 8711da177e4SLinus Torvalds #ifdef RPC_PARANOIA 8721da177e4SLinus Torvalds printk("svc: bad direction %d, dropping request\n", dir); 8731da177e4SLinus Torvalds #endif 8741da177e4SLinus Torvalds serv->sv_stats->rpcbadfmt++; 8751da177e4SLinus Torvalds goto dropit; /* drop request */ 8761da177e4SLinus Torvalds 8771da177e4SLinus Torvalds err_bad_rpc: 8781da177e4SLinus Torvalds serv->sv_stats->rpcbadfmt++; 87976994313SAlexey Dobriyan svc_putnl(resv, 1); /* REJECT */ 88076994313SAlexey Dobriyan svc_putnl(resv, 0); /* RPC_MISMATCH */ 88176994313SAlexey Dobriyan svc_putnl(resv, 2); /* Only RPCv2 supported */ 88276994313SAlexey Dobriyan svc_putnl(resv, 2); 8831da177e4SLinus Torvalds goto sendit; 8841da177e4SLinus Torvalds 8851da177e4SLinus Torvalds err_bad_auth: 8861da177e4SLinus Torvalds dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat)); 8871da177e4SLinus Torvalds serv->sv_stats->rpcbadauth++; 8881da177e4SLinus Torvalds /* Restore write pointer to location of accept status: */ 8891da177e4SLinus Torvalds xdr_ressize_check(rqstp, accept_statp); 89076994313SAlexey Dobriyan svc_putnl(resv, 1); /* REJECT */ 89176994313SAlexey Dobriyan svc_putnl(resv, 1); /* AUTH_ERROR */ 89276994313SAlexey Dobriyan svc_putnl(resv, ntohl(auth_stat)); /* status */ 8931da177e4SLinus Torvalds goto sendit; 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds err_bad_prog: 8969ba02638SAndreas Gruenbacher dprintk("svc: unknown program %d\n", prog); 8971da177e4SLinus Torvalds serv->sv_stats->rpcbadfmt++; 89876994313SAlexey Dobriyan svc_putnl(resv, RPC_PROG_UNAVAIL); 8991da177e4SLinus Torvalds goto sendit; 9001da177e4SLinus Torvalds 9011da177e4SLinus Torvalds err_bad_vers: 9021da177e4SLinus Torvalds #ifdef RPC_PARANOIA 9031da177e4SLinus Torvalds printk("svc: unknown version (%d)\n", vers); 9041da177e4SLinus Torvalds #endif 9051da177e4SLinus Torvalds serv->sv_stats->rpcbadfmt++; 90676994313SAlexey Dobriyan svc_putnl(resv, RPC_PROG_MISMATCH); 90776994313SAlexey Dobriyan svc_putnl(resv, progp->pg_lovers); 90876994313SAlexey Dobriyan svc_putnl(resv, progp->pg_hivers); 9091da177e4SLinus Torvalds goto sendit; 9101da177e4SLinus Torvalds 9111da177e4SLinus Torvalds err_bad_proc: 9121da177e4SLinus Torvalds #ifdef RPC_PARANOIA 9131da177e4SLinus Torvalds printk("svc: unknown procedure (%d)\n", proc); 9141da177e4SLinus Torvalds #endif 9151da177e4SLinus Torvalds serv->sv_stats->rpcbadfmt++; 91676994313SAlexey Dobriyan svc_putnl(resv, RPC_PROC_UNAVAIL); 9171da177e4SLinus Torvalds goto sendit; 9181da177e4SLinus Torvalds 9191da177e4SLinus Torvalds err_garbage: 9201da177e4SLinus Torvalds #ifdef RPC_PARANOIA 9211da177e4SLinus Torvalds printk("svc: failed to decode args\n"); 9221da177e4SLinus Torvalds #endif 9231da177e4SLinus Torvalds rpc_stat = rpc_garbage_args; 9241da177e4SLinus Torvalds err_bad: 9251da177e4SLinus Torvalds serv->sv_stats->rpcbadfmt++; 92676994313SAlexey Dobriyan svc_putnl(resv, ntohl(rpc_stat)); 9271da177e4SLinus Torvalds goto sendit; 9281da177e4SLinus Torvalds } 929