acpi/pci_link.c
PATCH 1/1 acpi/pci_link.c
Please review this interrupt patch.
It applies to both 2.4 and 2.6.
thanks,
-Len
What the patch does:
Re-enables PCI Link Device programming by default.
This fixes systems that stopped working when we stopped re-programming active PIRQs:
http://bugzilla.kernel.org/show_bug.cgi?id=1440
For systems that mark their PIRQ links active, this allows the kernel to again distribute PIRQs across all available IRQs:
http://bugzilla.kernel.org/show_bug.cgi?id=1391
Fixes an off-by-one bug in the PIRQ distribution code that caused one of the possible IRQs to be ignored on every PIRQ link.
To aid in minimizing IRQ sharing, added cmdline "acpi_irq_pci=" to tell the kernel when it can use an IRQ for PCI, even if the kernel default is to reserve that IRQ for ISA use.
Conversely, added "acpi_irq_isa=" cmdline param to reserve IRQs for ISA.
Workaround for SoundBlaster vs. ACPI PIRQ routing issues:
http://bugzilla.kernel.org/show_bug.cgi?id=430
http://bugzilla.kernel.org/show_bug.cgi?id=1139
Previously we had disabled re-programming active PIRQs to address http://bugzilla.kernel.org/show_bug.cgi?id=1186
This change will break those broken systems again, unless they've been fixed in other ways.
Mitigated the impact with a new cmdline parameter "acpi_irq_static" that restores the previous behavior of not moving PCI Link devices that are marked active.
===== drivers/acpi/pci_link.c 1.20 vs edited =====
drivers/acpi/pci_link.c +143 -27
@@ -99,7 +99,7 @@ void *context) {struct acpi_pci_link *link = (struct acpi_pci_link *) context; -int i =0 ; +u32 i =0 ;ACPI_FUNCTION_TRACE ("acpi_pci_link_check_possible" );
@@ -294,7 +294,10 @@if (!link->irq.active) {ACPI_DEBUG_PRINT ((ACPI_DB_ERROR ,"No active IRQ resource found\n" )); -printk (KERN_WARNING "_CRS returns NULL! Using IRQ %d for device (%s [%s]).\n" , irq,acpi_device_name (link->device),acpi_device_bid (link->device)); +printk (KERN_WARNING "_CRS returns NULL! Using IRQ %d for" +"device (%s [%s]).\n" , irq, +acpi_device_name (link->device), +acpi_device_bid (link->device)); link->irq.active = irq; }
@@ -429,30 +432,61 @@PCI Link IRQ Management -------------------------------------------------------------------------- */ -#define ACPI_MAX_IRQS 256 -#define ACPI_MAX_ISA_IRQ 16 -/* -* IRQ penalties are used to promote PCI IRQ balancing. We set each ISA- -* possible IRQ (0-15) with a default penalty relative to its feasibility -* for PCI's use: +* Interrupt sharing can impact performance, so this code +* spreads the PCI link devices across the available IRQs. +* +* A tables of penalties avoids directing PCI interrupts to well known +* ISA IRQs. Boot params are available to over-ride the default table: * -* Never use: 0, 1, 2 (timer, keyboard, and cascade) -* Avoid using: 13, 14, and 15 (FP error and IDE) -* Penalize: 3, 4, 6, 7, 12 (known ISA uses) +* List interrupts that are free for PCI use. +* acpi_irq_pci=n[,m] * -* Thus we're left with IRQs 5, 9, 10, 11, and everything above 15 (IO[S]APIC) -* as 'best bets' for PCI use. +* List interrupts that should not be used for PCI: +* acpi_irq_isa=n[,m] +* +* Note that PCI IRQ routers have a list of possible IRQs, +* which may not include the IRQs this table says are available. +* +* Since this heuristic can't tell the difference between a link +* that no device will attach to, vs. a link which may be shared +* by multiple active devices -- it is not optimal. +* +* If interrupt performance is that important, get an IO-APIC system +* with a pin dedicated to each device. Or for that matter, an MSI +* enabled system. */ +#define ACPI_MAX_IRQS 256 +#define ACPI_MAX_ISA_IRQ 16 + +#define PIRQ_PENALTY_PCI_AVAILABLE (0) +#define PIRQ_PENALTY_PCI_POSSIBLE (16*16) +#define PIRQ_PENALTY_PCI_USING (16*16*16) +#define PIRQ_PENALTY_ISA_TYPICAL (16*16*16*16) +#define PIRQ_PENALTY_ISA_USED (16*16*16*16*16) +#define PIRQ_PENALTY_ISA_ALWAYS (16*16*16*16*16*16) +static int acpi_irq_penalty[ACPI_MAX_IRQS ] = { -1000000 ,1000000 ,1000000 ,10000 , -10000 ,0 ,10000 ,10000 , -10000 ,0 ,0 ,0 , -10000 ,100000 ,100000 ,100000 , +PIRQ_PENALTY_ISA_ALWAYS ,/* IRQ0 timer */ +PIRQ_PENALTY_ISA_ALWAYS ,/* IRQ1 keyboard */ +PIRQ_PENALTY_ISA_ALWAYS ,/* IRQ2 cascade */ +PIRQ_PENALTY_ISA_TYPICAL ,/* IRQ3 serial */ +PIRQ_PENALTY_ISA_TYPICAL ,/* IRQ4 serial */ +PIRQ_PENALTY_PCI_AVAILABLE ,/* IRQ5 PCI */ +PIRQ_PENALTY_ISA_TYPICAL ,/* IRQ6 */ +PIRQ_PENALTY_ISA_TYPICAL ,/* IRQ7 parallel, spurious */ +PIRQ_PENALTY_ISA_TYPICAL ,/* IRQ8 rtc, sometimes */ +PIRQ_PENALTY_PCI_AVAILABLE ,/* IRQ9 PCI, often acpi */ +PIRQ_PENALTY_PCI_AVAILABLE ,/* IRQ10 PCI */ +PIRQ_PENALTY_PCI_AVAILABLE ,/* IRQ11 PCI */ +PIRQ_PENALTY_ISA_TYPICAL ,/* IRQ12 mouse */ +PIRQ_PENALTY_ISA_USED ,/* IRQ13 fpe, sometimes */ +PIRQ_PENALTY_ISA_USED ,/* IRQ14 ide0 */ +PIRQ_PENALTY_ISA_USED ,/* IRQ15 ide1 */ +/* >IRQ15 */ }; -int acpi_pci_link_check (void ) {
@@ -473,20 +507,27 @@continue ; } -if (link->irq.active) - acpi_irq_penalty[link->irq.active] +=100 ; -else if (link->irq.possible_count) { -int penalty =100 / link->irq.possible_count; -for (i=0 ; i<link->irq.possible_count; i++) { +/* +* reflect the possible and active irqs in the penalty table -- +* useful for breaking ties. +*/ +if (link->irq.possible_count) { +int penalty =PIRQ_PENALTY_PCI_POSSIBLE / link->irq.possible_count; + +for (i =0 ; i < link->irq.possible_count; i++) {if (link->irq.possible[i] <ACPI_MAX_ISA_IRQ ) acpi_irq_penalty[link->irq.possible[i]] += penalty; } + + }else if (link->irq.active) { + acpi_irq_penalty[link->irq.active] +=PIRQ_PENALTY_PCI_POSSIBLE ; } } -return_VALUE (0 ); } +static int acpi_irq_static;/* don't re-program active IRQs */ +static int acpi_pci_link_allocate (struct acpi_pci_link * link) {int irq;int i;
@@ -500,12 +541,14 @@ irq = link->irq.active; }else { irq = link->irq.possible[0 ]; + } +if (!(link->irq.active && acpi_irq_static)) {/* * Select the best IRQ. This is done in reverse to promote * the use of IRQs 9, 10, 11, and >15. */ -for (i=(link->irq.possible_count-1 ); i>0 ; i--) { +for (i = (link->irq.possible_count -1 ); i >=0 ; i--) {if (acpi_irq_penalty[irq] > acpi_irq_penalty[link->irq.possible[i]]) irq = link->irq.possible[i]; }
@@ -518,13 +561,14 @@ acpi_device_bid(link->device));return_VALUE (-ENODEV ); }else { - acpi_irq_penalty[link->irq.active] +=100 ; + acpi_irq_penalty[link->irq.active] +=PIRQ_PENALTY_PCI_USING ;printk (PREFIX "%s [%s] enabled at IRQ %d\n" ,acpi_device_name (link->device),acpi_device_bid (link->device), link->irq.active); } link->irq.setonboot =1 ; +return_VALUE (0 ); }
@@ -607,9 +651,12 @@if (result) goto end; +/* query and set link->irq.active */ acpi_pci_link_get_current (link); -printk (PREFIX "%s [%s] (IRQs" ,acpi_device_name (device),acpi_device_bid (device)); +//#ifdef CONFIG_ACPI_DEBUG +printk (PREFIX "%s [%s] (IRQs" ,acpi_device_name (device), +acpi_device_bid (device));for (i =0 ; i < link->irq.possible_count; i++) {if (link->irq.active == link->irq.possible[i]) {printk (" *%d" , link->irq.possible[i]);
@@ -619,6 +666,7 @@printk (" %d" , link->irq.possible[i]); }printk (")\n" ); +//#endif /* CONFIG_ACPI_DEBUG */ /* TBD: Acquire/release lock */ list_add_tail (&link->node, &acpi_link.entries);
@@ -670,5 +718,73 @@return_VALUE (0 ); } + +/* +* modify acpi_irq_penalty[] from cmdline +*/ +static int __init acpi_irq_penalty_update (char *str,int used) +{ +int i; + +for (i =0 ; i <16 ; i++) { +int retval; +int irq; + + retval =get_option (&str,&irq); + +if (!retval) +break ;/* no number found */ + +if (irq <0 ) +continue ; + +if (irq >=ACPI_MAX_IRQS ) +continue ; + +if (used) + acpi_irq_penalty[irq] +=PIRQ_PENALTY_ISA_USED ; +else + acpi_irq_penalty[irq] =PIRQ_PENALTY_PCI_AVAILABLE ; + +if (retval !=2 )/* no next number */ +break ; + } +return 1 ; +} + +/* +* Over-ride default table to reserve additional IRQs for use by ISA +* e.g. acpi_irq_isa=5 +* Useful for telling ACPI how not to interfere with your ISA sound card. +*/ +static int __init acpi_irq_isa (char *str) +{ +return (acpi_irq_penalty_update (str,1 )); +} +__setup ("acpi_irq_isa=" , acpi_irq_isa); + + +/* +* Over-ride default table to free additional IRQs for use by PCI +* e.g. acpi_irq_pci=7,15 +* Useful for freeing up IRQs to reduce PCI IRQ sharing. +*/ +static int __init acpi_irq_pci (char *str) +{ +return (acpi_irq_penalty_update (str,0 )); +} +__setup ("acpi_irq_pci=" , acpi_irq_pci); + +/* +* Do not re-program active IRQs, +* keep the static active settings left by the BIOS. +* Useful for working around broken BIOS. +*/ +static int __init acpi_irq_static_set (char *str) +{ + acpi_irq_static =1 ; +return (1 ); +} +__setup ("acpi_irq_static" , acpi_irq_static_set);subsys_initcall (acpi_pci_link_init);