diff --git a/[refs] b/[refs] index 791b43182628..1dbeab3d3e05 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: a690a4cbf09f70723f9721ca96ab8b2b472b3391 +refs/heads/master: ac5ad93e92c3ffca4c7ba386aaa34244e27b7759 diff --git a/trunk/Documentation/feature-removal-schedule.txt b/trunk/Documentation/feature-removal-schedule.txt index 529e8323ca82..afaff312bf41 100644 --- a/trunk/Documentation/feature-removal-schedule.txt +++ b/trunk/Documentation/feature-removal-schedule.txt @@ -253,6 +253,38 @@ Who: Dave Jones , Matthew Garrett ----------------------------- +What: fakephp and associated sysfs files in /sys/bus/pci/slots/ +When: 2011 +Why: In 2.6.27, the semantics of /sys/bus/pci/slots was redefined to + represent a machine's physical PCI slots. The change in semantics + had userspace implications, as the hotplug core no longer allowed + drivers to create multiple sysfs files per physical slot (required + for multi-function devices, e.g.). fakephp was seen as a developer's + tool only, and its interface changed. Too late, we learned that + there were some users of the fakephp interface. + + In 2.6.30, the original fakephp interface was restored. At the same + time, the PCI core gained the ability that fakephp provided, namely + function-level hot-remove and hot-add. + + Since the PCI core now provides the same functionality, exposed in: + + /sys/bus/pci/rescan + /sys/bus/pci/devices/.../remove + /sys/bus/pci/devices/.../rescan + + there is no functional reason to maintain fakephp as well. + + We will keep the existing module so that 'modprobe fakephp' will + present the old /sys/bus/pci/slots/... interface for compatibility, + but users are urged to migrate their applications to the API above. + + After a reasonable transition period, we will remove the legacy + fakephp interface. +Who: Alex Chiang + +--------------------------- + What: CONFIG_RFKILL_INPUT When: 2.6.33 Why: Should be implemented in userspace, policy daemon. diff --git a/trunk/arch/frv/mb93090-mb00/pci-vdk.c b/trunk/arch/frv/mb93090-mb00/pci-vdk.c index 71e9bcf58105..d04ed14bbf0c 100644 --- a/trunk/arch/frv/mb93090-mb00/pci-vdk.c +++ b/trunk/arch/frv/mb93090-mb00/pci-vdk.c @@ -330,8 +330,10 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) pci_read_bridge_bases(bus); if (bus->number == 0) { + struct list_head *ln; struct pci_dev *dev; - list_for_each_entry(dev, &bus->devices, bus_list) { + for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) { + dev = pci_dev_b(ln); if (dev->devfn == 0) { dev->resource[0].start = 0; dev->resource[0].end = 0; diff --git a/trunk/drivers/char/agp/sgi-agp.c b/trunk/drivers/char/agp/sgi-agp.c index 3a5af2f9b015..192000377737 100644 --- a/trunk/drivers/char/agp/sgi-agp.c +++ b/trunk/drivers/char/agp/sgi-agp.c @@ -289,11 +289,12 @@ static int __devinit agp_sgi_init(void) j = 0; list_for_each_entry(info, &tioca_list, ca_list) { + struct list_head *tmp; if (list_empty(info->ca_devices)) continue; - list_for_each_entry(pdev, info->ca_devices, bus_list) { + list_for_each(tmp, info->ca_devices) { u8 cap_ptr; - + pdev = pci_dev_b(tmp); if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8)) continue; cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); diff --git a/trunk/drivers/parisc/dino.c b/trunk/drivers/parisc/dino.c index fb6a1fe21b93..ffddc4f64268 100644 --- a/trunk/drivers/parisc/dino.c +++ b/trunk/drivers/parisc/dino.c @@ -477,12 +477,14 @@ dino_card_setup(struct pci_bus *bus, void __iomem *base_addr) if (ccio_allocate_resource(dino_dev->hba.dev, res, _8MB, F_EXTEND(0xf0000000UL) | _8MB, F_EXTEND(0xffffffffUL) &~ _8MB, _8MB) < 0) { - struct pci_dev *dev, *tmp; + struct list_head *ln, *tmp_ln; printk(KERN_ERR "Dino: cannot attach bus %s\n", dev_name(bus->bridge)); /* kill the bus, we can't do anything with it */ - list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { + list_for_each_safe(ln, tmp_ln, &bus->devices) { + struct pci_dev *dev = pci_dev_b(ln); + list_del(&dev->bus_list); } @@ -547,6 +549,7 @@ dino_card_fixup(struct pci_dev *dev) static void __init dino_fixup_bus(struct pci_bus *bus) { + struct list_head *ln; struct pci_dev *dev; struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge)); @@ -593,7 +596,8 @@ dino_fixup_bus(struct pci_bus *bus) } - list_for_each_entry(dev, &bus->devices, bus_list) { + list_for_each(ln, &bus->devices) { + dev = pci_dev_b(ln); if (is_card_dino(&dino_dev->hba.dev->id)) dino_card_fixup(dev); diff --git a/trunk/drivers/parisc/lba_pci.c b/trunk/drivers/parisc/lba_pci.c index fdd63a6a62d6..4f9cf2456f4e 100644 --- a/trunk/drivers/parisc/lba_pci.c +++ b/trunk/drivers/parisc/lba_pci.c @@ -629,7 +629,7 @@ truncate_pat_collision(struct resource *root, struct resource *new) static void lba_fixup_bus(struct pci_bus *bus) { - struct pci_dev *dev; + struct list_head *ln; #ifdef FBB_SUPPORT u16 status; #endif @@ -710,8 +710,9 @@ lba_fixup_bus(struct pci_bus *bus) } - list_for_each_entry(dev, &bus->devices, bus_list) { + list_for_each(ln, &bus->devices) { int i; + struct pci_dev *dev = pci_dev_b(ln); DBG("lba_fixup_bus() %s\n", pci_name(dev)); @@ -769,7 +770,7 @@ lba_fixup_bus(struct pci_bus *bus) } /* Lastly enable FBB/PERR/SERR on all devices too */ - list_for_each_entry(dev, &bus->devices, bus_list) { + list_for_each(ln, &bus->devices) { (void) pci_read_config_word(dev, PCI_COMMAND, &status); status |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | fbb_enable; (void) pci_write_config_word(dev, PCI_COMMAND, status); diff --git a/trunk/drivers/pci/hotplug/Kconfig b/trunk/drivers/pci/hotplug/Kconfig index b0e46dede1a9..66f29bc00be4 100644 --- a/trunk/drivers/pci/hotplug/Kconfig +++ b/trunk/drivers/pci/hotplug/Kconfig @@ -17,6 +17,28 @@ menuconfig HOTPLUG_PCI if HOTPLUG_PCI +config HOTPLUG_PCI_FAKE + tristate "Fake PCI Hotplug driver" + help + Say Y here if you want to use the fake PCI hotplug driver. It can + be used to simulate PCI hotplug events if even if your system is + not PCI hotplug capable. + + This driver will "emulate" removing PCI devices from the system. + If the "power" file is written to with "0" then the specified PCI + device will be completely removed from the kernel. + + WARNING, this does NOT turn off the power to the PCI device. + This is a "logical" removal, not a physical or electrical + removal. + + Use this module at your own risk. You have been warned! + + To compile this driver as a module, choose M here: the + module will be called fakephp. + + When in doubt, say N. + config HOTPLUG_PCI_COMPAQ tristate "Compaq PCI Hotplug driver" depends on X86 && PCI_BIOS @@ -121,7 +143,7 @@ config HOTPLUG_PCI_SHPC config HOTPLUG_PCI_RPA tristate "RPA PCI Hotplug driver" - depends on PPC_PSERIES && EEH + depends on PPC_PSERIES && EEH && !HOTPLUG_PCI_FAKE help Say Y here if you have a RPA system that supports PCI Hotplug. diff --git a/trunk/drivers/pci/hotplug/Makefile b/trunk/drivers/pci/hotplug/Makefile index c459cd4e39c2..6cd9f3c9887d 100644 --- a/trunk/drivers/pci/hotplug/Makefile +++ b/trunk/drivers/pci/hotplug/Makefile @@ -23,6 +23,9 @@ obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o +# Link this last so it doesn't claim devices that have a real hotplug driver +obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o + pci_hotplug-objs := pci_hotplug_core.o pcihp_slot.o ifdef CONFIG_HOTPLUG_PCI_CPCI diff --git a/trunk/drivers/pci/hotplug/acpiphp_glue.c b/trunk/drivers/pci/hotplug/acpiphp_glue.c index 7be4ca5e1f4c..ad6fd6695495 100644 --- a/trunk/drivers/pci/hotplug/acpiphp_glue.c +++ b/trunk/drivers/pci/hotplug/acpiphp_glue.c @@ -869,6 +869,17 @@ static int __ref enable_device(struct acpiphp_slot *slot) return retval; } +static void disable_bridges(struct pci_bus *bus) +{ + struct pci_dev *dev; + list_for_each_entry(dev, &bus->devices, bus_list) { + if (dev->subordinate) { + disable_bridges(dev->subordinate); + pci_disable_device(dev); + } + } +} + /* return first device in slot, acquiring a reference on it */ static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot) { @@ -920,7 +931,12 @@ static int disable_device(struct acpiphp_slot *slot) * here. */ while ((pdev = dev_in_slot(slot))) { - pci_stop_and_remove_bus_device(pdev); + pci_stop_bus_device(pdev); + if (pdev->subordinate) { + disable_bridges(pdev->subordinate); + pci_disable_device(pdev); + } + __pci_remove_bus_device(pdev); pci_dev_put(pdev); } @@ -1461,6 +1477,34 @@ int __init acpiphp_get_num_slots(void) } +#if 0 +/** + * acpiphp_for_each_slot - call function for each slot + * @fn: callback function + * @data: context to be passed to callback function + */ +static int acpiphp_for_each_slot(acpiphp_callback fn, void *data) +{ + struct list_head *node; + struct acpiphp_bridge *bridge; + struct acpiphp_slot *slot; + int retval = 0; + + list_for_each (node, &bridge_list) { + bridge = (struct acpiphp_bridge *)node; + for (slot = bridge->slots; slot; slot = slot->next) { + retval = fn(slot, data); + if (!retval) + goto err_exit; + } + } + + err_exit: + return retval; +} +#endif + + /** * acpiphp_enable_slot - power on slot * @slot: ACPI PHP slot diff --git a/trunk/drivers/pci/hotplug/fakephp.c b/trunk/drivers/pci/hotplug/fakephp.c new file mode 100644 index 000000000000..a019c9a712be --- /dev/null +++ b/trunk/drivers/pci/hotplug/fakephp.c @@ -0,0 +1,164 @@ +/* Works like the fakephp driver used to, except a little better. + * + * - It's possible to remove devices with subordinate busses. + * - New PCI devices that appear via any method, not just a fakephp triggered + * rescan, will be noticed. + * - Devices that are removed via any method, not just a fakephp triggered + * removal, will also be noticed. + * + * Uses nothing from the pci-hotplug subsystem. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../pci.h" + +struct legacy_slot { + struct kobject kobj; + struct pci_dev *dev; + struct list_head list; +}; + +static LIST_HEAD(legacy_list); + +static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + strcpy(buf, "1\n"); + return 2; +} + +static void remove_callback(void *data) +{ + pci_stop_and_remove_bus_device((struct pci_dev *)data); +} + +static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + unsigned long val; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) + pci_rescan_bus(slot->dev->bus); + else + sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback, + slot->dev, THIS_MODULE); + return len; +} + +static struct attribute *legacy_attrs[] = { + &(struct attribute){ .name = "power", .mode = 0644 }, + NULL, +}; + +static void legacy_release(struct kobject *kobj) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + + pci_dev_put(slot->dev); + kfree(slot); +} + +static struct kobj_type legacy_ktype = { + .sysfs_ops = &(const struct sysfs_ops){ + .store = legacy_store, .show = legacy_show + }, + .release = &legacy_release, + .default_attrs = legacy_attrs, +}; + +static int legacy_add_slot(struct pci_dev *pdev) +{ + struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL); + + if (!slot) + return -ENOMEM; + + if (kobject_init_and_add(&slot->kobj, &legacy_ktype, + &pci_slots_kset->kobj, "%s", + dev_name(&pdev->dev))) { + dev_warn(&pdev->dev, "Failed to created legacy fake slot\n"); + return -EINVAL; + } + slot->dev = pci_dev_get(pdev); + + list_add(&slot->list, &legacy_list); + + return 0; +} + +static int legacy_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct pci_dev *pdev = to_pci_dev(data); + + if (action == BUS_NOTIFY_ADD_DEVICE) { + legacy_add_slot(pdev); + } else if (action == BUS_NOTIFY_DEL_DEVICE) { + struct legacy_slot *slot; + + list_for_each_entry(slot, &legacy_list, list) + if (slot->dev == pdev) + goto found; + + dev_warn(&pdev->dev, "Missing legacy fake slot?"); + return -ENODEV; +found: + kobject_del(&slot->kobj); + list_del(&slot->list); + kobject_put(&slot->kobj); + } + + return 0; +} + +static struct notifier_block legacy_notifier = { + .notifier_call = legacy_notify +}; + +static int __init init_legacy(void) +{ + struct pci_dev *pdev = NULL; + + /* Add existing devices */ + for_each_pci_dev(pdev) + legacy_add_slot(pdev); + + /* Be alerted of any new ones */ + bus_register_notifier(&pci_bus_type, &legacy_notifier); + return 0; +} +module_init(init_legacy); + +static void __exit remove_legacy(void) +{ + struct legacy_slot *slot, *tmp; + + bus_unregister_notifier(&pci_bus_type, &legacy_notifier); + + list_for_each_entry_safe(slot, tmp, &legacy_list, list) { + list_del(&slot->list); + kobject_del(&slot->kobj); + kobject_put(&slot->kobj); + } +} +module_exit(remove_legacy); + + +MODULE_AUTHOR("Trent Piepho "); +MODULE_DESCRIPTION("Legacy version of the fakephp interface"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/pci/hotplug/pciehp_core.c b/trunk/drivers/pci/hotplug/pciehp_core.c index 916bf4f53aba..365c6b96c642 100644 --- a/trunk/drivers/pci/hotplug/pciehp_core.c +++ b/trunk/drivers/pci/hotplug/pciehp_core.c @@ -300,24 +300,24 @@ static int pciehp_suspend (struct pcie_device *dev) static int pciehp_resume (struct pcie_device *dev) { - struct controller *ctrl; - struct slot *slot; - u8 status; - dev_info(&dev->device, "%s ENTRY\n", __func__); - ctrl = get_service_data(dev); + if (pciehp_force) { + struct controller *ctrl = get_service_data(dev); + struct slot *slot; + u8 status; - /* reinitialize the chipset's event detection logic */ - pcie_enable_notification(ctrl); + /* reinitialize the chipset's event detection logic */ + pcie_enable_notification(ctrl); - slot = ctrl->slot; + slot = ctrl->slot; - /* Check if slot is occupied */ - pciehp_get_adapter_status(slot, &status); - if (status) - pciehp_enable_slot(slot); - else - pciehp_disable_slot(slot); + /* Check if slot is occupied */ + pciehp_get_adapter_status(slot, &status); + if (status) + pciehp_enable_slot(slot); + else + pciehp_disable_slot(slot); + } return 0; } #endif /* PM */ diff --git a/trunk/drivers/pci/pci.c b/trunk/drivers/pci/pci.c index 5ba60dd71faa..fac08f508d09 100644 --- a/trunk/drivers/pci/pci.c +++ b/trunk/drivers/pci/pci.c @@ -254,17 +254,20 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap) } /** - * pci_find_next_ext_capability - Find an extended capability + * pci_find_ext_capability - Find an extended capability * @dev: PCI device to query - * @start: address at which to start looking (0 to start at beginning of list) * @cap: capability code * - * Returns the address of the next matching extended capability structure + * Returns the address of the requested extended capability structure * within the device's PCI configuration space or 0 if the device does - * not support it. Some capabilities can occur several times, e.g., the - * vendor-specific capability, and this provides a way to find them all. + * not support it. Possible values for @cap: + * + * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting + * %PCI_EXT_CAP_ID_VC Virtual Channel + * %PCI_EXT_CAP_ID_DSN Device Serial Number + * %PCI_EXT_CAP_ID_PWR Power Budgeting */ -int pci_find_next_ext_capability(struct pci_dev *dev, int start, int cap) +int pci_find_ext_capability(struct pci_dev *dev, int cap) { u32 header; int ttl; @@ -276,9 +279,6 @@ int pci_find_next_ext_capability(struct pci_dev *dev, int start, int cap) if (dev->cfg_size <= PCI_CFG_SPACE_SIZE) return 0; - if (start) - pos = start; - if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL) return 0; @@ -290,7 +290,7 @@ int pci_find_next_ext_capability(struct pci_dev *dev, int start, int cap) return 0; while (ttl-- > 0) { - if (PCI_EXT_CAP_ID(header) == cap && pos != start) + if (PCI_EXT_CAP_ID(header) == cap) return pos; pos = PCI_EXT_CAP_NEXT(header); @@ -303,26 +303,6 @@ int pci_find_next_ext_capability(struct pci_dev *dev, int start, int cap) return 0; } -EXPORT_SYMBOL_GPL(pci_find_next_ext_capability); - -/** - * pci_find_ext_capability - Find an extended capability - * @dev: PCI device to query - * @cap: capability code - * - * Returns the address of the requested extended capability structure - * within the device's PCI configuration space or 0 if the device does - * not support it. Possible values for @cap: - * - * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting - * %PCI_EXT_CAP_ID_VC Virtual Channel - * %PCI_EXT_CAP_ID_DSN Device Serial Number - * %PCI_EXT_CAP_ID_PWR Power Budgeting - */ -int pci_find_ext_capability(struct pci_dev *dev, int cap) -{ - return pci_find_next_ext_capability(dev, 0, cap); -} EXPORT_SYMBOL_GPL(pci_find_ext_capability); static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap) diff --git a/trunk/drivers/pci/pcie/portdrv_core.c b/trunk/drivers/pci/pcie/portdrv_core.c index d03a7a39b2d8..aede99171e90 100644 --- a/trunk/drivers/pci/pcie/portdrv_core.c +++ b/trunk/drivers/pci/pcie/portdrv_core.c @@ -200,13 +200,10 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) { int i, irq = -1; - /* - * If MSI cannot be used for PCIe PME or hotplug, we have to use - * INTx or other interrupts, e.g. system shared interrupt. - */ + /* We have to use INTx if MSI cannot be used for PCIe PME or pciehp. */ if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) || ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) { - if (dev->irq) + if (dev->pin) irq = dev->irq; goto no_msi; } @@ -215,12 +212,8 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) if (!pcie_port_enable_msix(dev, irqs, mask)) return 0; - /* - * We're not going to use MSI-X, so try MSI and fall back to INTx. - * If neither MSI/MSI-X nor INTx available, try other interrupt. On - * some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode. - */ - if (!pci_enable_msi(dev) || dev->irq) + /* We're not going to use MSI-X, so try MSI and fall back to INTx */ + if (!pci_enable_msi(dev) || dev->pin) irq = dev->irq; no_msi: diff --git a/trunk/drivers/pci/proc.c b/trunk/drivers/pci/proc.c index eb907a8faf2a..27911b55c2a5 100644 --- a/trunk/drivers/pci/proc.c +++ b/trunk/drivers/pci/proc.c @@ -434,6 +434,25 @@ int pci_proc_detach_device(struct pci_dev *dev) return 0; } +#if 0 +int pci_proc_attach_bus(struct pci_bus* bus) +{ + struct proc_dir_entry *de = bus->procdir; + + if (!proc_initialized) + return -EACCES; + + if (!de) { + char name[16]; + sprintf(name, "%02x", bus->number); + de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir); + if (!de) + return -ENOMEM; + } + return 0; +} +#endif /* 0 */ + int pci_proc_detach_bus(struct pci_bus* bus) { struct proc_dir_entry *de = bus->procdir; diff --git a/trunk/drivers/pci/remove.c b/trunk/drivers/pci/remove.c index 4f9ca9162895..04a4861b4749 100644 --- a/trunk/drivers/pci/remove.c +++ b/trunk/drivers/pci/remove.c @@ -32,30 +32,53 @@ static void pci_stop_dev(struct pci_dev *dev) static void pci_destroy_dev(struct pci_dev *dev) { + /* Remove the device from the device lists, and prevent any further + * list accesses from this device */ down_write(&pci_bus_sem); list_del(&dev->bus_list); + dev->bus_list.next = dev->bus_list.prev = NULL; up_write(&pci_bus_sem); pci_free_resources(dev); pci_dev_put(dev); } -void pci_remove_bus(struct pci_bus *bus) +/** + * pci_remove_device_safe - remove an unused hotplug device + * @dev: the device to remove + * + * Delete the device structure from the device lists and + * notify userspace (/sbin/hotplug), but only if the device + * in question is not being used by a driver. + * Returns 0 on success. + */ +#if 0 +int pci_remove_device_safe(struct pci_dev *dev) { - pci_proc_detach_bus(bus); + if (pci_dev_driver(dev)) + return -EBUSY; + pci_destroy_dev(dev); + return 0; +} +#endif /* 0 */ + +void pci_remove_bus(struct pci_bus *pci_bus) +{ + pci_proc_detach_bus(pci_bus); down_write(&pci_bus_sem); - list_del(&bus->node); - pci_bus_release_busn_res(bus); + list_del(&pci_bus->node); + pci_bus_release_busn_res(pci_bus); up_write(&pci_bus_sem); - if (!bus->is_added) + if (!pci_bus->is_added) return; - pci_remove_legacy_files(bus); - device_unregister(&bus->dev); + pci_remove_legacy_files(pci_bus); + device_unregister(&pci_bus->dev); } EXPORT_SYMBOL(pci_remove_bus); +static void __pci_remove_behind_bridge(struct pci_dev *dev); /** * pci_stop_and_remove_bus_device - remove a PCI device and any children * @dev: the device to remove @@ -68,27 +91,93 @@ EXPORT_SYMBOL(pci_remove_bus); * device lists, remove the /proc entry, and notify userspace * (/sbin/hotplug). */ +void __pci_remove_bus_device(struct pci_dev *dev) +{ + if (dev->subordinate) { + struct pci_bus *b = dev->subordinate; + + __pci_remove_behind_bridge(dev); + pci_remove_bus(b); + dev->subordinate = NULL; + } + + pci_destroy_dev(dev); +} +EXPORT_SYMBOL(__pci_remove_bus_device); + void pci_stop_and_remove_bus_device(struct pci_dev *dev) { - struct pci_bus *bus = dev->subordinate; - struct pci_dev *child, *tmp; + pci_stop_bus_device(dev); + __pci_remove_bus_device(dev); +} + +static void __pci_remove_behind_bridge(struct pci_dev *dev) +{ + struct list_head *l, *n; + + if (dev->subordinate) + list_for_each_safe(l, n, &dev->subordinate->devices) + __pci_remove_bus_device(pci_dev_b(l)); +} + +static void pci_stop_behind_bridge(struct pci_dev *dev) +{ + struct list_head *l, *n; + + if (dev->subordinate) + list_for_each_safe(l, n, &dev->subordinate->devices) + pci_stop_bus_device(pci_dev_b(l)); +} + +/** + * pci_stop_and_remove_behind_bridge - stop and remove all devices behind + * a PCI bridge + * @dev: PCI bridge device + * + * Remove all devices on the bus, except for the parent bridge. + * This also removes any child buses, and any devices they may + * contain in a depth-first manner. + */ +void pci_stop_and_remove_behind_bridge(struct pci_dev *dev) +{ + pci_stop_behind_bridge(dev); + __pci_remove_behind_bridge(dev); +} + +static void pci_stop_bus_devices(struct pci_bus *bus) +{ + struct list_head *l, *n; /* - * Removing an SR-IOV PF device removes all the associated VFs, - * which will update the bus->devices list and confuse the - * iterator. Therefore, iterate in reverse so we remove the VFs - * first, then the PF. + * VFs could be removed by pci_stop_and_remove_bus_device() in the + * pci_stop_bus_devices() code path for PF. + * aka, bus->devices get updated in the process. + * but VFs are inserted after PFs when SRIOV is enabled for PF, + * We can iterate the list backwards to get prev valid PF instead + * of removed VF. */ - if (bus) { - list_for_each_entry_safe_reverse(child, tmp, - &bus->devices, bus_list) - pci_stop_and_remove_bus_device(child); - - pci_remove_bus(bus); - dev->subordinate = NULL; + list_for_each_prev_safe(l, n, &bus->devices) { + struct pci_dev *dev = pci_dev_b(l); + pci_stop_bus_device(dev); } +} + +/** + * pci_stop_bus_device - stop a PCI device and any children + * @dev: the device to stop + * + * Stop a PCI device (detach the driver, remove from the global list + * and so on). This also stop any subordinate buses and children in a + * depth-first manner. + */ +void pci_stop_bus_device(struct pci_dev *dev) +{ + if (dev->subordinate) + pci_stop_bus_devices(dev->subordinate); pci_stop_dev(dev); - pci_destroy_dev(dev); } + EXPORT_SYMBOL(pci_stop_and_remove_bus_device); +EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge); +EXPORT_SYMBOL_GPL(pci_stop_bus_device); diff --git a/trunk/drivers/pci/rom.c b/trunk/drivers/pci/rom.c index 0b3037ab8b93..48ebdb237f3f 100644 --- a/trunk/drivers/pci/rom.c +++ b/trunk/drivers/pci/rom.c @@ -167,6 +167,44 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) return rom; } +#if 0 +/** + * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy + * @pdev: pointer to pci device struct + * @size: pointer to receive size of pci window over ROM + * + * Return: kernel virtual pointer to image of ROM + * + * Map a PCI ROM into kernel space. If ROM is boot video ROM, + * the shadow BIOS copy will be returned instead of the + * actual ROM. + */ +void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size) +{ + struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; + void __iomem *rom; + + rom = pci_map_rom(pdev, size); + if (!rom) + return NULL; + + if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW | + IORESOURCE_ROM_BIOS_COPY)) + return rom; + + res->start = (unsigned long)kmalloc(*size, GFP_KERNEL); + if (!res->start) + return rom; + + res->end = res->start + *size; + memcpy_fromio((void*)(unsigned long)res->start, rom, *size); + pci_unmap_rom(pdev, rom); + res->flags |= IORESOURCE_ROM_COPY; + + return (void __iomem *)(unsigned long)res->start; +} +#endif /* 0 */ + /** * pci_unmap_rom - unmap the ROM from kernel space * @pdev: pointer to pci device struct @@ -188,6 +226,27 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) pci_disable_rom(pdev); } +#if 0 +/** + * pci_remove_rom - disable the ROM and remove its sysfs attribute + * @pdev: pointer to pci device struct + * + * Remove the rom file in sysfs and disable ROM decoding. + */ +void pci_remove_rom(struct pci_dev *pdev) +{ + struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; + + if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) + sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); + if (!(res->flags & (IORESOURCE_ROM_ENABLE | + IORESOURCE_ROM_SHADOW | + IORESOURCE_ROM_BIOS_COPY | + IORESOURCE_ROM_COPY))) + pci_disable_rom(pdev); +} +#endif /* 0 */ + /** * pci_cleanup_rom - free the ROM copy created by pci_map_rom_copy * @pdev: pointer to pci device struct diff --git a/trunk/drivers/pci/search.c b/trunk/drivers/pci/search.c index bf969ba58e59..621b162ceb69 100644 --- a/trunk/drivers/pci/search.c +++ b/trunk/drivers/pci/search.c @@ -130,14 +130,16 @@ pci_find_next_bus(const struct pci_bus *from) * decrement the reference count by calling pci_dev_put(). * If no device is found, %NULL is returned. */ -struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn) +struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn) { + struct list_head *tmp; struct pci_dev *dev; WARN_ON(in_interrupt()); down_read(&pci_bus_sem); - list_for_each_entry(dev, &bus->devices, bus_list) { + list_for_each(tmp, &bus->devices) { + dev = pci_dev_b(tmp); if (dev->devfn == devfn) goto out; } @@ -243,14 +245,30 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device, struct pci_dev *from) { - struct pci_device_id id = { - .vendor = vendor, - .device = device, - .subvendor = ss_vendor, - .subdevice = ss_device, - }; - - return pci_get_dev_by_id(&id, from); + struct pci_dev *pdev; + struct pci_device_id *id; + + /* + * pci_find_subsys() can be called on the ide_setup() path, + * super-early in boot. But the down_read() will enable local + * interrupts, which can cause some machines to crash. So here we + * detect and flag that situation and bail out early. + */ + if (unlikely(no_pci_devices())) + return NULL; + + id = kzalloc(sizeof(*id), GFP_KERNEL); + if (!id) + return NULL; + id->vendor = vendor; + id->device = device; + id->subvendor = ss_vendor; + id->subdevice = ss_device; + + pdev = pci_get_dev_by_id(id, from); + kfree(id); + + return pdev; } /** @@ -289,16 +307,19 @@ pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from) */ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from) { - struct pci_device_id id = { - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class_mask = PCI_ANY_ID, - .class = class, - }; - - return pci_get_dev_by_id(&id, from); + struct pci_dev *dev; + struct pci_device_id *id; + + id = kzalloc(sizeof(*id), GFP_KERNEL); + if (!id) + return NULL; + id->vendor = id->device = id->subvendor = id->subdevice = PCI_ANY_ID; + id->class_mask = PCI_ANY_ID; + id->class = class; + + dev = pci_get_dev_by_id(id, from); + kfree(id); + return dev; } /** diff --git a/trunk/drivers/pci/setup-bus.c b/trunk/drivers/pci/setup-bus.c index fb506137aaee..896f06e3e793 100644 --- a/trunk/drivers/pci/setup-bus.c +++ b/trunk/drivers/pci/setup-bus.c @@ -697,6 +697,38 @@ static resource_size_t calculate_memsize(resource_size_t size, return size; } +resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, + unsigned long type) +{ + return 1; +} + +#define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */ +#define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */ +#define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */ + +static resource_size_t window_alignment(struct pci_bus *bus, + unsigned long type) +{ + resource_size_t align = 1, arch_align; + + if (type & IORESOURCE_MEM) + align = PCI_P2P_DEFAULT_MEM_ALIGN; + else if (type & IORESOURCE_IO) { + /* + * Per spec, I/O windows are 4K-aligned, but some + * bridges have an extension to support 1K alignment. + */ + if (bus->self->io_window_1k) + align = PCI_P2P_DEFAULT_IO_ALIGN_1K; + else + align = PCI_P2P_DEFAULT_IO_ALIGN; + } + + arch_align = pcibios_window_alignment(bus, type); + return max(align, arch_align); +} + /** * pbus_size_io() - size the io window of a given bus * diff --git a/trunk/drivers/pcmcia/cardbus.c b/trunk/drivers/pcmcia/cardbus.c index 9d3ac998fc1f..24caeaf50529 100644 --- a/trunk/drivers/pcmcia/cardbus.c +++ b/trunk/drivers/pcmcia/cardbus.c @@ -105,17 +105,8 @@ int __ref cb_alloc(struct pcmcia_socket *s) */ void cb_free(struct pcmcia_socket *s) { - struct pci_dev *bridge, *dev, *tmp; - struct pci_bus *bus; + struct pci_dev *bridge = s->cb_dev; - bridge = s->cb_dev; - if (!bridge) - return; - - bus = bridge->subordinate; - if (!bus) - return; - - list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) - pci_stop_and_remove_bus_device(dev); + if (bridge) + pci_stop_and_remove_behind_bridge(bridge); } diff --git a/trunk/include/linux/pci.h b/trunk/include/linux/pci.h index 0d8718904e88..2c755243eef2 100644 --- a/trunk/include/linux/pci.h +++ b/trunk/include/linux/pci.h @@ -369,6 +369,7 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev) extern struct pci_dev *alloc_pci_dev(void); +#define pci_dev_b(n) list_entry(n, struct pci_dev, bus_list) #define to_pci_dev(n) container_of(n, struct pci_dev, dev) #define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) @@ -733,7 +734,9 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp); extern struct pci_dev *pci_dev_get(struct pci_dev *dev); extern void pci_dev_put(struct pci_dev *dev); extern void pci_remove_bus(struct pci_bus *b); +extern void __pci_remove_bus_device(struct pci_dev *dev); extern void pci_stop_and_remove_bus_device(struct pci_dev *dev); +extern void pci_stop_bus_device(struct pci_dev *dev); void pci_setup_cardbus(struct pci_bus *bus); extern void pci_sort_breadthfirst(void); #define dev_is_pci(d) ((d)->bus == &pci_bus_type) @@ -752,7 +755,6 @@ enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *dev); int pci_find_capability(struct pci_dev *dev, int cap); int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap); int pci_find_ext_capability(struct pci_dev *dev, int cap); -int pci_find_next_ext_capability(struct pci_dev *dev, int pos, int cap); int pci_find_ht_capability(struct pci_dev *dev, int ht_cap); int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap); struct pci_bus *pci_find_next_bus(const struct pci_bus *from); @@ -1044,6 +1046,7 @@ void pci_unregister_driver(struct pci_driver *dev); module_driver(__pci_driver, pci_register_driver, \ pci_unregister_driver) +void pci_stop_and_remove_behind_bridge(struct pci_dev *dev); struct pci_driver *pci_dev_driver(const struct pci_dev *dev); int pci_add_dynid(struct pci_driver *drv, unsigned int vendor, unsigned int device, @@ -1061,6 +1064,8 @@ int pci_cfg_space_size_ext(struct pci_dev *dev); int pci_cfg_space_size(struct pci_dev *dev); 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); #define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0) #define PCI_VGA_STATE_CHANGE_DECODES (1 << 1) diff --git a/trunk/include/linux/pci_regs.h b/trunk/include/linux/pci_regs.h index 20ae747ddf34..3958f70f3202 100644 --- a/trunk/include/linux/pci_regs.h +++ b/trunk/include/linux/pci_regs.h @@ -678,12 +678,6 @@ #define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ #define PCI_EXT_CAP_PWR_SIZEOF 16 -/* Vendor-Specific (VSEC, PCI_EXT_CAP_ID_VNDR) */ -#define PCI_VNDR_HEADER 4 /* Vendor-Specific Header */ -#define PCI_VNDR_HEADER_ID(x) ((x) & 0xffff) -#define PCI_VNDR_HEADER_REV(x) (((x) >> 16) & 0xf) -#define PCI_VNDR_HEADER_LEN(x) (((x) >> 20) & 0xfff) - /* * Hypertransport sub capability types *