From 3762bda5e2743c65ed65940add0cff8e75228d74 Mon Sep 17 00:00:00 2001 From: dulibo1 Date: Thu, 16 May 2024 22:19:54 +0800 Subject: [PATCH] pm: procfs add pm prepare fail stats Signed-off-by: dulibo1 Signed-off-by: buxiasen --- drivers/power/pm/pm_changestate.c | 130 ++++++++++++++++++++++-------- drivers/power/pm/pm_procfs.c | 125 +++++++++++++++++++++++++--- drivers/power/pm/pm_register.c | 8 ++ include/nuttx/power/pm.h | 13 +++ 4 files changed, 233 insertions(+), 43 deletions(-) diff --git a/drivers/power/pm/pm_changestate.c b/drivers/power/pm/pm_changestate.c index 17b17030fc..00e8790f68 100644 --- a/drivers/power/pm/pm_changestate.c +++ b/drivers/power/pm/pm_changestate.c @@ -39,6 +39,92 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: pm_stats + * + * Description: + * Statistic when domain on state change events. + * + * Input Parameters: + * dom - Identifies the target domain for Statistic + * curstate - Identifies the current PM state + * newstate - Identifies the new PM state + * + * Returned Value: + * None. + * + ****************************************************************************/ +#ifdef CONFIG_PM_PROCFS +static void pm_stats(FAR struct pm_domain_s *dom, int curstate, int newstate) +{ + struct timespec ts; + + clock_systime_timespec(&ts); + clock_timespec_subtract(&ts, &dom->start, &ts); + + if (newstate == PM_RESTORE) + { + /* Wakeup from WFI */ + + clock_timespec_add(&ts, &dom->sleep[curstate], &dom->sleep[curstate]); + } + else + { + /* Sleep to WFI */ + + clock_timespec_add(&ts, &dom->wake[curstate], &dom->wake[curstate]); + } + + /* Update start */ + + clock_systime_timespec(&dom->start); +} + +/**************************************************************************** + * Name: pm_stats_preparefail + * + * Description: + * Statistic the domain on drivers prepare failed. + * + * Input Parameters: + * domain - Identifies the target domain for Statistic + * callback - The prepare failed callback + * newstate - The target new state to prepare + * ret - The driver prepare failed returned value + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void pm_stats_preparefail(int domain, + FAR struct pm_callback_s *callback, + int newstate, int ret) +{ + struct timespec ts; + FAR struct pm_preparefail_s *pf = &callback->preparefail[domain]; + + if (pf->state != PM_RESTORE) + { + clock_systime_timespec(&ts); + clock_timespec_subtract(&ts, &pf->start, &ts); + clock_timespec_add(&ts, &pf->duration[pf->state], + &pf->duration[pf->state]); + pf->state = PM_RESTORE; + } + + if (ret < 0) + { + clock_systime_timespec(&pf->start); + pf->state = newstate; + } +} + +#else +# define pm_stats(dom, curstate, newstate) +# define pm_stats_preparefail(domain, callback, newstate, ret) +#endif + /**************************************************************************** * Name: pm_prepall * @@ -48,6 +134,7 @@ * Input Parameters: * domain - Identifies the domain of the new PM state * newstate - Identifies the new PM state + * restore - Indicate currently in revert the preceding prepare stage. * * Returned Value: * 0 (OK) means that the callback function for all registered drivers @@ -60,7 +147,7 @@ * ****************************************************************************/ -static int pm_prepall(int domain, enum pm_state_e newstate) +static int pm_prepall(int domain, enum pm_state_e newstate, bool restore) { FAR dq_entry_t *entry; int ret = OK; @@ -81,6 +168,10 @@ static int pm_prepall(int domain, enum pm_state_e newstate) /* Yes.. prepare the driver */ ret = cb->prepare(cb, domain, newstate); + if (!restore) + { + pm_stats_preparefail(domain, cb, newstate, ret); + } } } } @@ -100,6 +191,10 @@ static int pm_prepall(int domain, enum pm_state_e newstate) /* Yes.. prepare the driver */ ret = cb->prepare(cb, domain, newstate); + if (!restore) + { + pm_stats_preparefail(domain, cb, newstate, ret); + } } } } @@ -168,35 +263,6 @@ static inline void pm_changeall(int domain, enum pm_state_e newstate) } } -#ifdef CONFIG_PM_PROCFS -static void pm_stats(FAR struct pm_domain_s *dom, int curstate, int newstate) -{ - struct timespec ts; - - clock_systime_timespec(&ts); - clock_timespec_subtract(&ts, &dom->start, &ts); - - if (newstate == PM_RESTORE) - { - /* Wakeup from WFI */ - - clock_timespec_add(&ts, &dom->sleep[curstate], &dom->sleep[curstate]); - } - else - { - /* Sleep to WFI */ - - clock_timespec_add(&ts, &dom->wake[curstate], &dom->wake[curstate]); - } - - /* Update start */ - - clock_systime_timespec(&dom->start); -} -#else -# define pm_stats(dom, curstate, newstate) -#endif - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -250,7 +316,7 @@ int pm_changestate(int domain, enum pm_state_e newstate) * drivers may refuse the state state change. */ - ret = pm_prepall(domain, newstate); + ret = pm_prepall(domain, newstate, false); if (ret != OK) { /* One or more drivers is not ready for this state change. @@ -258,7 +324,7 @@ int pm_changestate(int domain, enum pm_state_e newstate) */ newstate = g_pmglobals.domain[domain].state; - pm_prepall(domain, newstate); + pm_prepall(domain, newstate, true); } } diff --git a/drivers/power/pm/pm_procfs.c b/drivers/power/pm/pm_procfs.c index 61760fb62c..b0e52ee560 100644 --- a/drivers/power/pm/pm_procfs.c +++ b/drivers/power/pm/pm_procfs.c @@ -44,17 +44,22 @@ * Pre-processor Definitions ****************************************************************************/ -#define STHDR "DOMAIN%d WAKE SLEEP TOTAL\n" -#define WAHDR "DOMAIN%d STATE COUNT TIME\n" +#define STHDR "DOMAIN%-2d WAKE SLEEP TOTAL\n" +#define PFHDR "CALLBACKS IDLE STANDBY SLEEP\n" +#define WAHDR "DOMAIN%-2d STATE COUNT TIME\n" #ifdef CONFIG_SYSTEM_TIME64 -# define STFMT "%-8s %8" PRIu64 "s %02" PRIu64 "%% %8" PRIu64 "s %02" \ - PRIu64 "%% %8" PRIu64 "s %02" PRIu64 "%%\n" -# define WAFMT "%-12s %-10s %4" PRIu32 " %8" PRIu64 "s\n" +# define STFMT "%-18s %8" PRIu64 "s %3" PRIu64 "%% %8" PRIu64 "s %3" \ + PRIu64 "%% %8" PRIu64 "s %3" PRIu64 "%%\n" +# define PFFMT "%-18p %8" PRIu64 "s %3" PRIu64 "%% %8" PRIu64 "s %3" \ + PRIu64 "%% %8" PRIu64 "s %3" PRIu64 "%%\n" +# define WAFMT "%-25s %-14s %-14" PRIu32 " %" PRIu64 "s\n" #else -# define STFMT "%-8s %8" PRIu32 "s %02" PRIu32 "%% %8" PRIu32 "s %02" \ - PRIu32 "%% %8" PRIu32 "s %02" PRIu32 "%%\n" -# define WAFMT "%-12s %-10s %4" PRIu32 " %8" PRIu32 "s\n" +# define STFMT "%-18s %8" PRIu32 "s %3" PRIu32 "%% %8" PRIu32 "s %3" \ + PRIu32 "%% %8" PRIu32 "s %3" PRIu32 "%%\n" +# define PFFMT "%-18p %8" PRIu32 "s %3" PRIu32 "%% %8" PRIu32 "s %3" \ + PRIu32 "%% %8" PRIu32 "s %3" PRIu32 "%%\n" +# define WAFMT "%-25s %-14s %-14" PRIu32 " %" PRIu32 "s\n" #endif /* Determines the size of an intermediate buffer that must be large enough @@ -100,6 +105,8 @@ static ssize_t pm_read_state(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t pm_read_wakelock(FAR struct file *filep, FAR char *buffer, size_t buflen); +static ssize_t pm_read_preparefail(FAR struct file *filep, FAR char *buffer, + size_t buflen); static ssize_t pm_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static int pm_dup(FAR const struct file *oldp, @@ -149,8 +156,9 @@ const struct procfs_operations g_pm_operations = static const struct pm_file_ops_s g_pm_files[] = { - {"state", pm_read_state}, - {"wakelock", pm_read_wakelock}, + {"state", pm_read_state}, + {"wakelock", pm_read_wakelock}, + {"preparefail", pm_read_preparefail}, }; static FAR const char *g_pm_state[PM_COUNT] = @@ -262,7 +270,7 @@ static ssize_t pm_read_state(FAR struct file *filep, FAR char *buffer, size_t linesize; size_t copysize; off_t offset; - uint32_t sum = 0; + time_t sum = 0; uint32_t state; finfo("buffer=%p buflen=%d\n", buffer, (int)buflen); @@ -398,6 +406,101 @@ static ssize_t pm_read_wakelock(FAR struct file *filep, FAR char *buffer, return totalsize; } +/**************************************************************************** + * Name: pm_read_preparefail + * + * Description: + * The statistic values about prepare callback failed. + * + ****************************************************************************/ + +static ssize_t pm_read_preparefail(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct pm_preparefail_s *pf; + FAR struct pm_file_s *pmfile; + FAR struct pm_callback_s *cb; + FAR struct pm_domain_s *dom; + FAR dq_entry_t *entry; + irqstate_t flags; + size_t totalsize = 0; + size_t linesize; + size_t copysize; + off_t offset; + time_t sum = 0; + uint32_t state; + + finfo("buffer=%p buflen=%d\n", buffer, (int)buflen); + + /* Recover our private data from the struct file instance */ + + pmfile = (FAR struct pm_file_s *)filep->f_priv; + dom = &g_pmglobals.domain[pmfile->domain]; + DEBUGASSERT(pmfile); + DEBUGASSERT(dom); + + /* Save the file offset and the user buffer information */ + + offset = filep->f_pos; + + /* Then list the power state */ + + linesize = snprintf(pmfile->line, PM_LINELEN, PFHDR); + copysize = procfs_memcpy(pmfile->line, linesize, buffer, + buflen, &offset); + totalsize += copysize; + + flags = pm_domain_lock(pmfile->domain); + for (entry = dq_peek(&g_pmglobals.registry); + entry; entry = dq_next(entry)) + { + cb = (FAR struct pm_callback_s *)entry; + pf = &cb->preparefail[pmfile->domain]; + for (state = 0; state < PM_COUNT; state++) + { + sum += pf->duration[state].tv_sec; + } + } + + sum = sum ? sum : 1; + for (entry = dq_peek(&g_pmglobals.registry); + entry; entry = dq_next(entry)) + { + time_t total = 0; + + cb = (FAR struct pm_callback_s *)entry; + pf = &cb->preparefail[pmfile->domain]; + for (state = 0; state < PM_COUNT; state++) + { + total += pf->duration[state].tv_sec; + } + + if (total == 0) + { + continue; + } + + linesize = snprintf(pmfile->line, PM_LINELEN, PFFMT, + cb->prepare, + pf->duration[PM_IDLE].tv_sec, + 100 * pf->duration[PM_IDLE].tv_sec / sum, + pf->duration[PM_STANDBY].tv_sec, + 100 * pf->duration[PM_STANDBY].tv_sec / sum, + pf->duration[PM_SLEEP].tv_sec, + 100 * pf->duration[PM_SLEEP].tv_sec / sum + ); + buffer += copysize; + buflen -= copysize; + copysize = procfs_memcpy(pmfile->line, linesize, buffer, + buflen, &offset); + totalsize += copysize; + } + + pm_domain_unlock(pmfile->domain, flags); + filep->f_pos += totalsize; + return totalsize; +} + /**************************************************************************** * Name: pm_read ****************************************************************************/ diff --git a/drivers/power/pm/pm_register.c b/drivers/power/pm/pm_register.c index 6a0b8e9dcb..b671062f67 100644 --- a/drivers/power/pm/pm_register.c +++ b/drivers/power/pm/pm_register.c @@ -64,6 +64,14 @@ int pm_register(FAR struct pm_callback_s *callbacks) flags = pm_lock(&g_pmglobals.reglock); dq_addlast(&callbacks->entry, &g_pmglobals.registry); + +#ifdef CONFIG_PM_PROCFS + for (int domain = 0; domain < CONFIG_PM_NDOMAINS; domain++) + { + callbacks->preparefail[domain].state = PM_RESTORE; + } +#endif + pm_unlock(&g_pmglobals.reglock, flags); return 0; diff --git a/include/nuttx/power/pm.h b/include/nuttx/power/pm.h index dc7311c32e..ee6e4c6431 100644 --- a/include/nuttx/power/pm.h +++ b/include/nuttx/power/pm.h @@ -145,6 +145,15 @@ enum pm_state_e PM_COUNT, }; +#ifdef CONFIG_PM_PROCFS +struct pm_preparefail_s +{ + enum pm_state_e state; + struct timespec start; + struct timespec duration[PM_COUNT]; +}; +#endif + /* This structure contain pointers callback functions in the driver. These * callback functions can be used to provide power management information * to the driver. @@ -211,6 +220,10 @@ struct pm_callback_s CODE void (*notify)(FAR struct pm_callback_s *cb, int domain, enum pm_state_e pmstate); + +#ifdef CONFIG_PM_PROCFS + struct pm_preparefail_s preparefail[CONFIG_PM_NDOMAINS]; +#endif }; /* An instance of a given PM governor */