diff options
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/datagram.c | 22 |
1 files changed, 14 insertions, 8 deletions
diff --git a/net/core/datagram.c b/net/core/datagram.c index ebba65d7e0da..b71423db7785 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
@@ -78,9 +78,10 @@ static int receiver_wake_function(wait_queue_t *wait, unsigned int mode, int syn | |||
78 | return autoremove_wake_function(wait, mode, sync, key); | 78 | return autoremove_wake_function(wait, mode, sync, key); |
79 | } | 79 | } |
80 | /* | 80 | /* |
81 | * Wait for a packet.. | 81 | * Wait for the last received packet to be different from skb |
82 | */ | 82 | */ |
83 | static int wait_for_packet(struct sock *sk, int *err, long *timeo_p) | 83 | static int wait_for_more_packets(struct sock *sk, int *err, long *timeo_p, |
84 | const struct sk_buff *skb) | ||
84 | { | 85 | { |
85 | int error; | 86 | int error; |
86 | DEFINE_WAIT_FUNC(wait, receiver_wake_function); | 87 | DEFINE_WAIT_FUNC(wait, receiver_wake_function); |
@@ -92,7 +93,7 @@ static int wait_for_packet(struct sock *sk, int *err, long *timeo_p) | |||
92 | if (error) | 93 | if (error) |
93 | goto out_err; | 94 | goto out_err; |
94 | 95 | ||
95 | if (!skb_queue_empty(&sk->sk_receive_queue)) | 96 | if (sk->sk_receive_queue.prev != skb) |
96 | goto out; | 97 | goto out; |
97 | 98 | ||
98 | /* Socket shut down? */ | 99 | /* Socket shut down? */ |
@@ -131,9 +132,9 @@ out_noerr: | |||
131 | * __skb_recv_datagram - Receive a datagram skbuff | 132 | * __skb_recv_datagram - Receive a datagram skbuff |
132 | * @sk: socket | 133 | * @sk: socket |
133 | * @flags: MSG_ flags | 134 | * @flags: MSG_ flags |
135 | * @peeked: returns non-zero if this packet has been seen before | ||
134 | * @off: an offset in bytes to peek skb from. Returns an offset | 136 | * @off: an offset in bytes to peek skb from. Returns an offset |
135 | * within an skb where data actually starts | 137 | * within an skb where data actually starts |
136 | * @peeked: returns non-zero if this packet has been seen before | ||
137 | * @err: error code returned | 138 | * @err: error code returned |
138 | * | 139 | * |
139 | * Get a datagram skbuff, understands the peeking, nonblocking wakeups | 140 | * Get a datagram skbuff, understands the peeking, nonblocking wakeups |
@@ -161,7 +162,7 @@ out_noerr: | |||
161 | struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, | 162 | struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, |
162 | int *peeked, int *off, int *err) | 163 | int *peeked, int *off, int *err) |
163 | { | 164 | { |
164 | struct sk_buff *skb; | 165 | struct sk_buff *skb, *last; |
165 | long timeo; | 166 | long timeo; |
166 | /* | 167 | /* |
167 | * Caller is allowed not to check sk->sk_err before skb_recv_datagram() | 168 | * Caller is allowed not to check sk->sk_err before skb_recv_datagram() |
@@ -182,13 +183,17 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, | |||
182 | */ | 183 | */ |
183 | unsigned long cpu_flags; | 184 | unsigned long cpu_flags; |
184 | struct sk_buff_head *queue = &sk->sk_receive_queue; | 185 | struct sk_buff_head *queue = &sk->sk_receive_queue; |
186 | int _off = *off; | ||
185 | 187 | ||
188 | last = (struct sk_buff *)queue; | ||
186 | spin_lock_irqsave(&queue->lock, cpu_flags); | 189 | spin_lock_irqsave(&queue->lock, cpu_flags); |
187 | skb_queue_walk(queue, skb) { | 190 | skb_queue_walk(queue, skb) { |
191 | last = skb; | ||
188 | *peeked = skb->peeked; | 192 | *peeked = skb->peeked; |
189 | if (flags & MSG_PEEK) { | 193 | if (flags & MSG_PEEK) { |
190 | if (*off >= skb->len && skb->len) { | 194 | if (_off >= skb->len && (skb->len || _off || |
191 | *off -= skb->len; | 195 | skb->peeked)) { |
196 | _off -= skb->len; | ||
192 | continue; | 197 | continue; |
193 | } | 198 | } |
194 | skb->peeked = 1; | 199 | skb->peeked = 1; |
@@ -197,6 +202,7 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, | |||
197 | __skb_unlink(skb, queue); | 202 | __skb_unlink(skb, queue); |
198 | 203 | ||
199 | spin_unlock_irqrestore(&queue->lock, cpu_flags); | 204 | spin_unlock_irqrestore(&queue->lock, cpu_flags); |
205 | *off = _off; | ||
200 | return skb; | 206 | return skb; |
201 | } | 207 | } |
202 | spin_unlock_irqrestore(&queue->lock, cpu_flags); | 208 | spin_unlock_irqrestore(&queue->lock, cpu_flags); |
@@ -206,7 +212,7 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, | |||
206 | if (!timeo) | 212 | if (!timeo) |
207 | goto no_packet; | 213 | goto no_packet; |
208 | 214 | ||
209 | } while (!wait_for_packet(sk, err, &timeo)); | 215 | } while (!wait_for_more_packets(sk, err, &timeo, last)); |
210 | 216 | ||
211 | return NULL; | 217 | return NULL; |
212 | 218 | ||