1
0
Fork 0
forked from nuttx/nuttx-update

sched: disable interrupt in non-SMP case

For the nested interrupt, one thing should decleared:
We are in ISR context, but no meaning we are disabled the interrupts.

Signed-off-by: ligd <liguiding1@xiaomi.com>
This commit is contained in:
ligd 2024-07-10 22:13:26 +08:00 committed by Xiang Xiao
parent 35fcd9331f
commit f23fb7ec09
4 changed files with 82 additions and 256 deletions

View file

@ -340,12 +340,8 @@ int nxsched_reprioritize(FAR struct tcb_s *tcb, int sched_priority);
/* Support for tickless operation */
#ifdef CONFIG_SCHED_TICKLESS
clock_t nxsched_cancel_timer(void);
void nxsched_resume_timer(void);
void nxsched_reassess_timer(void);
#else
# define nxsched_cancel_timer() (0)
# define nxsched_resume_timer()
# define nxsched_reassess_timer()
#endif

View file

@ -109,13 +109,15 @@ static inline void nxsched_cpu_scheduler(int cpu)
#if CONFIG_RR_INTERVAL > 0 || defined(CONFIG_SCHED_SPORADIC)
static inline void nxsched_process_scheduler(void)
{
#ifdef CONFIG_SMP
irqstate_t flags;
int i;
/* If we are running on a single CPU architecture, then we know interrupts
* are disabled and there is no need to explicitly call
* enter_critical_section(). However, in the SMP case,
/* Single CPU case:
* For nested interrupts, higher IRQs may interrupt nxsched_cpu_scheduler()
* but nxsched_cpu_scheduler() requires that interrupts be disabled.
* We are in ISR context, no meaning we are disabled the interrupts.
*
* SMP case:
* enter_critical_section() does much more than just disable interrupts on
* the local CPU; it also manages spinlocks to assure the stability of the
* TCB that we are manipulating.
@ -131,53 +133,11 @@ static inline void nxsched_process_scheduler(void)
}
leave_critical_section(flags);
#else
/* Perform scheduler operations on the single CPUs */
nxsched_cpu_scheduler(0);
#endif
}
#else
# define nxsched_process_scheduler()
#endif
/****************************************************************************
* Name: nxsched_process_wdtimer
*
* Description:
* Wdog timer process, should with critical_section when SMP mode.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_SMP
static inline void nxsched_process_wdtimer(clock_t ticks)
{
irqstate_t flags;
/* We are in an interrupt handler and, as a consequence, interrupts are
* disabled. But in the SMP case, interrupts MAY be disabled only on
* the local CPU since most architectures do not permit disabling
* interrupts on other CPUS.
*
* Hence, we must follow rules for critical sections even here in the
* SMP case.
*/
flags = enter_critical_section();
wd_timer(ticks);
leave_critical_section(flags);
}
#else
# define nxsched_process_wdtimer(ticks) wd_timer(ticks)
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -236,7 +196,7 @@ void nxsched_process_timer(void)
/* Process watchdogs */
nxsched_process_wdtimer(clock_systime_ticks());
wd_timer(clock_systime_ticks());
#ifdef CONFIG_SYSTEMTICK_HOOK
/* Call out to a user-provided function in order to perform board-specific,

View file

@ -80,7 +80,7 @@ static clock_t nxsched_process_scheduler(clock_t ticks, clock_t elapsed,
#endif
static clock_t nxsched_timer_process(clock_t ticks, clock_t elapsed,
bool noswitches);
static void nxsched_timer_start(clock_t interval);
static clock_t nxsched_timer_start(clock_t ticks, clock_t interval);
/****************************************************************************
* Private Data
@ -275,15 +275,17 @@ static clock_t nxsched_cpu_scheduler(int cpu, clock_t ticks,
static clock_t nxsched_process_scheduler(clock_t ticks, clock_t elapsed,
bool noswitches)
{
#ifdef CONFIG_SMP
clock_t minslice = CLOCK_MAX;
clock_t timeslice;
irqstate_t flags;
int i;
/* If we are running on a single CPU architecture, then we know interrupts
* are disabled and there is no need to explicitly call
* enter_critical_section(). However, in the SMP case,
/* Single CPU case:
* For nested interrupts, higher IRQs may interrupt nxsched_cpu_scheduler()
* but nxsched_cpu_scheduler() requires that interrupts be disabled.
* We are in ISR context, no meaning we are disabled the interrupts.
*
* SMP case:
* enter_critical_section() does much more than just disable interrupts on
* the local CPU; it also manages spinlocks to assure the stability of the
* TCB that we are manipulating.
@ -304,58 +306,11 @@ static clock_t nxsched_process_scheduler(clock_t ticks, clock_t elapsed,
leave_critical_section(flags);
return minslice < CLOCK_MAX ? minslice : 0;
#else
/* Perform scheduler operations on the single CPUs */
return nxsched_cpu_scheduler(0, ticks, elapsed, noswitches);
#endif
}
#else
# define nxsched_process_scheduler(t, e, n) (0)
#endif
/****************************************************************************
* Name: nxsched_process_wdtimer
*
* Description:
* Wdog timer process, should with critical_section when SMP mode.
*
* Input Parameters:
* ticks - The number of ticks that have elapsed on the interval timer.
* noswitches - True: Can't do context switches now.
*
* Returned Value:
* The number of ticks for the next delay is provided (zero if no delay).
*
****************************************************************************/
#ifdef CONFIG_SMP
static inline_function clock_t nxsched_process_wdtimer(clock_t ticks,
bool noswitches)
{
clock_t ret;
irqstate_t flags;
/* We are in an interrupt handler and, as a consequence, interrupts are
* disabled. But in the SMP case, interrupts MAY be disabled only on
* the local CPU since most architectures do not permit disabling
* interrupts on other CPUS.
*
* Hence, we must follow rules for critical sections even here in the
* SMP case.
*/
flags = enter_critical_section();
ret = wd_timer(ticks, noswitches);
leave_critical_section(flags);
return ret;
}
#else
# define nxsched_process_wdtimer(t, n) wd_timer(t, n)
#endif
/****************************************************************************
* Name: nxsched_timer_process
*
@ -394,7 +349,7 @@ static clock_t nxsched_timer_process(clock_t ticks, clock_t elapsed,
/* Process watchdogs */
tmp = nxsched_process_wdtimer(ticks, noswitches);
tmp = wd_timer(ticks, noswitches);
if (tmp > 0)
{
rettime = tmp;
@ -424,13 +379,14 @@ static clock_t nxsched_timer_process(clock_t ticks, clock_t elapsed,
*
* Input Parameters:
* ticks - The number of ticks defining the timer interval to setup.
* interval - The number of ticks to use when setting up the next timer.
*
* Returned Value:
* None
*
****************************************************************************/
static void nxsched_timer_start(clock_t interval)
static clock_t nxsched_timer_start(clock_t ticks, clock_t interval)
{
int ret;
@ -448,12 +404,8 @@ static void nxsched_timer_start(clock_t interval)
* to the time when last stopped the timer).
*/
ret = up_alarm_tick_start(g_timer_tick + interval);
ret = up_alarm_tick_start(ticks + interval);
#else
/* Save new timer interval */
g_timer_interval = interval;
/* [Re-]start the interval timer */
ret = up_timer_tick_start(interval);
@ -465,6 +417,8 @@ static void nxsched_timer_start(clock_t interval)
UNUSED(ret);
}
}
return interval;
}
/****************************************************************************
@ -496,35 +450,22 @@ void nxsched_alarm_tick_expiration(clock_t ticks)
{
clock_t elapsed;
clock_t nexttime;
#ifdef CONFIG_SMP
irqstate_t flags;
/* If we are running on a single CPU architecture, then we know interrupts
* are disabled and there is no need to explicitly call
* enter_critical_section(). However, in the SMP case,
* enter_critical_section() is required prevent multiple cpu to enter
* oneshot_tick_start.
*/
flags = enter_critical_section();
#endif
/* Calculate elapsed */
elapsed = ticks - g_timer_tick;
/* Save the time that the alarm occurred */
flags = enter_critical_section();
elapsed = ticks - g_timer_tick;
g_timer_tick = ticks;
leave_critical_section(flags);
/* Process the timer ticks and set up the next interval (or not) */
nexttime = nxsched_timer_process(ticks, elapsed, false);
nxsched_timer_start(nexttime);
#ifdef CONFIG_SMP
flags = enter_critical_section();
nxsched_timer_start(ticks, nexttime);
leave_critical_section(flags);
#endif
}
void nxsched_alarm_expiration(FAR const struct timespec *ts)
@ -557,142 +498,29 @@ void nxsched_alarm_expiration(FAR const struct timespec *ts)
#ifndef CONFIG_SCHED_TICKLESS_ALARM
void nxsched_timer_expiration(void)
{
clock_t ticks;
clock_t elapsed;
clock_t nexttime;
irqstate_t flags;
/* If we are running on a single CPU architecture, then we know interrupts
* are disabled and there is no need to explicitly call
* enter_critical_section(). However, in the SMP case,
* enter_critical_section() is required prevent multiple cpu to enter
* oneshot_tick_start.
*/
flags = enter_critical_section();
up_timer_gettick(&g_timer_tick);
/* Get the interval associated with last expiration */
elapsed = g_timer_interval;
g_timer_interval = 0;
flags = enter_critical_section();
up_timer_gettick(&ticks);
g_timer_tick = ticks;
elapsed = g_timer_interval;
leave_critical_section(flags);
/* Process the timer ticks and set up the next interval (or not) */
nexttime = nxsched_timer_process(g_timer_tick, elapsed, false);
nxsched_timer_start(nexttime);
nexttime = nxsched_timer_process(ticks, elapsed, false);
flags = enter_critical_section();
g_timer_interval = nxsched_timer_start(ticks, nexttime);
leave_critical_section(flags);
}
#endif
/****************************************************************************
* Name: nxsched_cancel_timer
*
* Description:
* Stop the current timing activity. This is currently called just before
* a new entry is inserted at the head of a timer list and also as part
* of the processing of nxsched_reassess_timer().
*
* This function(1) cancels the current timer, (2) determines how much of
* the interval has elapsed, (3) completes any partially timed events
* (including updating the delay of the timer at the head of the timer
* list), and (2) returns the number of ticks that would be needed to
* resume timing and complete this delay.
*
* Input Parameters:
* None
*
* Returned Value:
* Number of timer ticks that would be needed to complete the delay (zero
* if the timer was not active).
*
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS_ALARM
clock_t nxsched_cancel_timer(void)
{
clock_t ticks;
clock_t elapsed;
/* Cancel the alarm and and get the time that the alarm was cancelled.
* If the alarm was not enabled (or, perhaps, just expired since
* interrupts were disabled), up_timer_cancel() will return the
* current time.
*/
ticks = g_timer_tick;
up_alarm_tick_cancel(&g_timer_tick);
/* Convert this to the elapsed time and update clock tickbase */
elapsed = g_timer_tick - ticks;
/* Process the timer ticks and return the next interval */
return nxsched_timer_process(g_timer_tick, elapsed, true);
}
#else
clock_t nxsched_cancel_timer(void)
{
clock_t ticks;
clock_t elapsed;
/* Get the time remaining on the interval timer and cancel the timer. */
up_timer_tick_cancel(&ticks);
DEBUGASSERT(ticks <= g_timer_interval);
/* Handle the partial timer. This will reassess all timer conditions and
* re-start the interval timer with the correct delay. Context switches
* are not permitted in this case because we are not certain of the
* calling conditions.
*/
elapsed = g_timer_interval - ticks;
g_timer_interval = 0;
g_timer_tick += elapsed;
/* Process the timer ticks and return the next interval */
return nxsched_timer_process(g_timer_tick, elapsed, true);
}
#endif
/****************************************************************************
* Name: nxsched_resume_timer
*
* Description:
* Re-assess the next deadline and restart the interval timer. This is
* called from wd_start() after it has inserted a new delay into the
* timer list.
*
* Input Parameters:
* None
*
* Returned Value:
* None.
*
* Assumptions:
* This function is called right after nxsched_cancel_timer(). If
* CONFIG_SCHED_TICKLESS_ALARM=y, then g_timer_tick must be the
* value time when the timer was cancelled.
*
****************************************************************************/
void nxsched_resume_timer(void)
{
clock_t nexttime;
/* Reassess the next deadline (by simply processing a zero ticks expired)
* and set up the next interval (or not).
*/
nexttime = nxsched_timer_process(g_timer_tick, 0, true);
nxsched_timer_start(nexttime);
}
/****************************************************************************
* Name: nxsched_reassess_timer
*
@ -725,20 +553,51 @@ void nxsched_resume_timer(void)
* Returned Value:
* None
*
* Note:
* This function is called from the critical section
*
****************************************************************************/
void nxsched_reassess_timer(void)
{
clock_t nexttime;
clock_t ticks;
clock_t elapsed;
#ifndef CONFIG_SCHED_TICKLESS_ALARM
up_timer_gettick(&g_timer_tick);
#ifdef CONFIG_SCHED_TICKLESS_ALARM
/* Cancel the alarm and get the current time */
up_alarm_tick_cancel(&ticks);
/* Convert this to the elapsed time and update clock tickbase */
elapsed = ticks - g_timer_tick;
g_timer_tick = ticks;
#else
/* Cancel the timer and get the current time */
up_timer_gettick(&ticks);
up_timer_tick_cancel(&elapsed);
DEBUGASSERT(elapsed <= g_timer_interval);
/* Handle the partial timer. This will reassess all timer conditions and
* re-start the interval timer with the correct delay. Context switches
* are not permitted in this case because we are not certain of the
* calling conditions.
*/
ticks += g_timer_interval - elapsed;
g_timer_tick = ticks;
#endif
/* Cancel and restart the timer */
/* Process the timer ticks and start next timer */
nexttime = nxsched_cancel_timer();
nxsched_timer_start(nexttime);
nexttime = nxsched_timer_process(ticks, elapsed, true);
elapsed = nxsched_timer_start(ticks, nexttime);
#ifndef CONFIG_SCHED_TICKLESS_ALARM
g_timer_interval = elapsed;
#endif
}
#endif /* CONFIG_SCHED_TICKLESS */

View file

@ -99,8 +99,11 @@ static unsigned int g_wdtimernested;
static inline_function void wd_expiration(clock_t ticks)
{
FAR struct wdog_s *wdog;
irqstate_t flags;
wdentry_t func;
flags = enter_critical_section();
#ifdef CONFIG_SCHED_TICKLESS
/* Increment the nested watchdog timer count to handle cases where wd_start
* is called in the watchdog callback functions.
@ -144,6 +147,8 @@ static inline_function void wd_expiration(clock_t ticks)
g_wdtimernested--;
#endif
leave_critical_section(flags);
}
/****************************************************************************
@ -380,6 +385,7 @@ int wd_start(FAR struct wdog_s *wdog, sclock_t delay,
clock_t wd_timer(clock_t ticks, bool noswitches)
{
FAR struct wdog_s *wdog;
irqstate_t flags;
sclock_t ret;
/* Check if the watchdog at the head of the list is ready to run */
@ -389,10 +395,13 @@ clock_t wd_timer(clock_t ticks, bool noswitches)
wd_expiration(ticks);
}
flags = enter_critical_section();
/* Return the delay for the next watchdog to expire */
if (list_is_empty(&g_wdactivelist))
{
leave_critical_section(flags);
return CLOCK_MAX;
}
@ -403,6 +412,8 @@ clock_t wd_timer(clock_t ticks, bool noswitches)
wdog = list_first_entry(&g_wdactivelist, struct wdog_s, node);
ret = wdog->expired - ticks;
leave_critical_section(flags);
/* Return the delay for the next watchdog to expire */
return MAX(ret, 1);