summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorMat Martineau <mathewm@codeaurora.org>2011-07-22 21:53:59 (GMT)
committerGustavo F. Padovan <gustavo@padovan.org>2011-09-27 21:16:07 (GMT)
commit5b668eb3270f3f9c13ddf6e4fb57bf20c83dccff (patch)
treee50403979ce54f9e7c4aead7075c4fc266ee396a /net
parent449357200c5d73d80a9c42dee5dafed684b3cd17 (diff)
downloadlinux-fsl-qoriq-5b668eb3270f3f9c13ddf6e4fb57bf20c83dccff.tar.xz
Bluetooth: Handle fragmented skbs in bt_sock_stream_recvmsg()
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>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/af_bluetooth.c30
1 files changed, 28 insertions, 2 deletions
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 117e0d1..062124c 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -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;