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

investigate using the interrupt stack for the idle thread #27585

Closed
andrewboie opened this issue Aug 14, 2020 · 3 comments
Closed

investigate using the interrupt stack for the idle thread #27585

andrewboie opened this issue Aug 14, 2020 · 3 comments
Labels
area: Kernel area: Power Management Enhancement Changes/Updates/Additions to existing features

Comments

@andrewboie
Copy link
Contributor

andrewboie commented Aug 14, 2020

Is your enhancement proposal related to a problem? Please describe.
We reserve some RAM as the CPU's stack for handling interrupts. The HW either automatically switches to this stack on IRQ, or it is done by the OS (x86 32-bit). This is by default 2048 bytes.

We also reserve some ram for the idle thread's stack. The idle thread does very little when it is woken up, it locks interrupts and calls sys_power_save_idle(), which either calls deeper into the power management code (with interrupts still locked) or k_cpu_idle() which unlocks IRQs and issues a power saving instruction. This is currently by default 256 bytes on most platforms.

I need to take some direct measurements, but it appears to me that in all the cases where the idle thread is running without interrupts locked, the stack depth is small.

In the course of working on #26486 I am testing a solution where self-aborting thread cleanup tasks are deferred to the idle thread, like FreeRTOS does. This fixes a number of race conditions / chicken-and-the-egg problems, but requires more stack space. Fixing this problem by requiring an across-the-board increase in idle thread stack sizes may be unpalatable to most of our users. However...these abort tasks also run largely with interrupts disabled (the scheduler spinlock is held).

If it indeed holds that all the complicated stack-hungry stuff that the idle thread does happens with interrupts disabled, then we could save a bunch of memory by dual purposing the interrupt stack as also the stack for the idle thread.

The fact that irq-unlocked idle stack usage is very low is important, because if we do this the ISR stack size must be sized to account for worst-case usage by both the idle thread and system interrupts.

Describe the solution you'd like
The interrupt stack is used as the stack for the idle thread. We have to know what the upper bound is on stack usage for the idle thread at any time when an interrupt may happen, it must be pretty low to allow it to be dual purposed like this.

This will require potentially quite hairy changes to the interrupt handling code for every supported architecture, to handle the interrupt at a location on the stack past wherever the idle thread's context is.

On certain systems like x86_64 this may not even be possible due to how it handles nesting on completely separate stacks, but we don't care much about RAM usage on x86_64.

This would likely be a Kconfig option as we enable this on more and more architectures; if not enabled, allocate and use an idle thread stack like we currently do.

Testing will require some method of measuring the max call stack depth of the idle thread with interrupts enabled.

Describe alternatives you've considered
None so far.

@andrewboie andrewboie added Enhancement Changes/Updates/Additions to existing features area: Kernel area: Power Management labels Aug 14, 2020
@andyross
Copy link
Contributor

It's worth mentioning that as an optimization, there's no strict need that the Zephyr idle thread be restored back to its interrupted state. At the arch layer, when you reach the point where you enter hardware idle, it would be reasonable to allow it to optionally do a "NORETURN" kind of thing. Then the next point where we need to idle, we just restore the idle thread as if it was being launched from scratch. This has some overhead, but because it's by definition happening during idle time, we don't really care.

The upshot is that you can clobber the "suspended" interrupt/idle stack in the interrupt and lose no bytes at all. Not all suspend implementations will be able to handle this (e.g. they might be library/HAL code that has its own local state we can't control), but I bet most of them would be OK.

@ceolin
Copy link
Member

ceolin commented Oct 27, 2021

It's worth mentioning that as an optimization, there's no strict need that the Zephyr idle thread be restored back to its interrupted state. At the arch layer, when you reach the point where you enter hardware idle, it would be reasonable to allow it to optionally do a "NORETURN" kind of thing. Then the next point where we need to idle, we just restore the idle thread as if it was being launched from scratch. This has some overhead, but because it's by definition happening during idle time, we don't really care.

The upshot is that you can clobber the "suspended" interrupt/idle stack in the interrupt and lose no bytes at all. Not all suspend implementations will be able to handle this (e.g. they might be library/HAL code that has its own local state we can't control), but I bet most of them would be OK.

This is not longer true AFAIU, the idle thead needs to handle devices, among other things, after CPU wakes up. Am I missing something ?

@ceolin
Copy link
Member

ceolin commented Jun 18, 2022

I am closing this because it is evaluation is completely outdated. The PM subsystem now does more things that used to do, some of these things can be overwritten by the application which makes this re-use of the IRQ stack even more complex. Some items to mentions:

  • Send notifications about states transitions (done through provided callbacks)
  • Calls the policy asking which power state should be used
  • Do device power management (calling devices specific callback)

@ceolin ceolin closed this as completed Jun 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Kernel area: Power Management Enhancement Changes/Updates/Additions to existing features
Projects
None yet
Development

No branches or pull requests

3 participants