aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/core/datagram.c81
1 files changed, 53 insertions, 28 deletions
diff --git a/net/core/datagram.c b/net/core/datagram.c
index f8d322e1ea92..b8ce6bf81188 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -247,49 +247,74 @@ EXPORT_SYMBOL(skb_kill_datagram);
247int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, 247int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
248 struct iovec *to, int len) 248 struct iovec *to, int len)
249{ 249{
250 int i, err, fraglen, end = 0; 250 int start = skb_headlen(skb);
251 struct sk_buff *next = skb_shinfo(skb)->frag_list; 251 int i, copy = start - offset;
252 252
253 if (!len) 253 /* Copy header. */
254 return 0; 254 if (copy > 0) {
255 if (copy > len)
256 copy = len;
257 if (memcpy_toiovec(to, skb->data + offset, copy))
258 goto fault;
259 if ((len -= copy) == 0)
260 return 0;
261 offset += copy;
262 }
255 263
256next_skb: 264 /* Copy paged appendix. Hmm... why does this look so complicated? */
257 fraglen = skb_headlen(skb); 265 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
258 i = -1; 266 int end;
259 267
260 while (1) { 268 BUG_TRAP(start <= offset + len);
261 int start = end;
262 269
263 if ((end += fraglen) > offset) { 270 end = start + skb_shinfo(skb)->frags[i].size;
264 int copy = end - offset, o = offset - start; 271 if ((copy = end - offset) > 0) {
272 int err;
273 u8 *vaddr;
274 skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
275 struct page *page = frag->page;
265 276
266 if (copy > len) 277 if (copy > len)
267 copy = len; 278 copy = len;
268 if (i == -1) 279 vaddr = kmap(page);
269 err = memcpy_toiovec(to, skb->data + o, copy); 280 err = memcpy_toiovec(to, vaddr + frag->page_offset +
270 else { 281 offset - start, copy);
271 skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 282 kunmap(page);
272 struct page *page = frag->page;
273 void *p = kmap(page) + frag->page_offset + o;
274 err = memcpy_toiovec(to, p, copy);
275 kunmap(page);
276 }
277 if (err) 283 if (err)
278 goto fault; 284 goto fault;
279 if (!(len -= copy)) 285 if (!(len -= copy))
280 return 0; 286 return 0;
281 offset += copy; 287 offset += copy;
282 } 288 }
283 if (++i >= skb_shinfo(skb)->nr_frags) 289 start = end;
284 break;
285 fraglen = skb_shinfo(skb)->frags[i].size;
286 } 290 }
287 if (next) { 291
288 skb = next; 292 if (skb_shinfo(skb)->frag_list) {
289 BUG_ON(skb_shinfo(skb)->frag_list); 293 struct sk_buff *list = skb_shinfo(skb)->frag_list;
290 next = skb->next; 294
291 goto next_skb; 295 for (; list; list = list->next) {
296 int end;
297
298 BUG_TRAP(start <= offset + len);
299
300 end = start + list->len;
301 if ((copy = end - offset) > 0) {
302 if (copy > len)
303 copy = len;
304 if (skb_copy_datagram_iovec(list,
305 offset - start,
306 to, copy))
307 goto fault;
308 if ((len -= copy) == 0)
309 return 0;
310 offset += copy;
311 }
312 start = end;
313 }
292 } 314 }
315 if (!len)
316 return 0;
317
293fault: 318fault:
294 return -EFAULT; 319 return -EFAULT;
295} 320}