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>
This commit is contained in:
Luiz Augusto von Dentz 2019-05-28 15:39:35 +03:00 committed by Johan Hedberg
commit 8a7615f4a3
2 changed files with 47 additions and 28 deletions

View file

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

View file

@ -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;
};