Bluetooth: RFCOMM: Replace use of memcpy_from_msg with bt_skb_sendmmsg
This makes use of bt_skb_sendmmsg instead using memcpy_from_msg which is not considered safe to be used when lock_sock is held. Also make rfcomm_dlc_send handle skb with fragments and queue them all atomically. Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
0771cbb3b9
commit
81be03e026
@ -549,22 +549,58 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel)
|
||||
return dlc;
|
||||
}
|
||||
|
||||
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
|
||||
static int rfcomm_dlc_send_frag(struct rfcomm_dlc *d, struct sk_buff *frag)
|
||||
{
|
||||
int len = skb->len;
|
||||
|
||||
if (d->state != BT_CONNECTED)
|
||||
return -ENOTCONN;
|
||||
int len = frag->len;
|
||||
|
||||
BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
|
||||
|
||||
if (len > d->mtu)
|
||||
return -EINVAL;
|
||||
|
||||
rfcomm_make_uih(skb, d->addr);
|
||||
skb_queue_tail(&d->tx_queue, skb);
|
||||
rfcomm_make_uih(frag, d->addr);
|
||||
__skb_queue_tail(&d->tx_queue, frag);
|
||||
|
||||
if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
|
||||
return len;
|
||||
}
|
||||
|
||||
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct sk_buff *frag, *next;
|
||||
int len;
|
||||
|
||||
if (d->state != BT_CONNECTED)
|
||||
return -ENOTCONN;
|
||||
|
||||
frag = skb_shinfo(skb)->frag_list;
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
|
||||
/* Queue all fragments atomically. */
|
||||
spin_lock_irqsave(&d->tx_queue.lock, flags);
|
||||
|
||||
len = rfcomm_dlc_send_frag(d, skb);
|
||||
if (len < 0 || !frag)
|
||||
goto unlock;
|
||||
|
||||
for (; frag; frag = next) {
|
||||
int ret;
|
||||
|
||||
next = frag->next;
|
||||
|
||||
ret = rfcomm_dlc_send_frag(d, frag);
|
||||
if (ret < 0) {
|
||||
kfree_skb(frag);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
len += ret;
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&d->tx_queue.lock, flags);
|
||||
|
||||
if (len > 0 && !test_bit(RFCOMM_TX_THROTTLED, &d->flags))
|
||||
rfcomm_schedule();
|
||||
return len;
|
||||
}
|
||||
|
@ -575,47 +575,21 @@ static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
lock_sock(sk);
|
||||
|
||||
sent = bt_sock_wait_ready(sk, msg->msg_flags);
|
||||
if (sent)
|
||||
goto done;
|
||||
|
||||
while (len) {
|
||||
size_t size = min_t(size_t, len, d->mtu);
|
||||
int err;
|
||||
|
||||
skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
|
||||
msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
if (!skb) {
|
||||
if (sent == 0)
|
||||
sent = err;
|
||||
break;
|
||||
}
|
||||
skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
|
||||
|
||||
err = memcpy_from_msg(skb_put(skb, size), msg, size);
|
||||
if (err) {
|
||||
kfree_skb(skb);
|
||||
if (sent == 0)
|
||||
sent = err;
|
||||
break;
|
||||
}
|
||||
|
||||
skb->priority = sk->sk_priority;
|
||||
|
||||
err = rfcomm_dlc_send(d, skb);
|
||||
if (err < 0) {
|
||||
kfree_skb(skb);
|
||||
if (sent == 0)
|
||||
sent = err;
|
||||
break;
|
||||
}
|
||||
|
||||
sent += size;
|
||||
len -= size;
|
||||
}
|
||||
|
||||
done:
|
||||
release_sock(sk);
|
||||
|
||||
if (sent)
|
||||
return sent;
|
||||
|
||||
skb = bt_skb_sendmmsg(sk, msg, len, d->mtu, RFCOMM_SKB_HEAD_RESERVE,
|
||||
RFCOMM_SKB_TAIL_RESERVE);
|
||||
if (IS_ERR_OR_NULL(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
sent = rfcomm_dlc_send(d, skb);
|
||||
if (sent < 0)
|
||||
kfree_skb(skb);
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user