Skip to content
Permalink
Browse files

Bluetooth: conn: Defer bt_conn_tx callback to system wq

This makes the transmission complete callbacks to run on system wq
context so they are not executed in TX thread which usually has a much
smaller, and non-configurable, stack size.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
  • Loading branch information...
Vudentz authored and jhedberg committed May 28, 2019
1 parent 648a53d commit 8a7615f4a3d8932481277dffd41a4a72febd0a61
Showing with 46 additions and 27 deletions.
  1. +44 −27 subsys/bluetooth/host/conn.c
  2. +2 −0 subsys/bluetooth/host/conn_internal.h
@@ -71,7 +71,7 @@ static struct bt_conn_cb *callback_list;
#define conn_tx(buf) ((struct bt_conn_tx_data *)net_buf_user_data(buf))

static struct bt_conn_tx conn_tx[CONFIG_BT_CONN_TX_MAX];
static sys_slist_t free_tx = SYS_SLIST_STATIC_INIT(&free_tx);
K_FIFO_DEFINE(free_tx);

#if defined(CONFIG_BT_BREDR)
static struct bt_conn sco_conns[CONFIG_BT_MAX_SCO_CONN];
@@ -287,6 +287,30 @@ static void conn_le_update_timeout(struct k_work *work)
atomic_set_bit(conn->flags, BT_CONN_SLAVE_PARAM_UPDATE);
}

static void tx_free(struct bt_conn_tx *tx)
{
if (tx->conn) {
bt_conn_unref(tx->conn);
tx->conn = NULL;
}

tx->data.cb = NULL;
tx->data.user_data = NULL;
k_fifo_put(&free_tx, tx);
}

static void tx_notify_cb(struct k_work *work)
{
struct bt_conn_tx *tx = CONTAINER_OF(work, struct bt_conn_tx, work);

BT_DBG("tx %p conn %p cb %p user_data %p", tx, tx->conn, tx->data.cb,
tx->data.user_data);

tx->data.cb(tx->conn, tx->data.user_data);

tx_free(tx);
}

static struct bt_conn *conn_new(void)
{
struct bt_conn *conn = NULL;
@@ -1166,13 +1190,6 @@ int bt_conn_send_cb(struct bt_conn *conn, struct net_buf *buf,
return 0;
}

static void tx_free(struct bt_conn_tx *tx)
{
tx->data.cb = NULL;
tx->data.user_data = NULL;
sys_slist_prepend(&free_tx, &tx->node);
}

void bt_conn_notify_tx(struct bt_conn *conn)
{
struct bt_conn_tx *tx;
@@ -1181,11 +1198,13 @@ void bt_conn_notify_tx(struct bt_conn *conn)

while ((tx = k_fifo_get(&conn->tx_notify, K_NO_WAIT))) {
BT_DBG("cb %p user_data %p", tx->data.cb, tx->data.user_data);

/* Only submit if there is a callback set */
if (tx->data.cb) {
tx->data.cb(conn, tx->data.user_data);
k_work_submit(&tx->work);
} else {
tx_free(tx);
}

tx_free(tx);
}
}

@@ -1205,45 +1224,43 @@ static void notify_tx(void)
}
}

static sys_snode_t *add_pending_tx(struct bt_conn *conn, bt_conn_tx_cb_t cb,
void *user_data)
static struct bt_conn_tx *add_pending_tx(struct bt_conn *conn,
bt_conn_tx_cb_t cb, void *user_data)
{
struct bt_conn_tx *tx;
sys_snode_t *node;
unsigned int key;

BT_DBG("conn %p cb %p user_data %p", conn, cb, user_data);

__ASSERT(!sys_slist_is_empty(&free_tx), "No free conn TX contexts");

node = sys_slist_get_not_empty(&free_tx);
tx = CONTAINER_OF(node, struct bt_conn_tx, node);
tx = k_fifo_get(&free_tx, K_FOREVER);
tx->conn = bt_conn_ref(conn);
k_work_init(&tx->work, tx_notify_cb);
tx->data.cb = cb;
tx->data.user_data = user_data;

key = irq_lock();
sys_slist_append(&conn->tx_pending, node);
sys_slist_append(&conn->tx_pending, &tx->node);
irq_unlock(key);

return node;
return tx;
}

static void remove_pending_tx(struct bt_conn *conn, sys_snode_t *node)
static void remove_pending_tx(struct bt_conn *conn, struct bt_conn_tx *tx)
{
unsigned int key;

key = irq_lock();
sys_slist_find_and_remove(&conn->tx_pending, node);
sys_slist_find_and_remove(&conn->tx_pending, &tx->node);
irq_unlock(key);

tx_free(CONTAINER_OF(node, struct bt_conn_tx, node));
tx_free(tx);
}

static bool send_frag(struct bt_conn *conn, struct net_buf *buf, u8_t flags,
bool always_consume)
{
struct bt_hci_acl_hdr *hdr;
sys_snode_t *node;
struct bt_conn_tx *tx;
int err;

BT_DBG("conn %p buf %p len %u flags 0x%02x", conn, buf, buf->len,
@@ -1265,14 +1282,14 @@ static bool send_frag(struct bt_conn *conn, struct net_buf *buf, u8_t flags,
hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr));

/* Add to pending, it must be done before bt_buf_set_type */
node = add_pending_tx(conn, conn_tx(buf)->cb, conn_tx(buf)->user_data);
tx = add_pending_tx(conn, conn_tx(buf)->cb, conn_tx(buf)->user_data);

bt_buf_set_type(buf, BT_BUF_ACL_OUT);

err = bt_send(buf);
if (err) {
BT_ERR("Unable to send to driver (err %d)", err);
remove_pending_tx(conn, node);
remove_pending_tx(conn, tx);
goto fail;
}

@@ -2294,7 +2311,7 @@ int bt_conn_init(void)
int err, i;

for (i = 0; i < ARRAY_SIZE(conn_tx); i++) {
sys_slist_prepend(&free_tx, &conn_tx[i].node);
k_fifo_put(&free_tx, &conn_tx[i]);
}

bt_att_init();
@@ -88,6 +88,8 @@ struct bt_conn_tx_data {

struct bt_conn_tx {
sys_snode_t node;
struct bt_conn *conn;
struct k_work work;
struct bt_conn_tx_data data;
};

0 comments on commit 8a7615f

Please sign in to comment.
You can’t perform that action at this time.