]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Bluetooth: Handle fragmented skbs in bt_sock_stream_recvmsg()
authorMat Martineau <mathewm@codeaurora.org>
Fri, 22 Jul 2011 21:53:59 +0000 (14:53 -0700)
committerGustavo F. Padovan <padovan@profusion.mobi>
Mon, 25 Jul 2011 22:50:07 +0000 (19:50 -0300)
ERTM reassembly will be more efficient when skbs are linked together
rather than copying every incoming data byte. The existing stream recv
function assumes skbs are linear, so it needs to know how to handle
fragments before reassembly is changed.

bt_sock_recvmsg() already handles fragmented skbs.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
net/bluetooth/af_bluetooth.c

index 8add9b4999129bcbdf62122221bd4bfec0c686ac..9a4352015743856ae3a14b94ab7129bef1f174e5 100644 (file)
@@ -349,7 +349,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
                }
 
                chunk = min_t(unsigned int, skb->len, size);
-               if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+               if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) {
                        skb_queue_head(&sk->sk_receive_queue, skb);
                        if (!copied)
                                copied = -EFAULT;
@@ -361,7 +361,33 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
                sock_recv_ts_and_drops(msg, sk, skb);
 
                if (!(flags & MSG_PEEK)) {
-                       skb_pull(skb, chunk);
+                       int skb_len = skb_headlen(skb);
+
+                       if (chunk <= skb_len) {
+                               __skb_pull(skb, chunk);
+                       } else {
+                               struct sk_buff *frag;
+
+                               __skb_pull(skb, skb_len);
+                               chunk -= skb_len;
+
+                               skb_walk_frags(skb, frag) {
+                                       if (chunk <= frag->len) {
+                                               /* Pulling partial data */
+                                               skb->len -= chunk;
+                                               skb->data_len -= chunk;
+                                               __skb_pull(frag, chunk);
+                                               break;
+                                       } else if (frag->len) {
+                                               /* Pulling all frag data */
+                                               chunk -= frag->len;
+                                               skb->len -= frag->len;
+                                               skb->data_len -= frag->len;
+                                               __skb_pull(frag, frag->len);
+                                       }
+                               }
+                       }
+
                        if (skb->len) {
                                skb_queue_head(&sk->sk_receive_queue, skb);
                                break;