diff options
author | Jens Axboe <jens.axboe@oracle.com> | 2007-11-07 02:30:13 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 17:53:31 -0500 |
commit | 9c55e01c0cc835818475a6ce8c4d684df9949ac8 (patch) | |
tree | 1115311436677f837a4b477e3fd23c5e0ae184ef /net/ipv4 | |
parent | bbdfc2f70610bebb841d0874dc901c648308e43a (diff) |
[TCP]: Splice receive support.
Support for network splice receive.
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/af_inet.c | 1 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 129 |
2 files changed, 130 insertions, 0 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index d2f22e74b267..c75f20b49935 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -838,6 +838,7 @@ const struct proto_ops inet_stream_ops = { | |||
838 | .recvmsg = sock_common_recvmsg, | 838 | .recvmsg = sock_common_recvmsg, |
839 | .mmap = sock_no_mmap, | 839 | .mmap = sock_no_mmap, |
840 | .sendpage = tcp_sendpage, | 840 | .sendpage = tcp_sendpage, |
841 | .splice_read = tcp_splice_read, | ||
841 | #ifdef CONFIG_COMPAT | 842 | #ifdef CONFIG_COMPAT |
842 | .compat_setsockopt = compat_sock_common_setsockopt, | 843 | .compat_setsockopt = compat_sock_common_setsockopt, |
843 | .compat_getsockopt = compat_sock_common_getsockopt, | 844 | .compat_getsockopt = compat_sock_common_getsockopt, |
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8e65182f7af1..56ed40703f98 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -254,6 +254,10 @@ | |||
254 | #include <linux/poll.h> | 254 | #include <linux/poll.h> |
255 | #include <linux/init.h> | 255 | #include <linux/init.h> |
256 | #include <linux/fs.h> | 256 | #include <linux/fs.h> |
257 | #include <linux/skbuff.h> | ||
258 | #include <linux/splice.h> | ||
259 | #include <linux/net.h> | ||
260 | #include <linux/socket.h> | ||
257 | #include <linux/random.h> | 261 | #include <linux/random.h> |
258 | #include <linux/bootmem.h> | 262 | #include <linux/bootmem.h> |
259 | #include <linux/cache.h> | 263 | #include <linux/cache.h> |
@@ -265,6 +269,7 @@ | |||
265 | #include <net/xfrm.h> | 269 | #include <net/xfrm.h> |
266 | #include <net/ip.h> | 270 | #include <net/ip.h> |
267 | #include <net/netdma.h> | 271 | #include <net/netdma.h> |
272 | #include <net/sock.h> | ||
268 | 273 | ||
269 | #include <asm/uaccess.h> | 274 | #include <asm/uaccess.h> |
270 | #include <asm/ioctls.h> | 275 | #include <asm/ioctls.h> |
@@ -292,6 +297,15 @@ EXPORT_SYMBOL(tcp_memory_allocated); | |||
292 | EXPORT_SYMBOL(tcp_sockets_allocated); | 297 | EXPORT_SYMBOL(tcp_sockets_allocated); |
293 | 298 | ||
294 | /* | 299 | /* |
300 | * TCP splice context | ||
301 | */ | ||
302 | struct tcp_splice_state { | ||
303 | struct pipe_inode_info *pipe; | ||
304 | size_t len; | ||
305 | unsigned int flags; | ||
306 | }; | ||
307 | |||
308 | /* | ||
295 | * Pressure flag: try to collapse. | 309 | * Pressure flag: try to collapse. |
296 | * Technical note: it is used by multiple contexts non atomically. | 310 | * Technical note: it is used by multiple contexts non atomically. |
297 | * All the sk_stream_mem_schedule() is of this nature: accounting | 311 | * All the sk_stream_mem_schedule() is of this nature: accounting |
@@ -501,6 +515,120 @@ static inline void tcp_push(struct sock *sk, int flags, int mss_now, | |||
501 | } | 515 | } |
502 | } | 516 | } |
503 | 517 | ||
518 | int tcp_splice_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, | ||
519 | unsigned int offset, size_t len) | ||
520 | { | ||
521 | struct tcp_splice_state *tss = rd_desc->arg.data; | ||
522 | |||
523 | return skb_splice_bits(skb, offset, tss->pipe, tss->len, tss->flags); | ||
524 | } | ||
525 | |||
526 | static int __tcp_splice_read(struct sock *sk, struct tcp_splice_state *tss) | ||
527 | { | ||
528 | /* Store TCP splice context information in read_descriptor_t. */ | ||
529 | read_descriptor_t rd_desc = { | ||
530 | .arg.data = tss, | ||
531 | }; | ||
532 | |||
533 | return tcp_read_sock(sk, &rd_desc, tcp_splice_data_recv); | ||
534 | } | ||
535 | |||
536 | /** | ||
537 | * tcp_splice_read - splice data from TCP socket to a pipe | ||
538 | * @sock: socket to splice from | ||
539 | * @ppos: position (not valid) | ||
540 | * @pipe: pipe to splice to | ||
541 | * @len: number of bytes to splice | ||
542 | * @flags: splice modifier flags | ||
543 | * | ||
544 | * Description: | ||
545 | * Will read pages from given socket and fill them into a pipe. | ||
546 | * | ||
547 | **/ | ||
548 | ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, | ||
549 | struct pipe_inode_info *pipe, size_t len, | ||
550 | unsigned int flags) | ||
551 | { | ||
552 | struct sock *sk = sock->sk; | ||
553 | struct tcp_splice_state tss = { | ||
554 | .pipe = pipe, | ||
555 | .len = len, | ||
556 | .flags = flags, | ||
557 | }; | ||
558 | long timeo; | ||
559 | ssize_t spliced; | ||
560 | int ret; | ||
561 | |||
562 | /* | ||
563 | * We can't seek on a socket input | ||
564 | */ | ||
565 | if (unlikely(*ppos)) | ||
566 | return -ESPIPE; | ||
567 | |||
568 | ret = spliced = 0; | ||
569 | |||
570 | lock_sock(sk); | ||
571 | |||
572 | timeo = sock_rcvtimeo(sk, flags & SPLICE_F_NONBLOCK); | ||
573 | while (tss.len) { | ||
574 | ret = __tcp_splice_read(sk, &tss); | ||
575 | if (ret < 0) | ||
576 | break; | ||
577 | else if (!ret) { | ||
578 | if (spliced) | ||
579 | break; | ||
580 | if (flags & SPLICE_F_NONBLOCK) { | ||
581 | ret = -EAGAIN; | ||
582 | break; | ||
583 | } | ||
584 | if (sock_flag(sk, SOCK_DONE)) | ||
585 | break; | ||
586 | if (sk->sk_err) { | ||
587 | ret = sock_error(sk); | ||
588 | break; | ||
589 | } | ||
590 | if (sk->sk_shutdown & RCV_SHUTDOWN) | ||
591 | break; | ||
592 | if (sk->sk_state == TCP_CLOSE) { | ||
593 | /* | ||
594 | * This occurs when user tries to read | ||
595 | * from never connected socket. | ||
596 | */ | ||
597 | if (!sock_flag(sk, SOCK_DONE)) | ||
598 | ret = -ENOTCONN; | ||
599 | break; | ||
600 | } | ||
601 | if (!timeo) { | ||
602 | ret = -EAGAIN; | ||
603 | break; | ||
604 | } | ||
605 | sk_wait_data(sk, &timeo); | ||
606 | if (signal_pending(current)) { | ||
607 | ret = sock_intr_errno(timeo); | ||
608 | break; | ||
609 | } | ||
610 | continue; | ||
611 | } | ||
612 | tss.len -= ret; | ||
613 | spliced += ret; | ||
614 | |||
615 | release_sock(sk); | ||
616 | lock_sock(sk); | ||
617 | |||
618 | if (sk->sk_err || sk->sk_state == TCP_CLOSE || | ||
619 | (sk->sk_shutdown & RCV_SHUTDOWN) || !timeo || | ||
620 | signal_pending(current)) | ||
621 | break; | ||
622 | } | ||
623 | |||
624 | release_sock(sk); | ||
625 | |||
626 | if (spliced) | ||
627 | return spliced; | ||
628 | |||
629 | return ret; | ||
630 | } | ||
631 | |||
504 | static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, | 632 | static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, |
505 | size_t psize, int flags) | 633 | size_t psize, int flags) |
506 | { | 634 | { |
@@ -2532,6 +2660,7 @@ EXPORT_SYMBOL(tcp_poll); | |||
2532 | EXPORT_SYMBOL(tcp_read_sock); | 2660 | EXPORT_SYMBOL(tcp_read_sock); |
2533 | EXPORT_SYMBOL(tcp_recvmsg); | 2661 | EXPORT_SYMBOL(tcp_recvmsg); |
2534 | EXPORT_SYMBOL(tcp_sendmsg); | 2662 | EXPORT_SYMBOL(tcp_sendmsg); |
2663 | EXPORT_SYMBOL(tcp_splice_read); | ||
2535 | EXPORT_SYMBOL(tcp_sendpage); | 2664 | EXPORT_SYMBOL(tcp_sendpage); |
2536 | EXPORT_SYMBOL(tcp_setsockopt); | 2665 | EXPORT_SYMBOL(tcp_setsockopt); |
2537 | EXPORT_SYMBOL(tcp_shutdown); | 2666 | EXPORT_SYMBOL(tcp_shutdown); |