This is an automated email from the ASF dual-hosted git repository. jiuzhudong pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 3116c19efc39c6cff297d7ec329aff0f1958ff63 Author: wangzhi16 <[email protected]> AuthorDate: Mon Nov 11 09:44:28 2024 +0800 sched/spinlock: Add configuration to record busywait. Add configuration "CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT", which can record the busy waiting time to get spinlock or enter critical section. Signed-off-by: wangzhi16 <[email protected]> --- fs/procfs/fs_procfscritmon.c | 73 ++++++++++++++++++++++++++++++++++++++ fs/procfs/fs_procfsproc.c | 63 +++++++++++++++++++++++++++++++++ include/nuttx/irq.h | 4 +++ include/nuttx/sched.h | 13 +++++++ include/nuttx/spinlock.h | 22 ++++++++++++ sched/Kconfig | 10 ++++++ sched/sched/sched_critmonitor.c | 78 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 263 insertions(+) diff --git a/fs/procfs/fs_procfscritmon.c b/fs/procfs/fs_procfscritmon.c index 96c2e88a3b0..bc05e0a4e57 100644 --- a/fs/procfs/fs_procfscritmon.c +++ b/fs/procfs/fs_procfscritmon.c @@ -190,6 +190,11 @@ static ssize_t critmon_read_cpu(FAR struct critmon_file_s *attr, size_t linesize; size_t copysize; size_t totalsize; +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 + struct timespec alltime; + clock_t elapsed; + uint32_t rate; +#endif UNUSED(maxtime); UNUSED(linesize); @@ -279,6 +284,74 @@ static ssize_t critmon_read_cpu(FAR struct critmon_file_s *attr, } #endif +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 + /* Convert and generate output for max busywait time when trying spinlock */ + + if (g_busywait_max[cpu] > 0) + { + perf_convert(g_busywait_max[cpu], &maxtime); + } + else + { + maxtime.tv_sec = 0; + maxtime.tv_nsec = 0; + } + + /* Reset the maximum */ + + g_busywait_max[cpu] = 0; + + /* Generate output for max busywait time to enter csection(get spinlock) */ + + linesize = procfs_snprintf(attr->line, CRITMON_LINELEN, ",%lu.%09lu", + (unsigned long)maxtime.tv_sec, + (unsigned long)maxtime.tv_nsec); + copysize = procfs_memcpy(attr->line, linesize, buffer, buflen, offset); + + totalsize += copysize; + buffer += copysize; + buflen -= copysize; + + if (buflen <= 0) + { + return totalsize; + } + + /* Convert and generate output for all busywait time when trying spinlock */ + + if (g_busywait_total[cpu] > 0) + { + perf_convert(g_busywait_total[cpu], &alltime); + } + else + { + alltime.tv_sec = 0; + alltime.tv_nsec = 0; + } + + elapsed = clock() * CONFIG_USEC_PER_TICK; + rate = (uint64_t)(alltime.tv_sec * 1000000 + alltime.tv_nsec / 1000) * + 1000000 / elapsed; + + /* Generate output for all busywait time to enter csection(get spinlock) */ + + linesize = procfs_snprintf(attr->line, CRITMON_LINELEN, ",%lu.%09lu %2" + PRId32 ".%04" PRId32 "%%", + (unsigned long)alltime.tv_sec, + (unsigned long)alltime.tv_nsec, + rate / 10000, rate % 10000); + copysize = procfs_memcpy(attr->line, linesize, buffer, buflen, offset); + + totalsize += copysize; + buffer += copysize; + buflen -= copysize; + + if (buflen <= 0) + { + return totalsize; + } +#endif + linesize = procfs_snprintf(attr->line, CRITMON_LINELEN, "\n"); copysize = procfs_memcpy(attr->line, linesize, buffer, buflen, offset); diff --git a/fs/procfs/fs_procfsproc.c b/fs/procfs/fs_procfsproc.c index a1a58fe1b5a..49138a34297 100644 --- a/fs/procfs/fs_procfsproc.c +++ b/fs/procfs/fs_procfsproc.c @@ -844,6 +844,69 @@ static ssize_t proc_critmon(FAR struct proc_file_s *procfile, } #endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION >= 0 */ + /* Convert and generate output for max busywait time && all busywait time */ + +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 + if (tcb->busywait_max > 0) + { + perf_convert(tcb->busywait_max, &maxtime); + } + else + { + maxtime.tv_sec = 0; + maxtime.tv_nsec = 0; + } + + /* Reset the maximum */ + + tcb->busywait_max = 0; + + /* Generate output for max busywait time */ + + linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%lu.%09lu %p,", + (unsigned long)maxtime.tv_sec, + (unsigned long)maxtime.tv_nsec, + tcb->busywait_max_caller); + copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining, + &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } + + if (tcb->busywait_total > 0) + { + perf_convert(tcb->busywait_total, &maxtime); + } + else + { + maxtime.tv_sec = 0; + maxtime.tv_nsec = 0; + } + + /* Generate output for all busywait time */ + + linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%lu.%09lu,", + (unsigned long)maxtime.tv_sec, + (unsigned long)maxtime.tv_nsec); + copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining, + &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } +#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 */ + /* Convert and generate output for maximum time thread running */ #if CONFIG_SCHED_CRITMONITOR_MAXTIME_THREAD >= 0 if (tcb->run_max > 0) diff --git a/include/nuttx/irq.h b/include/nuttx/irq.h index 6f722481176..b1af8827e98 100644 --- a/include/nuttx/irq.h +++ b/include/nuttx/irq.h @@ -150,6 +150,10 @@ # define CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION -1 #endif +#ifndef CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT +# define CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT -1 +#endif + #ifndef CONFIG_SCHED_CRITMONITOR_MAXTIME_IRQ # define CONFIG_SCHED_CRITMONITOR_MAXTIME_IRQ -1 #endif diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h index 6aebeb05f5d..c362cb1011e 100644 --- a/include/nuttx/sched.h +++ b/include/nuttx/sched.h @@ -701,6 +701,14 @@ struct tcb_s void *crit_max_caller; /* Caller of max critical section */ #endif +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 + clock_t busywait_start; /* Time when thread busywait */ + clock_t busywait_max; /* Max time of busywait */ + clock_t busywait_total; /* Total time of busywait */ + void *busywait_caller; /* Caller of busywait */ + void *busywait_max_caller; /* Caller of max busywait */ +#endif + /* State save areas *******************************************************/ /* The form and content of these fields are platform-specific. */ @@ -821,6 +829,11 @@ EXTERN clock_t g_preemp_max[CONFIG_SMP_NCPUS]; EXTERN clock_t g_crit_max[CONFIG_SMP_NCPUS]; #endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION >= 0 */ +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 +EXTERN clock_t g_busywait_max[CONFIG_SMP_NCPUS]; +EXTERN clock_t g_busywait_total[CONFIG_SMP_NCPUS]; +#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 */ + /* g_running_tasks[] holds a references to the running task for each CPU. * It is valid only when up_interrupt_context() returns true. */ diff --git a/include/nuttx/spinlock.h b/include/nuttx/spinlock.h index 00005751bfd..fdb066d9f69 100644 --- a/include/nuttx/spinlock.h +++ b/include/nuttx/spinlock.h @@ -66,6 +66,12 @@ void sched_note_spinlock_unlock(FAR volatile spinlock_t *spinlock); # define sched_note_spinlock_unlock(spinlock) #endif +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 +void nxsched_critmon_busywait(bool state, FAR void *caller); +#else +# define nxsched_critmon_busywait(state, caller) +#endif + /**************************************************************************** * Public Data Types ****************************************************************************/ @@ -223,10 +229,18 @@ static inline_function void spin_lock(FAR volatile spinlock_t *lock) sched_note_spinlock_lock(lock); + /* If CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0, count busy-waiting. */ + + nxsched_critmon_busywait(true, return_address(0)); + /* Lock without trace note */ spin_lock_notrace(lock); + /* Get the lock, end counting busy-waiting */ + + nxsched_critmon_busywait(false, return_address(0)); + /* Notify that we have the spinlock */ sched_note_spinlock_locked(lock); @@ -988,6 +1002,8 @@ void rspin_restorelock(FAR rspinlock_t *lock, uint16_t count) static inline_function void read_lock(FAR volatile rwlock_t *lock) { + nxsched_critmon_busywait(true, return_address(0)); + while (true) { int old = atomic_read(lock); @@ -1004,6 +1020,8 @@ static inline_function void read_lock(FAR volatile rwlock_t *lock) } UP_DMB(); + + nxsched_critmon_busywait(false, return_address(0)); } /**************************************************************************** @@ -1106,6 +1124,8 @@ static inline_function void read_unlock(FAR volatile rwlock_t *lock) static inline_function void write_lock(FAR volatile rwlock_t *lock) { + nxsched_critmon_busywait(true, return_address(0)); + while (true) { int zero = RW_SP_UNLOCKED; @@ -1119,6 +1139,8 @@ static inline_function void write_lock(FAR volatile rwlock_t *lock) } UP_DMB(); + + nxsched_critmon_busywait(false, return_address(0)); } /**************************************************************************** diff --git a/sched/Kconfig b/sched/Kconfig index 34db4e5d92e..bc68d2808f6 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -955,6 +955,15 @@ config SCHED_CRITMONITOR_MAXTIME_CSECTION SCHED_CRITMONITOR_MAXTIME_CSECTION, or system will give a warning. For debugging system latency, 0 means disabled. +config SCHED_CRITMONITOR_MAXTIME_BUSYWAIT + int "Critical section or spinlock max busy waiting time" + default -1 + ---help--- + Busy waiting time to enter critical section or + get spinlock should be smaller than + SCHED_CRITMONITOR_MAXTIME_BUSYWAIT, or system will give a warning. + For debugging system latency, -1 means disabled. + config SCHED_CRITMONITOR_MAXTIME_IRQ int "IRQ max execution time" default SCHED_CRITMONITOR_MAXTIME_CSECTION @@ -981,6 +990,7 @@ config SCHED_CRITMONITOR_MAXTIME_PANIC SCHED_CRITMONITOR_MAXTIME_WQUEUE > 0 || \ SCHED_CRITMONITOR_MAXTIME_PREEMPTION > 0 || \ SCHED_CRITMONITOR_MAXTIME_CSECTION > 0 || \ + SCHED_CRITMONITOR_MAXTIME_BUSYWAIT > 0 || \ SCHED_CRITMONITOR_MAXTIME_IRQ > 0 default n ---help--- diff --git a/sched/sched/sched_critmonitor.c b/sched/sched/sched_critmonitor.c index 2843d51348b..2b2637c1f42 100644 --- a/sched/sched/sched_critmonitor.c +++ b/sched/sched/sched_critmonitor.c @@ -70,6 +70,22 @@ # define CHECK_CSECTION(pid, elapsed) #endif +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 +# define CHECK_BUSYWAIT(pid, elapsed) \ + do \ + { \ + if (pid > 0 && \ + elapsed > CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT) \ + { \ + CRITMONITOR_PANIC("PID %d wait for critical section or spin" \ + "lock too long %" PRIu32 "\n", pid, elapsed); \ + } \ + } \ + while (0) +#else +# define CHECK_BUSYWAIT(pid, elapsed) +#endif + #if CONFIG_SCHED_CRITMONITOR_MAXTIME_THREAD > 0 # define CHECK_THREAD(pid, elapsed) \ do \ @@ -108,6 +124,11 @@ clock_t g_preemp_max[CONFIG_SMP_NCPUS]; clock_t g_crit_max[CONFIG_SMP_NCPUS]; #endif +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 +clock_t g_busywait_max[CONFIG_SMP_NCPUS]; +clock_t g_busywait_total[CONFIG_SMP_NCPUS]; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -274,6 +295,63 @@ void nxsched_critmon_csection(FAR struct tcb_s *tcb, bool state, } #endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION >= 0 */ +/**************************************************************************** + * Name: nxsched_critmon_busywait + * + * Description: + * Called when a thread try to enter critical section(get spinlock) or + * successfully entered cirtical section. + * + * Assumptions: + * - Called before a critical section or within a critical section. + * - Might be called from an interrupt handler. + * + ****************************************************************************/ + +#if CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 +void nxsched_critmon_busywait(bool state, FAR void *caller) +{ + FAR struct tcb_s *tcb = this_task(); + clock_t current = perf_gettime(); + + /* Are we busy waiting for critical section or spinlock? */ + + if (state) + { + /* Waiting... Save the start time. */ + + tcb->busywait_start = current; + tcb->busywait_caller = caller; + } + else + { + /* Entered critical section... Check for the max elapsed time */ + + clock_t elapsed = current - tcb->busywait_start; + int cpu = this_cpu(); + + if (elapsed > tcb->busywait_max) + { + tcb->busywait_max = elapsed; + tcb->busywait_max_caller = tcb->busywait_caller; + CHECK_BUSYWAIT(tcb->pid, elapsed); + } + + /* Check for the global max elapsed time */ + + if (elapsed > g_busywait_max[cpu]) + { + g_busywait_max[cpu] = elapsed; + } + + /* Update thread-level and cpu-level busywait */ + + tcb->busywait_total += elapsed; + g_busywait_total[cpu] += elapsed; + } +} +#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_BUSYWAIT >= 0 */ + /**************************************************************************** * Name: nxsched_switch_critmon *
