Bluetooth: controller: Guard against race in conn. establishment
In the time between a NODE_RX_TYPE_CONNECTION node is sent from LLL and demuxed in ULL, an ADV role disable may be executed. This makes the LLL data referenced in the node NULL/invald, and ull_conn_setup would operate on invalid data. This commit introduces a check in ADV disable to disallow the operation (including conn invalidation), if a connection has been initiated. To prevent pipeline-queued prepares from advertising after disable has been initiated, set 'cancelled' flag for immediate signalling to LLL. Signed-off-by: Morten Priess <mtpr@oticon.com> Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
parent
cd96046bee
commit
07278fc98f
3 changed files with 45 additions and 7 deletions
|
@ -64,6 +64,7 @@ struct lll_conn {
|
||||||
#if defined(CONFIG_BT_PERIPHERAL)
|
#if defined(CONFIG_BT_PERIPHERAL)
|
||||||
struct {
|
struct {
|
||||||
uint8_t initiated:1;
|
uint8_t initiated:1;
|
||||||
|
uint8_t cancelled:1;
|
||||||
uint8_t latency_enabled:1;
|
uint8_t latency_enabled:1;
|
||||||
|
|
||||||
uint32_t window_widening_periodic_us;
|
uint32_t window_widening_periodic_us;
|
||||||
|
|
|
@ -741,10 +741,14 @@ static int prepare_cb(struct lll_prepare_param *p)
|
||||||
lll = p->param;
|
lll = p->param;
|
||||||
|
|
||||||
#if defined(CONFIG_BT_PERIPHERAL)
|
#if defined(CONFIG_BT_PERIPHERAL)
|
||||||
/* Check if stopped (on connection establishment race between LLL and
|
/* Check if stopped (on connection establishment- or disabled race
|
||||||
* ULL.
|
* between LLL and ULL.
|
||||||
|
* When connectable advertising is disabled in thread context, cancelled
|
||||||
|
* flag is set, and initiated flag is checked. Here, we avoid
|
||||||
|
* transmitting connectable advertising event if cancelled flag is set.
|
||||||
*/
|
*/
|
||||||
if (unlikely(lll->conn && lll->conn->slave.initiated)) {
|
if (unlikely(lll->conn &&
|
||||||
|
(lll->conn->slave.initiated || lll->conn->slave.cancelled))) {
|
||||||
radio_isr_set(lll_isr_early_abort, lll);
|
radio_isr_set(lll_isr_early_abort, lll);
|
||||||
radio_disable();
|
radio_disable();
|
||||||
|
|
||||||
|
@ -1079,7 +1083,14 @@ static void isr_done(void *param)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BT_PERIPHERAL */
|
#endif /* CONFIG_BT_PERIPHERAL */
|
||||||
|
|
||||||
if (lll->chan_map_curr) {
|
/* NOTE: Do not continue to connectable advertise if advertising is
|
||||||
|
* being disabled, by checking the cancelled flag.
|
||||||
|
*/
|
||||||
|
if (lll->chan_map_curr &&
|
||||||
|
#if defined(CONFIG_BT_PERIPHERAL)
|
||||||
|
(!lll->conn || !lll->conn->slave.cancelled) &&
|
||||||
|
#endif /* CONFIG_BT_PERIPHERAL */
|
||||||
|
1) {
|
||||||
struct pdu_adv *pdu;
|
struct pdu_adv *pdu;
|
||||||
uint32_t start_us;
|
uint32_t start_us;
|
||||||
|
|
||||||
|
@ -1321,12 +1332,22 @@ static inline int isr_rx_pdu(struct lll_adv *lll,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#if defined(CONFIG_BT_PERIPHERAL)
|
#if defined(CONFIG_BT_PERIPHERAL)
|
||||||
|
/* NOTE: Do not accept CONNECT_IND if cancelled flag is set in thread
|
||||||
|
* context when disabling connectable advertising. This is to
|
||||||
|
* avoid any race in checking the initiated flags in thread mode
|
||||||
|
* which is set here if accepting a connection establishment.
|
||||||
|
*
|
||||||
|
* Under this race, peer central would get failed to establish
|
||||||
|
* connection as the disconnect reason. This is an acceptable
|
||||||
|
* outcome to keep the thread mode implementation simple when
|
||||||
|
* disabling connectable advertising.
|
||||||
|
*/
|
||||||
} else if ((pdu_rx->type == PDU_ADV_TYPE_CONNECT_IND) &&
|
} else if ((pdu_rx->type == PDU_ADV_TYPE_CONNECT_IND) &&
|
||||||
(pdu_rx->len == sizeof(struct pdu_adv_connect_ind)) &&
|
(pdu_rx->len == sizeof(struct pdu_adv_connect_ind)) &&
|
||||||
|
lll->conn && !lll->conn->slave.cancelled &&
|
||||||
lll_adv_connect_ind_check(lll, pdu_rx, tx_addr, addr,
|
lll_adv_connect_ind_check(lll, pdu_rx, tx_addr, addr,
|
||||||
rx_addr, tgt_addr,
|
rx_addr, tgt_addr,
|
||||||
devmatch_ok, &rl_idx) &&
|
devmatch_ok, &rl_idx)) {
|
||||||
lll->conn) {
|
|
||||||
struct node_rx_ftr *ftr;
|
struct node_rx_ftr *ftr;
|
||||||
struct node_rx_pdu *rx;
|
struct node_rx_pdu *rx;
|
||||||
|
|
||||||
|
|
|
@ -874,6 +874,7 @@ uint8_t ll_adv_enable(uint8_t enable)
|
||||||
/* FIXME: BEGIN: Move to ULL? */
|
/* FIXME: BEGIN: Move to ULL? */
|
||||||
conn_lll->role = 1;
|
conn_lll->role = 1;
|
||||||
conn_lll->slave.initiated = 0;
|
conn_lll->slave.initiated = 0;
|
||||||
|
conn_lll->slave.cancelled = 0;
|
||||||
conn_lll->data_chan_sel = 0;
|
conn_lll->data_chan_sel = 0;
|
||||||
conn_lll->data_chan_use = 0;
|
conn_lll->data_chan_use = 0;
|
||||||
conn_lll->event_counter = 0;
|
conn_lll->event_counter = 0;
|
||||||
|
@ -1995,6 +1996,21 @@ static inline uint8_t disable(uint8_t handle)
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_PERIPHERAL)
|
||||||
|
if (adv->lll.conn) {
|
||||||
|
/* Indicate to LLL that a cancellation is requested */
|
||||||
|
adv->lll.conn->slave.cancelled = 1U;
|
||||||
|
cpu_dmb();
|
||||||
|
|
||||||
|
/* Check if a connection was initiated (connection
|
||||||
|
* establishment race between LLL and ULL).
|
||||||
|
*/
|
||||||
|
if (unlikely(adv->lll.conn->slave.initiated)) {
|
||||||
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_BT_PERIPHERAL */
|
||||||
|
|
||||||
mark = ull_disable_mark(adv);
|
mark = ull_disable_mark(adv);
|
||||||
LL_ASSERT(mark == adv);
|
LL_ASSERT(mark == adv);
|
||||||
|
|
||||||
|
@ -2012,7 +2028,7 @@ static inline uint8_t disable(uint8_t handle)
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif /* CONFIG_BT_PERIPHERAL */
|
||||||
|
|
||||||
ret_cb = TICKER_STATUS_BUSY;
|
ret_cb = TICKER_STATUS_BUSY;
|
||||||
ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD,
|
ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue