macppc, powerpc: retrigger deferred DEC interrupts from splx(9)
authorcheloha <cheloha@openbsd.org>
Sun, 24 Jul 2022 00:28:09 +0000 (00:28 +0000)
committercheloha <cheloha@openbsd.org>
Sun, 24 Jul 2022 00:28:09 +0000 (00:28 +0000)
On PowerPC, by design, you cannot mask decrementer (DEC) interrupts
without also masking other interrupts that we want to leave unmasked
at or above IPL_CLOCK.  So, currently, the DEC is left unmasked, even
when we're working at IPL_CLOCK or IPL_HIGH.  If a DEC interrupt
arrives while we're at those priority levels, the current solution is
to postpone any clock interrupt work until the next hardclock(9) or
statclock tick.

This is a problem for a machine-independent clock interrupt subsystem
because the MD code, e.g. decr_intr(), ideally shouldn't need to know
anything about when the next event is scheduled to occur.

The most obvious solution to this problem that I can think of is to
instead postpone clock interrupt work until the next time our priority
level drops below IPL_CLOCK.  This is something we can do from the MD
code without any knowledge of when the next clock interrupt event is
scheduled to occur.

So:

- Add a new boolean, ci_dec_deferred, to the PowerPC cpu_info struct.

- If we reach decr_intr() when the CPU's priority level is too high,
  set ci_dec_deferred, clear the DEC exception, and return.

- If we reach decr_intr() and the CPU's priority level is low enough,
  clear ci_dec_deferred and do any needed clock interrupt work.

- In splx(9) (there are three different versions we need to update),
  check ci_dec_deferred.  If it's set and our priority level is
  dropping below IPL_CLOCK, raise a DEC exception.

Tested by me on PowerMac7,3 (openpic).  Tested by miod@ on PowerMac1,1
(macintr) (`make build` completes).  Tested by gkoehler@ on an unknown
PowerMac (probably openpic).

With lots of help from kettenis@.

ok gkoehler@ miod@

sys/arch/macppc/dev/macintr.c
sys/arch/macppc/dev/openpic.c
sys/arch/macppc/macppc/clock.c
sys/arch/powerpc/include/cpu.h
sys/arch/powerpc/powerpc/intr.c

index 87dae89..123a3f3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: macintr.c,v 1.56 2022/03/13 12:33:01 mpi Exp $        */
+/*     $OpenBSD: macintr.c,v 1.57 2022/07/24 00:28:09 cheloha Exp $    */
 
 /*-
  * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org>
@@ -170,6 +170,10 @@ macintr_splx(int newcpl)
 
        intr = ppc_intr_disable();
        macintr_setipl(newcpl);
+       if (ci->ci_dec_deferred && newcpl < IPL_CLOCK) {
+               ppc_mtdec(0);
+               ppc_mtdec(UINT32_MAX);  /* raise DEC exception */
+       }
        if ((newcpl < IPL_SOFTTTY && ci->ci_ipending & ppc_smask[newcpl])) {
                s = splsofttty();
                dosoftint(newcpl);
index ae6931f..35fdf7c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: openpic.c,v 1.89 2022/02/21 10:38:50 jsg Exp $        */
+/*     $OpenBSD: openpic.c,v 1.90 2022/07/24 00:28:09 cheloha Exp $    */
 
 /*-
  * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org>
@@ -382,6 +382,10 @@ openpic_splx(int newcpl)
 
        intr = ppc_intr_disable();
        openpic_setipl(newcpl);
+       if (ci->ci_dec_deferred && newcpl < IPL_CLOCK) {
+               ppc_mtdec(0);
+               ppc_mtdec(UINT32_MAX);  /* raise DEC exception */
+       }
        if (newcpl < IPL_SOFTTTY && (ci->ci_ipending & ppc_smask[newcpl])) {
                s = splsofttty();
                dosoftint(newcpl);
index a24e7f9..1442860 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: clock.c,v 1.48 2021/02/23 04:44:30 cheloha Exp $      */
+/*     $OpenBSD: clock.c,v 1.49 2022/07/24 00:28:09 cheloha Exp $      */
 /*     $NetBSD: clock.c,v 1.1 1996/09/30 16:34:40 ws Exp $     */
 
 /*
@@ -127,6 +127,19 @@ decr_intr(struct clockframe *frame)
        if (!ticks_per_intr)
                return;
 
+       /*
+        * We can't actually mask DEC interrupts at or above IPL_CLOCK
+        * without masking other essential interrupts.  To simulate
+        * masking, we retrigger the DEC by hand from splx(9) the next
+        * time our IPL drops below IPL_CLOCK.
+        */
+       if (ci->ci_cpl >= IPL_CLOCK) {
+               ci->ci_dec_deferred = 1;
+               ppc_mtdec(UINT32_MAX >> 1);     /* clear DEC exception */
+               return;
+       }
+       ci->ci_dec_deferred = 0;
+
        /*
         * Based on the actual time delay since the last decrementer reload,
         * we arrange for earlier interrupt next time.
@@ -160,39 +173,35 @@ decr_intr(struct clockframe *frame)
         */
        ppc_mtdec(nextevent - tb);
 
-       if (ci->ci_cpl >= IPL_CLOCK) {
-               ci->ci_statspending += nstats;
-       } else {
-               nstats += ci->ci_statspending;
-               ci->ci_statspending = 0;
-
-               s = splclock();
-
-               /*
-                * Reenable interrupts
-                */
-               ppc_intr_enable(1);
-
-               /*
-                * Do standard timer interrupt stuff.
-                */
-               while (ci->ci_lasttb < ci->ci_prevtb) {
-                       /* sync lasttb with hardclock */
-                       ci->ci_lasttb += ticks_per_intr;
-                       clk_count.ec_count++;
-                       hardclock(frame);
-               }
+       nstats += ci->ci_statspending;
+       ci->ci_statspending = 0;
 
-               while (nstats-- > 0)
-                       statclock(frame);
+       s = splclock();
 
-               splx(s);
-               (void) ppc_intr_disable();
+       /*
+        * Reenable interrupts
+        */
+       ppc_intr_enable(1);
 
-               /* if a tick has occurred while dealing with these,
-                * dont service it now, delay until the next tick.
-                */
+       /*
+        * Do standard timer interrupt stuff.
+        */
+       while (ci->ci_lasttb < ci->ci_prevtb) {
+               /* sync lasttb with hardclock */
+               ci->ci_lasttb += ticks_per_intr;
+               clk_count.ec_count++;
+               hardclock(frame);
        }
+
+       while (nstats-- > 0)
+               statclock(frame);
+
+       splx(s);
+       (void) ppc_intr_disable();
+
+       /* if a tick has occurred while dealing with these,
+        * dont service it now, delay until the next tick.
+        */
 }
 
 void cpu_startclock(void);
index 727cd20..86630d5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cpu.h,v 1.71 2022/02/10 05:48:02 gkoehler Exp $       */
+/*     $OpenBSD: cpu.h,v 1.72 2022/07/24 00:28:09 cheloha Exp $        */
 /*     $NetBSD: cpu.h,v 1.1 1996/09/30 16:34:21 ws Exp $       */
 
 /*
@@ -55,6 +55,7 @@ struct cpu_info {
        volatile int ci_want_resched;
        volatile int ci_cpl;
        volatile int ci_ipending;
+       volatile int ci_dec_deferred;
 
        volatile int    ci_flags;
 #define        CI_FLAGS_SLEEPING               2
index 8471825..4b5195b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: intr.c,v 1.9 2015/09/13 14:06:40 kettenis Exp $       */
+/*     $OpenBSD: intr.c,v 1.10 2022/07/24 00:28:09 cheloha Exp $       */
 
 /*
  * Copyright (c) 1997 Per Fogelstrom, Opsycon AB and RTMX Inc, USA.
@@ -120,6 +120,11 @@ ppc_dflt_splx(int newcpl)
 
         ci->ci_cpl = newcpl;
 
+       if (ci->ci_dec_deferred && newcpl < IPL_CLOCK) {
+               ppc_mtdec(0);
+               ppc_mtdec(UINT32_MAX);  /* raise DEC exception */
+       }
+
         if (ci->ci_ipending & ppc_smask[newcpl])
                dosoftint(newcpl);
 }