Skip to content

Commit be669d8

Browse files
committed
[nrf fromlist] pm: device_runtime: Fix managing the device from multiple contexts
Add early exist to the suspend function if suspending client is not the last one. It fixes the case where user calls put function from the thread context and it gets interrupted by the device interrupt where another asynchronous put is called. In that case, in the interrupt context the call will return error as semaphore cannot be taken and reference counting gets broken. By adding an early exit if context is not the last one, we ensure that only last client executes suspending procedure. Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no> Upstream PR #: 108877
1 parent 26212fb commit be669d8

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

subsys/pm/device_runtime.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ LOG_MODULE_DECLARE(pm_device, CONFIG_PM_DEVICE_LOG_LEVEL);
2121
#define PM_DOMAIN(_pm) NULL
2222
#endif
2323

24+
static struct k_spinlock lock;
2425
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
2526
#ifdef CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ
2627
K_THREAD_STACK_DEFINE(pm_device_runtime_stack, CONFIG_PM_DEVICE_RUNTIME_DEDICATED_WQ_STACK_SIZE);
@@ -57,6 +58,7 @@ static int runtime_suspend(const struct device *dev, bool async,
5758
{
5859
int ret = 0;
5960
struct pm_device *pm = dev->pm;
61+
bool early_exit = false;
6062

6163
/*
6264
* Early return if device runtime is not enabled.
@@ -65,6 +67,18 @@ static int runtime_suspend(const struct device *dev, bool async,
6567
return 0;
6668
}
6769

70+
K_SPINLOCK(&lock) {
71+
/* If we are not the last user, return. */
72+
if (pm->base.usage > 1U) {
73+
pm->base.usage--;
74+
early_exit = true;
75+
}
76+
}
77+
78+
if (early_exit == true) {
79+
return 0;
80+
}
81+
6882
if (k_is_pre_kernel()) {
6983
async = false;
7084
} else {
@@ -219,6 +233,20 @@ int pm_device_runtime_get(const struct device *dev)
219233
}
220234

221235
if (!k_is_pre_kernel()) {
236+
bool early_exit = false;
237+
238+
K_SPINLOCK(&lock) {
239+
/* If we are not the first user and device is active, return. */
240+
if ((pm->base.usage > 0U) && (pm->base.state == PM_DEVICE_STATE_ACTIVE)) {
241+
pm->base.usage++;
242+
early_exit = true;
243+
}
244+
}
245+
246+
if (early_exit == true) {
247+
return 0;
248+
}
249+
222250
ret = k_sem_take(&pm->lock, k_is_in_isr() ? K_NO_WAIT : K_FOREVER);
223251
if (ret < 0) {
224252
return -EWOULDBLOCK;

0 commit comments

Comments
 (0)