feat(nimble): Added LFU method for device deletion post bond count overflow

This commit is contained in:
Rahul Tank
2026-09-25 12:24:25 +05:30
parent 544bb668ad
commit df474b447b
8 changed files with 344 additions and 7 deletions
+63
View File
@@ -451,8 +451,71 @@ int ble_store_util_delete_all(int type, const union ble_store_key *key);
int ble_store_util_delete_peer(const ble_addr_t *peer_id_addr);
int ble_store_util_delete_oldest_peer(void);
int ble_store_util_count(int type, int *out_count);
/**
* @brief Round-robin status callback for handling store status events.
*
* This function handles store status events, particularly in cases where there
* is insufficient storage capacity for new records.
* It attempts to resolve overflow issues by deleting the oldest bond and
* proceeds with the persist operation.
*
* @note This behavior may not be suitable for production use as it may lead to
* removal of important bonds by less relevant peers. It is more useful for
* demonstration purposes and sample applications.
*
* @note Retained for backward compatibility. It follows the eviction policy
* selected at build time, so it removes the least recently used bond instead
* of the oldest one when the least-recently-used policy is enabled. New
* applications should use ble_store_util_status_overflow().
*
* @param event A pointer to the store status event.
* @param arg A pointer to additional user-defined arguments.
*
* @return 0 on success;
* Non-zero on error.
*/
int ble_store_util_status_rr(struct ble_store_status_event *event, void *arg);
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
/**
* @brief Least-recently-used status callback for store overflow events.
*
* Removes the least recently used, disconnected bond when storage is full.
* The peer associated with the record being written is never selected.
*
* @param event A pointer to the store status event.
* @param arg A pointer to additional user-defined arguments.
*
* @return 0 on success;
* Non-zero on error.
*/
int ble_store_util_status_lfu(struct ble_store_status_event *event, void *arg);
/**
* @brief Mark a bonded peer as recently used.
*
* Updates OUR_SEC recency so least-recently-used overflow eviction preserves
* this bond. Safe to call when no bond exists yet (returns 0).
*/
int ble_store_util_touch_peer(const ble_addr_t *peer_id_addr);
#endif
/**
* @brief Configured status callback for handling store status events.
*
* Dispatches to the bond eviction policy selected at build time: round-robin
* by default, or least recently used when BLE_STORE_OVERFLOW_LFU is enabled.
*
* @param event A pointer to the store status event.
* @param arg A pointer to additional user-defined arguments.
*
* @return 0 on success;
* Non-zero on error.
*/
int ble_store_util_status_overflow(struct ble_store_status_event *event,
void *arg);
#ifdef __cplusplus
}
#endif
+6
View File
@@ -41,6 +41,12 @@ struct ble_hs_resolv_entry {
#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST)
/* Persist peer records in NVS. XXX Need to handle this in `store` module */
int ble_store_persist_peer_records(void);
#else
static inline int
ble_store_persist_peer_records(void)
{
return 0;
}
#endif
struct ble_hs_peer_sec {
+14 -1
View File
@@ -49,7 +49,6 @@
#include "host/ble_store.h"
#include "ble_hs_priv.h"
#include "ble_hs_resolv_priv.h"
#include "../store/config/src/ble_store_config_priv.h"
#include "esp_nimble_mem.h"
#include "host/ble_hs_log.h"
@@ -1449,6 +1448,10 @@ ble_sm_enc_event_rx(uint16_t conn_handle, uint8_t evt_status, int encrypted)
struct ble_sm_result res;
struct ble_sm_proc *proc;
bool terminate_conn;
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
struct ble_gap_conn_desc desc;
int rc;
#endif
int authenticated;
int bonded;
int key_size;
@@ -1560,6 +1563,16 @@ ble_sm_enc_event_rx(uint16_t conn_handle, uint8_t evt_status, int encrypted)
ble_gap_terminate(conn_handle, BLE_ERR_AUTH_FAIL);
}
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
if (evt_status == 0 && encrypted &&
ble_gap_conn_find(conn_handle, &desc) == 0) {
rc = ble_store_util_touch_peer(&desc.peer_id_addr);
if (rc != 0) {
BLE_HS_LOG(WARN, "failed to update bond recency; rc=%d\n", rc);
}
}
#endif
res.bonded = bonded;
ble_sm_process_result(conn_handle, &res, true);
}
+181 -2
View File
@@ -452,6 +452,7 @@ ble_store_util_delete_ead_oldest_peer(void)
}
#endif
#if !(MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS))
/**
* Round-robin status callback. If a there is insufficient storage capacity
* for a new record, delete the oldest bond and proceed with the persist
@@ -461,8 +462,8 @@ ble_store_util_delete_ead_oldest_peer(void)
* uninteresting peers could cause important bonds to be deleted. This is
* useful for demonstrations and sample apps.
*/
int
ble_store_util_status_rr(struct ble_store_status_event *event, void *arg)
static int
ble_store_util_status_rr_evict(struct ble_store_status_event *event, void *arg)
{
switch (event->event_code) {
case BLE_STORE_EVENT_OVERFLOW:
@@ -496,3 +497,181 @@ ble_store_util_status_rr(struct ble_store_status_event *event, void *arg)
return BLE_HS_EUNKNOWN;
}
}
#endif
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
struct ble_store_util_lfu {
const ble_addr_t *except_addr;
ble_addr_t peer_addr;
uint16_t bond_count;
bool found;
};
struct ble_store_util_conn_check {
const ble_addr_t *peer_id_addr;
bool found;
};
static int
ble_store_util_conn_check(struct ble_hs_conn *conn, void *arg)
{
struct ble_store_util_conn_check *check;
struct ble_hs_conn_addrs addrs;
check = arg;
ble_hs_conn_addrs(conn, &addrs);
if (ble_addr_cmp(&addrs.peer_id_addr, check->peer_id_addr) == 0) {
check->found = true;
return 1;
}
return 0;
}
static bool
ble_store_util_peer_connected(const ble_addr_t *peer_id_addr)
{
struct ble_store_util_conn_check check = {
.peer_id_addr = peer_id_addr,
};
ble_hs_lock();
ble_hs_conn_foreach(ble_store_util_conn_check, &check);
ble_hs_unlock();
return check.found;
}
static int
ble_store_util_find_lfu(int obj_type, union ble_store_value *val, void *arg)
{
struct ble_store_util_lfu *lfu;
BLE_HS_DBG_ASSERT(obj_type == BLE_STORE_OBJ_TYPE_OUR_SEC);
lfu = arg;
if (lfu->except_addr != NULL &&
ble_addr_cmp(&val->sec.peer_addr, lfu->except_addr) == 0) {
return 0;
}
/* Never remove a bond which belongs to an active connection. */
if (ble_store_util_peer_connected(&val->sec.peer_addr)) {
return 0;
}
if (!lfu->found || val->sec.bond_count < lfu->bond_count) {
lfu->peer_addr = val->sec.peer_addr;
lfu->bond_count = val->sec.bond_count;
lfu->found = true;
}
return 0;
}
static int
ble_store_util_unpair_lfu(const ble_addr_t *except_addr)
{
struct ble_store_util_lfu lfu = {
.except_addr = except_addr,
};
int rc;
rc = ble_store_iterate(BLE_STORE_OBJ_TYPE_OUR_SEC,
ble_store_util_find_lfu, &lfu);
if (rc != 0) {
return rc;
}
if (!lfu.found) {
return BLE_HS_ESTORE_CAP;
}
return ble_gap_unpair(&lfu.peer_addr);
}
int
ble_store_util_status_lfu(struct ble_store_status_event *event, void *arg)
{
const ble_addr_t *except_addr;
switch (event->event_code) {
case BLE_STORE_EVENT_OVERFLOW:
except_addr = NULL;
switch (event->overflow.obj_type) {
case BLE_STORE_OBJ_TYPE_OUR_SEC:
case BLE_STORE_OBJ_TYPE_PEER_SEC:
except_addr = &event->overflow.value->sec.peer_addr;
break;
case BLE_STORE_OBJ_TYPE_PEER_ADDR:
except_addr = &event->overflow.value->rpa_rec.peer_addr;
break;
case BLE_STORE_OBJ_TYPE_CCCD:
except_addr = &event->overflow.value->cccd.peer_addr;
break;
case BLE_STORE_OBJ_TYPE_CSFC:
except_addr = &event->overflow.value->csfc.peer_addr;
break;
#if MYNEWT_VAL(ENC_ADV_DATA)
case BLE_STORE_OBJ_TYPE_ENC_ADV_DATA:
return ble_store_util_delete_ead_oldest_peer();
#endif
default:
BLE_HS_LOG(ERROR, "%s rc=%d\n", __func__, BLE_HS_EUNKNOWN);
return BLE_HS_EUNKNOWN;
}
return ble_store_util_unpair_lfu(except_addr);
case BLE_STORE_EVENT_FULL:
return 0;
default:
BLE_HS_LOG(ERROR, "%s rc=%d\n", __func__, BLE_HS_EUNKNOWN);
return BLE_HS_EUNKNOWN;
}
}
#endif
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
int
ble_store_util_touch_peer(const ble_addr_t *peer_id_addr)
{
struct ble_store_key_sec key_sec = {0};
struct ble_store_value_sec value_sec;
int rc;
if (peer_id_addr == NULL ||
ble_addr_cmp(peer_id_addr, BLE_ADDR_ANY) == 0) {
return BLE_HS_EINVAL;
}
key_sec.peer_addr = *peer_id_addr;
rc = ble_store_read_our_sec(&key_sec, &value_sec);
if (rc == BLE_HS_ENOENT) {
/* Encryption can complete before bond keys are persisted. */
return 0;
}
if (rc != 0) {
return rc;
}
return ble_store_write_our_sec(&value_sec);
}
#endif
int
ble_store_util_status_overflow(struct ble_store_status_event *event, void *arg)
{
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
return ble_store_util_status_lfu(event, arg);
#else
return ble_store_util_status_rr_evict(event, arg);
#endif
}
int
ble_store_util_status_rr(struct ble_store_status_event *event, void *arg)
{
return ble_store_util_status_overflow(event, arg);
}
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "sysinit/sysinit.h"
@@ -284,6 +285,10 @@ ble_store_config_write_our_sec(const struct ble_store_value_sec *value_sec)
{
#if MYNEWT_VAL(BLE_STORE_MAX_BONDS)
struct ble_store_key_sec key_sec;
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
struct ble_store_value_sec persisted_sec;
int updated_existing;
#endif
int idx;
int rc;
@@ -293,6 +298,9 @@ ble_store_config_write_our_sec(const struct ble_store_value_sec *value_sec)
ble_store_key_from_value_sec(&key_sec, value_sec);
idx = ble_store_config_find_sec(&key_sec, ble_store_config_our_secs,
ble_store_config_num_our_secs);
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
updated_existing = idx != -1;
#endif
if (idx == -1) {
if (ble_store_config_num_our_secs >= MYNEWT_VAL(BLE_STORE_MAX_BONDS)) {
BLE_HS_LOG(DEBUG, "error persisting our sec; too many entries "
@@ -308,12 +316,25 @@ ble_store_config_write_our_sec(const struct ble_store_value_sec *value_sec)
ble_store_config_our_secs[idx] = *value_sec;
ble_store_config_our_secs[idx].bond_count = ++ble_store_config_our_bond_count;
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
persisted_sec = ble_store_config_our_secs[idx];
#endif
/* Ensure entries are sorted at all times */
qsort(ble_store_config_our_secs, ble_store_config_num_our_secs,
sizeof(struct ble_store_value_sec), ble_store_config_compare_bond_count);
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
if (updated_existing) {
rc = ble_store_config_persist_our_sec_value(&persisted_sec);
if (rc == BLE_HS_ENOENT) {
rc = ble_store_config_persist_our_secs();
}
} else
#endif
{
rc = ble_store_config_persist_our_secs();
}
if (rc != 0) {
return rc;
}
@@ -294,6 +294,16 @@ ble_store_config_persist_our_secs(void)
return 0;
}
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
int
ble_store_config_persist_our_sec_value(
const struct ble_store_value_sec *value_sec)
{
(void)value_sec;
return ble_store_config_persist_our_secs();
}
#endif
int
ble_store_config_persist_peer_secs(void)
{
@@ -151,6 +151,10 @@ extern int ble_store_config_num_local_irks;
#if MYNEWT_VAL(BLE_STORE_MAX_BONDS)
int ble_store_config_persist_our_secs(void);
int ble_store_config_persist_peer_secs(void);
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
int ble_store_config_persist_our_sec_value(
const struct ble_store_value_sec *value_sec);
#endif
#endif
#if MYNEWT_VAL(BLE_STORE_MAX_CCCDS)
int ble_store_config_persist_cccds(void);
@@ -175,6 +179,15 @@ void ble_store_config_deinit(void);
static inline int ble_store_config_persist_our_secs(void) { return 0; }
static inline int ble_store_config_persist_peer_secs(void) { return 0; }
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
static inline int
ble_store_config_persist_our_sec_value(
const struct ble_store_value_sec *value_sec)
{
(void)value_sec;
return 0;
}
#endif
static inline int ble_store_config_persist_cccds(void) { return 0; }
static inline int ble_store_config_persist_csfcs(void) { return 0; }
#if MYNEWT_VAL(ENC_ADV_DATA)
@@ -184,9 +197,6 @@ static inline int ble_store_config_persist_rpa_recs(void) { return 0; }
static inline int ble_store_config_persist_local_irk(void) { return 0; }
static inline void ble_store_config_conf_init(void) { }
//static inline void ble_store_config_deinit(void) { }
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
static inline int ble_store_persist_peer_records(void) { return 0; }
#endif
#endif /* MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) */
#ifdef __cplusplus
@@ -25,6 +25,7 @@
#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST)
#include <stdlib.h>
#include <string.h>
#include <esp_system.h>
#include "sysinit/sysinit.h"
@@ -1489,6 +1490,40 @@ end:
return rc;
}
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
int
ble_store_config_persist_our_sec_value(
const struct ble_store_value_sec *value_sec)
{
union ble_store_value val;
nvs_handle_t nimble_handle;
int nvs_idx;
int rc;
rc = nvs_open(NIMBLE_NVS_NAMESPACE, NVS_READWRITE, &nimble_handle);
if (rc != ESP_OK) {
ESP_LOGE(TAG, "NVS open operation failed");
return BLE_HS_ESTORE_FAIL;
}
nvs_idx = get_nvs_sec_identity_index(nimble_handle, value_sec,
BLE_STORE_OBJ_TYPE_OUR_SEC);
if (nvs_idx == -1) {
nvs_close(nimble_handle);
return BLE_HS_ENOENT;
}
val.sec = *value_sec;
rc = ble_store_nvs_update(nimble_handle, BLE_STORE_OBJ_TYPE_OUR_SEC,
nvs_idx, &val);
if (rc == 0) {
rc = ble_nvs_commit_checked(nimble_handle);
}
nvs_close(nimble_handle);
return rc;
}
#endif
int ble_store_config_persist_our_secs(void)
{
int nvs_count, nvs_idx;