Skip to content

Commit

Permalink
samples: fs: zms: add a sample app for ZMS storage system
Browse files Browse the repository at this point in the history
This adds a user application that shows the usage of ZMS
The sample app shows three main functions of ZMS:
- read/write/delete key/value pairs
- fill all storage and delete it
- calculate free remaining space

Signed-off-by: Riadh Ghaddab <rghaddab@baylibre.com>
  • Loading branch information
rghaddab authored and fabiobaltieri committed Oct 24, 2024
1 parent 004d6d6 commit fb7dae7
Show file tree
Hide file tree
Showing 5 changed files with 388 additions and 0 deletions.
10 changes: 10 additions & 0 deletions samples/subsys/fs/zms/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

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


target_sources(app PRIVATE src/main.c)
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/fs/zms)
96 changes: 96 additions & 0 deletions samples/subsys/fs/zms/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
.. zephyr:code-sample:: zms
:name: Zephyr Memory Storage (ZMS)
:relevant-api: zms_high_level_api

Store and retrieve data from storage using the ZMS API.

Overview
********
The sample shows how to use ZMS to store ID/VALUE pairs and reads them back.
Deleting an ID/VALUE pair is also shown in this sample.

The sample stores the following items:

#. A string representing an IP address: stored at id=1, data="192.168.1.1"
#. A binary blob representing a key/value pair: stored at id=0xbeefdead,
data={0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF}
#. A variable (32bit): stored at id=2, data=cnt
#. A long set of data (128 bytes)

A loop is executed where we mount the storage system, and then write all set
of data.

Each DELETE_ITERATION period, we delete all set of data and verify that it has been deleted.
We generate as well incremented ID/value pairs, we store them until storage is full, then we
delete them and verify that storage is empty.

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

* A board with flash support or native_sim target

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

This sample can be found under :zephyr_file:`samples/subsys/fs/zms` in the Zephyr tree.

The sample can be built for several platforms, but for the moment it has been tested only
on native_sim target

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

After running the generated image on a native_sim target, the output on the console shows the
multiple Iterations of read/write/delete exectuted.

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

.. code-block:: console
*** Booting Zephyr OS build v3.7.0-2383-g624f75400242 ***
[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
ITERATION: 0
Adding IP_ADDRESS 172.16.254.1 at id 1
Adding key/value at id beefdead
Adding counter at id 2
Adding Longarray at id 3
[00:00:00.000,000] <inf> fs_zms: 3 Sectors of 4096 bytes
[00:00:00.000,000] <inf> fs_zms: alloc wra: 0, f80
[00:00:00.000,000] <inf> fs_zms: data wra: 0, 8c
ITERATION: 1
ID: 1, IP Address: 172.16.254.1
Adding IP_ADDRESS 172.16.254.1 at id 1
Id: beefdead, Key: de ad be ef de ad be ef
Adding key/value at id beefdead
Id: 2, loop_cnt: 0
Adding counter at id 2
Id: 3, Longarray: 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 5
4 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
Adding Longarray at id 3
.
.
.
.
.
.
[00:00:00.000,000] <inf> fs_zms: 3 Sectors of 4096 bytes
[00:00:00.000,000] <inf> fs_zms: alloc wra: 0, f40
[00:00:00.000,000] <inf> fs_zms: data wra: 0, 80
ITERATION: 299
ID: 1, IP Address: 172.16.254.1
Adding IP_ADDRESS 172.16.254.1 at id 1
Id: beefdead, Key: de ad be ef de ad be ef
Adding key/value at id beefdead
Id: 2, loop_cnt: 298
Adding counter at id 2
Id: 3, Longarray: 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 5
4 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
Adding Longarray at id 3
Memory is full let's delete all items
Free space in storage is 8064 bytes
Sample code finished Successfully
5 changes: 5 additions & 0 deletions samples/subsys/fs/zms/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y

CONFIG_ZMS=y
CONFIG_LOG=y
10 changes: 10 additions & 0 deletions samples/subsys/fs/zms/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
sample:
name: ZMS Sample

tests:
sample.zms.basic:
tags: zms
depends_on: zms
platform_allow:
- qemu_x86
- native_posix
267 changes: 267 additions & 0 deletions samples/subsys/fs/zms/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
/*
* Copyright (c) 2024 BayLibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*
* ZMS Sample for Zephyr using high level API.
*
*/

#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/device.h>
#include <string.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/fs/zms.h>

static struct zms_fs fs;

#define ZMS_PARTITION storage_partition
#define ZMS_PARTITION_DEVICE FIXED_PARTITION_DEVICE(ZMS_PARTITION)
#define ZMS_PARTITION_OFFSET FIXED_PARTITION_OFFSET(ZMS_PARTITION)

#define IP_ADDRESS_ID 1
#define KEY_VALUE_ID 0xbeefdead
#define CNT_ID 2
#define LONG_DATA_ID 3

#define MAX_ITERATIONS 300
#define DELETE_ITERATION 10

static int delete_and_verify_items(struct zms_fs *fs, uint32_t id)
{
int rc = 0;

rc = zms_delete(fs, id);
if (rc) {
goto error1;
}
rc = zms_get_data_length(fs, id);
if (rc > 0) {
goto error2;
}

return 0;
error1:
printk("Error while deleting item rc=%d\n", rc);
return rc;
error2:
printk("Error, Delete failed item should not be present\n");
return -1;
}

static int delete_basic_items(struct zms_fs *fs)
{
int rc = 0;

rc = delete_and_verify_items(fs, IP_ADDRESS_ID);
if (rc) {
printk("Error while deleting item %x rc=%d\n", IP_ADDRESS_ID, rc);
return rc;
}
rc = delete_and_verify_items(fs, KEY_VALUE_ID);
if (rc) {
printk("Error while deleting item %x rc=%d\n", KEY_VALUE_ID, rc);
return rc;
}
rc = delete_and_verify_items(fs, CNT_ID);
if (rc) {
printk("Error while deleting item %x rc=%d\n", CNT_ID, rc);
return rc;
}
rc = delete_and_verify_items(fs, LONG_DATA_ID);
if (rc) {
printk("Error while deleting item %x rc=%d\n", LONG_DATA_ID, rc);
}

return rc;
}

int main(void)
{
int rc = 0;
char buf[16];
uint8_t key[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF}, longarray[128];
uint32_t i_cnt = 0U, i;
uint32_t id = 0;
ssize_t free_space = 0;
struct flash_pages_info info;

for (int n = 0; n < sizeof(longarray); n++) {
longarray[n] = n;
}

/* define the zms file system by settings with:
* sector_size equal to the pagesize,
* 3 sectors
* starting at ZMS_PARTITION_OFFSET
*/
fs.flash_device = ZMS_PARTITION_DEVICE;
if (!device_is_ready(fs.flash_device)) {
printk("Storage device %s is not ready\n", fs.flash_device->name);
return 0;
}
fs.offset = ZMS_PARTITION_OFFSET;
rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info);
if (rc) {
printk("Unable to get page info, rc=%d\n", rc);
return 0;
}
fs.sector_size = info.size;
fs.sector_count = 3U;

for (i = 0; i < MAX_ITERATIONS; i++) {
rc = zms_mount(&fs);
if (rc) {
printk("Storage Init failed, rc=%d\n", rc);
return 0;
}

printk("ITERATION: %u\n", i);
/* IP_ADDRESS_ID is used to store an address, lets see if we can
* read it from flash, since we don't know the size read the
* maximum possible
*/
rc = zms_read(&fs, IP_ADDRESS_ID, &buf, sizeof(buf));
if (rc > 0) {
/* item was found, show it */
buf[rc] = '\0';
printk("ID: %u, IP Address: %s\n", IP_ADDRESS_ID, buf);
}
/* Rewriting ADDRESS IP even if we found it */
strncpy(buf, "172.16.254.1", sizeof(buf) - 1);
printk("Adding IP_ADDRESS %s at id %u\n", buf, IP_ADDRESS_ID);
rc = zms_write(&fs, IP_ADDRESS_ID, &buf, strlen(buf));
if (rc < 0) {
printk("Error while writing Entry rc=%d\n", rc);
break;
}

/* KEY_VALUE_ID is used to store a key/value pair , lets see if we can read
* it from storage.
*/
rc = zms_read(&fs, KEY_VALUE_ID, &key, sizeof(key));
if (rc > 0) { /* item was found, show it */
printk("Id: %x, Key: ", KEY_VALUE_ID);
for (int n = 0; n < 8; n++) {
printk("%x ", key[n]);
}
printk("\n");
}
/* Rewriting KEY_VALUE even if we found it */
printk("Adding key/value at id %x\n", KEY_VALUE_ID);
rc = zms_write(&fs, KEY_VALUE_ID, &key, sizeof(key));
if (rc < 0) {
printk("Error while writing Entry rc=%d\n", rc);
break;
}

/* CNT_ID is used to store the loop counter, lets see
* if we can read it from storage
*/
rc = zms_read(&fs, CNT_ID, &i_cnt, sizeof(i_cnt));
if (rc > 0) { /* item was found, show it */
printk("Id: %d, loop_cnt: %u\n", CNT_ID, i_cnt);
if (i_cnt != (i - 1)) {
break;
}
}
printk("Adding counter at id %u\n", CNT_ID);
rc = zms_write(&fs, CNT_ID, &i, sizeof(i));
if (rc < 0) {
printk("Error while writing Entry rc=%d\n", rc);
break;
}

/* LONG_DATA_ID is used to store a larger dataset ,lets see if we can read
* it from flash
*/
rc = zms_read(&fs, LONG_DATA_ID, &longarray, sizeof(longarray));
if (rc > 0) {
/* item was found, show it */
printk("Id: %d, Longarray: ", LONG_DATA_ID);
for (int n = 0; n < sizeof(longarray); n++) {
printk("%x ", longarray[n]);
}
printk("\n");
}
/* Rewrite the entry even if we found it */
printk("Adding Longarray at id %d\n", LONG_DATA_ID);
rc = zms_write(&fs, LONG_DATA_ID, &longarray, sizeof(longarray));
if (rc < 0) {
printk("Error while writing Entry rc=%d\n", rc);
break;
}

/* Each DELETE_ITERATION delete all basic items */
if (!(i % DELETE_ITERATION) && (i)) {
rc = delete_basic_items(&fs);
if (rc) {
break;
}
}
}

if (i != MAX_ITERATIONS) {
printk("Error: Something went wrong at iteration %u rc=%d\n", i - 1, rc);
return 0;
}

while (1) {
/* fill all storage */
rc = zms_write(&fs, id, &id, sizeof(uint32_t));
if (rc < 0) {
break;
}
id++;
}

if (rc == -ENOSPC) {
/* Calculate free space and verify that it is 0 */
free_space = zms_calc_free_space(&fs);
if (free_space < 0) {
printk("Error while computing free space, rc=%d\n", free_space);
return 0;
}
if (free_space > 0) {
printk("Error: free_space should be 0, computed %u\n", free_space);
return 0;
}
printk("Memory is full let's delete all items\n");

/* Now delete all previously written items */
for (uint32_t n = 0; n < id; n++) {
rc = delete_and_verify_items(&fs, n);
if (rc) {
printk("Error deleting at id %u\n", n);
return 0;
}
}
rc = delete_basic_items(&fs);
if (rc) {
printk("Error deleting basic items\n");
return 0;
}
}

/*
* Let's compute free space in storage. But before doing that let's Garbage collect
* all sectors where we deleted all entries and then compute the free space
*/
for (uint32_t i = 0; i < fs.sector_count; i++) {
rc = zms_sector_use_next(&fs);
if (rc) {
printk("Error while changing sector rc=%d\n", rc);
}
}
free_space = zms_calc_free_space(&fs);
if (free_space < 0) {
printk("Error while computing free space, rc=%d\n", free_space);
return 0;
}
printk("Free space in storage is %u bytes\n", free_space);
printk("Sample code finished Successfully\n");

return 0;
}

0 comments on commit fb7dae7

Please sign in to comment.