Bluetooth: Merge bt_conn TX threads into a single one with k_poll

Now that the k_poll API is available we can use it to have single
connection TX thread instead of multiple ones, which helps reduce the
per-connection memory overhead by a substantial amount.

Change-Id: Icb5d4da87cf0d660bba8da43186d1e76f41c825a
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2017-02-02 08:24:28 +02:00
commit 1fb0351f9a
4 changed files with 126 additions and 45 deletions

View file

@ -30,6 +30,7 @@ config BLUETOOTH_HCI_HOST
bool bool
default y default y
depends on !BLUETOOTH_HCI_RAW depends on !BLUETOOTH_HCI_RAW
select POLL
select TINYCRYPT if !BLUETOOTH_CONTROLLER select TINYCRYPT if !BLUETOOTH_CONTROLLER
select TINYCRYPT_SHA256 if !BLUETOOTH_CONTROLLER select TINYCRYPT_SHA256 if !BLUETOOTH_CONTROLLER
select TINYCRYPT_SHA256_HMAC if !BLUETOOTH_CONTROLLER select TINYCRYPT_SHA256_HMAC if !BLUETOOTH_CONTROLLER

View file

@ -13,6 +13,7 @@
#include <atomic.h> #include <atomic.h>
#include <misc/byteorder.h> #include <misc/byteorder.h>
#include <misc/util.h> #include <misc/util.h>
#include <misc/stack.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_DEBUG_CONN) #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_DEBUG_CONN)
#include <bluetooth/log.h> #include <bluetooth/log.h>
@ -43,6 +44,8 @@ const struct bt_conn_auth_cb *bt_auth;
static struct bt_conn conns[CONFIG_BLUETOOTH_MAX_CONN]; static struct bt_conn conns[CONFIG_BLUETOOTH_MAX_CONN];
static struct bt_conn_cb *callback_list; static struct bt_conn_cb *callback_list;
static BT_STACK_NOINIT(conn_tx_stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE);
#if defined(CONFIG_BLUETOOTH_BREDR) #if defined(CONFIG_BLUETOOTH_BREDR)
enum pairing_method { enum pairing_method {
LEGACY, /* Legacy (pre-SSP) pairing */ LEGACY, /* Legacy (pre-SSP) pairing */
@ -1016,39 +1019,123 @@ static bool send_buf(struct bt_conn *conn, struct net_buf *buf)
return send_frag(conn, buf, BT_ACL_CONT, false); return send_frag(conn, buf, BT_ACL_CONT, false);
} }
static void conn_tx_thread(void *p1, void *p2, void *p3) static struct k_poll_signal conn_change = K_POLL_SIGNAL_INITIALIZER();
static int prepare_events(struct k_poll_event events[])
{ {
struct bt_conn *conn = p1; int i, ev_count = 0;
BT_DBG("");
k_poll_event_init(&events[ev_count++], K_POLL_TYPE_SIGNAL,
K_POLL_MODE_NOTIFY_ONLY, &conn_change);
for (i = 0; i < ARRAY_SIZE(conns); i++) {
struct bt_conn *conn = &conns[i];
if (!atomic_get(&conn->ref)) {
continue;
}
if (conn->state != BT_CONN_CONNECTED) {
continue;
}
BT_DBG("Adding conn %p to poll list", conn);
k_poll_event_init(&events[ev_count++],
K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&conn->tx_queue);
}
return ev_count;
}
static void process_fifo(struct k_fifo *fifo)
{
struct bt_conn *conn = CONTAINER_OF(fifo, struct bt_conn,
tx_queue);
struct net_buf *buf; struct net_buf *buf;
BT_DBG("Started for handle %u", conn->handle); BT_DBG("conn %p", conn);
while (conn->state == BT_CONN_CONNECTED) { if (conn->state != BT_CONN_CONNECTED) {
/* Get next ACL packet for connection */ BT_DBG("handle %u disconnected - cleaning up", conn->handle);
buf = net_buf_get(&conn->tx_queue, K_FOREVER);
if (conn->state != BT_CONN_CONNECTED) {
net_buf_unref(buf);
break;
}
if (!send_buf(conn, buf)) { /* Give back any allocated buffers */
while ((buf = net_buf_get(&conn->tx_queue, K_NO_WAIT))) {
net_buf_unref(buf); net_buf_unref(buf);
} }
BT_ASSERT(!conn->pending_pkts);
bt_conn_reset_rx_state(conn);
/* Release the reference we took for the very first
* state transition.
*/
bt_conn_unref(conn);
stack_analyze("conn tx stack", conn_tx_stack,
sizeof(conn_tx_stack));
return;
} }
BT_DBG("handle %u disconnected - cleaning up", conn->handle); /* Get next ACL packet for connection */
buf = net_buf_get(&conn->tx_queue, K_NO_WAIT);
/* Give back any allocated buffers */ BT_ASSERT(buf);
while ((buf = net_buf_get(&conn->tx_queue, K_NO_WAIT))) { if (!send_buf(conn, buf)) {
net_buf_unref(buf); net_buf_unref(buf);
} }
}
BT_ASSERT(!conn->pending_pkts); static void process_events(struct k_poll_event *ev, int count)
{
BT_DBG("count %d", count);
bt_conn_reset_rx_state(conn); for (; count; ev++, count--) {
BT_DBG("ev->state %u", ev->state);
BT_DBG("handle %u exiting", conn->handle); switch (ev->state) {
bt_conn_unref(conn); case K_POLL_STATE_SIGNALED:
/* prepare_events() will do the right thing */
break;
case K_POLL_STATE_FIFO_DATA_AVAILABLE:
process_fifo(ev->fifo);
break;
case K_POLL_STATE_NOT_READY:
break;
default:
BT_WARN("Unexpected k_poll event state %u", ev->state);
break;
}
}
}
static void conn_tx_thread(void *p1, void *p2, void *p3)
{
/* conn change signal + MAX_CONN */
static struct k_poll_event events[1 + CONFIG_BLUETOOTH_MAX_CONN];
BT_DBG("Started");
while (1) {
int ev_count, err;
ev_count = prepare_events(events);
BT_DBG("Calling k_poll with %d events", ev_count);
err = k_poll(events, ev_count, K_FOREVER);
if (err && err != -EADDRINUSE) {
BT_ERR("k_poll failed with err %d", err);
continue;
}
process_events(events, ev_count);
}
} }
struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer) struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer)
@ -1109,9 +1196,7 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state)
switch (conn->state) { switch (conn->state) {
case BT_CONN_CONNECTED: case BT_CONN_CONNECTED:
k_fifo_init(&conn->tx_queue); k_fifo_init(&conn->tx_queue);
k_thread_spawn(conn->stack, sizeof(conn->stack), conn_tx_thread, k_poll_signal(&conn_change, 0);
bt_conn_ref(conn), NULL, NULL, K_PRIO_COOP(7),
0, K_NO_WAIT);
bt_l2cap_connected(conn); bt_l2cap_connected(conn);
notify_connected(conn); notify_connected(conn);
@ -1125,32 +1210,31 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state)
old_state == BT_CONN_DISCONNECT) { old_state == BT_CONN_DISCONNECT) {
bt_l2cap_disconnected(conn); bt_l2cap_disconnected(conn);
notify_disconnected(conn); notify_disconnected(conn);
/* Return any unacknowledged packets */
while (conn->pending_pkts) {
k_sem_give(bt_conn_get_pkts(conn));
conn->pending_pkts--;
}
/* Cancel Connection Update if it is pending */
if (conn->type == BT_CONN_TYPE_LE) {
k_delayed_work_cancel(&conn->le.update_work);
}
net_buf_put(&conn->tx_queue, net_buf_put(&conn->tx_queue,
bt_conn_create_pdu(NULL, 0)); bt_conn_create_pdu(NULL, 0));
/* The last ref will be dropped by the tx_thread */
} else if (old_state == BT_CONN_CONNECT) { } else if (old_state == BT_CONN_CONNECT) {
/* conn->err will be set in this case */ /* conn->err will be set in this case */
notify_connected(conn); notify_connected(conn);
bt_conn_unref(conn);
} else if (old_state == BT_CONN_CONNECT_SCAN && conn->err) { } else if (old_state == BT_CONN_CONNECT_SCAN && conn->err) {
/* this indicate LE Create Connection failed */ /* this indicate LE Create Connection failed */
notify_connected(conn); notify_connected(conn);
bt_conn_unref(conn);
} }
/* Return any unacknowledged packets */
while (conn->pending_pkts) {
k_sem_give(bt_conn_get_pkts(conn));
conn->pending_pkts--;
}
/* Cancel Connection Update if it is pending */
if (conn->type == BT_CONN_TYPE_LE) {
k_delayed_work_cancel(&conn->le.update_work);
}
/* Release the reference we took for the very first
* state transition.
*/
bt_conn_unref(conn);
break; break;
case BT_CONN_CONNECT_SCAN: case BT_CONN_CONNECT_SCAN:
break; break;
@ -1720,6 +1804,9 @@ int bt_conn_init(void)
bt_l2cap_init(); bt_l2cap_init();
k_thread_spawn(conn_tx_stack, sizeof(conn_tx_stack), conn_tx_thread,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
/* Initialize background scan */ /* Initialize background scan */
if (IS_ENABLED(CONFIG_BLUETOOTH_CENTRAL)) { if (IS_ENABLED(CONFIG_BLUETOOTH_CENTRAL)) {
int i; int i;

View file

@ -102,12 +102,6 @@ struct bt_conn {
struct bt_conn_br br; struct bt_conn_br br;
#endif #endif
}; };
/* Stack for TX thread and timeout thread.
* Since these threads don't overlap, one stack can be used by
* both of them.
*/
BT_STACK(stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE);
}; };
/* Process incoming data for a connection */ /* Process incoming data for a connection */

View file

@ -554,7 +554,6 @@ static void hci_disconn_complete(struct net_buf *buf)
#endif #endif
stack_analyze("cmd tx stack", cmd_tx_thread_stack, stack_analyze("cmd tx stack", cmd_tx_thread_stack,
sizeof(cmd_tx_thread_stack)); sizeof(cmd_tx_thread_stack));
stack_analyze("conn tx stack", conn->stack, sizeof(conn->stack));
bt_conn_set_state(conn, BT_CONN_DISCONNECTED); bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
conn->handle = 0; conn->handle = 0;