diff --git a/nimble/host/include/host/ble_store.h b/nimble/host/include/host/ble_store.h index 83b1c4f15f..8480c3ce79 100644 --- a/nimble/host/include/host/ble_store.h +++ b/nimble/host/include/host/ble_store.h @@ -90,6 +90,13 @@ struct ble_store_value_sec { /** Peer address for which the security material is stored. */ ble_addr_t peer_addr; + /** + * Optional recency counter for LRU bond eviction. Maintained by the store + * implementation if ble_store_util_status_lru is used. A lower value means + * the peer was used less recently. + */ + uint16_t bond_count; + /** Encryption key size. */ uint8_t key_size; /** Encrypted Diversifier used for encryption key generation. */ @@ -744,6 +751,28 @@ int ble_store_util_count(int type, int *out_count); */ int ble_store_util_status_rr(struct ble_store_status_event *event, void *arg); +/** + * @brief LRU status callback for handling store status events. + * + * This function handles store status events when there is insufficient storage + * capacity for new records. It resolves overflow by deleting the + * least-recently-used bond and proceeds with the persist operation. + * + * Recency is determined from ble_store_value_sec.bond_count when the store + * implementation maintains it. Prefer this over ble_store_util_status_rr when + * recently used bonds should be retained. + * + * Register from the application with: + * ble_hs_cfg.store_status_cb = ble_store_util_status_lru; + * + * @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_lru(struct ble_store_status_event *event, void *arg); + /** @} */ #ifdef __cplusplus diff --git a/nimble/host/src/ble_store_util.c b/nimble/host/src/ble_store_util.c index 6dcbca25a1..ec64c599ad 100644 --- a/nimble/host/src/ble_store_util.c +++ b/nimble/host/src/ble_store_util.c @@ -211,3 +211,112 @@ ble_store_util_status_rr(struct ble_store_status_event *event, void *arg) return BLE_HS_EUNKNOWN; } } + +struct ble_store_util_lru_peer { + ble_addr_t peer_addr; + uint16_t bond_count; + int found; + const ble_addr_t *except; +}; + +static int +ble_store_util_iter_lru_peer(int obj_type, union ble_store_value *val, void *arg) +{ + struct ble_store_util_lru_peer *lru; + + BLE_HS_DBG_ASSERT(obj_type == BLE_STORE_OBJ_TYPE_OUR_SEC); + + lru = arg; + + if (lru->except != NULL && ble_addr_cmp(lru->except, &val->sec.peer_addr) == 0) { + return 0; + } + + if (!lru->found || val->sec.bond_count < lru->bond_count) { + lru->peer_addr = val->sec.peer_addr; + lru->bond_count = val->sec.bond_count; + lru->found = 1; + } + + return 0; +} + +static int +ble_store_util_find_lru_peer(ble_addr_t *out_peer_addr, const ble_addr_t *except) +{ + struct ble_store_util_lru_peer lru = { + .found = 0, + .except = except, + }; + int rc; + + rc = ble_store_iterate(BLE_STORE_OBJ_TYPE_OUR_SEC, + ble_store_util_iter_lru_peer, &lru); + if (rc != 0) { + return rc; + } + + if (!lru.found) { + return BLE_HS_ENOENT; + } + + *out_peer_addr = lru.peer_addr; + return 0; +} + +static int +ble_store_util_unpair_lru_peer(void) +{ + ble_addr_t peer_addr; + int rc; + + rc = ble_store_util_find_lru_peer(&peer_addr, NULL); + if (rc != 0) { + return rc; + } + + return ble_gap_unpair(&peer_addr); +} + +static int +ble_store_util_unpair_lru_except(const ble_addr_t *except) +{ + ble_addr_t peer_addr; + int rc; + + rc = ble_store_util_find_lru_peer(&peer_addr, except); + if (rc != 0) { + return rc; + } + + return ble_gap_unpair(&peer_addr); +} + +int +ble_store_util_status_lru(struct ble_store_status_event *event, void *arg) +{ + switch (event->event_code) { + case BLE_STORE_EVENT_OVERFLOW: + switch (event->overflow.obj_type) { + case BLE_STORE_OBJ_TYPE_OUR_SEC: + case BLE_STORE_OBJ_TYPE_PEER_SEC: + return ble_store_util_unpair_lru_peer(); + case BLE_STORE_OBJ_TYPE_CCCD: + /* Try unpairing LRU peer except current peer */ + return ble_store_util_unpair_lru_except( + &event->overflow.value->cccd.peer_addr); + + default: + return BLE_HS_EUNKNOWN; + } + + case BLE_STORE_EVENT_FULL: + /* Just proceed with the operation. If it results in an overflow, + * we'll delete a record when the overflow occurs. + */ + return 0; + + default: + return BLE_HS_EUNKNOWN; + } +}