PCI: add pci_bridge_release_resources and pci_bus_release_bridge_resources
authorYinghai Lu <yinghai@kernel.org>
Fri, 22 Jan 2010 09:02:20 +0000 (01:02 -0800)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 23 Feb 2010 00:17:19 +0000 (16:17 -0800)
We use this in later patches to free resrouce ranges for reassignment in
an effort to support a wider variety of PCI topologies.

Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Reviewed-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/setup-bus.c

index 52fbd426a1c7cdc1d23dfbfebfd2f2a17dfa3368..7371a5479a99792c4c099ecef6f5179b86906ab4 100644 (file)
@@ -604,6 +604,88 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus)
 }
 EXPORT_SYMBOL(pci_bus_assign_resources);
 
+static void pci_bridge_release_resources(struct pci_bus *bus,
+                                         unsigned long type)
+{
+       int idx;
+       bool changed = false;
+       struct pci_dev *dev;
+       struct resource *r;
+       unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+                                 IORESOURCE_PREFETCH;
+
+       dev = bus->self;
+       for (idx = PCI_BRIDGE_RESOURCES; idx <= PCI_BRIDGE_RESOURCE_END;
+            idx++) {
+               r = &dev->resource[idx];
+               if ((r->flags & type_mask) != type)
+                       continue;
+               if (!r->parent)
+                       continue;
+               /*
+                * if there are children under that, we should release them
+                *  all
+                */
+               release_child_resources(r);
+               if (!release_resource(r)) {
+                       dev_printk(KERN_DEBUG, &dev->dev,
+                                "resource %d %pR released\n", idx, r);
+                       /* keep the old size */
+                       r->end = resource_size(r) - 1;
+                       r->start = 0;
+                       r->flags = 0;
+                       changed = true;
+               }
+       }
+
+       if (changed) {
+               /* avoiding touch the one without PREF */
+               if (type & IORESOURCE_PREFETCH)
+                       type = IORESOURCE_PREFETCH;
+               __pci_setup_bridge(bus, type);
+       }
+}
+
+enum release_type {
+       leaf_only,
+       whole_subtree,
+};
+/*
+ * try to release pci bridge resources that is from leaf bridge,
+ * so we can allocate big new one later
+ */
+static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus,
+                                                  unsigned long type,
+                                                  enum release_type rel_type)
+{
+       struct pci_dev *dev;
+       bool is_leaf_bridge = true;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               struct pci_bus *b = dev->subordinate;
+               if (!b)
+                       continue;
+
+               is_leaf_bridge = false;
+
+               if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI)
+                       continue;
+
+               if (rel_type == whole_subtree)
+                       pci_bus_release_bridge_resources(b, type,
+                                                whole_subtree);
+       }
+
+       if (pci_is_root_bus(bus))
+               return;
+
+       if ((bus->self->class >> 8) != PCI_CLASS_BRIDGE_PCI)
+               return;
+
+       if ((rel_type == whole_subtree) || is_leaf_bridge)
+               pci_bridge_release_resources(bus, type);
+}
+
 static void pci_bus_dump_res(struct pci_bus *bus)
 {
         int i;