Bluetooth: Fix missing connection cleanup in some scenarios

If the TX thread is not in k_poll() when conn->state gets set to
DISCONNECTED and a dummy buffer is pushed to conn->tx_queue the
bt_conn_prepare_events() function would have failed to add the
connection to the poll list for cleanup. To ensure the cleanup always
happens introduce a new flag that indicates that a cleanup must
happen. The extra benefit of the flag is that we no-longer need a
dummy buffer, but can simply use the conn_change signal to wake up the
TX thread.

Change-Id: I369584d305261ab3666b931c786daff9d131d228
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2017-02-10 08:49:19 +02:00
commit 6c995a5053
2 changed files with 31 additions and 18 deletions

View file

@ -1020,6 +1020,25 @@ static bool send_buf(struct bt_conn *conn, struct net_buf *buf)
static struct k_poll_signal conn_change = K_POLL_SIGNAL_INITIALIZER();
static void conn_cleanup(struct bt_conn *conn)
{
struct net_buf *buf;
/* Give back any allocated buffers */
while ((buf = net_buf_get(&conn->tx_queue, K_NO_WAIT))) {
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);
}
int bt_conn_prepare_events(struct k_poll_event events[])
{
int i, ev_count = 0;
@ -1037,6 +1056,12 @@ int bt_conn_prepare_events(struct k_poll_event events[])
continue;
}
if (conn->state == BT_CONN_DISCONNECTED &&
atomic_test_and_clear_bit(conn->flags, BT_CONN_CLEANUP)) {
conn_cleanup(conn);
continue;
}
if (conn->state != BT_CONN_CONNECTED) {
continue;
}
@ -1059,23 +1084,10 @@ void bt_conn_process_tx(struct bt_conn *conn)
BT_DBG("conn %p", conn);
if (conn->state != BT_CONN_CONNECTED) {
if (conn->state == BT_CONN_DISCONNECTED &&
atomic_test_and_clear_bit(conn->flags, BT_CONN_CLEANUP)) {
BT_DBG("handle %u disconnected - cleaning up", conn->handle);
/* Give back any allocated buffers */
while ((buf = net_buf_get(&conn->tx_queue, K_NO_WAIT))) {
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);
conn_cleanup(conn);
return;
}
@ -1171,8 +1183,8 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state)
k_delayed_work_cancel(&conn->le.update_work);
}
net_buf_put(&conn->tx_queue,
bt_conn_create_pdu(NULL, 0));
atomic_set_bit(conn->flags, BT_CONN_CLEANUP);
k_poll_signal(&conn_change, 0);
/* The last ref will be dropped by the tx_thread */
} else if (old_state == BT_CONN_CONNECT) {
/* conn->err will be set in this case */

View file

@ -23,6 +23,7 @@ enum {
BT_CONN_BR_PAIRING, /* BR connection in pairing context */
BT_CONN_BR_NOBOND, /* SSP no bond pairing tracker */
BT_CONN_BR_PAIRING_INITIATOR, /* local host starts authentication */
BT_CONN_CLEANUP, /* Disconnected, pending cleanup */
/* Total number of flags - must be at the end of the enum */
BT_CONN_NUM_FLAGS,