diff -Naur linux-2.6.6-rc1/arch/ia64/kernel/iosapic.c linux-2.6.6-rc1-changed/arch/ia64/kernel/iosapic.c --- linux-2.6.6-rc1/arch/ia64/kernel/iosapic.c 2004-04-19 16:10:25.970346988 +0900 +++ linux-2.6.6-rc1-changed/arch/ia64/kernel/iosapic.c 2004-04-19 16:25:23.251036926 +0900 @@ -101,15 +101,23 @@ /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */ -static struct iosapic_intr_info { +struct iosapic_pin { + struct list_head pin_list; /* IOSAPIC pins which share the same vector */ char *addr; /* base address of IOSAPIC */ u32 low32; /* current value of low word of Redirection table entry */ unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ - char rte_index; /* IOSAPIC RTE index (-1 => not an IOSAPIC interrupt) */ - unsigned char dmode : 3; /* delivery mode (see iosapic.h) */ + char rte_index; /* IOSAPIC RTE index */ unsigned char polarity: 1; /* interrupt polarity (see iosapic.h) */ +}; + +static struct iosapic_intr_info { + struct list_head pin_head; /* List head of IOSAPIC pins */ + struct iosapic_pin pin; /* First entry of IOSAPIC pins list */ + int count; /* # of pins (0 => not an IOSAPIC interrupt) */ + unsigned char dmode : 3; /* delivery mode (see iosapic.h) */ unsigned char trigger : 1; /* trigger mode (see iosapic.h) */ -} iosapic_intr_info[IA64_NUM_VECTORS]; + unsigned char type : 1; /* Vector type */ +} iosapic_intr_info[IA64_NUM_VECTORS] __cacheline_aligned; static struct iosapic { char *addr; /* base address of IOSAPIC */ @@ -142,10 +150,14 @@ _gsi_to_vector (unsigned int gsi) { struct iosapic_intr_info *info; + struct iosapic_pin *pin; - for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info) - if (info->gsi_base + info->rte_index == gsi) - return info - iosapic_intr_info; + for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info) { + list_for_each_entry(pin, &info->pin_head, pin_list) { + if (pin->gsi_base + pin->rte_index == gsi) + return info - iosapic_intr_info; + } + } return -1; } @@ -177,15 +189,13 @@ char *addr; int rte_index; char redir; + struct iosapic_pin *pin; DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest); - rte_index = iosapic_intr_info[vector].rte_index; - if (rte_index < 0) + if (!iosapic_intr_info[vector].count) return; /* not an IOSAPIC interrupt */ - addr = iosapic_intr_info[vector].addr; - pol = iosapic_intr_info[vector].polarity; trigger = iosapic_intr_info[vector].trigger; dmode = iosapic_intr_info[vector].dmode; @@ -193,31 +203,47 @@ #ifdef CONFIG_SMP { unsigned int irq; + cpumask_t cpumask; for (irq = 0; irq < NR_IRQS; ++irq) if (irq_to_vector(irq) == vector) { - set_irq_affinity_info(irq, (int)(dest & 0xffff), redir); + /* All pins should use the same destination CPU */ + cpumask = get_irq_affinity_info(irq); + if (cpumask != CPU_MASK_ALL) + dest = cpu_physical_id(first_cpu(mask)); + else + set_irq_affinity_info(irq, (int)(dest & 0xffff), redir); break; } } #endif - low32 = ((pol << IOSAPIC_POLARITY_SHIFT) | - (trigger << IOSAPIC_TRIGGER_SHIFT) | - (dmode << IOSAPIC_DELIVERY_SHIFT) | - ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) | - vector); - /* dest contains both id and eid */ high32 = (dest << IOSAPIC_DEST_SHIFT); spin_lock_irqsave(&iosapic_lock, flags); { - writel(IOSAPIC_RTE_HIGH(rte_index), addr + IOSAPIC_REG_SELECT); - writel(high32, addr + IOSAPIC_WINDOW); - writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); - writel(low32, addr + IOSAPIC_WINDOW); - iosapic_intr_info[vector].low32 = low32; + list_for_each_entry(pin, &iosapic_intr_info[vector].pin_head, pin_list) { + /* Don't change RTE which is already unmasked */ + if (!(pin->low32 & IOSAPIC_MASK)) + continue; + + rte_index = pin->rte_index; + addr = pin->addr; + pol = pin->polarity; + + low32 = ((pol << IOSAPIC_POLARITY_SHIFT) | + (trigger << IOSAPIC_TRIGGER_SHIFT) | + (dmode << IOSAPIC_DELIVERY_SHIFT) | + ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) | + vector); + + writel(IOSAPIC_RTE_HIGH(rte_index), addr + IOSAPIC_REG_SELECT); + writel(high32, addr + IOSAPIC_WINDOW); + writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); + writel(low32, addr + IOSAPIC_WINDOW); + pin->low32 = low32; + } } spin_unlock_irqrestore(&iosapic_lock, flags); } @@ -236,21 +262,24 @@ u32 low32; int rte_index; ia64_vector vec = irq_to_vector(irq); + struct iosapic_pin *pin; - addr = iosapic_intr_info[vec].addr; - rte_index = iosapic_intr_info[vec].rte_index; - - if (rte_index < 0) + if (!iosapic_intr_info[vec].count) return; /* not an IOSAPIC interrupt! */ spin_lock_irqsave(&iosapic_lock, flags); { - writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); + list_for_each_entry(pin, &iosapic_intr_info[vec].pin_head, pin_list) { + addr = pin->addr; + rte_index = pin->rte_index; + + writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); - /* set only the mask bit */ - low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK; + /* set only the mask bit */ + low32 = pin->low32 |= IOSAPIC_MASK; - writel(low32, addr + IOSAPIC_WINDOW); + writel(low32, addr + IOSAPIC_WINDOW); + } } spin_unlock_irqrestore(&iosapic_lock, flags); } @@ -263,17 +292,21 @@ u32 low32; int rte_index; ia64_vector vec = irq_to_vector(irq); + struct iosapic_pin *pin; - addr = iosapic_intr_info[vec].addr; - rte_index = iosapic_intr_info[vec].rte_index; - if (rte_index < 0) + if (!iosapic_intr_info[vec].count) return; /* not an IOSAPIC interrupt! */ spin_lock_irqsave(&iosapic_lock, flags); { - writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); - low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK; - writel(low32, addr + IOSAPIC_WINDOW); + list_for_each_entry(pin, &iosapic_intr_info[vec].pin_head, pin_list) { + addr = pin->addr; + rte_index = pin->rte_index; + + writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); + low32 = pin->low32 &= ~IOSAPIC_MASK; + writel(low32, addr + IOSAPIC_WINDOW); + } } spin_unlock_irqrestore(&iosapic_lock, flags); } @@ -289,6 +322,7 @@ char *addr; int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0; ia64_vector vec; + struct iosapic_pin *pin; irq &= (~IA64_IRQ_REDIRECTED); vec = irq_to_vector(irq); @@ -298,10 +332,7 @@ dest = cpu_physical_id(first_cpu(mask)); - rte_index = iosapic_intr_info[vec].rte_index; - addr = iosapic_intr_info[vec].addr; - - if (rte_index < 0) + if (!iosapic_intr_info[vec].count) return; /* not an IOSAPIC interrupt */ set_irq_affinity_info(irq, dest, redir); @@ -311,21 +342,26 @@ spin_lock_irqsave(&iosapic_lock, flags); { - /* get current delivery mode by reading the low32 */ - writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); - low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT); - if (redir) - /* change delivery mode to lowest priority */ - low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT); - else - /* change delivery mode to fixed */ - low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT); - - iosapic_intr_info[vec].low32 = low32; - writel(IOSAPIC_RTE_HIGH(rte_index), addr + IOSAPIC_REG_SELECT); - writel(high32, addr + IOSAPIC_WINDOW); - writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); - writel(low32, addr + IOSAPIC_WINDOW); + list_for_each_entry(pin, &iosapic_intr_info[vec].pin_head, pin_list) { + rte_index = pin->rte_index; + addr = pin->addr; + + /* get current delivery mode by reading the low32 */ + writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); + low32 = pin->low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT); + if (redir) + /* change delivery mode to lowest priority */ + low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT); + else + /* change delivery mode to fixed */ + low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT); + + pin->low32 = low32; + writel(IOSAPIC_RTE_HIGH(rte_index), addr + IOSAPIC_REG_SELECT); + writel(high32, addr + IOSAPIC_WINDOW); + writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT); + writel(low32, addr + IOSAPIC_WINDOW); + } } spin_unlock_irqrestore(&iosapic_lock, flags); #endif @@ -346,8 +382,11 @@ iosapic_end_level_irq (unsigned int irq) { ia64_vector vec = irq_to_vector(irq); + struct iosapic_pin *pin; - writel(vec, iosapic_intr_info[vec].addr + IOSAPIC_EOI); + list_for_each_entry(pin, &iosapic_intr_info[vec].pin_head, pin_list) { + writel(vec, pin->addr + IOSAPIC_EOI); + } } #define iosapic_shutdown_level_irq mask_irq @@ -427,6 +466,30 @@ } /* + * Find a sharable vector. + * + * NOTE: This function assumes that platform interrupt doesn't share + * the vector. + */ +static int +iosapic_find_sharable_vector (unsigned long trigger) +{ + int i; + static int next_vector = IA64_FIRST_DEVICE_VECTOR; + + for (i = 0; i < IA64_LAST_DEVICE_VECTOR - IA64_FIRST_DEVICE_VECTOR + 1; i++) { + if (next_vector > IA64_LAST_DEVICE_VECTOR) + next_vector = IA64_FIRST_DEVICE_VECTOR; + + if (iosapic_intr_info[next_vector].type == IOSAPIC_VECTOR_SHARABLE && + iosapic_intr_info[next_vector].trigger == trigger) + return next_vector++; + } + + return -1; +} + +/* * if the given vector is already owned by other, * assign a new vector for the other and make the vector available */ @@ -435,22 +498,20 @@ { int new_vector; - if (iosapic_intr_info[vector].rte_index >= 0 || iosapic_intr_info[vector].addr - || iosapic_intr_info[vector].gsi_base || iosapic_intr_info[vector].dmode - || iosapic_intr_info[vector].polarity || iosapic_intr_info[vector].trigger) + if (!iosapic_intr_info[vector].count) { new_vector = assign_irq_vector(AUTO_ASSIGN); printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector); memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector], sizeof(struct iosapic_intr_info)); memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); - iosapic_intr_info[vector].rte_index = -1; + INIT_LIST_HEAD(&iosapic_intr_info[vector].pin_head); } } static void register_intr (unsigned int gsi, int vector, unsigned char delivery, - unsigned long polarity, unsigned long trigger) + unsigned long polarity, unsigned long trigger, unsigned long type) { irq_desc_t *idesc; struct hw_interrupt_type *irq_type; @@ -458,6 +519,7 @@ int index; unsigned long gsi_base; char *iosapic_address; + struct iosapic_pin *pin, *tmp; index = find_iosapic(gsi); if (index < 0) { @@ -468,13 +530,86 @@ iosapic_address = iosapic_lists[index].addr; gsi_base = iosapic_lists[index].gsi_base; - rte_index = gsi - gsi_base; - iosapic_intr_info[vector].rte_index = rte_index; - iosapic_intr_info[vector].polarity = polarity; - iosapic_intr_info[vector].dmode = delivery; - iosapic_intr_info[vector].addr = iosapic_address; - iosapic_intr_info[vector].gsi_base = gsi_base; - iosapic_intr_info[vector].trigger = trigger; + /* + * Only interrupt sources whose delivery mode is IOSAPIC_LOWEST_PRIORITY or + * IOSAPIC_FIXED can share the vector in the current implementation. + */ + if (delivery != IOSAPIC_LOWEST_PRIORITY && + delivery != IOSAPIC_FIXED && + type != IOSAPIC_VECTOR_EXCLUSIVE) + { + type = IOSAPIC_VECTOR_EXCLUSIVE; + } + + pin = NULL; + list_for_each_entry(tmp, &iosapic_intr_info[vector].pin_head, pin_list) { + if (tmp->gsi_base + tmp->rte_index == gsi) { + pin = tmp; + break; + } + } + + if (!pin) { + /* + * Register a new interrupt + */ + if (!iosapic_intr_info[vector].count) { + pin = &iosapic_intr_info[vector].pin; + iosapic_intr_info[vector].dmode = delivery; + iosapic_intr_info[vector].trigger = trigger; + iosapic_intr_info[vector].type = type; + } + else if (type == IOSAPIC_VECTOR_EXCLUSIVE) { + printk (KERN_WARNING "%s: vector %d is in use\n", __FUNCTION__, vector); + return; + } + else if (iosapic_intr_info[vector].type == IOSAPIC_VECTOR_EXCLUSIVE) { + printk (KERN_WARNING "%s: cannot share the vector %d\n", __FUNCTION__, vector); + return; + } + else { + pin = kmalloc(sizeof(struct iosapic_pin), GFP_KERNEL); + if (!pin) { + printk (KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__); + return; + } + } + + rte_index = gsi - gsi_base; + pin->rte_index = rte_index; + pin->polarity = polarity; + pin->addr = iosapic_address; + pin->gsi_base = gsi_base; + pin->low32 = IOSAPIC_MASK; + + list_add_tail(&pin->pin_list, &iosapic_intr_info[vector].pin_head); + iosapic_intr_info[vector].count++; + } + else { + /* + * Override a existing interupt + */ + if (iosapic_intr_info[vector].count > 1) { + if (iosapic_intr_info[vector].trigger != trigger) { + printk (KERN_WARNING "%s: cannot change trigger mode\n", __FUNCTION__); + return; + } + if (type == IOSAPIC_VECTOR_EXCLUSIVE) { + printk (KERN_WARNING "%s: vector %d is already shared\n", __FUNCTION__, vector); + return; + } + } + + rte_index = gsi - gsi_base; + pin->rte_index = rte_index; + pin->polarity = polarity; + pin->addr = iosapic_address; + pin->gsi_base = gsi_base; + pin->low32 = IOSAPIC_MASK; + iosapic_intr_info[vector].dmode = delivery; + iosapic_intr_info[vector].trigger = trigger; + iosapic_intr_info[vector].type = type; + } if (trigger == IOSAPIC_EDGE) irq_type = &irq_type_iosapic_edge; @@ -504,10 +639,17 @@ vector = gsi_to_vector(gsi); if (vector < 0) - vector = assign_irq_vector(AUTO_ASSIGN); + vector = assign_irq_vector_nopanic(AUTO_ASSIGN); + + if (vector < 0) + vector = iosapic_find_sharable_vector(trigger); + + if (vector < 0) + panic ("%s: out of interrupt vectors!", __FUNCTION__); register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, - polarity, trigger); + polarity, trigger, + (trigger == IOSAPIC_EDGE ? IOSAPIC_VECTOR_EXCLUSIVE : IOSAPIC_VECTOR_SHARABLE)); printk(KERN_INFO "GSI 0x%x(%s,%s) -> CPU 0x%04x vector %d\n", gsi, (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), @@ -555,7 +697,7 @@ } register_intr(gsi, vector, delivery, polarity, - trigger); + trigger, IOSAPIC_VECTOR_EXCLUSIVE); printk(KERN_INFO "PLATFORM int 0x%x: GSI 0x%x(%s,%s) -> CPU 0x%04x vector %d\n", int_type, gsi, (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), @@ -581,7 +723,7 @@ vector = isa_irq_to_vector(isa_irq); - register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger); + register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger, IOSAPIC_VECTOR_EXCLUSIVE); DBG("ISA: IRQ %u -> GSI 0x%x (%s,%s) -> CPU 0x%04x vector %d\n", isa_irq, gsi, polarity == IOSAPIC_POL_HIGH ? "high" : "low", @@ -596,8 +738,10 @@ { int vector; - for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) - iosapic_intr_info[vector].rte_index = -1; /* mark as unused */ + for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) { + iosapic_intr_info[vector].count = 0; /* mark as unused */ + INIT_LIST_HEAD(&iosapic_intr_info[vector].pin_head); + } pcat_compat = system_pcat_compat; if (pcat_compat) { @@ -710,12 +854,17 @@ /* allocate a vector for this interrupt line */ if (pcat_compat && (gsi < 16)) vector = isa_irq_to_vector(gsi); - else + else { /* new GSI; allocate a vector for it */ - vector = assign_irq_vector(AUTO_ASSIGN); + vector = assign_irq_vector_nopanic(AUTO_ASSIGN); + if (vector < 0) + vector = iosapic_find_sharable_vector(IOSAPIC_LEVEL); + if (vector < 0) + panic ("%s: out of interrupt vectors!", __FUNCTION__); + } register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, IOSAPIC_POL_LOW, - IOSAPIC_LEVEL); + IOSAPIC_LEVEL, IOSAPIC_VECTOR_SHARABLE); } entry->irq = vector; snprintf(pci_id, sizeof(pci_id), "%02x:%02x:%02x[%c]", @@ -728,7 +877,7 @@ idesc = irq_descp(vector); if (idesc->handler != irq_type) register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, IOSAPIC_POL_LOW, - IOSAPIC_LEVEL); + IOSAPIC_LEVEL, IOSAPIC_VECTOR_SHARABLE); } } diff -Naur linux-2.6.6-rc1/arch/ia64/kernel/irq.c linux-2.6.6-rc1-changed/arch/ia64/kernel/irq.c --- linux-2.6.6-rc1/arch/ia64/kernel/irq.c 2004-04-04 12:38:13.000000000 +0900 +++ linux-2.6.6-rc1-changed/arch/ia64/kernel/irq.c 2004-04-19 16:10:53.513448673 +0900 @@ -935,6 +935,11 @@ } } +cpumask_t get_irq_affinity_info (unsigned int irq) +{ + return irq_affinity[irq]; +} + static int irq_affinity_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) { diff -Naur linux-2.6.6-rc1/arch/ia64/kernel/irq_ia64.c linux-2.6.6-rc1-changed/arch/ia64/kernel/irq_ia64.c --- linux-2.6.6-rc1/arch/ia64/kernel/irq_ia64.c 2004-04-19 16:10:25.971323555 +0900 +++ linux-2.6.6-rc1-changed/arch/ia64/kernel/irq_ia64.c 2004-04-19 16:10:53.513448673 +0900 @@ -72,17 +72,25 @@ preempt_enable_no_resched(); } +static int next_vector = IA64_FIRST_DEVICE_VECTOR; + int assign_irq_vector (int irq) { - static int next_vector = IA64_FIRST_DEVICE_VECTOR; - if (next_vector > IA64_LAST_DEVICE_VECTOR) /* XXX could look for sharable vectors instead of panic'ing... */ panic("assign_irq_vector: out of interrupt vectors!"); return next_vector++; } +int +assign_irq_vector_nopanic (int irq) +{ + if (next_vector > IA64_LAST_DEVICE_VECTOR) + return -1; + return next_vector++; +} + extern unsigned int do_IRQ(unsigned long irq, struct pt_regs *regs); /* diff -Naur linux-2.6.6-rc1/include/asm-ia64/hw_irq.h linux-2.6.6-rc1-changed/include/asm-ia64/hw_irq.h --- linux-2.6.6-rc1/include/asm-ia64/hw_irq.h 2004-04-19 16:10:27.139297942 +0900 +++ linux-2.6.6-rc1-changed/include/asm-ia64/hw_irq.h 2004-04-19 16:10:53.514425240 +0900 @@ -83,6 +83,7 @@ extern struct hw_interrupt_type irq_type_ia64_lsapic; /* CPU-internal interrupt controller */ extern int assign_irq_vector (int irq); /* allocate a free vector */ +extern int assign_irq_vector_nopanic (int irq); extern void ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect); extern void register_percpu_irq (ia64_vector vec, struct irqaction *action); diff -Naur linux-2.6.6-rc1/include/asm-ia64/iosapic.h linux-2.6.6-rc1-changed/include/asm-ia64/iosapic.h --- linux-2.6.6-rc1/include/asm-ia64/iosapic.h 2004-04-04 12:37:23.000000000 +0900 +++ linux-2.6.6-rc1-changed/include/asm-ia64/iosapic.h 2004-04-19 16:10:53.514425240 +0900 @@ -49,6 +49,12 @@ #define IOSAPIC_MASK_SHIFT 16 #define IOSAPIC_MASK (1< +extern cpumask_t get_irq_affinity_info (unsigned int irq); +#endif /* CONFIG_SMP */ struct irqaction; struct pt_regs;