On Sun, Nov 06, 2022 at 07:46:37PM +0000, Scott Cheloha wrote:
> This patch switches i386 to clockintr(9).
>
> I have tested this on my Lenovo X1 Carbon 6th and Dell Optiplex 7070
> running in 32-bit compatibility mode. It has survived ~20 parallel
> release builds and upgrades from the resulting bsd.rd.
>
> mlarkin@ has tested this in an ESXi VM and reports that ACPI hibernate
> works.
>
> This needs additional testing. A couple things have not been
> confirmed to work yet:
>
> - Accelerated graphics
>
> - ACPI suspend and hibernate on real hardware
>
> - APM suspend and hibernate
>
> - Real 586- and 686-class hardware
>
> - Real machines without a lapic
>
> Notes:
>
> - i386 machines with a lapic now have a randomized statclock().
>
> - This includes a preliminary patch that disables/enables hpet_delay()
> across suspend/resume. It will be committed separately.
>
> - In i8254-mode, profhz = 1024 does not divide evenly into one billion.
> We could circumvent this problem by running the RTC at 512hz when
> profiling is enabled.
>
> - The same behavior change described in the amd64 patch:
>
> https://marc.info/?l=openbsd-tech&m=166776339203279&w=2
>
> applies to i386:
>
> > If we're using the i8254 as our interrupt clock, there is
> > a small behavior change.
> >
> > In i8254-mode, there are two interrupt clocks, the i8254 and mc146818.
> >
> > Currently, the i8254 runs hardclock() and the mc146818 runs statclock().
> > With this patch, neither interrupt handler has a monopoly on which events
> > are dispatched anymore. So if hardclock() is due, the mc146818 handler
> > will dispatch it. The i8254 might dispatch statclock(), too.
> >
> > Preserving the existing behavior would require per-intrclock event
> > queues. That is, the i8254 would have its own work schedule
> > maintained separately from that of the mc146818. I pitched this idea
> > to kettenis@ and he said we probably didn't need it. I agree with
> > him. Of course, if there is interest in this feature we could explore
> > it.
The HPET pieces have been committed separately. Here is an updated
patch.
Index: sys/arch/i386/i386/acpi_machdep.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/acpi_machdep.c,v
retrieving revision 1.85
diff -u -p -r1.85 acpi_machdep.c
--- sys/arch/i386/i386/acpi_machdep.c 21 Feb 2022 10:24:28 -0000 1.85
+++ sys/arch/i386/i386/acpi_machdep.c 9 Nov 2022 19:07:10 -0000
@@ -421,8 +421,6 @@ acpi_resume_cpu(struct acpi_softc *sc, i
#if NLAPIC > 0
lapic_tpr = save_lapic_tpr;
lapic_enable();
- if (initclock_func == lapic_initclocks)
- lapic_startclock();
lapic_set_lvt();
#endif
Index: sys/arch/i386/i386/apm.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/apm.c,v
retrieving revision 1.127
diff -u -p -r1.127 apm.c
--- sys/arch/i386/i386/apm.c 21 Feb 2022 10:24:28 -0000 1.127
+++ sys/arch/i386/i386/apm.c 9 Nov 2022 19:07:11 -0000
@@ -44,6 +44,7 @@
#include <sys/rwlock.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
+#include <sys/clockintr.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
@@ -271,6 +272,11 @@ apm_suspend(int state)
if (initclock_func == i8254_initclocks)
rtcstart(); /* in i8254 mode, rtc is profclock */
inittodr(gettime());
+
+#ifdef __HAVE_CLOCKINTR
+ clockintr_cpu_init(NULL);
+ clockintr_trigger();
+#endif
config_suspend_all(DVACT_RESUME);
cold = 0;
Index: sys/arch/i386/i386/cpu.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/cpu.c,v
retrieving revision 1.109
diff -u -p -r1.109 cpu.c
--- sys/arch/i386/i386/cpu.c 15 Aug 2022 04:17:50 -0000 1.109
+++ sys/arch/i386/i386/cpu.c 9 Nov 2022 19:07:11 -0000
@@ -701,7 +701,6 @@ cpu_hatch(void *v)
cpu_init_idt();
lapic_enable();
- lapic_startclock();
lapic_set_lvt();
gdt_init_cpu(ci);
@@ -727,6 +726,8 @@ cpu_hatch(void *v)
ci->ci_dev->dv_xname, ci->ci_cpuid);
nanouptime(&ci->ci_schedstate.spc_runtime);
splx(s);
+
+ lapic_startclock();
SCHED_LOCK(s);
cpu_switchto(NULL, sched_chooseproc());
Index: sys/arch/i386/i386/lapic.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/lapic.c,v
retrieving revision 1.52
diff -u -p -r1.52 lapic.c
--- sys/arch/i386/i386/lapic.c 10 Sep 2022 01:30:14 -0000 1.52
+++ sys/arch/i386/i386/lapic.c 9 Nov 2022 19:07:11 -0000
@@ -34,7 +34,9 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/clockintr.h>
#include <sys/device.h>
+#include <sys/stdint.h>
#include <uvm/uvm_extern.h>
@@ -68,7 +70,6 @@ struct evcount clk_count;
struct evcount ipi_count;
#endif
-void lapic_delay(int);
static u_int32_t lapic_gettick(void);
void lapic_clockintr(void *);
void lapic_initclocks(void);
@@ -239,19 +240,43 @@ lapic_gettick(void)
#include <sys/kernel.h> /* for hz */
-u_int32_t lapic_tval;
-
/*
* this gets us up to a 4GHz busclock....
*/
u_int32_t lapic_per_second = 0;
-u_int32_t lapic_frac_usec_per_cycle;
-u_int64_t lapic_frac_cycle_per_usec;
-u_int32_t lapic_delaytab[26];
+uint64_t lapic_timer_nsec_cycle_ratio;
+uint64_t lapic_timer_nsec_max;
+
+void lapic_timer_rearm(void *, uint64_t);
+void lapic_timer_trigger(void *);
+
+struct intrclock lapic_timer_intrclock = {
+ .ic_rearm = lapic_timer_rearm,
+ .ic_trigger = lapic_timer_trigger
+};
void lapic_timer_oneshot(uint32_t, uint32_t);
void lapic_timer_periodic(uint32_t, uint32_t);
+void
+lapic_timer_rearm(void *unused, uint64_t nsecs)
+{
+ uint32_t cycles;
+
+ if (nsecs > lapic_timer_nsec_max)
+ nsecs = lapic_timer_nsec_max;
+ cycles = (nsecs * lapic_timer_nsec_cycle_ratio) >> 32;
+ if (cycles == 0)
+ cycles = 1;
+ lapic_timer_oneshot(0, cycles);
+}
+
+void
+lapic_timer_trigger(void *unused)
+{
+ lapic_timer_oneshot(0, 1);
+}
+
/*
* Start the local apic countdown timer.
*
@@ -280,25 +305,28 @@ lapic_timer_periodic(uint32_t mask, uint
}
void
-lapic_clockintr(void *arg)
+lapic_clockintr(void *frame)
{
- struct clockframe *frame = arg;
-
- hardclock(frame);
-
+ clockintr_dispatch(frame);
clk_count.ec_count++;
}
void
lapic_startclock(void)
{
- lapic_timer_periodic(0, lapic_tval);
+ clockintr_cpu_init(&lapic_timer_intrclock);
+ clockintr_trigger();
}
void
lapic_initclocks(void)
{
i8254_inittimecounter_simple();
+
+ stathz = hz;
+ profhz = stathz * 10;
+ clockintr_init(CL_RNDSTAT);
+
lapic_startclock();
}
@@ -385,74 +413,14 @@ lapic_calibrate_timer(struct cpu_info *c
printf("%s: apic clock running at %dMHz\n",
ci->ci_dev->dv_xname, lapic_per_second / (1000 * 1000));
- if (lapic_per_second != 0) {
- /*
- * reprogram the apic timer to run in periodic mode.
- * XXX need to program timer on other cpu's, too.
- */
- lapic_tval = (lapic_per_second * 2) / hz;
- lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
-
- lapic_timer_periodic(LAPIC_LVTT_M, lapic_tval);
-
- /*
- * Compute fixed-point ratios between cycles and
- * microseconds to avoid having to do any division
- * in lapic_delay.
- */
-
- tmp = (1000000 * (u_int64_t)1 << 32) / lapic_per_second;
- lapic_frac_usec_per_cycle = tmp;
-
- tmp = (lapic_per_second * (u_int64_t)1 << 32) / 1000000;
-
- lapic_frac_cycle_per_usec = tmp;
-
- /*
- * Compute delay in cycles for likely short delays in usec.
- */
- for (i = 0; i < 26; i++)
- lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
- 32;
-
- /*
- * Now that the timer's calibrated, use the apic timer routines
- * for all our timing needs..
- */
- delay_init(lapic_delay, 3000);
- initclock_func = lapic_initclocks;
- }
-}
-
-/*
- * delay for N usec.
- */
-
-void
-lapic_delay(int usec)
-{
- int32_t tick, otick;
- int64_t deltat; /* XXX may want to be 64bit */
-
- otick = lapic_gettick();
-
- if (usec <= 0)
+ /* XXX What should we do here if the timer frequency is zero? */
+ if (lapic_per_second == 0)
return;
- if (usec <= 25)
- deltat = lapic_delaytab[usec];
- else
- deltat = (lapic_frac_cycle_per_usec * usec) >> 32;
-
- while (deltat > 0) {
- tick = lapic_gettick();
- if (tick > otick)
- deltat -= lapic_tval - (tick - otick);
- else
- deltat -= otick - tick;
- otick = tick;
- CPU_BUSY_CYCLE();
- }
+ lapic_timer_nsec_cycle_ratio =
+ lapic_per_second * (1ULL << 32) / 1000000000;
+ lapic_timer_nsec_max = UINT64_MAX / lapic_timer_nsec_cycle_ratio;
+ initclock_func = lapic_initclocks;
}
/*
Index: sys/arch/i386/isa/clock.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/isa/clock.c,v
retrieving revision 1.61
diff -u -p -r1.61 clock.c
--- sys/arch/i386/isa/clock.c 1 Nov 2022 13:59:00 -0000 1.61
+++ sys/arch/i386/isa/clock.c 9 Nov 2022 19:07:11 -0000
@@ -89,7 +89,9 @@ WITH THE USE OR PERFORMANCE OF THIS SOFT
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
+#include <sys/clockintr.h>
#include <sys/device.h>
+#include <sys/stdint.h>
#include <sys/timeout.h>
#include <sys/timetc.h>
#include <sys/mutex.h>
@@ -202,10 +204,8 @@ rtcdrain(void *v)
}
int
-clockintr(void *arg)
+clockintr(void *frame)
{
- struct clockframe *frame = arg; /* not strictly necessary */
-
if (timecounter->tc_get_timecount == i8254_get_timecount) {
if (i8254_ticked) {
i8254_ticked = 0;
@@ -214,33 +214,26 @@ clockintr(void *arg)
i8254_lastcount = 0;
}
}
-
- hardclock(frame);
+ clockintr_dispatch(frame);
return (1);
}
int
-rtcintr(void *arg)
+rtcintr(void *frame)
{
- struct clockframe *frame = arg; /* not strictly necessary */
u_int stat = 0;
- if (stathz == 0) {
- extern int psratio;
-
- stathz = 128;
- profhz = 1024;
- psratio = profhz / stathz;
- }
-
/*
* If rtcintr is 'late', next intr may happen immediately.
* Get them all. (Also, see comment in cpu_initclocks().)
*/
- while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
- statclock(frame);
+ while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
stat = 1;
- }
+
+ /* XXX Can rtcintr() run before i8254_initclocks() is complete? */
+ if (stathz != 0 && stat)
+ clockintr_dispatch(frame);
+
return (stat);
}
@@ -433,6 +426,14 @@ calibrate_cyclecounter(void)
void
i8254_initclocks(void)
{
+ i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */
+
+ stathz = 128;
+ profhz = 1024; /* XXX does not divide into 1 billion */
+ clockintr_init(0);
+
+ clockintr_cpu_init(NULL);
+
/* When using i8254 for clock, we also use the rtc for profclock */
(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK,
clockintr, 0, "clock");
@@ -440,8 +441,6 @@ i8254_initclocks(void)
rtcintr, 0, "rtc");
rtcstart(); /* start the mc146818 clock */
-
- i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */
}
void
@@ -668,6 +667,7 @@ setstatclockrate(int arg)
mc146818_write(NULL, MC_REGA,
MC_BASE_32_KHz | MC_RATE_1024_Hz);
}
+ clockintr_setstatclockrate(arg);
}
void
Index: sys/arch/i386/include/cpu.h
===================================================================
RCS file: /cvs/src/sys/arch/i386/include/cpu.h,v
retrieving revision 1.180
diff -u -p -r1.180 cpu.h
--- sys/arch/i386/include/cpu.h 8 Nov 2022 14:49:20 -0000 1.180
+++ sys/arch/i386/include/cpu.h 9 Nov 2022 19:07:11 -0000
@@ -64,6 +64,7 @@
*/
#define clockframe intrframe
+#include <sys/clockintr.h>
#include <sys/device.h>
#include <sys/sched.h>
#include <sys/sensors.h>
@@ -168,6 +169,7 @@ struct cpu_info {
#if defined(GPROF) || defined(DDBPROF)
struct gmonparam *ci_gmon;
#endif
+ struct clockintr_queue ci_queue;
char ci_panicbuf[512];
};
Index: sys/arch/i386/include/_types.h
===================================================================
RCS file: /cvs/src/sys/arch/i386/include/_types.h,v
retrieving revision 1.23
diff -u -p -r1.23 _types.h
--- sys/arch/i386/include/_types.h 5 Mar 2018 01:15:25 -0000 1.23
+++ sys/arch/i386/include/_types.h 9 Nov 2022 19:07:11 -0000
@@ -35,6 +35,8 @@
#ifndef _MACHINE__TYPES_H_
#define _MACHINE__TYPES_H_
+#define __HAVE_CLOCKINTR
+
/*
* _ALIGN(p) rounds p (pointer or byte index) up to a correctly-aligned
* value for all data types (int, long, ...). The result is an