net: Generalize socket rx gap / receive queue overflow cmsg
authorNeil Horman <nhorman@tuxdriver.com>
Mon, 12 Oct 2009 20:26:31 +0000 (13:26 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 12 Oct 2009 20:26:31 +0000 (13:26 -0700)
Create a new socket level option to report number of queue overflows

Recently I augmented the AF_PACKET protocol to report the number of frames lost
on the socket receive queue between any two enqueued frames.  This value was
exported via a SOL_PACKET level cmsg.  AFter I completed that work it was
requested that this feature be generalized so that any datagram oriented socket
could make use of this option.  As such I've created this patch, It creates a
new SOL_SOCKET level option called SO_RXQ_OVFL, which when enabled exports a
SOL_SOCKET level cmsg that reports the nubmer of times the sk_receive_queue
overflowed between any two given frames.  It also augments the AF_PACKET
protocol to take advantage of this new feature (as it previously did not touch
sk->sk_drops, which this patch uses to record the overflow count).  Tested
successfully by me.

Notes:

1) Unlike my previous patch, this patch simply records the sk_drops value, which
is not a number of drops between packets, but rather a total number of drops.
Deltas must be computed in user space.

2) While this patch currently works with datagram oriented protocols, it will
also be accepted by non-datagram oriented protocols. I'm not sure if thats
agreeable to everyone, but my argument in favor of doing so is that, for those
protocols which aren't applicable to this option, sk_drops will always be zero,
and reporting no drops on a receive queue that isn't used for those
non-participating protocols seems reasonable to me.  This also saves us having
to code in a per-protocol opt in mechanism.

3) This applies cleanly to net-next assuming that commit
977750076d98c7ff6cbda51858bb5a5894a9d9ab (my af packet cmsg patch) is reverted

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
36 files changed:
arch/alpha/include/asm/socket.h
arch/arm/include/asm/socket.h
arch/avr32/include/asm/socket.h
arch/cris/include/asm/socket.h
arch/frv/include/asm/socket.h
arch/h8300/include/asm/socket.h
arch/ia64/include/asm/socket.h
arch/m32r/include/asm/socket.h
arch/m68k/include/asm/socket.h
arch/mips/include/asm/socket.h
arch/mn10300/include/asm/socket.h
arch/parisc/include/asm/socket.h
arch/powerpc/include/asm/socket.h
arch/s390/include/asm/socket.h
arch/sparc/include/asm/socket.h
arch/xtensa/include/asm/socket.h
include/asm-generic/socket.h
include/linux/skbuff.h
include/net/sock.h
net/atm/common.c
net/bluetooth/af_bluetooth.c
net/bluetooth/rfcomm/sock.c
net/can/bcm.c
net/can/raw.c
net/core/sock.c
net/ieee802154/dgram.c
net/ieee802154/raw.c
net/ipv4/raw.c
net/ipv4/udp.c
net/ipv6/raw.c
net/ipv6/udp.c
net/key/af_key.c
net/packet/af_packet.c
net/rxrpc/ar-recvmsg.c
net/sctp/socket.c
net/socket.c

index 26773e3246e2b6fab6e4c342068688247dfee239..06edfefc337332915833c7141fac17f2b1a60bba 100644 (file)
@@ -67,6 +67,8 @@
 #define SO_TIMESTAMPING                37
 #define SCM_TIMESTAMPING       SO_TIMESTAMPING
 
+#define SO_RXQ_OVFL             40
+
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
index 92ac61d294fd81bc33403990c2a520d51578d0dc..90ffd04b8e74fb191a47ab79211cf1e128ad7b87 100644 (file)
@@ -60,4 +60,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_SOCKET_H */
index fe863f9794d545489686d0e47c1fc53daac1bd63..c8d1fae494763c2459279afd824dc576b7471795 100644 (file)
@@ -60,4 +60,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* __ASM_AVR32_SOCKET_H */
index 45ec49bdb7b16d11f2aa58c006bb27b26874dcef..1a4a61909ca8705b3d0f11f186c717ba3b5141f1 100644 (file)
@@ -62,6 +62,8 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_SOCKET_H */
 
 
index 2dea726095c20ae1b2ec530d8769eaf9d4d03cb1..a6b26880c1ec2e79e316ff5a0f32bbc7fe09a79e 100644 (file)
@@ -60,5 +60,7 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_SOCKET_H */
 
index 1547f01c8e22decafe3370824ac1020c1c5fce1a..04c0f4596eb5b8ae072109677a35069a7c15d4a4 100644 (file)
@@ -60,4 +60,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_SOCKET_H */
index 0b0d5ff062e5f1f276f0ae1d71a2035cf923fdcd..51427eaa51ba996031aa40bf6800f35917a430b3 100644 (file)
@@ -69,4 +69,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_IA64_SOCKET_H */
index 3390a864f224f9f21cd6f62dbb5daf2eeb62ebdb..469787c30098aea921ddc988087162749840b3de 100644 (file)
@@ -60,4 +60,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_M32R_SOCKET_H */
index eee01cce921b8a259e7bf57aa407b4afc6b29704..9bf49c87d954a6c5ecf89cc2779ae8642dbe4f34 100644 (file)
@@ -60,4 +60,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_SOCKET_H */
index ae05accd9fe4594c4c1d6a9f7b6f6713843680c5..9de5190f248743a5c8a3926993ad88e5025d4437 100644 (file)
@@ -80,6 +80,8 @@ To add: #define SO_REUSEPORT 0x0200   /* Allow local address and port reuse.  */
 #define SO_TIMESTAMPING                37
 #define SCM_TIMESTAMPING       SO_TIMESTAMPING
 
+#define SO_RXQ_OVFL             40
+
 #ifdef __KERNEL__
 
 /** sock_type - Socket types
index 4df75af29d76cfa63b5b12900f88f50911cc8aa9..4e60c42812880b8e3dcc05ddaf90e1815cfc198c 100644 (file)
@@ -60,4 +60,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_SOCKET_H */
index 960b1e5d8e16152381af97335c487be07cae316e..225b7d6a1a0af44447521de4b133efa6be591bf8 100644 (file)
@@ -59,6 +59,8 @@
 #define SO_TIMESTAMPING                0x4020
 #define SCM_TIMESTAMPING       SO_TIMESTAMPING
 
+#define SO_RXQ_OVFL             0x4021
+
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
index 3ab8b3e6feb0545e57a13f4e8258238efcd22e2b..866f7606da6803924f17d71f74ac4ed70309f8f3 100644 (file)
@@ -67,4 +67,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_POWERPC_SOCKET_H */
index e42df89a0b85e644e037f4d3ea5114599ebb0820..fdff1e995c73d45145ceb56afcf0f26a948636e6 100644 (file)
@@ -68,4 +68,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _ASM_SOCKET_H */
index 3a5ae3d12088dd94e3b07c1a4378208660149a5f..9d3fefcff2f576b277ebc776c3bd7ddcada49272 100644 (file)
@@ -56,6 +56,8 @@
 #define SO_TIMESTAMPING                0x0023
 #define SCM_TIMESTAMPING       SO_TIMESTAMPING
 
+#define SO_RXQ_OVFL             0x0024
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index beb3a6bdb61d0dddf8c9abfb9aab6667b2116a1f..cbdf2ffaacff9cdc82afb41be8723a55b141c390 100644 (file)
@@ -71,4 +71,6 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
+
 #endif /* _XTENSA_SOCKET_H */
index 538991cef6f0300be0eb5407dcc90491677001b8..9a6115e7cf631583f808d6807619c0207c0a5a0c 100644 (file)
@@ -63,4 +63,5 @@
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
+#define SO_RXQ_OVFL             40
 #endif /* __ASM_GENERIC_SOCKET_H */
index df7b23ac66e6dac7610efe67fa95888f0f4e3ae2..8c866b5cb97b2d7ffdc0ab5fcc081f2f6c063c8a 100644 (file)
@@ -389,8 +389,10 @@ struct sk_buff {
 #ifdef CONFIG_NETWORK_SECMARK
        __u32                   secmark;
 #endif
-
-       __u32                   mark;
+       union {
+               __u32           mark;
+               __u32           dropcount;
+       };
 
        __u16                   vlan_tci;
 
index 98398bdec57d04a4bef71b3d22d7aa0f79ed703d..10669b01eeab8e74b0fbed1bdf2650af9db0841c 100644 (file)
@@ -505,6 +505,7 @@ enum sock_flags {
        SOCK_TIMESTAMPING_RAW_HARDWARE, /* %SOF_TIMESTAMPING_RAW_HARDWARE */
        SOCK_TIMESTAMPING_SYS_HARDWARE, /* %SOF_TIMESTAMPING_SYS_HARDWARE */
        SOCK_FASYNC, /* fasync() active */
+       SOCK_RXQ_OVFL,
 };
 
 static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
@@ -1493,6 +1494,8 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
                sk->sk_stamp = kt;
 }
 
+extern void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, struct sk_buff *skb);
+
 /**
  * sock_tx_timestamp - checks whether the outgoing packet is to be time stamped
  * @msg:       outgoing packet
index 950bd16d2383463851c1f3f919c254ac8f89fdae..d61e051e0a3f047c4a0f3ea1cce56d4c176b1b4e 100644 (file)
@@ -496,7 +496,7 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
        error = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
        if (error)
                return error;
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
        pr_debug("RcvM %d -= %d\n", atomic_read(&sk->sk_rmem_alloc), skb->truesize);
        atm_return(vcc, skb->truesize);
        skb_free_datagram(sk, skb);
index 1f6e49c1cde8dec2a75c09acc607a4a8d0ebaa3a..399e59c9c6cb6de5c3bae281ceae833b5ed53894 100644 (file)
@@ -257,7 +257,7 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        skb_reset_transport_header(skb);
        err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
        if (err == 0)
-               sock_recv_timestamp(msg, sk, skb);
+               sock_recv_ts_and_drops(msg, sk, skb);
 
        skb_free_datagram(sk, skb);
 
index c7078650385022ab232c5369e08cd7374e13b03b..d3bfc1b0afb14c65a170fea886fb2ceeaf368a35 100644 (file)
@@ -703,7 +703,7 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
                copied += chunk;
                size   -= chunk;
 
-               sock_recv_timestamp(msg, sk, skb);
+               sock_recv_ts_and_drops(msg, sk, skb);
 
                if (!(flags & MSG_PEEK)) {
                        atomic_sub(chunk, &sk->sk_rmem_alloc);
index 597da4f8f888f71397bc0d8df12379c7a43b8ef0..2f47039c79dd1f604f890fff01e1d40e7e84e369 100644 (file)
@@ -1534,7 +1534,7 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,
                return err;
        }
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        if (msg->msg_name) {
                msg->msg_namelen = sizeof(struct sockaddr_can);
index b5e897922d32e85986e5a2ea949f43fca3d32884..962fc9f1d0c7003deb909733c28f799f8fbdd38c 100644 (file)
@@ -702,7 +702,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
                return err;
        }
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        if (msg->msg_name) {
                msg->msg_namelen = sizeof(struct sockaddr_can);
index 7626b6aacd685551cf13bf5fe8fbbbbc6105e56f..43ca2c99539330fb68753c63d92939796db36cca 100644 (file)
@@ -276,6 +276,8 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
        int err = 0;
        int skb_len;
+       unsigned long flags;
+       struct sk_buff_head *list = &sk->sk_receive_queue;
 
        /* Cast sk->rcvbuf to unsigned... It's pointless, but reduces
           number of warnings when compiling with -W --ANK
@@ -305,7 +307,10 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
         */
        skb_len = skb->len;
 
-       skb_queue_tail(&sk->sk_receive_queue, skb);
+       spin_lock_irqsave(&list->lock, flags);
+       skb->dropcount = atomic_read(&sk->sk_drops);
+       __skb_queue_tail(list, skb);
+       spin_unlock_irqrestore(&list->lock, flags);
 
        if (!sock_flag(sk, SOCK_DEAD))
                sk->sk_data_ready(sk, skb_len);
@@ -702,6 +707,12 @@ set_rcvbuf:
 
                /* We implement the SO_SNDLOWAT etc to
                   not be settable (1003.1g 5.3) */
+       case SO_RXQ_OVFL:
+               if (valbool)
+                       sock_set_flag(sk, SOCK_RXQ_OVFL);
+               else
+                       sock_reset_flag(sk, SOCK_RXQ_OVFL);
+               break;
        default:
                ret = -ENOPROTOOPT;
                break;
@@ -901,6 +912,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                v.val = sk->sk_mark;
                break;
 
+       case SO_RXQ_OVFL:
+               v.val = !!sock_flag(sk, SOCK_RXQ_OVFL);
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
index a413b1bf4465828860d9052d7fad828330edce89..25ad956a39d8a2b4ffafb4baeb5d29ba9964f72f 100644 (file)
@@ -303,7 +303,7 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
        if (err)
                goto done;
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        if (flags & MSG_TRUNC)
                copied = skb->len;
index 30e74eee07d664755882ef4f16b4ecf901baf7c2..769c8d138fc3a8bf4911064d205a38ad212768e6 100644 (file)
@@ -191,7 +191,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        if (err)
                goto done;
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        if (flags & MSG_TRUNC)
                copied = skb->len;
index 757c9171e7c25b021c21d26a681d5238fa0017c1..f18172b07611235736f3def254a12a0591c8a790 100644 (file)
@@ -682,7 +682,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        if (err)
                goto done;
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        /* Copy the address. */
        if (sin) {
index 194bcdc6d9fca80d1bd007827b8a96409ae9993a..71e5353b30c80cfef44eed762a90d5676da09b2f 100644 (file)
@@ -955,7 +955,7 @@ try_again:
                UDP_INC_STATS_USER(sock_net(sk),
                                UDP_MIB_INDATAGRAMS, is_udplite);
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        /* Copy the address. */
        if (sin) {
index 4f24570b0869bd5093b18884866d4c67f20dcf41..d8375bc7f2d5530d4748be73193b12166d37c705 100644 (file)
@@ -497,7 +497,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
                        sin6->sin6_scope_id = IP6CB(skb)->iif;
        }
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        if (np->rxopt.all)
                datagram_recv_ctl(sk, msg, skb);
index ff778c172ef20b4b554d4f9d1f9e985874716135..1f8e2afa449019ef902914aca2baa130ae6fec70 100644 (file)
@@ -252,7 +252,7 @@ try_again:
                                        UDP_MIB_INDATAGRAMS, is_udplite);
        }
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        /* Copy the address. */
        if (msg->msg_name) {
index c078ae6e975b6ab5bfee6fcc5ede535b18878fb8..472f6594184ade7da3a7d37bb472e55084be30f5 100644 (file)
@@ -3606,7 +3606,7 @@ static int pfkey_recvmsg(struct kiocb *kiocb,
        if (err)
                goto out_free;
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        err = (flags & MSG_TRUNC) ? skb->len : copied;
 
index f87ed4803c111369029029ab20df874aecfd8c28..bf3a2954cd4d2c9a7ba0ce6e89bd5aaa25301b60 100644 (file)
@@ -627,15 +627,14 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
 
        spin_lock(&sk->sk_receive_queue.lock);
        po->stats.tp_packets++;
+       skb->dropcount = atomic_read(&sk->sk_drops);
        __skb_queue_tail(&sk->sk_receive_queue, skb);
        spin_unlock(&sk->sk_receive_queue.lock);
        sk->sk_data_ready(sk, skb->len);
        return 0;
 
 drop_n_acct:
-       spin_lock(&sk->sk_receive_queue.lock);
-       po->stats.tp_drops++;
-       spin_unlock(&sk->sk_receive_queue.lock);
+       po->stats.tp_drops = atomic_inc_return(&sk->sk_drops);
 
 drop_n_restore:
        if (skb_head != skb->data && skb_shared(skb)) {
@@ -1478,7 +1477,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (err)
                goto out_free;
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
 
        if (msg->msg_name)
                memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa,
index a39bf97f8830703cf1a6c5e23367727532e26efb..60c2b94e6b547855e52c92f21046e9786b767e59 100644 (file)
@@ -146,7 +146,7 @@ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock,
                                memcpy(msg->msg_name,
                                       &call->conn->trans->peer->srx,
                                       sizeof(call->conn->trans->peer->srx));
-                       sock_recv_timestamp(msg, &rx->sk, skb);
+                       sock_recv_ts_and_drops(msg, &rx->sk, skb);
                }
 
                /* receive the message */
index c8d05758661d96cf09c41f3babb983c4f2194ce9..0970e92c6acdb0e34863e16f4f243ee468f02b55 100644 (file)
@@ -1958,7 +1958,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
        if (err)
                goto out_free;
 
-       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_ts_and_drops(msg, sk, skb);
        if (sctp_ulpevent_is_notification(event)) {
                msg->msg_flags |= MSG_NOTIFICATION;
                sp->pf->event_msgname(event, msg->msg_name, addr_len);
index 954f3381cc8ae4ebed8f898bfb28c7de9dd91970..807935693846661cf182bf7e777b6cc77b48dde6 100644 (file)
@@ -668,6 +668,21 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
 
 EXPORT_SYMBOL_GPL(__sock_recv_timestamp);
 
+inline void sock_recv_drops(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
+{
+       if (sock_flag(sk, SOCK_RXQ_OVFL) && skb && skb->dropcount)
+               put_cmsg(msg, SOL_SOCKET, SO_RXQ_OVFL,
+                       sizeof(__u32), &skb->dropcount);
+}
+
+void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
+       struct sk_buff *skb)
+{
+       sock_recv_timestamp(msg, sk, skb);
+       sock_recv_drops(msg, sk, skb);
+}
+EXPORT_SYMBOL_GPL(sock_recv_ts_and_drops);
+
 static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock,
                                 struct msghdr *msg, size_t size, int flags)
 {