summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/kernel-parameters.txt9
-rw-r--r--drivers/pci/Kconfig13
-rw-r--r--drivers/pci/pci.c4
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--drivers/pci/setup-bus.c75
5 files changed, 82 insertions, 21 deletions
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/Kconfig b/drivers/pci/Kconfig
index 37856f7c778..848bfb84c04 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -31,6 +31,19 @@ config PCI_DEBUG
When in doubt, say N.
+config PCI_REALLOC_ENABLE_AUTO
+ bool "Enable PCI resource re-allocation detection"
+ depends on PCI
+ help
+ Say Y here if you want the PCI core to detect if PCI resource
+ re-allocation needs to be enabled. You can always use pci=realloc=on
+ or pci=realloc=off to override it. Note this feature is a no-op
+ unless PCI_IOV support is also enabled; in that case it will
+ automatically re-allocate PCI resources if SR-IOV BARs have not
+ been allocated by the BIOS.
+
+ When in doubt, say N.
+
config PCI_STUB
tristate "PCI Stub driver"
depends on PCI
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 e241f2fd6cb..8fa2d4be88d 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -51,13 +51,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
@@ -1276,6 +1269,58 @@ 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;
+}
+
+static void __init pci_realloc_detect(void)
+{
+#if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO)
+ struct pci_dev *dev = NULL;
+
+ if (pci_realloc_enable != undefined)
+ return;
+
+ for_each_pci_dev(dev) {
+ int i;
+
+ for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) {
+ struct resource *r = &dev->resource[i];
+
+ /* Not assigned, or rejected by kernel ? */
+ if (r->flags && !r->start) {
+ pci_realloc_enable = auto_enabled;
+
+ return;
+ }
+ }
+ }
+#endif
+}
/*
* first try will not touch pci bridge res
@@ -1295,10 +1340,10 @@ pci_assign_unassigned_resources(void)
struct pci_dev_resource *fail_res;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH;
- unsigned long failed_type;
int pci_try_num = 1;
/* don't realloc if asked to do so */
+ pci_realloc_detect();
if (pci_realloc_enabled()) {
int max_depth = pci_get_max_depth();
@@ -1330,16 +1375,12 @@ again:
if (list_empty(&fail_head))
goto enable_and_dump;
- failed_type = 0;
- list_for_each_entry(fail_res, &fail_head, list)
- failed_type |= fail_res->flags;
+ if (tried_times >= pci_try_num) {
+ if (pci_realloc_enable == undefined)
+ printk(KERN_INFO "Some PCI device resources are unassigned, try booting with pci=realloc\n");
+ else if (pci_realloc_enable == auto_enabled)
+ printk(KERN_INFO "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n");
- /*
- * io port are tight, don't try extra
- * or if reach the limit, don't want to try more
- */
- failed_type &= type_mask;
- if ((failed_type == IORESOURCE_IO) || (tried_times >= pci_try_num)) {
free_list(&fail_head);
goto enable_and_dump;
}