* [PATCH RFC v3 02/21] PCI: Fix race condition in pci_enable/disable_device()
2019-02-04 15:35 [PATCH RFC v3 00/21] PCI: Allow BAR movement during hotplug Sergey Miroshnichenko
@ 2019-02-04 15:35 ` Sergey Miroshnichenko
2019-02-04 15:35 ` [PATCH RFC v3 05/21] PCI: hotplug: Add a flag for the movable BARs feature Sergey Miroshnichenko
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Sergey Miroshnichenko @ 2019-02-04 15:35 UTC (permalink / raw)
To: linux-pci
Cc: Sam Bobroff, Sergey Miroshnichenko, linux, Bjorn Helgaas, Oliver,
Rajat Jain, linuxppc-dev
CPU0 CPU1
pci_enable_device_mem() pci_enable_device_mem()
pci_enable_bridge() pci_enable_bridge()
pci_is_enabled()
return false;
atomic_inc_return(enable_cnt)
Start actual enabling the bridge
... pci_is_enabled()
... return true;
... Start memory requests <-- FAIL
...
Set the PCI_COMMAND_MEMORY bit <-- Must wait for this
This patch protects the pci_enable/disable_device() and pci_enable_bridge()
with mutexes.
Signed-off-by: Sergey Miroshnichenko <s.miroshnichenko@yadro.com>
---
drivers/pci/pci.c | 26 ++++++++++++++++++++++----
drivers/pci/probe.c | 1 +
include/linux/pci.h | 1 +
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e1fc93c9eea1..3a83e05f8363 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1571,6 +1571,8 @@ static void pci_enable_bridge(struct pci_dev *dev)
struct pci_dev *bridge;
int retval;
+ mutex_lock(&dev->enable_mutex);
+
bridge = pci_upstream_bridge(dev);
if (bridge)
pci_enable_bridge(bridge);
@@ -1578,6 +1580,7 @@ static void pci_enable_bridge(struct pci_dev *dev)
if (pci_is_enabled(dev)) {
if (!dev->is_busmaster)
pci_set_master(dev);
+ mutex_unlock(&dev->enable_mutex);
return;
}
@@ -1586,11 +1589,14 @@ static void pci_enable_bridge(struct pci_dev *dev)
pci_err(dev, "Error enabling bridge (%d), continuing\n",
retval);
pci_set_master(dev);
+ mutex_unlock(&dev->enable_mutex);
}
static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)
{
struct pci_dev *bridge;
+ /* Enable-locking of bridges is performed within the pci_enable_bridge() */
+ bool need_lock = !dev->subordinate;
int err;
int i, bars = 0;
@@ -1606,8 +1612,13 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
}
- if (atomic_inc_return(&dev->enable_cnt) > 1)
+ if (need_lock)
+ mutex_lock(&dev->enable_mutex);
+ if (pci_is_enabled(dev)) {
+ if (need_lock)
+ mutex_unlock(&dev->enable_mutex);
return 0; /* already enabled */
+ }
bridge = pci_upstream_bridge(dev);
if (bridge)
@@ -1622,8 +1633,10 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)
bars |= (1 << i);
err = do_pci_enable_device(dev, bars);
- if (err < 0)
- atomic_dec(&dev->enable_cnt);
+ if (err >= 0)
+ atomic_inc(&dev->enable_cnt);
+ if (need_lock)
+ mutex_unlock(&dev->enable_mutex);
return err;
}
@@ -1866,15 +1879,20 @@ void pci_disable_device(struct pci_dev *dev)
if (dr)
dr->enabled = 0;
+ mutex_lock(&dev->enable_mutex);
dev_WARN_ONCE(&dev->dev, atomic_read(&dev->enable_cnt) <= 0,
"disabling already-disabled device");
- if (atomic_dec_return(&dev->enable_cnt) != 0)
+ if (atomic_dec_return(&dev->enable_cnt) != 0) {
+ mutex_unlock(&dev->enable_mutex);
return;
+ }
do_pci_disable_device(dev);
dev->is_busmaster = 0;
+
+ mutex_unlock(&dev->enable_mutex);
}
EXPORT_SYMBOL(pci_disable_device);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 257b9f6f2ebb..bbc12934f041 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2191,6 +2191,7 @@ struct pci_dev *pci_alloc_dev(struct pci_bus *bus)
INIT_LIST_HEAD(&dev->bus_list);
dev->dev.type = &pci_dev_type;
dev->bus = pci_bus_get(bus);
+ mutex_init(&dev->enable_mutex);
return dev;
}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 65f1d8c2f082..28fecfdd598d 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -416,6 +416,7 @@ struct pci_dev {
unsigned int no_vf_scan:1; /* Don't scan for VFs after IOV enablement */
pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */
+ struct mutex enable_mutex;
u32 saved_config_space[16]; /* Config space saved at suspend time */
struct hlist_head saved_cap_space;
--
2.20.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH RFC v3 05/21] PCI: hotplug: Add a flag for the movable BARs feature
2019-02-04 15:35 [PATCH RFC v3 00/21] PCI: Allow BAR movement during hotplug Sergey Miroshnichenko
2019-02-04 15:35 ` [PATCH RFC v3 02/21] PCI: Fix race condition in pci_enable/disable_device() Sergey Miroshnichenko
@ 2019-02-04 15:35 ` Sergey Miroshnichenko
2019-02-04 15:35 ` [PATCH RFC v3 06/21] PCI: Pause the devices with movable BARs during rescan Sergey Miroshnichenko
2019-02-04 15:36 ` [PATCH RFC v3 21/21] powerpc/pci: Fix crash with enabled movable BARs Sergey Miroshnichenko
3 siblings, 0 replies; 5+ messages in thread
From: Sergey Miroshnichenko @ 2019-02-04 15:35 UTC (permalink / raw)
To: linux-pci
Cc: Sam Bobroff, Sergey Miroshnichenko, linux, Lukas Wunner,
Bjorn Helgaas, Oliver, Rajat Jain, linuxppc-dev
If a new PCIe device has been hot-plugged between the two active ones
without big enough gap between their BARs, these BARs should be moved
if their drivers support this feature. The drivers should be notified
and paused during the procedure:
1) dev 8 (new)
|
v
.. | dev 3 | dev 3 | dev 5 | dev 7 |
.. | BAR 0 | BAR 1 | BAR 0 | BAR 0 |
2) dev 8
|
v
.. | dev 3 | dev 3 | --> --> | dev 5 | dev 7 |
.. | BAR 0 | BAR 1 | --> --> | BAR 0 | BAR 0 |
3)
.. | dev 3 | dev 3 | dev 8 | dev 8 | dev 5 | dev 7 |
.. | BAR 0 | BAR 1 | BAR 0 | BAR 1 | BAR 0 | BAR 0 |
Thus, prior reservation of memory regions by BIOS/bootloader/firmware
is not required anymore for the PCIe hotplug.
The PCI_MOVABLE_BARS flag is set by the platform is this feature is
supported and tested, but can be overridden by the following command
line option:
pcie_movable_bars={ off | force }
Signed-off-by: Sergey Miroshnichenko <s.miroshnichenko@yadro.com>
---
.../admin-guide/kernel-parameters.txt | 7 ++++++
drivers/pci/pci.c | 24 +++++++++++++++++++
include/linux/pci.h | 2 ++
3 files changed, 33 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b799bcf67d7b..2165c4b5aea6 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3387,6 +3387,13 @@
nomsi Do not use MSI for native PCIe PME signaling (this makes
all PCIe root ports use INTx for all services).
+ pcie_movable_bars=[PCIE]
+ Override the movable BARs support detection:
+ off
+ Disable even if supported by the platform
+ force
+ Enable even if not explicitly declared as supported
+
pcmv= [HW,PCMCIA] BadgePAD 4
pd_ignore_unused
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 55cf18389c15..096413f9ee67 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -139,6 +139,30 @@ static int __init pcie_port_pm_setup(char *str)
}
__setup("pcie_port_pm=", pcie_port_pm_setup);
+static bool pcie_movable_bars_off;
+static bool pcie_movable_bars_force;
+static int __init pcie_movable_bars_setup(char *str)
+{
+ if (!strcmp(str, "off"))
+ pcie_movable_bars_off = true;
+ else if (!strcmp(str, "force"))
+ pcie_movable_bars_force = true;
+ return 1;
+}
+__setup("pcie_movable_bars=", pcie_movable_bars_setup);
+
+bool pci_movable_bars_enabled(void)
+{
+ if (pcie_movable_bars_off)
+ return false;
+
+ if (pcie_movable_bars_force)
+ return true;
+
+ return pci_has_flag(PCI_MOVABLE_BARS);
+}
+EXPORT_SYMBOL(pci_movable_bars_enabled);
+
/* Time to wait after a reset for device to become responsive */
#define PCIE_RESET_READY_POLL_MS 60000
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 28fecfdd598d..ba0b1d0ea2d2 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -863,6 +863,7 @@ enum {
PCI_ENABLE_PROC_DOMAINS = 0x00000010, /* Enable domains in /proc */
PCI_COMPAT_DOMAIN_0 = 0x00000020, /* ... except domain 0 */
PCI_SCAN_ALL_PCIE_DEVS = 0x00000040, /* Scan all, not just dev 0 */
+ PCI_MOVABLE_BARS = 0x00000080, /* Runtime BAR reassign after hotplug */
};
/* These external functions are only available when PCI support is enabled */
@@ -1342,6 +1343,7 @@ unsigned char pci_bus_max_busnr(struct pci_bus *bus);
void pci_setup_bridge(struct pci_bus *bus);
resource_size_t pcibios_window_alignment(struct pci_bus *bus,
unsigned long type);
+bool pci_movable_bars_enabled(void);
#define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0)
#define PCI_VGA_STATE_CHANGE_DECODES (1 << 1)
--
2.20.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH RFC v3 06/21] PCI: Pause the devices with movable BARs during rescan
2019-02-04 15:35 [PATCH RFC v3 00/21] PCI: Allow BAR movement during hotplug Sergey Miroshnichenko
2019-02-04 15:35 ` [PATCH RFC v3 02/21] PCI: Fix race condition in pci_enable/disable_device() Sergey Miroshnichenko
2019-02-04 15:35 ` [PATCH RFC v3 05/21] PCI: hotplug: Add a flag for the movable BARs feature Sergey Miroshnichenko
@ 2019-02-04 15:35 ` Sergey Miroshnichenko
2019-02-04 15:36 ` [PATCH RFC v3 21/21] powerpc/pci: Fix crash with enabled movable BARs Sergey Miroshnichenko
3 siblings, 0 replies; 5+ messages in thread
From: Sergey Miroshnichenko @ 2019-02-04 15:35 UTC (permalink / raw)
To: linux-pci
Cc: Sam Bobroff, Sergey Miroshnichenko, linux, Lukas Wunner,
Bjorn Helgaas, Oliver, Rajat Jain, linuxppc-dev
Drivers indicate their support of movable BARs by implementing the
new rescan_prepare() and rescan_done() hooks in the struct pci_driver.
All device's activity must be stopped during a rescan, and iounmap()
+ioremap() must be applied to every used BAR.
Signed-off-by: Sergey Miroshnichenko <s.miroshnichenko@yadro.com>
---
drivers/pci/probe.c | 51 +++++++++++++++++++++++++++++++++++++++++++--
include/linux/pci.h | 2 ++
2 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index bbc12934f041..e18d07996cf3 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -3172,6 +3172,38 @@ unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge)
return max;
}
+static void pci_bus_rescan_prepare(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ struct pci_bus *child = dev->subordinate;
+
+ if (child) {
+ pci_bus_rescan_prepare(child);
+ } else if (dev->driver &&
+ dev->driver->rescan_prepare) {
+ dev->driver->rescan_prepare(dev);
+ }
+ }
+}
+
+static void pci_bus_rescan_done(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ struct pci_bus *child = dev->subordinate;
+
+ if (child) {
+ pci_bus_rescan_done(child);
+ } else if (dev->driver &&
+ dev->driver->rescan_done) {
+ dev->driver->rescan_done(dev);
+ }
+ }
+}
+
/**
* pci_rescan_bus - Scan a PCI bus for devices
* @bus: PCI bus to scan
@@ -3185,8 +3217,23 @@ unsigned int pci_rescan_bus(struct pci_bus *bus)
{
unsigned int max;
- max = pci_scan_child_bus(bus);
- pci_assign_unassigned_bus_resources(bus);
+ if (pci_movable_bars_enabled()) {
+ struct pci_bus *root = bus;
+
+ while (!pci_is_root_bus(root))
+ root = root->parent;
+
+ pci_bus_rescan_prepare(root);
+
+ max = pci_scan_child_bus(root);
+ pci_assign_unassigned_root_bus_resources(root);
+
+ pci_bus_rescan_done(root);
+ } else {
+ max = pci_scan_child_bus(bus);
+ pci_assign_unassigned_bus_resources(bus);
+ }
+
pci_bus_add_devices(bus);
return max;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index ba0b1d0ea2d2..5cd534b6631b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -777,6 +777,8 @@ struct pci_driver {
int (*resume)(struct pci_dev *dev); /* Device woken up */
void (*shutdown)(struct pci_dev *dev);
int (*sriov_configure)(struct pci_dev *dev, int num_vfs); /* On PF */
+ void (*rescan_prepare)(struct pci_dev *dev);
+ void (*rescan_done)(struct pci_dev *dev);
const struct pci_error_handlers *err_handler;
const struct attribute_group **groups;
struct device_driver driver;
--
2.20.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH RFC v3 21/21] powerpc/pci: Fix crash with enabled movable BARs
2019-02-04 15:35 [PATCH RFC v3 00/21] PCI: Allow BAR movement during hotplug Sergey Miroshnichenko
` (2 preceding siblings ...)
2019-02-04 15:35 ` [PATCH RFC v3 06/21] PCI: Pause the devices with movable BARs during rescan Sergey Miroshnichenko
@ 2019-02-04 15:36 ` Sergey Miroshnichenko
3 siblings, 0 replies; 5+ messages in thread
From: Sergey Miroshnichenko @ 2019-02-04 15:36 UTC (permalink / raw)
To: linux-pci
Cc: Sam Bobroff, Sergey Miroshnichenko, linux, Bjorn Helgaas, Oliver,
Rajat Jain, linuxppc-dev
Check a resource for the UNSET flags.
Signed-off-by: Sergey Miroshnichenko <s.miroshnichenko@yadro.com>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 7db3119f8a5b..5354dfdf1028 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -3038,7 +3038,8 @@ static void pnv_ioda_setup_pe_res(struct pnv_ioda_pe *pe,
int index;
int64_t rc;
- if (!res || !res->flags || res->start > res->end)
+ if (!res || !res->flags || res->start > res->end ||
+ (res->flags & IORESOURCE_UNSET))
return;
if (res->flags & IORESOURCE_IO) {
--
2.20.1
^ permalink raw reply related [flat|nested] 5+ messages in thread