sock: deduplicate errqueue dequeue
sk->sk_error_queue is dequeued in four locations. All share the exact same logic. Deduplicate. Also collapse the two critical sections for dequeue (at the top of the recv handler) and signal (at the bottom). This moves signal generation for the next packet forward, which should be harmless. It also changes the behavior if the recv handler exits early with an error. Previously, a signal for follow-up packets on the errqueue would then not be scheduled. The new behavior, to always signal, is arguably a bug fix. For rxrpc, the change causes the same function to be called repeatedly for each queued packet (because the recv handler == sk_error_report). It is likely that all packets will fail for the same reason (e.g., memory exhaustion). This code runs without sk_lock held, so it is not safe to trust that sk->sk_err is immutable inbetween releasing q->lock and the subsequent test. Introduce int err just to avoid this potential race. Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
8fe2f761ca
commit
364a9e9324
@@ -332,7 +332,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
|
||||
{
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct sock_exterr_skb *serr;
|
||||
struct sk_buff *skb, *skb2;
|
||||
struct sk_buff *skb;
|
||||
DECLARE_SOCKADDR(struct sockaddr_in6 *, sin, msg->msg_name);
|
||||
struct {
|
||||
struct sock_extended_err ee;
|
||||
@@ -342,7 +342,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
|
||||
int copied;
|
||||
|
||||
err = -EAGAIN;
|
||||
skb = skb_dequeue(&sk->sk_error_queue);
|
||||
skb = sock_dequeue_err_skb(sk);
|
||||
if (skb == NULL)
|
||||
goto out;
|
||||
|
||||
@@ -415,17 +415,6 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
|
||||
msg->msg_flags |= MSG_ERRQUEUE;
|
||||
err = copied;
|
||||
|
||||
/* Reset and regenerate socket error */
|
||||
spin_lock_bh(&sk->sk_error_queue.lock);
|
||||
sk->sk_err = 0;
|
||||
if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) {
|
||||
sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
|
||||
spin_unlock_bh(&sk->sk_error_queue.lock);
|
||||
sk->sk_error_report(sk);
|
||||
} else {
|
||||
spin_unlock_bh(&sk->sk_error_queue.lock);
|
||||
}
|
||||
|
||||
out_free_skb:
|
||||
kfree_skb(skb);
|
||||
out:
|
||||
|
Reference in New Issue
Block a user