Bluetooth: Mesh: Remove outdated RPL entries in settings work item

This allows to move flash operations from the context that triggered
IVI update to the mesh settings work item that runs on the system
workqueue. After this change, the outdated RPL entries will be removed
in the setting work item triggered by store_pending function in
mesh/settings.c. This is required to for a case where the mesh settings
work item is running on a separate thread instead of the system
workqueue to unblock the system workqueue eventually.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
Pavel Vasilyev 2023-04-26 19:28:00 +02:00 committed by Carles Cufí
commit 1fdf283531

View file

@ -37,7 +37,12 @@ struct rpl_val {
static struct bt_mesh_rpl replay_list[CONFIG_BT_MESH_CRPL];
static ATOMIC_DEFINE(store, CONFIG_BT_MESH_CRPL);
static atomic_t clear;
enum {
PENDING_CLEAR,
PENDING_RESET,
};
static atomic_t rpl_flags;
static inline int rpl_idx(const struct bt_mesh_rpl *rpl)
{
@ -53,6 +58,8 @@ static void clear_rpl(struct bt_mesh_rpl *rpl)
return;
}
atomic_clear_bit(store, rpl_idx(rpl));
snprintk(path, sizeof(path), "bt/mesh/RPL/%x", rpl->src);
err = settings_delete(path);
if (err) {
@ -60,9 +67,6 @@ static void clear_rpl(struct bt_mesh_rpl *rpl)
} else {
LOG_DBG("Cleared RPL");
}
(void)memset(rpl, 0, sizeof(*rpl));
atomic_clear_bit(store, rpl_idx(rpl));
}
static void schedule_rpl_store(struct bt_mesh_rpl *entry, bool force)
@ -105,6 +109,7 @@ void bt_mesh_rpl_update(struct bt_mesh_rpl *rpl,
bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx,
struct bt_mesh_rpl **match)
{
struct bt_mesh_rpl *rpl;
int i;
/* Don't bother checking messages from ourselves */
@ -118,34 +123,33 @@ bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx,
}
for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
struct bt_mesh_rpl *rpl = &replay_list[i];
rpl = &replay_list[i];
/* Empty slot */
if (!rpl->src) {
if (match) {
*match = rpl;
} else {
bt_mesh_rpl_update(rpl, rx);
}
return false;
goto match;
}
/* Existing slot for given address */
if (rpl->src == rx->ctx.addr) {
if (!rpl->old_iv &&
atomic_test_bit(&rpl_flags, PENDING_RESET) &&
!atomic_test_bit(store, i)) {
/* Until rpl reset is finished, entry with old_iv == false and
* without "store" bit set will be removed, therefore it can be
* reused. If such entry is reused, "store" bit will be set and
* the entry won't be removed.
*/
goto match;
}
if (rx->old_iv && !rpl->old_iv) {
return true;
}
if ((!rx->old_iv && rpl->old_iv) ||
rpl->seq < rx->seq) {
if (match) {
*match = rpl;
} else {
bt_mesh_rpl_update(rpl, rx);
}
return false;
goto match;
} else {
return true;
}
@ -154,6 +158,15 @@ bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx,
LOG_ERR("RPL is full!");
return true;
match:
if (match) {
*match = rpl;
} else {
bt_mesh_rpl_update(rpl, rx);
}
return false;
}
void bt_mesh_rpl_clear(void)
@ -165,7 +178,7 @@ void bt_mesh_rpl_clear(void)
return;
}
(void)atomic_cas(&clear, 0, 1);
atomic_set_bit(&rpl_flags, PENDING_CLEAR);
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_RPL_PENDING);
}
@ -199,41 +212,56 @@ static struct bt_mesh_rpl *bt_mesh_rpl_alloc(uint16_t src)
void bt_mesh_rpl_reset(void)
{
int shift = 0;
int last = 0;
/* Discard "old old" IV Index entries from RPL and flag
* any other ones (which are valid) as old.
*/
for (int i = 0; i < ARRAY_SIZE(replay_list); i++) {
struct bt_mesh_rpl *rpl = &replay_list[i];
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
int i;
if (rpl->src) {
if (rpl->old_iv) {
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
clear_rpl(rpl);
} else {
(void)memset(rpl, 0, sizeof(*rpl));
}
for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
struct bt_mesh_rpl *rpl = &replay_list[i];
shift++;
} else {
rpl->old_iv = true;
if (shift > 0) {
replay_list[i - shift] = *rpl;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
schedule_rpl_store(&replay_list[i - shift], true);
}
if (!rpl->src) {
continue;
}
last = i;
/* Entries with "store" bit set will be stored, other entries will be
* removed.
*/
atomic_set_bit_to(store, i, !rpl->old_iv);
rpl->old_iv = !rpl->old_iv;
}
}
(void) memset(&replay_list[last - shift + 1], 0, sizeof(struct bt_mesh_rpl) * shift);
if (i != 0) {
atomic_set_bit(&rpl_flags, PENDING_RESET);
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_RPL_PENDING);
}
} else {
int shift = 0;
int last = 0;
for (int i = 0; i < ARRAY_SIZE(replay_list); i++) {
struct bt_mesh_rpl *rpl = &replay_list[i];
if (rpl->src) {
if (rpl->old_iv) {
(void)memset(rpl, 0, sizeof(*rpl));
shift++;
} else {
rpl->old_iv = true;
if (shift > 0) {
replay_list[i - shift] = *rpl;
}
}
last = i;
}
}
(void)memset(&replay_list[last - shift + 1], 0, sizeof(struct bt_mesh_rpl) * shift);
}
}
static int rpl_set(const char *name, size_t len_rd,
@ -315,7 +343,10 @@ static void store_rpl(struct bt_mesh_rpl *entry)
void bt_mesh_rpl_pending_store(uint16_t addr)
{
int shift = 0;
int last = 0;
bool clr;
bool rst;
if (!IS_ENABLED(CONFIG_BT_SETTINGS) ||
(!BT_MESH_ADDR_IS_UNICAST(addr) &&
@ -327,22 +358,49 @@ void bt_mesh_rpl_pending_store(uint16_t addr)
bt_mesh_settings_store_cancel(BT_MESH_SETTINGS_RPL_PENDING);
}
clr = atomic_cas(&clear, 1, 0);
clr = atomic_test_and_clear_bit(&rpl_flags, PENDING_CLEAR);
rst = atomic_test_bit(&rpl_flags, PENDING_RESET);
for (int i = 0; i < ARRAY_SIZE(replay_list); i++) {
if (addr != BT_MESH_ADDR_ALL_NODES &&
addr != replay_list[i].src) {
struct bt_mesh_rpl *rpl = &replay_list[i];
if (addr != BT_MESH_ADDR_ALL_NODES && addr != rpl->src) {
continue;
}
if (clr) {
clear_rpl(&replay_list[i]);
} else if (atomic_test_and_clear_bit(store, rpl_idx(&replay_list[i]))) {
store_rpl(&replay_list[i]);
clear_rpl(rpl);
shift++;
} else if (atomic_test_and_clear_bit(store, i)) {
if (shift > 0) {
replay_list[i - shift] = *rpl;
}
store_rpl(&replay_list[i - shift]);
} else if (rst) {
clear_rpl(rpl);
/* Check if this entry was re-used during removal. If so, shift it as well.
* Otherwise, increment shift counter.
*/
if (atomic_test_and_clear_bit(store, i)) {
replay_list[i - shift] = *rpl;
atomic_set_bit(store, i - shift);
} else {
shift++;
}
}
last = i;
if (addr != BT_MESH_ADDR_ALL_NODES) {
break;
}
}
atomic_test_and_clear_bit(&rpl_flags, PENDING_RESET);
if (addr == BT_MESH_ADDR_ALL_NODES) {
(void)memset(&replay_list[last - shift + 1], 0, sizeof(struct bt_mesh_rpl) * shift);
}
}