-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bluetooth: Host: Add API to clean BT settings #59821
Conversation
916c929
to
03e3c73
Compare
03e3c73
to
9a3d27c
Compare
14025a8
to
1af09fd
Compare
Shouldn't the Bluetooth subsystem do this automatically upon init? Introducing an explicit public API creates the need for applications to figure out when to call it, not to mention that the inconsistency in storage might not be the application's fault at all. It also seems arbitrary to leave mesh of the scope. What's the solution for inconsistent storage for mesh, or for non-Bluetooth related data for that matter? |
The problem might be that this will take too much time - writing to flash - at an infortunate time. That's why we thought it'd be better for the app to decide when to do GC. |
I discussed it with @PavelVPV, they might do it differently with their own settings layer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks good. One thing that I request changed is the break
in the for
loop, and then a few minor comments/suggestions
* Example of possible leftover: An identity has been deleted, the corresponding | ||
* address as been removed from 'bt/id' but before removing the corresponding | ||
* key in 'bt/keys', the device has been powered off. Calling | ||
* `bt_settings_cleanup` will ensure that the key is removed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to have an additional function that verifies the settings to indicate whether calling this function will change anything?
Something along the lines of bt_settings_valid
so that the use of this function will be
if (!bt_settings_valid()) {
bt_settings_cleanup();
}
or some other way of doing a dry run? I could imagine that for some it may be interesting to see what is incorrect / will be cleaned up before this is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure of the use case. Maybe we could simply add LOG_WRN
for each deleted setting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure of the use case.
If I am a user and I have my settings stored in flash, I'd perhaps rather be aware of an issue in my settings before deleting them, rather than just silently deleting them, e.g. if I store my own settings and that I am foolish enough to store something under bt/id
myself, then I'd rather become aware of that, rather than it just silently getting deleted by the stack
* @warning User must not call this function if they use the 'bt/' namespace to | ||
* store settings. All settings stored under 'bt/' namespace that are not | ||
* defined by the Bluetooth stack will be deleted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like something that should be placed other places as well.
We should perhaps generally recommend (if not already) that applications do not use the bt/
namespace for their BT settings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I think that sharing a settings namespace between an application and any Zephyr subsystem is a bad idea (applications need to use their own namespaces). Maybe we could assume that user should not do it?
subsys/bluetooth/host/settings.c
Outdated
LOG_ERR("Failed to read data (err %d)", len); | ||
} | ||
|
||
id_count = read_len / sizeof(id_addr[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dead code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it could become alive :p
I suggest a BT_DBG("Read back %u identity addresses", id_count);
.
The test fails if I add these: settings_save_one("bt/irk", &dummy_value, sizeof(dummy_value));
settings_save_one("bt/irk/", &dummy_value, sizeof(dummy_value));
settings_save_one("bt/irk/foo", &dummy_value, sizeof(dummy_value)); Should this be handled? |
"mesh", | ||
}; | ||
|
||
static bool mem_eq(const uint8_t *a1, size_t n1, const uint8_t *a2, size_t n2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could use create the is_supported_bt_key
function instead (mem_eq
is quite ambiguous name)?
That would also allow us to also limit scope of bt_settings_keys
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was actually thinking the same during my review
subsys/bluetooth/host/settings.c
Outdated
snprintk(full_key, sizeof(full_key), "bt/%s", key); | ||
LOG_DBG("Key to delete: %s", full_key); | ||
|
||
err = settings_delete(full_key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is deleting settings in the middle of load (exactly direct load) operation guaranteed to be safe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems there are no guarantees, and it is the backend (ie nvs, file, etc) that does the iteration and callback invocation.
So yeah, to be on the safe side, we could store the full names somewhere (maybe a list of malloc'd strings) and do the deleting (and str freeing) after settings_load_subtree_direct("bt", bt_settings_load_direct_cleanup, &id_addr);
.
subsys/bluetooth/host/settings.c
Outdated
int id_count; | ||
bt_addr_le_t *id_addr = (bt_addr_le_t *)param; | ||
|
||
read_len = read_cb(cb_arg, id_addr, CONFIG_BT_ID_MAX * sizeof(id_addr[0])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check for number of stored identities? The settings content may not be aligned with this expectations (e.g. after a DFU)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'm also concerned about that. A test could check the num of IDs increasing and decreasing. idk how settings work but with the current logic i'd think increasing would read back garbage and decreasing would/could not read back the right identities.
Although I think the stack could break down in other places if we tried to decrease the number of bonds. At the very least, test increasing the number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could make use of len
parameter of the direct load callback here? That could let you know the expected data length in advance.
btw. IIRC reading more bytes than available in the storage would simply lower the returned value of read_cb
to indicate that not all bytes were actually read.
For details, see: https://docs.zephyrproject.org/apidoc/latest/group__settings.html#ga51cdac276c1fb8cd27fc3eec42749a04
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may also think about how to handle cases where the number of identities from settings readout does not match value defined in Kconfig.
subsys/bluetooth/host/settings.c
Outdated
return 0; | ||
} | ||
|
||
next_len = settings_name_next(next, &id_str); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should somehow check if given record is expected to have associated Bluetooth address and identity string (e.g. hash
should not have it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe it should. That's way out of the scope of this PR, but storing the last hash seen by a given bond would simplify SC / caching handling quite a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. The main drawback is the fact that storing hash per bond would require quite significant changes in the implementation
subsys/bluetooth/host/settings.c
Outdated
int err; | ||
bt_addr_le_t id_addr[CONFIG_BT_ID_MAX] = {0}; | ||
|
||
err = settings_load_subtree_direct("bt/id", bt_settings_load_direct_id, (void *)id_addr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw. Keep in mind that there may be another dependencies similar to this one. E.g. one may delete keys and leave CCC/CF/SC as a leftover data in storage. We may need to think about some more advanced way of handling settings dependencies at some point if we want to ensure fully proper implementation.
subsys/bluetooth/host/settings.c
Outdated
int id_count; | ||
bt_addr_le_t *id_addr = (bt_addr_le_t *)param; | ||
|
||
read_len = read_cb(cb_arg, id_addr, CONFIG_BT_ID_MAX * sizeof(id_addr[0])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'm also concerned about that. A test could check the num of IDs increasing and decreasing. idk how settings work but with the current logic i'd think increasing would read back garbage and decreasing would/could not read back the right identities.
Although I think the stack could break down in other places if we tried to decrease the number of bonds. At the very least, test increasing the number.
subsys/bluetooth/host/settings.c
Outdated
{ | ||
ssize_t read_len; | ||
int id_count; | ||
bt_addr_le_t *id_addr = (bt_addr_le_t *)param; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/id_addr/id_addrs. I suggest the same for bt_settings_cleanup
, this way it's unambiguous that this is an array of identity addresses and not a ptr to a single identity address.
subsys/bluetooth/host/settings.c
Outdated
id = strtoul(id_str, NULL, 10); | ||
|
||
if (id >= CONFIG_BT_ID_MAX) { | ||
LOG_ERR("Invalid local identity (%lu)", id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah i'd suggest a LOG_WRN("Invalid local identity: %lu > %lu", id, CONFIG_BT_ID_MAX);
I believe them's the guidelines: ERR
for unrecoverable, WRN
for recoverable/ignored.
subsys/bluetooth/host/settings.c
Outdated
|
||
/* At this point, we know that the key need to be cleared */ | ||
snprintk(full_key, sizeof(full_key), "bt/%s", key); | ||
LOG_DBG("Key to delete: %s", full_key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggest a "Deleting key: %s"
subsys/bluetooth/host/settings.c
Outdated
snprintk(full_key, sizeof(full_key), "bt/%s", key); | ||
LOG_DBG("Key to delete: %s", full_key); | ||
|
||
err = settings_delete(full_key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems there are no guarantees, and it is the backend (ie nvs, file, etc) that does the iteration and callback invocation.
So yeah, to be on the safe side, we could store the full names somewhere (maybe a list of malloc'd strings) and do the deleting (and str freeing) after settings_load_subtree_direct("bt", bt_settings_load_direct_cleanup, &id_addr);
.
bt_settings_store_cf(2, &dummy_addr, &dummy_value, sizeof(dummy_value)); | ||
bt_settings_store_ccc(2, &dummy_addr, &dummy_value, sizeof(dummy_value)); | ||
|
||
settings_save_one("bt/mesh/dummy/key", &dummy_value, sizeof(dummy_value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: @alwa-nordic 's comment: you could add a "bt/mes/somekey" setting for that.
Add a new function `bt_settings_cleanup` that allow users to clean potential leftover present in the Bluetooth settings. The function will look for every settings key under the 'bt/' namespace and remove the one not listed in the `bt_settings_keys` list. The settings that are linked to an ID that no longer exist will also be deleted. Signed-off-by: Théo Battrel <[email protected]>
Add a test for the new `bt_settings_cleanup` function. The test artificially write settings then call `bt_settings_cleanup` and check that the settings that should not have been deleted have not been. Signed-off-by: Théo Battrel <[email protected]>
1af09fd
to
33ec6ed
Compare
This seems to be non-trivial to implement without a way to delete settings keys while loading them and trying to respect MISRA (ie no malloc). We are not happy with the current solution (w/ @alwa-nordic and @jori-nordic) so will close the PR. |
See commits message.
Fixes #55475.