diff --git a/os/include/tinyara/pm/pm.h b/os/include/tinyara/pm/pm.h index ef7fc75c69..828105a949 100644 --- a/os/include/tinyara/pm/pm.h +++ b/os/include/tinyara/pm/pm.h @@ -588,6 +588,22 @@ int pm_changestate(enum pm_state_e newstate); enum pm_state_e pm_querystate(void); +/**************************************************************************** + * Name: pm_recover + * + * Description: + * This function is called from task_recover() when a task is deleted via + * task_delete() or via pthread_cancel(). It does pm cleanup. + * + * Inputs: + * tcb - The TCB of the terminated task or thread + * + * Return Value: + * None. + * + ****************************************************************************/ +void pm_recover(FAR struct tcb_s *tcb); + #ifdef CONFIG_PM_DVFS /**************************************************************************** * Name: pm_dvfs @@ -656,6 +672,7 @@ int pm_metrics(int milliseconds); #define pm_checkstate() (0) #define pm_changestate(state) (0) #define pm_querystate() (0) +#define pm_recover(tcb) (0) #endif /* CONFIG_PM */ diff --git a/os/kernel/task/task_recover.c b/os/kernel/task/task_recover.c index ada55342ee..256e7668ac 100644 --- a/os/kernel/task/task_recover.c +++ b/os/kernel/task/task_recover.c @@ -59,6 +59,7 @@ #include #include #include +#include #include "semaphore/semaphore.h" #include "wdog/wdog.h" @@ -114,6 +115,8 @@ void task_recover(FAR struct tcb_s *tcb) { + /* This task is being deleted. Do PM cleanup for this thread */ + pm_recover(tcb); /* The task is being deleted. Cancel in pending timeout events. */ wd_recover(tcb); diff --git a/os/pm/Makefile b/os/pm/Makefile index ee2788a6cf..6768964b0c 100644 --- a/os/pm/Makefile +++ b/os/pm/Makefile @@ -24,6 +24,7 @@ CSRCS += pm_changestate.c pm_checkstate.c pm_initialize.c pm_idle.c CSRCS += pm_register.c pm_domain_register.c pm_unregister.c pm_procfs.c CSRCS += pm_suspend.c pm_resume.c pm_timedsuspend.c CSRCS += pm_suspendcount.c +CSRCS += pm_recover.c ifeq ($(CONFIG_PM_TIMEDWAKEUP),y) CSRCS += pm_sleep.c diff --git a/os/pm/pm.h b/os/pm/pm.h index 16c5e6f558..e0458f9a27 100644 --- a/os/pm/pm.h +++ b/os/pm/pm.h @@ -315,7 +315,40 @@ void pm_metrics_update_idle(void); * None * ****************************************************************************/ -void pm_metrics_update_wakehandler(clock_t missing_tick, pm_wakeup_reason_code_t wakeup_src); +void pm_metrics_update_wakehandler(uint32_t missing_tick, pm_wakeup_reason_code_t wakeup_src); + +/**************************************************************************** + * Name: pm_metrics_update_sleep + * + * Description: + * This function is called inside pm_sleep's callback. It counts the frequency of + * thread which wakeup the board. It also checks the minimum amount of time board + * was in sleep because of given thread. + * + * Input parameters: + * pid - the ID of thread + * + * Returned value: + * None + * + ****************************************************************************/ +void pm_metrics_update_sleep(pid_t pid); + +/**************************************************************************** + * Name: pm_metrics_update_recover + * + * Description: + * This function is called inside pm_recover. It resets the wakeup_counts and + * sleep_ticks of given thread for consistent PM Metrics result. + * + * Input parameters: + * pid - the ID of thread + * + * Returned value: + * None + * + ****************************************************************************/ +void pm_metrics_update_recover(pid_t pid); #endif #undef EXTERN diff --git a/os/pm/pm_metrics.c b/os/pm/pm_metrics.c index 8eb3763f3c..bbd26c840f 100644 --- a/os/pm/pm_metrics.c +++ b/os/pm/pm_metrics.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -39,9 +40,19 @@ struct pm_metric_state_s { typedef struct pm_metric_state_s pm_metric_state_t; +struct pm_metric_thread_s { + pm_wakeup_reason_code_t wakeup_src; /* The wakeup source */ + uint32_t wakeup_counts[CONFIG_MAX_TASKS]; /* Wakeup timer counts for each thread */ + uint32_t min_sleep_ticks[CONFIG_MAX_TASKS]; /* Minimum actual sleep time allowed by each thread */ + uint32_t delay; /* Wakeup timer delay (in ticks) */ +}; + +typedef struct pm_metric_thread_s pm_metric_thread_t; + struct pm_metric_s { pm_metric_domain_t domain_metrics; /* The domain metrics */ pm_metric_state_t state_metrics; /* The power management state metrics */ + pm_metric_thread_t thread_metrics; /* The power management thread metrics */ uint32_t board_sleep_ticks; /* The amount of time (in ticks) board was in sleep */ uint32_t wakeup_src_counts[PM_WAKEUP_SRC_COUNT]; /* It counts the frequency of wakeup sources */ uint32_t total_try_ticks; /* Total duration of time pm tries to make board sleep */ @@ -80,6 +91,17 @@ static void pm_print_metrics(double total_time, int n_domains) for (index = 0; index < PM_WAKEUP_SRC_COUNT; index++) { pmdbg(" %14s | %6d \n", wakeup_src_name[index], g_pm_metrics->wakeup_src_counts[index]); } + if (g_pm_metrics->wakeup_src_counts[PM_WAKEUP_HW_TIMER]) { + pmdbg("\n"); + pmdbg("\n"); + pmdbg(" PROCESS NAME | WAKEUP COUNTS | MIN SLEEP TIME \n"); + pmdbg("--------------|---------------|----------------\n"); + for (index = 0; index < CONFIG_MAX_TASKS; index++) { + if (g_pm_metrics->thread_metrics.wakeup_counts[index]) { + pmdbg(" %12s | %13d | %12dms \n", sched_gettcb(index)->name, g_pm_metrics->thread_metrics.wakeup_counts[index], TICK2MSEC(g_pm_metrics->thread_metrics.min_sleep_ticks[index])); + } + } + } pmdbg("\n"); pmdbg("\n"); pmdbg(" BOARD STATE | PM STATE | TIME \n"); @@ -117,7 +139,6 @@ void pm_metrics_update_domain(int domain_id) { if (g_pm_metrics_running) { g_pm_metrics->domain_metrics.stime[domain_id] = clock_systimer(); - g_pm_metrics->domain_metrics.suspend_ticks[domain_id] = 0; } } @@ -230,14 +251,68 @@ void pm_metrics_update_changestate(void) * None * ****************************************************************************/ -void pm_metrics_update_wakehandler(clock_t missing_tick, pm_wakeup_reason_code_t wakeup_src) +void pm_metrics_update_wakehandler(uint32_t missing_tick, pm_wakeup_reason_code_t wakeup_src) { if (g_pm_metrics_running) { + g_pm_metrics->thread_metrics.wakeup_src = wakeup_src; + g_pm_metrics->thread_metrics.delay = missing_tick; g_pm_metrics->wakeup_src_counts[wakeup_src]++; g_pm_metrics->board_sleep_ticks += missing_tick; } } +/**************************************************************************** + * Name: pm_metrics_update_sleep + * + * Description: + * This function is called inside pm_sleep's callback. It counts the frequency of + * thread which wakeup the board. It also checks the minimum amount of time board + * was in sleep because of given thread. + * + * Input parameters: + * pid - the ID of thread + * + * Returned value: + * None + * + ****************************************************************************/ +void pm_metrics_update_sleep(pid_t pid) +{ + int hash_pid; + if (g_pm_metrics_running && (g_pm_metrics->thread_metrics.wakeup_src == PM_WAKEUP_HW_TIMER)) { + hash_pid = PIDHASH(pid); + if (g_pm_metrics->thread_metrics.delay < g_pm_metrics->thread_metrics.min_sleep_ticks[hash_pid]) { + g_pm_metrics->thread_metrics.min_sleep_ticks[hash_pid] = g_pm_metrics->thread_metrics.delay; + } + g_pm_metrics->thread_metrics.wakeup_counts[hash_pid]++; + g_pm_metrics->thread_metrics.wakeup_src = PM_WAKEUP_UNKNOWN; + } +} + +/**************************************************************************** + * Name: pm_metrics_update_recover + * + * Description: + * This function is called inside pm_recover. It resets the wakeup_counts and + * sleep_ticks of given thread for consistent PM Metrics result. + * + * Input parameters: + * pid - the ID of thread + * + * Returned value: + * None + * + ****************************************************************************/ +void pm_metrics_update_recover(pid_t pid) +{ + int hash_pid; + if (g_pm_metrics_running) { + hash_pid = PIDHASH(pid); + g_pm_metrics->thread_metrics.min_sleep_ticks[hash_pid] = UINT32_MAX; + g_pm_metrics->thread_metrics.wakeup_counts[hash_pid] = 0; + } +} + /**************************************************************************** * Name: pm_metrics * @@ -280,16 +355,15 @@ int pm_metrics(int milliseconds) return ERROR; } /* PM Metrics Initialization */ - for (index = 0; index < PM_COUNT; index++) { - g_pm_metrics->state_metrics.state_accum_ticks[index] = 0; - } flags = enter_critical_section(); start_time = clock_systimer(); g_pm_metrics->state_metrics.stime = start_time; for (index = 0; (index < CONFIG_PM_NDOMAINS) && pm_domain_map[index]; index++) { - pm_metrics_update_domain(index); g_pm_metrics->domain_metrics.stime[index] = start_time; } + for (index = 0; index < CONFIG_MAX_TASKS; index++) { + g_pm_metrics->thread_metrics.min_sleep_ticks[index] = UINT32_MAX; + } g_pm_metrics_running = true; leave_critical_section(flags); /* Resume Board Sleep */ @@ -319,10 +393,10 @@ int pm_metrics(int milliseconds) } n_domains = index; g_pm_metrics->state_metrics.state_accum_ticks[g_pmglobals.state] += end_time - g_pm_metrics->state_metrics.stime; - leave_critical_section(flags); /* Show PM Metrics Results */ pm_print_metrics((double)(end_time - start_time), n_domains); /* Free allocated memory */ + leave_critical_section(flags); free(g_pm_metrics); g_pm_metrics = NULL; /* Resume Board Sleep */ diff --git a/os/pm/pm_recover.c b/os/pm/pm_recover.c new file mode 100644 index 0000000000..1797e8cb48 --- /dev/null +++ b/os/pm/pm_recover.c @@ -0,0 +1,54 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed 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 +#include + +#include "pm.h" + +#ifdef CONFIG_PM + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pm_recover + * + * Description: + * This function is called from task_recover() when a task is deleted via + * task_delete() or via pthread_cancel(). It does pm cleanup. + * + * Inputs: + * tcb - The TCB of the terminated task or thread + * + * Return Value: + * None. + * + ****************************************************************************/ +void pm_recover(FAR struct tcb_s *tcb) +{ +#ifdef CONFIG_PM_METRICS + pm_metrics_update_recover(tcb->pid); +#endif +} + +#endif /* CONFIG_PM */ diff --git a/os/pm/pm_sleep.c b/os/pm/pm_sleep.c index e0f768906f..91749680ca 100644 --- a/os/pm/pm_sleep.c +++ b/os/pm/pm_sleep.c @@ -73,8 +73,11 @@ * Public Functions ************************************************************************/ -static void pm_timer_callback(int argc, uint32_t sem) +static void pm_timer_callback(int argc, uint32_t sem, uint32_t pid) { +#ifdef CONFIG_PM_METRICS + pm_metrics_update_sleep((pid_t)pid); +#endif /* As the timer is expired, give back the semaphore to unlock the thread */ sem_post((sem_t*)sem); } @@ -121,7 +124,7 @@ int pm_sleep(int milliseconds) return ERROR; } - ret = wd_start(wdog, MSEC2TICK(milliseconds), (wdentry_t)pm_timer_callback, 1, (uint32_t)&pm_sem); + ret = wd_start(wdog, MSEC2TICK(milliseconds), (wdentry_t)pm_timer_callback, 2, (uint32_t)&pm_sem, (uint32_t)getpid()); if (ret != OK) { pmdbg("pm_sleep: wd_start failed\n"); wd_delete(wdog); diff --git a/os/pm/pm_wakehandler.c b/os/pm/pm_wakehandler.c index 9e89eef1e4..b8b2407a9d 100644 --- a/os/pm/pm_wakehandler.c +++ b/os/pm/pm_wakehandler.c @@ -56,7 +56,7 @@ void pm_wakehandler(clock_t missing_tick, pm_wakeup_reason_code_t wakeup_src) { irqstate_t flags = enter_critical_section(); #ifdef CONFIG_PM_METRICS - pm_metrics_update_wakehandler(missing_tick, wakeup_src); + pm_metrics_update_wakehandler((uint32_t)missing_tick, wakeup_src); #endif pmllvdbg("wakeup source code = %d\n", wakeup_src); pmllvdbg("missing_tick: %llu\n", missing_tick);