sock: Introduce the SO_PEEK_OFF sock option
Pavel Emelyanov [Tue, 21 Feb 2012 07:31:34 +0000 (07:31 +0000)]
This one specifies where to start MSG_PEEK-ing queue data from. When
set to negative value means that MSG_PEEK works as ususally -- peeks
from the head of the queue always.

When some bytes are peeked from queue and the peeking offset is non
negative it is moved forward so that the next peek will return next
portion of data.

When non-peeking recvmsg occurs and the peeking offset is non negative
is is moved backward so that the next peek will still peek the proper
data (i.e. the one that would have been picked if there were no non
peeking recv in between).

The offset is set using per-proto opteration to let the protocol handle
the locking issues and to check whether the peeking offset feature is
supported by the protocol the socket belongs to.

Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

20 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/net.h
include/net/sock.h
net/core/sock.c

index 082355f..16449d3 100644 (file)
@@ -71,6 +71,7 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
index dec6f9a..d958c74 100644 (file)
@@ -64,5 +64,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_SOCKET_H */
index 247b88c..30078f9 100644 (file)
@@ -64,5 +64,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* __ASM_AVR32_SOCKET_H */
index e269264..048aba6 100644 (file)
@@ -66,6 +66,7 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_SOCKET_H */
 
index ce80fda..7a36181 100644 (file)
@@ -64,6 +64,7 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_SOCKET_H */
 
index cf1daab..e7bbfce 100644 (file)
@@ -64,5 +64,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_SOCKET_H */
index 4b03664..ced62de 100644 (file)
@@ -73,5 +73,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_IA64_SOCKET_H */
index e8b8c5b..696cb4c 100644 (file)
@@ -64,5 +64,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_M32R_SOCKET_H */
index d4708ce..e8b41a6 100644 (file)
@@ -64,5 +64,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_SOCKET_H */
index ad5c0a7..5210487 100644 (file)
@@ -84,6 +84,7 @@ To add: #define SO_REUSEPORT 0x0200   /* Allow local address and port reuse.  */
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #ifdef __KERNEL__
 
index 876356d..013fcc5 100644 (file)
@@ -64,5 +64,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_SOCKET_H */
index d28c51b..f717c9b 100644 (file)
@@ -63,6 +63,7 @@
 
 #define SO_WIFI_STATUS         0x4022
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            0x4023
 
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
index 2fc2af8..fe1c0b4 100644 (file)
@@ -71,5 +71,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_POWERPC_SOCKET_H */
index 67b5c1b..581702f 100644 (file)
@@ -72,5 +72,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _ASM_SOCKET_H */
index 8af1b64..68e2e27 100644 (file)
@@ -60,6 +60,7 @@
 
 #define SO_WIFI_STATUS         0x0025
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            0x0026
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
index bb06968..74818b1 100644 (file)
@@ -75,5 +75,6 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS                SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 
 #endif /* _XTENSA_SOCKET_H */
index 49c1704..d9aaac0 100644 (file)
@@ -67,4 +67,5 @@
 
 #define SO_WIFI_STATUS         41
 #define SCM_WIFI_STATUS        SO_WIFI_STATUS
+#define SO_PEEK_OFF            42
 #endif /* __ASM_GENERIC_SOCKET_H */
index b299230..be60c7f 100644 (file)
@@ -206,6 +206,7 @@ struct proto_ops {
                                      int offset, size_t size, int flags);
        ssize_t         (*splice_read)(struct socket *sock,  loff_t *ppos,
                                       struct pipe_inode_info *pipe, size_t len, unsigned int flags);
+       void            (*set_peek_off)(struct sock *sk, int val);
 };
 
 #define DECLARE_SOCKADDR(type, dst, src)       \
index 91c1c8b..9c0553b 100644 (file)
@@ -357,6 +357,7 @@ struct sock {
        struct page             *sk_sndmsg_page;
        struct sk_buff          *sk_send_head;
        __u32                   sk_sndmsg_off;
+       __s32                   sk_peek_off;
        int                     sk_write_pending;
 #ifdef CONFIG_SECURITY
        void                    *sk_security;
@@ -373,6 +374,30 @@ struct sock {
        void                    (*sk_destruct)(struct sock *sk);
 };
 
+static inline int sk_peek_offset(struct sock *sk, int flags)
+{
+       if ((flags & MSG_PEEK) && (sk->sk_peek_off >= 0))
+               return sk->sk_peek_off;
+       else
+               return 0;
+}
+
+static inline void sk_peek_offset_bwd(struct sock *sk, int val)
+{
+       if (sk->sk_peek_off >= 0) {
+               if (sk->sk_peek_off >= val)
+                       sk->sk_peek_off -= val;
+               else
+                       sk->sk_peek_off = 0;
+       }
+}
+
+static inline void sk_peek_offset_fwd(struct sock *sk, int val)
+{
+       if (sk->sk_peek_off >= 0)
+               sk->sk_peek_off += val;
+}
+
 /*
  * Hashed lists helper routines
  */
index 02f8dfe..19942d4 100644 (file)
@@ -793,6 +793,12 @@ set_rcvbuf:
                sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool);
                break;
 
+       case SO_PEEK_OFF:
+               if (sock->ops->set_peek_off)
+                       sock->ops->set_peek_off(sk, val);
+               else
+                       ret = -EOPNOTSUPP;
+               break;
        default:
                ret = -ENOPROTOOPT;
                break;
@@ -1018,6 +1024,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                v.val = !!sock_flag(sk, SOCK_WIFI_STATUS);
                break;
 
+       case SO_PEEK_OFF:
+               if (!sock->ops->set_peek_off)
+                       return -EOPNOTSUPP;
+
+               v.val = sk->sk_peek_off;
+               break;
        default:
                return -ENOPROTOOPT;
        }
@@ -2092,6 +2104,7 @@ void sock_init_data(struct socket *sock, struct sock *sk)
 
        sk->sk_sndmsg_page      =       NULL;
        sk->sk_sndmsg_off       =       0;
+       sk->sk_peek_off         =       -1;
 
        sk->sk_peer_pid         =       NULL;
        sk->sk_peer_cred        =       NULL;