Skip to content

Commit 8028056

Browse files
committed
[nrf fromtree] bluetooth: buf: Add a callback for freed buffer in rx pool
The Bluetooth data buffer API currently lacks a mechanism to notify when a buffer is freed in the RX pool. This limitation forces HCI drivers to adopt inefficient workarounds to manage buffer allocation. HCI drivers face two suboptimal options: - Blocking calls: Use bt_buf_get_rx with K_FOREVER, which blocks the execution context until a buffer becomes available. - Polling: Repeatedly call bt_buf_get_rx with K_NO_WAIT, which increases CPU load and reduces efficiency. This commit introduces a callback mechanism that is triggered each time a buffer is freed in the RX pool. With this feature, HCI drivers can: - Call bt_buf_get_rx with K_NO_WAIT. - Wait for the callback notification if a NULL buffer is returned, avoiding unnecessary polling. The new callback improves efficiency by enabling event-driven behavior for buffer management, reducing CPU overhead while maintaining responsiveness. Signed-off-by: Pavel Vasilyev <[email protected]> (cherry picked from commit c2488fd)
1 parent 418b9c2 commit 8028056

File tree

5 files changed

+130
-12
lines changed

5 files changed

+130
-12
lines changed

include/zephyr/bluetooth/buf.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,27 @@ BUILD_ASSERT(BT_BUF_ACL_RX_COUNT <= BT_BUF_ACL_RX_COUNT_MAX,
138138
*/
139139
struct net_buf *bt_buf_get_rx(enum bt_buf_type type, k_timeout_t timeout);
140140

141+
/** A callback to notify about freed buffer in the incoming data pool.
142+
*
143+
* This callback is called when a buffer of a given type is freed and can be requested through the
144+
* @ref bt_buf_get_rx function. However, this callback is called from the context of the buffer
145+
* freeing operation and must not attempt to allocate a new buffer from the same pool.
146+
*
147+
* @warning When this callback is called, the scheduler is locked and the callee must not perform
148+
* any action that makes the current thread unready. This callback must only be used for very
149+
* short non-blocking operation (e.g. submitting a work item).
150+
*
151+
* @param type_mask A bit mask of buffer types that have been freed.
152+
*/
153+
typedef void (*bt_buf_rx_freed_cb_t)(enum bt_buf_type type_mask);
154+
155+
/** Set the callback to notify about freed buffer in the incoming data pool.
156+
*
157+
* @param cb Callback to notify about freed buffer in the incoming data pool. If NULL, the callback
158+
* is disabled.
159+
*/
160+
void bt_buf_rx_freed_cb_set(bt_buf_rx_freed_cb_t cb);
161+
141162
/** Allocate a buffer for outgoing data
142163
*
143164
* This will set the buffer type so bt_buf_set_type() does not need to

subsys/bluetooth/host/buf.c

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,26 @@ LOG_MODULE_REGISTER(bt_buf, CONFIG_BT_LOG_LEVEL);
2525
*/
2626
#define SYNC_EVT_SIZE (BT_BUF_RESERVE + BT_HCI_EVT_HDR_SIZE + 255)
2727

28+
static bt_buf_rx_freed_cb_t buf_rx_freed_cb;
29+
30+
static void buf_rx_freed_notify(enum bt_buf_type mask)
31+
{
32+
k_sched_lock();
33+
34+
if (buf_rx_freed_cb) {
35+
buf_rx_freed_cb(mask);
36+
}
37+
38+
k_sched_unlock();
39+
}
40+
41+
#if defined(CONFIG_BT_ISO_RX)
42+
static void iso_rx_freed_cb(void)
43+
{
44+
buf_rx_freed_notify(BT_BUF_ISO_IN);
45+
}
46+
#endif
47+
2848
/* Pool for RX HCI buffers that are always freed by `bt_recv`
2949
* before it returns.
3050
*
@@ -40,17 +60,37 @@ NET_BUF_POOL_FIXED_DEFINE(discardable_pool, CONFIG_BT_BUF_EVT_DISCARDABLE_COUNT,
4060
sizeof(struct bt_buf_data), NULL);
4161

4262
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
43-
NET_BUF_POOL_DEFINE(acl_in_pool, BT_BUF_ACL_RX_COUNT,
44-
BT_BUF_ACL_SIZE(CONFIG_BT_BUF_ACL_RX_SIZE),
45-
sizeof(struct acl_data), bt_hci_host_num_completed_packets);
63+
static void acl_in_pool_destroy(struct net_buf *buf)
64+
{
65+
bt_hci_host_num_completed_packets(buf);
66+
buf_rx_freed_notify(BT_BUF_ACL_IN);
67+
}
4668

47-
NET_BUF_POOL_FIXED_DEFINE(evt_pool, CONFIG_BT_BUF_EVT_RX_COUNT,
48-
BT_BUF_EVT_RX_SIZE, sizeof(struct bt_buf_data),
49-
NULL);
69+
static void evt_pool_destroy(struct net_buf *buf)
70+
{
71+
net_buf_destroy(buf);
72+
buf_rx_freed_notify(BT_BUF_EVT);
73+
}
74+
75+
NET_BUF_POOL_DEFINE(acl_in_pool, BT_BUF_ACL_RX_COUNT, BT_BUF_ACL_SIZE(CONFIG_BT_BUF_ACL_RX_SIZE),
76+
sizeof(struct acl_data), acl_in_pool_destroy);
77+
78+
NET_BUF_POOL_FIXED_DEFINE(evt_pool, CONFIG_BT_BUF_EVT_RX_COUNT, BT_BUF_EVT_RX_SIZE,
79+
sizeof(struct bt_buf_data), evt_pool_destroy);
5080
#else
51-
NET_BUF_POOL_FIXED_DEFINE(hci_rx_pool, BT_BUF_RX_COUNT,
52-
BT_BUF_RX_SIZE, sizeof(struct acl_data),
53-
NULL);
81+
static void hci_rx_pool_destroy(struct net_buf *buf)
82+
{
83+
net_buf_destroy(buf);
84+
85+
/* When ACL Flow Control is disabled, a single pool is used for events and acl data.
86+
* Therefore the callback will always notify about both types of buffers, BT_BUF_EVT and
87+
* BT_BUF_ACL_IN.
88+
*/
89+
buf_rx_freed_notify(BT_BUF_EVT | BT_BUF_ACL_IN);
90+
}
91+
92+
NET_BUF_POOL_FIXED_DEFINE(hci_rx_pool, BT_BUF_RX_COUNT, BT_BUF_RX_SIZE, sizeof(struct acl_data),
93+
hci_rx_pool_destroy);
5494
#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */
5595

5696
struct net_buf *bt_buf_get_rx(enum bt_buf_type type, k_timeout_t timeout)
@@ -82,6 +122,19 @@ struct net_buf *bt_buf_get_rx(enum bt_buf_type type, k_timeout_t timeout)
82122
return buf;
83123
}
84124

125+
void bt_buf_rx_freed_cb_set(bt_buf_rx_freed_cb_t cb)
126+
{
127+
k_sched_lock();
128+
129+
buf_rx_freed_cb = cb;
130+
131+
#if defined(CONFIG_BT_ISO_RX)
132+
bt_iso_buf_rx_freed_cb_set(cb != NULL ? iso_rx_freed_cb : NULL);
133+
#endif
134+
135+
k_sched_unlock();
136+
}
137+
85138
struct net_buf *bt_buf_get_evt(uint8_t evt, bool discardable,
86139
k_timeout_t timeout)
87140
{

subsys/bluetooth/host/hci_raw.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,20 @@ static uint8_t raw_mode = BT_HCI_RAW_MODE_H4;
3939
static uint8_t raw_mode;
4040
#endif
4141

42-
NET_BUF_POOL_FIXED_DEFINE(hci_rx_pool, BT_BUF_RX_COUNT,
43-
BT_BUF_RX_SIZE, sizeof(struct bt_buf_data), NULL);
42+
static bt_buf_rx_freed_cb_t buf_rx_freed_cb;
43+
44+
static void hci_rx_buf_destroy(struct net_buf *buf)
45+
{
46+
net_buf_destroy(buf);
47+
48+
if (buf_rx_freed_cb) {
49+
/* bt_buf_get_rx is used for all types of RX buffers */
50+
buf_rx_freed_cb(BT_BUF_EVT | BT_BUF_ACL_IN | BT_BUF_ISO_IN);
51+
}
52+
}
53+
54+
NET_BUF_POOL_FIXED_DEFINE(hci_rx_pool, BT_BUF_RX_COUNT, BT_BUF_RX_SIZE, sizeof(struct bt_buf_data),
55+
hci_rx_buf_destroy);
4456
NET_BUF_POOL_FIXED_DEFINE(hci_cmd_pool, CONFIG_BT_BUF_CMD_TX_COUNT,
4557
BT_BUF_CMD_SIZE(CONFIG_BT_BUF_CMD_TX_SIZE),
4658
sizeof(struct bt_buf_data), NULL);
@@ -112,6 +124,11 @@ struct net_buf *bt_buf_get_rx(enum bt_buf_type type, k_timeout_t timeout)
112124
return buf;
113125
}
114126

127+
void bt_buf_rx_freed_cb_set(bt_buf_rx_freed_cb_t cb)
128+
{
129+
buf_rx_freed_cb = cb;
130+
}
131+
115132
struct net_buf *bt_buf_get_tx(enum bt_buf_type type, k_timeout_t timeout,
116133
const void *data, size_t size)
117134
{

subsys/bluetooth/host/iso.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,20 @@ LOG_MODULE_REGISTER(bt_iso, CONFIG_BT_ISO_LOG_LEVEL);
5151
#define iso_chan(_iso) ((_iso)->iso.chan);
5252

5353
#if defined(CONFIG_BT_ISO_RX)
54+
static bt_iso_buf_rx_freed_cb_t buf_rx_freed_cb;
55+
56+
static void iso_rx_buf_destroy(struct net_buf *buf)
57+
{
58+
net_buf_destroy(buf);
59+
60+
if (buf_rx_freed_cb) {
61+
buf_rx_freed_cb();
62+
}
63+
}
64+
5465
NET_BUF_POOL_FIXED_DEFINE(iso_rx_pool, CONFIG_BT_ISO_RX_BUF_COUNT,
55-
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_RX_MTU), sizeof(struct iso_data), NULL);
66+
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_RX_MTU), sizeof(struct iso_data),
67+
iso_rx_buf_destroy);
5668

5769
static struct bt_iso_recv_info iso_info_data[CONFIG_BT_ISO_RX_BUF_COUNT];
5870
#define iso_info(buf) (&iso_info_data[net_buf_id(buf)])
@@ -581,6 +593,11 @@ struct net_buf *bt_iso_get_rx(k_timeout_t timeout)
581593
return buf;
582594
}
583595

596+
void bt_iso_buf_rx_freed_cb_set(bt_iso_buf_rx_freed_cb_t cb)
597+
{
598+
buf_rx_freed_cb = cb;
599+
}
600+
584601
void bt_iso_recv(struct bt_conn *iso, struct net_buf *buf, uint8_t flags)
585602
{
586603
struct bt_hci_iso_sdu_hdr *hdr;

subsys/bluetooth/host/iso_internal.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ void hci_iso(struct net_buf *buf);
8787
/* Allocates RX buffer */
8888
struct net_buf *bt_iso_get_rx(k_timeout_t timeout);
8989

90+
/** A callback used to notify about freed buffer in the iso rx pool. */
91+
typedef void (*bt_iso_buf_rx_freed_cb_t)(void);
92+
93+
/** Set a callback to notify about freed buffer in the iso rx pool.
94+
*
95+
* @param cb Callback to notify about freed buffer in the iso rx pool. If NULL, the callback is
96+
* disabled.
97+
*/
98+
void bt_iso_buf_rx_freed_cb_set(bt_iso_buf_rx_freed_cb_t cb);
99+
90100
/* Process CIS Established event */
91101
void hci_le_cis_established(struct net_buf *buf);
92102

0 commit comments

Comments
 (0)