Skip to content
Open
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
3 changes: 3 additions & 0 deletions include/zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ enum os_mgmt_err_code_t {

/** Query was recognized but there is no valid value for the response. */
OS_MGMT_ERR_QUERY_RESPONSE_VALUE_NOT_VALID,

/** Heap statistic fetch failed. */
OS_MGMT_ERR_HEAP_STATS_FETCH_FAILED,
};

/**
Expand Down
20 changes: 2 additions & 18 deletions subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,22 +130,6 @@ void img_mgmt_release_lock(void)
#endif
}

#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS)
static bool img_mgmt_reset_zse(struct smp_streamer *ctxt)
{
zcbor_state_t *zse = ctxt->writer->zs;

/* Because there is already data in the buffer, it must be cleared first */
net_buf_reset(ctxt->writer->nb);
ctxt->writer->nb->len = sizeof(struct smp_hdr);
zcbor_new_encode_state(zse, ARRAY_SIZE(ctxt->writer->zs),
ctxt->writer->nb->data + sizeof(struct smp_hdr),
net_buf_tailroom(ctxt->writer->nb), 0);

return zcbor_map_start_encode(zse, CONFIG_MCUMGR_SMP_CBOR_MAX_MAIN_MAP_ENTRIES);
}
#endif

#if defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD)
static bool img_mgmt_slot_max_size(size_t *area_sizes, zcbor_state_t *zse)
{
Expand Down Expand Up @@ -597,7 +581,7 @@ static int img_mgmt_slot_info(struct smp_streamer *ctxt)
return err_rc;
}

ok = img_mgmt_reset_zse(ctxt) &&
ok = smp_mgmt_reset_zse(ctxt) &&
smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);

goto finish;
Expand Down Expand Up @@ -645,7 +629,7 @@ static int img_mgmt_slot_info(struct smp_streamer *ctxt)
return err_rc;
}

ok = img_mgmt_reset_zse(ctxt) &&
ok = smp_mgmt_reset_zse(ctxt) &&
smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);

goto finish;
Expand Down
20 changes: 20 additions & 0 deletions subsys/mgmt/mcumgr/grp/os_mgmt/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ config MCUMGR_GRP_OS_TASKSTAT_STACK_INFO

endif

config MCUMGR_GRP_OS_MPSTAT
bool "Support for memory pool statistics command"
depends on SYS_HEAP_RUNTIME_STATS
depends on SYS_HEAP_ARRAY_SIZE > 0
select MCUMGR_SMP_CBOR_MIN_ENCODING_LEVEL_2
select MCUMGR_SMP_CBOR_MIN_ENCODING_LEVEL_3 if ZCBOR_CANONICAL
help
The memory pool statistics (mpstat) command will list the various heap memory pools
on the device, including sizes, free space and minimum amount of free space.

config MCUMGR_GRP_OS_MPSTAT_ONLY_SUPPORTED_STATS
bool "Send only data gathered by Zephyr"
depends on MCUMGR_GRP_OS_MPSTAT
default y
help
Response will not include the "blksiz" field which Zephyr does not support (as it has a
value of 1). Enable this if your client software is able to process "mpstat" response
without the "blksiz" field for each memory pool. Enabling this option will slightly
reduce code size.

config MCUMGR_GRP_OS_ECHO
bool "Support for echo command"
default y
Expand Down
98 changes: 98 additions & 0 deletions subsys/mgmt/mcumgr/grp/os_mgmt/src/os_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@

#include <mgmt/mcumgr/util/zcbor_bulk.h>

#ifdef CONFIG_MCUMGR_GRP_OS_MPSTAT
#include <zephyr/sys/sys_heap.h>
#include <mgmt/mcumgr/transport/smp_internal.h>
#endif

#ifdef CONFIG_REBOOT
#include <zephyr/sys/reboot.h>
#endif
Expand Down Expand Up @@ -145,6 +150,22 @@ struct datetime_parser {
extern uint8_t *MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME;
#endif

#ifdef CONFIG_MCUMGR_GRP_OS_MPSTAT
/* Specifies the maximum characters in the memory pool ID, allowing up to 999 outputs */
#define MPSTAT_ID_KEY_SIZE 4

#ifdef CONFIG_MCUMGR_GRP_OS_MPSTAT_ONLY_SUPPORTED_STATS
/* Number of items per memory pool output */
#define MPSTAT_KEY_MAP_ENTRIES 3
#else
/* Number of items per memory pool output */
#define MPSTAT_KEY_MAP_ENTRIES 4

/* Specifies the block size of memory pools, zephyr used byte-level allocation */
#define MPSTAT_BLOCK_SIZE 1
#endif
#endif

/**
* Command handler: os echo
*/
Expand Down Expand Up @@ -355,6 +376,76 @@ static int os_mgmt_taskstat_read(struct smp_streamer *ctxt)
}
#endif /* CONFIG_MCUMGR_GRP_OS_TASKSTAT */

#ifdef CONFIG_MCUMGR_GRP_OS_MPSTAT
/**
* Command handler: os mpstat
*/
static int os_mgmt_mpstat_read(struct smp_streamer *ctxt)
{
zcbor_state_t *zse = ctxt->writer->zs;
struct sys_heap **heap;
uint8_t key[MPSTAT_ID_KEY_SIZE] = { 0 };
int heap_elements;
int i = 0;
bool ok;

heap_elements = sys_heap_array_get(&heap);
ok = zcbor_tstr_put_lit(zse, "tasks") &&
zcbor_map_start_encode(zse, heap_elements);

if (!ok) {
goto end;
}

while (i < heap_elements) {
struct sys_memory_stats heap_stats;
uint32_t heap_total_size;
int rc;
uint8_t key_size;

rc = sys_heap_runtime_stats_get(heap[i], &heap_stats);

if (rc != 0) {
ok = smp_mgmt_reset_zse(ctxt) &&
smp_add_cmd_err(zse, MGMT_GROUP_ID_OS,
OS_MGMT_ERR_HEAP_STATS_FETCH_FAILED);
LOG_ERR("Failed to get heap stats from address %p: %d", heap[i], rc);
goto end;
}

key_size = u8_to_dec(key, sizeof(key), i);
heap_total_size = heap_stats.allocated_bytes + heap_stats.free_bytes;

ok = zcbor_tstr_encode_ptr(zse, key, (size_t)key_size) &&
zcbor_map_start_encode(zse, MPSTAT_KEY_MAP_ENTRIES) &&
#ifndef CONFIG_MCUMGR_GRP_OS_MPSTAT_ONLY_SUPPORTED_STATS
zcbor_tstr_put_lit(zse, "blksiz") &&
zcbor_uint32_put(zse, MPSTAT_BLOCK_SIZE) &&
#endif
zcbor_tstr_put_lit(zse, "nblks") &&
zcbor_uint32_put(zse, heap_total_size) &&
zcbor_tstr_put_lit(zse, "nfree") &&
zcbor_uint32_put(zse, heap_stats.free_bytes) &&
zcbor_tstr_put_lit(zse, "min") &&
zcbor_uint32_put(zse, (heap_total_size - heap_stats.max_allocated_bytes)) &&
zcbor_map_end_encode(zse, MPSTAT_KEY_MAP_ENTRIES);

if (!ok) {
break;
}

++i;
}

if (ok == true) {
ok = zcbor_map_end_encode(zse, heap_elements);
}

end:
return MGMT_RETURN_CHECK(ok);
}
#endif /* CONFIG_MCUMGR_GRP_OS_MPSTAT */

#ifdef CONFIG_REBOOT
/**
* Command handler: os reset
Expand Down Expand Up @@ -1117,6 +1208,7 @@ static int os_mgmt_translate_error_code(uint16_t err)
case OS_MGMT_ERR_QUERY_YIELDS_NO_ANSWER:
case OS_MGMT_ERR_RTC_NOT_SET:
case OS_MGMT_ERR_QUERY_RESPONSE_VALUE_NOT_VALID:
case OS_MGMT_ERR_HEAP_STATS_FETCH_FAILED:
rc = MGMT_ERR_ENOENT;
break;

Expand All @@ -1142,6 +1234,12 @@ static const struct mgmt_handler os_mgmt_group_handlers[] = {
},
#endif

#ifdef CONFIG_MCUMGR_GRP_OS_MPSTAT
[OS_MGMT_ID_MPSTAT] = {
os_mgmt_mpstat_read, NULL
},
#endif

#ifdef CONFIG_MCUMGR_GRP_OS_DATETIME
[OS_MGMT_ID_DATETIME_STR] = {
os_mgmt_datetime_read, os_mgmt_datetime_write
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/net_buf.h>
#include <zephyr/mgmt/mcumgr/smp/smp.h>
#include <zephyr/mgmt/mcumgr/transport/smp.h>
#include <zcbor_encode.h>

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -79,6 +81,30 @@ void *smp_alloc_rsp(const void *req, void *arg);
*/
void smp_free_buf(void *buf, void *arg);

/**
* @brief Reeset a zcbor encoder to allow a new response.
*
* If a response has already been (partially) generated than this will allow resetting back to
* the default state so that new response can be used (e.g. an error).
*
* @param streamer The streamer providing the required SMP callbacks.
*
* @return true on success, false on failure (memory error).
*/
static inline bool smp_mgmt_reset_zse(struct smp_streamer *streamer)
{
zcbor_state_t *zse = streamer->writer->zs;

/* Because there is already data in the buffer, it must be cleared first */
net_buf_reset(streamer->writer->nb);
streamer->writer->nb->len = sizeof(struct smp_hdr);
zcbor_new_encode_state(zse, ARRAY_SIZE(streamer->writer->zs),
streamer->writer->nb->data + sizeof(struct smp_hdr),
net_buf_tailroom(streamer->writer->nb), 0);

return zcbor_map_start_encode(zse, CONFIG_MCUMGR_SMP_CBOR_MAX_MAIN_MAP_ENTRIES);
}

#ifdef __cplusplus
}
#endif
Expand Down
14 changes: 14 additions & 0 deletions tests/subsys/mgmt/mcumgr/os_mgmt_mpstat/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#
# Copyright (c) 2023 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(os_mgmt_mpstat)

FILE(GLOB app_sources src/*.c)

target_sources(app PRIVATE ${app_sources})
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/mgmt/mcumgr/transport/include/mgmt/mcumgr/transport/)
24 changes: 24 additions & 0 deletions tests/subsys/mgmt/mcumgr/os_mgmt_mpstat/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# Copyright (c) 2025 Jamie McCrae
#
# SPDX-License-Identifier: Apache-2.0
#

CONFIG_ZTEST=y
CONFIG_NET_BUF=y
CONFIG_BASE64=y
CONFIG_ZCBOR=y
CONFIG_CRC=y
CONFIG_MINIMAL_LIBC=y
CONFIG_COMMON_LIBC_MALLOC=y
CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=1024
CONFIG_HEAP_MEM_POOL_SIZE=256
CONFIG_SYS_HEAP_RUNTIME_STATS=y
CONFIG_SYS_HEAP_ARRAY_SIZE=32
CONFIG_MCUMGR=y
CONFIG_MCUMGR_TRANSPORT_DUMMY=y
CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE=384
CONFIG_MCUMGR_GRP_OS=y
CONFIG_MCUMGR_GRP_OS_ECHO=n
CONFIG_MCUMGR_GRP_OS_MPSTAT=y
CONFIG_ZTEST_STACK_SIZE=2048
Loading