nuttx/sched/sched/sched_critmonitor.c
buxiasen b33e20a586 critmon: fix context switch critmon pause err.
Elapsed time statistic up to now when sched happen

Signed-off-by: buxiasen <buxiasen@xiaomi.com>
2024-09-19 03:16:14 +08:00

395 lines
11 KiB
C

/****************************************************************************
* sched/sched/sched_critmonitor.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <sys/types.h>
#include <sched.h>
#include <assert.h>
#include <debug.h>
#include <time.h>
#include "sched/sched.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION > 0
# define CHECK_PREEMPTION(pid, elapsed) \
do \
{ \
if (pid > 0 && \
elapsed > CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION) \
{ \
CRITMONITOR_PANIC("PID %d hold sched lock too long %"PRIu32"\n", \
pid, elapsed); \
} \
} \
while (0)
#else
# define CHECK_PREEMPTION(pid, elapsed)
#endif
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION > 0
# define CHECK_CSECTION(pid, elapsed) \
do \
{ \
if (pid > 0 && \
elapsed > CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION) \
{ \
CRITMONITOR_PANIC("PID %d hold critical section too long %" \
PRIu32 "\n", pid, elapsed); \
} \
} \
while (0)
#else
# define CHECK_CSECTION(pid, elapsed)
#endif
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_THREAD > 0
# define CHECK_THREAD(pid, elapsed) \
do \
{ \
if (pid > 0 && \
elapsed > CONFIG_SCHED_CRITMONITOR_MAXTIME_THREAD) \
{ \
CRITMONITOR_PANIC("PID %d execute too long %"PRIu32"\n", \
pid, elapsed); \
} \
} \
while (0)
#else
# define CHECK_THREAD(pid, elapsed)
#endif
/****************************************************************************
* Public Data
****************************************************************************/
/* Maximum time with pre-emption disabled or within critical section. */
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION >= 0
clock_t g_premp_max[CONFIG_SMP_NCPUS];
#endif
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION >= 0
clock_t g_crit_max[CONFIG_SMP_NCPUS];
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nxsched_critmon_cpuload
*
* Description:
* Update the running time of all running threads when switching threads
*
* Input Parameters:
* tcb - The task that we are performing the load operations on.
* current - The current time
* tick - The ticks that we process in this cpuload.
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_SCHED_CPULOAD_CRITMONITOR
static void nxsched_critmon_cpuload(FAR struct tcb_s *tcb, clock_t current,
clock_t tick)
{
int i;
UNUSED(i);
/* Update the cpuload of the thread ready to be suspended */
nxsched_process_taskload_ticks(tcb, tick);
/* Update the cpuload of threads running on other CPUs */
# ifdef CONFIG_SMP
for (i = 0; i < CONFIG_SMP_NCPUS; i++)
{
FAR struct tcb_s *rtcb = current_task(i);
if (tcb->cpu == rtcb->cpu)
{
continue;
}
nxsched_process_taskload_ticks(rtcb, tick);
/* Update start time, avoid repeated statistics when the next call */
rtcb->run_start = current;
}
# endif
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxsched_critmon_preemption
*
* Description:
* Called when there is any change in pre-emptible state of a thread.
*
* Assumptions:
* - Called within a critical section.
* - Never called from an interrupt handler
* - Caller is the address of the function that is changing the pre-emption
*
****************************************************************************/
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION >= 0
void nxsched_critmon_preemption(FAR struct tcb_s *tcb, bool state,
FAR void *caller)
{
clock_t current = perf_gettime();
/* Are we enabling or disabling pre-emption */
if (state)
{
/* Disabling.. Save the thread start time */
tcb->premp_start = current;
tcb->premp_caller = caller;
}
else
{
/* Re-enabling.. Check for the max elapsed time */
clock_t elapsed = current - tcb->premp_start;
int cpu = this_cpu();
if (elapsed > tcb->premp_max)
{
tcb->premp_max = elapsed;
tcb->premp_max_caller = tcb->premp_caller;
CHECK_PREEMPTION(tcb->pid, elapsed);
}
/* Check for the global max elapsed time */
if (elapsed > g_premp_max[cpu])
{
g_premp_max[cpu] = elapsed;
}
}
}
#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION >= 0 */
/****************************************************************************
* Name: nxsched_critmon_csection
*
* Description:
* Called when a thread enters or leaves a critical section.
*
* Assumptions:
* - Called within a critical section.
* - Never called from an interrupt handler
* - Caller is the address of the function that is entering the critical
*
****************************************************************************/
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION >= 0
void nxsched_critmon_csection(FAR struct tcb_s *tcb, bool state,
FAR void *caller)
{
clock_t current = perf_gettime();
/* Are we entering or leaving the critical section? */
if (state)
{
/* Entering... Save the start time. */
tcb->crit_start = current;
tcb->crit_caller = caller;
}
else
{
/* Leaving .. Check for the max elapsed time */
clock_t elapsed = current - tcb->crit_start;
int cpu = this_cpu();
if (elapsed > tcb->crit_max)
{
tcb->crit_max = elapsed;
tcb->crit_max_caller = tcb->crit_caller;
CHECK_CSECTION(tcb->pid, elapsed);
}
/* Check for the global max elapsed time */
if (elapsed > g_crit_max[cpu])
{
g_crit_max[cpu] = elapsed;
}
}
}
#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION >= 0 */
/****************************************************************************
* Name: nxsched_resume_critmon
*
* Description:
* Called when a thread resumes execution, perhaps re-establishing a
* critical section or a non-pre-emptible state.
*
* Assumptions:
* - Called within a critical section.
* - Might be called from an interrupt handler
*
****************************************************************************/
void nxsched_resume_critmon(FAR struct tcb_s *tcb)
{
clock_t current = perf_gettime();
UNUSED(current);
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_THREAD >= 0
tcb->run_start = current;
#endif
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION >= 0
/* Did this task disable pre-emption? */
if (tcb->lockcount > 0)
{
/* Yes.. Save the start time */
tcb->premp_start = current;
}
#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION */
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION >= 0
/* Was this task in a critical section? */
if (tcb->irqcount > 0)
{
/* Yes.. Save the start time */
tcb->crit_start = current;
}
#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION */
}
/****************************************************************************
* Name: nxsched_suspend_critmon
*
* Description:
* Called when a thread suspends execution, perhaps terminating a
* critical section or a non-preemptible state.
*
* Assumptions:
* - Called within a critical section.
* - Might be called from an interrupt handler
*
****************************************************************************/
void nxsched_suspend_critmon(FAR struct tcb_s *tcb)
{
clock_t current = perf_gettime();
clock_t elapsed = current - tcb->run_start;
int cpu = this_cpu();
#ifdef CONFIG_SCHED_CPULOAD_CRITMONITOR
clock_t tick = elapsed * CLOCKS_PER_SEC / perf_getfreq();
nxsched_critmon_cpuload(tcb, current, tick);
#endif
UNUSED(cpu);
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_THREAD >= 0
tcb->run_time += elapsed;
if (elapsed > tcb->run_max)
{
tcb->run_max = elapsed;
CHECK_THREAD(tcb->pid, elapsed);
}
#endif
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION >= 0
/* Did this task disable preemption? */
if (tcb->lockcount > 0)
{
/* Possibly re-enabling.. Check for the max elapsed time */
elapsed = current - tcb->premp_start;
if (elapsed > tcb->premp_max)
{
tcb->premp_max = elapsed;
tcb->premp_max_caller = tcb->premp_caller;
CHECK_PREEMPTION(tcb->pid, elapsed);
}
/* Suspend percore preemptible statistic and if necessary will
* re-open in nxsched_resume_critmon.
*/
if (elapsed > g_premp_max[cpu])
{
g_premp_max[cpu] = elapsed;
}
}
#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION */
#if CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION >= 0
/* Is this task in a critical section? */
if (tcb->irqcount > 0)
{
/* Possibly leaving .. Check for the max elapsed time */
elapsed = current - tcb->crit_start;
if (elapsed > tcb->crit_max)
{
tcb->crit_max = elapsed;
tcb->crit_max_caller = tcb->crit_caller;
CHECK_CSECTION(tcb->pid, elapsed);
}
/* Check for the global max elapsed time */
if (elapsed > g_crit_max[cpu])
{
g_crit_max[cpu] = elapsed;
}
}
#endif /* CONFIG_SCHED_CRITMONITOR_MAXTIME_CSECTION */
}