Skip to content

Proposal to improve the settings subsystem #12331

@fnde-ot

Description

@fnde-ot

Here is a proposal to improve the settings subsystem, I would like to make a PR but I'd prefer to know beforehand if it is something that looks acceptable. It is inspired by the proposal mentioned in issue #8854.

Introduction

Problem description

The current settings subsystem uses key-value settings where a key is a “path”-like string (fx. “bt/keys/aabbccddee0/1”) and binary values are stored as base64 encoded strings. Settings modules (fx. bt_settings) register handlers that are responsible for encoding/decoding keys and values. When loading from permanent storage, the key string is parsed to select the appropriate module handlers to set values in memory.

We would like to be able to:

  • Easily implement a custom storage backend on a per-module basis.
  • Have the encoding done by this custom storage backend.
  • Avoid strings for efficient storage of keys and values.

Proposed change

The suggestion would be to make the following changes to the settings subsystem:

  1. Add module-specific storage handlers.
    These storage functions would be responsible of the encoding/decoding of binary values and perform I/O. The same storage handlers could be used for multiple modules and could be registered from the application or other Zephyr component.

  2. Use module-specific integer keys.
    Each module (fx. bt_keys, bt_mesh, ...) would register its own handlers to the settings subsystem and manage its own key space. Integer keys would remove the need to do key string parsing and make it easier and more efficient for custom storage backends to manage. Information previously stored in the key (fx. bt address, ...) can be stored in the binary value instead.

Detailed RFC

Proposed change (Detailed)

A setting would be a key-value pair with:

  • key: a 32 bit integer, unique within a specific module
  • value: binary value of arbitrary length

The settings subsystem would contain a list of modules containing the following:

  • name: A unique string representing the module (fx. “bt_keys” or “bt_ccc”)
  • module handler: Functions to manage settings in memory, if NULL nothing is to be done.
    • h_set(module_t *module, int key, void *value): Puts a single setting in memory
    • h_commit(module_t *module): Applies settings after being placed in memory
    • h_get(module_t *module, int key): Fetches a single or all (when key=-1) settings from memory and calls the h_save function for each setting found.
  • storage handler: Functions to manage settings in permanent storage, if NULL nothing is to be done.
    • h_init(module_t *module): Initializes storage
    • h_save(module_t *module, int key, void *value): Saves a single item in permanent storage
    • h_load(module_t *module, int key): Loads a single or all settings from storage and calls the h_set function for each setting found. After all settings are loaded, call the h_commit function.

The existing API (settings_load, settings_save_one, ...) could mostly be maintained with minor changes to parameters:

  • settings_init(): Initializes the subsystem
  • settings_load(): Loads from storage all settings from all modules
  • settings_load_one(char *name, int key): Loads one or more settings from storage in a single module
  • settings_save(): Saves to storage all settings from all modules
  • settings_save_one(char *name, int key): Saves one of more settings in a single module

With added functions to register a module and its storage handler independently:

  • settings_register_module(char *name, struct module_handler *handler)
  • settings_register_storage(char *name, struct storage_handler *handler)

The internal flow could look similar to this:

settings_save():
// for each module:

  • h_get(module, key=-1)
    • h_save(module, key, value)
    • h_save(module, key, value)
    • h_save(module, key, value)

settings_load():
// for each module:

  • h_load(module, key=-1)
    • h_set(module, key, value)
    • h_set(module, key, value)
    • h_set(module, key, value)
    • h_commit(module)

settings_save_one(module, key):

  • h_get(module, key)
    • h_save(module, key, value)

settings_load_one(module, key):

  • h_load(module, key)
    • h_set(module, key, value)
    • h_commit(module)

The existing I/O backends (file, flash) and encodings (base64) can easily be ported to that new setup to maintain backward compatibility of already stored data.

Dependencies

Those API changes would require changes to the existing Zephyr settings modules (bluetooth host layer) and could affect user applications that use the existing settings subsystem for custom settings storage.

Alternatives

An alternative would be to create an alternative settings subsystem to choose from using Kconfig parameters, but this would increase the complexity of the BT host layer that would need to support multiple settings subsystems.

Metadata

Metadata

Assignees

Labels

EnhancementChanges/Updates/Additions to existing features

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions