diff mbox series

[v2] Bluetooth: keep LE flow credits when recvbuf full

Message ID 20240405102551.114999-1-surban@surban.net (mailing list archive)
State Superseded
Headers show
Series [v2] Bluetooth: keep LE flow credits when recvbuf full | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
tedd_an/CheckPatch success CheckPatch PASS
tedd_an/GitLint success Gitlint PASS
tedd_an/SubjectPrefix success Gitlint PASS
tedd_an/BuildKernel success BuildKernel PASS
tedd_an/CheckAllWarning success CheckAllWarning PASS
tedd_an/CheckSparse success CheckSparse PASS
tedd_an/CheckSmatch fail CheckSparse: FAIL: Segmentation fault (core dumped) make[4]: *** [scripts/Makefile.build:244: net/bluetooth/hci_core.o] Error 139 make[4]: *** Deleting file 'net/bluetooth/hci_core.o' make[3]: *** [scripts/Makefile.build:485: net/bluetooth] Error 2 make[2]: *** [scripts/Makefile.build:485: net] Error 2 make[2]: *** Waiting for unfinished jobs.... Segmentation fault (core dumped) make[4]: *** [scripts/Makefile.build:244: drivers/bluetooth/bcm203x.o] Error 139 make[4]: *** Deleting file 'drivers/bluetooth/bcm203x.o' make[4]: *** Waiting for unfinished jobs.... make[3]: *** [scripts/Makefile.build:485: drivers/bluetooth] Error 2 make[2]: *** [scripts/Makefile.build:485: drivers] Error 2 make[1]: *** [/github/workspace/src/src/Makefile:1919: .] Error 2 make: *** [Makefile:240: __sub-make] Error 2
tedd_an/BuildKernel32 success BuildKernel32 PASS
tedd_an/TestRunnerSetup success TestRunnerSetup PASS
tedd_an/TestRunner_l2cap-tester success TestRunner PASS
tedd_an/TestRunner_iso-tester fail TestRunner_iso-tester: Total: 121, Passed: 120 (99.2%), Failed: 1, Not Run: 0
tedd_an/TestRunner_bnep-tester success TestRunner PASS
tedd_an/TestRunner_mgmt-tester fail TestRunner_mgmt-tester: Total: 492, Passed: 489 (99.4%), Failed: 1, Not Run: 2
tedd_an/TestRunner_rfcomm-tester success TestRunner PASS
tedd_an/TestRunner_sco-tester success TestRunner PASS
tedd_an/TestRunner_ioctl-tester success TestRunner PASS
tedd_an/TestRunner_mesh-tester success TestRunner PASS
tedd_an/TestRunner_smp-tester success TestRunner PASS
tedd_an/TestRunner_userchan-tester success TestRunner PASS
tedd_an/IncrementalBuild success Incremental Build PASS

Commit Message

Sebastian Urban April 5, 2024, 10:25 a.m. UTC
Previously LE flow credits were returned to the
sender even if the socket's receive buffer was
full. This meant that no back-pressure
was applied to the sender, thus it continued to
send data, resulting in data loss without any
error being reported.

This is fixed by stopping the return of LE flow
credits when the receive buffer of an L2CAP socket
is full. Returning of the credits is resumed, once
the receive buffer is half-empty.

Already received data is temporary stored within
l2cap_pinfo, since Bluetooth LE provides no
retransmission mechanism once the data has been
acked by the physical layer.

Signed-off-by: Sebastian Urban <surban@surban.net>
---
 include/net/bluetooth/l2cap.h |  7 ++++-
 net/bluetooth/l2cap_core.c    | 38 ++++++++++++++++++++++---
 net/bluetooth/l2cap_sock.c    | 53 ++++++++++++++++++++++++++---------
 3 files changed, 79 insertions(+), 19 deletions(-)

Comments

bluez.test.bot@gmail.com April 5, 2024, 10:56 a.m. UTC | #1
This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=841749

---Test result---

Test Summary:
CheckPatch                    PASS      1.10 seconds
GitLint                       PASS      4.42 seconds
SubjectPrefix                 PASS      0.12 seconds
BuildKernel                   PASS      29.80 seconds
CheckAllWarning               PASS      32.38 seconds
CheckSparse                   PASS      37.79 seconds
CheckSmatch                   FAIL      34.75 seconds
BuildKernel32                 PASS      29.16 seconds
TestRunnerSetup               PASS      516.52 seconds
TestRunner_l2cap-tester       PASS      18.25 seconds
TestRunner_iso-tester         FAIL      32.46 seconds
TestRunner_bnep-tester        PASS      4.62 seconds
TestRunner_mgmt-tester        FAIL      112.20 seconds
TestRunner_rfcomm-tester      PASS      7.23 seconds
TestRunner_sco-tester         PASS      14.88 seconds
TestRunner_ioctl-tester       PASS      7.58 seconds
TestRunner_mesh-tester        PASS      5.68 seconds
TestRunner_smp-tester         PASS      6.66 seconds
TestRunner_userchan-tester    PASS      4.81 seconds
IncrementalBuild              PASS      28.17 seconds

Details
##############################
Test: CheckSmatch - FAIL
Desc: Run smatch tool with source
Output:

Segmentation fault (core dumped)
make[4]: *** [scripts/Makefile.build:244: net/bluetooth/hci_core.o] Error 139
make[4]: *** Deleting file 'net/bluetooth/hci_core.o'
make[3]: *** [scripts/Makefile.build:485: net/bluetooth] Error 2
make[2]: *** [scripts/Makefile.build:485: net] Error 2
make[2]: *** Waiting for unfinished jobs....
Segmentation fault (core dumped)
make[4]: *** [scripts/Makefile.build:244: drivers/bluetooth/bcm203x.o] Error 139
make[4]: *** Deleting file 'drivers/bluetooth/bcm203x.o'
make[4]: *** Waiting for unfinished jobs....
make[3]: *** [scripts/Makefile.build:485: drivers/bluetooth] Error 2
make[2]: *** [scripts/Makefile.build:485: drivers] Error 2
make[1]: *** [/github/workspace/src/src/Makefile:1919: .] Error 2
make: *** [Makefile:240: __sub-make] Error 2
##############################
Test: TestRunner_iso-tester - FAIL
Desc: Run iso-tester with test-runner
Output:
Total: 121, Passed: 120 (99.2%), Failed: 1, Not Run: 0

Failed Test Cases
ISO Connect2 Suspend - Success                       Failed       6.168 seconds
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 492, Passed: 489 (99.4%), Failed: 1, Not Run: 2

Failed Test Cases
LL Privacy - Remove Device 4 (Disable Adv)           Timed out    2.157 seconds


---
Regards,
Linux Bluetooth
Luiz Augusto von Dentz April 5, 2024, 3:30 p.m. UTC | #2
Hi Sebastian,

On Fri, Apr 5, 2024 at 6:26 AM Sebastian Urban <surban@surban.net> wrote:
>
> Previously LE flow credits were returned to the
> sender even if the socket's receive buffer was
> full. This meant that no back-pressure
> was applied to the sender, thus it continued to
> send data, resulting in data loss without any
> error being reported.
>
> This is fixed by stopping the return of LE flow
> credits when the receive buffer of an L2CAP socket
> is full. Returning of the credits is resumed, once
> the receive buffer is half-empty.
>
> Already received data is temporary stored within
> l2cap_pinfo, since Bluetooth LE provides no
> retransmission mechanism once the data has been
> acked by the physical layer.
>
> Signed-off-by: Sebastian Urban <surban@surban.net>
> ---
>  include/net/bluetooth/l2cap.h |  7 ++++-
>  net/bluetooth/l2cap_core.c    | 38 ++++++++++++++++++++++---
>  net/bluetooth/l2cap_sock.c    | 53 ++++++++++++++++++++++++++---------
>  3 files changed, 79 insertions(+), 19 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 92d7197f9a56..230c14ea944c 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -682,10 +682,15 @@ struct l2cap_user {
>  /* ----- L2CAP socket info ----- */
>  #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
>
> +struct l2cap_rx_busy {
> +       struct list_head        list;
> +       struct sk_buff          *skb;
> +};

In theory we only really to queue 1 skb at most, since we would stop
giving credits, or perhaps this is because we had given enough credits
for MTU + 1, so the +1 segment could result in a second SDU/skb to be
completed while waiting the user space process to start reading again?

>  struct l2cap_pinfo {
>         struct bt_sock          bt;
>         struct l2cap_chan       *chan;
> -       struct sk_buff          *rx_busy_skb;
> +       struct list_head        rx_busy;
>  };
>
>  enum {
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index ab5a9d42fae7..c78af7fad255 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -63,6 +63,8 @@ static void l2cap_retrans_timeout(struct work_struct *work);
>  static void l2cap_monitor_timeout(struct work_struct *work);
>  static void l2cap_ack_timeout(struct work_struct *work);
>
> +static void l2cap_chan_le_send_credits(struct l2cap_chan *chan);

We probably need to change the way send_credits calculates the number
of credits to be restored, it needs to consider the actual available
buffer size at the socket rather then assuming we always shall have
space for MTU + 1, that way the remote side would always have the
exact information of how much buffer space is left. That said perhaps
we need a way to inform when user space reads then we need to call
into send_credits again.

>  static inline u8 bdaddr_type(u8 link_type, u8 bdaddr_type)
>  {
>         if (link_type == LE_LINK) {
> @@ -5714,17 +5716,34 @@ static int l2cap_resegment(struct l2cap_chan *chan)
>         return 0;
>  }
>
> -void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
> +static void l2cap_chan_busy_ertm(struct l2cap_chan *chan, int busy)
>  {
>         u8 event;
>
> -       if (chan->mode != L2CAP_MODE_ERTM)
> -               return;
> -
>         event = busy ? L2CAP_EV_LOCAL_BUSY_DETECTED : L2CAP_EV_LOCAL_BUSY_CLEAR;
>         l2cap_tx(chan, NULL, NULL, event);
>  }
>
> +static void l2cap_chan_busy_le(struct l2cap_chan *chan, int busy)
> +{
> +       if (busy) {
> +               set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
> +       } else {
> +               clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
> +               l2cap_chan_le_send_credits(chan);
> +       }
> +}
> +
> +void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
> +{
> +       if (chan->mode == L2CAP_MODE_ERTM) {
> +               l2cap_chan_busy_ertm(chan, busy);
> +       } else if (chan->mode == L2CAP_MODE_LE_FLOWCTL ||
> +                  chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
> +               l2cap_chan_busy_le(chan, busy);
> +       }
> +}
> +
>  static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
>  {
>         int err = 0;
> @@ -6514,6 +6533,11 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
>         struct l2cap_le_credits pkt;
>         u16 return_credits;
>
> +       if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
> +               BT_DBG("busy chan %p not returning credits to sender", chan);
> +               return;
> +       }
> +
>         return_credits = (chan->imtu / chan->mps) + 1;
>
>         if (chan->rx_credits >= return_credits)
> @@ -6542,6 +6566,12 @@ static int l2cap_ecred_recv(struct l2cap_chan *chan, struct sk_buff *skb)
>         /* Wait recv to confirm reception before updating the credits */
>         err = chan->ops->recv(chan, skb);
>
> +       if (err < 0) {
> +               BT_ERR("Queueing received LE L2CAP data failed");
> +               l2cap_send_disconn_req(chan, ECONNRESET);
> +               return err;
> +       }
> +
>         /* Update credits whenever an SDU is received */
>         l2cap_chan_le_send_credits(chan);
>
> diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> index ee7a41d6994f..3b0fb6e0b61b 100644
> --- a/net/bluetooth/l2cap_sock.c
> +++ b/net/bluetooth/l2cap_sock.c
> @@ -1177,7 +1177,9 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
>         else
>                 err = bt_sock_recvmsg(sock, msg, len, flags);
>
> -       if (pi->chan->mode != L2CAP_MODE_ERTM)
> +       if (pi->chan->mode != L2CAP_MODE_ERTM &&
> +           pi->chan->mode != L2CAP_MODE_LE_FLOWCTL &&
> +           pi->chan->mode != L2CAP_MODE_EXT_FLOWCTL)
>                 return err;
>
>         /* Attempt to put pending rx data in the socket buffer */
> @@ -1187,11 +1189,15 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
>         if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state))
>                 goto done;
>
> -       if (pi->rx_busy_skb) {
> -               if (!__sock_queue_rcv_skb(sk, pi->rx_busy_skb))
> -                       pi->rx_busy_skb = NULL;
> -               else
> +       while (!list_empty(&pi->rx_busy)) {
> +               struct l2cap_rx_busy *rx_busy =
> +                       list_first_entry(&pi->rx_busy,
> +                                        struct l2cap_rx_busy,
> +                                        list);
> +               if (__sock_queue_rcv_skb(sk, rx_busy->skb) < 0)
>                         goto done;
> +               list_del(&rx_busy->list);
> +               kfree(rx_busy);

I see now, this is trying to dequeue packets if the socket is read,
which in case we turn the send_credits function to calculate the
credits based on the socket buffer size that would not be necessary
but then we would need to call into send_credits here.

>         }
>
>         /* Restore data flow when half of the receive buffer is
> @@ -1459,17 +1465,20 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
>  static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
>  {
>         struct sock *sk = chan->data;
> +       struct l2cap_pinfo *pi = l2cap_pi(sk);
>         int err;
>
>         lock_sock(sk);
>
> -       if (l2cap_pi(sk)->rx_busy_skb) {
> +       if (chan->mode == L2CAP_MODE_ERTM && !list_empty(&pi->rx_busy)) {
>                 err = -ENOMEM;
>                 goto done;
>         }
>
>         if (chan->mode != L2CAP_MODE_ERTM &&
> -           chan->mode != L2CAP_MODE_STREAMING) {
> +           chan->mode != L2CAP_MODE_STREAMING &&
> +           chan->mode != L2CAP_MODE_LE_FLOWCTL &&
> +           chan->mode != L2CAP_MODE_EXT_FLOWCTL) {
>                 /* Even if no filter is attached, we could potentially
>                  * get errors from security modules, etc.
>                  */
> @@ -1480,17 +1489,28 @@ static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
>
>         err = __sock_queue_rcv_skb(sk, skb);
>
> -       /* For ERTM, handle one skb that doesn't fit into the recv
> +       /* For ERTM and LE, handle a skb that doesn't fit into the recv
>          * buffer.  This is important to do because the data frames
>          * have already been acked, so the skb cannot be discarded.
>          *
>          * Notify the l2cap core that the buffer is full, so the
>          * LOCAL_BUSY state is entered and no more frames are
>          * acked and reassembled until there is buffer space
> -        * available.
> +        * available. In the case of LE this blocks returning of flow
> +        * credits.
>          */
> -       if (err < 0 && chan->mode == L2CAP_MODE_ERTM) {
> -               l2cap_pi(sk)->rx_busy_skb = skb;
> +       if (err < 0 &&
> +           (chan->mode == L2CAP_MODE_ERTM ||
> +            chan->mode == L2CAP_MODE_LE_FLOWCTL ||
> +            chan->mode == L2CAP_MODE_EXT_FLOWCTL)) {
> +               struct l2cap_rx_busy *rx_busy =
> +                       kmalloc(sizeof(*rx_busy), GFP_KERNEL);
> +               if (!rx_busy) {
> +                       err = -ENOMEM;
> +                       goto done;
> +               }
> +               rx_busy->skb = skb;
> +               list_add_tail(&rx_busy->list, &pi->rx_busy);
>                 l2cap_chan_busy(chan, 1);
>                 err = 0;
>         }
> @@ -1716,6 +1736,8 @@ static const struct l2cap_ops l2cap_chan_ops = {
>
>  static void l2cap_sock_destruct(struct sock *sk)
>  {
> +       struct l2cap_rx_busy *rx_busy, *next;
> +
>         BT_DBG("sk %p", sk);
>
>         if (l2cap_pi(sk)->chan) {
> @@ -1723,9 +1745,10 @@ static void l2cap_sock_destruct(struct sock *sk)
>                 l2cap_chan_put(l2cap_pi(sk)->chan);
>         }
>
> -       if (l2cap_pi(sk)->rx_busy_skb) {
> -               kfree_skb(l2cap_pi(sk)->rx_busy_skb);
> -               l2cap_pi(sk)->rx_busy_skb = NULL;
> +       list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
> +               kfree_skb(rx_busy->skb);
> +               list_del(&rx_busy->list);
> +               kfree(rx_busy);
>         }
>
>         skb_queue_purge(&sk->sk_receive_queue);
> @@ -1830,6 +1853,8 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
>         sk->sk_destruct = l2cap_sock_destruct;
>         sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT;
>
> +       INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
> +
>         chan = l2cap_chan_create();
>         if (!chan) {
>                 sk_free(sk);
> --
> 2.34.1
>
Sebastian Urban April 7, 2024, 6:57 p.m. UTC | #3
Hi Luiz,

On 4/5/24 17:30, Luiz Augusto von Dentz wrote:
> Hi Sebastian,
> 
> On Fri, Apr 5, 2024 at 6:26 AM Sebastian Urban <surban@surban.net> wrote:
>>
>> --- a/include/net/bluetooth/l2cap.h
>> +++ b/include/net/bluetooth/l2cap.h
>> @@ -682,10 +682,15 @@ struct l2cap_user {
>>   /* ----- L2CAP socket info ----- */
>>   #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
>>
>> +struct l2cap_rx_busy {
>> +       struct list_head        list;
>> +       struct sk_buff          *skb;
>> +};
> 
> In theory we only really to queue 1 skb at most, since we would stop
> giving credits, or perhaps this is because we had given enough credits
> for MTU + 1, so the +1 segment could result in a second SDU/skb to be
> completed while waiting the user space process to start reading again?

Yes, during testing it became apparent that there might be a second 
incoming skb, which also needs to be buffered.

Even if --as discussed below-- we change send_credits to return credits 
based on the actual available receive buffer space, I believe we still 
need to allow buffering more than one skb. This is because local 
user-space might decide to resize the receive buffer size (SO_RCVBUF) to 
a smaller value after the credits have already been given to the remote 
side.

>> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
>> index ab5a9d42fae7..c78af7fad255 100644
>> --- a/net/bluetooth/l2cap_core.c
>> +++ b/net/bluetooth/l2cap_core.c
>> @@ -63,6 +63,8 @@ static void l2cap_retrans_timeout(struct work_struct *work);
>>   static void l2cap_monitor_timeout(struct work_struct *work);
>>   static void l2cap_ack_timeout(struct work_struct *work);
>>
>> +static void l2cap_chan_le_send_credits(struct l2cap_chan *chan);
> 
> We probably need to change the way send_credits calculates the number
> of credits to be restored, it needs to consider the actual available
> buffer size at the socket rather then assuming we always shall have
> space for MTU + 1, that way the remote side would always have the
> exact information of how much buffer space is left. That said perhaps
> we need a way to inform when user space reads then we need to call
> into send_credits again.

Yes, this makes sense. I will extend the patch appropriately.

>> @@ -1187,11 +1189,15 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
>>          if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state))
>>                  goto done;
>>
>> -       if (pi->rx_busy_skb) {
>> -               if (!__sock_queue_rcv_skb(sk, pi->rx_busy_skb))
>> -                       pi->rx_busy_skb = NULL;
>> -               else
>> +       while (!list_empty(&pi->rx_busy)) {
>> +               struct l2cap_rx_busy *rx_busy =
>> +                       list_first_entry(&pi->rx_busy,
>> +                                        struct l2cap_rx_busy,
>> +                                        list);
>> +               if (__sock_queue_rcv_skb(sk, rx_busy->skb) < 0)
>>                          goto done;
>> +               list_del(&rx_busy->list);
>> +               kfree(rx_busy);
> 
> I see now, this is trying to dequeue packets if the socket is read,
> which in case we turn the send_credits function to calculate the
> credits based on the socket buffer size that would not be necessary
> but then we would need to call into send_credits here.

This is followed by (unmodified):

	if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1)
		l2cap_chan_busy(pi->chan, 0);

And will in fact call send_credits through l2cap_chan_busy from here 
once all queued skbs have been accepted by the socket and its receive 
buffer has become half empty.
diff mbox series

Patch

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 92d7197f9a56..230c14ea944c 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -682,10 +682,15 @@  struct l2cap_user {
 /* ----- L2CAP socket info ----- */
 #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
 
+struct l2cap_rx_busy {
+	struct list_head	list;
+	struct sk_buff		*skb;
+};
+
 struct l2cap_pinfo {
 	struct bt_sock		bt;
 	struct l2cap_chan	*chan;
-	struct sk_buff		*rx_busy_skb;
+	struct list_head	rx_busy;
 };
 
 enum {
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ab5a9d42fae7..c78af7fad255 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -63,6 +63,8 @@  static void l2cap_retrans_timeout(struct work_struct *work);
 static void l2cap_monitor_timeout(struct work_struct *work);
 static void l2cap_ack_timeout(struct work_struct *work);
 
+static void l2cap_chan_le_send_credits(struct l2cap_chan *chan);
+
 static inline u8 bdaddr_type(u8 link_type, u8 bdaddr_type)
 {
 	if (link_type == LE_LINK) {
@@ -5714,17 +5716,34 @@  static int l2cap_resegment(struct l2cap_chan *chan)
 	return 0;
 }
 
-void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
+static void l2cap_chan_busy_ertm(struct l2cap_chan *chan, int busy)
 {
 	u8 event;
 
-	if (chan->mode != L2CAP_MODE_ERTM)
-		return;
-
 	event = busy ? L2CAP_EV_LOCAL_BUSY_DETECTED : L2CAP_EV_LOCAL_BUSY_CLEAR;
 	l2cap_tx(chan, NULL, NULL, event);
 }
 
+static void l2cap_chan_busy_le(struct l2cap_chan *chan, int busy)
+{
+	if (busy) {
+		set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+	} else {
+		clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+		l2cap_chan_le_send_credits(chan);
+	}
+}
+
+void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
+{
+	if (chan->mode == L2CAP_MODE_ERTM) {
+		l2cap_chan_busy_ertm(chan, busy);
+	} else if (chan->mode == L2CAP_MODE_LE_FLOWCTL ||
+		   chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
+		l2cap_chan_busy_le(chan, busy);
+	}
+}
+
 static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
 {
 	int err = 0;
@@ -6514,6 +6533,11 @@  static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
 	struct l2cap_le_credits pkt;
 	u16 return_credits;
 
+	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+		BT_DBG("busy chan %p not returning credits to sender", chan);
+		return;
+	}
+
 	return_credits = (chan->imtu / chan->mps) + 1;
 
 	if (chan->rx_credits >= return_credits)
@@ -6542,6 +6566,12 @@  static int l2cap_ecred_recv(struct l2cap_chan *chan, struct sk_buff *skb)
 	/* Wait recv to confirm reception before updating the credits */
 	err = chan->ops->recv(chan, skb);
 
+	if (err < 0) {
+		BT_ERR("Queueing received LE L2CAP data failed");
+		l2cap_send_disconn_req(chan, ECONNRESET);
+		return err;
+	}
+
 	/* Update credits whenever an SDU is received */
 	l2cap_chan_le_send_credits(chan);
 
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index ee7a41d6994f..3b0fb6e0b61b 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1177,7 +1177,9 @@  static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
 	else
 		err = bt_sock_recvmsg(sock, msg, len, flags);
 
-	if (pi->chan->mode != L2CAP_MODE_ERTM)
+	if (pi->chan->mode != L2CAP_MODE_ERTM &&
+	    pi->chan->mode != L2CAP_MODE_LE_FLOWCTL &&
+	    pi->chan->mode != L2CAP_MODE_EXT_FLOWCTL)
 		return err;
 
 	/* Attempt to put pending rx data in the socket buffer */
@@ -1187,11 +1189,15 @@  static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
 	if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state))
 		goto done;
 
-	if (pi->rx_busy_skb) {
-		if (!__sock_queue_rcv_skb(sk, pi->rx_busy_skb))
-			pi->rx_busy_skb = NULL;
-		else
+	while (!list_empty(&pi->rx_busy)) {
+		struct l2cap_rx_busy *rx_busy =
+			list_first_entry(&pi->rx_busy,
+					 struct l2cap_rx_busy,
+					 list);
+		if (__sock_queue_rcv_skb(sk, rx_busy->skb) < 0)
 			goto done;
+		list_del(&rx_busy->list);
+		kfree(rx_busy);
 	}
 
 	/* Restore data flow when half of the receive buffer is
@@ -1459,17 +1465,20 @@  static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
 {
 	struct sock *sk = chan->data;
+	struct l2cap_pinfo *pi = l2cap_pi(sk);
 	int err;
 
 	lock_sock(sk);
 
-	if (l2cap_pi(sk)->rx_busy_skb) {
+	if (chan->mode == L2CAP_MODE_ERTM && !list_empty(&pi->rx_busy)) {
 		err = -ENOMEM;
 		goto done;
 	}
 
 	if (chan->mode != L2CAP_MODE_ERTM &&
-	    chan->mode != L2CAP_MODE_STREAMING) {
+	    chan->mode != L2CAP_MODE_STREAMING &&
+	    chan->mode != L2CAP_MODE_LE_FLOWCTL &&
+	    chan->mode != L2CAP_MODE_EXT_FLOWCTL) {
 		/* Even if no filter is attached, we could potentially
 		 * get errors from security modules, etc.
 		 */
@@ -1480,17 +1489,28 @@  static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
 
 	err = __sock_queue_rcv_skb(sk, skb);
 
-	/* For ERTM, handle one skb that doesn't fit into the recv
+	/* For ERTM and LE, handle a skb that doesn't fit into the recv
 	 * buffer.  This is important to do because the data frames
 	 * have already been acked, so the skb cannot be discarded.
 	 *
 	 * Notify the l2cap core that the buffer is full, so the
 	 * LOCAL_BUSY state is entered and no more frames are
 	 * acked and reassembled until there is buffer space
-	 * available.
+	 * available. In the case of LE this blocks returning of flow
+	 * credits.
 	 */
-	if (err < 0 && chan->mode == L2CAP_MODE_ERTM) {
-		l2cap_pi(sk)->rx_busy_skb = skb;
+	if (err < 0 &&
+	    (chan->mode == L2CAP_MODE_ERTM ||
+	     chan->mode == L2CAP_MODE_LE_FLOWCTL ||
+	     chan->mode == L2CAP_MODE_EXT_FLOWCTL)) {
+		struct l2cap_rx_busy *rx_busy =
+			kmalloc(sizeof(*rx_busy), GFP_KERNEL);
+		if (!rx_busy) {
+			err = -ENOMEM;
+			goto done;
+		}
+		rx_busy->skb = skb;
+		list_add_tail(&rx_busy->list, &pi->rx_busy);
 		l2cap_chan_busy(chan, 1);
 		err = 0;
 	}
@@ -1716,6 +1736,8 @@  static const struct l2cap_ops l2cap_chan_ops = {
 
 static void l2cap_sock_destruct(struct sock *sk)
 {
+	struct l2cap_rx_busy *rx_busy, *next;
+
 	BT_DBG("sk %p", sk);
 
 	if (l2cap_pi(sk)->chan) {
@@ -1723,9 +1745,10 @@  static void l2cap_sock_destruct(struct sock *sk)
 		l2cap_chan_put(l2cap_pi(sk)->chan);
 	}
 
-	if (l2cap_pi(sk)->rx_busy_skb) {
-		kfree_skb(l2cap_pi(sk)->rx_busy_skb);
-		l2cap_pi(sk)->rx_busy_skb = NULL;
+	list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
+		kfree_skb(rx_busy->skb);
+		list_del(&rx_busy->list);
+		kfree(rx_busy);
 	}
 
 	skb_queue_purge(&sk->sk_receive_queue);
@@ -1830,6 +1853,8 @@  static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
 	sk->sk_destruct = l2cap_sock_destruct;
 	sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT;
 
+	INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
+
 	chan = l2cap_chan_create();
 	if (!chan) {
 		sk_free(sk);