Skip to content

Commit

Permalink
ip: Report qdisc packet drops
Browse files Browse the repository at this point in the history
Christoph Lameter pointed out that packet drops at qdisc level where not
accounted in SNMP counters. Only if application sets IP_RECVERR, drops
are reported to user (-ENOBUFS errors) and SNMP counters updated.

IP_RECVERR is used to enable extended reliable error message passing,
but these are not needed to update system wide SNMP stats.

This patch changes things a bit to allow SNMP counters to be updated,
regardless of IP_RECVERR being set or not on the socket.

Example after an UDP tx flood
# netstat -s 
...
IP:
    1487048 outgoing packets dropped
...
Udp:
...
    SndbufErrors: 1487048


send() syscalls, do however still return an OK status, to not
break applications.

Note : send() manual page explicitly says for -ENOBUFS error :

 "The output queue for a network interface was full.
  This generally indicates that the interface has stopped sending,
  but may be caused by transient congestion.
  (Normally, this does not occur in Linux. Packets are just silently
  dropped when a device queue overflows.) "

This is not true for IP_RECVERR enabled sockets : a send() syscall
that hit a qdisc drop returns an ENOBUFS error.

Many thanks to Christoph, David, and last but not least, Alexey !

Signed-off-by: Eric Dumazet <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Eric Dumazet authored and davem330 committed Sep 3, 2009
1 parent 2e59af3 commit 6ce9e7b
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 11 deletions.
2 changes: 1 addition & 1 deletion net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,7 @@ int ip_push_pending_frames(struct sock *sk)
err = ip_local_out(skb);
if (err) {
if (err > 0)
err = inet->recverr ? net_xmit_errno(err) : 0;
err = net_xmit_errno(err);
if (err)
goto error;
}
Expand Down
9 changes: 7 additions & 2 deletions net/ipv4/raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
err = NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
if (err > 0)
err = inet->recverr ? net_xmit_errno(err) : 0;
err = net_xmit_errno(err);
if (err)
goto error;
out:
Expand All @@ -386,6 +386,8 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
kfree_skb(skb);
error:
IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
if (err == -ENOBUFS && !inet->recverr)
err = 0;
return err;
}

Expand Down Expand Up @@ -576,8 +578,11 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
&ipc, &rt, msg->msg_flags);
if (err)
ip_flush_pending_frames(sk);
else if (!(msg->msg_flags & MSG_MORE))
else if (!(msg->msg_flags & MSG_MORE)) {
err = ip_push_pending_frames(sk);
if (err == -ENOBUFS && !inet->recverr)
err = 0;
}
release_sock(sk);
}
done:
Expand Down
12 changes: 9 additions & 3 deletions net/ipv4/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,12 +561,18 @@ static int udp_push_pending_frames(struct sock *sk)

send:
err = ip_push_pending_frames(sk);
if (err) {
if (err == -ENOBUFS && !inet->recverr) {
UDP_INC_STATS_USER(sock_net(sk),
UDP_MIB_SNDBUFERRORS, is_udplite);
err = 0;
}
} else
UDP_INC_STATS_USER(sock_net(sk),
UDP_MIB_OUTDATAGRAMS, is_udplite);
out:
up->len = 0;
up->pending = 0;
if (!err)
UDP_INC_STATS_USER(sock_net(sk),
UDP_MIB_OUTDATAGRAMS, is_udplite);
return err;
}

Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/ip6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -1511,7 +1511,7 @@ int ip6_push_pending_frames(struct sock *sk)
err = ip6_local_out(skb);
if (err) {
if (err > 0)
err = np->recverr ? net_xmit_errno(err) : 0;
err = net_xmit_errno(err);
if (err)
goto error;
}
Expand Down
4 changes: 3 additions & 1 deletion net/ipv6/raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
if (err > 0)
err = np->recverr ? net_xmit_errno(err) : 0;
err = net_xmit_errno(err);
if (err)
goto error;
out:
Expand All @@ -653,6 +653,8 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
kfree_skb(skb);
error:
IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
if (err == -ENOBUFS && !np->recverr)
err = 0;
return err;
}

Expand Down
12 changes: 9 additions & 3 deletions net/ipv6/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,12 +724,18 @@ static int udp_v6_push_pending_frames(struct sock *sk)

send:
err = ip6_push_pending_frames(sk);
if (err) {
if (err == -ENOBUFS && !inet6_sk(sk)->recverr) {
UDP6_INC_STATS_USER(sock_net(sk),
UDP_MIB_SNDBUFERRORS, is_udplite);
err = 0;
}
} else
UDP6_INC_STATS_USER(sock_net(sk),
UDP_MIB_OUTDATAGRAMS, is_udplite);
out:
up->len = 0;
up->pending = 0;
if (!err)
UDP6_INC_STATS_USER(sock_net(sk),
UDP_MIB_OUTDATAGRAMS, is_udplite);
return err;
}

Expand Down

0 comments on commit 6ce9e7b

Please sign in to comment.