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:
parent
648a53d50f
commit
8a7615f4a3
2 changed files with 47 additions and 28 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue