[PATCH] Bluetooth: hci_h5: Avoid clearing the escape bit for ordinary bytes

From: Xuhua Zhang

Date: Tue Sep 08 2026 - 02:08:12 EST


h5_unslip_one_byte() calls test_and_clear_bit() for every byte other than
an initial SLIP escape byte, even when H5_RX_ESC is already clear. This
performs an unnecessary atomic read-modify-write on the ordinary-byte
receive path. On x86, it emits a locked btr instruction for each such
byte.

Test H5_RX_ESC first and clear it only when consuming an escaped byte.
Handle an initial SLIP escape byte in the alternative branch. The HCI
UART receive callbacks are serialized by the TTY layer, and H5_RX_ESC
is only changed by receive processing and initialization, so there is
no need to atomically test and clear it in one operation.

Keep set_bit() and clear_bit() atomic because other bits in h5->flags
can be updated by the transmit path concurrently. Escape decoding and
invalid-escape recovery remain unchanged.

Signed-off-by: Xuhua Zhang <zhangxuhua@xxxxxxxxxxxxxxx>
---
drivers/bluetooth/hci_h5.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index b1999e14aade..37f4d9327e72 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -543,12 +543,8 @@ static void h5_unslip_one_byte(struct h5 *h5, unsigned char c)
const u8 delim = SLIP_DELIMITER, esc = SLIP_ESC;
const u8 *byte = &c;

- if (!test_bit(H5_RX_ESC, &h5->flags) && c == SLIP_ESC) {
- set_bit(H5_RX_ESC, &h5->flags);
- return;
- }
-
- if (test_and_clear_bit(H5_RX_ESC, &h5->flags)) {
+ if (test_bit(H5_RX_ESC, &h5->flags)) {
+ clear_bit(H5_RX_ESC, &h5->flags);
switch (c) {
case SLIP_ESC_DELIM:
byte = &delim;
@@ -561,6 +557,9 @@ static void h5_unslip_one_byte(struct h5 *h5, unsigned char c)
h5_reset_rx(h5);
return;
}
+ } else if (c == SLIP_ESC) {
+ set_bit(H5_RX_ESC, &h5->flags);
+ return;
}

skb_put_data(h5->rx_skb, byte, 1);
--
2.43.0