diff --git a/components/bt/esp_ble_mesh/common/atomic.c b/components/bt/esp_ble_mesh/common/atomic.c index 723ce7e3ac1..9c856cc3372 100644 --- a/components/bt/esp_ble_mesh/common/atomic.c +++ b/components/bt/esp_ble_mesh/common/atomic.c @@ -13,7 +13,7 @@ /* * SPDX-FileCopyrightText: 2016 Intel Corporation * SPDX-FileCopyrightText: 2011-2014 Wind River Systems, Inc. - * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2018-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -170,4 +170,18 @@ bt_mesh_atomic_val_t bt_mesh_atomic_inc(bt_mesh_atomic_t *target) return ret; } +bool bt_mesh_atomic_cas(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t excepted, bt_mesh_atomic_val_t new_val) +{ + bt_mesh_atomic_lock(); + + if (*target == excepted) { + *target = new_val; + bt_mesh_atomic_unlock(); + return true; + } + + bt_mesh_atomic_unlock(); + return false; +} + #endif /* #ifndef CONFIG_ATOMIC_OPERATIONS_BUILTIN */ diff --git a/components/bt/esp_ble_mesh/common/include/mesh/atomic.h b/components/bt/esp_ble_mesh/common/include/mesh/atomic.h index f72834369fc..b284974363a 100644 --- a/components/bt/esp_ble_mesh/common/include/mesh/atomic.h +++ b/components/bt/esp_ble_mesh/common/include/mesh/atomic.h @@ -147,6 +147,33 @@ static inline bt_mesh_atomic_val_t bt_mesh_atomic_and(bt_mesh_atomic_t *target, extern bt_mesh_atomic_val_t bt_mesh_atomic_and(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value); #endif +/** + * @brief Atomic CAS operation. + * + * This compares the contents of @a *target + * with the contents of @a excepted. If equal, + * the operation is a read-modify-write operation + * that writes @a new_val into @a *target and return true. + * If they are not equal, the operation is a read + * and return false. + * + * @param target Address of atomic variable. + * @param excepted Value of excepted. + * @param new_val Write if target value is equal to expected one. + * + * @return + * - true: Target value updated. + * - false: Target value not updated. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline bool bt_mesh_atomic_cas(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t excepted, bt_mesh_atomic_val_t new_val) +{ + return __atomic_compare_exchange_n(target, &excepted, &new_val, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} +#else +extern bool bt_mesh_atomic_cas(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t excepted, bt_mesh_atomic_val_t new_val); +#endif + /** * @cond INTERNAL_HIDDEN */ diff --git a/components/bt/esp_ble_mesh/core/adv.c b/components/bt/esp_ble_mesh/core/adv.c index dbbda0ab147..79932a05829 100644 --- a/components/bt/esp_ble_mesh/core/adv.c +++ b/components/bt/esp_ble_mesh/core/adv.c @@ -2,7 +2,7 @@ /* * SPDX-FileCopyrightText: 2017 Intel Corporation - * SPDX-FileContributor: 2018-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2018-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -335,8 +335,7 @@ static void adv_thread(void *p) } /* busy == 0 means this was canceled */ - if (BLE_MESH_ADV(*buf)->busy) { - BLE_MESH_ADV(*buf)->busy = 0U; + if (bt_mesh_atomic_cas(&BLE_MESH_ADV_BUSY(*buf), 1, 0)) { #if !CONFIG_BLE_MESH_RELAY_ADV_BUF if (adv_send(*buf)) { BT_WARN("Failed to send adv packet"); @@ -449,7 +448,7 @@ static void bt_mesh_unref_buf(bt_mesh_msg_t *msg) if (msg->arg) { buf = (struct net_buf *)msg->arg; - BLE_MESH_ADV(buf)->busy = 0U; + bt_mesh_atomic_set(&BLE_MESH_ADV_BUSY(buf), 0); if (buf->ref > 1U) { buf->ref = 1U; } @@ -490,7 +489,7 @@ void bt_mesh_adv_send(struct net_buf *buf, uint8_t xmit, BLE_MESH_ADV(buf)->cb = cb; BLE_MESH_ADV(buf)->cb_data = cb_data; - BLE_MESH_ADV(buf)->busy = 1U; + bt_mesh_atomic_set(&BLE_MESH_ADV_BUSY(buf), 1); BLE_MESH_ADV(buf)->xmit = xmit; bt_mesh_adv_buf_ref_debug(__func__, buf, 3U, BLE_MESH_BUF_REF_SMALL); @@ -589,7 +588,7 @@ void bt_mesh_relay_adv_send(struct net_buf *buf, uint8_t xmit, BLE_MESH_ADV(buf)->cb = cb; BLE_MESH_ADV(buf)->cb_data = cb_data; - BLE_MESH_ADV(buf)->busy = 1U; + bt_mesh_atomic_set(&BLE_MESH_ADV_BUSY(buf), 1); BLE_MESH_ADV(buf)->xmit = xmit; msg.arg = (void *)net_buf_ref(buf); @@ -753,7 +752,7 @@ static void bt_mesh_ble_adv_send(struct net_buf *buf, const struct bt_mesh_send_ BLE_MESH_ADV(buf)->cb = cb; BLE_MESH_ADV(buf)->cb_data = cb_data; - BLE_MESH_ADV(buf)->busy = 1U; + bt_mesh_atomic_set(&BLE_MESH_ADV_BUSY(buf), 1); bt_mesh_adv_buf_ref_debug(__func__, buf, 3U, BLE_MESH_BUF_REF_SMALL); @@ -772,7 +771,7 @@ static void ble_adv_tx_reset(struct ble_adv_tx *tx, bool unref) } bt_mesh_atomic_set(tx->flags, 0); memset(&tx->param, 0, sizeof(tx->param)); - BLE_MESH_ADV(tx->buf)->busy = 0U; + bt_mesh_atomic_set(&BLE_MESH_ADV_BUSY(tx->buf), 0); if (unref) { net_buf_unref(tx->buf); } @@ -961,7 +960,8 @@ int bt_mesh_stop_ble_advertising(uint8_t index) /* busy 1, ref 1; busy 1, ref 2; * busy 0, ref 0; busy 0, ref 1; */ - if (BLE_MESH_ADV(tx->buf)->busy == 1U && + + if (bt_mesh_atomic_get(&BLE_MESH_ADV_BUSY(tx->buf)) && tx->buf->ref == 1U) { unref = false; } diff --git a/components/bt/esp_ble_mesh/core/adv.h b/components/bt/esp_ble_mesh/core/adv.h index ab37ec3cda8..33224d78b11 100644 --- a/components/bt/esp_ble_mesh/core/adv.h +++ b/components/bt/esp_ble_mesh/core/adv.h @@ -10,6 +10,7 @@ #ifndef _ADV_H_ #define _ADV_H_ +#include "mesh/atomic.h" #include "mesh/access.h" #include "mesh/adapter.h" @@ -24,6 +25,7 @@ extern "C" { #define BLE_MESH_ADV_USER_DATA_SIZE 4 #define BLE_MESH_ADV(buf) (*(struct bt_mesh_adv **)net_buf_user_data(buf)) +#define BLE_MESH_ADV_BUSY(buf) (BLE_MESH_ADV(buf)->busy) uint16_t bt_mesh_pdu_duration(uint8_t xmit); @@ -48,8 +50,10 @@ struct bt_mesh_adv { const struct bt_mesh_send_cb *cb; void *cb_data; - uint8_t type:3, - busy:1; + uint8_t type:3; + + bt_mesh_atomic_t busy; + uint8_t xmit; }; diff --git a/components/bt/esp_ble_mesh/core/friend.c b/components/bt/esp_ble_mesh/core/friend.c index fca3cf1358a..b1a298d5a97 100644 --- a/components/bt/esp_ble_mesh/core/friend.c +++ b/components/bt/esp_ble_mesh/core/friend.c @@ -183,7 +183,7 @@ static void friend_clear(struct bt_mesh_friend *frnd, uint8_t reason) /* Cancel the sending if necessary */ if (frnd->pending_buf) { bt_mesh_adv_buf_ref_debug(__func__, frnd->last, 2U, BLE_MESH_BUF_REF_EQUAL); - BLE_MESH_ADV(frnd->last)->busy = 0U; + bt_mesh_atomic_set(&BLE_MESH_ADV_BUSY(frnd->last), 0); } else { bt_mesh_adv_buf_ref_debug(__func__, frnd->last, 1U, BLE_MESH_BUF_REF_EQUAL); } diff --git a/components/bt/esp_ble_mesh/core/prov_common.c b/components/bt/esp_ble_mesh/core/prov_common.c index 655c4712f24..bc75b447d25 100644 --- a/components/bt/esp_ble_mesh/core/prov_common.c +++ b/components/bt/esp_ble_mesh/core/prov_common.c @@ -359,7 +359,7 @@ static void free_segments(struct bt_mesh_prov_link *link) link->tx.buf[i] = NULL; bt_mesh_adv_buf_ref_debug(__func__, buf, 3U, BLE_MESH_BUF_REF_SMALL); /* Mark as canceled */ - BLE_MESH_ADV(buf)->busy = 0U; + bt_mesh_atomic_set(&BLE_MESH_ADV_BUSY(buf), 0); net_buf_unref(buf); } } @@ -474,7 +474,7 @@ static void prov_retransmit(struct k_work *work) break; } - if (BLE_MESH_ADV(buf)->busy) { + if (bt_mesh_atomic_get(&BLE_MESH_ADV_BUSY(buf))) { continue; } diff --git a/components/bt/esp_ble_mesh/core/transport.c b/components/bt/esp_ble_mesh/core/transport.c index 64ac566aced..70589706dae 100644 --- a/components/bt/esp_ble_mesh/core/transport.c +++ b/components/bt/esp_ble_mesh/core/transport.c @@ -2,7 +2,7 @@ /* * SPDX-FileCopyrightText: 2017 Intel Corporation - * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2018-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -310,7 +310,15 @@ static void seg_tx_done(struct seg_tx *tx, uint8_t seg_idx) { bt_mesh_adv_buf_ref_debug(__func__, tx->seg[seg_idx], 3U, BLE_MESH_BUF_REF_SMALL); - BLE_MESH_ADV(tx->seg[seg_idx])->busy = 0U; + /** + * When cancelling a segment that is still in the adv sending queue, `tx->seg_pending` + * must else be decremented by one. More detailed information + * can be found in BLEMESH24-26. + */ + if (bt_mesh_atomic_cas(&BLE_MESH_ADV_BUSY(tx->seg[seg_idx]), 1, 0)) { + tx->seg_pending--; + } + net_buf_unref(tx->seg[seg_idx]); tx->seg[seg_idx] = NULL; tx->nack_count--; @@ -443,7 +451,7 @@ static void seg_tx_send_unacked(struct seg_tx *tx) continue; } - if (BLE_MESH_ADV(seg)->busy) { + if (bt_mesh_atomic_get(&BLE_MESH_ADV_BUSY(seg))) { BT_DBG("Skipping segment that's still advertising"); continue; } diff --git a/components/bt/esp_ble_mesh/core/transport.enh.c b/components/bt/esp_ble_mesh/core/transport.enh.c index 6ef6c0a55db..a4a5130e215 100644 --- a/components/bt/esp_ble_mesh/core/transport.enh.c +++ b/components/bt/esp_ble_mesh/core/transport.enh.c @@ -350,7 +350,7 @@ static void seg_tx_done(struct seg_tx *tx, uint8_t seg_idx) */ bt_mesh_adv_buf_ref_debug(__func__, tx->seg[seg_idx], 4U, BLE_MESH_BUF_REF_SMALL); - BLE_MESH_ADV(tx->seg[seg_idx])->busy = 0U; + bt_mesh_atomic_set(&BLE_MESH_ADV_BUSY(tx->seg[seg_idx]), 0); net_buf_unref(tx->seg[seg_idx]); tx->seg[seg_idx] = NULL; @@ -498,7 +498,7 @@ static bool send_next_segment(struct seg_tx *tx, int *result) /* The segment may have already been transmitted, for example, the * Segment Retransmission timer is expired earlier. */ - if (BLE_MESH_ADV(seg)->busy) { + if (bt_mesh_atomic_get(&BLE_MESH_ADV_BUSY(seg))) { return false; } @@ -768,7 +768,7 @@ static bool resend_unacked_seg(struct seg_tx *tx, int *result) * A is still going to be retransmitted, but at this moment we could * find that the "busy" flag of Segment A is 1. */ - if (BLE_MESH_ADV(seg)->busy) { + if (bt_mesh_atomic_get(&BLE_MESH_ADV_BUSY(seg))) { return false; }