* [PATCH] l2cap: do not return LE flow credits when buf full
@ 2024-04-05 9:38 Sebastian Urban
2024-04-05 10:25 ` [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full Sebastian Urban
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Sebastian Urban @ 2024-04-05 9:38 UTC (permalink / raw)
Cc: Sebastian Urban, Marcel Holtmann, Johan Hedberg,
Luiz Augusto von Dentz, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-bluetooth, netdev,
linux-kernel
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;
+};
+
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);
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full 2024-04-05 9:38 [PATCH] l2cap: do not return LE flow credits when buf full Sebastian Urban @ 2024-04-05 10:25 ` Sebastian Urban 2024-04-05 10:56 ` [v2] " bluez.test.bot 2024-04-05 15:30 ` [PATCH v2] " Luiz Augusto von Dentz 2024-04-05 10:33 ` l2cap: do not return LE flow credits when buf full bluez.test.bot 2024-04-08 0:56 ` [PATCH v3] Bluetooth: compute LE flow credits based on recvbuf space Sebastian Urban 2 siblings, 2 replies; 10+ messages in thread From: Sebastian Urban @ 2024-04-05 10:25 UTC (permalink / raw) Cc: Sebastian Urban, Marcel Holtmann, Johan Hedberg, Luiz Augusto von Dentz, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-bluetooth, netdev, linux-kernel 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; +}; + 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); -- 2.34.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* RE: [v2] Bluetooth: keep LE flow credits when recvbuf full 2024-04-05 10:25 ` [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full Sebastian Urban @ 2024-04-05 10:56 ` bluez.test.bot 2024-04-05 15:30 ` [PATCH v2] " Luiz Augusto von Dentz 1 sibling, 0 replies; 10+ messages in thread From: bluez.test.bot @ 2024-04-05 10:56 UTC (permalink / raw) To: linux-bluetooth, surban [-- Attachment #1: Type: text/plain, Size: 2846 bytes --] 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 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full 2024-04-05 10:25 ` [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full Sebastian Urban 2024-04-05 10:56 ` [v2] " bluez.test.bot @ 2024-04-05 15:30 ` Luiz Augusto von Dentz 2024-04-07 18:57 ` Sebastian Urban 1 sibling, 1 reply; 10+ messages in thread From: Luiz Augusto von Dentz @ 2024-04-05 15:30 UTC (permalink / raw) To: Sebastian Urban Cc: Marcel Holtmann, Johan Hedberg, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-bluetooth, netdev, linux-kernel 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 > -- Luiz Augusto von Dentz ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full 2024-04-05 15:30 ` [PATCH v2] " Luiz Augusto von Dentz @ 2024-04-07 18:57 ` Sebastian Urban 0 siblings, 0 replies; 10+ messages in thread From: Sebastian Urban @ 2024-04-07 18:57 UTC (permalink / raw) To: Luiz Augusto von Dentz Cc: Marcel Holtmann, Johan Hedberg, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-bluetooth@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org 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. ^ permalink raw reply [flat|nested] 10+ messages in thread
* RE: l2cap: do not return LE flow credits when buf full 2024-04-05 9:38 [PATCH] l2cap: do not return LE flow credits when buf full Sebastian Urban 2024-04-05 10:25 ` [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full Sebastian Urban @ 2024-04-05 10:33 ` bluez.test.bot 2024-04-08 0:56 ` [PATCH v3] Bluetooth: compute LE flow credits based on recvbuf space Sebastian Urban 2 siblings, 0 replies; 10+ messages in thread From: bluez.test.bot @ 2024-04-05 10:33 UTC (permalink / raw) To: linux-bluetooth, surban [-- Attachment #1: Type: text/plain, Size: 2479 bytes --] 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=841735 ---Test result--- Test Summary: CheckPatch PASS 1.13 seconds GitLint PASS 0.29 seconds SubjectPrefix FAIL 0.36 seconds BuildKernel PASS 29.62 seconds CheckAllWarning PASS 32.32 seconds CheckSparse PASS 37.84 seconds CheckSmatch FAIL 34.71 seconds BuildKernel32 PASS 28.82 seconds TestRunnerSetup PASS 517.48 seconds TestRunner_l2cap-tester PASS 22.31 seconds TestRunner_iso-tester PASS 32.29 seconds TestRunner_bnep-tester PASS 4.77 seconds TestRunner_mgmt-tester PASS 108.24 seconds TestRunner_rfcomm-tester PASS 7.13 seconds TestRunner_sco-tester PASS 14.91 seconds TestRunner_ioctl-tester PASS 7.51 seconds TestRunner_mesh-tester PASS 5.65 seconds TestRunner_smp-tester PASS 6.60 seconds TestRunner_userchan-tester PASS 4.86 seconds IncrementalBuild PASS 27.56 seconds Details ############################## Test: SubjectPrefix - FAIL Desc: Check subject contains "Bluetooth" prefix Output: "Bluetooth: " prefix is not specified in the subject ############################## 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 --- Regards, Linux Bluetooth ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3] Bluetooth: compute LE flow credits based on recvbuf space 2024-04-05 9:38 [PATCH] l2cap: do not return LE flow credits when buf full Sebastian Urban 2024-04-05 10:25 ` [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full Sebastian Urban 2024-04-05 10:33 ` l2cap: do not return LE flow credits when buf full bluez.test.bot @ 2024-04-08 0:56 ` Sebastian Urban 2024-04-08 1:47 ` [v3] " bluez.test.bot 2024-04-08 9:41 ` [PATCH v4] " Sebastian Urban 2 siblings, 2 replies; 10+ messages in thread From: Sebastian Urban @ 2024-04-08 0:56 UTC (permalink / raw) Cc: Sebastian Urban, Marcel Holtmann, Johan Hedberg, Luiz Augusto von Dentz, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-bluetooth, netdev, linux-kernel 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. Furthermore, the amount of credits was essentially fixed to a small amount, leading to reduced performance. This is fixed by computing the number of returned LE flow credits based on the available space in the receive buffer of an L2CAP socket. Consequently, if the receive buffer is full, no credits are returned until the buffer is read and thus cleared by user-space. Since the computation of available receive buffer space can only be performed approximately, i.e. sk_buff overhead is ignored, and the receive buffer size may be changed by user-space after flow credits have been sent, superfluous received data is temporary stored within l2cap_pinfo. This is necessary because 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 | 10 +++++- net/bluetooth/l2cap_core.c | 50 ++++++++++++++++++++++---- net/bluetooth/l2cap_sock.c | 67 +++++++++++++++++++++++++---------- 3 files changed, 102 insertions(+), 25 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 92d7197f9a56..8368463ada72 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -547,6 +547,8 @@ struct l2cap_chan { __u16 tx_credits; __u16 rx_credits; + int rx_avail; + int rx_staged; __u8 tx_state; __u8 rx_state; @@ -682,10 +684,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 { @@ -943,6 +950,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); +void l2cap_chan_rx_avail(struct l2cap_chan *chan, int rx_avail); int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); void l2cap_chan_set_defaults(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ab5a9d42fae7..1b5f5c3c2b41 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -454,6 +454,9 @@ struct l2cap_chan *l2cap_chan_create(void) /* Set default lock nesting level */ atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL); + /* Available receive buffer space is initially unknown */ + chan->rx_avail = -1; + write_lock(&chan_list_lock); list_add(&chan->global_l, &chan_list); write_unlock(&chan_list_lock); @@ -535,6 +538,26 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) } EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults); +static __u16 l2cap_le_rx_credits(struct l2cap_chan *chan) +{ + if (chan->mps == 0) + return 0; + + /* If we don't know the available space in the receiver buffer, give + * enough credits for a full packet. + */ + if (chan->rx_avail == -1) + return (chan->imtu / chan->mps) + 1; + + /* If we know how much space is available in the receive buffer, give + * out as many credits as would fill the buffer. + */ + if (chan->rx_avail <= chan->rx_staged) + return 0; + return min_t(int, U16_MAX, + (chan->rx_avail - chan->rx_staged) / chan->mps); +} + static void l2cap_le_flowctl_init(struct l2cap_chan *chan, u16 tx_credits) { chan->sdu = NULL; @@ -543,8 +566,7 @@ static void l2cap_le_flowctl_init(struct l2cap_chan *chan, u16 tx_credits) chan->tx_credits = tx_credits; /* Derive MPS from connection MTU to stop HCI fragmentation */ chan->mps = min_t(u16, chan->imtu, chan->conn->mtu - L2CAP_HDR_SIZE); - /* Give enough credits for a full packet */ - chan->rx_credits = (chan->imtu / chan->mps) + 1; + chan->rx_credits = l2cap_le_rx_credits(chan); skb_queue_head_init(&chan->tx_q); } @@ -556,7 +578,7 @@ static void l2cap_ecred_init(struct l2cap_chan *chan, u16 tx_credits) /* L2CAP implementations shall support a minimum MPS of 64 octets */ if (chan->mps < L2CAP_ECRED_MIN_MPS) { chan->mps = L2CAP_ECRED_MIN_MPS; - chan->rx_credits = (chan->imtu / chan->mps) + 1; + chan->rx_credits = l2cap_le_rx_credits(chan); } } @@ -6512,9 +6534,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; struct l2cap_le_credits pkt; - u16 return_credits; - - return_credits = (chan->imtu / chan->mps) + 1; + u16 return_credits = l2cap_le_rx_credits(chan); if (chan->rx_credits >= return_credits) return; @@ -6533,6 +6553,15 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); } +void l2cap_chan_rx_avail(struct l2cap_chan *chan, int rx_avail) +{ + BT_DBG("chan %p has %d bytes avail for rx", chan, rx_avail); + + chan->rx_avail = rx_avail; + + l2cap_chan_le_send_credits(chan); +} + static int l2cap_ecred_recv(struct l2cap_chan *chan, struct sk_buff *skb) { int err; @@ -6542,6 +6571,14 @@ 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); + chan->rx_staged = 0; + + if (err < 0 && chan->rx_avail != -1) { + 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); @@ -6563,6 +6600,7 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) return -ENOBUFS; } + chan->rx_staged += skb->len; chan->rx_credits--; BT_DBG("rx_credits %u -> %u", chan->rx_credits + 1, chan->rx_credits); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index ee7a41d6994f..8781f67f9c84 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1146,6 +1146,7 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; struct l2cap_pinfo *pi = l2cap_pi(sk); + int avail; int err; lock_sock(sk); @@ -1177,28 +1178,34 @@ 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 */ - lock_sock(sk); - if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state)) - goto done; + avail = max(0, sk->sk_rcvbuf - atomic_read(&sk->sk_rmem_alloc)); + l2cap_chan_rx_avail(pi->chan, avail); - if (pi->rx_busy_skb) { - if (!__sock_queue_rcv_skb(sk, pi->rx_busy_skb)) - pi->rx_busy_skb = NULL; - else + /* Attempt to put pending rx data in the socket buffer */ + 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 * available. This avoids resending large numbers of * frames. */ - if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) + if (test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state) && + atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) l2cap_chan_busy(pi->chan, 0); done: @@ -1459,17 +1466,21 @@ 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 avail; 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,7 +1491,10 @@ 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 + avail = max(0, sk->sk_rcvbuf - atomic_read(&sk->sk_rmem_alloc)); + l2cap_chan_rx_avail(chan, avail); + + /* 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. * @@ -1489,8 +1503,18 @@ static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) * acked and reassembled until there is buffer space * available. */ - 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 +1740,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 +1749,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 +1857,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); @@ -1838,6 +1867,8 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, l2cap_chan_hold(chan); + l2cap_chan_rx_avail(chan, sk->sk_rcvbuf); + l2cap_pi(sk)->chan = chan; return sk; -- 2.34.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* RE: [v3] Bluetooth: compute LE flow credits based on recvbuf space 2024-04-08 0:56 ` [PATCH v3] Bluetooth: compute LE flow credits based on recvbuf space Sebastian Urban @ 2024-04-08 1:47 ` bluez.test.bot 2024-04-08 9:41 ` [PATCH v4] " Sebastian Urban 1 sibling, 0 replies; 10+ messages in thread From: bluez.test.bot @ 2024-04-08 1:47 UTC (permalink / raw) To: linux-bluetooth, surban [-- Attachment #1: Type: text/plain, Size: 669 bytes --] This is an automated email and please do not reply to this email. Dear Submitter, Thank you for submitting the patches to the linux bluetooth mailing list. While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository. ----- Output ----- error: patch failed: include/net/bluetooth/l2cap.h:943 error: include/net/bluetooth/l2cap.h: patch does not apply error: patch failed: net/bluetooth/l2cap_sock.c:1146 error: net/bluetooth/l2cap_sock.c: patch does not apply hint: Use 'git am --show-current-patch' to see the failed patch Please resolve the issue and submit the patches again. --- Regards, Linux Bluetooth ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v4] Bluetooth: compute LE flow credits based on recvbuf space 2024-04-08 0:56 ` [PATCH v3] Bluetooth: compute LE flow credits based on recvbuf space Sebastian Urban 2024-04-08 1:47 ` [v3] " bluez.test.bot @ 2024-04-08 9:41 ` Sebastian Urban 2024-04-08 10:35 ` [v4] " bluez.test.bot 1 sibling, 1 reply; 10+ messages in thread From: Sebastian Urban @ 2024-04-08 9:41 UTC (permalink / raw) Cc: Sebastian Urban, Marcel Holtmann, Johan Hedberg, Luiz Augusto von Dentz, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-bluetooth, netdev, linux-kernel 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. Furthermore, the amount of credits was essentially fixed to a small amount, leading to reduced performance. This is fixed by computing the number of returned LE flow credits based on the available space in the receive buffer of an L2CAP socket. Consequently, if the receive buffer is full, no credits are returned until the buffer is read and thus cleared by user-space. Since the computation of available receive buffer space can only be performed approximately, i.e. sk_buff overhead is ignored, and the receive buffer size may be changed by user-space after flow credits have been sent, superfluous received data is temporary stored within l2cap_pinfo. This is necessary because 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 | 10 +++++- net/bluetooth/l2cap_core.c | 50 ++++++++++++++++++++++---- net/bluetooth/l2cap_sock.c | 67 +++++++++++++++++++++++++---------- 3 files changed, 102 insertions(+), 25 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 3f4057ced971..bc6ff40ebc9f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -547,6 +547,8 @@ struct l2cap_chan { __u16 tx_credits; __u16 rx_credits; + int rx_avail; + int rx_staged; __u8 tx_state; __u8 rx_state; @@ -682,10 +684,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 { @@ -944,6 +951,7 @@ int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, const struct sockcm_cookie *sockc); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); +void l2cap_chan_rx_avail(struct l2cap_chan *chan, int rx_avail); int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); void l2cap_chan_set_defaults(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b0970462a689..f3fc63992b00 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -454,6 +454,9 @@ struct l2cap_chan *l2cap_chan_create(void) /* Set default lock nesting level */ atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL); + /* Available receive buffer space is initially unknown */ + chan->rx_avail = -1; + write_lock(&chan_list_lock); list_add(&chan->global_l, &chan_list); write_unlock(&chan_list_lock); @@ -535,6 +538,26 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) } EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults); +static __u16 l2cap_le_rx_credits(struct l2cap_chan *chan) +{ + if (chan->mps == 0) + return 0; + + /* If we don't know the available space in the receiver buffer, give + * enough credits for a full packet. + */ + if (chan->rx_avail == -1) + return (chan->imtu / chan->mps) + 1; + + /* If we know how much space is available in the receive buffer, give + * out as many credits as would fill the buffer. + */ + if (chan->rx_avail <= chan->rx_staged) + return 0; + return min_t(int, U16_MAX, + (chan->rx_avail - chan->rx_staged) / chan->mps); +} + static void l2cap_le_flowctl_init(struct l2cap_chan *chan, u16 tx_credits) { chan->sdu = NULL; @@ -543,8 +566,7 @@ static void l2cap_le_flowctl_init(struct l2cap_chan *chan, u16 tx_credits) chan->tx_credits = tx_credits; /* Derive MPS from connection MTU to stop HCI fragmentation */ chan->mps = min_t(u16, chan->imtu, chan->conn->mtu - L2CAP_HDR_SIZE); - /* Give enough credits for a full packet */ - chan->rx_credits = (chan->imtu / chan->mps) + 1; + chan->rx_credits = l2cap_le_rx_credits(chan); skb_queue_head_init(&chan->tx_q); } @@ -556,7 +578,7 @@ static void l2cap_ecred_init(struct l2cap_chan *chan, u16 tx_credits) /* L2CAP implementations shall support a minimum MPS of 64 octets */ if (chan->mps < L2CAP_ECRED_MIN_MPS) { chan->mps = L2CAP_ECRED_MIN_MPS; - chan->rx_credits = (chan->imtu / chan->mps) + 1; + chan->rx_credits = l2cap_le_rx_credits(chan); } } @@ -6520,9 +6542,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; struct l2cap_le_credits pkt; - u16 return_credits; - - return_credits = (chan->imtu / chan->mps) + 1; + u16 return_credits = l2cap_le_rx_credits(chan); if (chan->rx_credits >= return_credits) return; @@ -6541,6 +6561,15 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); } +void l2cap_chan_rx_avail(struct l2cap_chan *chan, int rx_avail) +{ + BT_DBG("chan %p has %d bytes avail for rx", chan, rx_avail); + + chan->rx_avail = rx_avail; + + l2cap_chan_le_send_credits(chan); +} + static int l2cap_ecred_recv(struct l2cap_chan *chan, struct sk_buff *skb) { int err; @@ -6550,6 +6579,14 @@ 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); + chan->rx_staged = 0; + + if (err < 0 && chan->rx_avail != -1) { + 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); @@ -6571,6 +6608,7 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) return -ENOBUFS; } + chan->rx_staged += skb->len; chan->rx_credits--; BT_DBG("rx_credits %u -> %u", chan->rx_credits + 1, chan->rx_credits); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 7846a068bf60..46603605cb69 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1157,6 +1157,7 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; struct l2cap_pinfo *pi = l2cap_pi(sk); + int avail; int err; if (unlikely(flags & MSG_ERRQUEUE)) @@ -1192,28 +1193,34 @@ 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 */ - lock_sock(sk); - if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state)) - goto done; + avail = max(0, sk->sk_rcvbuf - atomic_read(&sk->sk_rmem_alloc)); + l2cap_chan_rx_avail(pi->chan, avail); - if (pi->rx_busy_skb) { - if (!__sock_queue_rcv_skb(sk, pi->rx_busy_skb)) - pi->rx_busy_skb = NULL; - else + /* Attempt to put pending rx data in the socket buffer */ + 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 * available. This avoids resending large numbers of * frames. */ - if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) + if (test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state) && + atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) l2cap_chan_busy(pi->chan, 0); done: @@ -1474,17 +1481,21 @@ 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 avail; 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. */ @@ -1495,7 +1506,10 @@ 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 + avail = max(0, sk->sk_rcvbuf - atomic_read(&sk->sk_rmem_alloc)); + l2cap_chan_rx_avail(chan, avail); + + /* 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. * @@ -1504,8 +1518,18 @@ static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) * acked and reassembled until there is buffer space * available. */ - 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; } @@ -1731,6 +1755,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) { @@ -1738,9 +1764,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); @@ -1845,6 +1872,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); @@ -1853,6 +1882,8 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, l2cap_chan_hold(chan); + l2cap_chan_rx_avail(chan, sk->sk_rcvbuf); + l2cap_pi(sk)->chan = chan; return sk; -- 2.34.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* RE: [v4] Bluetooth: compute LE flow credits based on recvbuf space 2024-04-08 9:41 ` [PATCH v4] " Sebastian Urban @ 2024-04-08 10:35 ` bluez.test.bot 0 siblings, 0 replies; 10+ messages in thread From: bluez.test.bot @ 2024-04-08 10:35 UTC (permalink / raw) To: linux-bluetooth, surban [-- Attachment #1: Type: text/plain, Size: 2821 bytes --] 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=842388 ---Test result--- Test Summary: CheckPatch PASS 0.96 seconds GitLint PASS 0.22 seconds SubjectPrefix PASS 0.07 seconds BuildKernel PASS 30.01 seconds CheckAllWarning PASS 32.77 seconds CheckSparse PASS 38.02 seconds CheckSmatch FAIL 35.04 seconds BuildKernel32 PASS 29.01 seconds TestRunnerSetup PASS 521.51 seconds TestRunner_l2cap-tester FAIL 20.61 seconds TestRunner_iso-tester PASS 127.88 seconds TestRunner_bnep-tester PASS 4.73 seconds TestRunner_mgmt-tester PASS 110.45 seconds TestRunner_rfcomm-tester PASS 7.26 seconds TestRunner_sco-tester PASS 15.03 seconds TestRunner_ioctl-tester PASS 7.69 seconds TestRunner_mesh-tester PASS 5.84 seconds TestRunner_smp-tester PASS 6.78 seconds TestRunner_userchan-tester PASS 4.91 seconds IncrementalBuild PASS 28.05 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_l2cap-tester - FAIL Desc: Run l2cap-tester with test-runner Output: Total: 55, Passed: 51 (92.7%), Failed: 4, Not Run: 0 Failed Test Cases L2CAP LE Server - Success Failed 0.093 seconds L2CAP Ext-Flowctl Server - Success Failed 0.102 seconds L2CAP LE EATT Server - Success Failed 0.111 seconds L2CAP LE EATT Server - Reject Failed 0.090 seconds --- Regards, Linux Bluetooth ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2024-04-08 10:35 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-04-05 9:38 [PATCH] l2cap: do not return LE flow credits when buf full Sebastian Urban 2024-04-05 10:25 ` [PATCH v2] Bluetooth: keep LE flow credits when recvbuf full Sebastian Urban 2024-04-05 10:56 ` [v2] " bluez.test.bot 2024-04-05 15:30 ` [PATCH v2] " Luiz Augusto von Dentz 2024-04-07 18:57 ` Sebastian Urban 2024-04-05 10:33 ` l2cap: do not return LE flow credits when buf full bluez.test.bot 2024-04-08 0:56 ` [PATCH v3] Bluetooth: compute LE flow credits based on recvbuf space Sebastian Urban 2024-04-08 1:47 ` [v3] " bluez.test.bot 2024-04-08 9:41 ` [PATCH v4] " Sebastian Urban 2024-04-08 10:35 ` [v4] " bluez.test.bot
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.