net: make skb_splice_bits more configureable
authorHannes Frederic Sowa <hannes@stressinduktion.org>
Thu, 21 May 2015 15:00:00 +0000 (17:00 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 25 May 2015 04:06:59 +0000 (00:06 -0400)
Prepare skb_splice_bits to be able to deal with AF_UNIX sockets.

AF_UNIX sockets don't use lock_sock/release_sock and thus we have to
use a callback to make the locking and unlocking configureable.

Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/skbuff.h
net/core/skbuff.c
net/ipv4/tcp.c

index f708936cdd23c415fab8cc577a4b0226194e84ae..6b41c15efa277f414d825a5042ebd7b259d66ac5 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/netdev_features.h>
 #include <linux/sched.h>
 #include <net/flow_dissector.h>
+#include <linux/splice.h>
 
 /* A. Checksumming of received packets by device.
  *
@@ -2699,9 +2700,15 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len);
 int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len);
 __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to,
                              int len, __wsum csum);
-int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
+ssize_t skb_socket_splice(struct sock *sk,
+                         struct pipe_inode_info *pipe,
+                         struct splice_pipe_desc *spd);
+int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
                    struct pipe_inode_info *pipe, unsigned int len,
-                   unsigned int flags);
+                   unsigned int flags,
+                   ssize_t (*splice_cb)(struct sock *,
+                                        struct pipe_inode_info *,
+                                        struct splice_pipe_desc *));
 void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
 unsigned int skb_zerocopy_headlen(const struct sk_buff *from);
 int skb_zerocopy(struct sk_buff *to, struct sk_buff *from,
index 4f2babeaf18d425f16ebc5d647f941a7e048182c..02769fa4f5c8df157c067ca55d7101de18f25d85 100644 (file)
@@ -1870,15 +1870,39 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
        return false;
 }
 
+ssize_t skb_socket_splice(struct sock *sk,
+                         struct pipe_inode_info *pipe,
+                         struct splice_pipe_desc *spd)
+{
+       int ret;
+
+       /* Drop the socket lock, otherwise we have reverse
+        * locking dependencies between sk_lock and i_mutex
+        * here as compared to sendfile(). We enter here
+        * with the socket lock held, and splice_to_pipe() will
+        * grab the pipe inode lock. For sendfile() emulation,
+        * we call into ->sendpage() with the i_mutex lock held
+        * and networking will grab the socket lock.
+        */
+       release_sock(sk);
+       ret = splice_to_pipe(pipe, spd);
+       lock_sock(sk);
+
+       return ret;
+}
+
 /*
  * Map data from the skb to a pipe. Should handle both the linear part,
  * the fragments, and the frag list. It does NOT handle frag lists within
  * the frag list, if such a thing exists. We'd probably need to recurse to
  * handle that cleanly.
  */
-int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
+int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
                    struct pipe_inode_info *pipe, unsigned int tlen,
-                   unsigned int flags)
+                   unsigned int flags,
+                   ssize_t (*splice_cb)(struct sock *,
+                                        struct pipe_inode_info *,
+                                        struct splice_pipe_desc *))
 {
        struct partial_page partial[MAX_SKB_FRAGS];
        struct page *pages[MAX_SKB_FRAGS];
@@ -1891,7 +1915,6 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
                .spd_release = sock_spd_release,
        };
        struct sk_buff *frag_iter;
-       struct sock *sk = skb->sk;
        int ret = 0;
 
        /*
@@ -1914,20 +1937,8 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
        }
 
 done:
-       if (spd.nr_pages) {
-               /*
-                * Drop the socket lock, otherwise we have reverse
-                * locking dependencies between sk_lock and i_mutex
-                * here as compared to sendfile(). We enter here
-                * with the socket lock held, and splice_to_pipe() will
-                * grab the pipe inode lock. For sendfile() emulation,
-                * we call into ->sendpage() with the i_mutex lock held
-                * and networking will grab the socket lock.
-                */
-               release_sock(sk);
-               ret = splice_to_pipe(pipe, &spd);
-               lock_sock(sk);
-       }
+       if (spd.nr_pages)
+               ret = splice_cb(sk, pipe, &spd);
 
        return ret;
 }
index 90afcec3f4273d6a2750c6cb615b2c668cc8c490..65f791f74845d51045d1a1af7ff9901babbf2f83 100644 (file)
@@ -695,8 +695,9 @@ static int tcp_splice_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
        struct tcp_splice_state *tss = rd_desc->arg.data;
        int ret;
 
-       ret = skb_splice_bits(skb, offset, tss->pipe, min(rd_desc->count, len),
-                             tss->flags);
+       ret = skb_splice_bits(skb, skb->sk, offset, tss->pipe,
+                             min(rd_desc->count, len), tss->flags,
+                             skb_socket_splice);
        if (ret > 0)
                rd_desc->count -= ret;
        return ret;