diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 13:16:19 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 16:07:01 -0400 |
commit | d05fdb0cec75415b2d9eb95748386e67414e49c3 (patch) | |
tree | 2d324e17a705547c7dbc0c20f8b20293d85abd2e /net/sunrpc/sched.c | |
parent | 4e93d3e8859c834ee18dfd33051d24df8669d0c0 (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.c | 53 |
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 | */ | ||
561 | static 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 | */ |
560 | static int __rpc_execute(struct rpc_task *task) | 584 | static 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 | ||