Bluetooth: Fix write_room() calculation
authorPeter Hurley <peter@hurleysoftware.com>
Mon, 10 Feb 2014 01:59:24 +0000 (20:59 -0500)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 14 Feb 2014 21:39:32 +0000 (13:39 -0800)
The skb truesize of a 12-byte payload with a 10-byte head/tail
reserve is 768 bytes. Consequently, even with 40 tx_credits, at
most 6 packets could be queued at any one time:

  40 tx_credits * 127-byte mtu < 768-byte truesize * 7

This error could also cause the tx queue to apparently stall if
credit flow control is disabled (where tx_credits is fixed at 5),
or if the receiver only granted a limited number of tx credits
(eg., less than 7).

Instead, track the outstanding number of queued packets not yet sent
in wmem_alloc and allow for a maximum of 40 queued packets. Report
the space avail for a single write() as the mtu * number of packets
left before reaching the maximum.

Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Tested-By: Alexander Holler <holler@ahsoftware.de>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/rfcomm/tty.c

index 3f44195c04ad8ec212d6734b3bbce3129018f62b..403ec09f480a2a72983b0b1b9db0222fd6e86742 100644 (file)
@@ -352,20 +352,16 @@ static inline unsigned int rfcomm_room(struct rfcomm_dev *dev)
 {
        struct rfcomm_dlc *dlc = dev->dlc;
 
-       /* The limit is bogus; the number of packets which can
-        * currently be sent by the krfcommd thread has no relevance
-        * to the number of packets which can be queued on the dlc's
-        * tx queue.
-        */
-       int limit = dlc->mtu * (dlc->tx_credits?:1);
+       /* Limit the outstanding number of packets not yet sent to 40 */
+       int pending = 40 - atomic_read(&dev->wmem_alloc);
 
-       return max(0, limit - atomic_read(&dev->wmem_alloc));
+       return max(0, pending) * dlc->mtu;
 }
 
 static void rfcomm_wfree(struct sk_buff *skb)
 {
        struct rfcomm_dev *dev = (void *) skb->sk;
-       atomic_sub(skb->truesize, &dev->wmem_alloc);
+       atomic_dec(&dev->wmem_alloc);
        if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
                tty_port_tty_wakeup(&dev->port);
        tty_port_put(&dev->port);
@@ -374,7 +370,7 @@ static void rfcomm_wfree(struct sk_buff *skb)
 static void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
 {
        tty_port_get(&dev->port);
-       atomic_add(skb->truesize, &dev->wmem_alloc);
+       atomic_inc(&dev->wmem_alloc);
        skb->sk = (void *) dev;
        skb->destructor = rfcomm_wfree;
 }