]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/pci/probe.c
Merge branch 'upstream-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/linvil...
[mv-sheeva.git] / drivers / pci / probe.c
index a3b0a5eb5054fefcc0e76e72b838fac1146fd310..e159d6604494acd62a617efcca0b6a3b26a8d4d4 100644 (file)
@@ -1067,3 +1067,95 @@ EXPORT_SYMBOL(pci_scan_bridge);
 EXPORT_SYMBOL(pci_scan_single_device);
 EXPORT_SYMBOL_GPL(pci_scan_child_bus);
 #endif
+
+static int __init pci_sort_bf_cmp(const struct pci_dev *a, const struct pci_dev *b)
+{
+       if      (pci_domain_nr(a->bus) < pci_domain_nr(b->bus)) return -1;
+       else if (pci_domain_nr(a->bus) > pci_domain_nr(b->bus)) return  1;
+
+       if      (a->bus->number < b->bus->number) return -1;
+       else if (a->bus->number > b->bus->number) return  1;
+
+       if      (a->devfn < b->devfn) return -1;
+       else if (a->devfn > b->devfn) return  1;
+
+       return 0;
+}
+
+/*
+ * Yes, this forcably breaks the klist abstraction temporarily.  It
+ * just wants to sort the klist, not change reference counts and
+ * take/drop locks rapidly in the process.  It does all this while
+ * holding the lock for the list, so objects can't otherwise be
+ * added/removed while we're swizzling.
+ */
+static void __init pci_insertion_sort_klist(struct pci_dev *a, struct list_head *list)
+{
+       struct list_head *pos;
+       struct klist_node *n;
+       struct device *dev;
+       struct pci_dev *b;
+
+       list_for_each(pos, list) {
+               n = container_of(pos, struct klist_node, n_node);
+               dev = container_of(n, struct device, knode_bus);
+               b = to_pci_dev(dev);
+               if (pci_sort_bf_cmp(a, b) <= 0) {
+                       list_move_tail(&a->dev.knode_bus.n_node, &b->dev.knode_bus.n_node);
+                       return;
+               }
+       }
+       list_move_tail(&a->dev.knode_bus.n_node, list);
+}
+
+static void __init pci_sort_breadthfirst_klist(void)
+{
+       LIST_HEAD(sorted_devices);
+       struct list_head *pos, *tmp;
+       struct klist_node *n;
+       struct device *dev;
+       struct pci_dev *pdev;
+
+       spin_lock(&pci_bus_type.klist_devices.k_lock);
+       list_for_each_safe(pos, tmp, &pci_bus_type.klist_devices.k_list) {
+               n = container_of(pos, struct klist_node, n_node);
+               dev = container_of(n, struct device, knode_bus);
+               pdev = to_pci_dev(dev);
+               pci_insertion_sort_klist(pdev, &sorted_devices);
+       }
+       list_splice(&sorted_devices, &pci_bus_type.klist_devices.k_list);
+       spin_unlock(&pci_bus_type.klist_devices.k_lock);
+}
+
+static void __init pci_insertion_sort_devices(struct pci_dev *a, struct list_head *list)
+{
+       struct pci_dev *b;
+
+       list_for_each_entry(b, list, global_list) {
+               if (pci_sort_bf_cmp(a, b) <= 0) {
+                       list_move_tail(&a->global_list, &b->global_list);
+                       return;
+               }
+       }
+       list_move_tail(&a->global_list, list);
+}
+
+static void __init pci_sort_breadthfirst_devices(void)
+{
+       LIST_HEAD(sorted_devices);
+       struct pci_dev *dev, *tmp;
+
+       down_write(&pci_bus_sem);
+       list_for_each_entry_safe(dev, tmp, &pci_devices, global_list) {
+               pci_insertion_sort_devices(dev, &sorted_devices);
+       }
+       list_splice(&sorted_devices, &pci_devices);
+       up_write(&pci_bus_sem);
+}
+
+void __init pci_sort_breadthfirst(void)
+{
+       pci_sort_breadthfirst_devices();
+       pci_sort_breadthfirst_klist();
+}
+