diff options
author | James Chapman <jchapman@katalix.com> | 2008-03-05 21:39:08 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-03-05 21:39:08 -0500 |
commit | cf3752e2d203bbbfc88d29e362e6938cef4339b3 (patch) | |
tree | f48f87708423899ca04590e0a004b3c4913443f9 | |
parent | 5a346a10c0b1192e7eae52f0f3a332f1d3f11226 (diff) |
[PPPOL2TP]: Make locking calls softirq-safe
Fix locking issues in the pppol2tp driver which can cause a kernel
crash on SMP boxes. There were two problems:-
1. The driver was violating read_lock() and write_lock() scheduling
rules because it wasn't using softirq-safe locks in softirq
contexts. So we now consistently use the _bh variants of the lock
functions.
2. The driver was calling sk_dst_get() in pppol2tp_xmit() which was
taking sk_dst_lock in softirq context. We now call __sk_dst_get().
Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/pppol2tp.c | 58 |
1 files changed, 29 insertions, 29 deletions
diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 86e5dba079fe..9a90cede0ecc 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c | |||
@@ -302,14 +302,14 @@ pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id) | |||
302 | struct pppol2tp_session *session; | 302 | struct pppol2tp_session *session; |
303 | struct hlist_node *walk; | 303 | struct hlist_node *walk; |
304 | 304 | ||
305 | read_lock(&tunnel->hlist_lock); | 305 | read_lock_bh(&tunnel->hlist_lock); |
306 | hlist_for_each_entry(session, walk, session_list, hlist) { | 306 | hlist_for_each_entry(session, walk, session_list, hlist) { |
307 | if (session->tunnel_addr.s_session == session_id) { | 307 | if (session->tunnel_addr.s_session == session_id) { |
308 | read_unlock(&tunnel->hlist_lock); | 308 | read_unlock_bh(&tunnel->hlist_lock); |
309 | return session; | 309 | return session; |
310 | } | 310 | } |
311 | } | 311 | } |
312 | read_unlock(&tunnel->hlist_lock); | 312 | read_unlock_bh(&tunnel->hlist_lock); |
313 | 313 | ||
314 | return NULL; | 314 | return NULL; |
315 | } | 315 | } |
@@ -320,14 +320,14 @@ static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id) | |||
320 | { | 320 | { |
321 | struct pppol2tp_tunnel *tunnel = NULL; | 321 | struct pppol2tp_tunnel *tunnel = NULL; |
322 | 322 | ||
323 | read_lock(&pppol2tp_tunnel_list_lock); | 323 | read_lock_bh(&pppol2tp_tunnel_list_lock); |
324 | list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) { | 324 | list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) { |
325 | if (tunnel->stats.tunnel_id == tunnel_id) { | 325 | if (tunnel->stats.tunnel_id == tunnel_id) { |
326 | read_unlock(&pppol2tp_tunnel_list_lock); | 326 | read_unlock_bh(&pppol2tp_tunnel_list_lock); |
327 | return tunnel; | 327 | return tunnel; |
328 | } | 328 | } |
329 | } | 329 | } |
330 | read_unlock(&pppol2tp_tunnel_list_lock); | 330 | read_unlock_bh(&pppol2tp_tunnel_list_lock); |
331 | 331 | ||
332 | return NULL; | 332 | return NULL; |
333 | } | 333 | } |
@@ -344,7 +344,7 @@ static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_ | |||
344 | struct sk_buff *skbp; | 344 | struct sk_buff *skbp; |
345 | u16 ns = PPPOL2TP_SKB_CB(skb)->ns; | 345 | u16 ns = PPPOL2TP_SKB_CB(skb)->ns; |
346 | 346 | ||
347 | spin_lock(&session->reorder_q.lock); | 347 | spin_lock_bh(&session->reorder_q.lock); |
348 | skb_queue_walk(&session->reorder_q, skbp) { | 348 | skb_queue_walk(&session->reorder_q, skbp) { |
349 | if (PPPOL2TP_SKB_CB(skbp)->ns > ns) { | 349 | if (PPPOL2TP_SKB_CB(skbp)->ns > ns) { |
350 | __skb_insert(skb, skbp->prev, skbp, &session->reorder_q); | 350 | __skb_insert(skb, skbp->prev, skbp, &session->reorder_q); |
@@ -360,7 +360,7 @@ static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_ | |||
360 | __skb_queue_tail(&session->reorder_q, skb); | 360 | __skb_queue_tail(&session->reorder_q, skb); |
361 | 361 | ||
362 | out: | 362 | out: |
363 | spin_unlock(&session->reorder_q.lock); | 363 | spin_unlock_bh(&session->reorder_q.lock); |
364 | } | 364 | } |
365 | 365 | ||
366 | /* Dequeue a single skb. | 366 | /* Dequeue a single skb. |
@@ -442,7 +442,7 @@ static void pppol2tp_recv_dequeue(struct pppol2tp_session *session) | |||
442 | * expect to send up next, dequeue it and any other | 442 | * expect to send up next, dequeue it and any other |
443 | * in-sequence packets behind it. | 443 | * in-sequence packets behind it. |
444 | */ | 444 | */ |
445 | spin_lock(&session->reorder_q.lock); | 445 | spin_lock_bh(&session->reorder_q.lock); |
446 | skb_queue_walk_safe(&session->reorder_q, skb, tmp) { | 446 | skb_queue_walk_safe(&session->reorder_q, skb, tmp) { |
447 | if (time_after(jiffies, PPPOL2TP_SKB_CB(skb)->expires)) { | 447 | if (time_after(jiffies, PPPOL2TP_SKB_CB(skb)->expires)) { |
448 | session->stats.rx_seq_discards++; | 448 | session->stats.rx_seq_discards++; |
@@ -470,13 +470,13 @@ static void pppol2tp_recv_dequeue(struct pppol2tp_session *session) | |||
470 | goto out; | 470 | goto out; |
471 | } | 471 | } |
472 | } | 472 | } |
473 | spin_unlock(&session->reorder_q.lock); | 473 | spin_unlock_bh(&session->reorder_q.lock); |
474 | pppol2tp_recv_dequeue_skb(session, skb); | 474 | pppol2tp_recv_dequeue_skb(session, skb); |
475 | spin_lock(&session->reorder_q.lock); | 475 | spin_lock_bh(&session->reorder_q.lock); |
476 | } | 476 | } |
477 | 477 | ||
478 | out: | 478 | out: |
479 | spin_unlock(&session->reorder_q.lock); | 479 | spin_unlock_bh(&session->reorder_q.lock); |
480 | } | 480 | } |
481 | 481 | ||
482 | /* Internal receive frame. Do the real work of receiving an L2TP data frame | 482 | /* Internal receive frame. Do the real work of receiving an L2TP data frame |
@@ -1059,7 +1059,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
1059 | 1059 | ||
1060 | /* Get routing info from the tunnel socket */ | 1060 | /* Get routing info from the tunnel socket */ |
1061 | dst_release(skb->dst); | 1061 | dst_release(skb->dst); |
1062 | skb->dst = sk_dst_get(sk_tun); | 1062 | skb->dst = dst_clone(__sk_dst_get(sk_tun)); |
1063 | skb_orphan(skb); | 1063 | skb_orphan(skb); |
1064 | skb->sk = sk_tun; | 1064 | skb->sk = sk_tun; |
1065 | 1065 | ||
@@ -1107,7 +1107,7 @@ static void pppol2tp_tunnel_closeall(struct pppol2tp_tunnel *tunnel) | |||
1107 | PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, | 1107 | PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, |
1108 | "%s: closing all sessions...\n", tunnel->name); | 1108 | "%s: closing all sessions...\n", tunnel->name); |
1109 | 1109 | ||
1110 | write_lock(&tunnel->hlist_lock); | 1110 | write_lock_bh(&tunnel->hlist_lock); |
1111 | for (hash = 0; hash < PPPOL2TP_HASH_SIZE; hash++) { | 1111 | for (hash = 0; hash < PPPOL2TP_HASH_SIZE; hash++) { |
1112 | again: | 1112 | again: |
1113 | hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) { | 1113 | hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) { |
@@ -1129,7 +1129,7 @@ again: | |||
1129 | * disappear as we're jumping between locks. | 1129 | * disappear as we're jumping between locks. |
1130 | */ | 1130 | */ |
1131 | sock_hold(sk); | 1131 | sock_hold(sk); |
1132 | write_unlock(&tunnel->hlist_lock); | 1132 | write_unlock_bh(&tunnel->hlist_lock); |
1133 | lock_sock(sk); | 1133 | lock_sock(sk); |
1134 | 1134 | ||
1135 | if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { | 1135 | if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { |
@@ -1154,11 +1154,11 @@ again: | |||
1154 | * list so we are guaranteed to make forward | 1154 | * list so we are guaranteed to make forward |
1155 | * progress. | 1155 | * progress. |
1156 | */ | 1156 | */ |
1157 | write_lock(&tunnel->hlist_lock); | 1157 | write_lock_bh(&tunnel->hlist_lock); |
1158 | goto again; | 1158 | goto again; |
1159 | } | 1159 | } |
1160 | } | 1160 | } |
1161 | write_unlock(&tunnel->hlist_lock); | 1161 | write_unlock_bh(&tunnel->hlist_lock); |
1162 | } | 1162 | } |
1163 | 1163 | ||
1164 | /* Really kill the tunnel. | 1164 | /* Really kill the tunnel. |
@@ -1167,9 +1167,9 @@ again: | |||
1167 | static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel) | 1167 | static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel) |
1168 | { | 1168 | { |
1169 | /* Remove from socket list */ | 1169 | /* Remove from socket list */ |
1170 | write_lock(&pppol2tp_tunnel_list_lock); | 1170 | write_lock_bh(&pppol2tp_tunnel_list_lock); |
1171 | list_del_init(&tunnel->list); | 1171 | list_del_init(&tunnel->list); |
1172 | write_unlock(&pppol2tp_tunnel_list_lock); | 1172 | write_unlock_bh(&pppol2tp_tunnel_list_lock); |
1173 | 1173 | ||
1174 | atomic_dec(&pppol2tp_tunnel_count); | 1174 | atomic_dec(&pppol2tp_tunnel_count); |
1175 | kfree(tunnel); | 1175 | kfree(tunnel); |
@@ -1245,9 +1245,9 @@ static void pppol2tp_session_destruct(struct sock *sk) | |||
1245 | /* Delete the session socket from the | 1245 | /* Delete the session socket from the |
1246 | * hash | 1246 | * hash |
1247 | */ | 1247 | */ |
1248 | write_lock(&tunnel->hlist_lock); | 1248 | write_lock_bh(&tunnel->hlist_lock); |
1249 | hlist_del_init(&session->hlist); | 1249 | hlist_del_init(&session->hlist); |
1250 | write_unlock(&tunnel->hlist_lock); | 1250 | write_unlock_bh(&tunnel->hlist_lock); |
1251 | 1251 | ||
1252 | atomic_dec(&pppol2tp_session_count); | 1252 | atomic_dec(&pppol2tp_session_count); |
1253 | } | 1253 | } |
@@ -1392,9 +1392,9 @@ static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id, | |||
1392 | 1392 | ||
1393 | /* Add tunnel to our list */ | 1393 | /* Add tunnel to our list */ |
1394 | INIT_LIST_HEAD(&tunnel->list); | 1394 | INIT_LIST_HEAD(&tunnel->list); |
1395 | write_lock(&pppol2tp_tunnel_list_lock); | 1395 | write_lock_bh(&pppol2tp_tunnel_list_lock); |
1396 | list_add(&tunnel->list, &pppol2tp_tunnel_list); | 1396 | list_add(&tunnel->list, &pppol2tp_tunnel_list); |
1397 | write_unlock(&pppol2tp_tunnel_list_lock); | 1397 | write_unlock_bh(&pppol2tp_tunnel_list_lock); |
1398 | atomic_inc(&pppol2tp_tunnel_count); | 1398 | atomic_inc(&pppol2tp_tunnel_count); |
1399 | 1399 | ||
1400 | /* Bump the reference count. The tunnel context is deleted | 1400 | /* Bump the reference count. The tunnel context is deleted |
@@ -1599,11 +1599,11 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, | |||
1599 | sk->sk_user_data = session; | 1599 | sk->sk_user_data = session; |
1600 | 1600 | ||
1601 | /* Add session to the tunnel's hash list */ | 1601 | /* Add session to the tunnel's hash list */ |
1602 | write_lock(&tunnel->hlist_lock); | 1602 | write_lock_bh(&tunnel->hlist_lock); |
1603 | hlist_add_head(&session->hlist, | 1603 | hlist_add_head(&session->hlist, |
1604 | pppol2tp_session_id_hash(tunnel, | 1604 | pppol2tp_session_id_hash(tunnel, |
1605 | session->tunnel_addr.s_session)); | 1605 | session->tunnel_addr.s_session)); |
1606 | write_unlock(&tunnel->hlist_lock); | 1606 | write_unlock_bh(&tunnel->hlist_lock); |
1607 | 1607 | ||
1608 | atomic_inc(&pppol2tp_session_count); | 1608 | atomic_inc(&pppol2tp_session_count); |
1609 | 1609 | ||
@@ -2205,7 +2205,7 @@ static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, str | |||
2205 | int next = 0; | 2205 | int next = 0; |
2206 | int i; | 2206 | int i; |
2207 | 2207 | ||
2208 | read_lock(&tunnel->hlist_lock); | 2208 | read_lock_bh(&tunnel->hlist_lock); |
2209 | for (i = 0; i < PPPOL2TP_HASH_SIZE; i++) { | 2209 | for (i = 0; i < PPPOL2TP_HASH_SIZE; i++) { |
2210 | hlist_for_each_entry(session, walk, &tunnel->session_hlist[i], hlist) { | 2210 | hlist_for_each_entry(session, walk, &tunnel->session_hlist[i], hlist) { |
2211 | if (curr == NULL) { | 2211 | if (curr == NULL) { |
@@ -2223,7 +2223,7 @@ static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, str | |||
2223 | } | 2223 | } |
2224 | } | 2224 | } |
2225 | out: | 2225 | out: |
2226 | read_unlock(&tunnel->hlist_lock); | 2226 | read_unlock_bh(&tunnel->hlist_lock); |
2227 | if (!found) | 2227 | if (!found) |
2228 | session = NULL; | 2228 | session = NULL; |
2229 | 2229 | ||
@@ -2234,13 +2234,13 @@ static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_tunnel *curr) | |||
2234 | { | 2234 | { |
2235 | struct pppol2tp_tunnel *tunnel = NULL; | 2235 | struct pppol2tp_tunnel *tunnel = NULL; |
2236 | 2236 | ||
2237 | read_lock(&pppol2tp_tunnel_list_lock); | 2237 | read_lock_bh(&pppol2tp_tunnel_list_lock); |
2238 | if (list_is_last(&curr->list, &pppol2tp_tunnel_list)) { | 2238 | if (list_is_last(&curr->list, &pppol2tp_tunnel_list)) { |
2239 | goto out; | 2239 | goto out; |
2240 | } | 2240 | } |
2241 | tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list); | 2241 | tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list); |
2242 | out: | 2242 | out: |
2243 | read_unlock(&pppol2tp_tunnel_list_lock); | 2243 | read_unlock_bh(&pppol2tp_tunnel_list_lock); |
2244 | 2244 | ||
2245 | return tunnel; | 2245 | return tunnel; |
2246 | } | 2246 | } |