Commit 93821778 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

udp: Fix rcv socket locking

The previous patch in response to the recursive locking on IPsec
reception is broken as it tries to drop the BH socket lock while in
user context.

This patch fixes it by shrinking the section protected by the
socket lock to sock_queue_rcv_skb only.  The only reason we added
the lock is for the accounting which happens in that function.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cff502a3
...@@ -951,6 +951,27 @@ int udp_disconnect(struct sock *sk, int flags) ...@@ -951,6 +951,27 @@ int udp_disconnect(struct sock *sk, int flags)
return 0; return 0;
} }
static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
int is_udplite = IS_UDPLITE(sk);
int rc;
if ((rc = sock_queue_rcv_skb(sk, skb)) < 0) {
/* Note that an ENOMEM error is charged twice */
if (rc == -ENOMEM)
UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS,
is_udplite);
goto drop;
}
return 0;
drop:
UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
kfree_skb(skb);
return -1;
}
/* returns: /* returns:
* -1: error * -1: error
* 0: success * 0: success
...@@ -989,9 +1010,7 @@ int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) ...@@ -989,9 +1010,7 @@ int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
up->encap_rcv != NULL) { up->encap_rcv != NULL) {
int ret; int ret;
bh_unlock_sock(sk);
ret = (*up->encap_rcv)(sk, skb); ret = (*up->encap_rcv)(sk, skb);
bh_lock_sock(sk);
if (ret <= 0) { if (ret <= 0) {
UDP_INC_STATS_BH(sock_net(sk), UDP_INC_STATS_BH(sock_net(sk),
UDP_MIB_INDATAGRAMS, UDP_MIB_INDATAGRAMS,
...@@ -1044,17 +1063,16 @@ int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) ...@@ -1044,17 +1063,16 @@ int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
goto drop; goto drop;
} }
if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) { rc = 0;
/* Note that an ENOMEM error is charged twice */
if (rc == -ENOMEM) {
UDP_INC_STATS_BH(sock_net(sk),
UDP_MIB_RCVBUFERRORS, is_udplite);
atomic_inc(&sk->sk_drops);
}
goto drop;
}
return 0; bh_lock_sock(sk);
if (!sock_owned_by_user(sk))
rc = __udp_queue_rcv_skb(sk, skb);
else
sk_add_backlog(sk, skb);
bh_unlock_sock(sk);
return rc;
drop: drop:
UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_INERRORS, is_udplite); UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
...@@ -1092,15 +1110,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, ...@@ -1092,15 +1110,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
skb1 = skb_clone(skb, GFP_ATOMIC); skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1) { if (skb1) {
int ret = 0; int ret = udp_queue_rcv_skb(sk, skb1);
bh_lock_sock(sk);
if (!sock_owned_by_user(sk))
ret = udp_queue_rcv_skb(sk, skb1);
else
sk_add_backlog(sk, skb1);
bh_unlock_sock(sk);
if (ret > 0) if (ret > 0)
/* we should probably re-process instead /* we should probably re-process instead
* of dropping packets here. */ * of dropping packets here. */
...@@ -1195,13 +1205,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], ...@@ -1195,13 +1205,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
uh->dest, inet_iif(skb), udptable); uh->dest, inet_iif(skb), udptable);
if (sk != NULL) { if (sk != NULL) {
int ret = 0; int ret = udp_queue_rcv_skb(sk, skb);
bh_lock_sock(sk);
if (!sock_owned_by_user(sk))
ret = udp_queue_rcv_skb(sk, skb);
else
sk_add_backlog(sk, skb);
bh_unlock_sock(sk);
sock_put(sk); sock_put(sk);
/* a return value > 0 means to resubmit the input, but /* a return value > 0 means to resubmit the input, but
...@@ -1494,7 +1498,7 @@ struct proto udp_prot = { ...@@ -1494,7 +1498,7 @@ struct proto udp_prot = {
.sendmsg = udp_sendmsg, .sendmsg = udp_sendmsg,
.recvmsg = udp_recvmsg, .recvmsg = udp_recvmsg,
.sendpage = udp_sendpage, .sendpage = udp_sendpage,
.backlog_rcv = udp_queue_rcv_skb, .backlog_rcv = __udp_queue_rcv_skb,
.hash = udp_lib_hash, .hash = udp_lib_hash,
.unhash = udp_lib_unhash, .unhash = udp_lib_unhash,
.get_port = udp_v4_get_port, .get_port = udp_v4_get_port,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment