zephyr/subsys/bluetooth/host/id.h
Johan Hedberg 39c2605930 Bluetooth: Fix deadlock with settings and bt_hci_cmd_send_sync()
We can't do synchronous HCI command sending in any settings commit()
callback, since we don't know what context the callback is being called
from. One particular deadlock can happen if settings_load() is called from
the preemptible main thread:

main()
 |--> settings_load() (aquire settings_lock mutex)
       |-->commit callback A that defers to system wq
       |-->commit callback B that calls bt_hci_cmd_send_sync()

system wq from the previous deferral from within settings_load():
 |--> work item
       |--> settings_save_one()
             |--> attempt to aquire settings_lock mutex

In the above scenario, the bt_hci_cmd_send_sync() call from the main thread
depends on the system workqueue being processed (since that's what does HCI
command processing by default), while at the same time holding the settings
subsystem's mutex. At the same time, a system wq item tries to store
something into settings, however it deadlocks waiting for the settings
mutex.

The actual scenario that we have in the Bluetooth subsystem is where
"commit callback A" is commit_settings() in host/settings.c, and "commit
callback B" is keys_commit() in host/keys.c.

The solution to the deadlock is to take advantage of deferred bt_id_add()
handling which already exists, i.e. set a flag and deferre the actual
adding to the system workqueue.

Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2025-04-01 16:28:00 +02:00

52 lines
1.4 KiB
C

/*
* Copyright (c) 2017-2021 Nordic Semiconductor ASA
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "keys.h"
#define RPA_TIMEOUT_MS(_rpa_timeout) (_rpa_timeout * MSEC_PER_SEC)
static inline bool bt_id_rpa_is_new(void)
{
#if defined(CONFIG_BT_PRIVACY)
uint32_t remaining_ms = k_ticks_to_ms_floor32(
k_work_delayable_remaining_get(&bt_dev.rpa_update));
/* RPA is considered new if there is less than half a second since the
* timeout was started.
*/
return remaining_ms > (RPA_TIMEOUT_MS(bt_dev.rpa_timeout) - 500);
#else
return false;
#endif
}
int bt_id_init(void);
uint8_t bt_id_read_public_addr(bt_addr_le_t *addr);
int bt_id_set_create_conn_own_addr(bool use_filter, uint8_t *own_addr_type);
int bt_id_set_scan_own_addr(bool active_scan, uint8_t *own_addr_type);
int bt_id_set_adv_own_addr(struct bt_le_ext_adv *adv, uint32_t options,
bool dir_adv, uint8_t *own_addr_type);
bool bt_id_adv_random_addr_check(const struct bt_le_adv_param *param);
bool bt_id_scan_random_addr_check(void);
int bt_id_set_adv_random_addr(struct bt_le_ext_adv *adv,
const bt_addr_t *addr);
int bt_id_set_adv_private_addr(struct bt_le_ext_adv *adv);
int bt_id_set_private_addr(uint8_t id);
void bt_id_pending_keys_update(void);
void bt_id_pending_keys_update_set(struct bt_keys *keys, uint8_t flag);
void bt_id_adv_limited_stopped(struct bt_le_ext_adv *adv);