Bluetooth: controller: Fix connection cancel deadlock

Calling bt_recv in the Bluetooth host Tx thread by the
controller implementation caused deadlock in combined host
controller builds when HCI LE Create Connection Cancel
generated the HCI LE Connection Complete or HCI LE Enhanced
Connection Complete events.

Controller's HCI implementation has been updated to place
the generated event into Rx FIFO to avoid the deadlock.

Relates to commit a59f544fb4 ("bluetooth: controller:
Handle non-priority events correctly")

Relates to #10314.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
Vinayak Kariappa Chettimada 2018-10-01 23:28:25 +02:00 committed by Carles Cufí
commit 049267a5ca
5 changed files with 53 additions and 48 deletions

View file

@ -1008,32 +1008,16 @@ static void le_create_connection(struct net_buf *buf, struct net_buf **evt)
*evt = cmd_status((!status) ? 0x00 : BT_HCI_ERR_CMD_DISALLOWED);
}
static void le_create_conn_cancel(struct net_buf *buf, struct net_buf **evt)
static void le_create_conn_cancel(struct net_buf *buf, struct net_buf **evt,
void **node_rx)
{
struct bt_hci_evt_cc_status *ccst;
struct net_buf *cc;
u8_t cmd_status;
u32_t status;
status = ll_connect_disable();
cmd_status = status ? BT_HCI_ERR_CMD_DISALLOWED : 0x00;
if (!cmd_status) {
*evt = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER);
le_conn_complete(BT_HCI_ERR_UNKNOWN_CONN_ID, NULL, 0x0000,
*evt);
if ((*evt)->len) {
ccst = cmd_complete(&cc, sizeof(*ccst));
ccst->status = cmd_status;
bt_recv_prio(cc);
return;
} else {
net_buf_unref(*evt);
}
}
status = ll_connect_disable(node_rx);
ccst = cmd_complete(evt, sizeof(*ccst));
ccst->status = cmd_status;
ccst->status = (!status) ? 0x00 : BT_HCI_ERR_CMD_DISALLOWED;
}
static void le_set_host_chan_classif(struct net_buf *buf, struct net_buf **evt)
@ -1513,7 +1497,7 @@ static void le_enh_tx_test(struct net_buf *buf, struct net_buf **evt)
#endif /* CONFIG_BT_CTLR_DTM_HCI */
static int controller_cmd_handle(u16_t ocf, struct net_buf *cmd,
struct net_buf **evt)
struct net_buf **evt, void **node_rx)
{
switch (ocf) {
case BT_OCF(BT_HCI_OP_LE_SET_EVENT_MASK):
@ -1601,7 +1585,7 @@ static int controller_cmd_handle(u16_t ocf, struct net_buf *cmd,
break;
case BT_OCF(BT_HCI_OP_LE_CREATE_CONN_CANCEL):
le_create_conn_cancel(cmd, evt);
le_create_conn_cancel(cmd, evt, node_rx);
break;
case BT_OCF(BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF):
@ -1991,7 +1975,7 @@ static void data_buf_overflow(struct net_buf **buf)
ep->link_type = BT_OVERFLOW_LINK_ACL;
}
struct net_buf *hci_cmd_handle(struct net_buf *cmd)
struct net_buf *hci_cmd_handle(struct net_buf *cmd, void **node_rx)
{
struct bt_hci_evt_cc_status *ccst;
struct bt_hci_cmd_hdr *chdr;
@ -2031,7 +2015,7 @@ struct net_buf *hci_cmd_handle(struct net_buf *cmd)
err = status_cmd_handle(ocf, cmd, &evt);
break;
case BT_OGF_LE:
err = controller_cmd_handle(ocf, cmd, &evt);
err = controller_cmd_handle(ocf, cmd, &evt, node_rx);
break;
#if defined(CONFIG_BT_HCI_VS)
case BT_OGF_VS:

View file

@ -348,17 +348,17 @@ static void recv_thread(void *p1, void *p2, void *p3)
static int cmd_handle(struct net_buf *buf)
{
struct bt_hci_evt_hdr *hdr;
void *node_rx = NULL;
struct net_buf *evt;
evt = hci_cmd_handle(buf);
evt = hci_cmd_handle(buf, &node_rx);
if (evt) {
hdr = (void *)evt->data;
BT_DBG("Replying with event of %u bytes", evt->len);
if (unlikely(!bt_hci_evt_is_prio(hdr->evt))) {
bt_recv(evt);
} else {
bt_recv_prio(evt);
bt_recv_prio(evt);
if (node_rx) {
BT_DBG("RX node enqueue");
k_fifo_put(&recv_fifo, node_rx);
}
}

View file

@ -32,7 +32,7 @@ extern atomic_t hci_state_mask;
#endif /* CONFIG_SOC_FAMILY_NRF */
void hci_init(struct k_poll_signal *signal_host_buf);
struct net_buf *hci_cmd_handle(struct net_buf *cmd);
struct net_buf *hci_cmd_handle(struct net_buf *cmd, void **node_rx);
void hci_evt_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf);
s8_t hci_get_class(struct radio_pdu_node_rx *node_rx);
#if defined(CONFIG_BT_CONN)

View file

@ -54,7 +54,7 @@ u32_t ll_create_connection(u16_t scan_interval, u16_t scan_window,
u8_t *p_peer_addr, u8_t own_addr_type,
u16_t interval, u16_t latency,
u16_t timeout);
u32_t ll_connect_disable(void);
u32_t ll_connect_disable(void **node_rx);
u32_t ll_conn_update(u16_t handle, u8_t cmd, u8_t status,
u16_t interval, u16_t latency,
u16_t timeout);

View file

@ -10458,24 +10458,12 @@ u32_t radio_scan_disable(void)
status = role_disable(RADIO_TICKER_ID_SCAN,
RADIO_TICKER_ID_SCAN_STOP);
if (!status) {
struct connection *conn;
if (!status && !_radio.scanner.conn) {
_radio.scanner.is_enabled = 0;
if (!_radio.advertiser.is_enabled) {
ll_adv_scan_state_cb(0);
}
conn = _radio.scanner.conn;
if (conn) {
_radio.scanner.conn = NULL;
mem_release(conn->llcp_terminate.
radio_pdu_node_rx.hdr.onion.link,
&_radio.link_rx_free);
mem_release(conn, &_radio.conn_free);
}
}
return status ? BT_HCI_ERR_CMD_DISALLOWED : 0;
@ -10661,7 +10649,7 @@ u32_t radio_connect_enable(u8_t adv_addr_type, u8_t *adv_addr, u16_t interval,
return 0;
}
u32_t ll_connect_disable(void)
u32_t ll_connect_disable(void **node_rx)
{
u32_t status;
@ -10670,6 +10658,20 @@ u32_t ll_connect_disable(void)
}
status = radio_scan_disable();
if (!status) {
struct connection *conn = _radio.scanner.conn;
struct radio_pdu_node_rx *rx;
rx = (void *)&conn->llcp_terminate.radio_pdu_node_rx;
/* free the memq link early, as caller could overwrite it */
mem_release(rx->hdr.onion.link, &_radio.link_rx_free);
rx->hdr.type = NODE_RX_TYPE_CONNECTION;
rx->hdr.handle = 0xffff;
*((u8_t *)rx->pdu_data) = BT_HCI_ERR_UNKNOWN_CONN_ID;
*node_rx = rx;
}
return status;
}
@ -11352,6 +11354,26 @@ void ll_rx_mem_release(void **node_rx)
_node_rx = _node_rx->hdr.onion.next;
switch (_node_rx_free->hdr.type) {
case NODE_RX_TYPE_CONNECTION:
if (*((u8_t *)_node_rx_free->pdu_data) ==
BT_HCI_ERR_UNKNOWN_CONN_ID) {
struct connection *conn;
conn = _radio.scanner.conn;
_radio.scanner.conn = NULL;
mem_release(conn, &_radio.conn_free);
_radio.scanner.is_enabled = 0;
if (!_radio.advertiser.is_enabled) {
ll_adv_scan_state_cb(0);
}
break;
}
/* passthrough */
case NODE_RX_TYPE_DC_PDU:
case NODE_RX_TYPE_REPORT:
@ -11364,7 +11386,6 @@ void ll_rx_mem_release(void **node_rx)
case NODE_RX_TYPE_SCAN_REQ:
#endif /* CONFIG_BT_CTLR_SCAN_REQ_NOTIFY */
case NODE_RX_TYPE_CONNECTION:
case NODE_RX_TYPE_CONN_UPDATE:
case NODE_RX_TYPE_ENC_REFRESH: