From patchwork Mon Sep 7 05:27:14 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Eric W. Biederman" X-Patchwork-Id: 46059 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n875RZQr026613 for ; Mon, 7 Sep 2009 05:27:35 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758804AbZIGF1Q (ORCPT ); Mon, 7 Sep 2009 01:27:16 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1758829AbZIGF1Q (ORCPT ); Mon, 7 Sep 2009 01:27:16 -0400 Received: from out02.mta.xmission.com ([166.70.13.232]:37431 "EHLO out02.mta.xmission.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758813AbZIGF1P (ORCPT ); Mon, 7 Sep 2009 01:27:15 -0400 Received: from in02.mta.xmission.com ([166.70.13.52]) by out02.mta.xmission.com with esmtps (TLSv1:AES256-SHA:256) (Exim 4.69) (envelope-from ) id 1MkWl4-0007Yz-Cg; Sun, 06 Sep 2009 23:27:18 -0600 Received: from c-76-21-114-89.hsd1.ca.comcast.net ([76.21.114.89] helo=fess.ebiederm.org) by in02.mta.xmission.com with esmtpsa (TLSv1:AES256-SHA:256) (Exim 4.69) (envelope-from ) id 1MkWl3-0003CJ-GN; Sun, 06 Sep 2009 23:27:18 -0600 Received: from fess.ebiederm.org (localhost [127.0.0.1]) by fess.ebiederm.org (8.14.3/8.14.3/Debian-4) with ESMTP id n875REoj022619; Sun, 6 Sep 2009 22:27:14 -0700 Received: (from eric@localhost) by fess.ebiederm.org (8.14.3/8.14.3/Submit) id n875REnF022618; Sun, 6 Sep 2009 22:27:14 -0700 To: Jesse Barnes CC: Kenji Kaneshige , From: ebiederm@xmission.com (Eric W. Biederman) Date: Sun, 06 Sep 2009 22:27:14 -0700 Message-ID: User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (gnu/linux) MIME-Version: 1.0 X-XM-SPF: eid=; ; ; mid=; ; ; hst=in02.mta.xmission.com; ; ; ip=76.21.114.89; ; ; frm=ebiederm@xmission.com; ; ; spf=neutral X-SA-Exim-Connect-IP: 76.21.114.89 X-SA-Exim-Rcpt-To: jbarnes@virtuousgeek.org, linux-pci@vger.kernel.org, kaneshige.kenji@jp.fujitsu.com X-SA-Exim-Mail-From: ebiederm@xmission.com X-Spam-DCC: XMission; sa01 1397; Body=1 Fuz1=1 Fuz2=1 X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on sa01.xmission.com X-Spam-Level: X-Spam-Status: No, score=-4.0 required=8.0 tests=ALL_TRUSTED,BAYES_00, DCC_CHECK_NEGATIVE, UNTRUSTED_Relay, XM_SPF_Neutral autolearn=disabled version=3.2.5 X-Spam-Combo: ;Jesse Barnes X-Spam-Relay-Country: X-Spam-Report: * -1.8 ALL_TRUSTED Passed through trusted hosts only via SMTP * -2.6 BAYES_00 BODY: Bayesian spam probability is 0 to 1% * [score: 0.0000] * -0.0 DCC_CHECK_NEGATIVE Not listed in DCC * [sa01 1397; Body=1 Fuz1=1 Fuz2=1] * 0.0 XM_SPF_Neutral SPF-Neutral * 0.4 UNTRUSTED_Relay Comes from a non-trusted relay Subject: [PATCH] pcie: Ensure hotplug ports have a minimum number of resources. X-SA-Exim-Version: 4.2.1 (built Thu, 25 Oct 2007 00:26:12 +0000) X-SA-Exim-Scanned: Yes (on in02.mta.xmission.com) Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org In general a BIOS may goof or we may hotplug in a hotplug controller. In either case the kernel needs to reserve resources for plugging in more devices in the future instead of creating a minimal resource assignment. We already do this for cardbus bridges I am just adding a variant for pcie bridges. Signed-off-by: Eric W. Biederman --- drivers/pci/pci.c | 10 ++++++++++ drivers/pci/setup-bus.c | 43 ++++++++++++++++++++++++++++++++++++++----- include/linux/pci.h | 3 +++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7b70312..36efcf3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -41,6 +41,12 @@ int pci_domains_supported = 1; unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; +#define DEFAULT_HOTPLUG_IO_SIZE (256) +#define DEFAULT_HOTPLUG_MEM_SIZE (2*1024*1024) +/* pci=hpmemsize=nnM,hpiosize=nn can override this */ +unsigned long pci_hotplug_io_size = DEFAULT_HOTPLUG_IO_SIZE; +unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE; + /** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * @bus: pointer to PCI bus structure to search @@ -2672,6 +2678,10 @@ static int __init pci_setup(char *str) strlen(str + 19)); } else if (!strncmp(str, "ecrc=", 5)) { pcie_ecrc_get_policy(str + 5); + } else if (!strncmp(str, "hpiosize=", 9)) { + pci_hotplug_io_size = memparse(str + 9, &str); + } else if (!strncmp(str, "hpmemsize=", 10)) { + pci_hotplug_mem_size = memparse(str + 10, &str); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b636e24..5ab90ec 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -309,7 +309,7 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon since these windows have 4K granularity and the IO ranges of non-bridge PCI devices are limited to 256 bytes. We must be careful with the ISA aliasing though. */ -static void pbus_size_io(struct pci_bus *bus) +static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) { struct pci_dev *dev; struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); @@ -336,6 +336,8 @@ static void pbus_size_io(struct pci_bus *bus) size1 += r_size; } } + if (size < min_size) + size = min_size; /* To be fixed in 2.5: we should have sort of HAVE_ISA flag in the struct pci_bus. */ #if defined(CONFIG_ISA) || defined(CONFIG_EISA) @@ -354,7 +356,8 @@ static void pbus_size_io(struct pci_bus *bus) /* Calculate the size of the bus and minimal alignment which guarantees that all child resources fit in this size. */ -static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type) +static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, + unsigned long type, resource_size_t min_size) { struct pci_dev *dev; resource_size_t min_align, align, size; @@ -404,6 +407,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long mem64_mask &= r->flags & IORESOURCE_MEM_64; } } + if (size < min_size) + size = min_size; align = 0; min_align = 0; @@ -479,10 +484,32 @@ static void pci_bus_size_cardbus(struct pci_bus *bus) } } +static int is_pci_hotplug_bridge(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + int pos; + + /* Test for a generic pci hotplug bridge */ + pos = pci_find_capability(bridge, PCI_CAP_ID_EXP); + if (pos) { + u32 reg32; + u16 reg16; + pci_read_config_word(bridge, pos + PCI_EXP_FLAGS, ®16); + if (reg16 & PCI_EXP_FLAGS_SLOT) { + pci_read_config_dword(bridge, + pos + PCI_EXP_SLTCAP, ®32); + if (reg32 & PCI_EXP_SLTCAP_HPC) + return 1; + } + } + return 0; +} + void __ref pci_bus_size_bridges(struct pci_bus *bus) { struct pci_dev *dev; unsigned long mask, prefmask; + resource_size_t min_mem_size = 0, min_io_size = 0; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; @@ -512,8 +539,12 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) case PCI_CLASS_BRIDGE_PCI: pci_bridge_check_ranges(bus); + if (is_pci_hotplug_bridge(bus)) { + min_io_size = pci_hotplug_io_size; + min_mem_size = pci_hotplug_mem_size; + } default: - pbus_size_io(bus); + pbus_size_io(bus, min_io_size); /* If the bridge supports prefetchable range, size it separately. If it doesn't, or its prefetchable window has already been allocated by arch code, try @@ -521,9 +552,11 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) resources. */ mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (pbus_size_mem(bus, prefmask, prefmask)) + if (pbus_size_mem(bus, prefmask, prefmask, min_mem_size)) mask = prefmask; /* Success, size non-prefetch only. */ - pbus_size_mem(bus, mask, IORESOURCE_MEM); + else + min_mem_size += min_mem_size; + pbus_size_mem(bus, mask, IORESOURCE_MEM, min_mem_size); break; } } diff --git a/include/linux/pci.h b/include/linux/pci.h index 115fb7b..d6e57b0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1236,6 +1236,9 @@ extern int pci_pci_problems; extern unsigned long pci_cardbus_io_size; extern unsigned long pci_cardbus_mem_size; +extern unsigned long pci_hotplug_io_size; +extern unsigned long pci_hotplug_mem_size; + int pcibios_add_platform_entries(struct pci_dev *dev); void pcibios_disable_device(struct pci_dev *dev); int pcibios_set_pcie_reset_state(struct pci_dev *dev,