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

pm: device: Several fixes #34781

Merged
merged 10 commits into from
May 7, 2021
Merged

Conversation

ceolin
Copy link
Member

@ceolin ceolin commented May 3, 2021

  • Don't wake up devices unnecessarily
  • Fix concurrence issues with k_poll_signal. Using k_condvar instead.
  • Fix pre-kernel API usage
  • Some code guideline fixes
  • Use spin lock instead of semaphore to protect critical sessions, since it was the semaphore with K_FOREVER which is not allowed from ISR.
  • Change API signature for clarity sake

@@ -60,26 +60,63 @@ static device_idx_t num_pm;
/* Number of devices successfully suspended. */
static device_idx_t num_susp;

static bool should_suspend(const struct device *dev, uint32_t state)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are there tests for this behaviour?

Copy link
Member Author

Choose a reason for hiding this comment

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

yep:

./samples/drivers/spi_flash_at45/prj.conf:CONFIG_PM_DEVICE=y
./samples/subsys/power/device_pm/prj.conf:CONFIG_PM_DEVICE=y
./samples/boards/ti/cc13x2_cc26x2/system_off/prj.conf:CONFIG_PM_DEVICE=y
./samples/boards/nrf/system_off/prj.conf:CONFIG_PM_DEVICE=y
./samples/boards/stm32/power_mgmt/blinky/prj.conf:CONFIG_PM_DEVICE=y
./tests/net/pm/prj.conf:CONFIG_PM_DEVICE=y
./tests/subsys/power/power_mgmt/prj.conf:CONFIG_PM_DEVICE=y

@@ -409,14 +409,14 @@ static void st7789v_enter_sleep(struct st7789v_data *data)
}

static int st7789v_pm_control(const struct device *dev, uint32_t ctrl_command,
void *context, device_pm_cb cb, void *arg)
uint32_t *state, device_pm_cb cb, void *arg)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What if we need to add something more in future...should this be a struct with an int inside?

Copy link
Member Author

Choose a reason for hiding this comment

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

the last parameter is supposed to be used as context to the callback. This variable that was being called void *context was being treated as an uint32_t * by the device_pm framework (and everywhere else), so it is not like you could just change it to be a different type in your use case. If you need to pass a different data you should use *arg.

The reason to change it is, as you can see, very confuse and with bad variable names this function signature.

Copy link
Contributor

Choose a reason for hiding this comment

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

Still unclear why it wasn't made into struct pm_context { uint32_t state }; to allow for seamless extensibility going forward, but ok...

k_poll_signal_raise(&dev->pm->signal, pm_state);
/*
* This function returns the number of woken threads on success. There
* is nothing we can do with this information. Just ignoring it.
Copy link
Collaborator

Choose a reason for hiding this comment

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

ignore


return result == target_state ? 0 : -EIO;
return target_state == atomic_get(&dev->pm->fsm_state) ? 0 : -EIO;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This code is a bit hard to understand. How does it actually end up being in the target state? How does it know what to do, since target_state is not stored anywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

you request to device to change to target_state, as this operation may be asynchronous the device is responsible to call the given callback with its state after this operation. The state given by the device is then stored in dev->pm->fsm_state we then compare here to check if the state was properly set.

@ceolin
Copy link
Member Author

ceolin commented May 4, 2021

@sjg20 is not clear for me what changes were requested, there is anything other than the typo that needs to be changed after my answers ?

Copy link
Collaborator

@sjg20 sjg20 left a comment

Choose a reason for hiding this comment

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

Looks good. I do think a comment on the operation (like you provided in the response) would be useful

@ceolin
Copy link
Member Author

ceolin commented May 4, 2021

Looks good. I do think a comment on the operation (like you provided in the response) would be useful

will add it, thanks for the feedback

@ceolin ceolin force-pushed the pm/device-split branch from 2a14e11 to d5bd2ab Compare May 4, 2021 17:21
@nashif nashif assigned nashif and unassigned ioannisg May 4, 2021
@pfalcon pfalcon removed their request for review May 5, 2021 14:40
@galak galak added this to the v2.6.0 milestone May 5, 2021
@ceolin ceolin force-pushed the pm/device-split branch from d5bd2ab to af42965 Compare May 6, 2021 21:04
@ceolin ceolin requested review from carlescufi and galak as code owners May 6, 2021 21:04
Flavio Ceolin added 9 commits May 6, 2021 15:42
Currently when the system goes to sleep it asks *all* devices that
support PM to suspend or go to a low power state, and the system wakes
up it put *all* suspended devices in active state, even if a device
was already suspended and not being used.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
The context parameter used across device power management is
actually the power state. Just use it and avoid a lot of
unnecessary casts.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
Don't use kernel services before in pre-kernel state.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
BIT macro properly handle bit shift operations.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
The sync API was using k_poll_signal and in certain conditions is
possible multiple threads waiting on a signal leading to an undefined
behavior.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
Add an explicit fallthrough in a case label.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
Device pm runtime was using semaphore to protect critical section but
enable / disable functions were waiting on the semaphore. So, just
replace it with a spin lock.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
Check if a device is busy before suspend it.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
Rename state variable.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
@ceolin ceolin force-pushed the pm/device-split branch from af42965 to d5f9832 Compare May 6, 2021 23:08
@@ -111,6 +111,16 @@ static int pm_device_request(const struct device *dev,
}
}

if (k_is_pre_kernel()) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not convinced this is the correct way.
This means clock activation should be done on device side, on top of calling pm_device_get primitive (which at least, should be documented).
Instead, isn't it a way to run the worker routine code in a synchronous way at this stage ?

Copy link
Member

Choose a reason for hiding this comment

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

What about adding the following just here ?

		if (dev->pm->usage == 1) {
			(void)device_set_power_state(dev,
						     DEVICE_PM_ACTIVE_STATE,
						     NULL, NULL);
		} else if (dev->pm->usage == 0) {
			(void)device_set_power_state(dev,
						     DEVICE_PM_SUSPEND_STATE,
						     NULL, NULL);
		}

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 was staring at the same code as well, this does not seem to be correct. Here we need to enable power directly and without having to worry about concurrency, along the lines of what @erwango is proposing.

Copy link
Member Author

Choose a reason for hiding this comment

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

This exactly code was part of my other pr
64dac71

The reason for what I have not included it in this one is because this is a workaround from my understanding. At this point devices should have being initialized and powered on accordingly and this activation unnecessary. This is fixing an existing problem not in the proper way imho.

But as we don't have it fixed, I can cherry-pick this commit and once we have it done properly I remove this path.

Copy link
Member Author

@ceolin ceolin May 7, 2021

Choose a reason for hiding this comment

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

Btw, this is not about concurrence, but putting the device in an unexpected condition. From the device perspective is expected the init function be called before the power control function. The device may do some data initialization in the init that will be used in the power control ....

Copy link
Member

Choose a reason for hiding this comment

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

At this point devices should have being initialized and powered on accordingly and this activation unnecessary.

No, this is not the case. you need to be able to use those drivers and use them means powering them up if they are not active. The issue was originally exactly that, we are trying to power on a device and cant be cause kernel is still not up, i.e. we try to deal with code designed for multithreds and concurrency that in this stage can be skipped.

Copy link
Member Author

Choose a reason for hiding this comment

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

yep, but not in pre-kernel. Honestly I don't see why devices are not initialized in the right order at this point. Anyway, I've cherry-picked that commit

Copy link
Member

Choose a reason for hiding this comment

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

what does initialized mean exactly? init() AFAIK does not power up a device necessarily..

Copy link
Member Author

Choose a reason for hiding this comment

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

call init()

@@ -111,6 +111,16 @@ static int pm_device_request(const struct device *dev,
}
}

if (k_is_pre_kernel()) {
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 was staring at the same code as well, this does not seem to be correct. Here we need to enable power directly and without having to worry about concurrency, along the lines of what @erwango is proposing.

Devices may be initialized but started powered down for this reason
is necessary to power a device on if requested even if in pre-kernel
state.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
@nashif nashif merged commit e89d9d6 into zephyrproject-rtos:master May 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants