AMD64 Local APIC Timer
Takeharu KATO
takeharu1219 at ybb.ne.jp
Tue Feb 22 16:30:59 GMT 2005
Hi
I found my bug in the patch which I sent before.
I re-post the local-apic-timer patch for AMD64.
Takeharu KATO wrote:
> Hi
>
> I ported the local APIC timer tick feature to AMD64.
> Please take a look on this patch.
>
> Regards,
>
>
--
Takeharu KATO
-------------- next part --------------
Index: amd64/amd64/apic_vector.S
===================================================================
RCS file: /home/kato/cvs/kato-sys/amd64/amd64/apic_vector.S,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- amd64/amd64/apic_vector.S 18 Feb 2005 14:05:55 -0000 1.1.1.1
+++ amd64/amd64/apic_vector.S 20 Feb 2005 18:15:29 -0000 1.2
@@ -137,6 +137,26 @@
ISR_VEC(6, apic_isr6)
ISR_VEC(7, apic_isr7)
+/*
+ * Local APIC periodic timer handler.
+ */
+ .text
+ SUPERALIGN_TEXT
+IDTVEC(timerint)
+ PUSH_FRAME
+
+ movq lapic, %rdx
+ movl $0, LA_EOI(%rdx) /* End Of Interrupt to APIC */
+
+ FAKE_MCOUNT(TF_RIP(%rsp))
+
+
+ pushq $0 /* XXX convert trapframe to clockframe */
+ call lapic_handle_timer
+ addq $8, %rsp /* XXX convert clockframe to trapframe */
+ MEXITCOUNT
+ jmp doreti
+
#ifdef SMP
/*
* Global address space TLB shootdown.
Index: amd64/amd64/local_apic.c
===================================================================
RCS file: /home/kato/cvs/kato-sys/amd64/amd64/local_apic.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 local_apic.c
--- amd64/amd64/local_apic.c 18 Feb 2005 14:05:55 -0000 1.1.1.1
+++ amd64/amd64/local_apic.c 22 Feb 2005 16:16:33 -0000
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2005 Takeharu KATO
+ * (Add LAPIC timer support).
* Copyright (c) 2003 John Baldwin <jhb at FreeBSD.org>
* Copyright (c) 1996, by Steve Passe
* All rights reserved.
@@ -66,6 +68,9 @@
CTASSERT(APIC_LOCAL_INTS == 240);
CTASSERT(IPI_STOP < APIC_SPURIOUS_INT);
+#define LAPIC_TIMER_STATHZ 128
+#define LAPIC_TIMER_PROFHZ 1024
+
/*
* Support for local APICs. Local APICs manage interrupts on each
* individual processor as opposed to I/O APICs which receive interrupts
@@ -90,6 +95,9 @@
u_int la_cluster:4;
u_int la_cluster_id:2;
u_int la_present:1;
+ u_long *la_timer_count;
+ u_long la_stat_ticks;
+ u_long la_prof_ticks;
} static lapics[MAX_APICID];
/* XXX: should thermal be an NMI? */
@@ -115,9 +123,23 @@
IDTVEC(apic_isr7), /* 224 - 255 */
};
+static u_int32_t lapic_timer_divisors[] = {
+ APIC_TDCR_1, APIC_TDCR_2, APIC_TDCR_4, APIC_TDCR_8, APIC_TDCR_16,
+ APIC_TDCR_32, APIC_TDCR_64, APIC_TDCR_128
+};
+
+
volatile lapic_t *lapic;
+static u_long lapic_timer_divisor, lapic_timer_period;
+static u_long *lapic_virtual_hardclock, *lapic_virtual_statclock,
+ *lapic_virtual_profclock;
static void lapic_enable(void);
+static void lapic_timer_enable_intr(void);
+static u_long calculate_lapic_timer_period(void);
+static void lapic_timer_oneshot(u_int count);
+static void lapic_timer_periodic(u_int count);
+static void lapic_timer_set_divisor(u_int divisor);
static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value);
static uint32_t
@@ -181,6 +203,7 @@
PCPU_SET(apic_id, lapic_id());
/* XXX: timer/error/thermal interrupts */
+ setidt(APIC_TIMER_INT, IDTVEC(timerint), SDT_SYSIGT, SEL_KPL,0);
}
/*
@@ -244,13 +267,56 @@
("No ISR handler for IRQ %u", irq));
setidt(vector, ioint_handlers[vector / 32], SDT_SYSIGT, SEL_KPL, 0);
}
+static u_long
+calculate_lapic_timer_period(void)
+{
+ u_long period,value;
+
+ /* Start off with a divisor of 2 (power on reset default). */
+ lapic_timer_divisor = 8;
+
+ /* Try to calibrate the local APIC timer. */
+ do {
+ printf("lapic timer divisor:%lu\n",lapic_timer_divisor);
+ lapic_timer_set_divisor(lapic_timer_divisor);
+ lapic_timer_oneshot(APIC_TIMER_MAX_COUNT);
+ DELAY(2000000);
+ value = APIC_TIMER_MAX_COUNT - lapic->ccr_timer;
+ printf("value:%lu(ccr:%u)\n",value,lapic->ccr_timer);
+ if (value != APIC_TIMER_MAX_COUNT)
+ break;
+ lapic_timer_divisor <<= 1;
+ } while (lapic_timer_divisor <= 128);
+ if (lapic_timer_divisor > 128)
+ panic("lapic: Divisor too big");
+ value /= 2;
+ printf("lapic: Frequency %lu hz\n", value);
+ /*
+ * We will drive the timer via hz. Require hz to be greater than
+ * stathz, but if hz is less than the default profhz, cap profhz
+ * at hz.
+ */
+ stathz = LAPIC_TIMER_STATHZ;
+ if (hz < stathz) {
+ printf("lapic: Adjusting hz from %d to %d\n", hz, stathz);
+ hz = stathz;
+ }
+ period=value / hz;
+ KASSERT(period!=0, ("CPU:%d lapic%u: zero divisor",PCPU_GET(cpuid),lapic_id()));
+#if 0 /* Please enable following lines if you want to show period/divisor */
+ printf("Setup CPU:%d period:%lu val=%lu\n",PCPU_GET(cpuid),lapic_timer_period,value);
+ printf("Setup CPU:%d div=%lu\n",PCPU_GET(cpuid),lapic_timer_divisor);
+#endif
+ return period;
+}
void
lapic_setup(void)
{
struct lapic *la;
u_int32_t value, maxlvt;
register_t eflags;
+ char buf[MAXCOMLEN + 1];
la = &lapics[lapic_id()];
KASSERT(la->la_present, ("missing APIC structure"));
@@ -281,9 +347,47 @@
lapic->lvt_lint1 = lvt_mode(la, LVT_LINT1, lapic->lvt_lint1);
/* XXX: more LVT entries */
+ /* Program timer LVT and setup handler. */
+ lapic->lvt_timer = lvt_mode(la, LVT_TIMER, lapic->lvt_timer);
+ snprintf(buf, sizeof(buf), "lapic%d: timer", lapic_id());
+ intrcnt_add(buf, &la->la_timer_count);
+ if (PCPU_GET(cpuid) != 0) {
+ lapic_timer_period=calculate_lapic_timer_period();
+ lapic_timer_set_divisor(lapic_timer_divisor);
+ lapic_timer_periodic(lapic_timer_period);
+ lapic_timer_enable_intr();
+ }
intr_restore(eflags);
}
+/*
+ * Called by cpu_initclocks() on the BSP to setup the local APIC timer so
+ * that it can drive hardclock, statclock, and profclock. This function
+ * returns true if it is able to use the local APIC timer to drive the
+ * clocks and false if it is not able.
+ */
+int
+lapic_setup_clock(void)
+{
+ /* Can't drive the timer without a local APIC. */
+ if (lapic == NULL)
+ return (0);
+
+ lapic_timer_period = calculate_lapic_timer_period();
+ profhz = imin(hz, LAPIC_TIMER_PROFHZ);
+ intrcnt_add("lapic: hardclock", &lapic_virtual_hardclock);
+ intrcnt_add("lapic: statclock", &lapic_virtual_statclock);
+ intrcnt_add("lapic: profclock", &lapic_virtual_profclock);
+
+ /*
+ * Start up the timer on the BSP. The APs will kick off their
+ * timer during lapic_setup().
+ */
+ lapic_timer_periodic(lapic_timer_period);
+ lapic_timer_enable_intr();
+ return (1);
+}
+
void
lapic_disable(void)
@@ -515,6 +619,87 @@
isrc = intr_lookup_source(apic_idt_to_irq(vec));
intr_execute_handlers(isrc, &frame);
}
+void
+lapic_handle_timer(struct clockframe frame)
+{
+ struct lapic *la;
+
+ la = &lapics[PCPU_GET(apic_id)];
+ (*la->la_timer_count)++;
+ critical_enter();
+
+ /* Hardclock fires on every interrupt since we interrupt at hz. */
+ if (PCPU_GET(cpuid) == 0) {
+ (*lapic_virtual_hardclock)++;
+ hardclock(&frame);
+ } else
+ hardclock_process(&frame);
+
+ /* Use a poor man's algorithm to fire statclock at stathz. */
+ la->la_stat_ticks += stathz;
+ if (la->la_stat_ticks >= hz) {
+ la->la_stat_ticks -= hz;
+ if (PCPU_GET(cpuid) == 0)
+ (*lapic_virtual_statclock)++;
+ statclock(&frame);
+ }
+
+ /* Use the same trick for profhz. */
+ la->la_prof_ticks += profhz;
+ if (la->la_prof_ticks >= hz) {
+ la->la_prof_ticks -= hz;
+ if (PCPU_GET(cpuid) == 0)
+ (*lapic_virtual_profclock)++;
+ if (profprocs != 0)
+ profclock(&frame);
+ }
+ critical_exit();
+}
+
+static void
+lapic_timer_set_divisor(u_int divisor)
+{
+
+ KASSERT(powerof2(divisor), ("lapic: invalid divisor %u", divisor));
+ KASSERT(ffs(divisor) <= sizeof(lapic_timer_divisors) /
+ sizeof(u_int32_t), ("lapic: invalid divisor %u", divisor));
+ lapic->dcr_timer = lapic_timer_divisors[ffs(divisor) - 1];
+}
+
+static void
+lapic_timer_oneshot(u_int count)
+{
+ u_int32_t value;
+
+ value = lapic->lvt_timer;
+ value &= ~APIC_LVTT_TM;
+ value |= APIC_LVTT_TM_ONE_SHOT;
+ lapic->lvt_timer = value;
+ lapic->icr_timer = count;
+}
+
+static void
+lapic_timer_periodic(u_int count)
+{
+ u_int32_t value;
+
+ value = lapic->lvt_timer;
+ value &= ~APIC_LVTT_TM;
+ value |= APIC_LVTT_TM_PERIODIC;
+ lapic->lvt_timer = value;
+ lapic->icr_timer = count;
+}
+
+static void
+lapic_timer_enable_intr(void)
+{
+ u_int32_t value;
+
+ value = lapic->lvt_timer;
+ value &= ~APIC_LVT_M;
+ lapic->lvt_timer = value;
+}
+
/* Translate between IDT vectors and IRQ vectors. */
u_int
Index: amd64/amd64/mp_machdep.c
===================================================================
RCS file: /home/kato/cvs/kato-sys/amd64/amd64/mp_machdep.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- amd64/amd64/mp_machdep.c 18 Feb 2005 14:05:55 -0000 1.1.1.1
+++ amd64/amd64/mp_machdep.c 20 Feb 2005 18:15:29 -0000 1.2
@@ -871,7 +871,7 @@
smp_targeted_tlb_shootdown(mask, IPI_INVLRNG, addr1, addr2);
}
-
+#if 0
/*
* For statclock, we send an IPI to all CPU's to have them call this
* function.
@@ -914,16 +914,16 @@
if (map != 0)
ipi_selected(map, IPI_HARDCLOCK);
}
-
+#endif
void
ipi_bitmap_handler(struct clockframe frame)
{
int cpu = PCPU_GET(cpuid);
u_int ipi_bitmap;
- struct thread *td;
- ipi_bitmap = atomic_readandclear_int(&cpu_ipi_pending[cpu]);
+ ipi_bitmap = atomic_readandclear_int(&cpu_ipi_pending[cpu]);
+#if 0
critical_enter();
/* Nothing to do for AST */
@@ -948,6 +948,7 @@
}
critical_exit();
+#endif
}
/*
Index: amd64/conf/CURRENT-MARS
===================================================================
RCS file: /home/kato/cvs/kato-sys/amd64/conf/CURRENT-MARS,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 CURRENT-MARS
Index: amd64/include/apicvar.h
===================================================================
RCS file: /home/kato/cvs/kato-sys/amd64/include/apicvar.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- amd64/include/apicvar.h 18 Feb 2005 14:05:55 -0000 1.1.1.1
+++ amd64/include/apicvar.h 20 Feb 2005 18:15:33 -0000 1.2
@@ -123,9 +123,12 @@
/* IPIs handled by IPI_BITMAPED_VECTOR (XXX ups is there a better place?) */
#define IPI_AST 0 /* Generate software trap. */
+#if 0
#define IPI_HARDCLOCK 1 /* Inter-CPU clock handling. */
#define IPI_STATCLOCK 2
#define IPI_BITMAP_LAST IPI_STATCLOCK
+#endif
+#define IPI_BITMAP_LAST IPI_AST
#define IPI_IS_BITMAPED(x) ((x) <= IPI_BITMAP_LAST)
#define IPI_STOP (APIC_IPI_INTS + 6) /* Stop CPU until restarted. */
@@ -172,7 +175,7 @@
inthand_t
IDTVEC(apic_isr1), IDTVEC(apic_isr2), IDTVEC(apic_isr3),
IDTVEC(apic_isr4), IDTVEC(apic_isr5), IDTVEC(apic_isr6),
- IDTVEC(apic_isr7), IDTVEC(spuriousint);
+ IDTVEC(apic_isr7), IDTVEC(spuriousint),IDTVEC(timerint);
u_int apic_irq_to_idt(u_int irq);
u_int apic_idt_to_irq(u_int vector);
@@ -203,6 +206,7 @@
void lapic_ipi_vectored(u_int vector, int dest);
int lapic_ipi_wait(int delay);
void lapic_handle_intr(void *cookie, struct intrframe frame);
+void lapic_handle_timer(struct clockframe frame);
void lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id);
int lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked);
int lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode);
@@ -212,6 +216,7 @@
enum intr_trigger trigger);
void lapic_set_tpr(u_int vector);
void lapic_setup(void);
+int lapic_setup_clock(void);
#endif /* !LOCORE */
#endif /* _MACHINE_APICVAR_H_ */
Index: amd64/isa/clock.c
===================================================================
RCS file: /home/kato/cvs/kato-sys/amd64/isa/clock.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 clock.c
--- amd64/isa/clock.c 18 Feb 2005 14:05:55 -0000 1.1.1.1
+++ amd64/isa/clock.c 22 Feb 2005 15:38:24 -0000
@@ -64,13 +64,14 @@
#include <sys/sysctl.h>
#include <sys/cons.h>
#include <sys/power.h>
-
+#define LAPIC_TIMER
#include <machine/clock.h>
#include <machine/frame.h>
#include <machine/intr_machdep.h>
#include <machine/md_var.h>
#include <machine/psl.h>
-#ifdef SMP
+#ifdef LAPIC_TIMER
+#include <machine/apicvar.h>
#include <machine/smp.h>
#endif
#include <machine/specialreg.h>
@@ -113,6 +114,7 @@
static u_int32_t i8254_offset;
static int (*i8254_pending)(struct intsrc *);
static int i8254_ticked;
+static int using_lapic_timer;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
@@ -139,7 +141,6 @@
static void
clkintr(struct clockframe *frame)
{
-
if (timecounter->tc_get_timecount == i8254_get_timecount) {
mtx_lock_spin(&clock_lock);
if (i8254_ticked)
@@ -151,10 +152,8 @@
clkintr_pending = 0;
mtx_unlock_spin(&clock_lock);
}
- hardclock(frame);
-#ifdef SMP
- forward_hardclock();
-#endif
+ if (!using_lapic_timer)
+ hardclock(frame);
}
int
@@ -221,9 +220,6 @@
}
if (pscnt == psdiv)
statclock(frame);
-#ifdef SMP
- forward_statclock();
-#endif
}
}
@@ -730,7 +726,11 @@
{
int diag;
- if (statclock_disable) {
+#ifdef LAPIC_TIMER
+ using_lapic_timer = lapic_setup_clock();
+#endif
+
+ if ( statclock_disable || using_lapic_timer ) {
/*
* The stat interrupt mask is different without the
* statistics clock. Also, don't set the interrupt
@@ -756,7 +756,7 @@
writertc(RTC_STATUSB, RTCSB_24HR);
/* Don't bother enabling the statistics clock. */
- if (!statclock_disable) {
+ if (!statclock_disable && !using_lapic_timer) {
diag = rtcin(RTC_DIAG);
if (diag != 0)
printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
@@ -774,7 +774,8 @@
void
cpu_startprofclock(void)
{
-
+ if (using_lapic_timer)
+ return;
rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF;
writertc(RTC_STATUSA, rtc_statusa);
psdiv = pscnt = psratio;
@@ -783,7 +784,8 @@
void
cpu_stopprofclock(void)
{
-
+ if (using_lapic_timer)
+ return;
rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
writertc(RTC_STATUSA, rtc_statusa);
psdiv = pscnt = 1;
More information about the freebsd-current
mailing list