diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-11-11 22:18:03 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-12-06 10:46:25 -0500 |
commit | e6b3c4db6fbcd0d33720696f37790d6b8be12313 (patch) | |
tree | 24ad4a93b00ba7236b9a2d896fd6cb59a1dc2334 /net | |
parent | cc4dc59e5580d6c0de1685a25b74d32175f43434 (diff) |
Fix a second potential rpc_wakeup race...
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/clnt.c | 19 | ||||
-rw-r--r-- | net/sunrpc/pmap_clnt.c | 2 | ||||
-rw-r--r-- | net/sunrpc/sched.c | 80 | ||||
-rw-r--r-- | net/sunrpc/sunrpc_syms.c | 1 |
4 files changed, 58 insertions, 44 deletions
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index dfeea4fea95a..a323abc7ea85 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -466,10 +466,9 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) | |||
466 | 466 | ||
467 | BUG_ON(flags & RPC_TASK_ASYNC); | 467 | BUG_ON(flags & RPC_TASK_ASYNC); |
468 | 468 | ||
469 | status = -ENOMEM; | ||
470 | task = rpc_new_task(clnt, flags, &rpc_default_ops, NULL); | 469 | task = rpc_new_task(clnt, flags, &rpc_default_ops, NULL); |
471 | if (task == NULL) | 470 | if (task == NULL) |
472 | goto out; | 471 | return -ENOMEM; |
473 | 472 | ||
474 | /* Mask signals on RPC calls _and_ GSS_AUTH upcalls */ | 473 | /* Mask signals on RPC calls _and_ GSS_AUTH upcalls */ |
475 | rpc_task_sigmask(task, &oldset); | 474 | rpc_task_sigmask(task, &oldset); |
@@ -478,15 +477,17 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) | |||
478 | 477 | ||
479 | /* Set up the call info struct and execute the task */ | 478 | /* Set up the call info struct and execute the task */ |
480 | status = task->tk_status; | 479 | status = task->tk_status; |
481 | if (status == 0) { | 480 | if (status != 0) { |
482 | atomic_inc(&task->tk_count); | 481 | rpc_release_task(task); |
483 | status = rpc_execute(task); | 482 | goto out; |
484 | if (status == 0) | ||
485 | status = task->tk_status; | ||
486 | } | 483 | } |
487 | rpc_restore_sigmask(&oldset); | 484 | atomic_inc(&task->tk_count); |
488 | rpc_release_task(task); | 485 | status = rpc_execute(task); |
486 | if (status == 0) | ||
487 | status = task->tk_status; | ||
488 | rpc_put_task(task); | ||
489 | out: | 489 | out: |
490 | rpc_restore_sigmask(&oldset); | ||
490 | return status; | 491 | return status; |
491 | } | 492 | } |
492 | 493 | ||
diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index e52afab413de..8d2e10fc3df9 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c | |||
@@ -134,7 +134,7 @@ void rpc_getport(struct rpc_task *task) | |||
134 | child = rpc_run_task(pmap_clnt, RPC_TASK_ASYNC, &pmap_getport_ops, map); | 134 | child = rpc_run_task(pmap_clnt, RPC_TASK_ASYNC, &pmap_getport_ops, map); |
135 | if (IS_ERR(child)) | 135 | if (IS_ERR(child)) |
136 | goto bailout; | 136 | goto bailout; |
137 | rpc_release_task(child); | 137 | rpc_put_task(child); |
138 | 138 | ||
139 | task->tk_xprt->stat.bind_count++; | 139 | task->tk_xprt->stat.bind_count++; |
140 | return; | 140 | return; |
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index b57d4062d429..66d01365f3a5 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c | |||
@@ -266,12 +266,28 @@ static int rpc_wait_bit_interruptible(void *word) | |||
266 | return 0; | 266 | return 0; |
267 | } | 267 | } |
268 | 268 | ||
269 | static void rpc_set_active(struct rpc_task *task) | ||
270 | { | ||
271 | if (test_and_set_bit(RPC_TASK_ACTIVE, &task->tk_runstate) != 0) | ||
272 | return; | ||
273 | spin_lock(&rpc_sched_lock); | ||
274 | #ifdef RPC_DEBUG | ||
275 | task->tk_magic = RPC_TASK_MAGIC_ID; | ||
276 | task->tk_pid = rpc_task_id++; | ||
277 | #endif | ||
278 | /* Add to global list of all tasks */ | ||
279 | list_add_tail(&task->tk_task, &all_tasks); | ||
280 | spin_unlock(&rpc_sched_lock); | ||
281 | } | ||
282 | |||
269 | /* | 283 | /* |
270 | * Mark an RPC call as having completed by clearing the 'active' bit | 284 | * Mark an RPC call as having completed by clearing the 'active' bit |
271 | */ | 285 | */ |
272 | static inline void rpc_mark_complete_task(struct rpc_task *task) | 286 | static void rpc_mark_complete_task(struct rpc_task *task) |
273 | { | 287 | { |
274 | rpc_clear_active(task); | 288 | smp_mb__before_clear_bit(); |
289 | clear_bit(RPC_TASK_ACTIVE, &task->tk_runstate); | ||
290 | smp_mb__after_clear_bit(); | ||
275 | wake_up_bit(&task->tk_runstate, RPC_TASK_ACTIVE); | 291 | wake_up_bit(&task->tk_runstate, RPC_TASK_ACTIVE); |
276 | } | 292 | } |
277 | 293 | ||
@@ -335,9 +351,6 @@ static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, | |||
335 | return; | 351 | return; |
336 | } | 352 | } |
337 | 353 | ||
338 | /* Mark the task as being activated if so needed */ | ||
339 | rpc_set_active(task); | ||
340 | |||
341 | __rpc_add_wait_queue(q, task); | 354 | __rpc_add_wait_queue(q, task); |
342 | 355 | ||
343 | BUG_ON(task->tk_callback != NULL); | 356 | BUG_ON(task->tk_callback != NULL); |
@@ -348,6 +361,9 @@ static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, | |||
348 | void rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, | 361 | void rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, |
349 | rpc_action action, rpc_action timer) | 362 | rpc_action action, rpc_action timer) |
350 | { | 363 | { |
364 | /* Mark the task as being activated if so needed */ | ||
365 | rpc_set_active(task); | ||
366 | |||
351 | /* | 367 | /* |
352 | * Protect the queue operations. | 368 | * Protect the queue operations. |
353 | */ | 369 | */ |
@@ -673,8 +689,6 @@ static int __rpc_execute(struct rpc_task *task) | |||
673 | } | 689 | } |
674 | 690 | ||
675 | dprintk("RPC: %4d, return %d, status %d\n", task->tk_pid, status, task->tk_status); | 691 | dprintk("RPC: %4d, return %d, status %d\n", task->tk_pid, status, task->tk_status); |
676 | /* Wake up anyone who is waiting for task completion */ | ||
677 | rpc_mark_complete_task(task); | ||
678 | /* Release all resources associated with the task */ | 692 | /* Release all resources associated with the task */ |
679 | rpc_release_task(task); | 693 | rpc_release_task(task); |
680 | return status; | 694 | return status; |
@@ -788,15 +802,6 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, cons | |||
788 | task->tk_flags |= RPC_TASK_NOINTR; | 802 | task->tk_flags |= RPC_TASK_NOINTR; |
789 | } | 803 | } |
790 | 804 | ||
791 | #ifdef RPC_DEBUG | ||
792 | task->tk_magic = RPC_TASK_MAGIC_ID; | ||
793 | task->tk_pid = rpc_task_id++; | ||
794 | #endif | ||
795 | /* Add to global list of all tasks */ | ||
796 | spin_lock(&rpc_sched_lock); | ||
797 | list_add_tail(&task->tk_task, &all_tasks); | ||
798 | spin_unlock(&rpc_sched_lock); | ||
799 | |||
800 | BUG_ON(task->tk_ops == NULL); | 805 | BUG_ON(task->tk_ops == NULL); |
801 | 806 | ||
802 | /* starting timestamp */ | 807 | /* starting timestamp */ |
@@ -849,16 +854,35 @@ cleanup: | |||
849 | goto out; | 854 | goto out; |
850 | } | 855 | } |
851 | 856 | ||
852 | void rpc_release_task(struct rpc_task *task) | 857 | |
858 | void rpc_put_task(struct rpc_task *task) | ||
853 | { | 859 | { |
854 | const struct rpc_call_ops *tk_ops = task->tk_ops; | 860 | const struct rpc_call_ops *tk_ops = task->tk_ops; |
855 | void *calldata = task->tk_calldata; | 861 | void *calldata = task->tk_calldata; |
856 | 862 | ||
863 | if (!atomic_dec_and_test(&task->tk_count)) | ||
864 | return; | ||
865 | /* Release resources */ | ||
866 | if (task->tk_rqstp) | ||
867 | xprt_release(task); | ||
868 | if (task->tk_msg.rpc_cred) | ||
869 | rpcauth_unbindcred(task); | ||
870 | if (task->tk_client) { | ||
871 | rpc_release_client(task->tk_client); | ||
872 | task->tk_client = NULL; | ||
873 | } | ||
874 | if (task->tk_flags & RPC_TASK_DYNAMIC) | ||
875 | rpc_free_task(task); | ||
876 | if (tk_ops->rpc_release) | ||
877 | tk_ops->rpc_release(calldata); | ||
878 | } | ||
879 | EXPORT_SYMBOL(rpc_put_task); | ||
880 | |||
881 | void rpc_release_task(struct rpc_task *task) | ||
882 | { | ||
857 | #ifdef RPC_DEBUG | 883 | #ifdef RPC_DEBUG |
858 | BUG_ON(task->tk_magic != RPC_TASK_MAGIC_ID); | 884 | BUG_ON(task->tk_magic != RPC_TASK_MAGIC_ID); |
859 | #endif | 885 | #endif |
860 | if (!atomic_dec_and_test(&task->tk_count)) | ||
861 | return; | ||
862 | dprintk("RPC: %4d release task\n", task->tk_pid); | 886 | dprintk("RPC: %4d release task\n", task->tk_pid); |
863 | 887 | ||
864 | /* Remove from global task list */ | 888 | /* Remove from global task list */ |
@@ -871,23 +895,13 @@ void rpc_release_task(struct rpc_task *task) | |||
871 | /* Synchronously delete any running timer */ | 895 | /* Synchronously delete any running timer */ |
872 | rpc_delete_timer(task); | 896 | rpc_delete_timer(task); |
873 | 897 | ||
874 | /* Release resources */ | ||
875 | if (task->tk_rqstp) | ||
876 | xprt_release(task); | ||
877 | if (task->tk_msg.rpc_cred) | ||
878 | rpcauth_unbindcred(task); | ||
879 | if (task->tk_client) { | ||
880 | rpc_release_client(task->tk_client); | ||
881 | task->tk_client = NULL; | ||
882 | } | ||
883 | |||
884 | #ifdef RPC_DEBUG | 898 | #ifdef RPC_DEBUG |
885 | task->tk_magic = 0; | 899 | task->tk_magic = 0; |
886 | #endif | 900 | #endif |
887 | if (task->tk_flags & RPC_TASK_DYNAMIC) | 901 | /* Wake up anyone who is waiting for task completion */ |
888 | rpc_free_task(task); | 902 | rpc_mark_complete_task(task); |
889 | if (tk_ops->rpc_release) | 903 | |
890 | tk_ops->rpc_release(calldata); | 904 | rpc_put_task(task); |
891 | } | 905 | } |
892 | 906 | ||
893 | /** | 907 | /** |
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 192dff5dabcb..faaf81e97720 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c | |||
@@ -33,7 +33,6 @@ EXPORT_SYMBOL(rpciod_down); | |||
33 | EXPORT_SYMBOL(rpciod_up); | 33 | EXPORT_SYMBOL(rpciod_up); |
34 | EXPORT_SYMBOL(rpc_new_task); | 34 | EXPORT_SYMBOL(rpc_new_task); |
35 | EXPORT_SYMBOL(rpc_wake_up_status); | 35 | EXPORT_SYMBOL(rpc_wake_up_status); |
36 | EXPORT_SYMBOL(rpc_release_task); | ||
37 | 36 | ||
38 | /* RPC client functions */ | 37 | /* RPC client functions */ |
39 | EXPORT_SYMBOL(rpc_clone_client); | 38 | EXPORT_SYMBOL(rpc_clone_client); |