From 1f96a965e30d097a25818196e33d2dce923973a7 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 21 Jan 2013 13:20:42 -0800 Subject: PCI: acpiphp: Add is_hotplug_bridge detection When system support hotplug bridge with children hotplug slots, we need to make sure that parent bridge get preallocated resource so later when device is plugged into children slot, those children devices will get resource allocated. We do not meet this problem, because for PCIe hotplug card, when acpiphp is used, pci_scan_bridge will set that for us when detect hotplug bit in slot cap. Reported-and-tested-by: Jason Baron Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Acked-by: Jason Baron Acked-by: Rafael J. Wysocki --- drivers/pci/hotplug/acpiphp_glue.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 9e2b1f6dbe41..b94879d20630 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -797,6 +797,29 @@ static void acpiphp_set_acpi_region(struct acpiphp_slot *slot) } } +static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev) +{ + struct acpiphp_func *func; + + if (!dev->subordinate) + return; + + /* quirk, or pcie could set it already */ + if (dev->is_hotplug_bridge) + return; + + if (PCI_SLOT(dev->devfn) != slot->device) + return; + + list_for_each_entry(func, &slot->funcs, sibling) { + if (PCI_FUNC(dev->devfn) == func->function) { + /* check if this bridge has ejectable slots */ + if ((detect_ejectable_slots(func->handle) > 0)) + dev->is_hotplug_bridge = 1; + break; + } + } +} /** * enable_device - enable, configure a slot * @slot: slot to be enabled @@ -831,8 +854,10 @@ static int __ref enable_device(struct acpiphp_slot *slot) if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) { max = pci_scan_bridge(bus, dev, max, pass); - if (pass && dev->subordinate) + if (pass && dev->subordinate) { + check_hotplug_bridge(slot, dev); pci_bus_size_bridges(dev->subordinate); + } } } } -- cgit v1.2.3 From 92d8aff3a317fcd6f78ed9ac13dbbaeae8cb11ed Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 21 Jan 2013 13:20:47 -0800 Subject: PCI/ACPI: acpiphp: Rename alloc_acpiphp_hp_work() to alloc_acpi_hp_work() Will need to use it for PCI root bridge hotplug support, so rename *acpiphp* to *acpi* and move to osc.c. Also make kacpi_hotplug_wq static after that. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki CC: Len Brown CC: linux-acpi@vger.kernel.org --- drivers/acpi/osl.c | 24 ++++++++++++++++++++-- drivers/pci/hotplug/acpiphp_glue.c | 42 ++++++-------------------------------- include/acpi/acpi_bus.h | 9 ++++++++ include/acpi/acpiosxf.h | 2 -- 4 files changed, 37 insertions(+), 40 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 3ff267861541..59ec5f52e849 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -84,8 +84,7 @@ static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; static struct workqueue_struct *kacpi_notify_wq; -struct workqueue_struct *kacpi_hotplug_wq; -EXPORT_SYMBOL(kacpi_hotplug_wq); +static struct workqueue_struct *kacpi_hotplug_wq; /* * This list of permanent mappings is for memory that may be accessed from @@ -1778,3 +1777,24 @@ void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state, { __acpi_os_prepare_sleep = func; } + +void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, + void (*func)(struct work_struct *work)) +{ + struct acpi_hp_work *hp_work; + int ret; + + hp_work = kmalloc(sizeof(*hp_work), GFP_KERNEL); + if (!hp_work) + return; + + hp_work->handle = handle; + hp_work->type = type; + hp_work->context = context; + + INIT_WORK(&hp_work->work, func); + ret = queue_work(kacpi_hotplug_wq, &hp_work->work); + if (!ret) + kfree(hp_work); +} +EXPORT_SYMBOL_GPL(alloc_acpi_hp_work); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index b94879d20630..bf338d2ff371 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -1203,34 +1203,6 @@ check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK ; } -struct acpiphp_hp_work { - struct work_struct work; - acpi_handle handle; - u32 type; - void *context; -}; - -static void alloc_acpiphp_hp_work(acpi_handle handle, u32 type, - void *context, - void (*func)(struct work_struct *work)) -{ - struct acpiphp_hp_work *hp_work; - int ret; - - hp_work = kmalloc(sizeof(*hp_work), GFP_KERNEL); - if (!hp_work) - return; - - hp_work->handle = handle; - hp_work->type = type; - hp_work->context = context; - - INIT_WORK(&hp_work->work, func); - ret = queue_work(kacpi_hotplug_wq, &hp_work->work); - if (!ret) - kfree(hp_work); -} - static void _handle_hotplug_event_bridge(struct work_struct *work) { struct acpiphp_bridge *bridge; @@ -1239,11 +1211,11 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) .pointer = objname }; struct acpi_device *device; int num_sub_bridges = 0; - struct acpiphp_hp_work *hp_work; + struct acpi_hp_work *hp_work; acpi_handle handle; u32 type; - hp_work = container_of(work, struct acpiphp_hp_work, work); + hp_work = container_of(work, struct acpi_hp_work, work); handle = hp_work->handle; type = hp_work->type; @@ -1346,8 +1318,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, * For now just re-add this work to the kacpi_hotplug_wq so we * don't deadlock on hotplug actions. */ - alloc_acpiphp_hp_work(handle, type, context, - _handle_hotplug_event_bridge); + alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge); } static void _handle_hotplug_event_func(struct work_struct *work) @@ -1356,12 +1327,12 @@ static void _handle_hotplug_event_func(struct work_struct *work) char objname[64]; struct acpi_buffer buffer = { .length = sizeof(objname), .pointer = objname }; - struct acpiphp_hp_work *hp_work; + struct acpi_hp_work *hp_work; acpi_handle handle; u32 type; void *context; - hp_work = container_of(work, struct acpiphp_hp_work, work); + hp_work = container_of(work, struct acpi_hp_work, work); handle = hp_work->handle; type = hp_work->type; context = hp_work->context; @@ -1422,8 +1393,7 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, * For now just re-add this work to the kacpi_hotplug_wq so we * don't deadlock on hotplug actions. */ - alloc_acpiphp_hp_work(handle, type, context, - _handle_hotplug_event_func); + alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func); } static acpi_status diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 2c722deb2490..d26c0d7a6d19 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -310,6 +310,15 @@ struct acpi_eject_event { u32 event; }; +struct acpi_hp_work { + struct work_struct work; + acpi_handle handle; + u32 type; + void *context; +}; +void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, + void (*func)(struct work_struct *work)); + extern struct kobject *acpi_kobj; extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); void acpi_bus_private_data_handler(acpi_handle, void *); diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 43152742b46f..66f1fd70e8c2 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -193,8 +193,6 @@ void acpi_os_fixed_event_count(u32 fixed_event_number); /* * Threads and Scheduling */ -extern struct workqueue_struct *kacpi_hotplug_wq; - acpi_thread_id acpi_os_get_thread_id(void); acpi_status -- cgit v1.2.3 From 668192b678201d2fff27c6cc76bb003c1ec4a52a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 21 Jan 2013 13:20:48 -0800 Subject: PCI: acpiphp: Move host bridge hotplug to pci_root.c The acpiphp driver is confusing because it contains partial support for PCI host bridge hotplug as well as support for hotplug of PCI devices. This patch moves the host bridge hot-add support to pci_root.c and adds hot-remove support in pci_root.c. How to test it: if sci_emu patch is applied, find out root bus number to ACPI root name mapping from dmesg or /sys. To remove root bus: echo "\_SB.PCIB 3" > /sys/kernel/debug/acpi/sci_notify To add back root bus: echo "\_SB.PCIB 1" > /sys/kernel/debug/acpi/sci_notify Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 1 + drivers/acpi/pci_root.c | 124 +++++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 3 + drivers/pci/hotplug/acpiphp_glue.c | 59 +++++------------- 4 files changed, 143 insertions(+), 44 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae143..0f24148a2b2a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -68,6 +68,7 @@ struct acpi_ec { extern struct acpi_ec *first_ec; int acpi_pci_root_init(void); +void acpi_pci_root_hp_init(void); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); int acpi_boot_ec_enable(void); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 471b2dcb1c67..1389811aa21b 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -673,3 +673,127 @@ int __init acpi_pci_root_init(void) return 0; } +/* Support root bridge hotplug */ + +static void handle_root_bridge_insertion(acpi_handle handle) +{ + struct acpi_device *device; + + if (!acpi_bus_get_device(handle, &device)) { + printk(KERN_DEBUG "acpi device exists...\n"); + return; + } + + if (acpi_bus_scan(handle)) + printk(KERN_ERR "cannot add bridge to acpi list\n"); +} + +static void handle_root_bridge_removal(struct acpi_device *device) +{ + struct acpi_eject_event *ej_event; + + ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); + if (!ej_event) { + /* Inform firmware the hot-remove operation has error */ + (void) acpi_evaluate_hotplug_ost(device->handle, + ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); + return; + } + + ej_event->device = device; + ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; + + acpi_bus_hot_remove_device(ej_event); +} + +static void _handle_hotplug_event_root(struct work_struct *work) +{ + struct acpi_pci_root *root; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER }; + struct acpi_hp_work *hp_work; + acpi_handle handle; + u32 type; + + hp_work = container_of(work, struct acpi_hp_work, work); + handle = hp_work->handle; + type = hp_work->type; + + root = acpi_pci_find_root(handle); + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + /* bus enumerate */ + printk(KERN_DEBUG "%s: Bus check notify on %s\n", __func__, + (char *)buffer.pointer); + if (!root) + handle_root_bridge_insertion(handle); + + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + /* device check */ + printk(KERN_DEBUG "%s: Device check notify on %s\n", __func__, + (char *)buffer.pointer); + if (!root) + handle_root_bridge_insertion(handle); + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + /* request device eject */ + printk(KERN_DEBUG "%s: Device eject notify on %s\n", __func__, + (char *)buffer.pointer); + if (root) + handle_root_bridge_removal(root->device); + break; + default: + printk(KERN_WARNING "notify_handler: unknown event type 0x%x for %s\n", + type, (char *)buffer.pointer); + break; + } + + kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ + kfree(buffer.pointer); +} + +static void handle_hotplug_event_root(acpi_handle handle, u32 type, + void *context) +{ + alloc_acpi_hp_work(handle, type, context, + _handle_hotplug_event_root); +} + +static acpi_status __init +find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + char objname[64]; + struct acpi_buffer buffer = { .length = sizeof(objname), + .pointer = objname }; + int *count = (int *)context; + + if (!acpi_is_root_bridge(handle)) + return AE_OK; + + (*count)++; + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_root, NULL); + printk(KERN_DEBUG "acpi root: %s notify handler installed\n", objname); + + return AE_OK; +} + +void __init acpi_pci_root_hp_init(void) +{ + int num = 0; + + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); + + printk(KERN_DEBUG "Found %d acpi root devices\n", num); +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c43bdc36abc..bc2f33790e83 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1706,5 +1706,8 @@ int __init acpi_scan_init(void) } acpi_update_all_gpes(); + + acpi_pci_root_hp_init(); + return 0; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index bf338d2ff371..c4a6301009f2 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -543,10 +543,13 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) acpi_status status; acpi_handle handle = bridge->handle; - status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + if (bridge->type != BRIDGE_TYPE_HOST) { + status = acpi_remove_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, handle_hotplug_event_bridge); - if (ACPI_FAILURE(status)) - err("failed to remove notify handler\n"); + if (ACPI_FAILURE(status)) + err("failed to remove notify handler\n"); + } if ((bridge->type != BRIDGE_TYPE_HOST) && ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)) { @@ -630,9 +633,6 @@ static void remove_bridge(struct acpi_pci_root *root) bridge = acpiphp_handle_to_bridge(handle); if (bridge) cleanup_bridge(bridge); - else - acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_bridge); } static int power_on_slot(struct acpiphp_slot *slot) @@ -1123,18 +1123,12 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) } /* Program resources in newly inserted bridge */ -static int acpiphp_configure_bridge (acpi_handle handle) +static int acpiphp_configure_p2p_bridge(acpi_handle handle) { - struct pci_bus *bus; + struct pci_dev *pdev = acpi_get_pci_dev(handle); + struct pci_bus *bus = pdev->subordinate; - if (acpi_is_root_bridge(handle)) { - struct acpi_pci_root *root = acpi_pci_find_root(handle); - bus = root->bus; - } else { - struct pci_dev *pdev = acpi_get_pci_dev(handle); - bus = pdev->subordinate; - pci_dev_put(pdev); - } + pci_dev_put(pdev); pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); @@ -1144,7 +1138,7 @@ static int acpiphp_configure_bridge (acpi_handle handle) return 0; } -static void handle_bridge_insertion(acpi_handle handle, u32 type) +static void handle_p2p_bridge_insertion(acpi_handle handle, u32 type) { struct acpi_device *device; @@ -1162,8 +1156,8 @@ static void handle_bridge_insertion(acpi_handle handle, u32 type) err("ACPI device object missing\n"); return; } - if (!acpiphp_configure_bridge(handle)) - add_bridge(handle); + if (!acpiphp_configure_p2p_bridge(handle)) + add_p2p_bridge(handle); else err("cannot configure and start bridge\n"); @@ -1221,7 +1215,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) if (acpi_bus_get_device(handle, &device)) { /* This bridge must have just been physically inserted */ - handle_bridge_insertion(handle, type); + handle_p2p_bridge_insertion(handle, type); goto out; } @@ -1396,21 +1390,6 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func); } -static acpi_status -find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - int *count = (int *)context; - - if (!acpi_is_root_bridge(handle)) - return AE_OK; - - (*count)++; - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_bridge, NULL); - - return AE_OK ; -} - static struct acpi_pci_driver acpi_pci_hp_driver = { .add = add_bridge, .remove = remove_bridge, @@ -1421,15 +1400,7 @@ static struct acpi_pci_driver acpi_pci_hp_driver = { */ int __init acpiphp_glue_init(void) { - int num = 0; - - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); - - if (num <= 0) - return -1; - else - acpi_pci_register_driver(&acpi_pci_hp_driver); + acpi_pci_register_driver(&acpi_pci_hp_driver); return 0; } -- cgit v1.2.3 From d59f53bc9bd80ee62072dea590fc623c67cb84a8 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 21 Jan 2013 13:20:50 -0800 Subject: PCI: acpiphp: Keep driver loaded even if no slots found Could have root bus hot-added later and there may be slots that need acpiphp. The result returned by acpiphp_get_num_slots() is meaningless, because the bridge the slots are under may be added after this function has been called, so drop acpiphp_get_num_slots() and the code using it. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/hotplug/acpiphp.h | 1 - drivers/pci/hotplug/acpiphp_core.c | 23 ++--------------------- drivers/pci/hotplug/acpiphp_glue.c | 22 ---------------------- 3 files changed, 2 insertions(+), 44 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index a1afb5b39ad4..b3ead7ad7b31 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -193,7 +193,6 @@ extern void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot); /* acpiphp_glue.c */ extern int acpiphp_glue_init (void); extern void acpiphp_glue_exit (void); -extern int acpiphp_get_num_slots (void); typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); extern int acpiphp_enable_slot (struct acpiphp_slot *slot); diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index 96316b74969f..c2fd3095701f 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -50,7 +50,6 @@ bool acpiphp_debug; /* local variables */ -static int num_slots; static struct acpiphp_attention_info *attention_info; #define DRIVER_VERSION "0.5" @@ -272,25 +271,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int __init init_acpi(void) -{ - int retval; - - /* initialize internal data structure etc. */ - retval = acpiphp_glue_init(); - - /* read initial number of slots */ - if (!retval) { - num_slots = acpiphp_get_num_slots(); - if (num_slots == 0) { - acpiphp_glue_exit(); - retval = -ENODEV; - } - } - - return retval; -} - /** * release_slot - free up the memory used by a slot * @hotplug_slot: slot to free @@ -379,7 +359,8 @@ static int __init acpiphp_init(void) return 0; /* read all the ACPI info from the system */ - return init_acpi(); + /* initialize internal data structure etc. */ + return acpiphp_glue_init(); } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index c4a6301009f2..bd784ff4a244 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -1416,28 +1416,6 @@ void acpiphp_glue_exit(void) acpi_pci_unregister_driver(&acpi_pci_hp_driver); } - -/** - * acpiphp_get_num_slots - count number of slots in a system - */ -int __init acpiphp_get_num_slots(void) -{ - struct acpiphp_bridge *bridge; - int num_slots = 0; - - list_for_each_entry(bridge, &bridge_list, list) { - dbg("Bus %04x:%02x has %d slot%s\n", - pci_domain_nr(bridge->pci_bus), - bridge->pci_bus->number, bridge->nr_slots, - bridge->nr_slots == 1 ? "" : "s"); - num_slots += bridge->nr_slots; - } - - dbg("Total %d slots\n", num_slots); - return num_slots; -} - - /** * acpiphp_enable_slot - power on slot * @slot: ACPI PHP slot -- cgit v1.2.3