Bluetooth: Provide L2CAP ops callback for memcpy_fromiovec
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Wed, 18 Jun 2014 13:37:07 +0000 (16:37 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 3 Jul 2014 15:42:43 +0000 (17:42 +0200)
The highly optimized TX path for L2CAP channels and its fragmentation
within the HCI ACL packets requires to copy data from user provided
IO vectors and also kernel provided memory buffers.

This patch allows channel clients to provide a memcpy_fromiovec callback
to keep this optimized behavior, but adapt it to kernel vs user memory
for the TX path. For all kernel internal L2CAP channels, a default
implementation is provided that can be referenced.

In case of A2MP, this fixes a long-standing issue with wrongly accessing
kernel memory as user memory.

This patch originally by Marcel Holtmann.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/l2cap.h
net/bluetooth/a2mp.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c

index 92511034d1d43002562f1b6c9b0b2e1d65be8ef2..1ee6f00d70961398182cd0e4f130312f9969547c 100644 (file)
@@ -602,6 +602,10 @@ struct l2cap_ops {
        struct sk_buff          *(*alloc_skb) (struct l2cap_chan *chan,
                                               unsigned long hdr_len,
                                               unsigned long len, int nb);
+       int                     (*memcpy_fromiovec) (struct l2cap_chan *chan,
+                                                    unsigned char *kdata,
+                                                    struct iovec *iov,
+                                                    int len);
 };
 
 struct l2cap_conn {
@@ -857,6 +861,31 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan)
        return 0;
 }
 
+static inline int l2cap_chan_no_memcpy_fromiovec(struct l2cap_chan *chan,
+                                                unsigned char *kdata,
+                                                struct iovec *iov,
+                                                int len)
+{
+       /* Following is safe since for compiler definitions of kvec and
+        * iovec are identical, yielding the same in-core layout and alignment
+        */
+       struct kvec *vec = (struct kvec *)iov;
+
+       while (len > 0) {
+               if (vec->iov_len) {
+                       int copy = min_t(unsigned int, len, vec->iov_len);
+                       memcpy(kdata, vec->iov_base, copy);
+                       len -= copy;
+                       kdata += copy;
+                       vec->iov_base += copy;
+                       vec->iov_len -= copy;
+               }
+               vec++;
+       }
+
+       return 0;
+}
+
 extern bool disable_ertm;
 
 int l2cap_init_sockets(void);
index 0fd8d1dda7092f1bcd24096e86bd1420fd9d417c..5dcade511fdbf7da80995ac2c8218171663b339f 100644 (file)
@@ -720,6 +720,7 @@ static const struct l2cap_ops a2mp_chan_ops = {
        .resume = l2cap_chan_no_resume,
        .set_shutdown = l2cap_chan_no_set_shutdown,
        .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
+       .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
 };
 
 static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
index ac2461442f21f06eb9f474ea402971db970f3a81..d0a5fde61a07b532a87b3ea3bb788100da2fe21b 100644 (file)
@@ -2118,7 +2118,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
        struct sk_buff **frag;
        int sent = 0;
 
-       if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count))
+       if (chan->ops->memcpy_fromiovec(chan, skb_put(skb, count),
+                                       msg->msg_iov, count))
                return -EFAULT;
 
        sent += count;
@@ -2138,7 +2139,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
 
                *frag = tmp;
 
-               if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
+               if (chan->ops->memcpy_fromiovec(chan, skb_put(*frag, count),
+                                               msg->msg_iov, count))
                        return -EFAULT;
 
                sent += count;
index 55215ebf6547a1e5875a1559385e898981f15757..bf72886de6efca8c4a115228ca71c0751e2edeb5 100644 (file)
@@ -1313,6 +1313,13 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
        return skb;
 }
 
+static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan,
+                                         unsigned char *kdata,
+                                         struct iovec *iov, int len)
+{
+       return memcpy_fromiovec(kdata, iov, len);
+}
+
 static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
 {
        struct sock *sk = chan->data;
@@ -1379,19 +1386,20 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan)
 }
 
 static const struct l2cap_ops l2cap_chan_ops = {
-       .name           = "L2CAP Socket Interface",
-       .new_connection = l2cap_sock_new_connection_cb,
-       .recv           = l2cap_sock_recv_cb,
-       .close          = l2cap_sock_close_cb,
-       .teardown       = l2cap_sock_teardown_cb,
-       .state_change   = l2cap_sock_state_change_cb,
-       .ready          = l2cap_sock_ready_cb,
-       .defer          = l2cap_sock_defer_cb,
-       .resume         = l2cap_sock_resume_cb,
-       .suspend        = l2cap_sock_suspend_cb,
-       .set_shutdown   = l2cap_sock_set_shutdown_cb,
-       .get_sndtimeo   = l2cap_sock_get_sndtimeo_cb,
-       .alloc_skb      = l2cap_sock_alloc_skb_cb,
+       .name                   = "L2CAP Socket Interface",
+       .new_connection         = l2cap_sock_new_connection_cb,
+       .recv                   = l2cap_sock_recv_cb,
+       .close                  = l2cap_sock_close_cb,
+       .teardown               = l2cap_sock_teardown_cb,
+       .state_change           = l2cap_sock_state_change_cb,
+       .ready                  = l2cap_sock_ready_cb,
+       .defer                  = l2cap_sock_defer_cb,
+       .resume                 = l2cap_sock_resume_cb,
+       .suspend                = l2cap_sock_suspend_cb,
+       .set_shutdown           = l2cap_sock_set_shutdown_cb,
+       .get_sndtimeo           = l2cap_sock_get_sndtimeo_cb,
+       .alloc_skb              = l2cap_sock_alloc_skb_cb,
+       .memcpy_fromiovec       = l2cap_sock_memcpy_fromiovec_cb,
 };
 
 static void l2cap_sock_destruct(struct sock *sk)
This page took 0.035373 seconds and 5 git commands to generate.