Date: Sat, 26 Feb 2005 12:39:09 -0500 From: Coleman Kane <zombyfork@gmail.com> To: Takeharu KATO <takeharu1219@ybb.ne.jp> Cc: freebsd-amd64@freebsd.org Subject: Re: AMD64 Local APIC Timer Message-ID: <346a80220502260939848bdf6@mail.gmail.com> In-Reply-To: <421B5E3D.60209@ybb.ne.jp> References: <421A4D5D.6040205@ybb.ne.jp> <421B5E3D.60209@ybb.ne.jp>
next in thread | previous in thread | raw e-mail | index | archive | help
Hi, The lapic timer patch seems to break something in the timeout(9)/untimeout(9) handling. I have a mobile athlon64 laptop, and have been using Fukuda Nobuhiko's acpi_ppc driver for the Cool'n'Quiet operation. With your patch applied, this driver no longer scales the CPU frequency. It seems to use timeout(9) to have the kernel call a polling function regularly to monitor CPU usage and scales the CPU speed to match the usage. This helps maintain bettery life. The driver is at: http://www.spa.is.uec.ac.jp/~nfukuda/software/dist/acpi_ppc-20050210.tgz -- coleman kane On Wed, 23 Feb 2005 01:30:53 +0900, Takeharu KATO <takeharu1219@ybb.ne.jp> wrote: > 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 > > > 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@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; > > > _______________________________________________ > freebsd-amd64@freebsd.org mailing list > http://lists.freebsd.org/mailman/listinfo/freebsd-amd64 > To unsubscribe, send any mail to "freebsd-amd64-unsubscribe@freebsd.org" > > >
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?346a80220502260939848bdf6>