diff options
author | Trond Myklebust <trondmy@gmail.com> | 2019-04-07 13:58:44 -0400 |
---|---|---|
committer | Anna Schumaker <Anna.Schumaker@Netapp.com> | 2019-04-25 14:18:12 -0400 |
commit | ae67bd3821bb0a54d97e7883d211196637d487a9 (patch) | |
tree | 351a54fd10a71f6c76d5e8d8a8ce323c9252192e | |
parent | 085b7755808aa11f78ab9377257e1dad2e6fa4bb (diff) |
SUNRPC: Fix up task signalling
The RPC_TASK_KILLED flag should really not be set from another context
because it can clobber data in the struct task when task->tk_flags is
changed non-atomically.
Let's therefore swap out RPC_TASK_KILLED with an atomic flag, and add
a function to set that flag and safely wake up the task.
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
-rw-r--r-- | fs/lockd/clntproc.c | 4 | ||||
-rw-r--r-- | fs/nfsd/nfs4callback.c | 4 | ||||
-rw-r--r-- | include/linux/sunrpc/sched.h | 6 | ||||
-rw-r--r-- | include/trace/events/sunrpc.h | 6 | ||||
-rw-r--r-- | net/sunrpc/clnt.c | 14 | ||||
-rw-r--r-- | net/sunrpc/sched.c | 28 | ||||
-rw-r--r-- | net/sunrpc/xprt.c | 4 |
7 files changed, 40 insertions, 26 deletions
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index e8a004097d18..d9c32d1a20c0 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c | |||
@@ -715,7 +715,7 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) | |||
715 | struct nlm_rqst *req = data; | 715 | struct nlm_rqst *req = data; |
716 | u32 status = ntohl(req->a_res.status); | 716 | u32 status = ntohl(req->a_res.status); |
717 | 717 | ||
718 | if (RPC_ASSASSINATED(task)) | 718 | if (RPC_SIGNALLED(task)) |
719 | goto die; | 719 | goto die; |
720 | 720 | ||
721 | if (task->tk_status < 0) { | 721 | if (task->tk_status < 0) { |
@@ -783,7 +783,7 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data) | |||
783 | struct nlm_rqst *req = data; | 783 | struct nlm_rqst *req = data; |
784 | u32 status = ntohl(req->a_res.status); | 784 | u32 status = ntohl(req->a_res.status); |
785 | 785 | ||
786 | if (RPC_ASSASSINATED(task)) | 786 | if (RPC_SIGNALLED(task)) |
787 | goto die; | 787 | goto die; |
788 | 788 | ||
789 | if (task->tk_status < 0) { | 789 | if (task->tk_status < 0) { |
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index d219159b98af..f7494be8dbe2 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -1032,7 +1032,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback | |||
1032 | * the submission code will error out, so we don't need to | 1032 | * the submission code will error out, so we don't need to |
1033 | * handle that case here. | 1033 | * handle that case here. |
1034 | */ | 1034 | */ |
1035 | if (task->tk_flags & RPC_TASK_KILLED) | 1035 | if (RPC_SIGNALLED(task)) |
1036 | goto need_restart; | 1036 | goto need_restart; |
1037 | 1037 | ||
1038 | return true; | 1038 | return true; |
@@ -1081,7 +1081,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback | |||
1081 | dprintk("%s: freed slot, new seqid=%d\n", __func__, | 1081 | dprintk("%s: freed slot, new seqid=%d\n", __func__, |
1082 | clp->cl_cb_session->se_cb_seq_nr); | 1082 | clp->cl_cb_session->se_cb_seq_nr); |
1083 | 1083 | ||
1084 | if (task->tk_flags & RPC_TASK_KILLED) | 1084 | if (RPC_SIGNALLED(task)) |
1085 | goto need_restart; | 1085 | goto need_restart; |
1086 | out: | 1086 | out: |
1087 | return ret; | 1087 | return ret; |
diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 52d41d0c1ae1..90e06c67f455 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h | |||
@@ -125,7 +125,6 @@ struct rpc_task_setup { | |||
125 | #define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */ | 125 | #define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */ |
126 | #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */ | 126 | #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */ |
127 | #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */ | 127 | #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */ |
128 | #define RPC_TASK_KILLED 0x0100 /* task was killed */ | ||
129 | #define RPC_TASK_SOFT 0x0200 /* Use soft timeouts */ | 128 | #define RPC_TASK_SOFT 0x0200 /* Use soft timeouts */ |
130 | #define RPC_TASK_SOFTCONN 0x0400 /* Fail if can't connect */ | 129 | #define RPC_TASK_SOFTCONN 0x0400 /* Fail if can't connect */ |
131 | #define RPC_TASK_SENT 0x0800 /* message was sent */ | 130 | #define RPC_TASK_SENT 0x0800 /* message was sent */ |
@@ -135,7 +134,6 @@ struct rpc_task_setup { | |||
135 | 134 | ||
136 | #define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC) | 135 | #define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC) |
137 | #define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER) | 136 | #define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER) |
138 | #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) | ||
139 | #define RPC_IS_SOFT(t) ((t)->tk_flags & (RPC_TASK_SOFT|RPC_TASK_TIMEOUT)) | 137 | #define RPC_IS_SOFT(t) ((t)->tk_flags & (RPC_TASK_SOFT|RPC_TASK_TIMEOUT)) |
140 | #define RPC_IS_SOFTCONN(t) ((t)->tk_flags & RPC_TASK_SOFTCONN) | 138 | #define RPC_IS_SOFTCONN(t) ((t)->tk_flags & RPC_TASK_SOFTCONN) |
141 | #define RPC_WAS_SENT(t) ((t)->tk_flags & RPC_TASK_SENT) | 139 | #define RPC_WAS_SENT(t) ((t)->tk_flags & RPC_TASK_SENT) |
@@ -146,6 +144,7 @@ struct rpc_task_setup { | |||
146 | #define RPC_TASK_NEED_XMIT 3 | 144 | #define RPC_TASK_NEED_XMIT 3 |
147 | #define RPC_TASK_NEED_RECV 4 | 145 | #define RPC_TASK_NEED_RECV 4 |
148 | #define RPC_TASK_MSG_PIN_WAIT 5 | 146 | #define RPC_TASK_MSG_PIN_WAIT 5 |
147 | #define RPC_TASK_SIGNALLED 6 | ||
149 | 148 | ||
150 | #define RPC_IS_RUNNING(t) test_bit(RPC_TASK_RUNNING, &(t)->tk_runstate) | 149 | #define RPC_IS_RUNNING(t) test_bit(RPC_TASK_RUNNING, &(t)->tk_runstate) |
151 | #define rpc_set_running(t) set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate) | 150 | #define rpc_set_running(t) set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate) |
@@ -169,6 +168,8 @@ struct rpc_task_setup { | |||
169 | 168 | ||
170 | #define RPC_IS_ACTIVATED(t) test_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate) | 169 | #define RPC_IS_ACTIVATED(t) test_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate) |
171 | 170 | ||
171 | #define RPC_SIGNALLED(t) test_bit(RPC_TASK_SIGNALLED, &(t)->tk_runstate) | ||
172 | |||
172 | /* | 173 | /* |
173 | * Task priorities. | 174 | * Task priorities. |
174 | * Note: if you change these, you must also change | 175 | * Note: if you change these, you must also change |
@@ -217,6 +218,7 @@ struct rpc_task *rpc_run_task(const struct rpc_task_setup *); | |||
217 | struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req); | 218 | struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req); |
218 | void rpc_put_task(struct rpc_task *); | 219 | void rpc_put_task(struct rpc_task *); |
219 | void rpc_put_task_async(struct rpc_task *); | 220 | void rpc_put_task_async(struct rpc_task *); |
221 | void rpc_signal_task(struct rpc_task *); | ||
220 | void rpc_exit_task(struct rpc_task *); | 222 | void rpc_exit_task(struct rpc_task *); |
221 | void rpc_exit(struct rpc_task *, int); | 223 | void rpc_exit(struct rpc_task *, int); |
222 | void rpc_release_calldata(const struct rpc_call_ops *, void *); | 224 | void rpc_release_calldata(const struct rpc_call_ops *, void *); |
diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 7e899e635d33..5e3b77d9daa7 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h | |||
@@ -82,7 +82,6 @@ TRACE_DEFINE_ENUM(RPC_TASK_SWAPPER); | |||
82 | TRACE_DEFINE_ENUM(RPC_CALL_MAJORSEEN); | 82 | TRACE_DEFINE_ENUM(RPC_CALL_MAJORSEEN); |
83 | TRACE_DEFINE_ENUM(RPC_TASK_ROOTCREDS); | 83 | TRACE_DEFINE_ENUM(RPC_TASK_ROOTCREDS); |
84 | TRACE_DEFINE_ENUM(RPC_TASK_DYNAMIC); | 84 | TRACE_DEFINE_ENUM(RPC_TASK_DYNAMIC); |
85 | TRACE_DEFINE_ENUM(RPC_TASK_KILLED); | ||
86 | TRACE_DEFINE_ENUM(RPC_TASK_SOFT); | 85 | TRACE_DEFINE_ENUM(RPC_TASK_SOFT); |
87 | TRACE_DEFINE_ENUM(RPC_TASK_SOFTCONN); | 86 | TRACE_DEFINE_ENUM(RPC_TASK_SOFTCONN); |
88 | TRACE_DEFINE_ENUM(RPC_TASK_SENT); | 87 | TRACE_DEFINE_ENUM(RPC_TASK_SENT); |
@@ -97,7 +96,6 @@ TRACE_DEFINE_ENUM(RPC_TASK_NO_RETRANS_TIMEOUT); | |||
97 | { RPC_CALL_MAJORSEEN, "MAJORSEEN" }, \ | 96 | { RPC_CALL_MAJORSEEN, "MAJORSEEN" }, \ |
98 | { RPC_TASK_ROOTCREDS, "ROOTCREDS" }, \ | 97 | { RPC_TASK_ROOTCREDS, "ROOTCREDS" }, \ |
99 | { RPC_TASK_DYNAMIC, "DYNAMIC" }, \ | 98 | { RPC_TASK_DYNAMIC, "DYNAMIC" }, \ |
100 | { RPC_TASK_KILLED, "KILLED" }, \ | ||
101 | { RPC_TASK_SOFT, "SOFT" }, \ | 99 | { RPC_TASK_SOFT, "SOFT" }, \ |
102 | { RPC_TASK_SOFTCONN, "SOFTCONN" }, \ | 100 | { RPC_TASK_SOFTCONN, "SOFTCONN" }, \ |
103 | { RPC_TASK_SENT, "SENT" }, \ | 101 | { RPC_TASK_SENT, "SENT" }, \ |
@@ -111,6 +109,7 @@ TRACE_DEFINE_ENUM(RPC_TASK_ACTIVE); | |||
111 | TRACE_DEFINE_ENUM(RPC_TASK_NEED_XMIT); | 109 | TRACE_DEFINE_ENUM(RPC_TASK_NEED_XMIT); |
112 | TRACE_DEFINE_ENUM(RPC_TASK_NEED_RECV); | 110 | TRACE_DEFINE_ENUM(RPC_TASK_NEED_RECV); |
113 | TRACE_DEFINE_ENUM(RPC_TASK_MSG_PIN_WAIT); | 111 | TRACE_DEFINE_ENUM(RPC_TASK_MSG_PIN_WAIT); |
112 | TRACE_DEFINE_ENUM(RPC_TASK_SIGNALLED); | ||
114 | 113 | ||
115 | #define rpc_show_runstate(flags) \ | 114 | #define rpc_show_runstate(flags) \ |
116 | __print_flags(flags, "|", \ | 115 | __print_flags(flags, "|", \ |
@@ -119,7 +118,8 @@ TRACE_DEFINE_ENUM(RPC_TASK_MSG_PIN_WAIT); | |||
119 | { (1UL << RPC_TASK_ACTIVE), "ACTIVE" }, \ | 118 | { (1UL << RPC_TASK_ACTIVE), "ACTIVE" }, \ |
120 | { (1UL << RPC_TASK_NEED_XMIT), "NEED_XMIT" }, \ | 119 | { (1UL << RPC_TASK_NEED_XMIT), "NEED_XMIT" }, \ |
121 | { (1UL << RPC_TASK_NEED_RECV), "NEED_RECV" }, \ | 120 | { (1UL << RPC_TASK_NEED_RECV), "NEED_RECV" }, \ |
122 | { (1UL << RPC_TASK_MSG_PIN_WAIT), "MSG_PIN_WAIT" }) | 121 | { (1UL << RPC_TASK_MSG_PIN_WAIT), "MSG_PIN_WAIT" }, \ |
122 | { (1UL << RPC_TASK_SIGNALLED), "SIGNALLED" }) | ||
123 | 123 | ||
124 | DECLARE_EVENT_CLASS(rpc_task_running, | 124 | DECLARE_EVENT_CLASS(rpc_task_running, |
125 | 125 | ||
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 8ff11dc98d7f..18f5392aa550 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -827,14 +827,8 @@ void rpc_killall_tasks(struct rpc_clnt *clnt) | |||
827 | * Spin lock all_tasks to prevent changes... | 827 | * Spin lock all_tasks to prevent changes... |
828 | */ | 828 | */ |
829 | spin_lock(&clnt->cl_lock); | 829 | spin_lock(&clnt->cl_lock); |
830 | list_for_each_entry(rovr, &clnt->cl_tasks, tk_task) { | 830 | list_for_each_entry(rovr, &clnt->cl_tasks, tk_task) |
831 | if (!RPC_IS_ACTIVATED(rovr)) | 831 | rpc_signal_task(rovr); |
832 | continue; | ||
833 | if (!(rovr->tk_flags & RPC_TASK_KILLED)) { | ||
834 | rovr->tk_flags |= RPC_TASK_KILLED; | ||
835 | rpc_exit(rovr, -EIO); | ||
836 | } | ||
837 | } | ||
838 | spin_unlock(&clnt->cl_lock); | 832 | spin_unlock(&clnt->cl_lock); |
839 | } | 833 | } |
840 | EXPORT_SYMBOL_GPL(rpc_killall_tasks); | 834 | EXPORT_SYMBOL_GPL(rpc_killall_tasks); |
@@ -1477,8 +1471,6 @@ EXPORT_SYMBOL_GPL(rpc_force_rebind); | |||
1477 | int | 1471 | int |
1478 | rpc_restart_call_prepare(struct rpc_task *task) | 1472 | rpc_restart_call_prepare(struct rpc_task *task) |
1479 | { | 1473 | { |
1480 | if (RPC_ASSASSINATED(task)) | ||
1481 | return 0; | ||
1482 | task->tk_action = call_start; | 1474 | task->tk_action = call_start; |
1483 | task->tk_status = 0; | 1475 | task->tk_status = 0; |
1484 | if (task->tk_ops->rpc_call_prepare != NULL) | 1476 | if (task->tk_ops->rpc_call_prepare != NULL) |
@@ -1494,8 +1486,6 @@ EXPORT_SYMBOL_GPL(rpc_restart_call_prepare); | |||
1494 | int | 1486 | int |
1495 | rpc_restart_call(struct rpc_task *task) | 1487 | rpc_restart_call(struct rpc_task *task) |
1496 | { | 1488 | { |
1497 | if (RPC_ASSASSINATED(task)) | ||
1498 | return 0; | ||
1499 | task->tk_action = call_start; | 1489 | task->tk_action = call_start; |
1500 | task->tk_status = 0; | 1490 | task->tk_status = 0; |
1501 | return 1; | 1491 | return 1; |
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 28956c70100a..3d6cb91ba598 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c | |||
@@ -759,8 +759,7 @@ static void | |||
759 | rpc_reset_task_statistics(struct rpc_task *task) | 759 | rpc_reset_task_statistics(struct rpc_task *task) |
760 | { | 760 | { |
761 | task->tk_timeouts = 0; | 761 | task->tk_timeouts = 0; |
762 | task->tk_flags &= ~(RPC_CALL_MAJORSEEN|RPC_TASK_KILLED|RPC_TASK_SENT); | 762 | task->tk_flags &= ~(RPC_CALL_MAJORSEEN|RPC_TASK_SENT); |
763 | |||
764 | rpc_init_task_statistics(task); | 763 | rpc_init_task_statistics(task); |
765 | } | 764 | } |
766 | 765 | ||
@@ -773,7 +772,6 @@ void rpc_exit_task(struct rpc_task *task) | |||
773 | if (task->tk_ops->rpc_call_done != NULL) { | 772 | if (task->tk_ops->rpc_call_done != NULL) { |
774 | task->tk_ops->rpc_call_done(task, task->tk_calldata); | 773 | task->tk_ops->rpc_call_done(task, task->tk_calldata); |
775 | if (task->tk_action != NULL) { | 774 | if (task->tk_action != NULL) { |
776 | WARN_ON(RPC_ASSASSINATED(task)); | ||
777 | /* Always release the RPC slot and buffer memory */ | 775 | /* Always release the RPC slot and buffer memory */ |
778 | xprt_release(task); | 776 | xprt_release(task); |
779 | rpc_reset_task_statistics(task); | 777 | rpc_reset_task_statistics(task); |
@@ -781,6 +779,19 @@ void rpc_exit_task(struct rpc_task *task) | |||
781 | } | 779 | } |
782 | } | 780 | } |
783 | 781 | ||
782 | void rpc_signal_task(struct rpc_task *task) | ||
783 | { | ||
784 | struct rpc_wait_queue *queue; | ||
785 | |||
786 | if (!RPC_IS_ACTIVATED(task)) | ||
787 | return; | ||
788 | set_bit(RPC_TASK_SIGNALLED, &task->tk_runstate); | ||
789 | smp_mb__after_atomic(); | ||
790 | queue = READ_ONCE(task->tk_waitqueue); | ||
791 | if (queue) | ||
792 | rpc_wake_up_queued_task_set_status(queue, task, -ERESTARTSYS); | ||
793 | } | ||
794 | |||
784 | void rpc_exit(struct rpc_task *task, int status) | 795 | void rpc_exit(struct rpc_task *task, int status) |
785 | { | 796 | { |
786 | task->tk_status = status; | 797 | task->tk_status = status; |
@@ -836,6 +847,13 @@ static void __rpc_execute(struct rpc_task *task) | |||
836 | */ | 847 | */ |
837 | if (!RPC_IS_QUEUED(task)) | 848 | if (!RPC_IS_QUEUED(task)) |
838 | continue; | 849 | continue; |
850 | |||
851 | /* | ||
852 | * Signalled tasks should exit rather than sleep. | ||
853 | */ | ||
854 | if (RPC_SIGNALLED(task)) | ||
855 | rpc_exit(task, -ERESTARTSYS); | ||
856 | |||
839 | /* | 857 | /* |
840 | * The queue->lock protects against races with | 858 | * The queue->lock protects against races with |
841 | * rpc_make_runnable(). | 859 | * rpc_make_runnable(). |
@@ -861,7 +879,7 @@ static void __rpc_execute(struct rpc_task *task) | |||
861 | status = out_of_line_wait_on_bit(&task->tk_runstate, | 879 | status = out_of_line_wait_on_bit(&task->tk_runstate, |
862 | RPC_TASK_QUEUED, rpc_wait_bit_killable, | 880 | RPC_TASK_QUEUED, rpc_wait_bit_killable, |
863 | TASK_KILLABLE); | 881 | TASK_KILLABLE); |
864 | if (status == -ERESTARTSYS) { | 882 | if (status < 0) { |
865 | /* | 883 | /* |
866 | * When a sync task receives a signal, it exits with | 884 | * When a sync task receives a signal, it exits with |
867 | * -ERESTARTSYS. In order to catch any callbacks that | 885 | * -ERESTARTSYS. In order to catch any callbacks that |
@@ -869,7 +887,7 @@ static void __rpc_execute(struct rpc_task *task) | |||
869 | * break the loop here, but go around once more. | 887 | * break the loop here, but go around once more. |
870 | */ | 888 | */ |
871 | dprintk("RPC: %5u got signal\n", task->tk_pid); | 889 | dprintk("RPC: %5u got signal\n", task->tk_pid); |
872 | task->tk_flags |= RPC_TASK_KILLED; | 890 | set_bit(RPC_TASK_SIGNALLED, &task->tk_runstate); |
873 | rpc_exit(task, -ERESTARTSYS); | 891 | rpc_exit(task, -ERESTARTSYS); |
874 | } | 892 | } |
875 | dprintk("RPC: %5u sync task resuming\n", task->tk_pid); | 893 | dprintk("RPC: %5u sync task resuming\n", task->tk_pid); |
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index d7117d241460..3a4156cb0134 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
@@ -1337,6 +1337,10 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task) | |||
1337 | if (status < 0) | 1337 | if (status < 0) |
1338 | goto out_dequeue; | 1338 | goto out_dequeue; |
1339 | } | 1339 | } |
1340 | if (RPC_SIGNALLED(task)) { | ||
1341 | status = -ERESTARTSYS; | ||
1342 | goto out_dequeue; | ||
1343 | } | ||
1340 | } | 1344 | } |
1341 | 1345 | ||
1342 | /* | 1346 | /* |