@@ -66,5 +66,6 @@
#define SO_RXQ_OVFL 40
#define SO_WIFI_STATUS 41
+#define SO_PEEK_OFF 42
#define SCM_WIFI_STATUS SO_WIFI_STATUS
#endif /* __ASM_GENERIC_SOCKET_H */
@@ -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;
@@ -180,21 +180,37 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
* However, this function was correct in any case. 8)
*/
unsigned long cpu_flags;
+ long skip;
spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb) {
+ skip = sk->sk_peek_off;
+ skb_queue_walk(&sk->sk_receive_queue, skb) {
*peeked = skb->peeked;
if (flags & MSG_PEEK) {
skb->peeked = 1;
atomic_inc(&skb->users);
- } else
+ if (skip >= skb->len) {
+ skip -= skb->len;
+ continue;
+ }
+
+ if (sk->sk_peek_off >= 0)
+ sk->sk_peek_off += (skb->len - skip);
+ } else {
__skb_unlink(skb, &sk->sk_receive_queue);
- }
- spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
- if (skb)
+ if (sk->sk_peek_off >= 0) {
+ if (sk->sk_peek_off >= skb->len)
+ sk->sk_peek_off -= skb->len;
+ else
+ sk->sk_peek_off = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
return skb;
+ }
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
/* User doesn't want to wait */
error = -EAGAIN;
@@ -792,7 +792,9 @@ set_rcvbuf:
case SO_WIFI_STATUS:
sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool);
break;
-
+ case SO_PEEK_OFF:
+ sk->sk_peek_off = val;
+ break;
default:
ret = -ENOPROTOOPT;
break;
@@ -1017,6 +1019,9 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
case SO_WIFI_STATUS:
v.val = !!sock_flag(sk, SOCK_WIFI_STATUS);
break;
+ case SO_PEEK_OFF:
+ v.val = sk->sk_peek_off;
+ break;
default:
return -ENOPROTOOPT;
@@ -2092,6 +2097,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;