From efdc87dab1cdf25ba631181ac0ead3fb2023dd10 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 27 Jan 2012 10:55:10 -0800 Subject: PCI: Separate pci_bus_read_dev_vendor_id from pci_scan_device We can reuse it for pciehp probing. -v2: according to Kenji, fix crs timeout checking, and export the function for later use when pciehp is compiled as a module. Suggested-by: Matthew Wilcox Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/pci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/pci/pci.h') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1009a5e88e5..aaf7ff8c517 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -207,6 +207,8 @@ enum pci_bar_type { pci_bar_mem64, /* A 64-bit memory BAR */ }; +bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, + int crs_timeout); extern int pci_setup_device(struct pci_dev *dev); extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg); -- cgit v1.2.3-70-g09d2 From f796841e49fe086176e27ed0e1f3f7a1123a4a6b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 11 Feb 2012 00:18:30 -0800 Subject: PCI: fix memleak for pci dev removing during hotplug unreferenced object 0xffff880276d17700 (size 64): comm "swapper/0", pid 1, jiffies 4294897182 (age 3976.028s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 18 f9 de 76 02 88 ff ff ...........v.... 10 00 00 00 0e 00 00 00 0f 28 40 00 00 00 00 00 .........(@..... backtrace: [] kmemleak_alloc+0x26/0x43 [] __kmalloc+0x121/0x183 [] pci_add_cap_save_buffer+0x35/0x7c [] pci_allocate_cap_save_buffers+0x1d/0x65 [] pci_device_add+0x92/0xf1 [] pci_scan_single_device+0x9f/0xa1 [] pci_scan_slot.part.20+0x21/0x106 [] pci_scan_slot+0x2b/0x35 [] __pci_scan_child_bus+0x51/0x107 [] pci_scan_bridge+0x376/0x6ae [] __pci_scan_child_bus+0xcd/0x107 [] pci_scan_child_bus+0x11/0x2a [] pci_acpi_scan_root+0x18b/0x21c [] acpi_pci_root_add+0x1e1/0x42a [] acpi_device_probe+0x50/0x190 [] really_probe+0x99/0x126 Need to free saved_buffer for capabilities. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 9 +++++++++ drivers/pci/pci.h | 1 + drivers/pci/probe.c | 1 + 3 files changed, 11 insertions(+) (limited to 'drivers/pci/pci.h') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9c89447e7b2..e7dfcd44757 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1916,6 +1916,15 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) "unable to preallocate PCI-X save buffer\n"); } +void pci_free_cap_save_buffers(struct pci_dev *dev) +{ + struct pci_cap_saved_state *tmp; + struct hlist_node *pos, *n; + + hlist_for_each_entry_safe(tmp, pos, n, &dev->saved_cap_space, next) + kfree(tmp); +} + /** * pci_enable_ari - enable ARI forwarding if hardware support it * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index aaf7ff8c517..586ac9b097e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -73,6 +73,7 @@ extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); 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); +void pci_free_cap_save_buffers(struct pci_dev *dev); static inline void pci_wakeup_event(struct pci_dev *dev) { diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index aa9b1dec0d3..dc904bd4b56 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1024,6 +1024,7 @@ static void pci_release_capabilities(struct pci_dev *dev) { pci_vpd_release(dev); pci_iov_release(dev); + pci_free_cap_save_buffers(dev); } /** -- cgit v1.2.3-70-g09d2 From b55438fdd5173a367659a7e200acea6c9f77b8cb Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 23 Feb 2012 19:23:30 -0800 Subject: PCI: prepare pci=realloc for multiple options Let the user could enable and disable with pci=realloc=on or pci=realloc=off Also 1. move variable and functions near the place they are used. 2. change macro to function 3. change related functions and variable to static and _init 4. update parameter description accordingly. This will let us add a config option to control default behavior, and still allow the user to turn off automatic reallocation if it fails on their platform until a permanent solution is found. -v2: still honor pci=realloc, and treat it as pci=realloc=on also use enum instead of ... -v3: update kernel-paramenters.txt according to Jesse. Signed-off-by: Yinghai Lu Acked-by: Jesse Barnes Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 9 +++++++-- drivers/pci/pci.c | 4 +++- drivers/pci/pci.h | 2 +- drivers/pci/setup-bus.c | 34 +++++++++++++++++++++++++++------- 4 files changed, 38 insertions(+), 11 deletions(-) (limited to 'drivers/pci/pci.h') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7fb7a4b161f..7dc523e082a 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2109,8 +2109,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. the default. off: Turn ECRC off on: Turn ECRC on. - realloc reallocate PCI resources if allocations done by BIOS - are erroneous. + realloc= Enable/disable reallocating PCI bridge resources + if allocations done by BIOS are too small to + accommodate resources required by all child + devices. + off: Turn realloc off + on: Turn realloc on + realloc same as realloc=on pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power Management. diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f30736dbf3..e9f9dc183cf 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3772,8 +3772,10 @@ static int __init pci_setup(char *str) pci_no_msi(); } else if (!strcmp(str, "noaer")) { pci_no_aer(); + } else if (!strncmp(str, "realloc=", 8)) { + pci_realloc_get_opt(str + 8); } else if (!strncmp(str, "realloc", 7)) { - pci_realloc(); + pci_realloc_get_opt("on"); } else if (!strcmp(str, "nodomains")) { pci_no_domains(); } else if (!strncmp(str, "cbiosize=", 9)) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 586ac9b097e..1fc63b39f83 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -149,7 +149,7 @@ static inline void pci_no_msi(void) { } static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } #endif -extern void pci_realloc(void); +void pci_realloc_get_opt(char *); static inline int pci_no_d1d2(struct pci_dev *dev) { diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 162edfb356b..219722df68d 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -48,13 +48,6 @@ static void free_list(struct list_head *head) } } -int pci_realloc_enable = 0; -#define pci_realloc_enabled() pci_realloc_enable -void pci_realloc(void) -{ - pci_realloc_enable = 1; -} - /** * add_to_list() - add a new resource tracker to the list * @head: Head of the list @@ -1273,6 +1266,33 @@ static int __init pci_get_max_depth(void) return depth; } +/* + * -1: undefined, will auto detect later + * 0: disabled by user + * 1: disabled by auto detect + * 2: enabled by user + * 3: enabled by auto detect + */ +enum enable_type { + undefined = -1, + user_disabled, + auto_disabled, + user_enabled, + auto_enabled, +}; + +static enum enable_type pci_realloc_enable __initdata = undefined; +void __init pci_realloc_get_opt(char *str) +{ + if (!strncmp(str, "off", 3)) + pci_realloc_enable = user_disabled; + else if (!strncmp(str, "on", 2)) + pci_realloc_enable = user_enabled; +} +static bool __init pci_realloc_enabled(void) +{ + return pci_realloc_enable >= user_enabled; +} /* * first try will not touch pci bridge res -- cgit v1.2.3-70-g09d2 From 2069ecfbe14ebd71a6f98e8a00724e9adf4fe4ee Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 15 Feb 2012 21:40:31 -0800 Subject: PCI: Move "pci reassigndev resource alignment" out of quirks.c This isn't really a quirk; calling it directly from pci_add_device makes more sense. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 5 +--- drivers/pci/probe.c | 3 +++ drivers/pci/quirks.c | 63 ------------------------------------------------- drivers/pci/setup-res.c | 4 ---- 5 files changed, 66 insertions(+), 71 deletions(-) (limited to 'drivers/pci/pci.h') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e9f9dc183cf..b832f0fece9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3694,6 +3694,68 @@ int pci_is_reassigndev(struct pci_dev *dev) return (pci_specified_resource_alignment(dev) != 0); } +/* + * This function disables memory decoding and releases memory resources + * of the device specified by kernel's boot parameter 'pci=resource_alignment='. + * It also rounds up size to specified alignment. + * Later on, the kernel will assign page-aligned memory resource back + * to the device. + */ +void pci_reassigndev_resource_alignment(struct pci_dev *dev) +{ + int i; + struct resource *r; + resource_size_t align, size; + u16 command; + + if (!pci_is_reassigndev(dev)) + return; + + if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && + (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { + dev_warn(&dev->dev, + "Can't reassign resources to host bridge.\n"); + return; + } + + dev_info(&dev->dev, + "Disabling memory decoding and releasing memory resources.\n"); + pci_read_config_word(dev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(dev, PCI_COMMAND, command); + + align = pci_specified_resource_alignment(dev); + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + size = resource_size(r); + if (size < align) { + size = align; + dev_info(&dev->dev, + "Rounding up size of resource #%d to %#llx.\n", + i, (unsigned long long)size); + } + r->end = size - 1; + r->start = 0; + } + /* Need to disable bridge's resource window, + * to enable the kernel to reassign new resource + * window later on. + */ + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + r->end = resource_size(r) - 1; + r->start = 0; + } + pci_disable_bridge_window(dev); + } +} + ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) { if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1fc63b39f83..e4943479b23 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -228,11 +228,8 @@ static inline int pci_ari_enabled(struct pci_bus *bus) return bus->self && bus->self->ari_enabled; } -#ifdef CONFIG_PCI_QUIRKS -extern int pci_is_reassigndev(struct pci_dev *dev); -resource_size_t pci_specified_resource_alignment(struct pci_dev *dev); +void pci_reassigndev_resource_alignment(struct pci_dev *dev); extern void pci_disable_bridge_window(struct pci_dev *dev); -#endif /* Single Root I/O Virtualization */ struct pci_sriov { diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 36c22032ea1..944e05a66b9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1325,6 +1325,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) /* Fix up broken headers */ pci_fixup_device(pci_fixup_header, dev); + /* moved out from quirk header fixup code */ + pci_reassigndev_resource_alignment(dev); + /* Clear the state_saved flag. */ dev->state_saved = false; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e198589d099..f8f81d4f29f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -31,69 +31,6 @@ #include /* isa_dma_bridge_buggy */ #include "pci.h" -/* - * This quirk function disables memory decoding and releases memory resources - * of the device specified by kernel's boot parameter 'pci=resource_alignment='. - * It also rounds up size to specified alignment. - * Later on, the kernel will assign page-aligned memory resource back - * to the device. - */ -static void __devinit quirk_resource_alignment(struct pci_dev *dev) -{ - int i; - struct resource *r; - resource_size_t align, size; - u16 command; - - if (!pci_is_reassigndev(dev)) - return; - - if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && - (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { - dev_warn(&dev->dev, - "Can't reassign resources to host bridge.\n"); - return; - } - - dev_info(&dev->dev, - "Disabling memory decoding and releasing memory resources.\n"); - pci_read_config_word(dev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MEMORY; - pci_write_config_word(dev, PCI_COMMAND, command); - - align = pci_specified_resource_alignment(dev); - for (i=0; i < PCI_BRIDGE_RESOURCES; i++) { - r = &dev->resource[i]; - if (!(r->flags & IORESOURCE_MEM)) - continue; - size = resource_size(r); - if (size < align) { - size = align; - dev_info(&dev->dev, - "Rounding up size of resource #%d to %#llx.\n", - i, (unsigned long long)size); - } - r->end = size - 1; - r->start = 0; - } - /* Need to disable bridge's resource window, - * to enable the kernel to reassign new resource - * window later on. - */ - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && - (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { - for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { - r = &dev->resource[i]; - if (!(r->flags & IORESOURCE_MEM)) - continue; - r->end = resource_size(r) - 1; - r->start = 0; - } - pci_disable_bridge_window(dev); - } -} -DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment); - /* * Decoding should be disabled for a PCI device during BAR sizing to avoid * conflict. But doing so may cause problems on host bridge and perhaps other diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index f968185aa19..eea85dafc76 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -114,7 +114,6 @@ int pci_claim_resource(struct pci_dev *dev, int resource) } EXPORT_SYMBOL(pci_claim_resource); -#ifdef CONFIG_PCI_QUIRKS void pci_disable_bridge_window(struct pci_dev *dev) { dev_info(&dev->dev, "disabling bridge mem windows\n"); @@ -127,9 +126,6 @@ void pci_disable_bridge_window(struct pci_dev *dev) pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); } -#endif /* CONFIG_PCI_QUIRKS */ - - static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, int resno, resource_size_t size, resource_size_t align) -- cgit v1.2.3-70-g09d2