From 28fc19d09e70a8721238dfc445c191171cee7e59 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Wed, 14 May 2025 15:59:08 +0200 Subject: [PATCH 1/5] init: Make entry init-function less and introduce service objects Since the addition of deinit operation in device, init and deinit have been within each device, rendering their init entry's init function useless. In order to save ROM space, let's remove the init function from init entry altogether, and introduce a new object called "service" which owns an init function to go along with SYS_INIT/SYS_INIT_NAMED. Signed-off-by: Tomasz Bursztyka --- include/zephyr/device.h | 3 +- include/zephyr/init.h | 24 +++-- .../common-rom/common-rom-kernel-devices.ld | 2 + include/zephyr/service.h | 92 +++++++++++++++++++ kernel/init.c | 33 ++++++- subsys/net/lib/sockets/sockets_service.c | 2 +- 6 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 include/zephyr/service.h diff --git a/include/zephyr/device.h b/include/zephyr/device.h index 07fea3c2d1fc7..b2c34bcab3835 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -1263,8 +1263,7 @@ device_get_dt_nodelabels(const struct device *dev) static const Z_DECL_ALIGN(struct init_entry) __used __noasan Z_INIT_ENTRY_SECTION( \ level, prio, Z_DEVICE_INIT_SUB_PRIO(node_id)) \ Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_id)) = { \ - .init_fn = NULL, \ - .dev = (const struct device *)&DEVICE_NAME_GET(dev_id), \ + .init_obj = { .dev = (const struct device *)&DEVICE_NAME_GET(dev_id) } \ } /** diff --git a/include/zephyr/init.h b/include/zephyr/init.h index d7f254c3c51b1..bf6a57df84298 100644 --- a/include/zephyr/init.h +++ b/include/zephyr/init.h @@ -13,6 +13,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -65,15 +67,14 @@ struct device; */ struct init_entry { /** - * If the init function belongs to a SYS_INIT, this field stored the - * initialization function, otherwise it is set to NULL. - */ - int (*init_fn)(void); - /** - * If the init entry belongs to a device, this fields stores a - * reference to it, otherwise it is set to NULL. + * An init entry can be about a device or a service, _obj will + * be used to differentiate depending on relative sections. */ - const struct device *dev; + union { + const void *_obj; + const struct device *dev; + const struct service *srv; + } init_obj; }; /** @cond INTERNAL_HIDDEN */ @@ -164,9 +165,14 @@ struct init_entry { * @see SYS_INIT() */ #define SYS_INIT_NAMED(name, init_fn_, level, prio) \ + Z_SERVICE_DEFINE(name, init_fn_, level, prio); \ static const Z_DECL_ALIGN(struct init_entry) \ Z_INIT_ENTRY_SECTION(level, prio, 0) __used __noasan \ - Z_INIT_ENTRY_NAME(name) = {.init_fn = (init_fn_), .dev = NULL} \ + Z_INIT_ENTRY_NAME(name) = { \ + .init_obj = { \ + .srv = (const struct service *)&Z_SERVICE_NAME_GET(name) \ + } \ + } /** @} */ diff --git a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld index 716a845134c36..87c91a54f3263 100644 --- a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld +++ b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld @@ -22,6 +22,8 @@ ITERABLE_SECTION_ROM_NUMERIC(device, Z_LINK_ITERABLE_SUBALIGN) + ITERABLE_SECTION_ROM_NUMERIC(service, Z_LINK_ITERABLE_SUBALIGN) + #if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_SHARED_INTERRUPTS) /* since z_shared_isr() is not referenced anywhere when * zephyr_pre0.elf is built, the linker will end up dropping it. diff --git a/include/zephyr/service.h b/include/zephyr/service.h new file mode 100644 index 0000000000000..9a904e91335b8 --- /dev/null +++ b/include/zephyr/service.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 Tomasz Bursztyka. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SERVICE_H_ +#define ZEPHYR_INCLUDE_SERVICE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup service System service + * @ingroup os_services + * + * A system service is a one-shot initialized software component, instantiated + * via SYS_INIT() or SYS_INIT_NAMED(). It does not provide any public API. + * + * @{ + */ + +/** + * @brief Structure to store service instance + * + * This will be defined through SYS_INIT/SYS_INIT_NAMED + */ +struct service { + /** + * Initialization function + */ + int (*init)(void); +}; + +/** @cond INTERNAL_HIDDEN */ + +/** + * @brief Expands to the name of a global service object + * + * @param name Service name. + * + * @return The full name of the service object defined by the service + * definition macro. + */ +#define Z_SERVICE_NAME_GET(name) _CONCAT(__service_, name) + +/** + * @brief Initializer for @ref service + * + * @param init_fn Initialization function (mandatory). + */ +#define Z_SERVICE_INIT(init_fn) \ + { \ + .init = (init_fn) \ + } + +/** + * @brief Service section name (used for sorting purposes). + * + * @param level Initialization level. + * @param prio Initialization priority. + */ +#define Z_SERVICE_SECTION_NAME(level, prio) \ + _CONCAT(INIT_LEVEL_ORD(level), _##prio) + +/** + * @brief Define a @ref service + * + * @param name Service name. + * @param init_fn Initialization function. + * @param level Initialization level. + * @param prio Initialization priority. + */ +#define Z_SERVICE_DEFINE(name, init_fn, level, prio) \ + static const \ + STRUCT_SECTION_ITERABLE_NAMED_ALTERNATE( \ + service, service, \ + Z_SERVICE_SECTION_NAME(level, prio), \ + Z_SERVICE_NAME_GET(name)) = Z_SERVICE_INIT(init_fn) + +/** @endcond */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_SERVICE_H_ */ diff --git a/kernel/init.c b/kernel/init.c index 190114b0d601f..b876676db8dfc 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -138,6 +138,9 @@ enum init_level { extern const struct init_entry __init_SMP_start[]; #endif /* CONFIG_SMP */ +TYPE_SECTION_START_EXTERN(struct service, service); +TYPE_SECTION_END_EXTERN(struct service, service); + /* * storage space for the interrupt stack * @@ -206,6 +209,19 @@ static void z_device_state_init(void) } } +/** + * @brief Check if the init_entry is for a service or not + * + * @param a pointer (see struct init_entry's init_obj._obj attribute) + * + * @return True if it's a service, false otherwise + */ +static inline bool is_entry_about_service(const void *obj) +{ + return (obj >= (void *)_service_list_start && + obj < (void *)_service_list_end); +} + /** * @brief Execute all the init entry initialization functions at a given level * @@ -234,17 +250,26 @@ static void z_sys_init_run_level(enum init_level level) const struct init_entry *entry; for (entry = levels[level]; entry < levels[level+1]; entry++) { - const struct device *dev = entry->dev; int result = 0; + if (unlikely(entry->init_obj._obj == NULL)) { + continue; + } + sys_trace_sys_init_enter(entry, level); - if (dev != NULL) { + + if (is_entry_about_service(entry->init_obj._obj)) { + const struct service *srv = entry->init_obj.srv; + + result = srv->init(); + } else { + const struct device *dev = entry->init_obj.dev; + if ((dev->flags & DEVICE_FLAG_INIT_DEFERRED) == 0U) { result = do_device_init(dev); } - } else { - result = entry->init_fn(); } + sys_trace_sys_init_exit(entry, level, result); } } diff --git a/subsys/net/lib/sockets/sockets_service.c b/subsys/net/lib/sockets/sockets_service.c index 3de03801d3818..4ff8ff51ede08 100644 --- a/subsys/net/lib/sockets/sockets_service.c +++ b/subsys/net/lib/sockets/sockets_service.c @@ -28,7 +28,7 @@ static K_CONDVAR_DEFINE(wait_start); STRUCT_SECTION_START_EXTERN(net_socket_service_desc); STRUCT_SECTION_END_EXTERN(net_socket_service_desc); -static struct service { +static struct service_context { struct zsock_pollfd events[CONFIG_ZVFS_POLL_MAX]; int count; } ctx; From 7b8f5d27849bd30306524b2f1ddb8fe399301eca Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 20 May 2025 15:34:51 +0200 Subject: [PATCH 2/5] scripts: Adapt where to get init function pointer in check_init scripts As the pointer has been removed from struct init_entry, it should be looked up in the pointed object instead, as it has been done already for device objects. Signed-off-by: Tomasz Bursztyka --- scripts/build/check_init_priorities.py | 27 ++++++++++++--------- scripts/build/check_init_priorities_test.py | 19 +++++++++------ 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/scripts/build/check_init_priorities.py b/scripts/build/check_init_priorities.py index 63ae4aa95708d..02ff40ec5a81a 100755 --- a/scripts/build/check_init_priorities.py +++ b/scripts/build/check_init_priorities.py @@ -51,6 +51,8 @@ # The offset of the init pointer in "struct device", in number of pointers. DEVICE_INIT_OFFSET = 5 +# The offset of the init pointer in "struct service", in number of pointers. +SERVICE_INIT_OFFSET = 0 class Priority: """Parses and holds a device initialization priority. @@ -207,20 +209,23 @@ def _process_initlevels(self): raise ValueError(f"no symbol at addr {addr:08x}") obj, size, shidx = self._objects[addr] - arg0_name = self._object_name(self._initlevel_pointer(addr, 0, shidx)) - arg1_name = self._object_name(self._initlevel_pointer(addr, 1, shidx)) + obj_name = self._object_name(self._initlevel_pointer(addr, 0, shidx)) + if obj_name != "unknown": + obj_addr = self._object_addr[obj_name] + _, _, shidx = self._objects[obj_addr] - ordinal = self._device_ord_from_name(arg1_name) - if ordinal: - dev_addr = self._object_addr[arg1_name] - _, _, shidx = self._objects[dev_addr] - arg0_name = self._object_name(self._initlevel_pointer( - dev_addr, DEVICE_INIT_OFFSET, shidx)) + ordinal = self._device_ord_from_name(obj_name) + if ordinal: + init_fn_name = self._object_name(self._initlevel_pointer( + obj_addr, DEVICE_INIT_OFFSET, shidx)) - prio = Priority(level, priority) - self.devices[ordinal] = (prio, arg0_name) + prio = Priority(level, priority) + self.devices[ordinal] = (prio, init_fn_name) + else: + init_fn_name = self._object_name(self._initlevel_pointer( + obj_addr, SERVICE_INIT_OFFSET, shidx)) - self.initlevels[level].append(f"{obj}: {arg0_name}({arg1_name})") + self.initlevels[level].append(f"{obj}: {init_fn_name}({obj_name})") addr += size priority += 1 diff --git a/scripts/build/check_init_priorities_test.py b/scripts/build/check_init_priorities_test.py index 72afc08586ee3..4b6120f733bcb 100755 --- a/scripts/build/check_init_priorities_test.py +++ b/scripts/build/check_init_priorities_test.py @@ -209,24 +209,29 @@ def test_process_initlevels(self, mock_zilinit, mock_ip, mock_on): 0x00: ("a", 4, 0), 0x04: ("b", 4, 0), 0x08: ("c", 4, 0), + 0x0a: ("d", 4, 0), } obj._object_addr = { "__device_dts_ord_11": 0x00, "__device_dts_ord_22": 0x04, + "__service_foo" : 0x0a, } mock_ip.side_effect = lambda *args: args def mock_obj_name(*args): - if args[0] == (0, 5, 0): - return "i0" - elif args[0] == (0, 1, 0): + if args[0] == (0, 0, 0): return "__device_dts_ord_11" + elif args[0] == (4, 0, 0): + return "__device_dts_ord_22" + elif args[0] == (8, 0, 0): + return "__service_foo" + elif args[0] == (10, 0, 0): + return "foo_init_fn" + elif args[0] == (0, 5, 0): + return "i0" elif args[0] == (4, 5, 0): return "i1" - elif args[0] == (4, 1, 0): - return "__device_dts_ord_22" - return f"name_{args[0][0]}_{args[0][1]}" mock_on.side_effect = mock_obj_name obj._process_initlevels() @@ -235,7 +240,7 @@ def mock_obj_name(*args): "EARLY": [], "PRE_KERNEL_1": [], "PRE_KERNEL_2": ["a: i0(__device_dts_ord_11)", "b: i1(__device_dts_ord_22)"], - "POST_KERNEL": ["c: name_8_0(name_8_1)"], + "POST_KERNEL": ["c: foo_init_fn(__service_foo)"], "APPLICATION": [], "SMP": [], }) From 09b9c63443cdd59564b081de3157d1a75e38c158 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 20 May 2025 15:36:33 +0200 Subject: [PATCH 3/5] tests: Fixing output or init_entry object access Following the changes on struct init_entry Signed-off-by: Tomasz Bursztyka --- tests/lib/devicetree/devices/src/main.c | 24 +++++++++---------- .../validate_check_init_priorities_output.py | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/lib/devicetree/devices/src/main.c b/tests/lib/devicetree/devices/src/main.c index 175a3670d822b..e4bc4cc401e7c 100644 --- a/tests/lib/devicetree/devices/src/main.c +++ b/tests/lib/devicetree/devices/src/main.c @@ -70,29 +70,29 @@ DEVICE_DT_DEFINE(TEST_NOLABEL, dev_init, NULL, ZTEST(devicetree_devices, test_init_get) { /* Check device pointers */ - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->init_obj.dev, DEVICE_DT_GET(TEST_GPIO)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->init_obj.dev, DEVICE_DT_GET(TEST_I2C)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->init_obj.dev, DEVICE_DT_GET(TEST_DEVA)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->init_obj.dev, DEVICE_DT_GET(TEST_DEVB)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->init_obj.dev, DEVICE_DT_GET(TEST_GPIOX)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->init_obj.dev, DEVICE_DT_GET(TEST_DEVC)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION_OUTER)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION_OUTER)->init_obj.dev, DEVICE_DT_GET(TEST_PARTITION_OUTER)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION0)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION0)->init_obj.dev, DEVICE_DT_GET(TEST_PARTITION0)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION1)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION1)->init_obj.dev, DEVICE_DT_GET(TEST_PARTITION1)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->init_obj.dev, DEVICE_DT_GET(TEST_GPIO_INJECTED)); - zassert_equal(DEVICE_INIT_GET(manual_dev)->dev, + zassert_equal(DEVICE_INIT_GET(manual_dev)->init_obj.dev, DEVICE_GET(manual_dev)); - zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->init_obj.dev, DEVICE_DT_GET(TEST_NOLABEL)); /* Check init functions */ diff --git a/tests/misc/check_init_priorities/validate_check_init_priorities_output.py b/tests/misc/check_init_priorities/validate_check_init_priorities_output.py index 4f87384c5542c..8b544e58eee38 100755 --- a/tests/misc/check_init_priorities/validate_check_init_priorities_output.py +++ b/tests/misc/check_init_priorities/validate_check_init_priorities_output.py @@ -22,9 +22,9 @@ "__init___device_dts_ord_33: init_fn_1(__device_dts_ord_33)", "__init___device_dts_ord_34: NULL(__device_dts_ord_34)", "__init___device_dts_ord_35: NULL(__device_dts_ord_35)", - "__init_posix_arch_console_init: posix_arch_console_init(NULL)", + "__init_posix_arch_console_init: posix_arch_console_init(__service_posix_arch_console_init)", "PRE_KERNEL_2", - "__init_sys_clock_driver_init: sys_clock_driver_init(NULL)", + "__init_sys_clock_driver_init: sys_clock_driver_init(__service_sys_clock_driver_init)", "POST_KERNEL", "APPLICATION", "SMP", From 7e5d7044c89595d61d3ef3818b7a8b0c91fcdf3e Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 20 May 2025 15:39:46 +0200 Subject: [PATCH 4/5] cmake: Add service section to commo-rom linker generator struct init_entry may point to a struct device or a struct service, each ones belonging to device or service iterable section. Service section being newly added, handling it in cmake relevantly. Signed-off-by: Tomasz Bursztyka --- cmake/linker_script/common/common-rom.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake index 601e987bd9696..1b1473e836d7c 100644 --- a/cmake/linker_script/common/common-rom.cmake +++ b/cmake/linker_script/common/common-rom.cmake @@ -12,6 +12,7 @@ zephyr_linker_section_obj_level(SECTION init LEVEL APPLICATION) zephyr_linker_section_obj_level(SECTION init LEVEL SMP) zephyr_iterable_section(NAME device NUMERIC KVMA RAM_REGION GROUP RODATA_REGION) +zephyr_iterable_section(NAME service NUMERIC KVMA RAM_REGION GROUP RODATA_REGION) if(CONFIG_GEN_SW_ISR_TABLE AND NOT CONFIG_SRAM_SW_ISR_TABLE) # ld align has been changed to subalign to provide identical behavior scatter vs. ld. From 5a08a404f80e5b112b88f0817960b5d9a7939ac8 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Fri, 10 Oct 2025 15:20:50 +0200 Subject: [PATCH 5/5] cmake: Fix codestyle in common-rom.cmake To make CI happier. Signed-off-by: Tomasz Bursztyka --- cmake/linker_script/common/common-rom.cmake | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake index 1b1473e836d7c..0983cd2f8f2fc 100644 --- a/cmake/linker_script/common/common-rom.cmake +++ b/cmake/linker_script/common/common-rom.cmake @@ -202,7 +202,7 @@ if(CONFIG_SYMTAB) zephyr_linker_section_configure(SECTION symtab INPUT ".gnu.linkonce.symtab*") endif() -if (CONFIG_DEVICE_DEPS) +if(CONFIG_DEVICE_DEPS) zephyr_linker_section(NAME device_deps KVMA RAM_REGION GROUP RODATA_REGION NOINPUT ${XIP_ALIGN_WITH_INPUT} ENDALIGN 16) zephyr_linker_section_configure(SECTION device_deps INPUT .__device_deps_pass1* KEEP SORT NAME PASS LINKER_DEVICE_DEPS_PASS1) zephyr_linker_section_configure(SECTION device_deps INPUT .__device_deps_pass2* KEEP SORT NAME PASS NOT LINKER_DEVICE_DEPS_PASS1) @@ -210,28 +210,28 @@ endif() zephyr_iterable_section(NAME _static_thread_data KVMA RAM_REGION GROUP RODATA_REGION) -if (CONFIG_BT_IAS) +if(CONFIG_BT_IAS) zephyr_iterable_section(NAME bt_ias_cb KVMA RAM_REGION GROUP RODATA_REGION) endif() -if (CONFIG_LOG) +if(CONFIG_LOG) zephyr_iterable_section(NAME log_link KVMA RAM_REGION GROUP RODATA_REGION) zephyr_iterable_section(NAME log_backend KVMA RAM_REGION GROUP RODATA_REGION) endif() -if (CONFIG_MULTI_LEVEL_INTERRUPTS) +if(CONFIG_MULTI_LEVEL_INTERRUPTS) zephyr_iterable_section(NAME intc_table KVMA RAM_REGION GROUP RODATA_REGION) endif() -if (CONFIG_HTTP_SERVER) +if(CONFIG_HTTP_SERVER) zephyr_iterable_section(NAME http_service_desc KVMA RAM_REGION GROUP RODATA_REGION) endif() -if (CONFIG_COAP_SERVER) +if(CONFIG_COAP_SERVER) zephyr_iterable_section(NAME coap_service KVMA RAM_REGION GROUP RODATA_REGION) endif() -if (CONFIG_NET_MGMT) +if(CONFIG_NET_MGMT) zephyr_iterable_section(NAME net_mgmt_event_static_handler KVMA RAM_REGION GROUP RODATA_REGION) endif()