diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2008-08-15 18:13:53 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2008-08-15 22:52:30 -0400 |
| commit | db543c1f973cd1d557cc32ceee76737c1e4d2898 (patch) | |
| tree | a87f3d359895b08648362f785a01559adbb30b24 | |
| parent | e3b99556975907530aeb9745e7b3945a0da48f17 (diff) | |
net: skb_copy_datagram_from_iovec()
There's an skb_copy_datagram_iovec() to copy out of a paged skb, but
nothing the other way around (because we don't do that).
We want to allocate big skbs in tun.c, so let's add the function.
It's a carbon copy of skb_copy_datagram_iovec() with enough changes to
be annoying.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/linux/skbuff.h | 4 | ||||
| -rw-r--r-- | net/core/datagram.c | 87 |
2 files changed, 91 insertions, 0 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 358661c9990e..909923717830 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
| @@ -1452,6 +1452,10 @@ extern int skb_copy_datagram_iovec(const struct sk_buff *from, | |||
| 1452 | extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, | 1452 | extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, |
| 1453 | int hlen, | 1453 | int hlen, |
| 1454 | struct iovec *iov); | 1454 | struct iovec *iov); |
| 1455 | extern int skb_copy_datagram_from_iovec(struct sk_buff *skb, | ||
| 1456 | int offset, | ||
| 1457 | struct iovec *from, | ||
| 1458 | int len); | ||
| 1455 | extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb); | 1459 | extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb); |
| 1456 | extern int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, | 1460 | extern int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, |
| 1457 | unsigned int flags); | 1461 | unsigned int flags); |
diff --git a/net/core/datagram.c b/net/core/datagram.c index dd61dcad6019..52f577a0f544 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
| @@ -339,6 +339,93 @@ fault: | |||
| 339 | return -EFAULT; | 339 | return -EFAULT; |
| 340 | } | 340 | } |
| 341 | 341 | ||
| 342 | /** | ||
| 343 | * skb_copy_datagram_from_iovec - Copy a datagram from an iovec. | ||
| 344 | * @skb: buffer to copy | ||
| 345 | * @offset: offset in the buffer to start copying to | ||
| 346 | * @from: io vector to copy to | ||
| 347 | * @len: amount of data to copy to buffer from iovec | ||
| 348 | * | ||
| 349 | * Returns 0 or -EFAULT. | ||
| 350 | * Note: the iovec is modified during the copy. | ||
| 351 | */ | ||
| 352 | int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, | ||
| 353 | struct iovec *from, int len) | ||
| 354 | { | ||
| 355 | int start = skb_headlen(skb); | ||
| 356 | int i, copy = start - offset; | ||
| 357 | |||
| 358 | /* Copy header. */ | ||
| 359 | if (copy > 0) { | ||
| 360 | if (copy > len) | ||
| 361 | copy = len; | ||
| 362 | if (memcpy_fromiovec(skb->data + offset, from, copy)) | ||
| 363 | goto fault; | ||
| 364 | if ((len -= copy) == 0) | ||
| 365 | return 0; | ||
| 366 | offset += copy; | ||
| 367 | } | ||
| 368 | |||
| 369 | /* Copy paged appendix. Hmm... why does this look so complicated? */ | ||
| 370 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { | ||
| 371 | int end; | ||
| 372 | |||
| 373 | WARN_ON(start > offset + len); | ||
| 374 | |||
| 375 | end = start + skb_shinfo(skb)->frags[i].size; | ||
| 376 | if ((copy = end - offset) > 0) { | ||
| 377 | int err; | ||
| 378 | u8 *vaddr; | ||
| 379 | skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; | ||
| 380 | struct page *page = frag->page; | ||
| 381 | |||
| 382 | if (copy > len) | ||
| 383 | copy = len; | ||
| 384 | vaddr = kmap(page); | ||
| 385 | err = memcpy_fromiovec(vaddr + frag->page_offset + | ||
| 386 | offset - start, from, copy); | ||
| 387 | kunmap(page); | ||
| 388 | if (err) | ||
| 389 | goto fault; | ||
| 390 | |||
| 391 | if (!(len -= copy)) | ||
| 392 | return 0; | ||
| 393 | offset += copy; | ||
| 394 | } | ||
| 395 | start = end; | ||
| 396 | } | ||
| 397 | |||
| 398 | if (skb_shinfo(skb)->frag_list) { | ||
| 399 | struct sk_buff *list = skb_shinfo(skb)->frag_list; | ||
| 400 | |||
| 401 | for (; list; list = list->next) { | ||
| 402 | int end; | ||
| 403 | |||
| 404 | WARN_ON(start > offset + len); | ||
| 405 | |||
| 406 | end = start + list->len; | ||
| 407 | if ((copy = end - offset) > 0) { | ||
| 408 | if (copy > len) | ||
| 409 | copy = len; | ||
| 410 | if (skb_copy_datagram_from_iovec(list, | ||
| 411 | offset - start, | ||
| 412 | from, copy)) | ||
| 413 | goto fault; | ||
| 414 | if ((len -= copy) == 0) | ||
| 415 | return 0; | ||
| 416 | offset += copy; | ||
| 417 | } | ||
| 418 | start = end; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | if (!len) | ||
| 422 | return 0; | ||
| 423 | |||
| 424 | fault: | ||
| 425 | return -EFAULT; | ||
| 426 | } | ||
| 427 | EXPORT_SYMBOL(skb_copy_datagram_from_iovec); | ||
| 428 | |||
| 342 | static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, | 429 | static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, |
| 343 | u8 __user *to, int len, | 430 | u8 __user *to, int len, |
| 344 | __wsum *csump) | 431 | __wsum *csump) |
