Merge tag 'pm-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
[linux-block.git] / drivers / pci / pci-driver.c
index ffe7d54d93282aec5802cb5d427b90c78d4342a9..df4aead394f29436027c46d5148fdcb0b4180759 100644 (file)
@@ -96,7 +96,7 @@ static void pci_free_dynids(struct pci_driver *drv)
  *
  * Allow PCI IDs to be added to an existing driver via sysfs.
  */
-static ssize_t store_new_id(struct device_driver *driver, const char *buf,
+static ssize_t new_id_store(struct device_driver *driver, const char *buf,
                            size_t count)
 {
        struct pci_driver *pdrv = to_pci_driver(driver);
@@ -154,7 +154,7 @@ static ssize_t store_new_id(struct device_driver *driver, const char *buf,
                return retval;
        return count;
 }
-static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+static DRIVER_ATTR_WO(new_id);
 
 /**
  * store_remove_id - remove a PCI device ID from this driver
@@ -164,7 +164,7 @@ static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
  *
  * Removes a dynamic pci device ID to this driver.
  */
-static ssize_t store_remove_id(struct device_driver *driver, const char *buf,
+static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
                               size_t count)
 {
        struct pci_dynid *dynid, *n;
@@ -198,7 +198,7 @@ static ssize_t store_remove_id(struct device_driver *driver, const char *buf,
 
        return retval;
 }
-static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
+static DRIVER_ATTR_WO(remove_id);
 
 static struct attribute *pci_drv_attrs[] = {
        &driver_attr_new_id.attr,
@@ -320,10 +320,19 @@ static long local_pci_probe(void *_ddi)
        return 0;
 }
 
+static bool pci_physfn_is_probed(struct pci_dev *dev)
+{
+#ifdef CONFIG_PCI_IOV
+       return dev->is_virtfn && dev->physfn->is_probed;
+#else
+       return false;
+#endif
+}
+
 static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
                          const struct pci_device_id *id)
 {
-       int error, node;
+       int error, node, cpu;
        struct drv_dev_and_id ddi = { drv, dev, id };
 
        /*
@@ -332,33 +341,27 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
         * on the right node.
         */
        node = dev_to_node(&dev->dev);
+       dev->is_probed = 1;
+
+       cpu_hotplug_disable();
 
        /*
-        * On NUMA systems, we are likely to call a PF probe function using
-        * work_on_cpu().  If that probe calls pci_enable_sriov() (which
-        * adds the VF devices via pci_bus_add_device()), we may re-enter
-        * this function to call the VF probe function.  Calling
-        * work_on_cpu() again will cause a lockdep warning.  Since VFs are
-        * always on the same node as the PF, we can work around this by
-        * avoiding work_on_cpu() when we're already on the correct node.
-        *
-        * Preemption is enabled, so it's theoretically unsafe to use
-        * numa_node_id(), but even if we run the probe function on the
-        * wrong node, it should be functionally correct.
+        * Prevent nesting work_on_cpu() for the case where a Virtual Function
+        * device is probed from work_on_cpu() of the Physical device.
         */
-       if (node >= 0 && node != numa_node_id()) {
-               int cpu;
-
-               get_online_cpus();
+       if (node < 0 || node >= MAX_NUMNODES || !node_online(node) ||
+           pci_physfn_is_probed(dev))
+               cpu = nr_cpu_ids;
+       else
                cpu = cpumask_any_and(cpumask_of_node(node), cpu_online_mask);
-               if (cpu < nr_cpu_ids)
-                       error = work_on_cpu(cpu, local_pci_probe, &ddi);
-               else
-                       error = local_pci_probe(&ddi);
-               put_online_cpus();
-       } else
+
+       if (cpu < nr_cpu_ids)
+               error = work_on_cpu(cpu, local_pci_probe, &ddi);
+       else
                error = local_pci_probe(&ddi);
 
+       dev->is_probed = 0;
+       cpu_hotplug_enable();
        return error;
 }