You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2024/09/10 15:32:45 UTC

(nuttx) 08/15: sched: disable interrupt in non-SMP case

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit f23fb7ec0963d84282a4eed6e9efc64ef66a1522
Author: ligd <li...@xiaomi.com>
AuthorDate: Wed Jul 10 22:13:26 2024 +0800

    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 <li...@xiaomi.com>
---
 sched/sched/sched.h                 |   4 -
 sched/sched/sched_processtimer.c    |  54 +-------
 sched/sched/sched_timerexpiration.c | 267 +++++++++---------------------------
 sched/wdog/wd_start.c               |  11 ++
 4 files changed, 81 insertions(+), 255 deletions(-)

diff --git a/sched/sched/sched.h b/sched/sched/sched.h
index 2a4bdf5ade..cfe8e44ca5 100644
--- a/sched/sched/sched.h
+++ b/sched/sched/sched.h
@@ -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
 
diff --git a/sched/sched/sched_processtimer.c b/sched/sched/sched_processtimer.c
index a9abe2ecce..c2fb98b8cc 100644
--- a/sched/sched/sched_processtimer.c
+++ b/sched/sched/sched_processtimer.c
@@ -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,
diff --git a/sched/sched/sched_timerexpiration.c b/sched/sched/sched_timerexpiration.c
index 5bbcfae16b..16ea26e616 100644
--- a/sched/sched/sched_timerexpiration.c
+++ b/sched/sched/sched_timerexpiration.c
@@ -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.
-   */
+  /* Save the time that the alarm occurred */
 
   flags = enter_critical_section();
-#endif
-
-  /* Calculate elapsed */
-
   elapsed = ticks - g_timer_tick;
-
-  /* Save the time that the alarm occurred */
-
   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;
-
-  /* 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);
+  flags = enter_critical_section();
+  up_timer_gettick(&ticks);
+  g_timer_tick = ticks;
+  elapsed = g_timer_interval;
   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 set up the next interval (or not) */
 
-  /* Process the timer ticks and return the next interval */
+  nexttime = nxsched_timer_process(ticks, elapsed, false);
 
-  return nxsched_timer_process(g_timer_tick, elapsed, true);
+  flags = enter_critical_section();
+  g_timer_interval = nxsched_timer_start(ticks, nexttime);
+  leave_critical_section(flags);
 }
 #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_timer_process(ticks, elapsed, true);
+  elapsed = nxsched_timer_start(ticks, nexttime);
 
-  nexttime = nxsched_cancel_timer();
-  nxsched_timer_start(nexttime);
+#ifndef CONFIG_SCHED_TICKLESS_ALARM
+  g_timer_interval = elapsed;
+#endif
 }
 
 #endif /* CONFIG_SCHED_TICKLESS */
diff --git a/sched/wdog/wd_start.c b/sched/wdog/wd_start.c
index 4fa0996dc3..911a86e2b7 100644
--- a/sched/wdog/wd_start.c
+++ b/sched/wdog/wd_start.c
@@ -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);