From 42b219322a97ccef347388b233aceaafe3fa517d Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 2 Sep 2010 14:28:51 -0700 Subject: PCI: pci_driver make name const The name field in pci_driver should be const, it is not modified by PCI subsystem. Signed-off-by: Stephen Hemminger Signed-off-by: Jesse Barnes --- include/linux/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index c8d95e369ff4..30faf4f3db0b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -541,7 +541,7 @@ struct pci_error_handlers { struct module; struct pci_driver { struct list_head node; - char *name; + const char *name; const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */ int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */ void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */ -- cgit v1.2.3-70-g09d2 From 66db60eaf158aa953651d03e43e931e757e87262 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 21 Sep 2010 13:54:39 -0400 Subject: PCI: add quirk for non-symmetric-mode irq routing to versions 0 and 4 of the MCP55 northbridge A long time ago I worked on a RHEL5 bug in which kdump hung during boot on a set of systems. The systems hung because they never received timer interrupts during calibrate_delay. These systems also all had Opteron processors on a hypertransport bus, bridged to a pci bus via an Nvidia MCP55 northbridge chip. After much wrangling I managed to learn from Nvidia that they have an undocumented register in some versions of that chip which control how legacy interrupts are send to the cpu complex when the ioapic isn't active. Nvidia defaults this register to only send legacy interrupts to the BSP, so if kdump happens to boot on an AP, we never get timer interrupts and boom. I had initially used this quirk as a workaround, with my intent being to move apic initalization to an earlier point in the boot process, so the setting of the register would be irrelevant. Given the work involved in doing that however, the fragile nature of the apic initalization code, and the fact that, over the 2 years since we found this bug, the MCP55 is the only chip which seems to have this issue, I've figure at this point its likely safer to just carry the quirk around. By setting the referenced bits in this hidden register, interrupts will be broadcast to all cpus when the ioapic isn't active on the above described systems. Acked-by: Simon Horman Acked-by: Vivek Goyal Signed-off-by: Neil Horman Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 31 +++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 2 ++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 857ae01734a6..034430690a5b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2296,6 +2296,37 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_15, nvenet_msi_disable); +/* + * Some versions of the MCP55 bridge from nvidia have a legacy irq routing + * config register. This register controls the routing of legacy interrupts + * from devices that route through the MCP55. If this register is misprogramed + * interrupts are only sent to the bsp, unlike conventional systems where the + * irq is broadxast to all online cpus. Not having this register set + * properly prevents kdump from booting up properly, so lets make sure that + * we have it set correctly. + * Note this is an undocumented register. + */ +static void __devinit nvbridge_check_legacy_irq_routing(struct pci_dev *dev) +{ + u32 cfg; + + pci_read_config_dword(dev, 0x74, &cfg); + + if (cfg & ((1 << 2) | (1 << 15))) { + printk(KERN_INFO "Rewriting irq routing register on MCP55\n"); + cfg &= ~((1 << 2) | (1 << 15)); + pci_write_config_dword(dev, 0x74, cfg); + } +} + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, + PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0, + nvbridge_check_legacy_irq_routing); + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, + PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4, + nvbridge_check_legacy_irq_routing); + static int __devinit ht_check_msi_mapping(struct pci_dev *dev) { int pos, ttl = 48; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 570fddeb0388..dc2827723c1e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1249,6 +1249,8 @@ #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2 0x0348 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000 0x034C #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E +#define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0 0x0360 +#define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4 0x0364 #define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS 0x03EB -- cgit v1.2.3-70-g09d2 From 25143fd1270d28782ae0620aa86ef5f8c14030fd Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Fri, 10 Sep 2010 16:36:39 -0700 Subject: x86/PCI: irq and pci_ids patch for Intel Patsburg DeviceIDs This patch adds the LPC Controller DeviceIDs for the Intel Patsburg PCH. Signed-off-by: Seth Heasley Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 1 + include/linux/pci_ids.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index f547ee05f715..ee7fc8fc8a83 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -589,6 +589,7 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route case PCI_DEVICE_ID_INTEL_ICH10_1: case PCI_DEVICE_ID_INTEL_ICH10_2: case PCI_DEVICE_ID_INTEL_ICH10_3: + case PCI_DEVICE_ID_INTEL_PBG_LPC: r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index dc2827723c1e..b9ff2801cf76 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2436,6 +2436,7 @@ #define PCI_DEVICE_ID_INTEL_CPT_SMBUS 0x1c22 #define PCI_DEVICE_ID_INTEL_CPT_LPC_MIN 0x1c41 #define PCI_DEVICE_ID_INTEL_CPT_LPC_MAX 0x1c5f +#define PCI_DEVICE_ID_INTEL_PBG_LPC 0x1d40 #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 #define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 -- cgit v1.2.3-70-g09d2 From 7473fbf4a016301bfa3faa4f81c9a9c978330359 Mon Sep 17 00:00:00 2001 From: Anders Wallin Date: Thu, 23 Sep 2010 19:39:04 +0200 Subject: PCI: add PCI vendor id for STmicroelectronics Signed-off-by: Anders Wallin Signed-off-by: Jesse Barnes --- include/linux/pci_ids.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index b9ff2801cf76..ea5a3d19aaba 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -765,6 +765,8 @@ #define PCI_DEVICE_ID_ELSA_MICROLINK 0x1000 #define PCI_DEVICE_ID_ELSA_QS3000 0x3000 +#define PCI_VENDOR_ID_STMICRO 0x104A + #define PCI_VENDOR_ID_BUSLOGIC 0x104B #define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140 #define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040 -- cgit v1.2.3-70-g09d2 From db5004195481fcb500c929bd3a0e1c0c48eec527 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Wed, 13 Oct 2010 15:00:23 +0900 Subject: PCI: add PCI_MSIX_TABLE/PBA defines These are already defined in pcilib's pci/header.h but not in kernel's linux/pci_regs.h. Copy them to avoid using magic numbers. Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.h | 4 ++-- include/linux/pci_regs.h | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index de27c1cb5a2b..feff3bee6fe5 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h @@ -22,8 +22,8 @@ #define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) #define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) -#define msix_table_offset_reg(base) (base + 0x04) -#define msix_pba_offset_reg(base) (base + 0x08) +#define msix_table_offset_reg(base) (base + PCI_MSIX_TABLE) +#define msix_pba_offset_reg(base) (base + PCI_MSIX_PBA) #define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) #define multi_msix_capable(control) msix_table_size((control)) diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 455b9ccdfca7..af83076c31a6 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -300,12 +300,14 @@ #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ #define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ -/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */ +/* MSI-X registers */ #define PCI_MSIX_FLAGS 2 #define PCI_MSIX_FLAGS_QSIZE 0x7FF #define PCI_MSIX_FLAGS_ENABLE (1 << 15) #define PCI_MSIX_FLAGS_MASKALL (1 << 14) -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_MSIX_TABLE 4 +#define PCI_MSIX_PBA 8 +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) /* CompactPCI Hotswap Register */ -- cgit v1.2.3-70-g09d2 From cb04e95bdd0bfd618ab731c84a3ab56b56974df8 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Mon, 4 Oct 2010 13:27:14 -0700 Subject: PCI: update Intel chipset names and defines This patch updates the defines for Intel devices in include/linux/pci_ids.h, referenced in arch/x86/pci/irq.c and drivers/i2c/busses/i2c-i801.c, reflecting approved legal branding, and using fuller code-names for products under development. Acked-by: Jean Delvare Signed-off-by: Seth Heasley Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 12 ++++++------ drivers/i2c/busses/Kconfig | 4 ++-- drivers/i2c/busses/i2c-i801.c | 10 +++++----- include/linux/pci_ids.h | 18 +++++++++--------- 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index ee7fc8fc8a83..9f9bfb705cf9 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -584,28 +584,28 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route case PCI_DEVICE_ID_INTEL_ICH9_3: case PCI_DEVICE_ID_INTEL_ICH9_4: case PCI_DEVICE_ID_INTEL_ICH9_5: - case PCI_DEVICE_ID_INTEL_TOLAPAI_0: + case PCI_DEVICE_ID_INTEL_EP80579_0: case PCI_DEVICE_ID_INTEL_ICH10_0: case PCI_DEVICE_ID_INTEL_ICH10_1: case PCI_DEVICE_ID_INTEL_ICH10_2: case PCI_DEVICE_ID_INTEL_ICH10_3: - case PCI_DEVICE_ID_INTEL_PBG_LPC: + case PCI_DEVICE_ID_INTEL_PATSBURG_LPC: r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; return 1; } - if ((device >= PCI_DEVICE_ID_INTEL_PCH_LPC_MIN) && - (device <= PCI_DEVICE_ID_INTEL_PCH_LPC_MAX)) { + if ((device >= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN) && + (device <= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX)) { r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; return 1; } - if ((device >= PCI_DEVICE_ID_INTEL_CPT_LPC_MIN) && - (device <= PCI_DEVICE_ID_INTEL_CPT_LPC_MAX)) { + if ((device >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN) && + (device <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX)) { r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 6539ac2907e9..fd455a2fdd12 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -95,9 +95,9 @@ config I2C_I801 ESB2 ICH8 ICH9 - Tolapai + EP80579 (Tolapai) ICH10 - 3400/5 Series (PCH) + 5/3400 Series (PCH) Cougar Point (PCH) This driver can also be built as a module. If so, the module diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index c60081169cc3..59d65981eed7 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -38,10 +38,10 @@ 82801G (ICH7) 0x27da 32 hard yes yes yes 82801H (ICH8) 0x283e 32 hard yes yes yes 82801I (ICH9) 0x2930 32 hard yes yes yes - Tolapai 0x5032 32 hard yes yes yes + EP80579 (Tolapai) 0x5032 32 hard yes yes yes ICH10 0x3a30 32 hard yes yes yes ICH10 0x3a60 32 hard yes yes yes - 3400/5 Series (PCH) 0x3b30 32 hard yes yes yes + 5/3400 Series (PCH) 0x3b30 32 hard yes yes yes Cougar Point (PCH) 0x1c22 32 hard yes yes yes Features supported by this driver: @@ -587,11 +587,11 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_17) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_5) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_6) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TOLAPAI_1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EP80579_1) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_4) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_5) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PCH_SMBUS) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CPT_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS) }, { 0, } }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ea5a3d19aaba..bb6daa5f8240 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2435,10 +2435,10 @@ #define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 #define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 #define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 -#define PCI_DEVICE_ID_INTEL_CPT_SMBUS 0x1c22 -#define PCI_DEVICE_ID_INTEL_CPT_LPC_MIN 0x1c41 -#define PCI_DEVICE_ID_INTEL_CPT_LPC_MAX 0x1c5f -#define PCI_DEVICE_ID_INTEL_PBG_LPC 0x1d40 +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22 +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN 0x1c41 +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX 0x1c5f +#define PCI_DEVICE_ID_INTEL_PATSBURG_LPC 0x1d40 #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 #define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 @@ -2644,9 +2644,9 @@ #define PCI_DEVICE_ID_INTEL_ICH10_3 0x3a1a #define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 #define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 -#define PCI_DEVICE_ID_INTEL_PCH_LPC_MIN 0x3b00 -#define PCI_DEVICE_ID_INTEL_PCH_LPC_MAX 0x3b1f -#define PCI_DEVICE_ID_INTEL_PCH_SMBUS 0x3b30 +#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN 0x3b00 +#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX 0x3b1f +#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f #define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 #define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 @@ -2655,8 +2655,8 @@ #define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035 #define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 #define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff -#define PCI_DEVICE_ID_INTEL_TOLAPAI_0 0x5031 -#define PCI_DEVICE_ID_INTEL_TOLAPAI_1 0x5032 +#define PCI_DEVICE_ID_INTEL_EP80579_0 0x5031 +#define PCI_DEVICE_ID_INTEL_EP80579_1 0x5032 #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 #define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 -- cgit v1.2.3-70-g09d2 From bf4d29086972ceaeaf72544d8f64933c2cfdc992 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 4 Oct 2010 14:22:26 -0400 Subject: PCI: Export some PCI PM functionality It's helpful to have some extra PCI power management functions available to platform code, so move the declarations to an exported header. Acked-by: Rafael J. Wysocki Signed-off-by: Matthew Garrett Signed-off-by: Jesse Barnes --- drivers/pci/pci.h | 3 --- include/linux/pci.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6beb11b617a9..f5c7c382765f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -63,11 +63,8 @@ struct pci_platform_pm_ops { extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); -extern bool pci_check_pme_status(struct pci_dev *dev); extern int pci_finish_runtime_suspend(struct pci_dev *dev); -extern void pci_wakeup_event(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); -extern void pci_pme_wakeup_bus(struct pci_bus *bus); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index 30faf4f3db0b..7454408c41b6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -819,6 +819,9 @@ pci_power_t pci_target_state(struct pci_dev *dev); int pci_prepare_to_sleep(struct pci_dev *dev); int pci_back_from_sleep(struct pci_dev *dev); bool pci_dev_run_wake(struct pci_dev *dev); +bool pci_check_pme_status(struct pci_dev *dev); +void pci_wakeup_event(struct pci_dev *dev); +void pci_pme_wakeup_bus(struct pci_bus *bus); static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) -- cgit v1.2.3-70-g09d2 From e7f8567db9a7f6b3151b0b275e245c1cef0d9c70 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 26 Oct 2010 15:41:33 -0600 Subject: resources: support allocating space within a region from the top down Allocate space from the top of a region first, then work downward, if an architecture desires this. When we allocate space from a resource, we look for gaps between children of the resource. Previously, we always looked at gaps from the bottom up. For example, given this: [mem 0xbff00000-0xf7ffffff] PCI Bus 0000:00 [mem 0xbff00000-0xbfffffff] gap -- available [mem 0xc0000000-0xdfffffff] PCI Bus 0000:02 [mem 0xe0000000-0xf7ffffff] gap -- available we attempted to allocate from the [mem 0xbff00000-0xbfffffff] gap first, then the [mem 0xe0000000-0xf7ffffff] gap. With this patch an architecture can choose to allocate from the top gap [mem 0xe0000000-0xf7ffffff] first. We can't do this across the board because iomem_resource.end is initialized to 0xffffffff_ffffffff on 64-bit architectures, and most machines can't address the entire 64-bit physical address space. Therefore, we only allocate top-down if the arch requests it by clearing "resource_alloc_from_bottom". Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 5 ++ include/linux/ioport.h | 1 + kernel/resource.c | 98 +++++++++++++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8dd7248508a9..fe50cbd315b0 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2156,6 +2156,11 @@ and is between 256 and 4096 characters. It is defined in the file reset_devices [KNL] Force drivers to reset the underlying device during initialization. + resource_alloc_from_bottom + Allocate new resources from the beginning of available + space, not the end. If you need to use this, please + report a bug. + resume= [SWSUSP] Specify the partition device for software suspend diff --git a/include/linux/ioport.h b/include/linux/ioport.h index b22790268b64..d377ea815d45 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -112,6 +112,7 @@ struct resource_list { /* PC/ISA/whatever - the normal PC address spaces: IO and memory */ extern struct resource ioport_resource; extern struct resource iomem_resource; +extern int resource_alloc_from_bottom; extern struct resource *request_resource_conflict(struct resource *root, struct resource *new); extern int request_resource(struct resource *root, struct resource *new); diff --git a/kernel/resource.c b/kernel/resource.c index e15b922d4ba4..716b6804077e 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -40,6 +40,23 @@ EXPORT_SYMBOL(iomem_resource); static DEFINE_RWLOCK(resource_lock); +/* + * By default, we allocate free space bottom-up. The architecture can request + * top-down by clearing this flag. The user can override the architecture's + * choice with the "resource_alloc_from_bottom" kernel boot option, but that + * should only be a debugging tool. + */ +int resource_alloc_from_bottom = 1; + +static __init int setup_alloc_from_bottom(char *s) +{ + printk(KERN_INFO + "resource: allocating from bottom-up; please report a bug\n"); + resource_alloc_from_bottom = 1; + return 0; +} +early_param("resource_alloc_from_bottom", setup_alloc_from_bottom); + static void *r_next(struct seq_file *m, void *v, loff_t *pos) { struct resource *p = v; @@ -379,8 +396,75 @@ static bool resource_contains(struct resource *res1, struct resource *res2) return res1->start <= res2->start && res1->end >= res2->end; } +/* + * Find the resource before "child" in the sibling list of "root" children. + */ +static struct resource *find_sibling_prev(struct resource *root, struct resource *child) +{ + struct resource *this; + + for (this = root->child; this; this = this->sibling) + if (this->sibling == child) + return this; + + return NULL; +} + +/* + * Find empty slot in the resource tree given range and alignment. + * This version allocates from the end of the root resource first. + */ +static int find_resource_from_top(struct resource *root, struct resource *new, + resource_size_t size, resource_size_t min, + resource_size_t max, resource_size_t align, + resource_size_t (*alignf)(void *, + const struct resource *, + resource_size_t, + resource_size_t), + void *alignf_data) +{ + struct resource *this; + struct resource tmp, avail, alloc; + + tmp.start = root->end; + tmp.end = root->end; + + this = find_sibling_prev(root, NULL); + for (;;) { + if (this) { + if (this->end < root->end) + tmp.start = this->end + 1; + } else + tmp.start = root->start; + + resource_clip(&tmp, min, max); + + /* Check for overflow after ALIGN() */ + avail = *new; + avail.start = ALIGN(tmp.start, align); + avail.end = tmp.end; + if (avail.start >= tmp.start) { + alloc.start = alignf(alignf_data, &avail, size, align); + alloc.end = alloc.start + size - 1; + if (resource_contains(&avail, &alloc)) { + new->start = alloc.start; + new->end = alloc.end; + return 0; + } + } + + if (!this || this->start == root->start) + break; + + tmp.end = this->start - 1; + this = find_sibling_prev(root, this); + } + return -EBUSY; +} + /* * Find empty slot in the resource tree given range and alignment. + * This version allocates from the beginning of the root resource first. */ static int find_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, @@ -396,14 +480,15 @@ static int find_resource(struct resource *root, struct resource *new, tmp.start = root->start; /* - * Skip past an allocated resource that starts at 0, since the assignment - * of this->start - 1 to tmp->end below would cause an underflow. + * Skip past an allocated resource that starts at 0, since the + * assignment of this->start - 1 to tmp->end below would cause an + * underflow. */ if (this && this->start == 0) { tmp.start = this->end + 1; this = this->sibling; } - for(;;) { + for (;;) { if (this) tmp.end = this->start - 1; else @@ -424,8 +509,10 @@ static int find_resource(struct resource *root, struct resource *new, return 0; } } + if (!this) break; + tmp.start = this->end + 1; this = this->sibling; } @@ -458,7 +545,10 @@ int allocate_resource(struct resource *root, struct resource *new, alignf = simple_align_resource; write_lock(&resource_lock); - err = find_resource(root, new, size, min, max, align, alignf, alignf_data); + if (resource_alloc_from_bottom) + err = find_resource(root, new, size, min, max, align, alignf, alignf_data); + else + err = find_resource_from_top(root, new, size, min, max, align, alignf, alignf_data); if (err >= 0 && __request_resource(root, new)) err = -EBUSY; write_unlock(&resource_lock); -- cgit v1.2.3-70-g09d2