diff --git a/include/linux/snmp.h b/include/linux/snmp.h index a33f70fa1bf..71f348970d5 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -48,6 +48,7 @@ enum IPSTATS_MIB_OUTMCASTOCTETS, /* OutMcastOctets */ IPSTATS_MIB_INBCASTOCTETS, /* InBcastOctets */ IPSTATS_MIB_OUTBCASTOCTETS, /* OutBcastOctets */ + LINUX_MIB_TCPWQUEUETOOBIG, /* TCPWqueueTooBig */ __IPSTATS_MIB_MAX }; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 212897517b8..13a6356c011 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -259,6 +259,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPRcvCoalesce", LINUX_MIB_TCPRCVCOALESCE), SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK), SNMP_MIB_ITEM("TCPSYNChallenge", LINUX_MIB_TCPSYNCHALLENGE), + SNMP_MIB_ITEM("TCPWqueueTooBig", LINUX_MIB_TCPWQUEUETOOBIG), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 2a6d74559c7..916d192d2e2 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1017,6 +1017,11 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, if (nsize < 0) nsize = 0; + if (unlikely((sk->sk_wmem_queued >> 1) > sk->sk_sndbuf)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPWQUEUETOOBIG); + return -ENOMEM; + } + if (skb_unclone(skb, GFP_ATOMIC)) return -ENOMEM;