Bluetooth: controller: Fix regression in ctrl tx queue handling

Fix control Tx buffer leak into data Tx pool that happens
after a cross-over control procedure response was paused due
to currently active encryption setup procedure, and a new
control Tx PDU in addition to the paused one is enqueued
thereafter.

When the control tx PDUs is resumed but not yet enqueued
towards the radio, if there is a new control Tx PDU enqueued
then the paused control Tx PDU is not set as the head of the
control PDUs in the Tx queue. This caused the paused control
Tx PDU to be associated with data Tx pool, hence causing the
incorrect release into data Tx pool.

Relates to the commit bff76b4cce ("Bluetooth: controller:
split: Fix control tx queue handling") and to the
commit 6991d09977 ("Bluetooth: controller: Fix control tx
queue handling").

Fixes #32898.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
Vinayak Kariappa Chettimada 2021-03-13 10:16:40 +05:30 committed by Anas Nashif
commit 7f727ac322

View file

@ -91,6 +91,7 @@ static inline void event_ch_map_prep(struct ll_conn *conn,
uint16_t event_counter); uint16_t event_counter);
#if defined(CONFIG_BT_CTLR_LE_ENC) #if defined(CONFIG_BT_CTLR_LE_ENC)
static inline void ctrl_tx_check_and_resume(struct ll_conn *conn);
static bool is_enc_req_pause_tx(struct ll_conn *conn); static bool is_enc_req_pause_tx(struct ll_conn *conn);
static inline void event_enc_prep(struct ll_conn *conn); static inline void event_enc_prep(struct ll_conn *conn);
#if defined(CONFIG_BT_PERIPHERAL) #if defined(CONFIG_BT_PERIPHERAL)
@ -1868,18 +1869,11 @@ static void tx_demux(void *param)
static struct node_tx *tx_ull_dequeue(struct ll_conn *conn, struct node_tx *tx) static struct node_tx *tx_ull_dequeue(struct ll_conn *conn, struct node_tx *tx)
{ {
#if defined(CONFIG_BT_CTLR_LE_ENC)
if (!conn->tx_ctrl && (conn->tx_head != conn->tx_data)) { if (!conn->tx_ctrl && (conn->tx_head != conn->tx_data)) {
struct pdu_data *pdu_data_tx; ctrl_tx_check_and_resume(conn);
pdu_data_tx = (void *)conn->tx_head->pdu;
if ((pdu_data_tx->ll_id != PDU_DATA_LLID_CTRL) ||
((pdu_data_tx->llctrl.opcode !=
PDU_DATA_LLCTRL_TYPE_ENC_REQ) &&
(pdu_data_tx->llctrl.opcode !=
PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ))) {
conn->tx_ctrl = conn->tx_ctrl_last = conn->tx_head;
}
} }
#endif /* CONFIG_BT_CTLR_LE_ENC */
if (conn->tx_head == conn->tx_ctrl) { if (conn->tx_head == conn->tx_ctrl) {
conn->tx_head = conn->tx_head->next; conn->tx_head = conn->tx_head->next;
@ -1990,7 +1984,23 @@ static int empty_data_start_release(struct ll_conn *conn, struct node_tx *tx)
} }
#endif /* CONFIG_BT_CTLR_LLID_DATA_START_EMPTY */ #endif /* CONFIG_BT_CTLR_LLID_DATA_START_EMPTY */
static void ctrl_tx_last_enqueue(struct ll_conn *conn, #if defined(CONFIG_BT_CTLR_LE_ENC)
static inline void ctrl_tx_check_and_resume(struct ll_conn *conn)
{
struct pdu_data *pdu_data_tx;
pdu_data_tx = (void *)conn->tx_head->pdu;
if ((pdu_data_tx->ll_id != PDU_DATA_LLID_CTRL) ||
((pdu_data_tx->llctrl.opcode !=
PDU_DATA_LLCTRL_TYPE_ENC_REQ) &&
(pdu_data_tx->llctrl.opcode !=
PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ))) {
conn->tx_ctrl = conn->tx_ctrl_last = conn->tx_head;
}
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
static inline void ctrl_tx_last_enqueue(struct ll_conn *conn,
struct node_tx *tx) struct node_tx *tx)
{ {
tx->next = conn->tx_ctrl_last->next; tx->next = conn->tx_ctrl_last->next;
@ -2022,6 +2032,10 @@ static inline void ctrl_tx_pause_enqueue(struct ll_conn *conn,
*/ */
if (conn->tx_head == conn->tx_data) { if (conn->tx_head == conn->tx_data) {
conn->tx_data = conn->tx_data->next; conn->tx_data = conn->tx_data->next;
#if defined(CONFIG_BT_CTLR_LE_ENC)
} else if (!conn->tx_ctrl) {
ctrl_tx_check_and_resume(conn);
#endif /* CONFIG_BT_CTLR_LE_ENC */
} }
/* if no ctrl packet already queued, new ctrl added will be /* if no ctrl packet already queued, new ctrl added will be