Skip to content

Commit 364a9e9

Browse files
wdebruijdavem330
authored andcommitted
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>
1 parent 8fe2f76 commit 364a9e9

File tree

6 files changed

+28
-51
lines changed

6 files changed

+28
-51
lines changed

include/net/sock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,7 @@ void sk_stop_timer(struct sock *sk, struct timer_list *timer);
20412041
int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
20422042

20432043
int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb);
2044+
struct sk_buff *sock_dequeue_err_skb(struct sock *sk);
20442045

20452046
/*
20462047
* Recover an error report and clear atomically

net/core/skbuff.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,6 +3491,26 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
34913491
}
34923492
EXPORT_SYMBOL(sock_queue_err_skb);
34933493

3494+
struct sk_buff *sock_dequeue_err_skb(struct sock *sk)
3495+
{
3496+
struct sk_buff_head *q = &sk->sk_error_queue;
3497+
struct sk_buff *skb, *skb_next;
3498+
int err = 0;
3499+
3500+
spin_lock_bh(&q->lock);
3501+
skb = __skb_dequeue(q);
3502+
if (skb && (skb_next = skb_peek(q)))
3503+
err = SKB_EXT_ERR(skb_next)->ee.ee_errno;
3504+
spin_unlock_bh(&q->lock);
3505+
3506+
sk->sk_err = err;
3507+
if (err)
3508+
sk->sk_error_report(sk);
3509+
3510+
return skb;
3511+
}
3512+
EXPORT_SYMBOL(sock_dequeue_err_skb);
3513+
34943514
void __skb_tstamp_tx(struct sk_buff *orig_skb,
34953515
struct skb_shared_hwtstamps *hwtstamps,
34963516
struct sock *sk, int tstype)

net/core/sock.c

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2488,11 +2488,11 @@ int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len,
24882488
int level, int type)
24892489
{
24902490
struct sock_exterr_skb *serr;
2491-
struct sk_buff *skb, *skb2;
2491+
struct sk_buff *skb;
24922492
int copied, err;
24932493

24942494
err = -EAGAIN;
2495-
skb = skb_dequeue(&sk->sk_error_queue);
2495+
skb = sock_dequeue_err_skb(sk);
24962496
if (skb == NULL)
24972497
goto out;
24982498

@@ -2513,16 +2513,6 @@ int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len,
25132513
msg->msg_flags |= MSG_ERRQUEUE;
25142514
err = copied;
25152515

2516-
/* Reset and regenerate socket error */
2517-
spin_lock_bh(&sk->sk_error_queue.lock);
2518-
sk->sk_err = 0;
2519-
if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) {
2520-
sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
2521-
spin_unlock_bh(&sk->sk_error_queue.lock);
2522-
sk->sk_error_report(sk);
2523-
} else
2524-
spin_unlock_bh(&sk->sk_error_queue.lock);
2525-
25262516
out_free_skb:
25272517
kfree_skb(skb);
25282518
out:

net/ipv4/ip_sockglue.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 inf
405405
int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
406406
{
407407
struct sock_exterr_skb *serr;
408-
struct sk_buff *skb, *skb2;
408+
struct sk_buff *skb;
409409
DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
410410
struct {
411411
struct sock_extended_err ee;
@@ -415,7 +415,7 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
415415
int copied;
416416

417417
err = -EAGAIN;
418-
skb = skb_dequeue(&sk->sk_error_queue);
418+
skb = sock_dequeue_err_skb(sk);
419419
if (skb == NULL)
420420
goto out;
421421

@@ -462,17 +462,6 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
462462
msg->msg_flags |= MSG_ERRQUEUE;
463463
err = copied;
464464

465-
/* Reset and regenerate socket error */
466-
spin_lock_bh(&sk->sk_error_queue.lock);
467-
sk->sk_err = 0;
468-
skb2 = skb_peek(&sk->sk_error_queue);
469-
if (skb2 != NULL) {
470-
sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
471-
spin_unlock_bh(&sk->sk_error_queue.lock);
472-
sk->sk_error_report(sk);
473-
} else
474-
spin_unlock_bh(&sk->sk_error_queue.lock);
475-
476465
out_free_skb:
477466
kfree_skb(skb);
478467
out:

net/ipv6/datagram.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
332332
{
333333
struct ipv6_pinfo *np = inet6_sk(sk);
334334
struct sock_exterr_skb *serr;
335-
struct sk_buff *skb, *skb2;
335+
struct sk_buff *skb;
336336
DECLARE_SOCKADDR(struct sockaddr_in6 *, sin, msg->msg_name);
337337
struct {
338338
struct sock_extended_err ee;
@@ -342,7 +342,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
342342
int copied;
343343

344344
err = -EAGAIN;
345-
skb = skb_dequeue(&sk->sk_error_queue);
345+
skb = sock_dequeue_err_skb(sk);
346346
if (skb == NULL)
347347
goto out;
348348

@@ -415,17 +415,6 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
415415
msg->msg_flags |= MSG_ERRQUEUE;
416416
err = copied;
417417

418-
/* Reset and regenerate socket error */
419-
spin_lock_bh(&sk->sk_error_queue.lock);
420-
sk->sk_err = 0;
421-
if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) {
422-
sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
423-
spin_unlock_bh(&sk->sk_error_queue.lock);
424-
sk->sk_error_report(sk);
425-
} else {
426-
spin_unlock_bh(&sk->sk_error_queue.lock);
427-
}
428-
429418
out_free_skb:
430419
kfree_skb(skb);
431420
out:

net/rxrpc/ar-error.c

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void rxrpc_UDP_error_report(struct sock *sk)
3737

3838
_enter("%p{%d}", sk, local->debug_id);
3939

40-
skb = skb_dequeue(&sk->sk_error_queue);
40+
skb = sock_dequeue_err_skb(sk);
4141
if (!skb) {
4242
_leave("UDP socket errqueue empty");
4343
return;
@@ -111,18 +111,6 @@ void rxrpc_UDP_error_report(struct sock *sk)
111111
skb_queue_tail(&trans->error_queue, skb);
112112
rxrpc_queue_work(&trans->error_handler);
113113

114-
/* reset and regenerate socket error */
115-
spin_lock_bh(&sk->sk_error_queue.lock);
116-
sk->sk_err = 0;
117-
skb = skb_peek(&sk->sk_error_queue);
118-
if (skb) {
119-
sk->sk_err = SKB_EXT_ERR(skb)->ee.ee_errno;
120-
spin_unlock_bh(&sk->sk_error_queue.lock);
121-
sk->sk_error_report(sk);
122-
} else {
123-
spin_unlock_bh(&sk->sk_error_queue.lock);
124-
}
125-
126114
_leave("");
127115
}
128116

0 commit comments

Comments
 (0)