diff options
| -rw-r--r-- | include/linux/sunrpc/svc.h | 4 | ||||
| -rw-r--r-- | include/trace/events/sunrpc.h | 3 | ||||
| -rw-r--r-- | net/sunrpc/svc.c | 7 | ||||
| -rw-r--r-- | net/sunrpc/svc_xprt.c | 221 |
4 files changed, 132 insertions, 103 deletions
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 513957eba0a5..6f22cfeef5e3 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h | |||
| @@ -45,7 +45,6 @@ struct svc_pool_stats { | |||
| 45 | struct svc_pool { | 45 | struct svc_pool { |
| 46 | unsigned int sp_id; /* pool id; also node id on NUMA */ | 46 | unsigned int sp_id; /* pool id; also node id on NUMA */ |
| 47 | spinlock_t sp_lock; /* protects all fields */ | 47 | spinlock_t sp_lock; /* protects all fields */ |
| 48 | struct list_head sp_threads; /* idle server threads */ | ||
| 49 | struct list_head sp_sockets; /* pending sockets */ | 48 | struct list_head sp_sockets; /* pending sockets */ |
| 50 | unsigned int sp_nrthreads; /* # of threads in pool */ | 49 | unsigned int sp_nrthreads; /* # of threads in pool */ |
| 51 | struct list_head sp_all_threads; /* all server threads */ | 50 | struct list_head sp_all_threads; /* all server threads */ |
| @@ -221,7 +220,6 @@ static inline void svc_putu32(struct kvec *iov, __be32 val) | |||
| 221 | * processed. | 220 | * processed. |
| 222 | */ | 221 | */ |
| 223 | struct svc_rqst { | 222 | struct svc_rqst { |
| 224 | struct list_head rq_list; /* idle list */ | ||
| 225 | struct list_head rq_all; /* all threads list */ | 223 | struct list_head rq_all; /* all threads list */ |
| 226 | struct rcu_head rq_rcu_head; /* for RCU deferred kfree */ | 224 | struct rcu_head rq_rcu_head; /* for RCU deferred kfree */ |
| 227 | struct svc_xprt * rq_xprt; /* transport ptr */ | 225 | struct svc_xprt * rq_xprt; /* transport ptr */ |
| @@ -264,6 +262,7 @@ struct svc_rqst { | |||
| 264 | * to prevent encrypting page | 262 | * to prevent encrypting page |
| 265 | * cache pages */ | 263 | * cache pages */ |
| 266 | #define RQ_VICTIM (5) /* about to be shut down */ | 264 | #define RQ_VICTIM (5) /* about to be shut down */ |
| 265 | #define RQ_BUSY (6) /* request is busy */ | ||
| 267 | unsigned long rq_flags; /* flags field */ | 266 | unsigned long rq_flags; /* flags field */ |
| 268 | 267 | ||
| 269 | void * rq_argp; /* decoded arguments */ | 268 | void * rq_argp; /* decoded arguments */ |
| @@ -285,6 +284,7 @@ struct svc_rqst { | |||
| 285 | struct auth_domain * rq_gssclient; /* "gss/"-style peer info */ | 284 | struct auth_domain * rq_gssclient; /* "gss/"-style peer info */ |
| 286 | struct svc_cacherep * rq_cacherep; /* cache info */ | 285 | struct svc_cacherep * rq_cacherep; /* cache info */ |
| 287 | struct task_struct *rq_task; /* service thread */ | 286 | struct task_struct *rq_task; /* service thread */ |
| 287 | spinlock_t rq_lock; /* per-request lock */ | ||
| 288 | }; | 288 | }; |
| 289 | 289 | ||
| 290 | #define SVC_NET(svc_rqst) (svc_rqst->rq_xprt->xpt_net) | 290 | #define SVC_NET(svc_rqst) (svc_rqst->rq_xprt->xpt_net) |
diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 08a5fed50f34..ee4438a63a48 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h | |||
| @@ -419,7 +419,8 @@ TRACE_EVENT(xs_tcp_data_recv, | |||
| 419 | { (1UL << RQ_USEDEFERRAL), "RQ_USEDEFERRAL"}, \ | 419 | { (1UL << RQ_USEDEFERRAL), "RQ_USEDEFERRAL"}, \ |
| 420 | { (1UL << RQ_DROPME), "RQ_DROPME"}, \ | 420 | { (1UL << RQ_DROPME), "RQ_DROPME"}, \ |
| 421 | { (1UL << RQ_SPLICE_OK), "RQ_SPLICE_OK"}, \ | 421 | { (1UL << RQ_SPLICE_OK), "RQ_SPLICE_OK"}, \ |
| 422 | { (1UL << RQ_VICTIM), "RQ_VICTIM"}) | 422 | { (1UL << RQ_VICTIM), "RQ_VICTIM"}, \ |
| 423 | { (1UL << RQ_BUSY), "RQ_BUSY"}) | ||
| 423 | 424 | ||
| 424 | TRACE_EVENT(svc_recv, | 425 | TRACE_EVENT(svc_recv, |
| 425 | TP_PROTO(struct svc_rqst *rqst, int status), | 426 | TP_PROTO(struct svc_rqst *rqst, int status), |
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index b90d1bca4349..91eaef1844c8 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c | |||
| @@ -476,7 +476,6 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, | |||
| 476 | i, serv->sv_name); | 476 | i, serv->sv_name); |
| 477 | 477 | ||
| 478 | pool->sp_id = i; | 478 | pool->sp_id = i; |
| 479 | INIT_LIST_HEAD(&pool->sp_threads); | ||
| 480 | INIT_LIST_HEAD(&pool->sp_sockets); | 479 | INIT_LIST_HEAD(&pool->sp_sockets); |
| 481 | INIT_LIST_HEAD(&pool->sp_all_threads); | 480 | INIT_LIST_HEAD(&pool->sp_all_threads); |
| 482 | spin_lock_init(&pool->sp_lock); | 481 | spin_lock_init(&pool->sp_lock); |
| @@ -614,12 +613,14 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node) | |||
| 614 | goto out_enomem; | 613 | goto out_enomem; |
| 615 | 614 | ||
| 616 | serv->sv_nrthreads++; | 615 | serv->sv_nrthreads++; |
| 616 | __set_bit(RQ_BUSY, &rqstp->rq_flags); | ||
| 617 | spin_lock_init(&rqstp->rq_lock); | ||
| 618 | rqstp->rq_server = serv; | ||
| 619 | rqstp->rq_pool = pool; | ||
| 617 | spin_lock_bh(&pool->sp_lock); | 620 | spin_lock_bh(&pool->sp_lock); |
| 618 | pool->sp_nrthreads++; | 621 | pool->sp_nrthreads++; |
| 619 | list_add_rcu(&rqstp->rq_all, &pool->sp_all_threads); | 622 | list_add_rcu(&rqstp->rq_all, &pool->sp_all_threads); |
| 620 | spin_unlock_bh(&pool->sp_lock); | 623 | spin_unlock_bh(&pool->sp_lock); |
| 621 | rqstp->rq_server = serv; | ||
| 622 | rqstp->rq_pool = pool; | ||
| 623 | 624 | ||
| 624 | rqstp->rq_argp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node); | 625 | rqstp->rq_argp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node); |
| 625 | if (!rqstp->rq_argp) | 626 | if (!rqstp->rq_argp) |
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 579ff2249562..ed90d955f733 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c | |||
| @@ -310,25 +310,6 @@ char *svc_print_addr(struct svc_rqst *rqstp, char *buf, size_t len) | |||
| 310 | } | 310 | } |
| 311 | EXPORT_SYMBOL_GPL(svc_print_addr); | 311 | EXPORT_SYMBOL_GPL(svc_print_addr); |
| 312 | 312 | ||
| 313 | /* | ||
| 314 | * Queue up an idle server thread. Must have pool->sp_lock held. | ||
| 315 | * Note: this is really a stack rather than a queue, so that we only | ||
| 316 | * use as many different threads as we need, and the rest don't pollute | ||
| 317 | * the cache. | ||
| 318 | */ | ||
| 319 | static void svc_thread_enqueue(struct svc_pool *pool, struct svc_rqst *rqstp) | ||
| 320 | { | ||
| 321 | list_add(&rqstp->rq_list, &pool->sp_threads); | ||
| 322 | } | ||
| 323 | |||
| 324 | /* | ||
| 325 | * Dequeue an nfsd thread. Must have pool->sp_lock held. | ||
| 326 | */ | ||
| 327 | static void svc_thread_dequeue(struct svc_pool *pool, struct svc_rqst *rqstp) | ||
| 328 | { | ||
| 329 | list_del(&rqstp->rq_list); | ||
| 330 | } | ||
| 331 | |||
| 332 | static bool svc_xprt_has_something_to_do(struct svc_xprt *xprt) | 313 | static bool svc_xprt_has_something_to_do(struct svc_xprt *xprt) |
| 333 | { | 314 | { |
| 334 | if (xprt->xpt_flags & ((1<<XPT_CONN)|(1<<XPT_CLOSE))) | 315 | if (xprt->xpt_flags & ((1<<XPT_CONN)|(1<<XPT_CLOSE))) |
| @@ -343,6 +324,7 @@ static void svc_xprt_do_enqueue(struct svc_xprt *xprt) | |||
| 343 | struct svc_pool *pool; | 324 | struct svc_pool *pool; |
| 344 | struct svc_rqst *rqstp; | 325 | struct svc_rqst *rqstp; |
| 345 | int cpu; | 326 | int cpu; |
| 327 | bool queued = false; | ||
| 346 | 328 | ||
| 347 | if (!svc_xprt_has_something_to_do(xprt)) | 329 | if (!svc_xprt_has_something_to_do(xprt)) |
| 348 | return; | 330 | return; |
| @@ -360,37 +342,60 @@ static void svc_xprt_do_enqueue(struct svc_xprt *xprt) | |||
| 360 | 342 | ||
| 361 | cpu = get_cpu(); | 343 | cpu = get_cpu(); |
| 362 | pool = svc_pool_for_cpu(xprt->xpt_server, cpu); | 344 | pool = svc_pool_for_cpu(xprt->xpt_server, cpu); |
| 363 | spin_lock_bh(&pool->sp_lock); | ||
| 364 | 345 | ||
| 365 | atomic_long_inc(&pool->sp_stats.packets); | 346 | atomic_long_inc(&pool->sp_stats.packets); |
| 366 | 347 | ||
| 367 | if (!list_empty(&pool->sp_threads)) { | 348 | redo_search: |
| 368 | rqstp = list_entry(pool->sp_threads.next, | 349 | /* find a thread for this xprt */ |
| 369 | struct svc_rqst, | 350 | rcu_read_lock(); |
| 370 | rq_list); | 351 | list_for_each_entry_rcu(rqstp, &pool->sp_all_threads, rq_all) { |
| 371 | dprintk("svc: transport %p served by daemon %p\n", | 352 | /* Do a lockless check first */ |
| 372 | xprt, rqstp); | 353 | if (test_bit(RQ_BUSY, &rqstp->rq_flags)) |
| 373 | svc_thread_dequeue(pool, rqstp); | 354 | continue; |
| 374 | if (rqstp->rq_xprt) | 355 | |
| 375 | printk(KERN_ERR | 356 | /* |
| 376 | "svc_xprt_enqueue: server %p, rq_xprt=%p!\n", | 357 | * Once the xprt has been queued, it can only be dequeued by |
| 377 | rqstp, rqstp->rq_xprt); | 358 | * the task that intends to service it. All we can do at that |
| 378 | /* Note the order of the following 3 lines: | 359 | * point is to try to wake this thread back up so that it can |
| 379 | * We want to assign xprt to rqstp->rq_xprt only _after_ | 360 | * do so. |
| 380 | * we've woken up the process, so that we don't race with | ||
| 381 | * the lockless check in svc_get_next_xprt(). | ||
| 382 | */ | 361 | */ |
| 383 | svc_xprt_get(xprt); | 362 | if (!queued) { |
| 384 | wake_up_process(rqstp->rq_task); | 363 | spin_lock_bh(&rqstp->rq_lock); |
| 385 | rqstp->rq_xprt = xprt; | 364 | if (test_and_set_bit(RQ_BUSY, &rqstp->rq_flags)) { |
| 365 | /* already busy, move on... */ | ||
| 366 | spin_unlock_bh(&rqstp->rq_lock); | ||
| 367 | continue; | ||
| 368 | } | ||
| 369 | |||
| 370 | /* this one will do */ | ||
| 371 | rqstp->rq_xprt = xprt; | ||
| 372 | svc_xprt_get(xprt); | ||
| 373 | spin_unlock_bh(&rqstp->rq_lock); | ||
| 374 | } | ||
| 375 | rcu_read_unlock(); | ||
| 376 | |||
| 386 | atomic_long_inc(&pool->sp_stats.threads_woken); | 377 | atomic_long_inc(&pool->sp_stats.threads_woken); |
| 387 | } else { | 378 | wake_up_process(rqstp->rq_task); |
| 379 | put_cpu(); | ||
| 380 | return; | ||
