Skip to content
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

[RFC] Deferred device initialization #67335

Merged
merged 2 commits into from
Apr 11, 2024

Conversation

edersondisouza
Copy link
Collaborator

Following discussions at #64869, this PR implements Option 1 from #39896.

With this PR, a new devicetree property, zephyr,deferred-init can be used to make a device not be initialised during boot. The device can be initialised by calling device_init() from the application.

Under the hood, this PR achieves that by grouping the deferred devices intialisation code in a different ELF section. This way, there's no overhead during normal initialisation: neither memory nor processing penalty. Deferred initialisation however loops over the deferred devices and initialises the matching one, incurring some processing penalty, which is hopefully acceptable.

A new test helps showcase the new feature.

Opens

  • Dependencies are not handled. If a non-deferred device depends on a deferred one, its initialisation will simply fail. We could A) issue warnings/errors during build or B) forcefully make all dependencies also be deferred.
  • Is the processing penalty to initialise a deferred device acceptable? If not, one thing I can think is to expose the initialiser function as non-static and providing a macro to get it and call it directly (think of DEVICE_INIT(DT_PATH(some_node))), but this seems way too ugly. Any ideas?
  • This PR doesn't check if a deferred device is initialised more than once - it will simply re-run the code. My reasoning is that Zephyr currently marks a device as initialised if initialisation was attempted, not if it succeeded. This behaviour is kept for deferred devices for the sake of consistency. But if someone wants to retry initialise some device, checking for attempted initialisation would prevent that.
  • This PR doesn't attempt to tackle the "device de-init" problem - only selective device init.

@@ -149,6 +149,8 @@ typedef int16_t device_handle_t;
#define DEVICE_DT_NAME(node_id) \
DT_PROP_OR(node_id, label, DT_NODE_FULL_NAME(node_id))

#define DEVICE_DT_DEFER(node_id) \
DT_PROP_OR(node_id, zephyr_deferred_init, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DT_PROP(node_id, zephyr_deferred_init) (boolean prop, defaults to 0 if not defined)

kernel/init.c Outdated
Comment on lines 334 to 385
for (entry = __deferred_init_start; entry <= __deferred_init_end; entry++) {
if (entry->dev == dev) {
return do_device_init(entry);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use iterable sections

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - I can't remember the exact macro off the top of my head ITERABLE_SECTION_FOR_EACH() maybe? There is likely an ALT version that should allow for pointer aliasing if necessary

andyross
andyross previously approved these changes Jan 10, 2024
Copy link
Contributor

@andyross andyross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clever and useful. Dropping a +1 even though it'll probably respin for the macrobatic review comments.

@edersondisouza
Copy link
Collaborator Author

v2:

  • Use STRUCT_SECTION_FOREACH_ALTERNATE to loop over init entries;
  • DT_PROP instead of DT_PROP_OR;
  • A bit more docs and style fixes.

npitre
npitre previously approved these changes Jan 10, 2024
@@ -149,6 +149,16 @@ typedef int16_t device_handle_t;
#define DEVICE_DT_NAME(node_id) \
DT_PROP_OR(node_id, label, DT_NODE_FULL_NAME(node_id))

/**
* @brief Deterimine if a devicetree node initialization should be deferred.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

@tbursztyka
Copy link
Collaborator

@tbursztyka, @cfriedt do you have more comment about this approach vs #67877? Did you have a chance to take a look at the more recent discussion at #39896?

I still think it's not the right way. Whether it's a callback and/or a dts property (which is worse than the cb imo), because of the dependencies, because of shortcomings in the device driver model. But I won't block, there seems to be a consensus already.

jhedberg
jhedberg previously approved these changes Apr 5, 2024
@pillo79
Copy link
Collaborator

pillo79 commented Apr 8, 2024

This proposal is pretty close to what I had mocked up for the Arduino-on-Zephyr PoC last year, but properly done - so it definitely gets my preference! 👍

While I also appreciate the extra runtime flexibilty given by the alternative PR, I think the subtle issues arising with such an early state callback (as nicely detailed in this comment's second point), are pretty difficult to convey to users.

gmarull
gmarull previously approved these changes Apr 9, 2024
@nashif nashif added DNM This PR should not be merged (Do Not Merge) and removed DNM This PR should not be merged (Do Not Merge) Architecture Review Discussion in the Architecture WG required labels Apr 9, 2024
dts/bindings/base/base.yaml Outdated Show resolved Hide resolved
include/zephyr/device.h Outdated Show resolved Hide resolved
tests/kernel/device/boards/hifive_unmatched.overlay Outdated Show resolved Hide resolved
@carlescufi
Copy link
Member

Architecture WG:

  • Consensus is moving into the direction of using this approach
  • No current objections to this approach

Currently, all devices are initialized at boot time (following their
level and priority order). This patch introduces deferred
initialization: by setting the property `zephyr,deferred-init` on a
device on the devicetree, Zephyr will not initialized the device.

To initialize such devices, one has to call `device_init()`.

Deferred initialization is done by grouping all deferred devices on a
different ELF section. In this way, there's no need to consume more
memory to keep track of deferred devices. When `device_init()` is
called, Zephyr will scan the deferred devices section and call the
initialization function for the matching device. As this scanning is
done only during deferred device initialization, its cost should be
bearable.

Signed-off-by: Ederson de Souza <[email protected]>
Ensure that devices are not ready before calling `device_init()`, but
are after the call.

Signed-off-by: Ederson de Souza <[email protected]>
@edersondisouza
Copy link
Collaborator Author

v4:

  • Fix typo;
  • Fix documentation quotes;
  • Rebased.

Copy link
Member

@gmarull gmarull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely happy with the result, but let's move on.

@nashif nashif merged commit 2febacc into zephyrproject-rtos:main Apr 11, 2024
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.