aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc/sched.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2005-06-22 13:16:19 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-06-22 16:07:01 -0400
commitd05fdb0cec75415b2d9eb95748386e67414e49c3 (patch)
tree2d324e17a705547c7dbc0c20f8b20293d85abd2e /net/sunrpc/sched.c
parent4e93d3e8859c834ee18dfd33051d24df8669d0c0 (diff)
[PATCH] RPC: Fix a race with rpc_restart_call()
If the task->tk_exit() wants to restart the RPC call after delaying then the current RPC code will clobber the timer by calling rpc_delete_timer() immediately after re-entering the loop in __rpc_execute(). Problem noticed by Oleg Nesterov <oleg@tv-sign.ru> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc/sched.c')
-rw-r--r--net/sunrpc/sched.c53
1 files changed, 30 insertions, 23 deletions
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index c06614d0e31d..cc298fa4b81d 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -555,6 +555,30 @@ __rpc_atrun(struct rpc_task *task)
555} 555}
556 556
557/* 557/*
558 * Helper that calls task->tk_exit if it exists and then returns
559 * true if we should exit __rpc_execute.
560 */
561static inline int __rpc_do_exit(struct rpc_task *task)
562{
563 if (task->tk_exit != NULL) {
564 lock_kernel();
565 task->tk_exit(task);
566 unlock_kernel();
567 /* If tk_action is non-null, we should restart the call */
568 if (task->tk_action != NULL) {
569 if (!RPC_ASSASSINATED(task)) {
570 /* Release RPC slot and buffer memory */
571 xprt_release(task);
572 rpc_free(task);
573 return 0;
574 }
575 printk(KERN_ERR "RPC: dead task tried to walk away.\n");
576 }
577 }
578 return 1;
579}
580
581/*
558 * This is the RPC `scheduler' (or rather, the finite state machine). 582 * This is the RPC `scheduler' (or rather, the finite state machine).
559 */ 583 */
560static int __rpc_execute(struct rpc_task *task) 584static int __rpc_execute(struct rpc_task *task)
@@ -566,8 +590,7 @@ static int __rpc_execute(struct rpc_task *task)
566 590
567 BUG_ON(RPC_IS_QUEUED(task)); 591 BUG_ON(RPC_IS_QUEUED(task));
568 592
569 restarted: 593 for (;;) {
570 while (1) {
571 /* 594 /*
572 * Garbage collection of pending timers... 595 * Garbage collection of pending timers...
573 */ 596 */
@@ -600,11 +623,12 @@ static int __rpc_execute(struct rpc_task *task)
600 * by someone else. 623 * by someone else.
601 */ 624 */
602 if (!RPC_IS_QUEUED(task)) { 625 if (!RPC_IS_QUEUED(task)) {
603 if (!task->tk_action) 626 if (task->tk_action != NULL) {
627 lock_kernel();
628 task->tk_action(task);
629 unlock_kernel();
630 } else if (__rpc_do_exit(task))
604 break; 631 break;
605 lock_kernel();
606 task->tk_action(task);
607 unlock_kernel();
608 } 632 }
609 633
610 /* 634 /*
@@ -645,23 +669,6 @@ static int __rpc_execute(struct rpc_task *task)
645 dprintk("RPC: %4d sync task resuming\n", task->tk_pid); 669 dprintk("RPC: %4d sync task resuming\n", task->tk_pid);
646 } 670 }
647 671
648 if (task->tk_exit) {
649 lock_kernel();
650 task->tk_exit(task);
651 unlock_kernel();
652 /* If tk_action is non-null, the user wants us to restart */
653 if (task->tk_action) {
654 if (!RPC_ASSASSINATED(task)) {
655 /* Release RPC slot and buffer memory */
656 if (task->tk_rqstp)
657 xprt_release(task);
658 rpc_free(task);
659 goto restarted;
660 }
661 printk(KERN_ERR "RPC: dead task tries to walk away.\n");
662 }
663 }
664
665 dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status); 672 dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status);
666 status = task->tk_status; 673 status = task->tk_status;
667 674