This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit 93a431d19b150bffb0cdc79df6096526dbd789cc Author: buxiasen <[email protected]> AuthorDate: Mon Aug 5 22:36:24 2024 +0800 testing/drivertest: add pm_smp test Signed-off-by: buxiasen <[email protected]> --- testing/drivertest/CMakeLists.txt | 16 ++ testing/drivertest/Makefile | 6 +- testing/drivertest/drivertest_pm.c | 2 +- testing/drivertest/drivertest_pm_smp.c | 490 +++++++++++++++++++++++++++++++++ 4 files changed, 512 insertions(+), 2 deletions(-) diff --git a/testing/drivertest/CMakeLists.txt b/testing/drivertest/CMakeLists.txt index 8bae9ce21..73ef14c03 100644 --- a/testing/drivertest/CMakeLists.txt +++ b/testing/drivertest/CMakeLists.txt @@ -389,6 +389,22 @@ if(CONFIG_TESTING_DRIVER_TEST) drivertest_pm.c) endif() + if(CONFIG_PM AND CONFIG_SMP) + nuttx_add_application( + NAME + cmocka_driver_pm_smp + PRIORITY + ${CONFIG_TESTING_DRIVER_TEST_PRIORITY} + STACKSIZE + ${CONFIG_TESTING_DRIVER_TEST_STACKSIZE} + MODULE + ${CONFIG_TESTING_DRIVER_TEST} + DEPENDS + cmocka + SRCS + drivertest_pm_smp.c) + endif() + if(CONFIG_PM_RUNTIME) nuttx_add_application( NAME diff --git a/testing/drivertest/Makefile b/testing/drivertest/Makefile index aae6992df..5bd0744af 100644 --- a/testing/drivertest/Makefile +++ b/testing/drivertest/Makefile @@ -140,9 +140,13 @@ PROGNAME += cmocka_driver_mps2_zerointerrupt endif endif -ifneq ($(CONFIG_PM),) +ifeq ($(CONFIG_PM),y) MAINSRC += drivertest_pm.c PROGNAME += cmocka_driver_pm +ifeq ($(CONFIG_SMP),y) +MAINSRC += drivertest_pm_smp.c +PROGNAME += cmocka_driver_pm_smp +endif endif ifneq ($(CONFIG_PM_RUNTIME),) diff --git a/testing/drivertest/drivertest_pm.c b/testing/drivertest/drivertest_pm.c index b7a3209b2..0abbc3247 100644 --- a/testing/drivertest/drivertest_pm.c +++ b/testing/drivertest/drivertest_pm.c @@ -277,7 +277,7 @@ static void test_pm(FAR void **argv) /* test when pm prepare succeeded */ - test_pm_fake_driver_init(); + g_test_pm_dev.prepare_fail = false; for (int state = 0; state < PM_COUNT; state++) { diff --git a/testing/drivertest/drivertest_pm_smp.c b/testing/drivertest/drivertest_pm_smp.c new file mode 100644 index 000000000..02aeee818 --- /dev/null +++ b/testing/drivertest/drivertest_pm_smp.c @@ -0,0 +1,490 @@ +/**************************************************************************** + * apps/testing/drivertest/drivertest_pm_smp.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/nuttx.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <string.h> +#include <cmocka.h> +#include <nuttx/power/pm.h> +#include <sys/param.h> +#include <unistd.h> +#include <pthread.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TEST_PM_LOOP_COUNT 5 + +#define TEST_STAYTIMEOUT 2 /* in ms */ + +#define TEST_SNAP_TICK_MAX 3 + +#define TEST_TIMEOUT_ENABLED 1 + +#define ASSERT_EQUAL_SUBTHREAD(actual, expected) \ + { \ + if ((actual) != (expected)) \ + { \ + ret = __LINE__; \ + goto error_out; \ + } \ + } + +/**************************************************************************** + * Private Functions Prototypes + ****************************************************************************/ + +static int test_pm_callback_prepare(FAR struct pm_callback_s *cb, + int domain, + enum pm_state_e pmstate); +static void test_pm_callback_notify(FAR struct pm_callback_s *cb, + int domain, + enum pm_state_e pmstate); + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct test_pm_entry_s +{ + pid_t tid; + int cpu; + int domain; + cpu_set_t cpuset; + bool fail_request; + enum pm_state_e state; + struct pm_callback_s cb; + struct pm_wakelock_s wakelock[PM_COUNT]; +}; + +struct test_pm_s +{ + struct test_pm_entry_s entry[CONFIG_SMP_NCPUS]; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char *test_pm_name[PM_COUNT] = +{ + "normal", + "idle", + "standby", + "sleep" +}; + +static struct test_pm_s g_test_pm_smp; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int test_pm_fake_driver_init(void) +{ + memset(&g_test_pm_smp, 0, sizeof(g_test_pm_smp)); + for (int i = 0; i < CONFIG_SMP_NCPUS; i++) + { + struct test_pm_entry_s *entry = &g_test_pm_smp.entry[i]; + entry->cpu = i; + CPU_ZERO(&entry->cpuset); + CPU_SET(i, &entry->cpuset); + entry->domain = CONFIG_PM_NDOMAINS - CONFIG_SMP_NCPUS + i; + entry->cb.prepare = test_pm_callback_prepare; + entry->cb.notify = test_pm_callback_notify; + + for (int state = 0; state < PM_COUNT; state++) + { + char name[32]; + snprintf(name, sizeof(name), "cpu%d-%s", i, test_pm_name[state]); + pm_wakelock_init( + &entry->wakelock[state], name, entry->domain, state); + } + } + + return 0; +} + +static int test_pm_callback_prepare(FAR struct pm_callback_s *cb, + int domain, + enum pm_state_e pmstate) +{ + struct test_pm_entry_s *entry; + entry = container_of(cb, struct test_pm_entry_s, cb); + if (entry->fail_request) + { + return -EBUSY; + } + + return 0; +} + +static void test_pm_callback_notify(FAR struct pm_callback_s *cb, + int domain, + enum pm_state_e pmstate) +{ + struct test_pm_entry_s *entry; + entry = container_of(cb, struct test_pm_entry_s, cb); + switch (pmstate) + { + case PM_NORMAL: + case PM_IDLE: + case PM_STANDBY: + case PM_SLEEP: + entry->state = pmstate; + break; + + default: + break; + } + + return; +} + +#if TEST_TIMEOUT_ENABLED + +/* Make sure the Idle can execute */ + +static void test_pm_smp_yield(void) +{ + usleep(USEC_PER_TICK); +} +#endif + +static void test_pm_smp_naps(void) +{ + int r = random() % (TEST_SNAP_TICK_MAX * USEC_PER_TICK); + usleep(r + USEC_PER_TICK); +} + +static void *test_pm_smp_thread_entry(void *arg) +{ + struct test_pm_entry_s *entry = (struct test_pm_entry_s *)arg; + + int persist_stay_cnt[PM_COUNT]; + int init_delay; + int staycount; + int target; + int cnt = TEST_PM_LOOP_COUNT; + int domain = entry->domain; + int ret = 0; + + init_delay = MAX(CONFIG_PM_GOVERNOR_EXPLICIT_RELAX, + CONFIG_SERIAL_PM_ACTIVITY_PRIORITY); + + usleep(init_delay * 1000000); + test_pm_smp_naps(); + + for (int i = 0; i < PM_COUNT; i++) + { + persist_stay_cnt[i] = pm_staycount(domain, i); + } + + while (cnt--) + { + ret = pm_domain_register(domain, &entry->cb); + ASSERT_EQUAL_SUBTHREAD(ret, 0); + + /* test when pm prepare failed */ + + entry->fail_request = true; + for (int state = 0; state < PM_COUNT; state++) + { + target = persist_stay_cnt[state] + 0; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + + pm_stay(domain, state); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + target = persist_stay_cnt[state] + 1; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + +#if TEST_TIMEOUT_ENABLED + pm_staytimeout(domain, state, TEST_STAYTIMEOUT); + test_pm_smp_yield(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + target = persist_stay_cnt[state] + 2; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + usleep(TEST_STAYTIMEOUT * 1000); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + target = persist_stay_cnt[state] + 1; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); +#endif + + pm_relax(domain, state); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + target = persist_stay_cnt[state] + 0; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + +#if TEST_TIMEOUT_ENABLED + pm_staytimeout(domain, state, TEST_STAYTIMEOUT); + test_pm_smp_yield(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + target = persist_stay_cnt[state] + 1; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + usleep(TEST_STAYTIMEOUT * 1000); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + target = persist_stay_cnt[state] + 0; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); +#endif + } + + for (int state = 0; state < PM_COUNT; state++) + { + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 0); + + pm_wakelock_stay(&entry->wakelock[state]); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 1); + +#if TEST_TIMEOUT_ENABLED + pm_wakelock_staytimeout(&entry->wakelock[state], TEST_STAYTIMEOUT); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 2); + usleep(TEST_STAYTIMEOUT * 1000); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 1); +#endif + + pm_wakelock_relax(&entry->wakelock[state]); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 0); + +#if TEST_TIMEOUT_ENABLED + pm_wakelock_staytimeout(&entry->wakelock[state], TEST_STAYTIMEOUT); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 1); + usleep(TEST_STAYTIMEOUT * 1000); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 0); +#endif + } + + /* test when pm prepare succeeded */ + + entry->fail_request = false; + + for (int state = 0; state < PM_COUNT; state++) + { + target = persist_stay_cnt[state] + 0; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + + pm_stay(domain, state); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, state); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), state); + target = persist_stay_cnt[state] + 1; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + +#if TEST_TIMEOUT_ENABLED + pm_staytimeout(domain, state, TEST_STAYTIMEOUT); + test_pm_smp_yield(); + ASSERT_EQUAL_SUBTHREAD(entry->state, state); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), state); + target = persist_stay_cnt[state] + 2; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + usleep(TEST_STAYTIMEOUT * 1000); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, state); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), state); + target = persist_stay_cnt[state] + 1; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); +#endif + + pm_relax(domain, state); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + target = persist_stay_cnt[state] + 0; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + +#if TEST_TIMEOUT_ENABLED + pm_staytimeout(domain, state, TEST_STAYTIMEOUT); + test_pm_smp_yield(); + ASSERT_EQUAL_SUBTHREAD(entry->state, state); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), state); + target = persist_stay_cnt[state] + 1; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); + usleep(TEST_STAYTIMEOUT * 1000); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + target = persist_stay_cnt[state] + 0; + ASSERT_EQUAL_SUBTHREAD(pm_staycount(domain, state), target); +#endif + } + + for (int state = 0; state < PM_COUNT; state++) + { + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 0); + + pm_wakelock_stay(&entry->wakelock[state]); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, state); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), state); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 1); + +#if TEST_TIMEOUT_ENABLED + pm_wakelock_staytimeout(&entry->wakelock[state], TEST_STAYTIMEOUT); + test_pm_smp_yield(); + ASSERT_EQUAL_SUBTHREAD(entry->state, state); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), state); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 2); + usleep(TEST_STAYTIMEOUT * 1000); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, state); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), state); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 1); +#endif + + pm_wakelock_relax(&entry->wakelock[state]); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 0); + +#if TEST_TIMEOUT_ENABLED + pm_wakelock_staytimeout(&entry->wakelock[state], TEST_STAYTIMEOUT); + test_pm_smp_yield(); + ASSERT_EQUAL_SUBTHREAD(entry->state, state); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), state); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 1); + usleep(TEST_STAYTIMEOUT * 1000); + test_pm_smp_naps(); + ASSERT_EQUAL_SUBTHREAD(entry->state, PM_SLEEP); + ASSERT_EQUAL_SUBTHREAD(pm_querystate(domain), PM_SLEEP); + staycount = pm_wakelock_staycount(&entry->wakelock[state]); + ASSERT_EQUAL_SUBTHREAD(staycount, 0); +#endif + } + + ret = pm_domain_unregister(domain, &entry->cb); + ASSERT_EQUAL_SUBTHREAD(ret, 0); + } + +error_out: + return (void *)(uintptr_t)ret; +} + +static void test_pm_smp(FAR void **argv) +{ + for (int i = 0; i < CONFIG_SMP_NCPUS; i++) + { + struct test_pm_entry_s *entry = &g_test_pm_smp.entry[i]; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &entry->cpuset); + attr.priority = CONFIG_SCHED_HPWORKPRIORITY + 1; + pthread_create(&entry->tid, &attr, test_pm_smp_thread_entry, entry); + } + + for (int i = 0; i < CONFIG_SMP_NCPUS; i++) + { + struct test_pm_entry_s *entry = &g_test_pm_smp.entry[i]; + pthread_addr_t pthread_ret; + int ret; + pthread_join(entry->tid, &pthread_ret); + ret = (uintptr_t)pthread_ret; + assert_int_equal(ret, 0); + } +} + +static int setup(FAR void **argv) +{ + int ret = 0; + + if (CONFIG_PM_GOVERNOR_EXPLICIT_RELAX < 0) + { + return -1; + } + + ret = test_pm_fake_driver_init(); + if (ret < 0) + { + return ret; + } + + return ret; +} + +static int teardown(FAR void **argv) +{ + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + const struct CMUnitTest tests[] = + { + cmocka_unit_test_prestate_setup_teardown(test_pm_smp, setup, + teardown, NULL), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +}
