Skip to content

kernel: fix error propagation for device deferred initialization #91221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
21 changes: 18 additions & 3 deletions kernel/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,28 +313,43 @@ static int do_device_init(const struct device *dev)

if (dev->ops.init != NULL) {
rc = dev->ops.init(dev);
/* Mark device initialized. If initialization
* failed, record the error condition.
/* If initialization failed, record in dev->state->init_res
* the POSITIVE value of the resulting errno
*/
if (rc != 0) {
/* device's init function should return:
* 0 on success
* a negative value on failure (-errno)
* errno value maps to an uint8_t range as of now.
*/
__ASSERT(rc >= -UINT8_MAX && rc < 0, "device %s init: invalid error (%d)",
dev->name, rc);

if (rc < 0) {
rc = -rc;
}
/* handle error value overflow in production
* this is likely a bug in the device's init function. Signals it
*/
if (rc > UINT8_MAX) {
rc = UINT8_MAX;
}
dev->state->init_res = rc;
}
}

/* device initialization has been invoked */
dev->state->initialized = true;

if (rc == 0) {
/* Run automatic device runtime enablement */
(void)pm_device_runtime_auto_enable(dev);
}

return rc;
/* here, the value of rc is either 0 or +errno
* flip the sign to return a negative value on failure as expected
*/
return -rc;
}

/**
Expand Down
7 changes: 7 additions & 0 deletions tests/kernel/device/app.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@
zephyr,deferred-init;
};

fakedeferdriver@F9000000 {
compatible = "fakedeferdriver";
reg = <0xF9000000 0x2000>;
status = "okay";
zephyr,deferred-init;
};

fakedomain_0: fakedomain_0 {
compatible = "fakedomain";
status = "okay";
Expand Down
39 changes: 39 additions & 0 deletions tests/kernel/device/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#define FAKEDEFERDRIVER0 DEVICE_DT_GET(DT_PATH(fakedeferdriver_e7000000))
#define FAKEDEFERDRIVER1 DEVICE_DT_GET(DT_PATH(fakedeferdriver_e8000000))
#define FAKEDEFERDRIVER2 DEVICE_DT_GET(DT_PATH(fakedeferdriver_f9000000))

/* A device without init call */
DEVICE_DEFINE(dummy_noinit, DUMMY_NOINIT, NULL, NULL, NULL, NULL,
Expand All @@ -39,6 +40,12 @@ DEVICE_DT_DEFINE(DT_INST(1, fakedeferdriver), NULL, NULL, NULL, NULL,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&fakedeferdriverapi);

/* fake devices used to test deferred initialization failure */
static int fakedeferdriver_init(const struct device *dev);

DEVICE_DT_DEFINE(DT_INST(2, fakedeferdriver), fakedeferdriver_init, NULL, NULL, NULL, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);

/**
* @brief Test cases to verify device objects
*
Expand Down Expand Up @@ -415,6 +422,38 @@ ZTEST(device, test_deferred_init)
zassert_true(device_is_ready(FAKEDEFERDRIVER0));
}

static int fakedeferdriver_init(const struct device *dev)
{
return -EIO;
}

/**
* @brief Test deferred initialization error
*
* @details Verify device_init error cases and expected device states
*
* - case -errno: if the device initialization fails
* - case -EALREADY: if the device is already initialized.
*
* @see device_init
* @ingroup kernel_device_tests
*/
ZTEST(device, test_deferred_init_failure)
{
int ret;
const struct device *dev = FAKEDEFERDRIVER2;

zassert_false(device_is_ready(dev));
ret = device_init(dev);
zassert_equal(ret, -EIO);
zassert_false(device_is_ready(dev));
zassert_equal(dev->state->init_res, EIO);

ret = device_init(dev);
zassert_equal(ret, -EALREADY);
zassert_equal(dev->state->init_res, EIO);
}

ZTEST(device, test_device_api)
{
const struct device *dev;
Expand Down