Skip to content

Commit 05597d7

Browse files
ppryga-nordicnordicjm
authored andcommitted
mpsl: pm: Fix MPSL Pl uninit -NRF_ETIMEDOUT
There was a lock if a MPSL PM was uninitialized by mpsl_lib_uninit() from hci_driver.c hci_driver_close(). The hci_driver_close() locks multi-threading by call MULTITHREADING_LOCK_ACQUIRE(). The same mutex is acquired by MPSL mpsl_low_prio_work_handler. The MPSL PM used an uninit work item that was scheduled to MPSL low prio work queue. The work was never executed during the MPSL PM uninit, even though the uninit was waiting on a semaphore. That was a lock that caused -NRF_ETIMEDOUT error to be returned. The mpsl_pm_uninit function can't be called without earlier MULTITHREADING_LOCK_ACQUIRE() call. That is a prerequisite to be fulfilled by a user. On the other hand, the use of the lock allows to stop using a work item during uninitialization of the MPSL PM. The is not needed because if there is any work item in the queue, then it will be handled after uninit is done. It will have no effect on PM subsystem. These items will be just removed from the MPSL work queue. Signed-off-by: Piotr Pryga <[email protected]>
1 parent 129d7fc commit 05597d7

File tree

6 files changed

+13
-196
lines changed

6 files changed

+13
-196
lines changed

subsys/mpsl/pm/Kconfig

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@ config MPSL_USE_ZEPHYR_PM
1515

1616
if MPSL_USE_ZEPHYR_PM
1717

18-
config MPSL_PM_UNINIT_WORK_WAIT_TIME_MS
19-
int "Timeout value the MPSL PM uninit procedure will wait for work queue to complete, in milliseconds"
20-
default 100
21-
help
22-
The option specifies a timeout value in milliseconds, the MPLS PM uninit procedure
23-
will wait for MPSL work queue to complete handling of scheduled uninit work item.
24-
2518
config MPSL_PM_NO_RADIO_EVENT_PERIOD_LATENCY_US
2619
int "Latency value the MPSL PM allows in periods outside of radio events, in microseconds"
2720
default 499999

subsys/mpsl/pm/mpsl_pm_utils.c

Lines changed: 7 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,9 @@
2020
LOG_MODULE_REGISTER(mpsl_pm_utils, CONFIG_MPSL_LOG_LEVEL);
2121

2222
#define NO_RADIO_EVENT_PERIOD_LATENCY_US CONFIG_MPSL_PM_NO_RADIO_EVENT_PERIOD_LATENCY_US
23-
#define UNINIT_WORK_WAIT_TIMEOUT_MS K_MSEC(CONFIG_MPSL_PM_UNINIT_WORK_WAIT_TIME_MS)
24-
25-
/* All MPSL PM event and latency actions are triggered inside the library.
26-
* The uninitialization may be started outside of the library thgough public API.
27-
* To avoid interference with other MPSL work items we need dedicated work
28-
* item for uninitialization purpose.
29-
*/
30-
static void m_pm_uninit_work_handler(struct k_work *work);
31-
static K_WORK_DELAYABLE_DEFINE(m_pm_uninit_work, m_pm_uninit_work_handler);
3223

3324
enum MPLS_PM_STATE {
3425
MPSL_PM_UNINITIALIZED,
35-
MPSL_PM_UNINITIALIZING,
3626
MPSL_PM_INITIALIZED
3727
};
3828

@@ -43,7 +33,6 @@ static struct pm_policy_latency_request m_latency_req;
4333
static struct pm_policy_event m_evt;
4434

4535
static atomic_t m_pm_state = (atomic_val_t)MPSL_PM_UNINITIALIZED;
46-
struct k_sem m_uninit_wait_sem;
4736

4837
#if defined(CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE)
4938
#define LOW_LATENCY_ATOMIC_BITS_NUM 2
@@ -214,36 +203,13 @@ static void m_mram_low_latency_release(void)
214203
}
215204
#endif /* CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE */
216205

217-
static void m_pm_uninit_work_handler(struct k_work *work)
218-
{
219-
ARG_UNUSED(work);
220-
221-
mpsl_pm_utils_work_handler();
222-
}
223-
224206
void mpsl_pm_utils_work_handler(void)
225207
{
226208
enum MPLS_PM_STATE pm_state = (enum MPLS_PM_STATE)atomic_get(&m_pm_state);
227209

228210
if (pm_state == MPSL_PM_INITIALIZED) {
229211
m_register_event();
230212
m_register_latency();
231-
} else if (pm_state == MPSL_PM_UNINITIALIZING) {
232-
233-
/* The uninitialization is handled by all MPSL work items as well as by dedicated
234-
* uninit work item. In case regular MPSL work item cleans MPSL PM the uninit
235-
* work queue will not do anything.
236-
*/
237-
pm_policy_latency_request_remove(&m_latency_req);
238-
pm_policy_event_unregister(&m_evt);
239-
240-
/* The MPSL PM status is updated here to make the code unit testable.
241-
* There is no work queue when running UTs, so the mpsl_pm_utils_work_handler()
242-
* is manualy executed after mpsl_pm_utils_uninit() returns.
243-
*/
244-
atomic_set(&m_pm_state, (atomic_val_t)MPSL_PM_UNINITIALIZED);
245-
246-
k_sem_give(&m_uninit_wait_sem);
247213
}
248214
}
249215

@@ -271,31 +237,20 @@ int32_t mpsl_pm_utils_init(void)
271237

272238
int32_t mpsl_pm_utils_uninit(void)
273239
{
274-
int err;
275-
276240
if (atomic_get(&m_pm_state) != (atomic_val_t)MPSL_PM_INITIALIZED) {
277241
return -NRF_EPERM;
278242
}
279243

280244
mpsl_pm_uninit();
281-
atomic_set(&m_pm_state, (atomic_val_t)MPSL_PM_UNINITIALIZING);
282-
/* In case there is any pended MPSL work item that was not handled, a dedicated
283-
* work item is used to remove PM policy event and unregister latency request.
284-
*/
285-
(void)k_sem_init(&m_uninit_wait_sem, 0, 1);
286-
287-
mpsl_work_reschedule(&m_pm_uninit_work, K_NO_WAIT);
288245

289-
/* Wait for completion of the uninit work item to make sure user can re-initialize
290-
* MPSL PM after return.
246+
/* The mpsl_pm_utils_uninit() must be called with MULTITHREADING_LOCK_ACQUIRE to make sure
247+
* there is no mpsl_low_prio_work running. That could lead to a race condition.
248+
* Call to MULTITHREADING_LOCK_ACQUIRE() is responsibility of the function caller.
291249
*/
292-
err = k_sem_take(&m_uninit_wait_sem, UNINIT_WORK_WAIT_TIMEOUT_MS);
293-
if (err == -EAGAIN) {
294-
return -NRF_ETIMEDOUT;
295-
} else if (err != 0) {
296-
__ASSERT(false, "MPSL PM uninit failed to complete: %d", err);
297-
return -NRF_EFAULT;
298-
}
250+
pm_policy_latency_request_remove(&m_latency_req);
251+
pm_policy_event_unregister(&m_evt);
252+
253+
atomic_set(&m_pm_state, (atomic_val_t)MPSL_PM_UNINITIALIZED);
299254

300255
return 0;
301256
}

subsys/mpsl/pm/mpsl_pm_utils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ int32_t mpsl_pm_utils_init(void);
2424
*
2525
* This routine uninitializes MPSL PM (via `mpsl_pm_uninit`).
2626
*
27+
* @pre The function requires the multithreading lock (@see MULTITHREADING_LOCK_ACQUIRE())
28+
* to be acquired before.
29+
*
2730
* @retval 0 MPSL PM was uninitialized successfully.
2831
* @retval -NRF_EPERM MPSL was not initialized before the call.
2932
* @retval -NRF_ETIMEDOUT MPSL PM uninitialization timed out while waiting for completion.

tests/subsys/mpsl/pm/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ project(pm_test)
1313
test_runner_generate(pm_test.c)
1414

1515
# Create mocks for pm module.
16-
cmock_handle(${CMAKE_CURRENT_SOURCE_DIR}/kernel_minimal_mock.h)
1716
cmock_handle(${ZEPHYR_BASE}/include/zephyr/pm/policy.h)
1817
cmock_handle(${ZEPHYR_NRFXLIB_MODULE_DIR}/mpsl/include/mpsl_pm.h)
1918
cmock_handle(${ZEPHYR_NRFXLIB_MODULE_DIR}/mpsl/include/mpsl_pm_config.h)
@@ -39,6 +38,5 @@ set_property(SOURCE ${ZEPHYR_NRF_MODULE_DIR}/subsys/mpsl/pm/mpsl_pm_utils.c
3938
target_compile_options(app PRIVATE
4039
-DCONFIG_PM=y
4140
-DCONFIG_MPSL_USE_ZEPHYR_PM=y
42-
-DCONFIG_MPSL_PM_UNINIT_WORK_WAIT_TIME_MS=100
4341
-DCONFIG_MPSL_PM_NO_RADIO_EVENT_PERIOD_LATENCY_US=499999
4442
)

tests/subsys/mpsl/pm/kernel_minimal_mock.h

Lines changed: 0 additions & 38 deletions
This file was deleted.

tests/subsys/mpsl/pm/pm_test.c

Lines changed: 3 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include <zephyr/kernel.h>
1212

1313
#include "cmock_policy.h"
14-
#include "cmock_kernel_minimal_mock.h"
1514
#include "cmock_mpsl_pm.h"
1615
#include "cmock_mpsl_pm_config.h"
1716
#include "cmock_mpsl_work.h"
@@ -20,7 +19,6 @@
2019
#include "nrf_errno.h"
2120

2221
#define NO_RADIO_EVENT_PERIOD_LATENCY_US CONFIG_MPSL_PM_NO_RADIO_EVENT_PERIOD_LATENCY_US
23-
#define UNINIT_WORK_WAIT_TIMEOUT_MS K_MSEC(CONFIG_MPSL_PM_UNINIT_WORK_WAIT_TIME_MS)
2422
/* The unity_main is not declared in any header file. It is only defined in the generated test
2523
* runner because of ncs' unity configuration. It is therefore declared here to avoid a compiler
2624
* warning.
@@ -55,52 +53,17 @@ typedef struct {
5553
mpsl_pm_low_latency_state_t low_latency_state_next;
5654
} test_vector_latency_t;
5755

58-
/* Init and uninit tests */
59-
void start_uninit_module(void)
56+
/* Manual teardown tested module, there is no way to overload it for given test case. */
57+
void uninit_tested_module(void)
6058
{
61-
__cmock_mpsl_work_reschedule_Expect(0, (k_timeout_t){0});
62-
__cmock_mpsl_work_reschedule_IgnoreArg_dwork();
6359
__cmock_mpsl_pm_uninit_Expect();
6460

65-
__cmock_z_impl_k_sem_init_ExpectAndReturn(NULL, 0, 1, 0);
66-
__cmock_z_impl_k_sem_init_IgnoreArg_sem();
67-
68-
/* The k_sem_take will return immediately because there is no real work queue in the test.
69-
* To make the proper uninitialization we must call mpsl_pm_utils_work_handler() after that.
70-
*
71-
* NOTE: There is an assumption the remaining part of unitialization is done by
72-
* mpsl_pm_utils_work_handler(). The mpsl_pm_utils_uninit() just waits for it to end.
73-
*/
74-
__cmock_z_impl_k_sem_take_ExpectAndReturn(NULL, UNINIT_WORK_WAIT_TIMEOUT_MS, 0);
75-
__cmock_z_impl_k_sem_take_IgnoreArg_sem();
76-
77-
TEST_ASSERT_EQUAL(0, mpsl_pm_utils_uninit());
78-
}
79-
80-
void commit_uninit_module(void)
81-
{
8261
__cmock_pm_policy_latency_request_remove_Expect(0);
8362
__cmock_pm_policy_latency_request_remove_IgnoreArg_req();
8463
__cmock_pm_policy_event_unregister_Expect(0);
8564
__cmock_pm_policy_event_unregister_IgnoreArg_evt();
8665

87-
/* There is no real work queue so the mpsl_pm_utils_uninit() is not waiting for this
88-
* semaphore to be signaled. We must call this function to make unintialization to complete.
89-
*
90-
* NOTE: There is an assumption the remaining part of unitialization is done by
91-
* mpsl_pm_utils_work_handler(). The mpsl_pm_utils_uninit() just waits for it to end.
92-
*/
93-
__cmock_z_impl_k_sem_give_Expect(NULL);
94-
__cmock_z_impl_k_sem_give_IgnoreArg_sem();
95-
96-
mpsl_pm_utils_work_handler();
97-
}
98-
99-
/* Manual teardown tested module, there is no way to overload it for given test case. */
100-
void uninit_tested_module(void)
101-
{
102-
start_uninit_module();
103-
commit_uninit_module();
66+
TEST_ASSERT_EQUAL(0, mpsl_pm_utils_uninit());
10467
}
10568

10669
void init_expect_prepare(void)
@@ -136,68 +99,11 @@ void test_init_when_already_initialized(void)
13699
uninit_tested_module();
137100
}
138101

139-
void test_init_when_uninit_started(void)
140-
{
141-
init_expect_prepare();
142-
143-
TEST_ASSERT_EQUAL(0, mpsl_pm_utils_init());
144-
145-
start_uninit_module();
146-
147-
TEST_ASSERT_EQUAL(-NRF_EPERM, mpsl_pm_utils_init());
148-
149-
commit_uninit_module();
150-
}
151-
152102
void test_uninit_when_not_initialized(void)
153103
{
154104
TEST_ASSERT_EQUAL(-NRF_EPERM, mpsl_pm_utils_uninit());
155105
}
156106

157-
void test_uninit_wait_timeout(void)
158-
{
159-
init_expect_prepare();
160-
161-
TEST_ASSERT_EQUAL(0, mpsl_pm_utils_init());
162-
163-
__cmock_mpsl_work_reschedule_Expect(0, (k_timeout_t){0});
164-
__cmock_mpsl_work_reschedule_IgnoreArg_dwork();
165-
__cmock_mpsl_pm_uninit_Expect();
166-
167-
__cmock_z_impl_k_sem_init_ExpectAndReturn(NULL, 0, 1, 0);
168-
__cmock_z_impl_k_sem_init_IgnoreArg_sem();
169-
170-
__cmock_z_impl_k_sem_take_ExpectAndReturn(NULL, UNINIT_WORK_WAIT_TIMEOUT_MS, -EAGAIN);
171-
__cmock_z_impl_k_sem_take_IgnoreArg_sem();
172-
173-
TEST_ASSERT_EQUAL(-NRF_ETIMEDOUT, mpsl_pm_utils_uninit());
174-
175-
/* Just to cleanup after the test case */
176-
commit_uninit_module();
177-
}
178-
179-
void test_uninit_wait_fatal_err(void)
180-
{
181-
init_expect_prepare();
182-
183-
TEST_ASSERT_EQUAL(0, mpsl_pm_utils_init());
184-
185-
__cmock_mpsl_work_reschedule_Expect(0, (k_timeout_t){0});
186-
__cmock_mpsl_work_reschedule_IgnoreArg_dwork();
187-
__cmock_mpsl_pm_uninit_Expect();
188-
189-
__cmock_z_impl_k_sem_init_ExpectAndReturn(NULL, 0, 1, 0);
190-
__cmock_z_impl_k_sem_init_IgnoreArg_sem();
191-
192-
__cmock_z_impl_k_sem_take_ExpectAndReturn(NULL, UNINIT_WORK_WAIT_TIMEOUT_MS, -EBUSY);
193-
__cmock_z_impl_k_sem_take_IgnoreArg_sem();
194-
195-
TEST_ASSERT_EQUAL(-NRF_EFAULT, mpsl_pm_utils_uninit());
196-
197-
/* Just to cleanup after the test case */
198-
commit_uninit_module();
199-
}
200-
201107
/* Latency and event handling tests */
202108

203109
void run_test(test_vector_event_t *p_test_vectors, int num_test_vctr)

0 commit comments

Comments
 (0)