aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc
diff options
context:
space:
mode:
authorParthasarathy Bhuvaragan <parthasarathy.bhuvaragan@ericsson.com>2016-04-12 07:05:21 -0400
committerDavid S. Miller <davem@davemloft.net>2016-04-14 16:46:46 -0400
commit333f796235a52727db7e0a13888045f3aa3d5335 (patch)
tree7e1230a9d803af9b5cef3492f5482ba01cc382ff /net/tipc
parentedd93cd72741b80633969c247a2f86f177242cfb (diff)
tipc: fix a race condition leading to subscriber refcnt bug
Until now, the requests sent to topology server are queued to a workqueue by the generic server framework. These messages are processed by worker threads and trigger the registered callbacks. To reduce latency on uniprocessor systems, explicit rescheduling is performed using cond_resched() after MAX_RECV_MSG_COUNT(25) messages. This implementation on SMP systems leads to an subscriber refcnt error as described below: When a worker thread yields by calling cond_resched() in a SMP system, a new worker is created on another CPU to process the pending workitem. Sometimes the sleeping thread wakes up before the new thread finishes execution. This breaks the assumption on ordering and being single threaded. The fault is more frequent when MAX_RECV_MSG_COUNT is lowered. If the first thread was processing subscription create and the second thread processing close(), the close request will free the subscriber and the create request oops as follows: [31.224137] WARNING: CPU: 2 PID: 266 at include/linux/kref.h:46 tipc_subscrb_rcv_cb+0x317/0x380 [tipc] [31.228143] CPU: 2 PID: 266 Comm: kworker/u8:1 Not tainted 4.5.0+ #97 [31.228377] Workqueue: tipc_rcv tipc_recv_work [tipc] [...] [31.228377] Call Trace: [31.228377] [<ffffffff812fbb6b>] dump_stack+0x4d/0x72 [31.228377] [<ffffffff8105a311>] __warn+0xd1/0xf0 [31.228377] [<ffffffff8105a3fd>] warn_slowpath_null+0x1d/0x20 [31.228377] [<ffffffffa0098067>] tipc_subscrb_rcv_cb+0x317/0x380 [tipc] [31.228377] [<ffffffffa00a4984>] tipc_receive_from_sock+0xd4/0x130 [tipc] [31.228377] [<ffffffffa00a439b>] tipc_recv_work+0x2b/0x50 [tipc] [31.228377] [<ffffffff81071925>] process_one_work+0x145/0x3d0 [31.246554] ---[ end trace c3882c9baa05a4fd ]--- [31.248327] BUG: spinlock bad magic on CPU#2, kworker/u8:1/266 [31.249119] BUG: unable to handle kernel NULL pointer dereference at 0000000000000428 [31.249323] IP: [<ffffffff81099d0c>] spin_dump+0x5c/0xe0 [31.249323] PGD 0 [31.249323] Oops: 0000 [#1] SMP In this commit, we - rename tipc_conn_shutdown() to tipc_conn_release(). - move connection release callback execution from tipc_close_conn() to a new function tipc_sock_release(), which is executed before we free the connection. Thus we release the subscriber during connection release procedure rather than connection shutdown procedure. Signed-off-by: Parthasarathy Bhuvaragan <parthasarathy.bhuvaragan@ericsson.com> Acked-by: Ying Xue <ying.xue@windriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc')
-rw-r--r--net/tipc/server.c19
-rw-r--r--net/tipc/server.h4
-rw-r--r--net/tipc/subscr.c4
3 files changed, 17 insertions, 10 deletions
diff --git a/net/tipc/server.c b/net/tipc/server.c
index 2446bfbaa309..7a0af2dc0406 100644
--- a/net/tipc/server.c
+++ b/net/tipc/server.c
@@ -86,6 +86,7 @@ struct outqueue_entry {
86static void tipc_recv_work(struct work_struct *work); 86static void tipc_recv_work(struct work_struct *work);
87static void tipc_send_work(struct work_struct *work); 87static void tipc_send_work(struct work_struct *work);
88static void tipc_clean_outqueues(struct tipc_conn *con); 88static void tipc_clean_outqueues(struct tipc_conn *con);
89static void tipc_sock_release(struct tipc_conn *con);
89 90
90static void tipc_conn_kref_release(struct kref *kref) 91static void tipc_conn_kref_release(struct kref *kref)
91{ 92{
@@ -102,6 +103,7 @@ static void tipc_conn_kref_release(struct kref *kref)
102 } 103 }
103 saddr->scope = -TIPC_NODE_SCOPE; 104 saddr->scope = -TIPC_NODE_SCOPE;
104 kernel_bind(sock, (struct sockaddr *)saddr, sizeof(*saddr)); 105 kernel_bind(sock, (struct sockaddr *)saddr, sizeof(*saddr));
106 tipc_sock_release(con);
105 sock_release(sock); 107 sock_release(sock);
106 con->sock = NULL; 108 con->sock = NULL;
107 } 109 }
@@ -184,26 +186,31 @@ static void tipc_unregister_callbacks(struct tipc_conn *con)
184 write_unlock_bh(&sk->sk_callback_lock); 186 write_unlock_bh(&sk->sk_callback_lock);
185} 187}
186 188
189static void tipc_sock_release(struct tipc_conn *con)
190{
191 struct tipc_server *s = con->server;
192
193 if (con->conid)
194 s->tipc_conn_release(con->conid, con->usr_data);
195
196 tipc_unregister_callbacks(con);
197}
198
187static void tipc_close_conn(struct tipc_conn *con) 199static void tipc_close_conn(struct tipc_conn *con)
188{ 200{
189 struct tipc_server *s = con->server; 201 struct tipc_server *s = con->server;
190 202
191 if (test_and_clear_bit(CF_CONNECTED, &con->flags)) { 203 if (test_and_clear_bit(CF_CONNECTED, &con->flags)) {
192 if (con->conid)
193 s->tipc_conn_shutdown(con->conid, con->usr_data);
194 204
195 spin_lock_bh(&s->idr_lock); 205 spin_lock_bh(&s->idr_lock);
196 idr_remove(&s->conn_idr, con->conid); 206 idr_remove(&s->conn_idr, con->conid);
197 s->idr_in_use--; 207 s->idr_in_use--;
198 spin_unlock_bh(&s->idr_lock); 208 spin_unlock_bh(&s->idr_lock);
199 209
200 tipc_unregister_callbacks(con);
201
202 /* We shouldn't flush pending works as we may be in the 210 /* We shouldn't flush pending works as we may be in the
203 * thread. In fact the races with pending rx/tx work structs 211 * thread. In fact the races with pending rx/tx work structs
204 * are harmless for us here as we have already deleted this 212 * are harmless for us here as we have already deleted this
205 * connection from server connection list and set 213 * connection from server connection list.
206 * sk->sk_user_data to 0 before releasing connection object.
207 */ 214 */
208 kernel_sock_shutdown(con->sock, SHUT_RDWR); 215 kernel_sock_shutdown(con->sock, SHUT_RDWR);
209 216
diff --git a/net/tipc/server.h b/net/tipc/server.h
index 9015faedb1b0..34f8055afa3b 100644
--- a/net/tipc/server.h
+++ b/net/tipc/server.h
@@ -53,7 +53,7 @@
53 * @send_wq: send workqueue 53 * @send_wq: send workqueue
54 * @max_rcvbuf_size: maximum permitted receive message length 54 * @max_rcvbuf_size: maximum permitted receive message length
55 * @tipc_conn_new: callback will be called when new connection is incoming 55 * @tipc_conn_new: callback will be called when new connection is incoming
56 * @tipc_conn_shutdown: callback will be called when connection is shut down 56 * @tipc_conn_release: callback will be called before releasing the connection
57 * @tipc_conn_recvmsg: callback will be called when message arrives 57 * @tipc_conn_recvmsg: callback will be called when message arrives
58 * @saddr: TIPC server address 58 * @saddr: TIPC server address
59 * @name: server name 59 * @name: server name
@@ -70,7 +70,7 @@ struct tipc_server {
70 struct workqueue_struct *send_wq; 70 struct workqueue_struct *send_wq;
71 int max_rcvbuf_size; 71 int max_rcvbuf_size;
72 void *(*tipc_conn_new)(int conid); 72 void *(*tipc_conn_new)(int conid);
73 void (*tipc_conn_shutdown)(int conid, void *usr_data); 73 void (*tipc_conn_release)(int conid, void *usr_data);
74 void (*tipc_conn_recvmsg)(struct net *net, int conid, 74 void (*tipc_conn_recvmsg)(struct net *net, int conid,
75 struct sockaddr_tipc *addr, void *usr_data, 75 struct sockaddr_tipc *addr, void *usr_data,
76 void *buf, size_t len); 76 void *buf, size_t len);
diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c
index e6cb386fbf34..79de588c7bd6 100644
--- a/net/tipc/subscr.c
+++ b/net/tipc/subscr.c
@@ -302,7 +302,7 @@ static void tipc_subscrp_subscribe(struct net *net, struct tipc_subscr *s,
302} 302}
303 303
304/* Handle one termination request for the subscriber */ 304/* Handle one termination request for the subscriber */
305static void tipc_subscrb_shutdown_cb(int conid, void *usr_data) 305static void tipc_subscrb_release_cb(int conid, void *usr_data)
306{ 306{
307 tipc_subscrb_delete((struct tipc_subscriber *)usr_data); 307 tipc_subscrb_delete((struct tipc_subscriber *)usr_data);
308} 308}
@@ -365,7 +365,7 @@ int tipc_topsrv_start(struct net *net)
365 topsrv->max_rcvbuf_size = sizeof(struct tipc_subscr); 365 topsrv->max_rcvbuf_size = sizeof(struct tipc_subscr);
366 topsrv->tipc_conn_recvmsg = tipc_subscrb_rcv_cb; 366 topsrv->tipc_conn_recvmsg = tipc_subscrb_rcv_cb;
367 topsrv->tipc_conn_new = tipc_subscrb_connect_cb; 367 topsrv->tipc_conn_new = tipc_subscrb_connect_cb;
368 topsrv->tipc_conn_shutdown = tipc_subscrb_shutdown_cb; 368 topsrv->tipc_conn_release = tipc_subscrb_release_cb;
369 369
370 strncpy(topsrv->name, name, strlen(name) + 1); 370 strncpy(topsrv->name, name, strlen(name) + 1);
371 tn->topsrv = topsrv; 371 tn->topsrv = topsrv;