* [PATCH 0/2 v5] SGI RTC: add clocksource/clockevent driver and generic timer vector @ 2008-12-19 16:07 Dimitri Sivanich 2008-12-19 16:09 ` [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt Dimitri Sivanich 0 siblings, 1 reply; 5+ messages in thread From: Dimitri Sivanich @ 2008-12-19 16:07 UTC (permalink / raw) To: Ingo Molnar; +Cc: Thomas Gleixner, H. Peter Anvin, linux-kernel The following patches provide a driver for synchronized RTC clocksource and clockevents for SGI systems, as well as a generic timer system interrupt. With these patches, a module can be installed that registers the system-wide synchronized RTC clocksource and timers as both a clocksource and clockevents device running in high resolution mode. [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt [PATCH 2/2 v5] SGI RTC: add clocksource driver ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt 2008-12-19 16:07 [PATCH 0/2 v5] SGI RTC: add clocksource/clockevent driver and generic timer vector Dimitri Sivanich @ 2008-12-19 16:09 ` Dimitri Sivanich 2008-12-19 16:11 ` [PATCH 2/2 v5] SGI RTC: add clocksource driver Dimitri Sivanich 2008-12-21 18:22 ` [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt Thomas Gleixner 0 siblings, 2 replies; 5+ messages in thread From: Dimitri Sivanich @ 2008-12-19 16:09 UTC (permalink / raw) To: Ingo Molnar; +Cc: Thomas Gleixner, H. Peter Anvin, linux-kernel This patch allocates a system interrupt vector for platform specific use in implementing timer interrupts. Signed-off-by: Dimitri Sivanich <sivanich@sgi.com> --- Refreshed for latest -tip. arch/x86/include/asm/hw_irq.h | 1 arch/x86/include/asm/irq.h | 2 + arch/x86/include/asm/irq_vectors.h | 5 +++ arch/x86/kernel/entry_64.S | 2 + arch/x86/kernel/irqinit_64.c | 45 +++++++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+) Index: linux/arch/x86/kernel/entry_64.S =================================================================== --- linux.orig/arch/x86/kernel/entry_64.S 2008-12-19 10:02:26.000000000 -0600 +++ linux/arch/x86/kernel/entry_64.S 2008-12-19 10:02:28.000000000 -0600 @@ -985,6 +985,8 @@ apicinterrupt UV_BAU_MESSAGE \ uv_bau_message_intr1 uv_bau_message_interrupt apicinterrupt LOCAL_TIMER_VECTOR \ apic_timer_interrupt smp_apic_timer_interrupt +apicinterrupt GENERIC_TIMER_VECTOR \ + generic_timer_interrupt smp_generic_timer_interrupt #ifdef CONFIG_SMP apicinterrupt INVALIDATE_TLB_VECTOR_START+0 \ Index: linux/arch/x86/kernel/irqinit_64.c =================================================================== --- linux.orig/arch/x86/kernel/irqinit_64.c 2008-12-19 10:02:26.000000000 -0600 +++ linux/arch/x86/kernel/irqinit_64.c 2008-12-19 10:02:28.000000000 -0600 @@ -22,6 +22,7 @@ #include <asm/desc.h> #include <asm/apic.h> #include <asm/i8259.h> +#include <asm/idle.h> /* * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts: @@ -93,6 +94,47 @@ void __init init_ISA_irqs(void) void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ"))); + +/* Function pointer for generic timer interrupt handling */ +static void (*generic_timer_interrupt_extension)(void); + +int +register_generic_timer_extension(void (*fn)(void)) +{ + if (generic_timer_interrupt_extension) + return 1; + + generic_timer_interrupt_extension = fn; + return 0; +} +EXPORT_SYMBOL_GPL(register_generic_timer_extension); + +void +unregister_generic_timer_extension(void) +{ + if (generic_timer_interrupt_extension) + generic_timer_interrupt_extension = NULL; +} +EXPORT_SYMBOL_GPL(unregister_generic_timer_extension); + +void smp_generic_timer_interrupt(struct pt_regs *regs) +{ + struct pt_regs *old_regs = set_irq_regs(regs); + + ack_APIC_irq(); + + exit_idle(); + + irq_enter(); + + if (generic_timer_interrupt_extension) + generic_timer_interrupt_extension(); + + irq_exit(); + + set_irq_regs(old_regs); +} + static void __init smp_intr_init(void) { #ifdef CONFIG_SMP @@ -134,6 +176,9 @@ static void __init apic_intr_init(void) /* self generated IPI for local APIC timer */ alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); + /* IPI for platform specific timers */ + alloc_intr_gate(GENERIC_TIMER_VECTOR, generic_timer_interrupt); + /* IPI vectors for APIC spurious and error interrupts */ alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt); Index: linux/arch/x86/include/asm/irq_vectors.h =================================================================== --- linux.orig/arch/x86/include/asm/irq_vectors.h 2008-12-19 10:02:26.000000000 -0600 +++ linux/arch/x86/include/asm/irq_vectors.h 2008-12-19 10:02:28.000000000 -0600 @@ -92,6 +92,11 @@ #define LOCAL_PERF_VECTOR 0xee /* + * Generic timer vector for platform specific use + */ +#define GENERIC_TIMER_VECTOR 0xed + +/* * First APIC vector available to drivers: (vectors 0x30-0xee) we * start at 0x31(0x41) to spread out vectors evenly between priority * levels. (0x80 is the syscall vector) Index: linux/arch/x86/include/asm/hw_irq.h =================================================================== --- linux.orig/arch/x86/include/asm/hw_irq.h 2008-12-19 10:02:26.000000000 -0600 +++ linux/arch/x86/include/asm/hw_irq.h 2008-12-19 10:02:28.000000000 -0600 @@ -29,6 +29,7 @@ /* Interrupt handlers registered during init_IRQ */ extern void apic_timer_interrupt(void); +extern void generic_timer_interrupt(void); extern void error_interrupt(void); extern void perf_counter_interrupt(void); Index: linux/arch/x86/include/asm/irq.h =================================================================== --- linux.orig/arch/x86/include/asm/irq.h 2008-12-19 10:02:26.000000000 -0600 +++ linux/arch/x86/include/asm/irq.h 2008-12-19 10:02:28.000000000 -0600 @@ -38,6 +38,8 @@ extern void fixup_irqs(void); extern unsigned int do_IRQ(struct pt_regs *regs); extern void init_IRQ(void); +extern int register_generic_timer_extension(void (*fn)(void)); +extern void unregister_generic_timer_extension(void); extern void native_init_IRQ(void); /* Interrupt vector management */ ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/2 v5] SGI RTC: add clocksource driver 2008-12-19 16:09 ` [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt Dimitri Sivanich @ 2008-12-19 16:11 ` Dimitri Sivanich 2008-12-21 18:13 ` Thomas Gleixner 2008-12-21 18:22 ` [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt Thomas Gleixner 1 sibling, 1 reply; 5+ messages in thread From: Dimitri Sivanich @ 2008-12-19 16:11 UTC (permalink / raw) To: Ingo Molnar; +Cc: Thomas Gleixner, H. Peter Anvin, linux-kernel This patch provides a driver for SGI RTC clocks and timers. This provides a high resolution clock and timer source using the SGI system-wide synchronized RTC clock/timer hardware. Signed-off-by: Dimitri Sivanich <sivanich@sgi.com> --- Refreshed for latest -tip. drivers/clocksource/Makefile | 1 drivers/clocksource/rtc_uv.c | 410 +++++++++++++++++++++++++++++++++++++++ drivers/misc/Kconfig | 9 kernel/time/clockevents.c | 1 kernel/time/clocksource.c | 1 5 files changed, 422 insertions(+) Index: linux/drivers/clocksource/Makefile =================================================================== --- linux.orig/drivers/clocksource/Makefile 2008-12-19 10:02:23.000000000 -0600 +++ linux/drivers/clocksource/Makefile 2008-12-19 10:02:36.000000000 -0600 @@ -2,3 +2,4 @@ obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_cl obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o +obj-$(CONFIG_SGI_RTC_TIMER) += rtc_uv.o Index: linux/drivers/misc/Kconfig =================================================================== --- linux.orig/drivers/misc/Kconfig 2008-12-19 10:02:23.000000000 -0600 +++ linux/drivers/misc/Kconfig 2008-12-19 10:02:36.000000000 -0600 @@ -498,6 +498,15 @@ config SGI_GRU_DEBUG This option enables addition debugging code for the SGI GRU driver. If you are unsure, say N. +config SGI_RTC_TIMER + tristate "SGI RTC driver" + depends on GENERIC_CLOCKEVENTS_BUILD + depends on (X86_64 || IA64_SGI_UV || IA64_GENERIC) && SMP + default m + ---help--- + This option enables RTC event handling and adds the systemwide RTC + as a high resolution clock source for SGI systems. + source "drivers/misc/c2port/Kconfig" endif # MISC_DEVICES Index: linux/drivers/clocksource/rtc_uv.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/clocksource/rtc_uv.c 2008-12-19 10:02:36.000000000 -0600 @@ -0,0 +1,410 @@ +/* + * SGI RTC clock/timer routines. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) Dimitri Sivanich + */ +#include <linux/module.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <asm/genapic.h> +#include <asm/uv/bios.h> +#include <asm/uv/uv_mmrs.h> +#include <asm/uv/uv_hub.h> + +MODULE_LICENSE("GPL"); + +#define RTC_NAME "sgi_rtc" + +static cycle_t uv_read_rtc(void); +static int uv_rtc_next_event(unsigned long, struct clock_event_device *); +static void uv_rtc_timer_setup(enum clock_event_mode, + struct clock_event_device *); +static void uv_rtc_timer_broadcast(cpumask_t); + + +static struct clocksource clocksource_uv = { + .name = RTC_NAME, + .rating = 400, + .read = uv_read_rtc, + .mask = (cycle_t)UVH_RTC_REAL_TIME_CLOCK_MASK, + .shift = 10, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static struct clock_event_device clock_event_device_uv = { + .name = RTC_NAME, + .features = CLOCK_EVT_FEAT_ONESHOT, + .shift = 20, + .rating = 400, + .irq = -1, + .set_next_event = uv_rtc_next_event, + .set_mode = uv_rtc_timer_setup, + .event_handler = NULL, + .broadcast = uv_rtc_timer_broadcast +}; + +static DEFINE_PER_CPU(struct clock_event_device, cpu_ced); + +/* There is one of these allocated per node */ +struct uv_rtc_timer_head { + spinlock_t lock; + int fcpu; /* first cpu on this node, system-wide reference */ + int cpus; /* number of cpus on this node */ + u64 next_cpu; /* next cpu waiting for timer, local node relative */ + u64 expires[]; /* next timer expiration for each cpu on this node */ +}; + +/* + * This points to the uv_rtc_timer_head allocated for this cpu's node. + */ +static DEFINE_PER_CPU(struct uv_rtc_timer_head *, rtc_timer_head); + +int cpu_to_pnode(int cpu) +{ + return uv_apicid_to_pnode(per_cpu(x86_cpu_to_apicid, cpu)); +} + +/* + * Hardware interface routines + */ + +/* Send IPIs to another node */ +static void +uv_rtc_send_IPI(int cpu, int vector) +{ + unsigned long val, apicid, lapicid; + int pnode; + + apicid = per_cpu(x86_cpu_to_apicid, cpu); + lapicid = apicid & 0x3f; + pnode = uv_apicid_to_pnode(apicid); + + val = (1UL << UVH_IPI_INT_SEND_SHFT) | (lapicid << + UVH_IPI_INT_APIC_ID_SHFT) | + (vector << UVH_IPI_INT_VECTOR_SHFT); + uv_write_global_mmr64(pnode, UVH_IPI_INT, val); +} + +/* Check for an RTC interrupt pending */ +static int +uv_intr_pending(int pnode) +{ + if (uv_read_global_mmr64(pnode, UVH_EVENT_OCCURRED0) & + UVH_EVENT_OCCURRED0_RTC1_MASK) + return 1; + else + return 0; +} + +static void +uv_clr_intr_pending(int pnode) +{ + uv_write_global_mmr64(pnode, UVH_EVENT_OCCURRED0_ALIAS, + UVH_EVENT_OCCURRED0_RTC1_MASK); +} + +static void +uv_setup_intr(int cpu, u64 expires) +{ + u64 val; + int pnode = cpu_to_pnode(cpu); + + uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG, + UVH_RTC1_INT_CONFIG_M_MASK); + uv_write_global_mmr64(pnode, UVH_INT_CMPB, -1L); + + uv_clr_intr_pending(pnode); + + val = ((u64)GENERIC_TIMER_VECTOR << UVH_RTC1_INT_CONFIG_VECTOR_SHFT) | + ((u64)cpu_physical_id(cpu) << UVH_RTC1_INT_CONFIG_APIC_ID_SHFT); + /* Set configuration */ + uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG, val); + /* Initialize comparator value */ + uv_write_global_mmr64(pnode, UVH_INT_CMPB, expires); +} + + +/* + * Per-cpu timer tracking routines + */ + +/* Allocate per-node list of cpu timer expiration times. */ +static int +uv_rtc_allocate_timers(void) +{ + int cpu; + int max = 0; + int pnode = -1; + int i = 0; + struct uv_rtc_timer_head *head = NULL; + + /* + * Determine max possible hyperthreads/pnode for allocation. + * + * Allocate for all cpus that could come up, although we don't + * fully support hotplug (yet). + */ + for_each_possible_cpu(cpu) { + if (pnode != cpu_to_pnode(cpu)) { + i = 0; + pnode = cpu_to_pnode(cpu); + } + if (max < ++i) + max = i; + } + + pnode = -1; + for_each_possible_cpu(cpu) { + if (pnode != cpu_to_pnode(cpu)) { + i = 0; + pnode = cpu_to_pnode(cpu); + head = kmalloc_node(sizeof(struct uv_rtc_timer_head) + + (max * sizeof(u64)), + GFP_KERNEL, pnode); + if (!head) + return -ENOMEM; + spin_lock_init(&head->lock); + head->fcpu = cpu; + head->cpus = 0; + head->next_cpu = -1; + } + head->cpus++; + head->expires[i++] = ULLONG_MAX; + per_cpu(rtc_timer_head, cpu) = head; + } + return 0; +} + +/* Find and set the next expiring timer. */ +static void +uv_rtc_find_next_timer(struct uv_rtc_timer_head *head, int pnode) +{ + u64 exp; + u64 lowest = ULLONG_MAX; + int cpu = -1; + int c; + + head->next_cpu = -1; + for (c = 0; c < head->cpus; c++) { + exp = head->expires[c]; + if (exp < lowest) { + cpu = c; + lowest = exp; + } + } + if (cpu >= 0) { + head->next_cpu = cpu; + c = head->fcpu + cpu; + uv_setup_intr(c, lowest); + /* If we didn't set it up in time, trigger */ + if (lowest < uv_read_rtc() && !uv_intr_pending(pnode)) + uv_rtc_send_IPI(c, GENERIC_TIMER_VECTOR); + } else + uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG, + UVH_RTC1_INT_CONFIG_M_MASK); +} + +/* + * Set expiration time for current cpu. + * + * Returns 1 if we missed the expiration time. + */ +static int +uv_rtc_set_timer(int cpu, u64 expires) +{ + int pnode = cpu_to_pnode(cpu); + struct uv_rtc_timer_head *head = per_cpu(rtc_timer_head, cpu); + int local_cpu = cpu - head->fcpu; + u64 *t = &head->expires[local_cpu]; + unsigned long flags; + int next_cpu; + + spin_lock_irqsave(&head->lock, flags); + + next_cpu = head->next_cpu; + *t = expires; + + /* Will this one be next to go off? */ + if (expires < head->expires[next_cpu]) { + head->next_cpu = cpu; + uv_setup_intr(cpu, expires); + if (expires <= uv_read_rtc() && !uv_intr_pending(pnode)) { + *t = ULLONG_MAX; + uv_rtc_find_next_timer(head, pnode); + spin_unlock_irqrestore(&head->lock, flags); + return 1; + } + } + + spin_unlock_irqrestore(&head->lock, flags); + return 0; +} + +/* + * Unset expiration time for current cpu. + * + * Returns 1 if this timer was pending. + */ +static int +uv_rtc_unset_timer(int cpu) +{ + int pnode = cpu_to_pnode(cpu); + struct uv_rtc_timer_head *head = per_cpu(rtc_timer_head, cpu); + int local_cpu = cpu - head->fcpu; + u64 *t = &head->expires[local_cpu]; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&head->lock, flags); + + if (head->next_cpu == cpu && uv_read_rtc() >= *t) + rc = 1; + + *t = ULLONG_MAX; + /* Was the hardware setup for this timer? */ + if (head->next_cpu == cpu) + uv_rtc_find_next_timer(head, pnode); + + spin_unlock_irqrestore(&head->lock, flags); + return rc; +} + + + +/* + * Kernel interface routines. + */ + +/* + * Read the RTC. + */ +static cycle_t +uv_read_rtc(void) +{ + return (cycle_t)uv_read_local_mmr(UVH_RTC); +} + +/* + * Program the next event, relative to now + */ +static int +uv_rtc_next_event(unsigned long delta, struct clock_event_device *ced) +{ + int ced_cpu = first_cpu(ced->cpumask); + + return uv_rtc_set_timer(ced_cpu, delta + uv_read_rtc()); +} + +/* + * Setup the RTC timer in oneshot mode + */ +static void +uv_rtc_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt) +{ + int ced_cpu = first_cpu(evt->cpumask); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_RESUME: + /* Nothing to do here yet */ + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + uv_rtc_unset_timer(ced_cpu); + break; + } +} + +/* + * Local APIC timer broadcast function + */ +static void +uv_rtc_timer_broadcast(cpumask_t mask) +{ + int cpu; + for_each_cpu_mask(cpu, mask) + uv_rtc_send_IPI(cpu, GENERIC_TIMER_VECTOR); +} + +static void +uv_rtc_interrupt(void) +{ + struct clock_event_device *ced = &__get_cpu_var(cpu_ced); + unsigned long flags; + int cpu = smp_processor_id(); + + local_irq_save(flags); + if (!ced || !ced->event_handler || !uv_rtc_unset_timer(cpu)) { + local_irq_restore(flags); + printk(KERN_WARNING + "Spurious uv_rtc timer interrupt on cpu %d\n", cpu); + return; + } + local_irq_restore(flags); + ced->event_handler(ced); +} + +static __init void +uv_rtc_register_clockevents(void *data) +{ + struct clock_event_device *ced = &__get_cpu_var(cpu_ced); + + *ced = clock_event_device_uv; + cpus_clear(ced->cpumask); + cpu_set(smp_processor_id(), ced->cpumask); + clockevents_register_device(ced); +} + +static __init int +uv_rtc_setup_clock(void) +{ + int rc; + + if (!is_uv_system() || + register_generic_timer_extension(uv_rtc_interrupt)) + return -ENODEV; + + clocksource_uv.mult = clocksource_hz2mult(sn_rtc_cycles_per_second, + clocksource_uv.shift); + + rc = clocksource_register(&clocksource_uv); + if (rc) { + unregister_generic_timer_extension(); + return rc; + } + + /* Setup and register clockevents */ + rc = uv_rtc_allocate_timers(); + if (rc) { + unregister_generic_timer_extension(); + return rc; + } + + clock_event_device_uv.mult = div_sc(sn_rtc_cycles_per_second, + NSEC_PER_SEC, clock_event_device_uv.shift); + + clock_event_device_uv.min_delta_ns = NSEC_PER_SEC / + sn_rtc_cycles_per_second; + clock_event_device_uv.max_delta_ns = clocksource_uv.mask * + (NSEC_PER_SEC / sn_rtc_cycles_per_second); + + on_each_cpu(uv_rtc_register_clockevents, NULL, 0); + return 0; +} +module_init(uv_rtc_setup_clock); Index: linux/kernel/time/clockevents.c =================================================================== --- linux.orig/kernel/time/clockevents.c 2008-12-19 10:02:23.000000000 -0600 +++ linux/kernel/time/clockevents.c 2008-12-19 10:02:36.000000000 -0600 @@ -185,6 +185,7 @@ void clockevents_register_device(struct spin_unlock(&clockevents_lock); } +EXPORT_SYMBOL_GPL(clockevents_register_device); /* * Noop handler when we shut down an event device Index: linux/kernel/time/clocksource.c =================================================================== --- linux.orig/kernel/time/clocksource.c 2008-12-19 10:02:23.000000000 -0600 +++ linux/kernel/time/clocksource.c 2008-12-19 10:02:36.000000000 -0600 @@ -369,6 +369,7 @@ void clocksource_unregister(struct clock next_clocksource = select_clocksource(); spin_unlock_irqrestore(&clocksource_lock, flags); } +EXPORT_SYMBOL(clocksource_unregister); #ifdef CONFIG_SYSFS /** ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2 v5] SGI RTC: add clocksource driver 2008-12-19 16:11 ` [PATCH 2/2 v5] SGI RTC: add clocksource driver Dimitri Sivanich @ 2008-12-21 18:13 ` Thomas Gleixner 0 siblings, 0 replies; 5+ messages in thread From: Thomas Gleixner @ 2008-12-21 18:13 UTC (permalink / raw) To: Dimitri Sivanich; +Cc: Ingo Molnar, H. Peter Anvin, linux-kernel On Fri, 19 Dec 2008, Dimitri Sivanich wrote: > + > +int cpu_to_pnode(int cpu) > +{ > + return uv_apicid_to_pnode(per_cpu(x86_cpu_to_apicid, cpu)); > +} static please > +/* > + * Hardware interface routines > + */ > + > +/* Send IPIs to another node */ > +static void > +uv_rtc_send_IPI(int cpu, int vector) One line, except the line exceeds 80 chars. All over the place. > +{ > + unsigned long val, apicid, lapicid; > + int pnode; > + > + apicid = per_cpu(x86_cpu_to_apicid, cpu); > + lapicid = apicid & 0x3f; > + pnode = uv_apicid_to_pnode(apicid); > + > + val = (1UL << UVH_IPI_INT_SEND_SHFT) | (lapicid << > + UVH_IPI_INT_APIC_ID_SHFT) | > + (vector << UVH_IPI_INT_VECTOR_SHFT); This is horrible to read. + val = (1UL << UVH_IPI_INT_SEND_SHFT) | + (lapicid << UVH_IPI_INT_APIC_ID_SHFT) | + (vector << UVH_IPI_INT_VECTOR_SHFT); Takes the same amount of lines, but is parseable. > + uv_write_global_mmr64(pnode, UVH_IPI_INT, val); > +} > + > +/* Check for an RTC interrupt pending */ > +static int > +uv_intr_pending(int pnode) > +{ > + if (uv_read_global_mmr64(pnode, UVH_EVENT_OCCURRED0) & > + UVH_EVENT_OCCURRED0_RTC1_MASK) > + return 1; > + else > + return 0; return uv_read_global_mmr64(pnode, UVH_EVENT_OCCURRED0) & UVH_EVENT_OCCURRED0_RTC1_MASK; > + > +/* Allocate per-node list of cpu timer expiration times. */ > +static int > +uv_rtc_allocate_timers(void) > +{ > + int cpu; > + int max = 0; > + int pnode = -1; > + int i = 0; Please collapse those ints into one line > + struct uv_rtc_timer_head *head = NULL; > + > + /* > + * Determine max possible hyperthreads/pnode for allocation. > + * > + * Allocate for all cpus that could come up, although we don't > + * fully support hotplug (yet). > + */ > + for_each_possible_cpu(cpu) { > + if (pnode != cpu_to_pnode(cpu)) { > + i = 0; > + pnode = cpu_to_pnode(cpu); > + } > + if (max < ++i) > + max = i; > + } Is this worth the trouble whether we allocate a couple of u64s per node or not ? Also isn't this kind of topology evaluation not done in x86 code already ? > + pnode = -1; > + for_each_possible_cpu(cpu) { > + if (pnode != cpu_to_pnode(cpu)) { > + i = 0; > + pnode = cpu_to_pnode(cpu); > + head = kmalloc_node(sizeof(struct uv_rtc_timer_head) + > + (max * sizeof(u64)), > + GFP_KERNEL, pnode); > + if (!head) > + return -ENOMEM; Leaks memory for already allocated nodes > + spin_lock_init(&head->lock); > + head->fcpu = cpu; > + head->cpus = 0; > + head->next_cpu = -1; > + } > + head->cpus++; > + head->expires[i++] = ULLONG_MAX; > + per_cpu(rtc_timer_head, cpu) = head; > + } > + return 0; > +} > + > +/* Find and set the next expiring timer. */ > +static void > +uv_rtc_find_next_timer(struct uv_rtc_timer_head *head, int pnode) > +{ > + u64 exp; > + u64 lowest = ULLONG_MAX; > + int cpu = -1; > + int c; u64 exp, lowest = ULLONG_MAX; int c, cpu = -1; please > + head->next_cpu = -1; > + for (c = 0; c < head->cpus; c++) { > + exp = head->expires[c]; > + if (exp < lowest) { > + cpu = c; > + lowest = exp; > + } > + } How many cpus do we iterate here ? Is this really scaling ? > + if (cpu >= 0) { > + head->next_cpu = cpu; > + c = head->fcpu + cpu; > + uv_setup_intr(c, lowest); > + /* If we didn't set it up in time, trigger */ > + if (lowest < uv_read_rtc() && !uv_intr_pending(pnode)) > + uv_rtc_send_IPI(c, GENERIC_TIMER_VECTOR); Please move the expired check into uv_setup_intr() and return the expired status. You need the same check in uv_rtc_set_timer() below. > +/* > + * Local APIC timer broadcast function > + */ > +static void > +uv_rtc_timer_broadcast(cpumask_t mask) > +{ > + int cpu; > + for_each_cpu_mask(cpu, mask) > + uv_rtc_send_IPI(cpu, GENERIC_TIMER_VECTOR); > +} No need to implement broadcast. Your clock event device is not affected from power states or such, so this will never be called. > +static void > +uv_rtc_interrupt(void) > +{ > + struct clock_event_device *ced = &__get_cpu_var(cpu_ced); > + unsigned long flags; > + int cpu = smp_processor_id(); > + > + local_irq_save(flags); called with interrupts disabled already. > + if (!ced || !ced->event_handler || !uv_rtc_unset_timer(cpu)) { > + local_irq_restore(flags); > + printk(KERN_WARNING > + "Spurious uv_rtc timer interrupt on cpu %d\n", cpu); > + return; > + } > + local_irq_restore(flags); > + ced->event_handler(ced); > +} > + > +static __init void > +uv_rtc_register_clockevents(void *data) > +{ > + struct clock_event_device *ced = &__get_cpu_var(cpu_ced); > + > + *ced = clock_event_device_uv; > + cpus_clear(ced->cpumask); > + cpu_set(smp_processor_id(), ced->cpumask); > + clockevents_register_device(ced); clockevents_register_device can not be called from interrupt context. You use an SMP call for this. Please test your code with lockdep enabled. > +} > + > +static __init int > +uv_rtc_setup_clock(void) > +{ > + int rc; > + > + if (!is_uv_system() || > + register_generic_timer_extension(uv_rtc_interrupt)) > + return -ENODEV; > + > + clocksource_uv.mult = clocksource_hz2mult(sn_rtc_cycles_per_second, > + clocksource_uv.shift); > + > + rc = clocksource_register(&clocksource_uv); > + if (rc) { > + unregister_generic_timer_extension(); > + return rc; > + } > + > + /* Setup and register clockevents */ > + rc = uv_rtc_allocate_timers(); > + if (rc) { The clock source is already registered. So now the module is removed and the timekeeping code explodes. clocksource_unregister(...) perpaps? > + unregister_generic_timer_extension(); > + return rc; > + } > + > + clock_event_device_uv.mult = div_sc(sn_rtc_cycles_per_second, > + NSEC_PER_SEC, clock_event_device_uv.shift); > + > + clock_event_device_uv.min_delta_ns = NSEC_PER_SEC / > + sn_rtc_cycles_per_second; > + clock_event_device_uv.max_delta_ns = clocksource_uv.mask * > + (NSEC_PER_SEC / sn_rtc_cycles_per_second); > + > + on_each_cpu(uv_rtc_register_clockevents, NULL, 0); > + return 0; > +} > +module_init(uv_rtc_setup_clock); > Index: linux/kernel/time/clockevents.c > =================================================================== > --- linux.orig/kernel/time/clockevents.c 2008-12-19 10:02:23.000000000 -0600 > +++ linux/kernel/time/clockevents.c 2008-12-19 10:02:36.000000000 -0600 > @@ -185,6 +185,7 @@ void clockevents_register_device(struct > > spin_unlock(&clockevents_lock); > } > +EXPORT_SYMBOL_GPL(clockevents_register_device); Separate patch please > /* > * Noop handler when we shut down an event device > Index: linux/kernel/time/clocksource.c > =================================================================== > --- linux.orig/kernel/time/clocksource.c 2008-12-19 10:02:23.000000000 -0600 > +++ linux/kernel/time/clocksource.c 2008-12-19 10:02:36.000000000 -0600 > @@ -369,6 +369,7 @@ void clocksource_unregister(struct clock > next_clocksource = select_clocksource(); > spin_unlock_irqrestore(&clocksource_lock, flags); > } > +EXPORT_SYMBOL(clocksource_unregister); EXPORT_SYMBOL_GPL and separate patch please. Thanks, tglx ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt 2008-12-19 16:09 ` [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt Dimitri Sivanich 2008-12-19 16:11 ` [PATCH 2/2 v5] SGI RTC: add clocksource driver Dimitri Sivanich @ 2008-12-21 18:22 ` Thomas Gleixner 1 sibling, 0 replies; 5+ messages in thread From: Thomas Gleixner @ 2008-12-21 18:22 UTC (permalink / raw) To: Dimitri Sivanich; +Cc: Ingo Molnar, H. Peter Anvin, linux-kernel On Fri, 19 Dec 2008, Dimitri Sivanich wrote: > This patch allocates a system interrupt vector for platform specific use > in implementing timer interrupts. Why is this restricted to timer interrupts. That's simply a dynamic allocation of an interrupt vector. > + > +/* Function pointer for generic timer interrupt handling */ > +static void (*generic_timer_interrupt_extension)(void); > + > +int > +register_generic_timer_extension(void (*fn)(void)) One line. All over the place. > +{ > + if (generic_timer_interrupt_extension) > + return 1; -EBUSY perhaps ? > + generic_timer_interrupt_extension = fn; > + return 0; > +} > +EXPORT_SYMBOL_GPL(register_generic_timer_extension); > + > +void > +unregister_generic_timer_extension(void) > +{ > + if (generic_timer_interrupt_extension) > + generic_timer_interrupt_extension = NULL; > +} > +EXPORT_SYMBOL_GPL(unregister_generic_timer_extension); > + > +void smp_generic_timer_interrupt(struct pt_regs *regs) > +{ > + struct pt_regs *old_regs = set_irq_regs(regs); > + > + ack_APIC_irq(); > + > + exit_idle(); > + > + irq_enter(); > + > + if (generic_timer_interrupt_extension) > + generic_timer_interrupt_extension(); interrupt statistics are missing. > + irq_exit(); > + > + set_irq_regs(old_regs); > +} > + Thanks, tglx ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2008-12-21 18:22 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-12-19 16:07 [PATCH 0/2 v5] SGI RTC: add clocksource/clockevent driver and generic timer vector Dimitri Sivanich 2008-12-19 16:09 ` [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt Dimitri Sivanich 2008-12-19 16:11 ` [PATCH 2/2 v5] SGI RTC: add clocksource driver Dimitri Sivanich 2008-12-21 18:13 ` Thomas Gleixner 2008-12-21 18:22 ` [PATCH 1/2 v5] SGI RTC: add generic timer system interrupt Thomas Gleixner
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox