Hi I ported the local APIC timer tick feature to AMD64. Please take a look on this patch. Regards, -- Takeharu KATO 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 _at__at_ -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 20 Feb 2005 19:43:06 -0000 _at__at_ -1,4 +1,6 _at__at_ /*- + * Copyright (c) 2005 Takeharu KATO<takeharu1219_at_ybb.ne.jp> + * (Add LAPIC timer support). * Copyright (c) 2003 John Baldwin <jhb_at_FreeBSD.org> * Copyright (c) 1996, by Steve Passe * All rights reserved. _at__at_ -66,6 +68,9 _at__at_ 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 _at__at_ -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? */ _at__at_ -115,9 +123,23 _at__at_ 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 _at__at_ -181,6 +203,7 _at__at_ PCPU_SET(apic_id, lapic_id()); /* XXX: timer/error/thermal interrupts */ + setidt(APIC_TIMER_INT, IDTVEC(timerint), SDT_SYSIGT, SEL_KPL,0); } /* _at__at_ -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")); _at__at_ -281,9 +347,47 _at__at_ 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) _at__at_ -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 _at__at_ -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. _at__at_ -914,16 +914,16 _at__at_ 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 */ _at__at_ -948,6 +948,7 _at_@ } 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 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 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 _at__at_ -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. */ _at__at_ -172,7 +175,7 _at__at_ 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); _at__at_ -203,6 +206,7 _at__at_ 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); _at__at_ -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 20 Feb 2005 18:25:50 -0000 _at__at_ -64,13 +64,14 _at__at_ #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> _at__at_ -113,6 +114,7 _at__at_ 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; _at__at_ -139,7 +141,6 _at__at_ static void clkintr(struct clockframe *frame) { - if (timecounter->tc_get_timecount == i8254_get_timecount) { mtx_lock_spin(&clock_lock); if (i8254_ticked) _at__at_ -151,10 +152,8 _at__at_ clkintr_pending = 0; mtx_unlock_spin(&clock_lock); } - hardclock(frame); -#ifdef SMP - forward_hardclock(); -#endif + if (!using_lapic_timer) + hardclock(frame); } int _at__at_ -221,9 +220,6 @@ } if (pscnt == psdiv) statclock(frame); -#ifdef SMP - forward_statclock(); -#endif } } _at__at_ -731,6 +727,9 _at__at_ int diag; if (statclock_disable) { +#ifdef LAPIC_TIMER + using_lapic_timer = lapic_setup_clock(); +#endif /* * The stat interrupt mask is different without the * statistics clock. Also, don't set the interrupt _at__at_ -756,7 +755,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); _at__at_ -774,7 +773,8 _at__at_ void cpu_startprofclock(void) { - + if (using_lapic_timer) + return; rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF; writertc(RTC_STATUSA, rtc_statusa); psdiv = pscnt = psratio; _at_@ -783,7 +783,8 @@ void cpu_stopprofclock(void) { - + if (using_lapic_timer) + return; rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; writertc(RTC_STATUSA, rtc_statusa); psdiv = pscnt = 1;Received on Mon Feb 21 2005 - 20:06:41 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:28 UTC