Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/services/storage/zms/zms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,18 @@ By default, :c:func:`zms_mount` returns an error if the partition cannot be moun
For recovery-oriented use cases, :c:func:`zms_mount_force` can be used to automatically
wipe and reinitialize the partition when the first mount attempt fails.

Mount flags
-----------

ZMS mount behavior can be controlled with optional flags in ``zms_fs.mount_flags``.

- Default behavior (no optional flags set, ``mount_flags = 0``): if the partition is erased and no valid
ZMS header is found, :c:func:`zms_mount` formats the partition by creating the
initial ZMS header.
- ``ZMS_MOUNT_FLAG_NO_FORMAT``: if the partition is erased and no valid ZMS
header is found, :c:func:`zms_mount` does not format the partition and returns
``-ENOTSUP``.

To mount the filesystem the following elements in the :c:struct:`zms_fs` structure must be initialized:

.. code-block:: c
Expand All @@ -125,6 +137,9 @@ To mount the filesystem the following elements in the :c:struct:`zms_fs` structu
/** Number of sectors in the file system */
uint32_t sector_count;

/** Optional mount behavior flags (enum zms_mount_flags) */
uint32_t mount_flags;

/** Flash device runtime structure */
const struct device *flash_device;
};
Expand Down
48 changes: 48 additions & 0 deletions include/zephyr/kvss/zms.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ extern "C" {
* @{
*/

/** Mount options for @ref zms_mount and @ref zms_mount_force. */
enum zms_mount_flags {
/**
* Do not format erased media during mount.
*
* When this flag is set and the backing flash area is erased (no valid ZMS
* header), mount fails instead of creating a new ZMS header.
*/
ZMS_MOUNT_FLAG_NO_FORMAT = BIT(0),
};

/** Zephyr Memory Storage file system structure */
struct zms_fs {
/** File system offset in flash */
Expand All @@ -49,6 +60,8 @@ struct zms_fs {
uint32_t sector_size;
/** Number of sectors in the file system */
uint32_t sector_count;
/** Mount behavior flags from @ref zms_mount_flags */
uint32_t mount_flags;
/** Current cycle counter of the active sector (pointed to by `ate_wra`) */
uint8_t sector_cycle;
/** Flag indicating if the file system is initialized */
Expand Down Expand Up @@ -91,6 +104,11 @@ typedef uint32_t zms_id_t;
/**
* @brief Mount a ZMS file system onto the device specified in `fs`.
*
* @details If the flash area is erased and no valid ZMS header is found,
* mount will format the area and create a valid header by default.
* Set @ref ZMS_MOUNT_FLAG_NO_FORMAT in `fs->mount_flags` to disable this
* auto-format behavior and fail the mount instead.
*
* @param fs Pointer to the file system.
*
* @retval 0 on success.
Expand Down Expand Up @@ -272,6 +290,36 @@ ssize_t zms_active_sector_free_space(struct zms_fs *fs);
*/
int zms_sector_use_next(struct zms_fs *fs);

/**
* @brief Return the maximum sector recycle count across all sectors.
*
* Iterates all sectors and stores the highest 32-bit cycle counter found in
* each sector's empty ATE in @p cycles. This can be used to estimate
* write-cycle consumption during testing.
*
* @param fs Pointer to the file system.
* @param cycles Pointer to store the maximum 32-bit cycle count across sectors.
*
* @retval 0 on success.
* @retval -EINVAL if @p fs or @p cycles is NULL.
* @retval -EACCES if the file system is not mounted.
*/
int zms_get_num_cycles(struct zms_fs *fs, uint32_t *cycles);

/**
* @brief Return the recycle count for a specific sector.
*
* @param fs Pointer to the file system.
* @param sector Sector index (0-based, must be less than @c fs->sector_count).
* @param cycles Pointer to store the 32-bit cycle count.
*
* @retval 0 on success.
* @retval -EINVAL if @p fs or @p cycles is NULL, or @p sector is out of range.
* @retval -EACCES if the file system is not mounted.
* @retval -ENOENT if the sector has no valid empty ATE.
*/
int zms_get_sector_num_cycles(struct zms_fs *fs, uint32_t sector, uint32_t *cycles);

/**
* @}
*/
Expand Down
1 change: 1 addition & 0 deletions samples/subsys/kvss/kvss.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.. zephyr:code-sample-category:: kvss
:name: Key-Value Storage Systems
:show-listing:
:glob: **/*

Samples that demonstrate how to interact with Key-Value Storage Systems
8 changes: 8 additions & 0 deletions samples/subsys/kvss/zms/zms_cycle_count/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(zms_cycle_count)

target_sources(app PRIVATE src/main.c)
90 changes: 90 additions & 0 deletions samples/subsys/kvss/zms/zms_cycle_count/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
.. zephyr:code-sample:: zms-cycle-count
:name: ZMS Cycle Count Verification
:relevant-api: zms_high_level_api

Verify that ZMS sector cycle counters are tracked correctly across garbage
collection cycles, including past the uint8_t wraparound boundary.

Overview
********

This sample exercises and verifies the ZMS sector cycle counting APIs:

* :c:func:`zms_get_num_cycles`
* :c:func:`zms_get_sector_num_cycles`
* :c:func:`zms_sector_use_next`

It checks that the recycle count returned by ZMS matches the expected value
after many forced sector advances, and that the count keeps growing correctly
once it exceeds the original ``uint8_t`` cycle counter range (256), which is
where the legacy field used to wrap around.

The sample runs in three phases:

#. **Phase 1** -- Mounts the partition, clears it, and performs 30 sector
advances. The cycle count is checked against the expected value
(``base + PHASE1_ITERATIONS / SECTOR_COUNT``).
#. **Phase 2** -- Performs additional sector advances until the relative
cycle count exceeds 256, validating that the counter keeps incrementing
past the historical ``uint8_t`` wraparound point.
#. **Phase 3** -- Reads the per-sector cycle count via
:c:func:`zms_get_sector_num_cycles` for every sector, ensures none of them
are zero, and verifies that an out-of-range sector index returns
``-EINVAL``.

On success the sample prints ``PASS: cycle count verification succeeded``.

Requirements
************

* A board with flash support or the ``native_sim`` target

Building and Running
********************

The sample can be built for several platforms. It has been tested on
``native_sim``, ``qemu_x86`` and ``nrf7002dk/nrf5340/cpuapp``.

.. zephyr-app-commands::
:zephyr-app: samples/subsys/kvss/zms/zms_cycle_count
:goals: build
:compact:

Sample Output
=============

The exact ``base`` value depends on the existing state of the storage
partition (it grows by one full cycle each time the sample is re-run against
persistent flash). The example below shows a fresh-flash run where
``base=1``; subsequent runs simply start from a higher base and end at a
higher final value, but the relative growth and the ``PASS`` line are the
same.

.. code-block:: console

*** Booting Zephyr OS build v4.4.0-rc2 ***
[00:00:00.000,000] <inf> fs_zms: 3 Sectors of 4096 bytes
[00:00:00.000,000] <inf> fs_zms: alloc wra: 0, fc0
[00:00:00.000,000] <inf> fs_zms: data wra: 0, 0
[00:00:00.000,000] <inf> fs_zms: 3 Sectors of 4096 bytes
[00:00:00.000,000] <inf> fs_zms: alloc wra: 0, fc0
[00:00:00.000,000] <inf> fs_zms: data wra: 0, 0
After mount: num_cycles=1 (base=1)
--- Phase 1: 30 sector advances ---
After sector_use_next[1]: num_cycles=1
After sector_use_next[2]: num_cycles=2
After sector_use_next[3]: num_cycles=2
...
After sector_use_next[30]: num_cycles=11
Phase 1 expected=11, got=11
--- Phase 2: 741 sector advances to exceed 256 cycles above base ---
Phase 2 progress [3/741]: num_cycles=12
Phase 2 progress [6/741]: num_cycles=13
...
Phase 2 progress [741/741]: num_cycles=258
Final expected=258, got=258 (total_advances=771)
--- Phase 3: per-sector cycle count verification ---
Sector 0: cycles=258
Sector 1: cycles=258
Sector 2: cycles=257
PASS: cycle count verification succeeded (num_cycles=258 > 255)
6 changes: 6 additions & 0 deletions samples/subsys/kvss/zms/zms_cycle_count/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_ZMS=y
CONFIG_LOG=y
CONFIG_LOG_BLOCK_IN_THREAD=y
CONFIG_MAIN_STACK_SIZE=4096
15 changes: 15 additions & 0 deletions samples/subsys/kvss/zms/zms_cycle_count/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
sample:
name: ZMS Cycle Count Verification

tests:
sample.zms.cycle_count:
tags: zms
platform_allow:
- nrf7002dk/nrf5340/cpuapp
- qemu_x86
- native_sim
harness: console
harness_config:
type: one_line
regex:
- "PASS: cycle count verification succeeded"
Loading
Loading