From a50ac6bfd6042b16e0de4ac3264c407e678c9b10 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 19 Aug 2018 16:29:00 +0200 Subject: PCI: Simplify disconnected marking Commit 89ee9f768003 ("PCI: Add device disconnected state") iterates over the devices on a parent bus, marks each as disconnected, then marks each device's children as disconnected using pci_walk_bus(). The same can be achieved more succinctly by calling pci_walk_bus() on the parent bus. Moreover, this does not need to wait until acquiring pci_lock_rescan_remove(), so move it out of that critical section. The critical section in err.c contains a pci_dev_get() / pci_dev_put() pair which was apparently copy-pasted from pciehp_pci.c. In the latter it serves the purpose of holding the struct pci_dev in place until the Command register is updated. err.c doesn't do anything like that, hence the pair is unnecessary. Remove it. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Cc: Keith Busch Cc: Oza Pawandeep Cc: Sinan Kaya Cc: Benjamin Herrenschmidt --- drivers/pci/hotplug/pciehp_pci.c | 9 +++------ drivers/pci/pcie/err.c | 8 ++------ 2 files changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 5c58c22e0c08..3ef5c0744249 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -74,6 +74,9 @@ void pciehp_unconfigure_device(struct slot *p_slot) __func__, pci_domain_nr(parent), parent->number); pciehp_get_adapter_status(p_slot, &presence); + if (!presence) + pci_walk_bus(parent, pci_dev_set_disconnected, NULL); + pci_lock_rescan_remove(); /* @@ -85,12 +88,6 @@ void pciehp_unconfigure_device(struct slot *p_slot) list_for_each_entry_safe_reverse(dev, temp, &parent->devices, bus_list) { pci_dev_get(dev); - if (!presence) { - pci_dev_set_disconnected(dev, NULL); - if (pci_has_subordinate(dev)) - pci_walk_bus(dev->subordinate, - pci_dev_set_disconnected, NULL); - } pci_stop_and_remove_bus_device(dev); /* * Ensure that no new Requests will be generated from diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 708fd3a0d646..cac406b6e936 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -292,17 +292,13 @@ void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service) udev = dev->bus->self; parent = udev->subordinate; + pci_walk_bus(parent, pci_dev_set_disconnected, NULL); + pci_lock_rescan_remove(); pci_dev_get(dev); list_for_each_entry_safe_reverse(pdev, temp, &parent->devices, bus_list) { - pci_dev_get(pdev); - pci_dev_set_disconnected(pdev, NULL); - if (pci_has_subordinate(pdev)) - pci_walk_bus(pdev->subordinate, - pci_dev_set_disconnected, NULL); pci_stop_and_remove_bus_device(pdev); - pci_dev_put(pdev); } result = reset_link(udev, service); -- cgit v1.2.3 From 11e87702be65780be92fb1f0a5b7b293954185f7 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 31 Jul 2018 07:50:37 +0200 Subject: PCI: pciehp: Differentiate between surprise and safe removal When removing PCI devices below a hotplug bridge, pciehp marks them as disconnected if the card is no longer present in the slot or it quiesces them if the card is still present (by disabling INTx interrupts, bus mastering and SERR# reporting). To detect whether the card is still present, pciehp checks the Presence Detect State bit in the Slot Status register. The problem with this approach is that even if the card is present, the link to it may be down, and it that case it would be better to mark the devices as disconnected instead of trying to quiesce them. Moreover, if the card in the slot was quickly replaced by another one, the Presence Detect State bit would be set, yet trying to quiesce the new card's devices would be wrong and the correct thing to do is to mark the previous card's devices as disconnected. Instead of looking at the Presence Detect State bit, it is better to differentiate whether the card was surprise removed versus safely removed (via sysfs or an Attention Button press). On surprise removal, the devices should be marked as disconnected, whereas on safe removal it is correct to quiesce the devices. The knowledge whether a surprise removal or a safe removal is at hand does exist further up in the call stack: A surprise removal is initiated by pciehp_handle_presence_or_link_change(), a safe removal by pciehp_handle_disable_request(). Pass that information down to pciehp_unconfigure_device() and use it in lieu of the Presence Detect State bit. While there, add kernel-doc to pciehp_unconfigure_device() and pciehp_configure_device(). Tested-by: Alexandru Gagniuc Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Cc: Keith Busch --- drivers/pci/hotplug/pciehp.h | 2 +- drivers/pci/hotplug/pciehp_ctrl.c | 22 +++++++++++++--------- drivers/pci/hotplug/pciehp_pci.c | 23 ++++++++++++++++++++--- 3 files changed, 34 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 811cf83f956d..39c9c8815a35 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -181,7 +181,7 @@ void pciehp_handle_button_press(struct slot *slot); void pciehp_handle_disable_request(struct slot *slot); void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events); int pciehp_configure_device(struct slot *p_slot); -void pciehp_unconfigure_device(struct slot *p_slot); +void pciehp_unconfigure_device(struct slot *p_slot, bool presence); void pciehp_queue_pushbutton_work(struct work_struct *work); struct controller *pcie_init(struct pcie_device *dev); int pcie_init_notification(struct controller *ctrl); diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index da7c72372ffc..c283253d2cd7 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -26,6 +26,9 @@ hotplug controller logic */ +#define SAFE_REMOVAL true +#define SURPRISE_REMOVAL false + static void set_slot_off(struct controller *ctrl, struct slot *pslot) { /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ @@ -101,12 +104,13 @@ err_exit: /** * remove_board - Turns off slot and LEDs * @p_slot: slot where board is being removed + * @safe_removal: whether the board is safely removed (versus surprise removed) */ -static void remove_board(struct slot *p_slot) +static void remove_board(struct slot *p_slot, bool safe_removal) { struct controller *ctrl = p_slot->ctrl; - pciehp_unconfigure_device(p_slot); + pciehp_unconfigure_device(p_slot, safe_removal); if (POWER_CTRL(ctrl)) { pciehp_power_off_slot(p_slot); @@ -124,7 +128,7 @@ static void remove_board(struct slot *p_slot) } static int pciehp_enable_slot(struct slot *slot); -static int pciehp_disable_slot(struct slot *slot); +static int pciehp_disable_slot(struct slot *slot, bool safe_removal); void pciehp_request(struct controller *ctrl, int action) { @@ -216,7 +220,7 @@ void pciehp_handle_disable_request(struct slot *slot) slot->state = POWEROFF_STATE; mutex_unlock(&slot->lock); - ctrl->request_result = pciehp_disable_slot(slot); + ctrl->request_result = pciehp_disable_slot(slot, SAFE_REMOVAL); } void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) @@ -243,7 +247,7 @@ void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) if (events & PCI_EXP_SLTSTA_PDC) ctrl_info(ctrl, "Slot(%s): Card not present\n", slot_name(slot)); - pciehp_disable_slot(slot); + pciehp_disable_slot(slot, SURPRISE_REMOVAL); break; default: mutex_unlock(&slot->lock); @@ -329,7 +333,7 @@ static int pciehp_enable_slot(struct slot *slot) return ret; } -static int __pciehp_disable_slot(struct slot *p_slot) +static int __pciehp_disable_slot(struct slot *p_slot, bool safe_removal) { u8 getstatus = 0; struct controller *ctrl = p_slot->ctrl; @@ -343,17 +347,17 @@ static int __pciehp_disable_slot(struct slot *p_slot) } } - remove_board(p_slot); + remove_board(p_slot, safe_removal); return 0; } -static int pciehp_disable_slot(struct slot *slot) +static int pciehp_disable_slot(struct slot *slot, bool safe_removal) { struct controller *ctrl = slot->ctrl; int ret; pm_runtime_get_sync(&ctrl->pcie->port->dev); - ret = __pciehp_disable_slot(slot); + ret = __pciehp_disable_slot(slot, safe_removal); pm_runtime_put(&ctrl->pcie->port->dev); mutex_lock(&slot->lock); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 3ef5c0744249..0322bd4f0a7a 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -20,6 +20,14 @@ #include "../pci.h" #include "pciehp.h" +/** + * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge + * @p_slot: PCIe hotplug slot + * + * Enumerate PCI devices below a hotplug bridge and add them to the system. + * Return 0 on success, %-EEXIST if the devices are already enumerated or + * %-ENODEV if enumeration failed. + */ int pciehp_configure_device(struct slot *p_slot) { struct pci_dev *dev; @@ -62,9 +70,19 @@ int pciehp_configure_device(struct slot *p_slot) return ret; } -void pciehp_unconfigure_device(struct slot *p_slot) +/** + * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge + * @p_slot: PCIe hotplug slot + * @presence: whether the card is still present in the slot; + * true for safe removal via sysfs or an Attention Button press, + * false for surprise removal + * + * Unbind PCI devices below a hotplug bridge from their drivers and remove + * them from the system. Safely removed devices are quiesced. Surprise + * removed devices are marked as such to prevent further accesses. + */ +void pciehp_unconfigure_device(struct slot *p_slot, bool presence) { - u8 presence = 0; struct pci_dev *dev, *temp; struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; u16 command; @@ -72,7 +90,6 @@ void pciehp_unconfigure_device(struct slot *p_slot) ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", __func__, pci_domain_nr(parent), parent->number); - pciehp_get_adapter_status(p_slot, &presence); if (!presence) pci_walk_bus(parent, pci_dev_set_disconnected, NULL); -- cgit v1.2.3 From 7d4ba52317c4aab6bbb266f31e28713d397e570c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 19 Aug 2018 16:29:00 +0200 Subject: PCI: pciehp: Drop unnecessary includes Drop the following includes from pciehp source files which no longer use any of the included symbols: * in pciehp.h in pciehp_hpc.c Added by commit de25968cc87c ("fix more missing includes") to accommodate for a call to signal_pending(). The call was removed by commit 262303fe329a ("pciehp: fix wait command completion"). * in pciehp_core.c Added by historic commit f308a2dfbe63 ("PCI: add PCI Express Port Bus Driver subsystem") to accommodate for a call to free_irq(): https://git.kernel.org/tglx/history/c/f308a2dfbe63 The call was removed by commit 407f452b05f9 ("pciehp: remove unnecessary free_irq"). * in pciehp_core.c and pciehp_hpc.c Added by commit 34d03419f03b ("PCIEHP: Add Electro Mechanical Interlock (EMI) support to the PCIE hotplug driver."), which was reverted by commit bd3d99c17039 ("PCI: Remove untested Electromechanical Interlock (EMI) support in pciehp."). * in pciehp_ctrl.c, pciehp_hpc.c and pciehp_pci.c Added by historic commit c16b4b14d980 ("PCI Hotplug: Add SHPC and PCI Express hot-plug drivers"): https://git.kernel.org/tglx/history/c/c16b4b14d980 Module-related symbols were neither used back then in those files, nor are they used today. * in pciehp_ctrl.c Added by commit 5a0e3ad6af86 ("include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h") to accommodate for calls to kmalloc(). The calls were removed by commit 0e94916e6091 ("PCI: pciehp: Handle events synchronously"). * "../pci.h" in pciehp_ctrl.c Added by historic commit 67f4660b72f2 ("PCI: ASPM patch for") to accommodate for usage of the global variable pcie_mch_quirk: https://git.kernel.org/tglx/history/c/67f4660b72f2 The global variable was removed by commit 0ba379ec0fb1 ("PCI: Simplify hotplug mch quirk"). Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 1 - drivers/pci/hotplug/pciehp_core.c | 2 -- drivers/pci/hotplug/pciehp_ctrl.c | 3 --- drivers/pci/hotplug/pciehp_hpc.c | 3 --- drivers/pci/hotplug/pciehp_pci.c | 1 - 5 files changed, 10 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 39c9c8815a35..337f2a5a303f 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -19,7 +19,6 @@ #include #include #include -#include /* signal_pending() */ #include #include #include diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index ec48c9433ae5..e77e17106f52 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -23,8 +23,6 @@ #include #include #include "pciehp.h" -#include -#include #include "../pci.h" diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index c283253d2cd7..ffaf0976e6af 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -13,13 +13,10 @@ * */ -#include #include #include -#include #include #include -#include "../pci.h" #include "pciehp.h" /* The following routines constitute the bulk of the diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index a938abdb41ce..c3295ebf896d 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -13,15 +13,12 @@ */ #include -#include #include -#include #include #include #include #include #include -#include #include #include "../pci.h" diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 0322bd4f0a7a..8da87931bd45 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -13,7 +13,6 @@ * */ -#include #include #include #include -- cgit v1.2.3 From eee6e273843dc3084c15efc98a78702ac9a4c69b Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 19 Aug 2018 16:29:00 +0200 Subject: PCI: pciehp: Drop hotplug_slot_ops wrappers pciehp's ->enable_slot, ->disable_slot, ->get_attention_status and ->reset_slot callbacks are currently implemented by wrapper functions that do nothing else but call down to a backend function. The backends are not called from anywhere else, so drop the wrappers and use the backends directly as callbacks, thereby shaving off a few lines of unnecessary code. No functional change intended. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 8 ++++---- drivers/pci/hotplug/pciehp_core.c | 43 ++++----------------------------------- drivers/pci/hotplug/pciehp_ctrl.c | 6 ++++-- drivers/pci/hotplug/pciehp_hpc.c | 8 ++++++-- 4 files changed, 18 insertions(+), 47 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 337f2a5a303f..8131c08b21e5 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -173,8 +173,6 @@ struct controller { #define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS) #define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19) -int pciehp_sysfs_enable_slot(struct slot *slot); -int pciehp_sysfs_disable_slot(struct slot *slot); void pciehp_request(struct controller *ctrl, int action); void pciehp_handle_button_press(struct slot *slot); void pciehp_handle_disable_request(struct slot *slot); @@ -189,7 +187,6 @@ void pcie_clear_hotplug_events(struct controller *ctrl); int pciehp_power_on_slot(struct slot *slot); void pciehp_power_off_slot(struct slot *slot); void pciehp_get_power_status(struct slot *slot, u8 *status); -void pciehp_get_attention_status(struct slot *slot, u8 *status); void pciehp_set_attention_status(struct slot *slot, u8 status); void pciehp_get_latch_status(struct slot *slot, u8 *status); @@ -201,8 +198,11 @@ void pciehp_green_led_blink(struct slot *slot); int pciehp_check_link_status(struct controller *ctrl); bool pciehp_check_link_active(struct controller *ctrl); void pciehp_release_ctrl(struct controller *ctrl); -int pciehp_reset_slot(struct slot *slot, int probe); +int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot); +int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot); +int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe); +int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status); int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status); int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index e77e17106f52..ccaf01e6eced 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -45,13 +45,9 @@ MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); #define PCIE_MODULE_NAME "pciehp" static int set_attention_status(struct hotplug_slot *slot, u8 value); -static int enable_slot(struct hotplug_slot *slot); -static int disable_slot(struct hotplug_slot *slot); static int get_power_status(struct hotplug_slot *slot, u8 *value); -static int get_attention_status(struct hotplug_slot *slot, u8 *value); static int get_latch_status(struct hotplug_slot *slot, u8 *value); static int get_adapter_status(struct hotplug_slot *slot, u8 *value); -static int reset_slot(struct hotplug_slot *slot, int probe); static int init_slot(struct controller *ctrl) { @@ -75,15 +71,15 @@ static int init_slot(struct controller *ctrl) if (!ops) goto out; - ops->enable_slot = enable_slot; - ops->disable_slot = disable_slot; + ops->enable_slot = pciehp_sysfs_enable_slot; + ops->disable_slot = pciehp_sysfs_disable_slot; ops->get_power_status = get_power_status; ops->get_adapter_status = get_adapter_status; - ops->reset_slot = reset_slot; + ops->reset_slot = pciehp_reset_slot; if (MRL_SENS(ctrl)) ops->get_latch_status = get_latch_status; if (ATTN_LED(ctrl)) { - ops->get_attention_status = get_attention_status; + ops->get_attention_status = pciehp_get_attention_status; ops->set_attention_status = set_attention_status; } else if (ctrl->pcie->port->hotplug_user_indicators) { ops->get_attention_status = pciehp_get_raw_indicator_status; @@ -134,22 +130,6 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) return 0; } - -static int enable_slot(struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = hotplug_slot->private; - - return pciehp_sysfs_enable_slot(slot); -} - - -static int disable_slot(struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = hotplug_slot->private; - - return pciehp_sysfs_disable_slot(slot); -} - static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { struct slot *slot = hotplug_slot->private; @@ -161,14 +141,6 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = hotplug_slot->private; - - pciehp_get_attention_status(slot, value); - return 0; -} - static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) { struct slot *slot = hotplug_slot->private; @@ -191,13 +163,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int reset_slot(struct hotplug_slot *hotplug_slot, int probe) -{ - struct slot *slot = hotplug_slot->private; - - return pciehp_reset_slot(slot, probe); -} - /** * pciehp_check_presence() - synthesize event if presence has changed * diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index ffaf0976e6af..c4d0f902f1d2 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -364,8 +364,9 @@ static int pciehp_disable_slot(struct slot *slot, bool safe_removal) return ret; } -int pciehp_sysfs_enable_slot(struct slot *p_slot) +int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) { + struct slot *p_slot = hotplug_slot->private; struct controller *ctrl = p_slot->ctrl; mutex_lock(&p_slot->lock); @@ -402,8 +403,9 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot) return -ENODEV; } -int pciehp_sysfs_disable_slot(struct slot *p_slot) +int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) { + struct slot *p_slot = hotplug_slot->private; struct controller *ctrl = p_slot->ctrl; mutex_lock(&p_slot->lock); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index c3295ebf896d..93003ff81166 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -326,8 +326,9 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, return 0; } -void pciehp_get_attention_status(struct slot *slot, u8 *status) +int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status) { + struct slot *slot = hotplug_slot->private; struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; @@ -352,6 +353,8 @@ void pciehp_get_attention_status(struct slot *slot, u8 *status) *status = 0xFF; break; } + + return 0; } void pciehp_get_power_status(struct slot *slot, u8 *status) @@ -753,8 +756,9 @@ void pcie_clear_hotplug_events(struct controller *ctrl) * momentarily, if we see that they could interfere. Also, clear any spurious * events after. */ -int pciehp_reset_slot(struct slot *slot, int probe) +int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe) { + struct slot *slot = hotplug_slot->private; struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); u16 stat_mask = 0, ctrl_mask = 0; -- cgit v1.2.3 From 80696f991424d05a784c0cf9c314ac09ac280406 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 8 Sep 2018 09:59:01 +0200 Subject: PCI: pciehp: Tolerate Presence Detect hardwired to zero The WiGig Bus Extension (WBE) specification allows tunneling PCIe over IEEE 802.11. A product implementing this spec is the wil6210 from Wilocity (now part of Qualcomm Atheros). It integrates a PCIe switch with a wireless network adapter: 00.0-+ [1ae9:0101] Upstream Port +-00.0-+ [1ae9:0200] Downstream Port | +-00.0 [168c:0034] Atheros AR9462 Wireless Network Adapter +-02.0 [1ae9:0201] Downstream Port +-03.0 [1ae9:0201] Downstream Port Wirelessly attached devices presumably appear below the hotplug ports with device ID [1ae9:0201]. Oddly, the Downstream Port [1ae9:0200] leading to the wireless network adapter is likewise Hotplug Capable, but has its Presence Detect State bit hardwired to zero. Even if the Link Active bit is set, Presence Detect is zero, so this cannot be caused by in-band presence detection but only by broken hardware. pciehp assumes an empty slot if Presence Detect State is zero, regardless of Link Active being one. Consequently, up until v4.18 it removes the wireless network adapter in pciehp_resume(). From v4.19 it already does so in pciehp_probe(). Be lenient towards broken hardware and assume the slot is occupied if Link Active is set: Introduce pciehp_card_present_or_link_active() and use it in lieu of pciehp_get_adapter_status() everywhere, except in pciehp_handle_presence_or_link_change() whose log messages depend on which of Presence Detect State or Link Active is set. Remove the Presence Detect State check from __pciehp_enable_slot() because it is only called if either of Presence Detect State or Link Active is set. Caution: There is a possibility that broken hardware exists which has working Presence Detect but hardwires Link Active to one. On such hardware the slot will now incorrectly be considered always occupied. If such hardware is discovered, this commit can be rolled back and a quirk can be added which sets is_hotplug_bridge = 0 for [1ae9:0200]. Link: https://bugzilla.kernel.org/show_bug.cgi?id=200839 Reported-and-tested-by: David Yang Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Cc: Rajat Jain Cc: Ashok Raj --- drivers/pci/hotplug/pciehp.h | 3 ++- drivers/pci/hotplug/pciehp_core.c | 6 +++--- drivers/pci/hotplug/pciehp_ctrl.c | 10 ++-------- drivers/pci/hotplug/pciehp_hpc.c | 25 +++++++++++++++++++------ 4 files changed, 26 insertions(+), 18 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 8131c08b21e5..b9204ef3ecd7 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -190,11 +190,12 @@ void pciehp_get_power_status(struct slot *slot, u8 *status); void pciehp_set_attention_status(struct slot *slot, u8 status); void pciehp_get_latch_status(struct slot *slot, u8 *status); -void pciehp_get_adapter_status(struct slot *slot, u8 *status); int pciehp_query_power_fault(struct slot *slot); void pciehp_green_led_on(struct slot *slot); void pciehp_green_led_off(struct slot *slot); void pciehp_green_led_blink(struct slot *slot); +bool pciehp_card_present(struct controller *ctrl); +bool pciehp_card_present_or_link_active(struct controller *ctrl); int pciehp_check_link_status(struct controller *ctrl); bool pciehp_check_link_active(struct controller *ctrl); void pciehp_release_ctrl(struct controller *ctrl); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index ccaf01e6eced..d24875102b1f 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -158,7 +158,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) struct pci_dev *pdev = slot->ctrl->pcie->port; pci_config_pm_runtime_get(pdev); - pciehp_get_adapter_status(slot, value); + *value = pciehp_card_present_or_link_active(slot->ctrl); pci_config_pm_runtime_put(pdev); return 0; } @@ -176,12 +176,12 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) static void pciehp_check_presence(struct controller *ctrl) { struct slot *slot = ctrl->slot; - u8 occupied; + bool occupied; down_read(&ctrl->reset_lock); mutex_lock(&slot->lock); - pciehp_get_adapter_status(slot, &occupied); + occupied = pciehp_card_present_or_link_active(ctrl); if ((occupied && (slot->state == OFF_STATE || slot->state == BLINKINGON_STATE)) || (!occupied && (slot->state == ON_STATE || diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index c4d0f902f1d2..1fda6ea96fdc 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -223,8 +223,7 @@ void pciehp_handle_disable_request(struct slot *slot) void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) { struct controller *ctrl = slot->ctrl; - bool link_active; - u8 present; + bool present, link_active; /* * If the slot is on and presence or link has changed, turn it off. @@ -253,7 +252,7 @@ void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) /* Turn the slot on if it's occupied or link is up */ mutex_lock(&slot->lock); - pciehp_get_adapter_status(slot, &present); + present = pciehp_card_present(ctrl); link_active = pciehp_check_link_active(ctrl); if (!present && !link_active) { mutex_unlock(&slot->lock); @@ -286,11 +285,6 @@ static int __pciehp_enable_slot(struct slot *p_slot) u8 getstatus = 0; struct controller *ctrl = p_slot->ctrl; - pciehp_get_adapter_status(p_slot, &getstatus); - if (!getstatus) { - ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot)); - return -ENODEV; - } if (MRL_SENS(p_slot->ctrl)) { pciehp_get_latch_status(p_slot, &getstatus); if (getstatus) { diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 93003ff81166..d6cd4fbc72da 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -389,13 +389,27 @@ void pciehp_get_latch_status(struct slot *slot, u8 *status) *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); } -void pciehp_get_adapter_status(struct slot *slot, u8 *status) +bool pciehp_card_present(struct controller *ctrl) { - struct pci_dev *pdev = ctrl_dev(slot->ctrl); + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - *status = !!(slot_status & PCI_EXP_SLTSTA_PDS); + return slot_status & PCI_EXP_SLTSTA_PDS; +} + +/** + * pciehp_card_present_or_link_active() - whether given slot is occupied + * @ctrl: PCIe hotplug controller + * + * Unlike pciehp_card_present(), which determines presence solely from the + * Presence Detect State bit, this helper also returns true if the Link Active + * bit is set. This is a concession to broken hotplug ports which hardwire + * Presence Detect State to zero, such as Wilocity's [1ae9:0200]. + */ +bool pciehp_card_present_or_link_active(struct controller *ctrl) +{ + return pciehp_card_present(ctrl) || pciehp_check_link_active(ctrl); } int pciehp_query_power_fault(struct slot *slot) @@ -858,7 +872,7 @@ struct controller *pcie_init(struct pcie_device *dev) { struct controller *ctrl; u32 slot_cap, link_cap; - u8 occupied, poweron; + u8 poweron; struct pci_dev *pdev = dev->port; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); @@ -918,9 +932,8 @@ struct controller *pcie_init(struct pcie_device *dev) * requested yet, so avoid triggering a notification with this command. */ if (POWER_CTRL(ctrl)) { - pciehp_get_adapter_status(ctrl->slot, &occupied); pciehp_get_power_status(ctrl->slot, &poweron); - if (!occupied && poweron) { + if (!pciehp_card_present_or_link_active(ctrl) && poweron) { pcie_disable_notification(ctrl); pciehp_power_off_slot(ctrl->slot); } -- cgit v1.2.3 From 5790a9c78e78aa2c35bb7439bee434301dff004c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 18 Sep 2018 21:46:17 +0200 Subject: PCI: pciehp: Unify controller and slot structs pciehp was originally introduced together with shpchp in a single commit, c16b4b14d980 ("PCI Hotplug: Add SHPC and PCI Express hot-plug drivers"): https://git.kernel.org/tglx/history/c/c16b4b14d980 shpchp supports up to 31 slots per controller, hence uses separate slot and controller structs. pciehp has a 1:1 relationship between slot and controller and therefore never required this separation. Nevertheless, because much of the code had been copy-pasted between the two drivers, pciehp likewise uses separate structs to this very day. The artificial separation of data structures adds unnecessary complexity and bloat to pciehp and requires constantly chasing pointers at runtime. Simplify the driver by merging struct slot into struct controller. Merge the slot constructor pcie_init_slot() and the destructor pcie_cleanup_slot() into the controller counterparts. No functional change intended. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 67 +++++------ drivers/pci/hotplug/pciehp_core.c | 53 ++++----- drivers/pci/hotplug/pciehp_ctrl.c | 244 ++++++++++++++++++-------------------- drivers/pci/hotplug/pciehp_hpc.c | 114 ++++++------------ drivers/pci/hotplug/pciehp_pci.c | 14 +-- 5 files changed, 210 insertions(+), 282 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index b9204ef3ecd7..df9308f6dafa 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -58,24 +58,6 @@ do { \ #define SLOT_NAME_SIZE 10 -/** - * struct slot - PCIe hotplug slot - * @state: current state machine position - * @ctrl: pointer to the slot's controller structure - * @hotplug_slot: pointer to the structure registered with the PCI hotplug core - * @work: work item to turn the slot on or off after 5 seconds in response to - * an Attention Button press - * @lock: protects reads and writes of @state; - * protects scheduling, execution and cancellation of @work - */ -struct slot { - u8 state; - struct controller *ctrl; - struct hotplug_slot *hotplug_slot; - struct delayed_work work; - struct mutex lock; -}; - /** * struct controller - PCIe hotplug controller * @ctrl_lock: serializes writes to the Slot Control register @@ -83,7 +65,6 @@ struct slot { * @reset_lock: prevents access to the Data Link Layer Link Active bit in the * Link Status register and to the Presence Detect State bit in the Slot * Status register during a slot reset which may cause them to flap - * @slot: pointer to the controller's slot structure * @queue: wait queue to wake up on reception of a Command Completed event, * used for synchronous writes to the Slot Control register * @slot_cap: cached copy of the Slot Capabilities register @@ -105,15 +86,23 @@ struct slot { * that has not yet been cleared by the user * @pending_events: used by the IRQ handler to save events retrieved from the * Slot Status register for later consumption by the IRQ thread + * @state: current state machine position + * @lock: protects reads and writes of @state; + * protects scheduling, execution and cancellation of @work + * @work: work item to turn the slot on or off after 5 seconds + * in response to an Attention Button press + * @hotplug_slot: pointer to the structure registered with the PCI hotplug core * @request_result: result of last user request submitted to the IRQ thread * @requester: wait queue to wake up on completion of user request, * used for synchronous slot enable/disable request via sysfs + * + * PCIe hotplug has a 1:1 relationship between controller and slot, hence + * unlike other drivers, the two aren't represented by separate structures. */ struct controller { struct mutex ctrl_lock; struct pcie_device *pcie; struct rw_semaphore reset_lock; - struct slot *slot; wait_queue_head_t queue; u32 slot_cap; u16 slot_ctrl; @@ -124,6 +113,10 @@ struct controller { unsigned int notification_enabled:1; unsigned int power_fault_detected; atomic_t pending_events; + u8 state; + struct mutex lock; + struct delayed_work work; + struct hotplug_slot *hotplug_slot; int request_result; wait_queue_head_t requester; }; @@ -174,26 +167,26 @@ struct controller { #define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19) void pciehp_request(struct controller *ctrl, int action); -void pciehp_handle_button_press(struct slot *slot); -void pciehp_handle_disable_request(struct slot *slot); -void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events); -int pciehp_configure_device(struct slot *p_slot); -void pciehp_unconfigure_device(struct slot *p_slot, bool presence); +void pciehp_handle_button_press(struct controller *ctrl); +void pciehp_handle_disable_request(struct controller *ctrl); +void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events); +int pciehp_configure_device(struct controller *ctrl); +void pciehp_unconfigure_device(struct controller *ctrl, bool presence); void pciehp_queue_pushbutton_work(struct work_struct *work); struct controller *pcie_init(struct pcie_device *dev); int pcie_init_notification(struct controller *ctrl); void pcie_shutdown_notification(struct controller *ctrl); void pcie_clear_hotplug_events(struct controller *ctrl); -int pciehp_power_on_slot(struct slot *slot); -void pciehp_power_off_slot(struct slot *slot); -void pciehp_get_power_status(struct slot *slot, u8 *status); - -void pciehp_set_attention_status(struct slot *slot, u8 status); -void pciehp_get_latch_status(struct slot *slot, u8 *status); -int pciehp_query_power_fault(struct slot *slot); -void pciehp_green_led_on(struct slot *slot); -void pciehp_green_led_off(struct slot *slot); -void pciehp_green_led_blink(struct slot *slot); +int pciehp_power_on_slot(struct controller *ctrl); +void pciehp_power_off_slot(struct controller *ctrl); +void pciehp_get_power_status(struct controller *ctrl, u8 *status); + +void pciehp_set_attention_status(struct controller *ctrl, u8 status); +void pciehp_get_latch_status(struct controller *ctrl, u8 *status); +int pciehp_query_power_fault(struct controller *ctrl); +void pciehp_green_led_on(struct controller *ctrl); +void pciehp_green_led_off(struct controller *ctrl); +void pciehp_green_led_blink(struct controller *ctrl); bool pciehp_card_present(struct controller *ctrl); bool pciehp_card_present_or_link_active(struct controller *ctrl); int pciehp_check_link_status(struct controller *ctrl); @@ -207,9 +200,9 @@ int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status); int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status); int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status); -static inline const char *slot_name(struct slot *slot) +static inline const char *slot_name(struct controller *ctrl) { - return hotplug_slot_name(slot->hotplug_slot); + return hotplug_slot_name(ctrl->hotplug_slot); } #endif /* _PCIEHP_H */ diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index d24875102b1f..4a371ef80842 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -51,7 +51,6 @@ static int get_adapter_status(struct hotplug_slot *slot, u8 *value); static int init_slot(struct controller *ctrl) { - struct slot *slot = ctrl->slot; struct hotplug_slot *hotplug = NULL; struct hotplug_slot_info *info = NULL; struct hotplug_slot_ops *ops = NULL; @@ -88,9 +87,9 @@ static int init_slot(struct controller *ctrl) /* register this slot with the hotplug pci core */ hotplug->info = info; - hotplug->private = slot; + hotplug->private = ctrl; hotplug->ops = ops; - slot->hotplug_slot = hotplug; + ctrl->hotplug_slot = hotplug; snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); retval = pci_hp_initialize(hotplug, @@ -108,7 +107,7 @@ out: static void cleanup_slot(struct controller *ctrl) { - struct hotplug_slot *hotplug_slot = ctrl->slot->hotplug_slot; + struct hotplug_slot *hotplug_slot = ctrl->hotplug_slot; pci_hp_destroy(hotplug_slot); kfree(hotplug_slot->ops); @@ -121,44 +120,44 @@ static void cleanup_slot(struct controller *ctrl) */ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) { - struct slot *slot = hotplug_slot->private; - struct pci_dev *pdev = slot->ctrl->pcie->port; + struct controller *ctrl = hotplug_slot->private; + struct pci_dev *pdev = ctrl->pcie->port; pci_config_pm_runtime_get(pdev); - pciehp_set_attention_status(slot, status); + pciehp_set_attention_status(ctrl, status); pci_config_pm_runtime_put(pdev); return 0; } static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; - struct pci_dev *pdev = slot->ctrl->pcie->port; + struct controller *ctrl = hotplug_slot->private; + struct pci_dev *pdev = ctrl->pcie->port; pci_config_pm_runtime_get(pdev); - pciehp_get_power_status(slot, value); + pciehp_get_power_status(ctrl, value); pci_config_pm_runtime_put(pdev); return 0; } static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; - struct pci_dev *pdev = slot->ctrl->pcie->port; + struct controller *ctrl = hotplug_slot->private; + struct pci_dev *pdev = ctrl->pcie->port; pci_config_pm_runtime_get(pdev); - pciehp_get_latch_status(slot, value); + pciehp_get_latch_status(ctrl, value); pci_config_pm_runtime_put(pdev); return 0; } static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; - struct pci_dev *pdev = slot->ctrl->pcie->port; + struct controller *ctrl = hotplug_slot->private; + struct pci_dev *pdev = ctrl->pcie->port; pci_config_pm_runtime_get(pdev); - *value = pciehp_card_present_or_link_active(slot->ctrl); + *value = pciehp_card_present_or_link_active(ctrl); pci_config_pm_runtime_put(pdev); return 0; } @@ -175,20 +174,19 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) */ static void pciehp_check_presence(struct controller *ctrl) { - struct slot *slot = ctrl->slot; bool occupied; down_read(&ctrl->reset_lock); - mutex_lock(&slot->lock); + mutex_lock(&ctrl->lock); occupied = pciehp_card_present_or_link_active(ctrl); - if ((occupied && (slot->state == OFF_STATE || - slot->state == BLINKINGON_STATE)) || - (!occupied && (slot->state == ON_STATE || - slot->state == BLINKINGOFF_STATE))) + if ((occupied && (ctrl->state == OFF_STATE || + ctrl->state == BLINKINGON_STATE)) || + (!occupied && (ctrl->state == ON_STATE || + ctrl->state == BLINKINGOFF_STATE))) pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); - mutex_unlock(&slot->lock); + mutex_unlock(&ctrl->lock); up_read(&ctrl->reset_lock); } @@ -196,7 +194,6 @@ static int pciehp_probe(struct pcie_device *dev) { int rc; struct controller *ctrl; - struct slot *slot; /* If this is not a "hotplug" service, we have no business here. */ if (dev->service != PCIE_PORT_SERVICE_HP) @@ -234,8 +231,7 @@ static int pciehp_probe(struct pcie_device *dev) } /* Publish to user space */ - slot = ctrl->slot; - rc = pci_hp_add(slot->hotplug_slot); + rc = pci_hp_add(ctrl->hotplug_slot); if (rc) { ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc); goto err_out_shutdown_notification; @@ -258,7 +254,7 @@ static void pciehp_remove(struct pcie_device *dev) { struct controller *ctrl = get_service_data(dev); - pci_hp_del(ctrl->slot->hotplug_slot); + pci_hp_del(ctrl->hotplug_slot); pcie_shutdown_notification(ctrl); cleanup_slot(ctrl); pciehp_release_ctrl(ctrl); @@ -273,14 +269,13 @@ static int pciehp_suspend(struct pcie_device *dev) static int pciehp_resume_noirq(struct pcie_device *dev) { struct controller *ctrl = get_service_data(dev); - struct slot *slot = ctrl->slot; /* pci_restore_state() just wrote to the Slot Control register */ ctrl->cmd_started = jiffies; ctrl->cmd_busy = true; /* clear spurious events from rediscovery of inserted card */ - if (slot->state == ON_STATE || slot->state == BLINKINGOFF_STATE) + if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) pcie_clear_hotplug_events(ctrl); return 0; diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 1fda6ea96fdc..cd0541d80946 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -26,11 +26,11 @@ #define SAFE_REMOVAL true #define SURPRISE_REMOVAL false -static void set_slot_off(struct controller *ctrl, struct slot *pslot) +static void set_slot_off(struct controller *ctrl) { /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ if (POWER_CTRL(ctrl)) { - pciehp_power_off_slot(pslot); + pciehp_power_off_slot(ctrl); /* * After turning power off, we must wait for at least 1 second @@ -40,31 +40,30 @@ static void set_slot_off(struct controller *ctrl, struct slot *pslot) msleep(1000); } - pciehp_green_led_off(pslot); - pciehp_set_attention_status(pslot, 1); + pciehp_green_led_off(ctrl); + pciehp_set_attention_status(ctrl, 1); } /** * board_added - Called after a board has been added to the system. - * @p_slot: &slot where board is added + * @ctrl: PCIe hotplug controller where board is added * * Turns power on for the board. * Configures board. */ -static int board_added(struct slot *p_slot) +static int board_added(struct controller *ctrl) { int retval = 0; - struct controller *ctrl = p_slot->ctrl; struct pci_bus *parent = ctrl->pcie->port->subordinate; if (POWER_CTRL(ctrl)) { /* Power on slot */ - retval = pciehp_power_on_slot(p_slot); + retval = pciehp_power_on_slot(ctrl); if (retval) return retval; } - pciehp_green_led_blink(p_slot); + pciehp_green_led_blink(ctrl); /* Check link training status */ retval = pciehp_check_link_status(ctrl); @@ -74,13 +73,13 @@ static int board_added(struct slot *p_slot) } /* Check for a power fault */ - if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) { - ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot)); + if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) { + ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl)); retval = -EIO; goto err_exit; } - retval = pciehp_configure_device(p_slot); + retval = pciehp_configure_device(ctrl); if (retval) { if (retval != -EEXIST) { ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", @@ -89,28 +88,26 @@ static int board_added(struct slot *p_slot) } } - pciehp_green_led_on(p_slot); - pciehp_set_attention_status(p_slot, 0); + pciehp_green_led_on(ctrl); + pciehp_set_attention_status(ctrl, 0); return 0; err_exit: - set_slot_off(ctrl, p_slot); + set_slot_off(ctrl); return retval; } /** * remove_board - Turns off slot and LEDs - * @p_slot: slot where board is being removed + * @ctrl: PCIe hotplug controller where board is being removed * @safe_removal: whether the board is safely removed (versus surprise removed) */ -static void remove_board(struct slot *p_slot, bool safe_removal) +static void remove_board(struct controller *ctrl, bool safe_removal) { - struct controller *ctrl = p_slot->ctrl; - - pciehp_unconfigure_device(p_slot, safe_removal); + pciehp_unconfigure_device(ctrl, safe_removal); if (POWER_CTRL(ctrl)) { - pciehp_power_off_slot(p_slot); + pciehp_power_off_slot(ctrl); /* * After turning power off, we must wait for at least 1 second @@ -121,11 +118,11 @@ static void remove_board(struct slot *p_slot, bool safe_removal) } /* turn off Green LED */ - pciehp_green_led_off(p_slot); + pciehp_green_led_off(ctrl); } -static int pciehp_enable_slot(struct slot *slot); -static int pciehp_disable_slot(struct slot *slot, bool safe_removal); +static int pciehp_enable_slot(struct controller *ctrl); +static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal); void pciehp_request(struct controller *ctrl, int action) { @@ -136,11 +133,11 @@ void pciehp_request(struct controller *ctrl, int action) void pciehp_queue_pushbutton_work(struct work_struct *work) { - struct slot *p_slot = container_of(work, struct slot, work.work); - struct controller *ctrl = p_slot->ctrl; + struct controller *ctrl = container_of(work, struct controller, + work.work); - mutex_lock(&p_slot->lock); - switch (p_slot->state) { + mutex_lock(&ctrl->lock); + switch (ctrl->state) { case BLINKINGOFF_STATE: pciehp_request(ctrl, DISABLE_SLOT); break; @@ -150,30 +147,28 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) default: break; } - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->lock); } -void pciehp_handle_button_press(struct slot *p_slot) +void pciehp_handle_button_press(struct controller *ctrl) { - struct controller *ctrl = p_slot->ctrl; - - mutex_lock(&p_slot->lock); - switch (p_slot->state) { + mutex_lock(&ctrl->lock); + switch (ctrl->state) { case OFF_STATE: case ON_STATE: - if (p_slot->state == ON_STATE) { - p_slot->state = BLINKINGOFF_STATE; + if (ctrl->state == ON_STATE) { + ctrl->state = BLINKINGOFF_STATE; ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", - slot_name(p_slot)); + slot_name(ctrl)); } else { - p_slot->state = BLINKINGON_STATE; + ctrl->state = BLINKINGON_STATE; ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n", - slot_name(p_slot)); + slot_name(ctrl)); } /* blink green LED and turn off amber */ - pciehp_green_led_blink(p_slot); - pciehp_set_attention_status(p_slot, 0); - schedule_delayed_work(&p_slot->work, 5 * HZ); + pciehp_green_led_blink(ctrl); + pciehp_set_attention_status(ctrl, 0); + schedule_delayed_work(&ctrl->work, 5 * HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: @@ -182,192 +177,184 @@ void pciehp_handle_button_press(struct slot *p_slot) * press the attention again before the 5 sec. limit * expires to cancel hot-add or hot-remove */ - ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot)); - cancel_delayed_work(&p_slot->work); - if (p_slot->state == BLINKINGOFF_STATE) { - p_slot->state = ON_STATE; - pciehp_green_led_on(p_slot); + ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl)); + cancel_delayed_work(&ctrl->work); + if (ctrl->state == BLINKINGOFF_STATE) { + ctrl->state = ON_STATE; + pciehp_green_led_on(ctrl); } else { - p_slot->state = OFF_STATE; - pciehp_green_led_off(p_slot); + ctrl->state = OFF_STATE; + pciehp_green_led_off(ctrl); } - pciehp_set_attention_status(p_slot, 0); + pciehp_set_attention_status(ctrl, 0); ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", - slot_name(p_slot)); + slot_name(ctrl)); break; default: ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", - slot_name(p_slot), p_slot->state); + slot_name(ctrl), ctrl->state); break; } - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->lock); } -void pciehp_handle_disable_request(struct slot *slot) +void pciehp_handle_disable_request(struct controller *ctrl) { - struct controller *ctrl = slot->ctrl; - - mutex_lock(&slot->lock); - switch (slot->state) { + mutex_lock(&ctrl->lock); + switch (ctrl->state) { case BLINKINGON_STATE: case BLINKINGOFF_STATE: - cancel_delayed_work(&slot->work); + cancel_delayed_work(&ctrl->work); break; } - slot->state = POWEROFF_STATE; - mutex_unlock(&slot->lock); + ctrl->state = POWEROFF_STATE; + mutex_unlock(&ctrl->lock); - ctrl->request_result = pciehp_disable_slot(slot, SAFE_REMOVAL); + ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL); } -void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) +void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) { - struct controller *ctrl = slot->ctrl; bool present, link_active; /* * If the slot is on and presence or link has changed, turn it off. * Even if it's occupied again, we cannot assume the card is the same. */ - mutex_lock(&slot->lock); - switch (slot->state) { + mutex_lock(&ctrl->lock); + switch (ctrl->state) { case BLINKINGOFF_STATE: - cancel_delayed_work(&slot->work); + cancel_delayed_work(&ctrl->work); /* fall through */ case ON_STATE: - slot->state = POWEROFF_STATE; - mutex_unlock(&slot->lock); + ctrl->state = POWEROFF_STATE; + mutex_unlock(&ctrl->lock); if (events & PCI_EXP_SLTSTA_DLLSC) ctrl_info(ctrl, "Slot(%s): Link Down\n", - slot_name(slot)); + slot_name(ctrl)); if (events & PCI_EXP_SLTSTA_PDC) ctrl_info(ctrl, "Slot(%s): Card not present\n", - slot_name(slot)); - pciehp_disable_slot(slot, SURPRISE_REMOVAL); + slot_name(ctrl)); + pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); break; default: - mutex_unlock(&slot->lock); + mutex_unlock(&ctrl->lock); break; } /* Turn the slot on if it's occupied or link is up */ - mutex_lock(&slot->lock); + mutex_lock(&ctrl->lock); present = pciehp_card_present(ctrl); link_active = pciehp_check_link_active(ctrl); if (!present && !link_active) { - mutex_unlock(&slot->lock); + mutex_unlock(&ctrl->lock); return; } - switch (slot->state) { + switch (ctrl->state) { case BLINKINGON_STATE: - cancel_delayed_work(&slot->work); + cancel_delayed_work(&ctrl->work); /* fall through */ case OFF_STATE: - slot->state = POWERON_STATE; - mutex_unlock(&slot->lock); + ctrl->state = POWERON_STATE; + mutex_unlock(&ctrl->lock); if (present) ctrl_info(ctrl, "Slot(%s): Card present\n", - slot_name(slot)); + slot_name(ctrl)); if (link_active) ctrl_info(ctrl, "Slot(%s): Link Up\n", - slot_name(slot)); - ctrl->request_result = pciehp_enable_slot(slot); + slot_name(ctrl)); + ctrl->request_result = pciehp_enable_slot(ctrl); break; default: - mutex_unlock(&slot->lock); + mutex_unlock(&ctrl->lock); break; } } -static int __pciehp_enable_slot(struct slot *p_slot) +static int __pciehp_enable_slot(struct controller *ctrl) { u8 getstatus = 0; - struct controller *ctrl = p_slot->ctrl; - if (MRL_SENS(p_slot->ctrl)) { - pciehp_get_latch_status(p_slot, &getstatus); + if (MRL_SENS(ctrl)) { + pciehp_get_latch_status(ctrl, &getstatus); if (getstatus) { ctrl_info(ctrl, "Slot(%s): Latch open\n", - slot_name(p_slot)); + slot_name(ctrl)); return -ENODEV; } } - if (POWER_CTRL(p_slot->ctrl)) { - pciehp_get_power_status(p_slot, &getstatus); + if (POWER_CTRL(ctrl)) { + pciehp_get_power_status(ctrl, &getstatus); if (getstatus) { ctrl_info(ctrl, "Slot(%s): Already enabled\n", - slot_name(p_slot)); + slot_name(ctrl)); return 0; } } - return board_added(p_slot); + return board_added(ctrl); } -static int pciehp_enable_slot(struct slot *slot) +static int pciehp_enable_slot(struct controller *ctrl) { - struct controller *ctrl = slot->ctrl; int ret; pm_runtime_get_sync(&ctrl->pcie->port->dev); - ret = __pciehp_enable_slot(slot); + ret = __pciehp_enable_slot(ctrl); if (ret && ATTN_BUTTN(ctrl)) - pciehp_green_led_off(slot); /* may be blinking */ + pciehp_green_led_off(ctrl); /* may be blinking */ pm_runtime_put(&ctrl->pcie->port->dev); - mutex_lock(&slot->lock); - slot->state = ret ? OFF_STATE : ON_STATE; - mutex_unlock(&slot->lock); + mutex_lock(&ctrl->lock); + ctrl->state = ret ? OFF_STATE : ON_STATE; + mutex_unlock(&ctrl->lock); return ret; } -static int __pciehp_disable_slot(struct slot *p_slot, bool safe_removal) +static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal) { u8 getstatus = 0; - struct controller *ctrl = p_slot->ctrl; - if (POWER_CTRL(p_slot->ctrl)) { - pciehp_get_power_status(p_slot, &getstatus); + if (POWER_CTRL(ctrl)) { + pciehp_get_power_status(ctrl, &getstatus); if (!getstatus) { ctrl_info(ctrl, "Slot(%s): Already disabled\n", - slot_name(p_slot)); + slot_name(ctrl)); return -EINVAL; } } - remove_board(p_slot, safe_removal); + remove_board(ctrl, safe_removal); return 0; } -static int pciehp_disable_slot(struct slot *slot, bool safe_removal) +static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal) { - struct controller *ctrl = slot->ctrl; int ret; pm_runtime_get_sync(&ctrl->pcie->port->dev); - ret = __pciehp_disable_slot(slot, safe_removal); + ret = __pciehp_disable_slot(ctrl, safe_removal); pm_runtime_put(&ctrl->pcie->port->dev); - mutex_lock(&slot->lock); - slot->state = OFF_STATE; - mutex_unlock(&slot->lock); + mutex_lock(&ctrl->lock); + ctrl->state = OFF_STATE; + mutex_unlock(&ctrl->lock); return ret; } int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *p_slot = hotplug_slot->private; - struct controller *ctrl = p_slot->ctrl; + struct controller *ctrl = hotplug_slot->private; - mutex_lock(&p_slot->lock); - switch (p_slot->state) { + mutex_lock(&ctrl->lock); + switch (ctrl->state) { case BLINKINGON_STATE: case OFF_STATE: - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->lock); /* * The IRQ thread becomes a no-op if the user pulls out the * card before the thread wakes up, so initialize to -ENODEV. @@ -379,54 +366,53 @@ int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) return ctrl->request_result; case POWERON_STATE: ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", - slot_name(p_slot)); + slot_name(ctrl)); break; case BLINKINGOFF_STATE: case ON_STATE: case POWEROFF_STATE: ctrl_info(ctrl, "Slot(%s): Already enabled\n", - slot_name(p_slot)); + slot_name(ctrl)); break; default: ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", - slot_name(p_slot), p_slot->state); + slot_name(ctrl), ctrl->state); break; } - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->lock); return -ENODEV; } int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *p_slot = hotplug_slot->private; - struct controller *ctrl = p_slot->ctrl; + struct controller *ctrl = hotplug_slot->private; - mutex_lock(&p_slot->lock); - switch (p_slot->state) { + mutex_lock(&ctrl->lock); + switch (ctrl->state) { case BLINKINGOFF_STATE: case ON_STATE: - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->lock); pciehp_request(ctrl, DISABLE_SLOT); wait_event(ctrl->requester, !atomic_read(&ctrl->pending_events)); return ctrl->request_result; case POWEROFF_STATE: ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", - slot_name(p_slot)); + slot_name(ctrl)); break; case BLINKINGON_STATE: case OFF_STATE: case POWERON_STATE: ctrl_info(ctrl, "Slot(%s): Already disabled\n", - slot_name(p_slot)); + slot_name(ctrl)); break; default: ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", - slot_name(p_slot), p_slot->state); + slot_name(ctrl), ctrl->state); break; } - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->lock); return -ENODEV; } diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index d6cd4fbc72da..fa3759c4ab02 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -40,7 +40,7 @@ static inline int pciehp_request_irq(struct controller *ctrl) if (pciehp_poll_mode) { ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl, "pciehp_poll-%s", - slot_name(ctrl->slot)); + slot_name(ctrl)); return PTR_ERR_OR_ZERO(ctrl->poll_thread); } @@ -315,8 +315,8 @@ static int pciehp_link_enable(struct controller *ctrl) int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, u8 *status) { - struct slot *slot = hotplug_slot->private; - struct pci_dev *pdev = ctrl_dev(slot->ctrl); + struct controller *ctrl = hotplug_slot->private; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; pci_config_pm_runtime_get(pdev); @@ -328,8 +328,7 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status) { - struct slot *slot = hotplug_slot->private; - struct controller *ctrl = slot->ctrl; + struct controller *ctrl = hotplug_slot->private; struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; @@ -357,9 +356,8 @@ int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status) return 0; } -void pciehp_get_power_status(struct slot *slot, u8 *status) +void pciehp_get_power_status(struct controller *ctrl, u8 *status) { - struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; @@ -380,9 +378,9 @@ void pciehp_get_power_status(struct slot *slot, u8 *status) } } -void pciehp_get_latch_status(struct slot *slot, u8 *status) +void pciehp_get_latch_status(struct controller *ctrl, u8 *status) { - struct pci_dev *pdev = ctrl_dev(slot->ctrl); + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); @@ -412,9 +410,9 @@ bool pciehp_card_present_or_link_active(struct controller *ctrl) return pciehp_card_present(ctrl) || pciehp_check_link_active(ctrl); } -int pciehp_query_power_fault(struct slot *slot) +int pciehp_query_power_fault(struct controller *ctrl) { - struct pci_dev *pdev = ctrl_dev(slot->ctrl); + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); @@ -424,8 +422,7 @@ int pciehp_query_power_fault(struct slot *slot) int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, u8 status) { - struct slot *slot = hotplug_slot->private; - struct controller *ctrl = slot->ctrl; + struct controller *ctrl = hotplug_slot->private; struct pci_dev *pdev = ctrl_dev(ctrl); pci_config_pm_runtime_get(pdev); @@ -435,9 +432,8 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, return 0; } -void pciehp_set_attention_status(struct slot *slot, u8 value) +void pciehp_set_attention_status(struct controller *ctrl, u8 value) { - struct controller *ctrl = slot->ctrl; u16 slot_cmd; if (!ATTN_LED(ctrl)) @@ -461,10 +457,8 @@ void pciehp_set_attention_status(struct slot *slot, u8 value) pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); } -void pciehp_green_led_on(struct slot *slot) +void pciehp_green_led_on(struct controller *ctrl) { - struct controller *ctrl = slot->ctrl; - if (!PWR_LED(ctrl)) return; @@ -475,10 +469,8 @@ void pciehp_green_led_on(struct slot *slot) PCI_EXP_SLTCTL_PWR_IND_ON); } -void pciehp_green_led_off(struct slot *slot) +void pciehp_green_led_off(struct controller *ctrl) { - struct controller *ctrl = slot->ctrl; - if (!PWR_LED(ctrl)) return; @@ -489,10 +481,8 @@ void pciehp_green_led_off(struct slot *slot) PCI_EXP_SLTCTL_PWR_IND_OFF); } -void pciehp_green_led_blink(struct slot *slot) +void pciehp_green_led_blink(struct controller *ctrl) { - struct controller *ctrl = slot->ctrl; - if (!PWR_LED(ctrl)) return; @@ -503,9 +493,8 @@ void pciehp_green_led_blink(struct slot *slot) PCI_EXP_SLTCTL_PWR_IND_BLINK); } -int pciehp_power_on_slot(struct slot *slot) +int pciehp_power_on_slot(struct controller *ctrl) { - struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; int retval; @@ -529,10 +518,8 @@ int pciehp_power_on_slot(struct slot *slot) return retval; } -void pciehp_power_off_slot(struct slot *slot) +void pciehp_power_off_slot(struct controller *ctrl) { - struct controller *ctrl = slot->ctrl; - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, @@ -630,7 +617,6 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) { struct controller *ctrl = (struct controller *)dev_id; struct pci_dev *pdev = ctrl_dev(ctrl); - struct slot *slot = ctrl->slot; irqreturn_t ret; u32 events; @@ -656,16 +642,16 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) /* Check Attention Button Pressed */ if (events & PCI_EXP_SLTSTA_ABP) { ctrl_info(ctrl, "Slot(%s): Attention button pressed\n", - slot_name(slot)); - pciehp_handle_button_press(slot); + slot_name(ctrl)); + pciehp_handle_button_press(ctrl); } /* Check Power Fault Detected */ if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { ctrl->power_fault_detected = 1; - ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot)); - pciehp_set_attention_status(slot, 1); - pciehp_green_led_off(slot); + ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl)); + pciehp_set_attention_status(ctrl, 1); + pciehp_green_led_off(ctrl); } /* @@ -674,9 +660,9 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) */ down_read(&ctrl->reset_lock); if (events & DISABLE_SLOT) - pciehp_handle_disable_request(slot); + pciehp_handle_disable_request(ctrl); else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC)) - pciehp_handle_presence_or_link_change(slot, events); + pciehp_handle_presence_or_link_change(ctrl, events); up_read(&ctrl->reset_lock); pci_config_pm_runtime_put(pdev); @@ -772,8 +758,7 @@ void pcie_clear_hotplug_events(struct controller *ctrl) */ int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe) { - struct slot *slot = hotplug_slot->private; - struct controller *ctrl = slot->ctrl; + struct controller *ctrl = hotplug_slot->private; struct pci_dev *pdev = ctrl_dev(ctrl); u16 stat_mask = 0, ctrl_mask = 0; int rc; @@ -823,34 +808,6 @@ void pcie_shutdown_notification(struct controller *ctrl) } } -static int pcie_init_slot(struct controller *ctrl) -{ - struct pci_bus *subordinate = ctrl_dev(ctrl)->subordinate; - struct slot *slot; - - slot = kzalloc(sizeof(*slot), GFP_KERNEL); - if (!slot) - return -ENOMEM; - - down_read(&pci_bus_sem); - slot->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE; - up_read(&pci_bus_sem); - - slot->ctrl = ctrl; - mutex_init(&slot->lock); - INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); - ctrl->slot = slot; - return 0; -} - -static void pcie_cleanup_slot(struct controller *ctrl) -{ - struct slot *slot = ctrl->slot; - - cancel_delayed_work_sync(&slot->work); - kfree(slot); -} - static inline void dbg_ctrl(struct controller *ctrl) { struct pci_dev *pdev = ctrl->pcie->port; @@ -874,10 +831,11 @@ struct controller *pcie_init(struct pcie_device *dev) u32 slot_cap, link_cap; u8 poweron; struct pci_dev *pdev = dev->port; + struct pci_bus *subordinate = pdev->subordinate; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) - goto abort; + return NULL; ctrl->pcie = dev; pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); @@ -894,11 +852,17 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->slot_cap = slot_cap; mutex_init(&ctrl->ctrl_lock); + mutex_init(&ctrl->lock); init_rwsem(&ctrl->reset_lock); init_waitqueue_head(&ctrl->requester); init_waitqueue_head(&ctrl->queue); + INIT_DELAYED_WORK(&ctrl->work, pciehp_queue_pushbutton_work); dbg_ctrl(ctrl); + down_read(&pci_bus_sem); + ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE; + up_read(&pci_bus_sem); + /* Check if Data Link Layer Link Active Reporting is implemented */ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); if (link_cap & PCI_EXP_LNKCAP_DLLLARC) @@ -924,32 +888,24 @@ struct controller *pcie_init(struct pcie_device *dev) FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC), pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : ""); - if (pcie_init_slot(ctrl)) - goto abort_ctrl; - /* * If empty slot's power status is on, turn power off. The IRQ isn't * requested yet, so avoid triggering a notification with this command. */ if (POWER_CTRL(ctrl)) { - pciehp_get_power_status(ctrl->slot, &poweron); + pciehp_get_power_status(ctrl, &poweron); if (!pciehp_card_present_or_link_active(ctrl) && poweron) { pcie_disable_notification(ctrl); - pciehp_power_off_slot(ctrl->slot); + pciehp_power_off_slot(ctrl); } } return ctrl; - -abort_ctrl: - kfree(ctrl); -abort: - return NULL; } void pciehp_release_ctrl(struct controller *ctrl) { - pcie_cleanup_slot(ctrl); + cancel_delayed_work_sync(&ctrl->work); kfree(ctrl); } diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 8da87931bd45..b9c1396db6fe 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -21,19 +21,18 @@ /** * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge - * @p_slot: PCIe hotplug slot + * @ctrl: PCIe hotplug controller * * Enumerate PCI devices below a hotplug bridge and add them to the system. * Return 0 on success, %-EEXIST if the devices are already enumerated or * %-ENODEV if enumeration failed. */ -int pciehp_configure_device(struct slot *p_slot) +int pciehp_configure_device(struct controller *ctrl) { struct pci_dev *dev; - struct pci_dev *bridge = p_slot->ctrl->pcie->port; + struct pci_dev *bridge = ctrl->pcie->port; struct pci_bus *parent = bridge->subordinate; int num, ret = 0; - struct controller *ctrl = p_slot->ctrl; pci_lock_rescan_remove(); @@ -71,7 +70,7 @@ int pciehp_configure_device(struct slot *p_slot) /** * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge - * @p_slot: PCIe hotplug slot + * @ctrl: PCIe hotplug controller * @presence: whether the card is still present in the slot; * true for safe removal via sysfs or an Attention Button press, * false for surprise removal @@ -80,12 +79,11 @@ int pciehp_configure_device(struct slot *p_slot) * them from the system. Safely removed devices are quiesced. Surprise * removed devices are marked as such to prevent further accesses. */ -void pciehp_unconfigure_device(struct slot *p_slot, bool presence) +void pciehp_unconfigure_device(struct controller *ctrl, bool presence) { struct pci_dev *dev, *temp; - struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; + struct pci_bus *parent = ctrl->pcie->port->subordinate; u16 command; - struct controller *ctrl = p_slot->ctrl; ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", __func__, pci_domain_nr(parent), parent->number); -- cgit v1.2.3 From 4ff3126e80fc2db9d961467f783b5c2f4ccd1ca9 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 8 Sep 2018 09:59:01 +0200 Subject: PCI: pciehp: Rename controller struct members for clarity Of the members which were just moved from pciehp's slot struct to the controller struct, rename "lock" to "state_lock" and rename "work" to "button_work" for clarity. Perform the rename separately to the unification of the two structs per Sinan's request. No functional change intended. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Cc: Sinan Kaya --- drivers/pci/hotplug/pciehp.h | 10 +++---- drivers/pci/hotplug/pciehp_core.c | 4 +-- drivers/pci/hotplug/pciehp_ctrl.c | 58 +++++++++++++++++++-------------------- drivers/pci/hotplug/pciehp_hpc.c | 6 ++-- 4 files changed, 39 insertions(+), 39 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index df9308f6dafa..39b97e2384c3 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -87,9 +87,9 @@ do { \ * @pending_events: used by the IRQ handler to save events retrieved from the * Slot Status register for later consumption by the IRQ thread * @state: current state machine position - * @lock: protects reads and writes of @state; - * protects scheduling, execution and cancellation of @work - * @work: work item to turn the slot on or off after 5 seconds + * @state_lock: protects reads and writes of @state; + * protects scheduling, execution and cancellation of @button_work + * @button_work: work item to turn the slot on or off after 5 seconds * in response to an Attention Button press * @hotplug_slot: pointer to the structure registered with the PCI hotplug core * @request_result: result of last user request submitted to the IRQ thread @@ -114,8 +114,8 @@ struct controller { unsigned int power_fault_detected; atomic_t pending_events; u8 state; - struct mutex lock; - struct delayed_work work; + struct mutex state_lock; + struct delayed_work button_work; struct hotplug_slot *hotplug_slot; int request_result; wait_queue_head_t requester; diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 4a371ef80842..80cc7ba534bf 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -177,7 +177,7 @@ static void pciehp_check_presence(struct controller *ctrl) bool occupied; down_read(&ctrl->reset_lock); - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); occupied = pciehp_card_present_or_link_active(ctrl); if ((occupied && (ctrl->state == OFF_STATE || @@ -186,7 +186,7 @@ static void pciehp_check_presence(struct controller *ctrl) ctrl->state == BLINKINGOFF_STATE))) pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); up_read(&ctrl->reset_lock); } diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index cd0541d80946..04f7ad9fffe1 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -134,9 +134,9 @@ void pciehp_request(struct controller *ctrl, int action) void pciehp_queue_pushbutton_work(struct work_struct *work) { struct controller *ctrl = container_of(work, struct controller, - work.work); + button_work.work); - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); switch (ctrl->state) { case BLINKINGOFF_STATE: pciehp_request(ctrl, DISABLE_SLOT); @@ -147,12 +147,12 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) default: break; } - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); } void pciehp_handle_button_press(struct controller *ctrl) { - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); switch (ctrl->state) { case OFF_STATE: case ON_STATE: @@ -168,7 +168,7 @@ void pciehp_handle_button_press(struct controller *ctrl) /* blink green LED and turn off amber */ pciehp_green_led_blink(ctrl); pciehp_set_attention_status(ctrl, 0); - schedule_delayed_work(&ctrl->work, 5 * HZ); + schedule_delayed_work(&ctrl->button_work, 5 * HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: @@ -178,7 +178,7 @@ void pciehp_handle_button_press(struct controller *ctrl) * expires to cancel hot-add or hot-remove */ ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl)); - cancel_delayed_work(&ctrl->work); + cancel_delayed_work(&ctrl->button_work); if (ctrl->state == BLINKINGOFF_STATE) { ctrl->state = ON_STATE; pciehp_green_led_on(ctrl); @@ -195,20 +195,20 @@ void pciehp_handle_button_press(struct controller *ctrl) slot_name(ctrl), ctrl->state); break; } - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); } void pciehp_handle_disable_request(struct controller *ctrl) { - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); switch (ctrl->state) { case BLINKINGON_STATE: case BLINKINGOFF_STATE: - cancel_delayed_work(&ctrl->work); + cancel_delayed_work(&ctrl->button_work); break; } ctrl->state = POWEROFF_STATE; - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL); } @@ -221,14 +221,14 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) * If the slot is on and presence or link has changed, turn it off. * Even if it's occupied again, we cannot assume the card is the same. */ - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); switch (ctrl->state) { case BLINKINGOFF_STATE: - cancel_delayed_work(&ctrl->work); + cancel_delayed_work(&ctrl->button_work); /* fall through */ case ON_STATE: ctrl->state = POWEROFF_STATE; - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); if (events & PCI_EXP_SLTSTA_DLLSC) ctrl_info(ctrl, "Slot(%s): Link Down\n", slot_name(ctrl)); @@ -238,26 +238,26 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); break; default: - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); break; } /* Turn the slot on if it's occupied or link is up */ - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); present = pciehp_card_present(ctrl); link_active = pciehp_check_link_active(ctrl); if (!present && !link_active) { - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); return; } switch (ctrl->state) { case BLINKINGON_STATE: - cancel_delayed_work(&ctrl->work); + cancel_delayed_work(&ctrl->button_work); /* fall through */ case OFF_STATE: ctrl->state = POWERON_STATE; - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); if (present) ctrl_info(ctrl, "Slot(%s): Card present\n", slot_name(ctrl)); @@ -267,7 +267,7 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) ctrl->request_result = pciehp_enable_slot(ctrl); break; default: - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); break; } } @@ -307,9 +307,9 @@ static int pciehp_enable_slot(struct controller *ctrl) pciehp_green_led_off(ctrl); /* may be blinking */ pm_runtime_put(&ctrl->pcie->port->dev); - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); ctrl->state = ret ? OFF_STATE : ON_STATE; - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); return ret; } @@ -339,9 +339,9 @@ static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal) ret = __pciehp_disable_slot(ctrl, safe_removal); pm_runtime_put(&ctrl->pcie->port->dev); - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); ctrl->state = OFF_STATE; - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); return ret; } @@ -350,11 +350,11 @@ int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) { struct controller *ctrl = hotplug_slot->private; - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); switch (ctrl->state) { case BLINKINGON_STATE: case OFF_STATE: - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); /* * The IRQ thread becomes a no-op if the user pulls out the * card before the thread wakes up, so initialize to -ENODEV. @@ -379,7 +379,7 @@ int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) slot_name(ctrl), ctrl->state); break; } - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); return -ENODEV; } @@ -388,11 +388,11 @@ int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) { struct controller *ctrl = hotplug_slot->private; - mutex_lock(&ctrl->lock); + mutex_lock(&ctrl->state_lock); switch (ctrl->state) { case BLINKINGOFF_STATE: case ON_STATE: - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); pciehp_request(ctrl, DISABLE_SLOT); wait_event(ctrl->requester, !atomic_read(&ctrl->pending_events)); @@ -412,7 +412,7 @@ int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) slot_name(ctrl), ctrl->state); break; } - mutex_unlock(&ctrl->lock); + mutex_unlock(&ctrl->state_lock); return -ENODEV; } diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index fa3759c4ab02..0289a3ae4d90 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -852,11 +852,11 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->slot_cap = slot_cap; mutex_init(&ctrl->ctrl_lock); - mutex_init(&ctrl->lock); + mutex_init(&ctrl->state_lock); init_rwsem(&ctrl->reset_lock); init_waitqueue_head(&ctrl->requester); init_waitqueue_head(&ctrl->queue); - INIT_DELAYED_WORK(&ctrl->work, pciehp_queue_pushbutton_work); + INIT_DELAYED_WORK(&ctrl->button_work, pciehp_queue_pushbutton_work); dbg_ctrl(ctrl); down_read(&pci_bus_sem); @@ -905,7 +905,7 @@ struct controller *pcie_init(struct pcie_device *dev) void pciehp_release_ctrl(struct controller *ctrl) { - cancel_delayed_work_sync(&ctrl->work); + cancel_delayed_work_sync(&ctrl->button_work); kfree(ctrl); } -- cgit v1.2.3 From d758714235e7abb7984468370c912ca2f4dc4e57 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 8 Sep 2018 09:59:01 +0200 Subject: PCI: pciehp: Reshuffle controller struct for clarity The members in pciehp's controller struct are arranged in a seemingly arbitrary order and have grown to an amount that I no longer consider easily graspable by contributors. Sort the members into 5 rubrics: * Slot Capabilities register and quirks * Slot Control register access * Slot Status register event handling * state machine * hotplug core interface Obviously, this is just my personal bikeshed color and if anyone has a better idea, please come forward. Any ordering will do as long as the information is presented in a manageable manner. No functional change intended. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 57 ++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 26 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 39b97e2384c3..3cc88f3e4368 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -60,38 +60,38 @@ do { \ /** * struct controller - PCIe hotplug controller - * @ctrl_lock: serializes writes to the Slot Control register * @pcie: pointer to the controller's PCIe port service device - * @reset_lock: prevents access to the Data Link Layer Link Active bit in the - * Link Status register and to the Presence Detect State bit in the Slot - * Status register during a slot reset which may cause them to flap - * @queue: wait queue to wake up on reception of a Command Completed event, - * used for synchronous writes to the Slot Control register * @slot_cap: cached copy of the Slot Capabilities register + * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting + * Capable bit in Link Capabilities register; if this bit is zero, the + * Data Link Layer Link Active bit in the Link Status register will never + * be set and the driver is thus confined to wait 1 second before assuming + * the link to a hotplugged device is up and accessing it * @slot_ctrl: cached copy of the Slot Control register - * @poll_thread: thread to poll for slot events if no IRQ is available, - * enabled with pciehp_poll_mode module parameter + * @ctrl_lock: serializes writes to the Slot Control register * @cmd_started: jiffies when the Slot Control register was last written; * the next write is allowed 1 second later, absent a Command Completed * interrupt (PCIe r4.0, sec 6.7.3.2) * @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler * on reception of a Command Completed event - * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting - * Capable bit in Link Capabilities register; if this bit is zero, the - * Data Link Layer Link Active bit in the Link Status register will never - * be set and the driver is thus confined to wait 1 second before assuming - * the link to a hotplugged device is up and accessing it + * @queue: wait queue to wake up on reception of a Command Completed event, + * used for synchronous writes to the Slot Control register + * @pending_events: used by the IRQ handler to save events retrieved from the + * Slot Status register for later consumption by the IRQ thread * @notification_enabled: whether the IRQ was requested successfully * @power_fault_detected: whether a power fault was detected by the hardware * that has not yet been cleared by the user - * @pending_events: used by the IRQ handler to save events retrieved from the - * Slot Status register for later consumption by the IRQ thread + * @poll_thread: thread to poll for slot events if no IRQ is available, + * enabled with pciehp_poll_mode module parameter * @state: current state machine position * @state_lock: protects reads and writes of @state; * protects scheduling, execution and cancellation of @button_work * @button_work: work item to turn the slot on or off after 5 seconds * in response to an Attention Button press * @hotplug_slot: pointer to the structure registered with the PCI hotplug core + * @reset_lock: prevents access to the Data Link Layer Link Active bit in the + * Link Status register and to the Presence Detect State bit in the Slot + * Status register during a slot reset which may cause them to flap * @request_result: result of last user request submitted to the IRQ thread * @requester: wait queue to wake up on completion of user request, * used for synchronous slot enable/disable request via sysfs @@ -100,23 +100,28 @@ do { \ * unlike other drivers, the two aren't represented by separate structures. */ struct controller { - struct mutex ctrl_lock; struct pcie_device *pcie; - struct rw_semaphore reset_lock; - wait_queue_head_t queue; - u32 slot_cap; - u16 slot_ctrl; - struct task_struct *poll_thread; - unsigned long cmd_started; /* jiffies */ - unsigned int cmd_busy:1; + + u32 slot_cap; /* capabilities and quirks */ unsigned int link_active_reporting:1; + + u16 slot_ctrl; /* control register access */ + struct mutex ctrl_lock; + unsigned long cmd_started; + unsigned int cmd_busy:1; + wait_queue_head_t queue; + + atomic_t pending_events; /* event handling */ unsigned int notification_enabled:1; unsigned int power_fault_detected; - atomic_t pending_events; - u8 state; + struct task_struct *poll_thread; + + u8 state; /* state machine */ struct mutex state_lock; struct delayed_work button_work; - struct hotplug_slot *hotplug_slot; + + struct hotplug_slot *hotplug_slot; /* hotplug core interface */ + struct rw_semaphore reset_lock; int request_result; wait_queue_head_t requester; }; -- cgit v1.2.3 From 81c4b5bf30de01a0f6b43ccaa1d220f4a0a5d99c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 8 Sep 2018 09:59:01 +0200 Subject: PCI: hotplug: Constify hotplug_slot_ops Hotplug drivers cannot declare their hotplug_slot_ops const, making them attractive targets for attackers, because upon registration of a hotplug slot, __pci_hp_initialize() writes to the "owner" and "mod_name" members in that struct. Fix by moving these members to struct hotplug_slot and constify every driver's hotplug_slot_ops except for pciehp. pciehp constructs its hotplug_slot_ops at runtime based on the PCIe port's capabilities, hence cannot declare them const. It can be converted to __write_rarely once that's mainlined: http://www.openwall.com/lists/kernel-hardening/2016/11/16/3 Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Acked-by: Tyrel Datwyler # drivers/pci/hotplug/rpa* Acked-by: Andy Shevchenko # drivers/platform/x86 Cc: Len Brown Cc: Scott Murray Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: Oliver OHalloran Cc: Gavin Shan Cc: Sebastian Ott Cc: Gerald Schaefer Cc: Corentin Chary Cc: Darren Hart --- drivers/pci/hotplug/acpiphp_core.c | 2 +- drivers/pci/hotplug/cpci_hotplug_core.c | 2 +- drivers/pci/hotplug/cpqphp_core.c | 2 +- drivers/pci/hotplug/ibmphp.h | 2 +- drivers/pci/hotplug/ibmphp_core.c | 2 +- drivers/pci/hotplug/pci_hotplug_core.c | 27 ++++++++++++++------------- drivers/pci/hotplug/pnv_php.c | 2 +- drivers/pci/hotplug/rpaphp.h | 2 +- drivers/pci/hotplug/rpaphp_core.c | 2 +- drivers/pci/hotplug/s390_pci_hpc.c | 2 +- drivers/pci/hotplug/sgi_hotplug.c | 2 +- drivers/pci/hotplug/shpchp_core.c | 2 +- drivers/pci/pci.c | 4 ++-- drivers/pci/slot.c | 2 +- drivers/platform/x86/asus-wmi.c | 3 +-- drivers/platform/x86/eeepc-laptop.c | 3 +-- include/linux/pci_hotplug.h | 10 +++++----- 17 files changed, 35 insertions(+), 36 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index ad32ffbc4b91..e883cef0f3bc 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -57,7 +57,7 @@ static int get_attention_status(struct hotplug_slot *slot, u8 *value); static int get_latch_status(struct hotplug_slot *slot, u8 *value); static int get_adapter_status(struct hotplug_slot *slot, u8 *value); -static struct hotplug_slot_ops acpi_hotplug_slot_ops = { +static const struct hotplug_slot_ops acpi_hotplug_slot_ops = { .enable_slot = enable_slot, .disable_slot = disable_slot, .set_attention_status = set_attention_status, diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index 52a339baf06c..97c32e4c74c8 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -57,7 +57,7 @@ static int get_attention_status(struct hotplug_slot *slot, u8 *value); static int get_adapter_status(struct hotplug_slot *slot, u8 *value); static int get_latch_status(struct hotplug_slot *slot, u8 *value); -static struct hotplug_slot_ops cpci_hotplug_slot_ops = { +static const struct hotplug_slot_ops cpci_hotplug_slot_ops = { .enable_slot = enable_slot, .disable_slot = disable_slot, .set_attention_status = set_attention_status, diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 5a06636e910a..3409b62fceac 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -560,7 +560,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { +static const struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { .set_attention_status = set_attention_status, .enable_slot = process_SI, .disable_slot = process_SS, diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h index fddb78606c74..db387e10581e 100644 --- a/drivers/pci/hotplug/ibmphp.h +++ b/drivers/pci/hotplug/ibmphp.h @@ -740,7 +740,7 @@ int ibmphp_do_disable_slot(struct slot *slot_cur); int ibmphp_update_slot_info(struct slot *); /* This function is called from HPC, so we need it to not be be static */ int ibmphp_configure_card(struct pci_func *, u8); int ibmphp_unconfigure_card(struct slot **, int); -extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops; +extern const struct hotplug_slot_ops ibmphp_hotplug_slot_ops; #endif //__IBMPHP_H diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 4ea57e9019f1..b82fdc17040d 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -1259,7 +1259,7 @@ error: goto exit; } -struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { +const struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { .set_attention_status = set_attention_status, .enable_slot = enable_slot, .disable_slot = ibmphp_disable_slot, diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 90fde5f106d8..ede2ed6f4ce0 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -49,15 +49,15 @@ static DEFINE_MUTEX(pci_hp_mutex); #define GET_STATUS(name, type) \ static int get_##name(struct hotplug_slot *slot, type *value) \ { \ - struct hotplug_slot_ops *ops = slot->ops; \ + const struct hotplug_slot_ops *ops = slot->ops; \ int retval = 0; \ - if (!try_module_get(ops->owner)) \ + if (!try_module_get(slot->owner)) \ return -ENODEV; \ if (ops->get_##name) \ retval = ops->get_##name(slot, value); \ else \ *value = slot->info->name; \ - module_put(ops->owner); \ + module_put(slot->owner); \ return retval; \ } @@ -90,7 +90,7 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, power = (u8)(lpower & 0xff); dbg("power = %d\n", power); - if (!try_module_get(slot->ops->owner)) { + if (!try_module_get(slot->owner)) { retval = -ENODEV; goto exit; } @@ -109,7 +109,7 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, err("Illegal value specified for power\n"); retval = -EINVAL; } - module_put(slot->ops->owner); + module_put(slot->owner); exit: if (retval) @@ -138,7 +138,8 @@ static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf) static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf, size_t count) { - struct hotplug_slot_ops *ops = pci_slot->hotplug->ops; + struct hotplug_slot *slot = pci_slot->hotplug; + const struct hotplug_slot_ops *ops = slot->ops; unsigned long lattention; u8 attention; int retval = 0; @@ -147,13 +148,13 @@ static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf, attention = (u8)(lattention & 0xff); dbg(" - attention = %d\n", attention); - if (!try_module_get(ops->owner)) { + if (!try_module_get(slot->owner)) { retval = -ENODEV; goto exit; } if (ops->set_attention_status) - retval = ops->set_attention_status(pci_slot->hotplug, attention); - module_put(ops->owner); + retval = ops->set_attention_status(slot, attention); + module_put(slot->owner); exit: if (retval) @@ -213,13 +214,13 @@ static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf, test = (u32)(ltest & 0xffffffff); dbg("test = %d\n", test); - if (!try_module_get(slot->ops->owner)) { + if (!try_module_get(slot->owner)) { retval = -ENODEV; goto exit; } if (slot->ops->hardware_test) retval = slot->ops->hardware_test(slot, test); - module_put(slot->ops->owner); + module_put(slot->owner); exit: if (retval) @@ -447,8 +448,8 @@ int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus, if ((slot->info == NULL) || (slot->ops == NULL)) return -EINVAL; - slot->ops->owner = owner; - slot->ops->mod_name = mod_name; + slot->owner = owner; + slot->mod_name = mod_name; /* * No problems if we call this interface from both ACPI_PCI_SLOT diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index 3276a5e4c430..12b92a0ff688 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -530,7 +530,7 @@ static int pnv_php_disable_slot(struct hotplug_slot *slot) return ret; } -static struct hotplug_slot_ops php_slot_ops = { +static const struct hotplug_slot_ops php_slot_ops = { .get_power_status = pnv_php_get_power_state, .get_adapter_status = pnv_php_get_adapter_state, .set_attention_status = pnv_php_set_attention_state, diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h index c8311724bd76..f83347819f7b 100644 --- a/drivers/pci/hotplug/rpaphp.h +++ b/drivers/pci/hotplug/rpaphp.h @@ -70,7 +70,7 @@ struct slot { struct hotplug_slot *hotplug_slot; }; -extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops; +extern const struct hotplug_slot_ops rpaphp_hotplug_slot_ops; extern struct list_head rpaphp_slot_head; /* function prototypes */ diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index 857c358b727b..8620a3f8c987 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -477,7 +477,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) return 0; } -struct hotplug_slot_ops rpaphp_hotplug_slot_ops = { +const struct hotplug_slot_ops rpaphp_hotplug_slot_ops = { .enable_slot = enable_slot, .disable_slot = disable_slot, .set_attention_status = set_attention_status, diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 93b5341d282c..5bd45fd4a92a 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -130,7 +130,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static struct hotplug_slot_ops s390_hotplug_slot_ops = { +static const struct hotplug_slot_ops s390_hotplug_slot_ops = { .enable_slot = enable_slot, .disable_slot = disable_slot, .get_power_status = get_power_status, diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index babd23409f61..af4c28c574dd 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -80,7 +80,7 @@ static int enable_slot(struct hotplug_slot *slot); static int disable_slot(struct hotplug_slot *slot); static inline int get_power_status(struct hotplug_slot *slot, u8 *value); -static struct hotplug_slot_ops sn_hotplug_slot_ops = { +static const struct hotplug_slot_ops sn_hotplug_slot_ops = { .enable_slot = enable_slot, .disable_slot = disable_slot, .get_power_status = get_power_status, diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 97cee23f3d51..26cbea04237c 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -51,7 +51,7 @@ static int get_attention_status(struct hotplug_slot *slot, u8 *value); static int get_latch_status(struct hotplug_slot *slot, u8 *value); static int get_adapter_status(struct hotplug_slot *slot, u8 *value); -static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { +static const struct hotplug_slot_ops shpchp_hotplug_slot_ops = { .set_attention_status = set_attention_status, .enable_slot = enable_slot, .disable_slot = disable_slot, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1835f3a7aa8d..0e54588825cb 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4571,13 +4571,13 @@ static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe) { int rc = -ENOTTY; - if (!hotplug || !try_module_get(hotplug->ops->owner)) + if (!hotplug || !try_module_get(hotplug->owner)) return rc; if (hotplug->ops->reset_slot) rc = hotplug->ops->reset_slot(hotplug, probe); - module_put(hotplug->ops->owner); + module_put(hotplug->owner); return rc; } diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index e634229ece89..145cd953b518 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -371,7 +371,7 @@ void pci_hp_create_module_link(struct pci_slot *pci_slot) if (!slot || !slot->ops) return; - kobj = kset_find_obj(module_kset, slot->ops->mod_name); + kobj = kset_find_obj(module_kset, slot->mod_name); if (!kobj) return; ret = sysfs_create_link(&pci_slot->kobj, kobj, "module"); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 2d6e272315a8..a8aa2eadfd82 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -868,8 +868,7 @@ static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, return 0; } -static struct hotplug_slot_ops asus_hotplug_slot_ops = { - .owner = THIS_MODULE, +static const struct hotplug_slot_ops asus_hotplug_slot_ops = { .get_adapter_status = asus_get_adapter_status, .get_power_status = asus_get_adapter_status, }; diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index a4bbf6ecd1f0..41a364376e91 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -726,8 +726,7 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, return 0; } -static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { - .owner = THIS_MODULE, +static const struct hotplug_slot_ops eeepc_hotplug_slot_ops = { .get_adapter_status = eeepc_get_adapter_status, .get_power_status = eeepc_get_adapter_status, }; diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index a6d6650a0490..372dbe95c207 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -16,8 +16,6 @@ /** * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use - * @owner: The module owner of this structure - * @mod_name: The module name (KBUILD_MODNAME) of this structure * @enable_slot: Called when the user wants to enable a specific pci slot * @disable_slot: Called when the user wants to disable a specific pci slot * @set_attention_status: Called to set the specific slot's attention LED to @@ -46,8 +44,6 @@ * set an LED, enable / disable power, etc.) */ struct hotplug_slot_ops { - struct module *owner; - const char *mod_name; int (*enable_slot) (struct hotplug_slot *slot); int (*disable_slot) (struct hotplug_slot *slot); int (*set_attention_status) (struct hotplug_slot *slot, u8 value); @@ -82,15 +78,19 @@ struct hotplug_slot_info { * this slot. * @private: used by the hotplug pci controller driver to store whatever it * needs. + * @owner: The module owner of this structure + * @mod_name: The module name (KBUILD_MODNAME) of this structure */ struct hotplug_slot { - struct hotplug_slot_ops *ops; + const struct hotplug_slot_ops *ops; struct hotplug_slot_info *info; void *private; /* Variables below this are for use only by the hotplug pci core. */ struct list_head slot_list; struct pci_slot *pci_slot; + struct module *owner; + const char *mod_name; }; static inline const char *hotplug_slot_name(const struct hotplug_slot *slot) -- cgit v1.2.3 From a7da21613c4efcd4cc0235e6a30bec96ae47c619 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 8 Sep 2018 09:59:01 +0200 Subject: PCI: hotplug: Drop hotplug_slot_info Ever since the PCI hotplug core was introduced in 2002, drivers had to allocate and register a struct hotplug_slot_info for every slot: https://git.kernel.org/tglx/history/c/a8a2069f432c Apparently the idea was that drivers furnish the hotplug core with an up-to-date card presence status, power status, latch status and attention indicator status as well as notify the hotplug core of changes thereof. However only 4 out of 12 hotplug drivers bother to notify the hotplug core with pci_hp_change_slot_info() and the hotplug core never made any use of the information: There is just a single macro in pci_hotplug_core.c, GET_STATUS(), which uses the hotplug_slot_info if the driver lacks the corresponding callback in hotplug_slot_ops. The macro is called when the user reads the attribute via sysfs. Now, if the callback isn't defined, the attribute isn't exposed in sysfs in the first place (see e.g. has_power_file()). There are only two situations when the hotplug_slot_info would actually be accessed: * If the driver defines ->enable_slot or ->disable_slot but not ->get_power_status. * If the driver defines ->set_attention_status but not ->get_attention_status. There is no driver doing the former and just a single driver doing the latter, namely pnv_php.c. Amend it with a ->get_attention_status callback. With that, the hotplug_slot_info becomes completely unused by the PCI hotplug core. But a few drivers use it internally as a cache: cpcihp uses it to cache the latch_status and adapter_status. cpqhp uses it to cache the adapter_status. pnv_php and rpaphp use it to cache the attention_status. shpchp uses it to cache all four values. Amend these drivers to cache the information in their private slot struct. shpchp's slot struct already contains members to cache the power_status and adapter_status, so additional members are only needed for the other two values. In the case of cpqphp, the cached value is only accessed in a single place, so instead of caching it, read the current value from the hardware. Caution: acpiphp, cpci, cpqhp, shpchp, asus-wmi and eeepc-laptop populate the hotplug_slot_info with initial values on probe. That code is herewith removed. There is a theoretical chance that the code has side effects without which the driver fails to function, e.g. if the ACPI method to read the adapter status needs to be executed at least once on probe. That seems unlikely to me, still maintainers should review the changes carefully for this possibility. Rafael adds: "I'm not aware of any case in which it will break anything, [...] but if that happens, it may be necessary to add the execution of the control methods in question directly to the initialization part." Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Acked-by: Tyrel Datwyler # drivers/pci/hotplug/rpa* Acked-by: Sebastian Ott # drivers/pci/hotplug/s390* Acked-by: Andy Shevchenko # drivers/platform/x86 Cc: Len Brown Cc: Scott Murray Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: Oliver OHalloran Cc: Gavin Shan Cc: Gerald Schaefer Cc: Corentin Chary Cc: Darren Hart --- arch/powerpc/include/asm/pnv-pci.h | 2 +- drivers/pci/hotplug/acpiphp.h | 1 - drivers/pci/hotplug/acpiphp_core.c | 6 --- drivers/pci/hotplug/cpci_hotplug.h | 2 + drivers/pci/hotplug/cpci_hotplug_core.c | 72 +++++++-------------------------- drivers/pci/hotplug/cpqphp_core.c | 22 +--------- drivers/pci/hotplug/cpqphp_ctrl.c | 31 +------------- drivers/pci/hotplug/ibmphp_core.c | 27 +------------ drivers/pci/hotplug/ibmphp_ebda.c | 33 --------------- drivers/pci/hotplug/pci_hotplug_core.c | 26 +----------- drivers/pci/hotplug/pciehp_core.c | 8 ---- drivers/pci/hotplug/pnv_php.c | 24 ++++++++--- drivers/pci/hotplug/rpaphp.h | 1 + drivers/pci/hotplug/rpaphp_core.c | 4 +- drivers/pci/hotplug/rpaphp_pci.c | 11 +---- drivers/pci/hotplug/rpaphp_slot.c | 9 +---- drivers/pci/hotplug/s390_pci_hpc.c | 12 ------ drivers/pci/hotplug/sgi_hotplug.c | 9 ----- drivers/pci/hotplug/shpchp.h | 2 + drivers/pci/hotplug/shpchp_core.c | 31 +++++--------- drivers/pci/hotplug/shpchp_ctrl.c | 21 +++------- drivers/platform/x86/asus-wmi.c | 10 ----- drivers/platform/x86/eeepc-laptop.c | 10 ----- include/linux/pci_hotplug.h | 30 -------------- 24 files changed, 64 insertions(+), 340 deletions(-) (limited to 'drivers/pci') diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h index 7f627e3f4da4..630eb8b1b7ed 100644 --- a/arch/powerpc/include/asm/pnv-pci.h +++ b/arch/powerpc/include/asm/pnv-pci.h @@ -54,7 +54,6 @@ void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs, struct pnv_php_slot { struct hotplug_slot slot; - struct hotplug_slot_info slot_info; uint64_t id; char *name; int slot_no; @@ -72,6 +71,7 @@ struct pnv_php_slot { struct pci_dev *pdev; struct pci_bus *bus; bool power_state_check; + u8 attention_state; void *fdt; void *dt; struct of_changeset ocs; diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index e438a2d734f2..8377e736ea69 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -35,7 +35,6 @@ struct acpiphp_slot; struct slot { struct hotplug_slot *hotplug_slot; struct acpiphp_slot *acpi_slot; - struct hotplug_slot_info info; unsigned int sun; /* ACPI _SUN (Slot User Number) value */ }; diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index e883cef0f3bc..abd4f8d7e16a 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -270,16 +270,10 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, if (!slot->hotplug_slot) goto error_slot; - slot->hotplug_slot->info = &slot->info; - slot->hotplug_slot->private = slot; slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; slot->acpi_slot = acpiphp_slot; - slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot); - slot->hotplug_slot->info->attention_status = 0; - slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot); - slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot); acpiphp_slot->slot = slot; slot->sun = sun; diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h index 4658557be01a..a35f40a2290c 100644 --- a/drivers/pci/hotplug/cpci_hotplug.h +++ b/drivers/pci/hotplug/cpci_hotplug.h @@ -32,6 +32,8 @@ struct slot { unsigned int devfn; struct pci_bus *bus; struct pci_dev *dev; + unsigned int latch_status:1; + unsigned int adapter_status:1; unsigned int extracting; struct hotplug_slot *hotplug_slot; struct list_head slot_list; diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index 97c32e4c74c8..a17fb24c28cd 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -67,26 +67,6 @@ static const struct hotplug_slot_ops cpci_hotplug_slot_ops = { .get_latch_status = get_latch_status, }; -static int -update_latch_status(struct hotplug_slot *hotplug_slot, u8 value) -{ - struct hotplug_slot_info info; - - memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info)); - info.latch_status = value; - return pci_hp_change_slot_info(hotplug_slot, &info); -} - -static int -update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value) -{ - struct hotplug_slot_info info; - - memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info)); - info.adapter_status = value; - return pci_hp_change_slot_info(hotplug_slot, &info); -} - static int enable_slot(struct hotplug_slot *hotplug_slot) { @@ -135,8 +115,7 @@ disable_slot(struct hotplug_slot *hotplug_slot) goto disable_error; } - if (update_adapter_status(slot->hotplug_slot, 0)) - warn("failure to update adapter file"); + slot->adapter_status = 0; if (slot->extracting) { slot->extracting = 0; @@ -184,20 +163,23 @@ set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - *value = hotplug_slot->info->adapter_status; + struct slot *slot = hotplug_slot->private; + + *value = slot->adapter_status; return 0; } static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) { - *value = hotplug_slot->info->latch_status; + struct slot *slot = hotplug_slot->private; + + *value = slot->latch_status; return 0; } static void release_slot(struct slot *slot) { - kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot); pci_dev_put(slot->dev); kfree(slot); @@ -210,7 +192,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) { struct slot *slot; struct hotplug_slot *hotplug_slot; - struct hotplug_slot_info *info; char name[SLOT_NAME_SIZE]; int status; int i; @@ -237,13 +218,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) } slot->hotplug_slot = hotplug_slot; - info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); - if (!info) { - status = -ENOMEM; - goto error_hpslot; - } - hotplug_slot->info = info; - slot->bus = bus; slot->number = i; slot->devfn = PCI_DEVFN(i, 0); @@ -253,19 +227,11 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) hotplug_slot->private = slot; hotplug_slot->ops = &cpci_hotplug_slot_ops; - /* - * Initialize the slot info structure with some known - * good values. - */ - dbg("initializing slot %s", name); - info->power_status = cpci_get_power_status(slot); - info->attention_status = cpci_get_attention_status(slot); - dbg("registering slot %s", name); status = pci_hp_register(slot->hotplug_slot, bus, i, name); if (status) { err("pci_hp_register failed with error %d", status); - goto error_info; + goto error_hpslot; } dbg("slot registered with name: %s", slot_name(slot)); @@ -276,8 +242,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) up_write(&list_rwsem); } return 0; -error_info: - kfree(info); error_hpslot: kfree(hotplug_slot); error_slot: @@ -359,10 +323,8 @@ init_slots(int clear_ins) __func__, slot_name(slot)); dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0)); if (dev) { - if (update_adapter_status(slot->hotplug_slot, 1)) - warn("failure to update adapter file"); - if (update_latch_status(slot->hotplug_slot, 1)) - warn("failure to update latch file"); + slot->adapter_status = 1; + slot->latch_status = 1; slot->dev = dev; } } @@ -424,11 +386,8 @@ check_slots(void) dbg("%s - slot %s HS_CSR (2) = %04x", __func__, slot_name(slot), hs_csr); - if (update_latch_status(slot->hotplug_slot, 1)) - warn("failure to update latch file"); - - if (update_adapter_status(slot->hotplug_slot, 1)) - warn("failure to update adapter file"); + slot->latch_status = 1; + slot->adapter_status = 1; cpci_led_off(slot); @@ -449,9 +408,7 @@ check_slots(void) __func__, slot_name(slot), hs_csr); if (!slot->extracting) { - if (update_latch_status(slot->hotplug_slot, 0)) - warn("failure to update latch file"); - + slot->latch_status = 0; slot->extracting = 1; atomic_inc(&extracting); } @@ -465,8 +422,7 @@ check_slots(void) */ err("card in slot %s was improperly removed", slot_name(slot)); - if (update_adapter_status(slot->hotplug_slot, 0)) - warn("failure to update adapter file"); + slot->adapter_status = 0; slot->extracting = 0; atomic_dec(&extracting); } diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 3409b62fceac..bb354a7fc112 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -276,7 +276,6 @@ static int ctrl_slot_cleanup(struct controller *ctrl) while (old_slot) { next_slot = old_slot->next; pci_hp_deregister(old_slot->hotplug_slot); - kfree(old_slot->hotplug_slot->info); kfree(old_slot->hotplug_slot); kfree(old_slot); old_slot = next_slot; @@ -579,7 +578,6 @@ static int ctrl_slot_setup(struct controller *ctrl, { struct slot *slot; struct hotplug_slot *hotplug_slot; - struct hotplug_slot_info *hotplug_slot_info; struct pci_bus *bus = ctrl->pci_bus; u8 number_of_slots; u8 slot_device; @@ -613,14 +611,6 @@ static int ctrl_slot_setup(struct controller *ctrl, } hotplug_slot = slot->hotplug_slot; - hotplug_slot->info = kzalloc(sizeof(*(hotplug_slot->info)), - GFP_KERNEL); - if (!hotplug_slot->info) { - result = -ENOMEM; - goto error_hpslot; - } - hotplug_slot_info = hotplug_slot->info; - slot->ctrl = ctrl; slot->bus = ctrl->bus; slot->device = slot_device; @@ -673,14 +663,6 @@ static int ctrl_slot_setup(struct controller *ctrl, snprintf(name, SLOT_NAME_SIZE, "%u", slot->number); hotplug_slot->ops = &cpqphp_hotplug_slot_ops; - hotplug_slot_info->power_status = get_slot_enabled(ctrl, slot); - hotplug_slot_info->attention_status = - cpq_get_attention_status(ctrl, slot); - hotplug_slot_info->latch_status = - cpq_get_latch_status(ctrl, slot); - hotplug_slot_info->adapter_status = - get_presence_status(ctrl, slot); - dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n", slot->bus, slot->device, slot->number, ctrl->slot_device_offset, @@ -691,7 +673,7 @@ static int ctrl_slot_setup(struct controller *ctrl, name); if (result) { err("pci_hp_register failed with error %d\n", result); - goto error_info; + goto error_hpslot; } slot->next = ctrl->slot; @@ -703,8 +685,6 @@ static int ctrl_slot_setup(struct controller *ctrl, } return 0; -error_info: - kfree(hotplug_slot_info); error_hpslot: kfree(hotplug_slot); error_slot: diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index 616df442520b..9c4826ac6a4f 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -1130,9 +1130,9 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ for (slot = ctrl->slot; slot; slot = slot->next) { if (slot->device == (hp_slot + ctrl->slot_device_offset)) continue; - if (!slot->hotplug_slot || !slot->hotplug_slot->info) + if (!slot->hotplug_slot) continue; - if (slot->hotplug_slot->info->adapter_status == 0) + if (get_presence_status(ctrl, slot) == 0) continue; /* If another adapter is running on the same segment but at a * lower speed/mode, we allow the new adapter to function at @@ -1767,24 +1767,6 @@ void cpqhp_event_stop_thread(void) } -static int update_slot_info(struct controller *ctrl, struct slot *slot) -{ - struct hotplug_slot_info *info; - int result; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->power_status = get_slot_enabled(ctrl, slot); - info->attention_status = cpq_get_attention_status(ctrl, slot); - info->latch_status = cpq_get_latch_status(ctrl, slot); - info->adapter_status = get_presence_status(ctrl, slot); - result = pci_hp_change_slot_info(slot->hotplug_slot, info); - kfree(info); - return result; -} - static void interrupt_event_handler(struct controller *ctrl) { int loop = 0; @@ -1884,9 +1866,6 @@ static void interrupt_event_handler(struct controller *ctrl) /***********POWER FAULT */ else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { dbg("power fault\n"); - } else { - /* refresh notification */ - update_slot_info(ctrl, p_slot); } ctrl->event_queue[loop].event_type = 0; @@ -2057,9 +2036,6 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func) if (rc) dbg("%s: rc = %d\n", __func__, rc); - if (p_slot) - update_slot_info(ctrl, p_slot); - return rc; } @@ -2125,9 +2101,6 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func) rc = 1; } - if (p_slot) - update_slot_info(ctrl, p_slot); - return rc; } diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index b82fdc17040d..96e5b1f544ac 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -582,29 +582,10 @@ static int validate(struct slot *slot_cur, int opn) ****************************************************************************/ int ibmphp_update_slot_info(struct slot *slot_cur) { - struct hotplug_slot_info *info; struct pci_bus *bus = slot_cur->hotplug_slot->pci_slot->bus; - int rc; u8 bus_speed; u8 mode; - info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->power_status = SLOT_PWRGD(slot_cur->status); - info->attention_status = SLOT_ATTN(slot_cur->status, - slot_cur->ext_status); - info->latch_status = SLOT_LATCH(slot_cur->status); - if (!SLOT_PRESENT(slot_cur->status)) { - info->adapter_status = 0; -/* info->max_adapter_speed_status = MAX_ADAPTER_NONE; */ - } else { - info->adapter_status = 1; -/* get_max_adapter_speed_1(slot_cur->hotplug_slot, - &info->max_adapter_speed_status, 0); */ - } - bus_speed = slot_cur->bus_on->current_speed; mode = slot_cur->bus_on->current_bus_mode; @@ -630,9 +611,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur) bus->cur_bus_speed = bus_speed; // To do: bus_names - rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info); - kfree(info); - return rc; + return 0; } @@ -684,7 +663,6 @@ static void free_slots(void) ibmphp_unconfigure_card(&slot_cur, -1); pci_hp_destroy(slot_cur->hotplug_slot); - kfree(slot_cur->hotplug_slot->info); kfree(slot_cur->hotplug_slot); kfree(slot_cur); } @@ -1095,8 +1073,7 @@ static int enable_slot(struct hotplug_slot *hs) slot_cur->func = kzalloc(sizeof(struct pci_func), GFP_KERNEL); if (!slot_cur->func) { - /* We cannot do update_slot_info here, since no memory for - * kmalloc n.e.ways, and update_slot_info allocates some */ + /* do update_slot_info here? */ rc = -ENOMEM; goto error_power; } diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index 6f8e90e3ec08..c05d066ab0d5 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c @@ -671,31 +671,6 @@ static int fillslotinfo(struct hotplug_slot *hotplug_slot) slot = hotplug_slot->private; rc = ibmphp_hpc_readslot(slot, READ_ALLSTAT, NULL); - if (rc) - return rc; - - // power - enabled:1 not:0 - hotplug_slot->info->power_status = SLOT_POWER(slot->status); - - // attention - off:0, on:1, blinking:2 - hotplug_slot->info->attention_status = SLOT_ATTN(slot->status, slot->ext_status); - - // latch - open:1 closed:0 - hotplug_slot->info->latch_status = SLOT_LATCH(slot->status); - - // pci board - present:1 not:0 - if (SLOT_PRESENT(slot->status)) - hotplug_slot->info->adapter_status = 1; - else - hotplug_slot->info->adapter_status = 0; -/* - if (slot->bus_on->supported_bus_mode - && (slot->bus_on->supported_speed == BUS_SPEED_66)) - hotplug_slot->info->max_bus_speed_status = BUS_SPEED_66PCIX; - else - hotplug_slot->info->max_bus_speed_status = slot->bus_on->supported_speed; -*/ - return rc; } @@ -877,12 +852,6 @@ static int __init ebda_rsrc_controller(void) goto error_no_hp_slot; } - hp_slot_ptr->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); - if (!hp_slot_ptr->info) { - rc = -ENOMEM; - goto error_no_hp_info; - } - tmp_slot = kzalloc(sizeof(*tmp_slot), GFP_KERNEL); if (!tmp_slot) { rc = -ENOMEM; @@ -955,8 +924,6 @@ static int __init ebda_rsrc_controller(void) error: kfree(hp_slot_ptr->private); error_no_slot: - kfree(hp_slot_ptr->info); -error_no_hp_info: kfree(hp_slot_ptr); error_no_hp_slot: free_ebda_hpc(hpc_ptr); diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index ede2ed6f4ce0..5ac31f683b85 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -55,8 +55,6 @@ static int get_##name(struct hotplug_slot *slot, type *value) \ return -ENODEV; \ if (ops->get_##name) \ retval = ops->get_##name(slot, value); \ - else \ - *value = slot->info->name; \ module_put(slot->owner); \ return retval; \ } @@ -445,7 +443,7 @@ int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus, if (slot == NULL) return -ENODEV; - if ((slot->info == NULL) || (slot->ops == NULL)) + if (slot->ops == NULL) return -EINVAL; slot->owner = owner; @@ -560,28 +558,6 @@ void pci_hp_destroy(struct hotplug_slot *slot) } EXPORT_SYMBOL_GPL(pci_hp_destroy); -/** - * pci_hp_change_slot_info - changes the slot's information structure in the core - * @slot: pointer to the slot whose info has changed - * @info: pointer to the info copy into the slot's info structure - * - * @slot must have been registered with the pci - * hotplug subsystem previously with a call to pci_hp_register(). - * - * Returns 0 if successful, anything else for an error. - */ -int pci_hp_change_slot_info(struct hotplug_slot *slot, - struct hotplug_slot_info *info) -{ - if (!slot || !info) - return -ENODEV; - - memcpy(slot->info, info, sizeof(struct hotplug_slot_info)); - - return 0; -} -EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); - static int __init pci_hotplug_init(void) { int result; diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 80cc7ba534bf..ac5baf887c5d 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -52,7 +52,6 @@ static int get_adapter_status(struct hotplug_slot *slot, u8 *value); static int init_slot(struct controller *ctrl) { struct hotplug_slot *hotplug = NULL; - struct hotplug_slot_info *info = NULL; struct hotplug_slot_ops *ops = NULL; char name[SLOT_NAME_SIZE]; int retval = -ENOMEM; @@ -61,10 +60,6 @@ static int init_slot(struct controller *ctrl) if (!hotplug) goto out; - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - goto out; - /* Setup hotplug slot ops */ ops = kzalloc(sizeof(*ops), GFP_KERNEL); if (!ops) @@ -86,7 +81,6 @@ static int init_slot(struct controller *ctrl) } /* register this slot with the hotplug pci core */ - hotplug->info = info; hotplug->private = ctrl; hotplug->ops = ops; ctrl->hotplug_slot = hotplug; @@ -99,7 +93,6 @@ static int init_slot(struct controller *ctrl) out: if (retval) { kfree(ops); - kfree(info); kfree(hotplug); } return retval; @@ -111,7 +104,6 @@ static void cleanup_slot(struct controller *ctrl) pci_hp_destroy(hotplug_slot); kfree(hotplug_slot->ops); - kfree(hotplug_slot->info); kfree(hotplug_slot); } diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index 12b92a0ff688..5bb63430262e 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -328,6 +328,11 @@ out: return ret; } +static inline struct pnv_php_slot *to_pnv_php_slot(struct hotplug_slot *slot) +{ + return container_of(slot, struct pnv_php_slot, slot); +} + int pnv_php_set_slot_power_state(struct hotplug_slot *slot, uint8_t state) { @@ -378,7 +383,6 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state) ret); } else { *state = power_state; - slot->info->power_status = power_state; } return 0; @@ -397,7 +401,6 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state) ret = pnv_pci_get_presence_state(php_slot->id, &presence); if (ret >= 0) { *state = presence; - slot->info->adapter_status = presence; ret = 0; } else { pci_warn(php_slot->pdev, "Error %d getting presence\n", ret); @@ -406,10 +409,20 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state) return ret; } +static int pnv_php_get_attention_state(struct hotplug_slot *slot, u8 *state) +{ + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); + + *state = php_slot->attention_state; + return 0; +} + static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state) { + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); + /* FIXME: Make it real once firmware supports it */ - slot->info->attention_status = state; + php_slot->attention_state = state; return 0; } @@ -501,8 +514,7 @@ scan: static int pnv_php_enable_slot(struct hotplug_slot *slot) { - struct pnv_php_slot *php_slot = container_of(slot, - struct pnv_php_slot, slot); + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); return pnv_php_enable(php_slot, true); } @@ -533,6 +545,7 @@ static int pnv_php_disable_slot(struct hotplug_slot *slot) static const struct hotplug_slot_ops php_slot_ops = { .get_power_status = pnv_php_get_power_state, .get_adapter_status = pnv_php_get_adapter_state, + .get_attention_status = pnv_php_get_attention_state, .set_attention_status = pnv_php_set_attention_state, .enable_slot = pnv_php_enable_slot, .disable_slot = pnv_php_disable_slot, @@ -594,7 +607,6 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) php_slot->id = id; php_slot->power_state_check = false; php_slot->slot.ops = &php_slot_ops; - php_slot->slot.info = &php_slot->slot_info; php_slot->slot.private = php_slot; INIT_LIST_HEAD(&php_slot->children); diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h index f83347819f7b..26a3dd731b5e 100644 --- a/drivers/pci/hotplug/rpaphp.h +++ b/drivers/pci/hotplug/rpaphp.h @@ -63,6 +63,7 @@ struct slot { u32 index; u32 type; u32 power_domain; + u8 attention_status; char *name; struct device_node *dn; struct pci_bus *bus; diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index 8620a3f8c987..898e78dcd311 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -66,7 +66,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) rc = rtas_set_indicator(DR_INDICATOR, slot->index, value); if (!rc) - hotplug_slot->info->attention_status = value; + slot->attention_status = value; return rc; } @@ -95,7 +95,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) { struct slot *slot = (struct slot *)hotplug_slot->private; - *value = slot->hotplug_slot->info->attention_status; + *value = slot->attention_status; return 0; } diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c index 0aac33e15dab..beca61badeea 100644 --- a/drivers/pci/hotplug/rpaphp_pci.c +++ b/drivers/pci/hotplug/rpaphp_pci.c @@ -54,25 +54,21 @@ int rpaphp_get_sensor_state(struct slot *slot, int *state) * rpaphp_enable_slot - record slot state, config pci device * @slot: target &slot * - * Initialize values in the slot, and the hotplug_slot info - * structures to indicate if there is a pci card plugged into - * the slot. If the slot is not empty, run the pcibios routine + * Initialize values in the slot structure to indicate if there is a pci card + * plugged into the slot. If the slot is not empty, run the pcibios routine * to get pcibios stuff correctly set up. */ int rpaphp_enable_slot(struct slot *slot) { int rc, level, state; struct pci_bus *bus; - struct hotplug_slot_info *info = slot->hotplug_slot->info; - info->adapter_status = NOT_VALID; slot->state = EMPTY; /* Find out if the power is turned on for the slot */ rc = rtas_get_power_level(slot->power_domain, &level); if (rc) return rc; - info->power_status = level; /* Figure out if there is an adapter in the slot */ rc = rpaphp_get_sensor_state(slot, &state); @@ -85,13 +81,11 @@ int rpaphp_enable_slot(struct slot *slot) return -EINVAL; } - info->adapter_status = EMPTY; slot->bus = bus; slot->pci_devs = &bus->devices; /* if there's an adapter in the slot, go add the pci devices */ if (state == PRESENT) { - info->adapter_status = NOT_CONFIGURED; slot->state = NOT_CONFIGURED; /* non-empty slot has to have child */ @@ -105,7 +99,6 @@ int rpaphp_enable_slot(struct slot *slot) pci_hp_add_devices(bus); if (!list_empty(&bus->devices)) { - info->adapter_status = CONFIGURED; slot->state = CONFIGURED; } diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index b916c8e4372d..6e2658ce300b 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c @@ -21,7 +21,6 @@ /* free up the memory used by a slot */ void dealloc_slot_struct(struct slot *slot) { - kfree(slot->hotplug_slot->info); kfree(slot->name); kfree(slot->hotplug_slot); kfree(slot); @@ -38,13 +37,9 @@ struct slot *alloc_slot_struct(struct device_node *dn, slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); if (!slot->hotplug_slot) goto error_slot; - slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), - GFP_KERNEL); - if (!slot->hotplug_slot->info) - goto error_hpslot; slot->name = kstrdup(drc_name, GFP_KERNEL); if (!slot->name) - goto error_info; + goto error_hpslot; slot->dn = dn; slot->index = drc_index; slot->power_domain = power_domain; @@ -53,8 +48,6 @@ struct slot *alloc_slot_struct(struct device_node *dn, return (slot); -error_info: - kfree(slot->hotplug_slot->info); error_hpslot: kfree(slot->hotplug_slot); error_slot: diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 5bd45fd4a92a..d04634b0defe 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -140,7 +140,6 @@ static const struct hotplug_slot_ops s390_hotplug_slot_ops = { int zpci_init_slot(struct zpci_dev *zdev) { struct hotplug_slot *hotplug_slot; - struct hotplug_slot_info *info; char name[SLOT_NAME_SIZE]; struct slot *slot; int rc; @@ -160,16 +159,8 @@ int zpci_init_slot(struct zpci_dev *zdev) slot->hotplug_slot = hotplug_slot; slot->zdev = zdev; - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - goto error_info; - hotplug_slot->info = info; - hotplug_slot->ops = &s390_hotplug_slot_ops; - get_power_status(hotplug_slot, &info->power_status); - get_adapter_status(hotplug_slot, &info->adapter_status); - snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid); rc = pci_hp_register(slot->hotplug_slot, zdev->bus, ZPCI_DEVFN, name); @@ -180,8 +171,6 @@ int zpci_init_slot(struct zpci_dev *zdev) return 0; error_reg: - kfree(info); -error_info: kfree(hotplug_slot); error_hp: kfree(slot); @@ -199,7 +188,6 @@ void zpci_exit_slot(struct zpci_dev *zdev) continue; list_del(&slot->slot_list); pci_hp_deregister(slot->hotplug_slot); - kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot); kfree(slot); } diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index af4c28c574dd..e103826c83e3 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -585,7 +585,6 @@ static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot, static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot) { - kfree(bss_hotplug_slot->info); kfree(bss_hotplug_slot->private); kfree(bss_hotplug_slot); } @@ -614,14 +613,6 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) goto alloc_err; } - bss_hotplug_slot->info = - kzalloc(sizeof(struct hotplug_slot_info), - GFP_KERNEL); - if (!bss_hotplug_slot->info) { - rc = -ENOMEM; - goto alloc_err; - } - if (sn_hp_slot_private_alloc(bss_hotplug_slot, pci_bus, device, name)) { rc = -ENOMEM; diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 516e4835019c..a7bb816e6f25 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -67,7 +67,9 @@ struct slot { u32 number; u8 is_a_board; u8 state; + u8 attention_save; u8 presence_save; + u8 latch_save; u8 pwr_save; struct controller *ctrl; const struct hpc_ops *hpc_ops; diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 26cbea04237c..b7181b7e7b98 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -65,7 +65,6 @@ static int init_slots(struct controller *ctrl) { struct slot *slot; struct hotplug_slot *hotplug_slot; - struct hotplug_slot_info *info; char name[SLOT_NAME_SIZE]; int retval; int i; @@ -84,13 +83,6 @@ static int init_slots(struct controller *ctrl) } slot->hotplug_slot = hotplug_slot; - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - retval = -ENOMEM; - goto error_hpslot; - } - hotplug_slot->info = info; - slot->hp_slot = i; slot->ctrl = ctrl; slot->bus = ctrl->pci_dev->subordinate->number; @@ -101,7 +93,7 @@ static int init_slots(struct controller *ctrl) slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number); if (!slot->wq) { retval = -ENOMEM; - goto error_info; + goto error_hpslot; } mutex_init(&slot->lock); @@ -124,10 +116,10 @@ static int init_slots(struct controller *ctrl) goto error_slotwq; } - get_power_status(hotplug_slot, &info->power_status); - get_attention_status(hotplug_slot, &info->attention_status); - get_latch_status(hotplug_slot, &info->latch_status); - get_adapter_status(hotplug_slot, &info->adapter_status); + get_power_status(hotplug_slot, &slot->pwr_save); + get_attention_status(hotplug_slot, &slot->attention_save); + get_latch_status(hotplug_slot, &slot->latch_save); + get_adapter_status(hotplug_slot, &slot->presence_save); list_add(&slot->slot_list, &ctrl->slot_list); } @@ -135,8 +127,6 @@ static int init_slots(struct controller *ctrl) return 0; error_slotwq: destroy_workqueue(slot->wq); -error_info: - kfree(info); error_hpslot: kfree(hotplug_slot); error_slot: @@ -154,7 +144,6 @@ void cleanup_slots(struct controller *ctrl) cancel_delayed_work(&slot->work); destroy_workqueue(slot->wq); pci_hp_deregister(slot->hotplug_slot); - kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot); kfree(slot); } @@ -170,7 +159,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - hotplug_slot->info->attention_status = status; + slot->attention_save = status; slot->hpc_ops->set_attention_status(slot, status); return 0; @@ -206,7 +195,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) retval = slot->hpc_ops->get_power_status(slot, value); if (retval < 0) - *value = hotplug_slot->info->power_status; + *value = slot->pwr_save; return 0; } @@ -221,7 +210,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) retval = slot->hpc_ops->get_attention_status(slot, value); if (retval < 0) - *value = hotplug_slot->info->attention_status; + *value = slot->attention_save; return 0; } @@ -236,7 +225,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) retval = slot->hpc_ops->get_latch_status(slot, value); if (retval < 0) - *value = hotplug_slot->info->latch_status; + *value = slot->latch_save; return 0; } @@ -251,7 +240,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) retval = slot->hpc_ops->get_adapter_status(slot, value); if (retval < 0) - *value = hotplug_slot->info->adapter_status; + *value = slot->presence_save; return 0; } diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index 1267dcc5a531..078003dcde5b 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -446,23 +446,12 @@ void shpchp_queue_pushbutton_work(struct work_struct *work) mutex_unlock(&p_slot->lock); } -static int update_slot_info (struct slot *slot) +static void update_slot_info(struct slot *slot) { - struct hotplug_slot_info *info; - int result; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - slot->hpc_ops->get_power_status(slot, &(info->power_status)); - slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); - slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); - slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); - - result = pci_hp_change_slot_info(slot->hotplug_slot, info); - kfree (info); - return result; + slot->hpc_ops->get_power_status(slot, &slot->pwr_save); + slot->hpc_ops->get_attention_status(slot, &slot->attention_save); + slot->hpc_ops->get_latch_status(slot, &slot->latch_save); + slot->hpc_ops->get_adapter_status(slot, &slot->presence_save); } /* diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index a8aa2eadfd82..019b037319e3 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -902,15 +902,8 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus) if (!asus->hotplug_slot) goto error_slot; - asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), - GFP_KERNEL); - if (!asus->hotplug_slot->info) - goto error_info; - asus->hotplug_slot->private = asus; asus->hotplug_slot->ops = &asus_hotplug_slot_ops; - asus_get_adapter_status(asus->hotplug_slot, - &asus->hotplug_slot->info->adapter_status); ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi"); if (ret) { @@ -921,8 +914,6 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus) return 0; error_register: - kfree(asus->hotplug_slot->info); -error_info: kfree(asus->hotplug_slot); asus->hotplug_slot = NULL; error_slot: @@ -1055,7 +1046,6 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus) asus_rfkill_hotplug(asus); if (asus->hotplug_slot) { pci_hp_deregister(asus->hotplug_slot); - kfree(asus->hotplug_slot->info); kfree(asus->hotplug_slot); } if (asus->hotplug_workqueue) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 41a364376e91..028b20f82962 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -745,15 +745,8 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) if (!eeepc->hotplug_slot) goto error_slot; - eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), - GFP_KERNEL); - if (!eeepc->hotplug_slot->info) - goto error_info; - eeepc->hotplug_slot->private = eeepc; eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; - eeepc_get_adapter_status(eeepc->hotplug_slot, - &eeepc->hotplug_slot->info->adapter_status); ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); if (ret) { @@ -764,8 +757,6 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) return 0; error_register: - kfree(eeepc->hotplug_slot->info); -error_info: kfree(eeepc->hotplug_slot); eeepc->hotplug_slot = NULL; error_slot: @@ -831,7 +822,6 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) if (eeepc->hotplug_slot) { pci_hp_deregister(eeepc->hotplug_slot); - kfree(eeepc->hotplug_slot->info); kfree(eeepc->hotplug_slot); } diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 372dbe95c207..6f07a4e1de8d 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -23,17 +23,9 @@ * @hardware_test: Called to run a specified hardware test on the specified * slot. * @get_power_status: Called to get the current power status of a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. * @get_attention_status: Called to get the current attention status of a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. * @get_latch_status: Called to get the current latch status of a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. * @get_adapter_status: Called to get see if an adapter is present in the slot or not. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. * @reset_slot: Optional interface to allow override of a bus reset for the * slot for cases where a secondary bus reset can result in spurious * hotplug events or where a slot can be reset independent of the bus. @@ -55,27 +47,9 @@ struct hotplug_slot_ops { int (*reset_slot) (struct hotplug_slot *slot, int probe); }; -/** - * struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot - * @power_status: if power is enabled or not (1/0) - * @attention_status: if the attention light is enabled or not (1/0) - * @latch_status: if the latch (if any) is open or closed (1/0) - * @adapter_status: if there is a pci board present in the slot or not (1/0) - * - * Used to notify the hotplug pci core of the status of a specific slot. - */ -struct hotplug_slot_info { - u8 power_status; - u8 attention_status; - u8 latch_status; - u8 adapter_status; -}; - /** * struct hotplug_slot - used to register a physical slot with the hotplug pci core * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot - * @info: pointer to the &struct hotplug_slot_info for the initial values for - * this slot. * @private: used by the hotplug pci controller driver to store whatever it * needs. * @owner: The module owner of this structure @@ -83,7 +57,6 @@ struct hotplug_slot_info { */ struct hotplug_slot { const struct hotplug_slot_ops *ops; - struct hotplug_slot_info *info; void *private; /* Variables below this are for use only by the hotplug pci core. */ @@ -110,9 +83,6 @@ void pci_hp_del(struct hotplug_slot *slot); void pci_hp_destroy(struct hotplug_slot *slot); void pci_hp_deregister(struct hotplug_slot *slot); -int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot, - struct hotplug_slot_info *info); - /* use a define to avoid include chaining to get THIS_MODULE & friends */ #define pci_hp_register(slot, pbus, devnr, name) \ __pci_hp_register(slot, pbus, devnr, name, THIS_MODULE, KBUILD_MODNAME) -- cgit v1.2.3 From 125450f814418b9f889c9885831467d1b2e25a7d Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 8 Sep 2018 09:59:01 +0200 Subject: PCI: hotplug: Embed hotplug_slot When the PCI hotplug core and its first user, cpqphp, were introduced in February 2002 with historic commit a8a2069f432c, cpqphp allocated a slot struct for its internal use plus a hotplug_slot struct to be registered with the hotplug core and linked the two with pointers: https://git.kernel.org/tglx/history/c/a8a2069f432c Nowadays, the predominant pattern in the tree is to embed ("subclass") such structures in one another and cast to the containing struct with container_of(). But it wasn't until July 2002 that container_of() was introduced with historic commit ec4f214232cf: https://git.kernel.org/tglx/history/c/ec4f214232cf pnv_php, introduced in 2016, did the right thing and embedded struct hotplug_slot in its internal struct pnv_php_slot, but all other drivers cargo-culted cpqphp's design and linked separate structs with pointers. Embedding structs is preferrable to linking them with pointers because it requires fewer allocations, thereby reducing overhead and simplifying error paths. Casting an embedded struct to the containing struct becomes a cheap subtraction rather than a dereference. And having fewer pointers reduces the risk of them pointing nowhere either accidentally or due to an attack. Convert all drivers to embed struct hotplug_slot in their internal slot struct. The "private" pointer in struct hotplug_slot thereby becomes unused, so drop it. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Acked-by: Tyrel Datwyler # drivers/pci/hotplug/rpa* Acked-by: Sebastian Ott # drivers/pci/hotplug/s390* Acked-by: Andy Shevchenko # drivers/platform/x86 Cc: Len Brown Cc: Scott Murray Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: Oliver OHalloran Cc: Gavin Shan Cc: Gerald Schaefer Cc: Corentin Chary Cc: Darren Hart --- drivers/pci/hotplug/acpiphp.h | 9 +++- drivers/pci/hotplug/acpiphp_core.c | 28 ++++------ drivers/pci/hotplug/acpiphp_ibm.c | 2 +- drivers/pci/hotplug/cpci_hotplug.h | 9 +++- drivers/pci/hotplug/cpci_hotplug_core.c | 37 +++++-------- drivers/pci/hotplug/cpci_hotplug_pci.c | 6 +-- drivers/pci/hotplug/cpqphp.h | 9 +++- drivers/pci/hotplug/cpqphp_core.c | 37 +++++-------- drivers/pci/hotplug/cpqphp_ctrl.c | 2 - drivers/pci/hotplug/ibmphp.h | 7 ++- drivers/pci/hotplug/ibmphp_core.c | 92 ++++++++++++++------------------- drivers/pci/hotplug/ibmphp_ebda.c | 37 ++++--------- drivers/pci/hotplug/pciehp.h | 11 ++-- drivers/pci/hotplug/pciehp_core.c | 37 +++++-------- drivers/pci/hotplug/pciehp_ctrl.c | 4 +- drivers/pci/hotplug/pciehp_hpc.c | 8 +-- drivers/pci/hotplug/pnv_php.c | 9 ++-- drivers/pci/hotplug/rpaphp.h | 7 ++- drivers/pci/hotplug/rpaphp_core.c | 14 ++--- drivers/pci/hotplug/rpaphp_slot.c | 15 ++---- drivers/pci/hotplug/s390_pci_hpc.c | 30 +++++------ drivers/pci/hotplug/sgi_hotplug.c | 52 ++++++++----------- drivers/pci/hotplug/shpchp.h | 6 +-- drivers/pci/hotplug/shpchp_core.c | 17 ++---- drivers/platform/x86/asus-wmi.c | 26 ++++------ drivers/platform/x86/eeepc-laptop.c | 30 +++++------ include/linux/pci_hotplug.h | 3 -- 27 files changed, 223 insertions(+), 321 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 8377e736ea69..cf3058404f41 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -33,14 +33,19 @@ struct acpiphp_slot; * struct slot - slot information for each *physical* slot */ struct slot { - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct acpiphp_slot *acpi_slot; unsigned int sun; /* ACPI _SUN (Slot User Number) value */ }; static inline const char *slot_name(struct slot *slot) { - return hotplug_slot_name(slot->hotplug_slot); + return hotplug_slot_name(&slot->hotplug_slot); +} + +static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot) +{ + return container_of(hotplug_slot, struct slot, hotplug_slot); } /* diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index abd4f8d7e16a..c9e2bd40c038 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(acpiphp_unregister_attention); */ static int enable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -135,7 +135,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) */ static int disable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -179,7 +179,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) */ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -225,7 +225,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) */ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -245,7 +245,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) */ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -266,12 +266,7 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, if (!slot) goto error; - slot->hotplug_slot = kzalloc(sizeof(*slot->hotplug_slot), GFP_KERNEL); - if (!slot->hotplug_slot) - goto error_slot; - - slot->hotplug_slot->private = slot; - slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; + slot->hotplug_slot.ops = &acpi_hotplug_slot_ops; slot->acpi_slot = acpiphp_slot; @@ -279,20 +274,18 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, slot->sun = sun; snprintf(name, SLOT_NAME_SIZE, "%u", sun); - retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus, + retval = pci_hp_register(&slot->hotplug_slot, acpiphp_slot->bus, acpiphp_slot->device, name); if (retval == -EBUSY) - goto error_hpslot; + goto error_slot; if (retval) { pr_err("pci_hp_register failed with error %d\n", retval); - goto error_hpslot; + goto error_slot; } pr_info("Slot [%s] registered\n", slot_name(slot)); return 0; -error_hpslot: - kfree(slot->hotplug_slot); error_slot: kfree(slot); error: @@ -306,8 +299,7 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) pr_info("Slot [%s] unregistered\n", slot_name(slot)); - pci_hp_deregister(slot->hotplug_slot); - kfree(slot->hotplug_slot); + pci_hp_deregister(&slot->hotplug_slot); kfree(slot); } diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 41713f16ff97..df48b3b03ab4 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -41,7 +41,7 @@ MODULE_VERSION(DRIVER_VERSION); #define IBM_HARDWARE_ID1 "IBM37D0" #define IBM_HARDWARE_ID2 "IBM37D4" -#define hpslot_to_sun(A) (((struct slot *)((A)->private))->sun) +#define hpslot_to_sun(A) (to_slot(A)->sun) /* union apci_descriptor - allows access to the * various device descriptors that are embedded in the diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h index a35f40a2290c..f33ff2bca414 100644 --- a/drivers/pci/hotplug/cpci_hotplug.h +++ b/drivers/pci/hotplug/cpci_hotplug.h @@ -35,7 +35,7 @@ struct slot { unsigned int latch_status:1; unsigned int adapter_status:1; unsigned int extracting; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct list_head slot_list; }; @@ -60,7 +60,12 @@ struct cpci_hp_controller { static inline const char *slot_name(struct slot *slot) { - return hotplug_slot_name(slot->hotplug_slot); + return hotplug_slot_name(&slot->hotplug_slot); +} + +static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot) +{ + return container_of(hotplug_slot, struct slot, hotplug_slot); } int cpci_hp_register_controller(struct cpci_hp_controller *controller); diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index a17fb24c28cd..603eadf3d965 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -70,7 +70,7 @@ static const struct hotplug_slot_ops cpci_hotplug_slot_ops = { static int enable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); int retval = 0; dbg("%s - physical_slot = %s", __func__, slot_name(slot)); @@ -83,7 +83,7 @@ enable_slot(struct hotplug_slot *hotplug_slot) static int disable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); int retval = 0; dbg("%s - physical_slot = %s", __func__, slot_name(slot)); @@ -139,7 +139,7 @@ cpci_get_power_status(struct slot *slot) static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); *value = cpci_get_power_status(slot); return 0; @@ -148,7 +148,7 @@ get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); *value = cpci_get_attention_status(slot); return 0; @@ -157,13 +157,13 @@ get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) { - return cpci_set_attention_status(hotplug_slot->private, status); + return cpci_set_attention_status(to_slot(hotplug_slot), status); } static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); *value = slot->adapter_status; return 0; @@ -172,7 +172,7 @@ get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); *value = slot->latch_status; return 0; @@ -180,7 +180,6 @@ get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) static void release_slot(struct slot *slot) { - kfree(slot->hotplug_slot); pci_dev_put(slot->dev); kfree(slot); } @@ -191,7 +190,6 @@ int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) { struct slot *slot; - struct hotplug_slot *hotplug_slot; char name[SLOT_NAME_SIZE]; int status; int i; @@ -210,28 +208,19 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) goto error; } - hotplug_slot = - kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!hotplug_slot) { - status = -ENOMEM; - goto error_slot; - } - slot->hotplug_slot = hotplug_slot; - slot->bus = bus; slot->number = i; slot->devfn = PCI_DEVFN(i, 0); snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i); - hotplug_slot->private = slot; - hotplug_slot->ops = &cpci_hotplug_slot_ops; + slot->hotplug_slot.ops = &cpci_hotplug_slot_ops; dbg("registering slot %s", name); - status = pci_hp_register(slot->hotplug_slot, bus, i, name); + status = pci_hp_register(&slot->hotplug_slot, bus, i, name); if (status) { err("pci_hp_register failed with error %d", status); - goto error_hpslot; + goto error_slot; } dbg("slot registered with name: %s", slot_name(slot)); @@ -242,8 +231,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) up_write(&list_rwsem); } return 0; -error_hpslot: - kfree(hotplug_slot); error_slot: kfree(slot); error: @@ -269,7 +256,7 @@ cpci_hp_unregister_bus(struct pci_bus *bus) slots--; dbg("deregistering slot %s", slot_name(slot)); - pci_hp_deregister(slot->hotplug_slot); + pci_hp_deregister(&slot->hotplug_slot); release_slot(slot); } } @@ -571,7 +558,7 @@ cleanup_slots(void) goto cleanup_null; list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) { list_del(&slot->slot_list); - pci_hp_deregister(slot->hotplug_slot); + pci_hp_deregister(&slot->hotplug_slot); release_slot(slot); } cleanup_null: diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 389b8fb50cd9..2c16adb7f4ec 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -194,8 +194,7 @@ int cpci_led_on(struct slot *slot) slot->devfn, hs_cap + 2, hs_csr)) { - err("Could not set LOO for slot %s", - hotplug_slot_name(slot->hotplug_slot)); + err("Could not set LOO for slot %s", slot_name(slot)); return -ENODEV; } } @@ -223,8 +222,7 @@ int cpci_led_off(struct slot *slot) slot->devfn, hs_cap + 2, hs_csr)) { - err("Could not clear LOO for slot %s", - hotplug_slot_name(slot->hotplug_slot)); + err("Could not clear LOO for slot %s", slot_name(slot)); return -ENODEV; } } diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h index db78b394a075..77e4e0142fbc 100644 --- a/drivers/pci/hotplug/cpqphp.h +++ b/drivers/pci/hotplug/cpqphp.h @@ -260,7 +260,7 @@ struct slot { u8 hp_slot; struct controller *ctrl; void __iomem *p_sm_slot; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; }; struct pci_resource { @@ -445,7 +445,12 @@ extern u8 cpqhp_disk_irq; static inline const char *slot_name(struct slot *slot) { - return hotplug_slot_name(slot->hotplug_slot); + return hotplug_slot_name(&slot->hotplug_slot); +} + +static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot) +{ + return container_of(hotplug_slot, struct slot, hotplug_slot); } /* diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index bb354a7fc112..95b7d60cf119 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -275,8 +275,7 @@ static int ctrl_slot_cleanup(struct controller *ctrl) while (old_slot) { next_slot = old_slot->next; - pci_hp_deregister(old_slot->hotplug_slot); - kfree(old_slot->hotplug_slot); + pci_hp_deregister(&old_slot->hotplug_slot); kfree(old_slot); old_slot = next_slot; } @@ -418,7 +417,7 @@ cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func, static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) { struct pci_func *slot_func; - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct controller *ctrl = slot->ctrl; u8 bus; u8 devfn; @@ -445,7 +444,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) static int process_SI(struct hotplug_slot *hotplug_slot) { struct pci_func *slot_func; - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct controller *ctrl = slot->ctrl; u8 bus; u8 devfn; @@ -477,7 +476,7 @@ static int process_SI(struct hotplug_slot *hotplug_slot) static int process_SS(struct hotplug_slot *hotplug_slot) { struct pci_func *slot_func; - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct controller *ctrl = slot->ctrl; u8 bus; u8 devfn; @@ -504,7 +503,7 @@ static int process_SS(struct hotplug_slot *hotplug_slot) static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct controller *ctrl = slot->ctrl; dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -515,7 +514,7 @@ static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value) static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct controller *ctrl = slot->ctrl; dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -526,7 +525,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct controller *ctrl = slot->ctrl; dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -537,7 +536,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct controller *ctrl = slot->ctrl; dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -549,7 +548,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct controller *ctrl = slot->ctrl; dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); @@ -577,7 +576,6 @@ static int ctrl_slot_setup(struct controller *ctrl, void __iomem *smbios_table) { struct slot *slot; - struct hotplug_slot *hotplug_slot; struct pci_bus *bus = ctrl->pci_bus; u8 number_of_slots; u8 slot_device; @@ -603,14 +601,6 @@ static int ctrl_slot_setup(struct controller *ctrl, goto error; } - slot->hotplug_slot = kzalloc(sizeof(*(slot->hotplug_slot)), - GFP_KERNEL); - if (!slot->hotplug_slot) { - result = -ENOMEM; - goto error_slot; - } - hotplug_slot = slot->hotplug_slot; - slot->ctrl = ctrl; slot->bus = ctrl->bus; slot->device = slot_device; @@ -659,21 +649,20 @@ static int ctrl_slot_setup(struct controller *ctrl, ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; /* register this slot with the hotplug pci core */ - hotplug_slot->private = slot; snprintf(name, SLOT_NAME_SIZE, "%u", slot->number); - hotplug_slot->ops = &cpqphp_hotplug_slot_ops; + slot->hotplug_slot.ops = &cpqphp_hotplug_slot_ops; dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n", slot->bus, slot->device, slot->number, ctrl->slot_device_offset, slot_number); - result = pci_hp_register(hotplug_slot, + result = pci_hp_register(&slot->hotplug_slot, ctrl->pci_dev->bus, slot->device, name); if (result) { err("pci_hp_register failed with error %d\n", result); - goto error_hpslot; + goto error_slot; } slot->next = ctrl->slot; @@ -685,8 +674,6 @@ static int ctrl_slot_setup(struct controller *ctrl, } return 0; -error_hpslot: - kfree(hotplug_slot); error_slot: kfree(slot); error: diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index 9c4826ac6a4f..b7f4e1f099d9 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -1130,8 +1130,6 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ for (slot = ctrl->slot; slot; slot = slot->next) { if (slot->device == (hp_slot + ctrl->slot_device_offset)) continue; - if (!slot->hotplug_slot) - continue; if (get_presence_status(ctrl, slot) == 0) continue; /* If another adapter is running on the same segment but at a diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h index db387e10581e..b89f850c3a4e 100644 --- a/drivers/pci/hotplug/ibmphp.h +++ b/drivers/pci/hotplug/ibmphp.h @@ -698,7 +698,7 @@ struct slot { u8 supported_bus_mode; u8 flag; /* this is for disable slot and polling */ u8 ctlr_index; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct controller *ctrl; struct pci_func *func; u8 irq[4]; @@ -742,5 +742,10 @@ int ibmphp_configure_card(struct pci_func *, u8); int ibmphp_unconfigure_card(struct slot **, int); extern const struct hotplug_slot_ops ibmphp_hotplug_slot_ops; +static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot) +{ + return container_of(hotplug_slot, struct slot, hotplug_slot); +} + #endif //__IBMPHP_H diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 96e5b1f544ac..08a58e911fc2 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -247,11 +247,8 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) break; } if (rc == 0) { - pslot = hotplug_slot->private; - if (pslot) - rc = ibmphp_hpc_writeslot(pslot, cmd); - else - rc = -ENODEV; + pslot = to_slot(hotplug_slot); + rc = ibmphp_hpc_writeslot(pslot, cmd); } } else rc = -ENODEV; @@ -273,19 +270,15 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) ibmphp_lock_operations(); if (hotplug_slot) { - pslot = hotplug_slot->private; - if (pslot) { - memcpy(&myslot, pslot, sizeof(struct slot)); - rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, - &(myslot.status)); - if (!rc) - rc = ibmphp_hpc_readslot(pslot, - READ_EXTSLOTSTATUS, - &(myslot.ext_status)); - if (!rc) - *value = SLOT_ATTN(myslot.status, - myslot.ext_status); - } + pslot = to_slot(hotplug_slot); + memcpy(&myslot, pslot, sizeof(struct slot)); + rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, + &myslot.status); + if (!rc) + rc = ibmphp_hpc_readslot(pslot, READ_EXTSLOTSTATUS, + &myslot.ext_status); + if (!rc) + *value = SLOT_ATTN(myslot.status, myslot.ext_status); } ibmphp_unlock_operations(); @@ -303,14 +296,12 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) (ulong) hotplug_slot, (ulong) value); ibmphp_lock_operations(); if (hotplug_slot) { - pslot = hotplug_slot->private; - if (pslot) { - memcpy(&myslot, pslot, sizeof(struct slot)); - rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, - &(myslot.status)); - if (!rc) - *value = SLOT_LATCH(myslot.status); - } + pslot = to_slot(hotplug_slot); + memcpy(&myslot, pslot, sizeof(struct slot)); + rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, + &myslot.status); + if (!rc) + *value = SLOT_LATCH(myslot.status); } ibmphp_unlock_operations(); @@ -330,14 +321,12 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) (ulong) hotplug_slot, (ulong) value); ibmphp_lock_operations(); if (hotplug_slot) { - pslot = hotplug_slot->private; - if (pslot) { - memcpy(&myslot, pslot, sizeof(struct slot)); - rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, - &(myslot.status)); - if (!rc) - *value = SLOT_PWRGD(myslot.status); - } + pslot = to_slot(hotplug_slot); + memcpy(&myslot, pslot, sizeof(struct slot)); + rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, + &myslot.status); + if (!rc) + *value = SLOT_PWRGD(myslot.status); } ibmphp_unlock_operations(); @@ -357,18 +346,16 @@ static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value) (ulong) hotplug_slot, (ulong) value); ibmphp_lock_operations(); if (hotplug_slot) { - pslot = hotplug_slot->private; - if (pslot) { - memcpy(&myslot, pslot, sizeof(struct slot)); - rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, - &(myslot.status)); - if (!rc) { - present = SLOT_PRESENT(myslot.status); - if (present == HPC_SLOT_EMPTY) - *value = 0; - else - *value = 1; - } + pslot = to_slot(hotplug_slot); + memcpy(&myslot, pslot, sizeof(struct slot)); + rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, + &myslot.status); + if (!rc) { + present = SLOT_PRESENT(myslot.status); + if (present == HPC_SLOT_EMPTY) + *value = 0; + else + *value = 1; } } @@ -382,7 +369,7 @@ static int get_max_bus_speed(struct slot *slot) int rc = 0; u8 mode = 0; enum pci_bus_speed speed; - struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus; + struct pci_bus *bus = slot->hotplug_slot.pci_slot->bus; debug("%s - Entry slot[%p]\n", __func__, slot); @@ -582,7 +569,7 @@ static int validate(struct slot *slot_cur, int opn) ****************************************************************************/ int ibmphp_update_slot_info(struct slot *slot_cur) { - struct pci_bus *bus = slot_cur->hotplug_slot->pci_slot->bus; + struct pci_bus *bus = slot_cur->hotplug_slot.pci_slot->bus; u8 bus_speed; u8 mode; @@ -652,7 +639,7 @@ static void free_slots(void) list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head, ibm_slot_list) { - pci_hp_del(slot_cur->hotplug_slot); + pci_hp_del(&slot_cur->hotplug_slot); slot_cur->ctrl = NULL; slot_cur->bus_on = NULL; @@ -662,8 +649,7 @@ static void free_slots(void) */ ibmphp_unconfigure_card(&slot_cur, -1); - pci_hp_destroy(slot_cur->hotplug_slot); - kfree(slot_cur->hotplug_slot); + pci_hp_destroy(&slot_cur->hotplug_slot); kfree(slot_cur); } debug("%s -- exit\n", __func__); @@ -985,7 +971,7 @@ static int enable_slot(struct hotplug_slot *hs) ibmphp_lock_operations(); debug("ENABLING SLOT........\n"); - slot_cur = hs->private; + slot_cur = to_slot(hs); rc = validate(slot_cur, ENABLE); if (rc) { @@ -1146,7 +1132,7 @@ error_power: **************************************************************/ static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); int rc; ibmphp_lock_operations(); diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index c05d066ab0d5..11a2661dc062 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c @@ -666,10 +666,7 @@ static int fillslotinfo(struct hotplug_slot *hotplug_slot) struct slot *slot; int rc = 0; - if (!hotplug_slot || !hotplug_slot->private) - return -EINVAL; - - slot = hotplug_slot->private; + slot = to_slot(hotplug_slot); rc = ibmphp_hpc_readslot(slot, READ_ALLSTAT, NULL); return rc; } @@ -687,7 +684,6 @@ static int __init ebda_rsrc_controller(void) u8 ctlr_id, temp, bus_index; u16 ctlr, slot, bus; u16 slot_num, bus_num, index; - struct hotplug_slot *hp_slot_ptr; struct controller *hpc_ptr; struct ebda_hpc_bus *bus_ptr; struct ebda_hpc_slot *slot_ptr; @@ -746,7 +742,7 @@ static int __init ebda_rsrc_controller(void) bus_info_ptr1 = kzalloc(sizeof(struct bus_info), GFP_KERNEL); if (!bus_info_ptr1) { rc = -ENOMEM; - goto error_no_hp_slot; + goto error_no_slot; } bus_info_ptr1->slot_min = slot_ptr->slot_num; bus_info_ptr1->slot_max = slot_ptr->slot_num; @@ -817,7 +813,7 @@ static int __init ebda_rsrc_controller(void) (hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1), "ibmphp")) { rc = -ENODEV; - goto error_no_hp_slot; + goto error_no_slot; } hpc_ptr->irq = readb(io_mem + addr + 4); addr += 5; @@ -832,7 +828,7 @@ static int __init ebda_rsrc_controller(void) break; default: rc = -ENODEV; - goto error_no_hp_slot; + goto error_no_slot; } //reorganize chassis' linked list @@ -845,13 +841,6 @@ static int __init ebda_rsrc_controller(void) // register slots with hpc core as well as create linked list of ibm slot for (index = 0; index < hpc_ptr->slot_count; index++) { - - hp_slot_ptr = kzalloc(sizeof(*hp_slot_ptr), GFP_KERNEL); - if (!hp_slot_ptr) { - rc = -ENOMEM; - goto error_no_hp_slot; - } - tmp_slot = kzalloc(sizeof(*tmp_slot), GFP_KERNEL); if (!tmp_slot) { rc = -ENOMEM; @@ -878,7 +867,6 @@ static int __init ebda_rsrc_controller(void) bus_info_ptr1 = ibmphp_find_same_bus_num(hpc_ptr->slots[index].slot_bus_num); if (!bus_info_ptr1) { - kfree(tmp_slot); rc = -ENODEV; goto error; } @@ -888,22 +876,19 @@ static int __init ebda_rsrc_controller(void) tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index; tmp_slot->number = hpc_ptr->slots[index].slot_num; - tmp_slot->hotplug_slot = hp_slot_ptr; - - hp_slot_ptr->private = tmp_slot; - rc = fillslotinfo(hp_slot_ptr); + rc = fillslotinfo(&tmp_slot->hotplug_slot); if (rc) goto error; - rc = ibmphp_init_devno((struct slot **) &hp_slot_ptr->private); + rc = ibmphp_init_devno(&tmp_slot); if (rc) goto error; - hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops; + tmp_slot->hotplug_slot.ops = &ibmphp_hotplug_slot_ops; // end of registering ibm slot with hotplug core - list_add(&((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head); + list_add(&tmp_slot->ibm_slot_list, &ibmphp_slot_head); } print_bus_info(); @@ -913,7 +898,7 @@ static int __init ebda_rsrc_controller(void) list_for_each_entry(tmp_slot, &ibmphp_slot_head, ibm_slot_list) { snprintf(name, SLOT_NAME_SIZE, "%s", create_file_name(tmp_slot)); - pci_hp_register(tmp_slot->hotplug_slot, + pci_hp_register(&tmp_slot->hotplug_slot, pci_find_bus(0, tmp_slot->bus), tmp_slot->device, name); } @@ -922,10 +907,8 @@ static int __init ebda_rsrc_controller(void) return 0; error: - kfree(hp_slot_ptr->private); + kfree(tmp_slot); error_no_slot: - kfree(hp_slot_ptr); -error_no_hp_slot: free_ebda_hpc(hpc_ptr); error_no_hpc: iounmap(io_mem); diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 3cc88f3e4368..3740f1a759c5 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -88,7 +88,7 @@ do { \ * protects scheduling, execution and cancellation of @button_work * @button_work: work item to turn the slot on or off after 5 seconds * in response to an Attention Button press - * @hotplug_slot: pointer to the structure registered with the PCI hotplug core + * @hotplug_slot: structure registered with the PCI hotplug core * @reset_lock: prevents access to the Data Link Layer Link Active bit in the * Link Status register and to the Presence Detect State bit in the Slot * Status register during a slot reset which may cause them to flap @@ -120,7 +120,7 @@ struct controller { struct mutex state_lock; struct delayed_work button_work; - struct hotplug_slot *hotplug_slot; /* hotplug core interface */ + struct hotplug_slot hotplug_slot; /* hotplug core interface */ struct rw_semaphore reset_lock; int request_result; wait_queue_head_t requester; @@ -207,7 +207,12 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status); static inline const char *slot_name(struct controller *ctrl) { - return hotplug_slot_name(ctrl->hotplug_slot); + return hotplug_slot_name(&ctrl->hotplug_slot); +} + +static inline struct controller *to_ctrl(struct hotplug_slot *hotplug_slot) +{ + return container_of(hotplug_slot, struct controller, hotplug_slot); } #endif /* _PCIEHP_H */ diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index ac5baf887c5d..68b20e667764 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -51,19 +51,14 @@ static int get_adapter_status(struct hotplug_slot *slot, u8 *value); static int init_slot(struct controller *ctrl) { - struct hotplug_slot *hotplug = NULL; - struct hotplug_slot_ops *ops = NULL; + struct hotplug_slot_ops *ops; char name[SLOT_NAME_SIZE]; - int retval = -ENOMEM; - - hotplug = kzalloc(sizeof(*hotplug), GFP_KERNEL); - if (!hotplug) - goto out; + int retval; /* Setup hotplug slot ops */ ops = kzalloc(sizeof(*ops), GFP_KERNEL); if (!ops) - goto out; + return -ENOMEM; ops->enable_slot = pciehp_sysfs_enable_slot; ops->disable_slot = pciehp_sysfs_disable_slot; @@ -81,30 +76,24 @@ static int init_slot(struct controller *ctrl) } /* register this slot with the hotplug pci core */ - hotplug->private = ctrl; - hotplug->ops = ops; - ctrl->hotplug_slot = hotplug; + ctrl->hotplug_slot.ops = ops; snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); - retval = pci_hp_initialize(hotplug, + retval = pci_hp_initialize(&ctrl->hotplug_slot, ctrl->pcie->port->subordinate, 0, name); - if (retval) - ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval); -out: if (retval) { + ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval); kfree(ops); - kfree(hotplug); } return retval; } static void cleanup_slot(struct controller *ctrl) { - struct hotplug_slot *hotplug_slot = ctrl->hotplug_slot; + struct hotplug_slot *hotplug_slot = &ctrl->hotplug_slot; pci_hp_destroy(hotplug_slot); kfree(hotplug_slot->ops); - kfree(hotplug_slot); } /* @@ -112,7 +101,7 @@ static void cleanup_slot(struct controller *ctrl) */ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); struct pci_dev *pdev = ctrl->pcie->port; pci_config_pm_runtime_get(pdev); @@ -123,7 +112,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); struct pci_dev *pdev = ctrl->pcie->port; pci_config_pm_runtime_get(pdev); @@ -134,7 +123,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); struct pci_dev *pdev = ctrl->pcie->port; pci_config_pm_runtime_get(pdev); @@ -145,7 +134,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); struct pci_dev *pdev = ctrl->pcie->port; pci_config_pm_runtime_get(pdev); @@ -223,7 +212,7 @@ static int pciehp_probe(struct pcie_device *dev) } /* Publish to user space */ - rc = pci_hp_add(ctrl->hotplug_slot); + rc = pci_hp_add(&ctrl->hotplug_slot); if (rc) { ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc); goto err_out_shutdown_notification; @@ -246,7 +235,7 @@ static void pciehp_remove(struct pcie_device *dev) { struct controller *ctrl = get_service_data(dev); - pci_hp_del(ctrl->hotplug_slot); + pci_hp_del(&ctrl->hotplug_slot); pcie_shutdown_notification(ctrl); cleanup_slot(ctrl); pciehp_release_ctrl(ctrl); diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 04f7ad9fffe1..3f3df4c29f6e 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -348,7 +348,7 @@ static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal) int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); mutex_lock(&ctrl->state_lock); switch (ctrl->state) { @@ -386,7 +386,7 @@ int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); mutex_lock(&ctrl->state_lock); switch (ctrl->state) { diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 0289a3ae4d90..7b5f9db60d9a 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -315,7 +315,7 @@ static int pciehp_link_enable(struct controller *ctrl) int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, u8 *status) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; @@ -328,7 +328,7 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; @@ -422,7 +422,7 @@ int pciehp_query_power_fault(struct controller *ctrl) int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, u8 status) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); struct pci_dev *pdev = ctrl_dev(ctrl); pci_config_pm_runtime_get(pdev); @@ -758,7 +758,7 @@ void pcie_clear_hotplug_events(struct controller *ctrl) */ int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe) { - struct controller *ctrl = hotplug_slot->private; + struct controller *ctrl = to_ctrl(hotplug_slot); struct pci_dev *pdev = ctrl_dev(ctrl); u16 stat_mask = 0, ctrl_mask = 0; int rc; diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index 5bb63430262e..5070620a4f9f 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -336,7 +336,7 @@ static inline struct pnv_php_slot *to_pnv_php_slot(struct hotplug_slot *slot) int pnv_php_set_slot_power_state(struct hotplug_slot *slot, uint8_t state) { - struct pnv_php_slot *php_slot = slot->private; + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); struct opal_msg msg; int ret; @@ -368,7 +368,7 @@ EXPORT_SYMBOL_GPL(pnv_php_set_slot_power_state); static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state) { - struct pnv_php_slot *php_slot = slot->private; + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); uint8_t power_state = OPAL_PCI_SLOT_POWER_ON; int ret; @@ -390,7 +390,7 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state) static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state) { - struct pnv_php_slot *php_slot = slot->private; + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); uint8_t presence = OPAL_PCI_SLOT_EMPTY; int ret; @@ -521,7 +521,7 @@ static int pnv_php_enable_slot(struct hotplug_slot *slot) static int pnv_php_disable_slot(struct hotplug_slot *slot) { - struct pnv_php_slot *php_slot = slot->private; + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); int ret; if (php_slot->state != PNV_PHP_STATE_POPULATED) @@ -607,7 +607,6 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) php_slot->id = id; php_slot->power_state_check = false; php_slot->slot.ops = &php_slot_ops; - php_slot->slot.private = php_slot; INIT_LIST_HEAD(&php_slot->children); INIT_LIST_HEAD(&php_slot->link); diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h index 26a3dd731b5e..bdc954d70869 100644 --- a/drivers/pci/hotplug/rpaphp.h +++ b/drivers/pci/hotplug/rpaphp.h @@ -68,12 +68,17 @@ struct slot { struct device_node *dn; struct pci_bus *bus; struct list_head *pci_devs; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; }; extern const struct hotplug_slot_ops rpaphp_hotplug_slot_ops; extern struct list_head rpaphp_slot_head; +static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot) +{ + return container_of(hotplug_slot, struct slot, hotplug_slot); +} + /* function prototypes */ /* rpaphp_pci.c */ diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index 898e78dcd311..bcd5d357ca23 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -52,7 +52,7 @@ module_param_named(debug, rpaphp_debug, bool, 0644); static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) { int rc; - struct slot *slot = (struct slot *)hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); switch (value) { case 0: @@ -79,7 +79,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { int retval, level; - struct slot *slot = (struct slot *)hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); retval = rtas_get_power_level(slot->power_domain, &level); if (!retval) @@ -94,14 +94,14 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) */ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = (struct slot *)hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); *value = slot->attention_status; return 0; } static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = (struct slot *)hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); int rc, state; rc = rpaphp_get_sensor_state(slot, &state); @@ -409,7 +409,7 @@ static void __exit cleanup_slots(void) list_for_each_entry_safe(slot, next, &rpaphp_slot_head, rpaphp_slot_list) { list_del(&slot->rpaphp_slot_list); - pci_hp_deregister(slot->hotplug_slot); + pci_hp_deregister(&slot->hotplug_slot); dealloc_slot_struct(slot); } return; @@ -434,7 +434,7 @@ static void __exit rpaphp_exit(void) static int enable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = (struct slot *)hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); int state; int retval; @@ -464,7 +464,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) static int disable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = (struct slot *)hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); if (slot->state == NOT_CONFIGURED) return -EINVAL; diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index 6e2658ce300b..5282aa3e33c5 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c @@ -22,7 +22,6 @@ void dealloc_slot_struct(struct slot *slot) { kfree(slot->name); - kfree(slot->hotplug_slot); kfree(slot); } @@ -34,22 +33,16 @@ struct slot *alloc_slot_struct(struct device_node *dn, slot = kzalloc(sizeof(struct slot), GFP_KERNEL); if (!slot) goto error_nomem; - slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!slot->hotplug_slot) - goto error_slot; slot->name = kstrdup(drc_name, GFP_KERNEL); if (!slot->name) - goto error_hpslot; + goto error_slot; slot->dn = dn; slot->index = drc_index; slot->power_domain = power_domain; - slot->hotplug_slot->private = slot; - slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops; + slot->hotplug_slot.ops = &rpaphp_hotplug_slot_ops; return (slot); -error_hpslot: - kfree(slot->hotplug_slot); error_slot: kfree(slot); error_nomem: @@ -70,7 +63,7 @@ static int is_registered(struct slot *slot) int rpaphp_deregister_slot(struct slot *slot) { int retval = 0; - struct hotplug_slot *php_slot = slot->hotplug_slot; + struct hotplug_slot *php_slot = &slot->hotplug_slot; dbg("%s - Entry: deregistering slot=%s\n", __func__, slot->name); @@ -86,7 +79,7 @@ EXPORT_SYMBOL_GPL(rpaphp_deregister_slot); int rpaphp_register_slot(struct slot *slot) { - struct hotplug_slot *php_slot = slot->hotplug_slot; + struct hotplug_slot *php_slot = &slot->hotplug_slot; struct device_node *child; u32 my_index; int retval; diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index d04634b0defe..30ee72268790 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -32,10 +32,15 @@ static int zpci_fn_configured(enum zpci_state state) */ struct slot { struct list_head slot_list; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct zpci_dev *zdev; }; +static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot) +{ + return container_of(hotplug_slot, struct slot, hotplug_slot); +} + static inline int slot_configure(struct slot *slot) { int ret = sclp_pci_configure(slot->zdev->fid); @@ -60,7 +65,7 @@ static inline int slot_deconfigure(struct slot *slot) static int enable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); int rc; if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) @@ -88,7 +93,7 @@ out_deconfigure: static int disable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); struct pci_dev *pdev; int rc; @@ -110,7 +115,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct slot *slot = to_slot(hotplug_slot); switch (slot->zdev->state) { case ZPCI_FN_STATE_STANDBY: @@ -139,7 +144,6 @@ static const struct hotplug_slot_ops s390_hotplug_slot_ops = { int zpci_init_slot(struct zpci_dev *zdev) { - struct hotplug_slot *hotplug_slot; char name[SLOT_NAME_SIZE]; struct slot *slot; int rc; @@ -151,18 +155,11 @@ int zpci_init_slot(struct zpci_dev *zdev) if (!slot) goto error; - hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); - if (!hotplug_slot) - goto error_hp; - hotplug_slot->private = slot; - - slot->hotplug_slot = hotplug_slot; slot->zdev = zdev; - - hotplug_slot->ops = &s390_hotplug_slot_ops; + slot->hotplug_slot.ops = &s390_hotplug_slot_ops; snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid); - rc = pci_hp_register(slot->hotplug_slot, zdev->bus, + rc = pci_hp_register(&slot->hotplug_slot, zdev->bus, ZPCI_DEVFN, name); if (rc) goto error_reg; @@ -171,8 +168,6 @@ int zpci_init_slot(struct zpci_dev *zdev) return 0; error_reg: - kfree(hotplug_slot); -error_hp: kfree(slot); error: return -ENOMEM; @@ -187,8 +182,7 @@ void zpci_exit_slot(struct zpci_dev *zdev) if (slot->zdev != zdev) continue; list_del(&slot->slot_list); - pci_hp_deregister(slot->hotplug_slot); - kfree(slot->hotplug_slot); + pci_hp_deregister(&slot->hotplug_slot); kfree(slot); } } diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index e103826c83e3..231f5bdd3d2d 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -56,7 +56,7 @@ struct slot { int device_num; struct pci_bus *pci_bus; /* this struct for glue internal only */ - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct list_head hp_list; char physical_path[SN_SLOT_NAME_SIZE]; }; @@ -88,10 +88,15 @@ static const struct hotplug_slot_ops sn_hotplug_slot_ops = { static DEFINE_MUTEX(sn_hotplug_mutex); +static struct slot *to_slot(struct hotplug_slot *bss_hotplug_slot) +{ + return container_of(bss_hotplug_slot, struct slot, hotplug_slot); +} + static ssize_t path_show(struct pci_slot *pci_slot, char *buf) { int retval = -ENOENT; - struct slot *slot = pci_slot->hotplug->private; + struct slot *slot = to_slot(pci_slot->hotplug); if (!slot) return retval; @@ -156,7 +161,7 @@ static int sn_pci_bus_valid(struct pci_bus *pci_bus) return -EIO; } -static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, +static int sn_hp_slot_private_alloc(struct hotplug_slot **bss_hotplug_slot, struct pci_bus *pci_bus, int device, char *name) { @@ -168,7 +173,6 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; - bss_hotplug_slot->private = slot; slot->device_num = device; slot->pci_bus = pci_bus; @@ -179,8 +183,8 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, sn_generate_path(pci_bus, slot->physical_path); - slot->hotplug_slot = bss_hotplug_slot; list_add(&slot->hp_list, &sn_hp_list); + *bss_hotplug_slot = &slot->hotplug_slot; return 0; } @@ -192,10 +196,9 @@ static struct hotplug_slot *sn_hp_destroy(void) struct hotplug_slot *bss_hotplug_slot = NULL; list_for_each_entry(slot, &sn_hp_list, hp_list) { - bss_hotplug_slot = slot->hotplug_slot; + bss_hotplug_slot = &slot->hotplug_slot; pci_slot = bss_hotplug_slot->pci_slot; - list_del(&((struct slot *)bss_hotplug_slot->private)-> - hp_list); + list_del(&slot->hp_list); sysfs_remove_file(&pci_slot->kobj, &sn_slot_path_attr.attr); break; @@ -227,7 +230,7 @@ static void sn_bus_free_data(struct pci_dev *dev) static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot, int device_num, char **ssdt) { - struct slot *slot = bss_hotplug_slot->private; + struct slot *slot = to_slot(bss_hotplug_slot); struct pcibus_info *pcibus_info; struct pcibr_slot_enable_resp resp; int rc; @@ -267,7 +270,7 @@ static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot, static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot, int device_num, int action) { - struct slot *slot = bss_hotplug_slot->private; + struct slot *slot = to_slot(bss_hotplug_slot); struct pcibus_info *pcibus_info; struct pcibr_slot_disable_resp resp; int rc; @@ -323,7 +326,7 @@ static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot, */ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) { - struct slot *slot = bss_hotplug_slot->private; + struct slot *slot = to_slot(bss_hotplug_slot); struct pci_bus *new_bus = NULL; struct pci_dev *dev; int num_funcs; @@ -469,7 +472,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) static int disable_slot(struct hotplug_slot *bss_hotplug_slot) { - struct slot *slot = bss_hotplug_slot->private; + struct slot *slot = to_slot(bss_hotplug_slot); struct pci_dev *dev, *temp; int rc; acpi_handle ssdt_hdl = NULL; @@ -571,7 +574,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot, u8 *value) { - struct slot *slot = bss_hotplug_slot->private; + struct slot *slot = to_slot(bss_hotplug_slot); struct pcibus_info *pcibus_info; u32 power; @@ -585,8 +588,7 @@ static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot, static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot) { - kfree(bss_hotplug_slot->private); - kfree(bss_hotplug_slot); + kfree(to_slot(bss_hotplug_slot)); } static int sn_hotplug_slot_register(struct pci_bus *pci_bus) @@ -606,14 +608,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) if (sn_pci_slot_valid(pci_bus, device) != 1) continue; - bss_hotplug_slot = kzalloc(sizeof(*bss_hotplug_slot), - GFP_KERNEL); - if (!bss_hotplug_slot) { - rc = -ENOMEM; - goto alloc_err; - } - - if (sn_hp_slot_private_alloc(bss_hotplug_slot, + if (sn_hp_slot_private_alloc(&bss_hotplug_slot, pci_bus, device, name)) { rc = -ENOMEM; goto alloc_err; @@ -628,7 +623,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) rc = sysfs_create_file(&pci_slot->kobj, &sn_slot_path_attr.attr); if (rc) - goto register_err; + goto alloc_err; } pci_dbg(pci_bus->self, "Registered bus with hotplug\n"); return rc; @@ -637,14 +632,11 @@ register_err: pci_dbg(pci_bus->self, "bus failed to register with err = %d\n", rc); -alloc_err: - if (rc == -ENOMEM) - pci_dbg(pci_bus->self, "Memory allocation error\n"); - /* destroy THIS element */ - if (bss_hotplug_slot) - sn_release_slot(bss_hotplug_slot); + sn_hp_destroy(); + sn_release_slot(bss_hotplug_slot); +alloc_err: /* destroy anything else on the list */ while ((bss_hotplug_slot = sn_hp_destroy())) { pci_hp_deregister(bss_hotplug_slot); diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index a7bb816e6f25..f7f13ee5d06e 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -73,7 +73,7 @@ struct slot { u8 pwr_save; struct controller *ctrl; const struct hpc_ops *hpc_ops; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct list_head slot_list; struct delayed_work work; /* work for button event */ struct mutex lock; @@ -171,7 +171,7 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev); static inline const char *slot_name(struct slot *slot) { - return hotplug_slot_name(slot->hotplug_slot); + return hotplug_slot_name(&slot->hotplug_slot); } struct ctrl_reg { @@ -209,7 +209,7 @@ enum ctrl_offsets { static inline struct slot *get_slot(struct hotplug_slot *hotplug_slot) { - return hotplug_slot->private; + return container_of(hotplug_slot, struct slot, hotplug_slot); } static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device) diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index b7181b7e7b98..81a918d47895 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -76,12 +76,7 @@ static int init_slots(struct controller *ctrl) goto error; } - hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); - if (!hotplug_slot) { - retval = -ENOMEM; - goto error_slot; - } - slot->hotplug_slot = hotplug_slot; + hotplug_slot = &slot->hotplug_slot; slot->hp_slot = i; slot->ctrl = ctrl; @@ -93,14 +88,13 @@ static int init_slots(struct controller *ctrl) slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number); if (!slot->wq) { retval = -ENOMEM; - goto error_hpslot; + goto error_slot; } mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work); /* register this slot with the hotplug pci core */ - hotplug_slot->private = slot; snprintf(name, SLOT_NAME_SIZE, "%d", slot->number); hotplug_slot->ops = &shpchp_hotplug_slot_ops; @@ -108,7 +102,7 @@ static int init_slots(struct controller *ctrl) pci_domain_nr(ctrl->pci_dev->subordinate), slot->bus, slot->device, slot->hp_slot, slot->number, ctrl->slot_device_offset); - retval = pci_hp_register(slot->hotplug_slot, + retval = pci_hp_register(hotplug_slot, ctrl->pci_dev->subordinate, slot->device, name); if (retval) { ctrl_err(ctrl, "pci_hp_register failed with error %d\n", @@ -127,8 +121,6 @@ static int init_slots(struct controller *ctrl) return 0; error_slotwq: destroy_workqueue(slot->wq); -error_hpslot: - kfree(hotplug_slot); error_slot: kfree(slot); error: @@ -143,8 +135,7 @@ void cleanup_slots(struct controller *ctrl) list_del(&slot->slot_list); cancel_delayed_work(&slot->work); destroy_workqueue(slot->wq); - pci_hp_deregister(slot->hotplug_slot); - kfree(slot->hotplug_slot); + pci_hp_deregister(&slot->hotplug_slot); kfree(slot); } } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 019b037319e3..93ee2d5466f8 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -254,7 +254,7 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; struct workqueue_struct *hotplug_workqueue; @@ -753,7 +753,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) if (asus->wlan.rfkill) rfkill_set_sw_state(asus->wlan.rfkill, blocked); - if (asus->hotplug_slot) { + if (asus->hotplug_slot.ops) { bus = pci_find_bus(0, 1); if (!bus) { pr_warn("Unable to find PCI bus 1?\n"); @@ -858,7 +858,8 @@ static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node) static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct asus_wmi *asus = hotplug_slot->private; + struct asus_wmi *asus = container_of(hotplug_slot, + struct asus_wmi, hotplug_slot); int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); if (result < 0) @@ -898,14 +899,9 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus) INIT_WORK(&asus->hotplug_work, asus_hotplug_work); - asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!asus->hotplug_slot) - goto error_slot; + asus->hotplug_slot.ops = &asus_hotplug_slot_ops; - asus->hotplug_slot->private = asus; - asus->hotplug_slot->ops = &asus_hotplug_slot_ops; - - ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi"); + ret = pci_hp_register(&asus->hotplug_slot, bus, 0, "asus-wifi"); if (ret) { pr_err("Unable to register hotplug slot - %d\n", ret); goto error_register; @@ -914,9 +910,7 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus) return 0; error_register: - kfree(asus->hotplug_slot); - asus->hotplug_slot = NULL; -error_slot: + asus->hotplug_slot.ops = NULL; destroy_workqueue(asus->hotplug_workqueue); error_workqueue: return ret; @@ -1044,10 +1038,8 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus) * asus_unregister_rfkill_notifier() */ asus_rfkill_hotplug(asus); - if (asus->hotplug_slot) { - pci_hp_deregister(asus->hotplug_slot); - kfree(asus->hotplug_slot); - } + if (asus->hotplug_slot.ops) + pci_hp_deregister(&asus->hotplug_slot); if (asus->hotplug_workqueue) destroy_workqueue(asus->hotplug_workqueue); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 028b20f82962..e6946a9beb5a 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -177,7 +177,7 @@ struct eeepc_laptop { struct rfkill *wwan3g_rfkill; struct rfkill *wimax_rfkill; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct led_classdev tpd_led; @@ -582,7 +582,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) mutex_lock(&eeepc->hotplug_lock); pci_lock_rescan_remove(); - if (!eeepc->hotplug_slot) + if (!eeepc->hotplug_slot.ops) goto out_unlock; port = acpi_get_pci_dev(handle); @@ -715,8 +715,11 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc, static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct eeepc_laptop *eeepc = hotplug_slot->private; - int val = get_acpi(eeepc, CM_ASL_WLAN); + struct eeepc_laptop *eeepc; + int val; + + eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot); + val = get_acpi(eeepc, CM_ASL_WLAN); if (val == 1 || val == 0) *value = val; @@ -741,14 +744,9 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) return -ENODEV; } - eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!eeepc->hotplug_slot) - goto error_slot; + eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops; - eeepc->hotplug_slot->private = eeepc; - eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; - - ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); + ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); if (ret) { pr_err("Unable to register hotplug slot - %d\n", ret); goto error_register; @@ -757,9 +755,7 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) return 0; error_register: - kfree(eeepc->hotplug_slot); - eeepc->hotplug_slot = NULL; -error_slot: + eeepc->hotplug_slot.ops = NULL; return ret; } @@ -820,10 +816,8 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) eeepc->wlan_rfkill = NULL; } - if (eeepc->hotplug_slot) { - pci_hp_deregister(eeepc->hotplug_slot); - kfree(eeepc->hotplug_slot); - } + if (eeepc->hotplug_slot.ops) + pci_hp_deregister(&eeepc->hotplug_slot); if (eeepc->bluetooth_rfkill) { rfkill_unregister(eeepc->bluetooth_rfkill); diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 6f07a4e1de8d..7acc9f91e72b 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -50,14 +50,11 @@ struct hotplug_slot_ops { /** * struct hotplug_slot - used to register a physical slot with the hotplug pci core * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot - * @private: used by the hotplug pci controller driver to store whatever it - * needs. * @owner: The module owner of this structure * @mod_name: The module name (KBUILD_MODNAME) of this structure */ struct hotplug_slot { const struct hotplug_slot_ops *ops; - void *private; /* Variables below this are for use only by the hotplug pci core. */ struct list_head slot_list; -- cgit v1.2.3 From a0d58937404f5fe095120687c8914175587e6c51 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 8 Sep 2018 09:59:01 +0200 Subject: PCI: hotplug: Document TODOs While refactoring the PCI hotplug core's API, I noticed a significant amount of technical debt in some of the hotplug drivers. Document the issues that caught my eye for starters. I do not have hardware at my disposal that utilizes the listed drivers and I think that's a prerequisite to work on them to ensure that no regressions sneak in. But some of this hardware is so old that it may be hard to come by. Obviously, it is fine to support old hardware, but the drivers need to be maintained. If noone steps up, perhaps we should consider sunsetting a few drivers by moving them to staging. Based on my findings, ibmphp would be the first candidate. I've found it fairly difficult to apply my API refactorings to it and have listed some obvious bugs in the driver. cpqphp is also in need of a modernization and would be a second candidate for relegation to staging. shpchp was introduced in the same commit as pciehp but hasn't benefited from the same amount of refactoring due to the decline of conventional PCI's relevance. Yet hardware supporting it may be more prevalent than for the proprietary hotplug methods. Per Documentation/process/2.Process.rst, "a TODO file should be present" for drivers in staging. The file introduced by the present commit may serve as a basis for this. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Cc: Greg Kroah-Hartman Cc: Scott Murray Cc: Dan Zink Cc: Prarit Bhargava --- drivers/pci/hotplug/TODO | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 drivers/pci/hotplug/TODO (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/TODO b/drivers/pci/hotplug/TODO new file mode 100644 index 000000000000..a32070be5adf --- /dev/null +++ b/drivers/pci/hotplug/TODO @@ -0,0 +1,74 @@ +Contributions are solicited in particular to remedy the following issues: + +cpcihp: + +* There are no implementations of the ->hardware_test, ->get_power and + ->set_power callbacks in struct cpci_hp_controller_ops. Why were they + introduced? Can they be removed from the struct? + +cpqphp: + +* The driver spawns a kthread cpqhp_event_thread() which is woken by the + hardirq handler cpqhp_ctrl_intr(). Convert this to threaded IRQ handling. + The kthread is also woken from the timer pushbutton_helper_thread(), + convert it to call irq_wake_thread(). Use pciehp as a template. + +* A large portion of cpqphp_ctrl.c and cpqphp_pci.c concerns resource + management. Doesn't this duplicate functionality in the core? + +ibmphp: + +* Implementations of hotplug_slot_ops callbacks such as get_adapter_present() + in ibmphp_core.c create a copy of the struct slot on the stack, then perform + the actual operation on that copy. Determine if this overhead is necessary, + delete it if not. The functions also perform a NULL pointer check on the + struct hotplug_slot, this seems superfluous. + +* Several functions access the pci_slot member in struct hotplug_slot even + though pci_hotplug.h declares it private. See get_max_bus_speed() for an + example. Either the pci_slot member should no longer be declared private + or ibmphp should store a pointer to its bus in struct slot. Probably the + former. + +* The functions get_max_adapter_speed() and get_bus_name() are commented out. + Can they be deleted? There are also forward declarations at the top of + ibmphp_core.c as well as pointers in ibmphp_hotplug_slot_ops, likewise + commented out. + +* ibmphp_init_devno() takes a struct slot **, it could instead take a + struct slot *. + +* The return value of pci_hp_register() is not checked. + +* iounmap(io_mem) is called in the error path of ebda_rsrc_controller() + and once more in the error path of its caller ibmphp_access_ebda(). + +* The various slot data structures are difficult to follow and need to be + simplified. A lot of functions are too large and too complex, they need + to be broken up into smaller, manageable pieces. Negative examples are + ebda_rsrc_controller() and configure_bridge(). + +* A large portion of ibmphp_res.c and ibmphp_pci.c concerns resource + management. Doesn't this duplicate functionality in the core? + +sgi_hotplug: + +* Several functions access the pci_slot member in struct hotplug_slot even + though pci_hotplug.h declares it private. See sn_hp_destroy() for an + example. Either the pci_slot member should no longer be declared private + or sgi_hotplug should store a pointer to it in struct slot. Probably the + former. + +shpchp: + +* There is only a single implementation of struct hpc_ops. Can the struct be + removed and its functions invoked directly? This has already been done in + pciehp with commit 82a9e79ef132 ("PCI: pciehp: remove hpc_ops"). Clarify + if there was a specific reason not to apply the same change to shpchp. + +* The ->get_mode1_ECC_cap callback in shpchp_hpc_ops is never invoked. + Why was it introduced? Can it be removed? + +* The hardirq handler shpc_isr() queues events on a workqueue. It can be + simplified by converting it to threaded IRQ handling. Use pciehp as a + template. -- cgit v1.2.3 From c29de84149aba5f74e87b6491c13ac7203c12f55 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:06 -0600 Subject: PCI: portdrv: Initialize service drivers directly The PCI port driver saves the PCI state after initializing the device with the applicable service devices. This was, however, before the service drivers were even registered because PCI probe happens before the device_initcall initialized those service drivers. The config space state that the services set up were not being saved. The end result would cause PCI devices to not react to events that the drivers think they did if the PCI state ever needed to be restored. Fix this by changing the service drivers from using the init calls to having the portdrv driver calling the services directly. This will get the state saved as desired, while making the relationship between the port driver and the services under it more explicit in the code. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/hotplug/pciehp_core.c | 3 +-- drivers/pci/pcie/aer.c | 3 +-- drivers/pci/pcie/dpc.c | 3 +-- drivers/pci/pcie/pme.c | 3 +-- drivers/pci/pcie/portdrv.h | 24 ++++++++++++++++++++++++ drivers/pci/pcie/portdrv_pci.c | 9 +++++++++ 6 files changed, 37 insertions(+), 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 68b20e667764..944047976569 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -287,7 +287,7 @@ static struct pcie_port_service_driver hpdriver_portdrv = { #endif /* PM */ }; -static int __init pcied_init(void) +int __init pcie_hp_init(void) { int retval = 0; @@ -298,4 +298,3 @@ static int __init pcied_init(void) return retval; } -device_initcall(pcied_init); diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 83180edd6ed4..637d638f73da 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1569,10 +1569,9 @@ static struct pcie_port_service_driver aerdriver = { * * Invoked when AER root service driver is loaded. */ -static int __init aer_service_init(void) +int __init pcie_aer_init(void) { if (!pci_aer_available() || aer_acpi_firmware_first()) return -ENXIO; return pcie_port_service_register(&aerdriver); } -device_initcall(aer_service_init); diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index f03279fc87cd..a1fd16bf1cab 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -282,8 +282,7 @@ static struct pcie_port_service_driver dpcdriver = { .reset_link = dpc_reset_link, }; -static int __init dpc_service_init(void) +int __init pcie_dpc_init(void) { return pcie_port_service_register(&dpcdriver); } -device_initcall(dpc_service_init); diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 3ed67676ea2a..1a8b85051b1b 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -446,8 +446,7 @@ static struct pcie_port_service_driver pcie_pme_driver = { /** * pcie_pme_service_init - Register the PCIe PME service driver. */ -static int __init pcie_pme_service_init(void) +int __init pcie_pme_init(void) { return pcie_port_service_register(&pcie_pme_driver); } -device_initcall(pcie_pme_service_init); diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index d59afa42fc14..2498b2d34009 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -23,6 +23,30 @@ #define PCIE_PORT_DEVICE_MAXSERVICES 4 +#ifdef CONFIG_PCIEAER +int pcie_aer_init(void); +#else +static inline int pcie_aer_init(void) { return 0; } +#endif + +#ifdef CONFIG_HOTPLUG_PCI_PCIE +int pcie_hp_init(void); +#else +static inline int pcie_hp_init(void) { return 0; } +#endif + +#ifdef CONFIG_PCIE_PME +int pcie_pme_init(void); +#else +static inline int pcie_pme_init(void) { return 0; } +#endif + +#ifdef CONFIG_PCIE_DPC +int pcie_dpc_init(void); +#else +static inline int pcie_dpc_init(void) { return 0; } +#endif + /* Port Type */ #define PCIE_ANY_PORT (~0) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index eef22dc29140..23a5a0c2c3fe 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -226,11 +226,20 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = { {} }; +static void __init pcie_init_services(void) +{ + pcie_aer_init(); + pcie_pme_init(); + pcie_dpc_init(); + pcie_hp_init(); +} + static int __init pcie_portdrv_init(void) { if (pcie_ports_disabled) return -EACCES; + pcie_init_services(); dmi_check_system(pcie_portdrv_dmi_table); return pci_register_driver(&pcie_portdriver); -- cgit v1.2.3 From 874b3251113a1e2cbe79c24994dc03fe4fe4b99b Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:07 -0600 Subject: PCI: portdrv: Restore PCI config state on slot reset The port's config space may be cleared after a link reset, which wipes out the bridge's bus and memory windows. Restore the config space that was saved during probe so we can access downstream devices. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pcie/portdrv_pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 23a5a0c2c3fe..17256733fa43 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -146,6 +146,13 @@ static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev, return PCI_ERS_RESULT_CAN_RECOVER; } +static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) +{ + pci_restore_state(dev); + pci_save_state(dev); + return PCI_ERS_RESULT_RECOVERED; +} + static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev) { return PCI_ERS_RESULT_RECOVERED; @@ -185,6 +192,7 @@ static const struct pci_device_id port_pci_ids[] = { { static const struct pci_error_handlers pcie_portdrv_err_handler = { .error_detected = pcie_portdrv_error_detected, + .slot_reset = pcie_portdrv_slot_reset, .mmio_enabled = pcie_portdrv_mmio_enabled, .resume = pcie_portdrv_err_resume, }; -- cgit v1.2.3 From 4f802170a861265680cad03f47b19c4c3a137052 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:08 -0600 Subject: PCI/DPC: Save and restore config state This patch provides DPC save and restore capabilities. This is necessary for the driver to observe DPC events in the event the configuration space needs to be restored after a reset. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pci.c | 2 ++ drivers/pci/pci.h | 8 +++++++ drivers/pci/pcie/dpc.c | 61 +++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0e54588825cb..d6bb56fbee6d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1284,6 +1284,7 @@ int pci_save_state(struct pci_dev *dev) if (i != 0) return i; + pci_save_dpc_state(dev); return pci_save_vc_state(dev); } EXPORT_SYMBOL(pci_save_state); @@ -1378,6 +1379,7 @@ void pci_restore_state(struct pci_dev *dev) pci_restore_ats_state(dev); pci_restore_vc_state(dev); pci_restore_rebar_state(dev); + pci_restore_dpc_state(dev); pci_cleanup_aer_error_status_regs(dev); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6e0d1528d471..b5af5642c6c9 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -346,6 +346,14 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info); void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); #endif /* CONFIG_PCIEAER */ +#ifdef CONFIG_PCIE_DPC +void pci_save_dpc_state(struct pci_dev *dev); +void pci_restore_dpc_state(struct pci_dev *dev); +#else +static inline void pci_save_dpc_state(struct pci_dev *dev) {} +static inline void pci_restore_dpc_state(struct pci_dev *dev) {} +#endif + #ifdef CONFIG_PCI_ATS void pci_restore_ats_state(struct pci_dev *dev); #else diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index a1fd16bf1cab..ed815a28512e 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -44,6 +44,58 @@ static const char * const rp_pio_error_string[] = { "Memory Request Completion Timeout", /* Bit Position 18 */ }; +static struct dpc_dev *to_dpc_dev(struct pci_dev *dev) +{ + struct device *device; + + device = pcie_port_find_device(dev, PCIE_PORT_SERVICE_DPC); + if (!device) + return NULL; + return get_service_data(to_pcie_device(device)); +} + +void pci_save_dpc_state(struct pci_dev *dev) +{ + struct dpc_dev *dpc; + struct pci_cap_saved_state *save_state; + u16 *cap; + + if (!pci_is_pcie(dev)) + return; + + dpc = to_dpc_dev(dev); + if (!dpc) + return; + + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC); + if (!save_state) + return; + + cap = (u16 *)&save_state->cap.data[0]; + pci_read_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, cap); +} + +void pci_restore_dpc_state(struct pci_dev *dev) +{ + struct dpc_dev *dpc; + struct pci_cap_saved_state *save_state; + u16 *cap; + + if (!pci_is_pcie(dev)) + return; + + dpc = to_dpc_dev(dev); + if (!dpc) + return; + + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC); + if (!save_state) + return; + + cap = (u16 *)&save_state->cap.data[0]; + pci_write_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, *cap); +} + static int dpc_wait_rp_inactive(struct dpc_dev *dpc) { unsigned long timeout = jiffies + HZ; @@ -67,18 +119,13 @@ static int dpc_wait_rp_inactive(struct dpc_dev *dpc) static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev) { struct dpc_dev *dpc; - struct pcie_device *pciedev; - struct device *devdpc; - u16 cap; /* * DPC disables the Link automatically in hardware, so it has * already been reset by the time we get here. */ - devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC); - pciedev = to_pcie_device(devdpc); - dpc = get_service_data(pciedev); + dpc = to_dpc_dev(pdev); cap = dpc->cap_pos; /* @@ -259,6 +306,8 @@ static int dpc_probe(struct pcie_device *dev) FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP), FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size, FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE)); + + pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_DPC, sizeof(u16)); return status; } -- cgit v1.2.3 From 60271ab044a53edb9dcbe76bebea2221c4ff04d9 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:09 -0600 Subject: PCI/AER: Take reference on error devices Error handling may be running in parallel with a hot removal. Reference count the device during AER handling so the device can not be freed while AER wants to reference it. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pcie/aer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 637d638f73da..ffbbd759683c 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -866,7 +866,7 @@ void cper_print_aer(struct pci_dev *dev, int aer_severity, static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) { if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { - e_info->dev[e_info->error_dev_num] = dev; + e_info->dev[e_info->error_dev_num] = pci_dev_get(dev); e_info->error_dev_num++; return 0; } @@ -1013,6 +1013,7 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info) pcie_do_nonfatal_recovery(dev); else if (info->severity == AER_FATAL) pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER); + pci_dev_put(dev); } #ifdef CONFIG_ACPI_APEI_PCIEAER -- cgit v1.2.3 From 9d938ea53b265ed6df6cdd1715d971f0235fdbfc Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:10 -0600 Subject: PCI/AER: Don't read upstream ports below fatal errors The AER driver has never read the config space of an endpoint that reported a fatal error because the link to that device is considered unreliable. An ERR_FATAL from an upstream port almost certainly indicates an error on its upstream link, so we can't expect to reliably read its config space for the same reason. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pcie/aer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index ffbbd759683c..5c3ea7254c6a 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1116,8 +1116,9 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) &info->mask); if (!(info->status & ~info->mask)) return 0; - } else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || - info->severity == AER_NONFATAL) { + } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM || + info->severity == AER_NONFATAL) { /* Link is still healthy for IO reads */ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, -- cgit v1.2.3 From c4eed62a214330908eec11b0dc170d34fa50b412 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:11 -0600 Subject: PCI/ERR: Use slot reset if available The secondary bus reset may have link side effects that a hotplug capable port may incorrectly react to. Use the slot specific reset for hotplug ports, fixing the undesirable link down-up handling during error recovering. Signed-off-by: Keith Busch [bhelgaas: fold in https://lore.kernel.org/linux-pci/20180926152326.14821-1-keith.busch@intel.com for issue reported by Stephen Rothwell ] Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pci.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 2 ++ drivers/pci/pcie/aer.c | 2 +- drivers/pci/pcie/err.c | 2 +- drivers/pci/slot.c | 1 - 5 files changed, 41 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d6bb56fbee6d..6916af269b19 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -35,6 +35,8 @@ #include #include "pci.h" +DEFINE_MUTEX(pci_slot_mutex); + const char *pci_power_names[] = { "error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown", }; @@ -5155,6 +5157,41 @@ static int pci_bus_reset(struct pci_bus *bus, int probe) return ret; } +/** + * pci_bus_error_reset - reset the bridge's subordinate bus + * @bridge: The parent device that connects to the bus to reset + * + * This function will first try to reset the slots on this bus if the method is + * available. If slot reset fails or is not available, this will fall back to a + * secondary bus reset. + */ +int pci_bus_error_reset(struct pci_dev *bridge) +{ + struct pci_bus *bus = bridge->subordinate; + struct pci_slot *slot; + + if (!bus) + return -ENOTTY; + + mutex_lock(&pci_slot_mutex); + if (list_empty(&bus->slots)) + goto bus_reset; + + list_for_each_entry(slot, &bus->slots, list) + if (pci_probe_reset_slot(slot)) + goto bus_reset; + + list_for_each_entry(slot, &bus->slots, list) + if (pci_slot_reset(slot, 0)) + goto bus_reset; + + mutex_unlock(&pci_slot_mutex); + return 0; +bus_reset: + mutex_unlock(&pci_slot_mutex); + return pci_bus_reset(bridge->subordinate, 0); +} + /** * pci_probe_reset_bus - probe whether a PCI bus can be reset * @bus: PCI bus to probe diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b5af5642c6c9..b4ada8c383a8 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -35,6 +35,7 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai, int pci_probe_reset_function(struct pci_dev *dev); int pci_bridge_secondary_bus_reset(struct pci_dev *dev); +int pci_bus_error_reset(struct pci_dev *dev); /** * struct pci_platform_pm_ops - Firmware PM callbacks @@ -136,6 +137,7 @@ static inline void pci_remove_legacy_files(struct pci_bus *bus) { return; } /* Lock for read/write access to pci device and bus lists */ extern struct rw_semaphore pci_bus_sem; +extern struct mutex pci_slot_mutex; extern raw_spinlock_t pci_lock; diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 5c3ea7254c6a..1563e22600ec 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1528,7 +1528,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); - rc = pci_bridge_secondary_bus_reset(dev); + rc = pci_bus_error_reset(dev); pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n"); /* Clear Root Error Status */ diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index cac406b6e936..62ab665f0f03 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -177,7 +177,7 @@ static pci_ers_result_t default_reset_link(struct pci_dev *dev) { int rc; - rc = pci_bridge_secondary_bus_reset(dev); + rc = pci_bus_error_reset(dev); pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n"); return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED; } diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 145cd953b518..c46d5e1ff536 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -14,7 +14,6 @@ struct kset *pci_slots_kset; EXPORT_SYMBOL_GPL(pci_slots_kset); -static DEFINE_MUTEX(pci_slot_mutex); static ssize_t pci_slot_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) -- cgit v1.2.3 From bdb5ac85777de67c909c9ad4327f03f7648b543f Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:12 -0600 Subject: PCI/ERR: Handle fatal error recovery We don't need to be paranoid about the topology changing while handling an error. If the device has changed in a hotplug capable slot, we can rely on the presence detection handling to react to a changing topology. Restore the fatal error handling behavior that existed before merging DPC with AER with 7e9084b36740 ("PCI/AER: Handle ERR_FATAL with removal and re-enumeration of devices"). Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- Documentation/PCI/pci-error-recovery.txt | 35 +++++---------- drivers/pci/pci.h | 4 +- drivers/pci/pcie/aer.c | 12 +++-- drivers/pci/pcie/dpc.c | 4 +- drivers/pci/pcie/err.c | 75 +++----------------------------- 5 files changed, 28 insertions(+), 102 deletions(-) (limited to 'drivers/pci') diff --git a/Documentation/PCI/pci-error-recovery.txt b/Documentation/PCI/pci-error-recovery.txt index 688b69121e82..0b6bb3ef449e 100644 --- a/Documentation/PCI/pci-error-recovery.txt +++ b/Documentation/PCI/pci-error-recovery.txt @@ -110,7 +110,7 @@ The actual steps taken by a platform to recover from a PCI error event will be platform-dependent, but will follow the general sequence described below. -STEP 0: Error Event: ERR_NONFATAL +STEP 0: Error Event ------------------- A PCI bus error is detected by the PCI hardware. On powerpc, the slot is isolated, in that all I/O is blocked: all reads return 0xffffffff, @@ -228,7 +228,13 @@ proceeds to either STEP3 (Link Reset) or to STEP 5 (Resume Operations). If any driver returned PCI_ERS_RESULT_NEED_RESET, then the platform proceeds to STEP 4 (Slot Reset) -STEP 3: Slot Reset +STEP 3: Link Reset +------------------ +The platform resets the link. This is a PCI-Express specific step +and is done whenever a fatal error has been detected that can be +"solved" by resetting the link. + +STEP 4: Slot Reset ------------------ In response to a return value of PCI_ERS_RESULT_NEED_RESET, the @@ -314,7 +320,7 @@ Failure). >>> However, it probably should. -STEP 4: Resume Operations +STEP 5: Resume Operations ------------------------- The platform will call the resume() callback on all affected device drivers if all drivers on the segment have returned @@ -326,7 +332,7 @@ a result code. At this point, if a new error happens, the platform will restart a new error recovery sequence. -STEP 5: Permanent Failure +STEP 6: Permanent Failure ------------------------- A "permanent failure" has occurred, and the platform cannot recover the device. The platform will call error_detected() with a @@ -349,27 +355,6 @@ errors. See the discussion in powerpc/eeh-pci-error-recovery.txt for additional detail on real-life experience of the causes of software errors. -STEP 0: Error Event: ERR_FATAL -------------------- -PCI bus error is detected by the PCI hardware. On powerpc, the slot is -isolated, in that all I/O is blocked: all reads return 0xffffffff, all -writes are ignored. - -STEP 1: Remove devices --------------------- -Platform removes the devices depending on the error agent, it could be -this port for all subordinates or upstream component (likely downstream -port) - -STEP 2: Reset link --------------------- -The platform resets the link. This is a PCI-Express specific step and is -done whenever a fatal error has been detected that can be "solved" by -resetting the link. - -STEP 3: Re-enumerate the devices --------------------- -Initiates the re-enumeration. Conclusion; General Remarks --------------------------- diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b4ada8c383a8..9b279805489f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -433,8 +433,8 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev) #endif /* PCI error reporting and recovery */ -void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service); -void pcie_do_nonfatal_recovery(struct pci_dev *dev); +void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, + u32 service); bool pcie_wait_for_link(struct pci_dev *pdev, bool active); #ifdef CONFIG_PCIEASPM diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 1563e22600ec..0619ec5d7bb5 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1010,9 +1010,11 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info) info->status); pci_aer_clear_device_status(dev); } else if (info->severity == AER_NONFATAL) - pcie_do_nonfatal_recovery(dev); + pcie_do_recovery(dev, pci_channel_io_normal, + PCIE_PORT_SERVICE_AER); else if (info->severity == AER_FATAL) - pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER); + pcie_do_recovery(dev, pci_channel_io_frozen, + PCIE_PORT_SERVICE_AER); pci_dev_put(dev); } @@ -1048,9 +1050,11 @@ static void aer_recover_work_func(struct work_struct *work) } cper_print_aer(pdev, entry.severity, entry.regs); if (entry.severity == AER_NONFATAL) - pcie_do_nonfatal_recovery(pdev); + pcie_do_recovery(pdev, pci_channel_io_normal, + PCIE_PORT_SERVICE_AER); else if (entry.severity == AER_FATAL) - pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_AER); + pcie_do_recovery(pdev, pci_channel_io_frozen, + PCIE_PORT_SERVICE_AER); pci_dev_put(pdev); } } diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index ed815a28512e..23e063aefddf 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -216,7 +216,7 @@ static irqreturn_t dpc_handler(int irq, void *context) reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1; ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5; - dev_warn(dev, "DPC %s detected, remove downstream devices\n", + dev_warn(dev, "DPC %s detected\n", (reason == 0) ? "unmasked uncorrectable error" : (reason == 1) ? "ERR_NONFATAL" : (reason == 2) ? "ERR_FATAL" : @@ -233,7 +233,7 @@ static irqreturn_t dpc_handler(int irq, void *context) } /* We configure DPC so it only triggers on ERR_FATAL */ - pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_DPC); + pcie_do_recovery(pdev, pci_channel_io_frozen, PCIE_PORT_SERVICE_DPC); return IRQ_HANDLED; } diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 62ab665f0f03..644f3f725ef0 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -271,83 +271,20 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, return result_data.result; } -/** - * pcie_do_fatal_recovery - handle fatal error recovery process - * @dev: pointer to a pci_dev data structure of agent detecting an error - * - * Invoked when an error is fatal. Once being invoked, removes the devices - * beneath this AER agent, followed by reset link e.g. secondary bus reset - * followed by re-enumeration of devices. - */ -void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service) -{ - struct pci_dev *udev; - struct pci_bus *parent; - struct pci_dev *pdev, *temp; - pci_ers_result_t result; - - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) - udev = dev; - else - udev = dev->bus->self; - - parent = udev->subordinate; - pci_walk_bus(parent, pci_dev_set_disconnected, NULL); - - pci_lock_rescan_remove(); - pci_dev_get(dev); - list_for_each_entry_safe_reverse(pdev, temp, &parent->devices, - bus_list) { - pci_stop_and_remove_bus_device(pdev); - } - - result = reset_link(udev, service); - - if ((service == PCIE_PORT_SERVICE_AER) && - (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { - /* - * If the error is reported by a bridge, we think this error - * is related to the downstream link of the bridge, so we - * do error recovery on all subordinates of the bridge instead - * of the bridge and clear the error status of the bridge. - */ - pci_aer_clear_fatal_status(dev); - pci_aer_clear_device_status(dev); - } - - if (result == PCI_ERS_RESULT_RECOVERED) { - if (pcie_wait_for_link(udev, true)) - pci_rescan_bus(udev->bus); - pci_info(dev, "Device recovery from fatal error successful\n"); - } else { - pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT); - pci_info(dev, "Device recovery from fatal error failed\n"); - } - - pci_dev_put(dev); - pci_unlock_rescan_remove(); -} - -/** - * pcie_do_nonfatal_recovery - handle nonfatal error recovery process - * @dev: pointer to a pci_dev data structure of agent detecting an error - * - * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast - * error detected message to all downstream drivers within a hierarchy in - * question and return the returned code. - */ -void pcie_do_nonfatal_recovery(struct pci_dev *dev) +void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, + u32 service) { pci_ers_result_t status; - enum pci_channel_state state; - - state = pci_channel_io_normal; status = broadcast_error_message(dev, state, "error_detected", report_error_detected); + if (state == pci_channel_io_frozen && + reset_link(dev, service) != PCI_ERS_RESULT_RECOVERED) + goto failed; + if (status == PCI_ERS_RESULT_CAN_RECOVER) status = broadcast_error_message(dev, state, -- cgit v1.2.3 From bfcb79fca19d267712e425af1dd48812c40dec0c Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:13 -0600 Subject: PCI/ERR: Run error recovery callbacks for all affected devices If an Endpoint reported an error with ERR_FATAL, we previously ran driver error recovery callbacks only for the Endpoint's driver. But if we reset a Link to recover from the error, all downstream components are affected, including the Endpoint, any multi-function peers, and children of those peers. Initiate the Link reset from the deepest Downstream Port that is reliable, and call the error recovery callbacks for all its children. If a Downstream Port (including a Root Port) reports an error, we assume the Port itself is reliable and we need to reset its downstream Link. In all other cases (Switch Upstream Ports, Endpoints, Bridges, etc), we assume the Link leading to the component needs to be reset, so we initiate the reset at the parent Downstream Port. This allows two other clean-ups. First, we currently only use a Link reset, which can only be initiated using a Downstream Port, so we can remove checks for Endpoints. Second, the Downstream Port where we initiate the Link reset is reliable (unlike components downstream from it), so the special cases for error detect and resume are no longer necessary. Signed-off-by: Keith Busch [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pcie/err.c | 85 +++++++++++++------------------------------------- 1 file changed, 21 insertions(+), 64 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 644f3f725ef0..0fa5e1417a4a 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -63,30 +63,12 @@ static int report_error_detected(struct pci_dev *dev, void *data) if (!dev->driver || !dev->driver->err_handler || !dev->driver->err_handler->error_detected) { - if (result_data->state == pci_channel_io_frozen && - dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { - /* - * In case of fatal recovery, if one of down- - * stream device has no driver. We might be - * unable to recover because a later insmod - * of a driver for this device is unaware of - * its hw state. - */ - pci_printk(KERN_DEBUG, dev, "device has %s\n", - dev->driver ? - "no AER-aware driver" : "no driver"); - } - /* - * If there's any device in the subtree that does not - * have an error_detected callback, returning - * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of - * the subsequent mmio_enabled/slot_reset/resume - * callbacks of "any" device in the subtree. All the - * devices in the subtree are left in the error state - * without recovery. + * If any device in the subtree does not have an error_detected + * callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent + * error callbacks of "any" device in the subtree, and will + * exit in the disconnected error state. */ - if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) vote = PCI_ERS_RESULT_NO_AER_DRIVER; else @@ -184,34 +166,23 @@ static pci_ers_result_t default_reset_link(struct pci_dev *dev) static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service) { - struct pci_dev *udev; pci_ers_result_t status; struct pcie_port_service_driver *driver = NULL; - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - /* Reset this port for all subordinates */ - udev = dev; - } else { - /* Reset the upstream component (likely downstream port) */ - udev = dev->bus->self; - } - - /* Use the aer driver of the component firstly */ - driver = pcie_port_find_service(udev, service); - + driver = pcie_port_find_service(dev, service); if (driver && driver->reset_link) { - status = driver->reset_link(udev); - } else if (udev->has_secondary_link) { - status = default_reset_link(udev); + status = driver->reset_link(dev); + } else if (dev->has_secondary_link) { + status = default_reset_link(dev); } else { pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n", - pci_name(udev)); + pci_name(dev)); return PCI_ERS_RESULT_DISCONNECT; } if (status != PCI_ERS_RESULT_RECOVERED) { pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n", - pci_name(udev)); + pci_name(dev)); return PCI_ERS_RESULT_DISCONNECT; } @@ -243,31 +214,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, else result_data.result = PCI_ERS_RESULT_RECOVERED; - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - /* - * If the error is reported by a bridge, we think this error - * is related to the downstream link of the bridge, so we - * do error recovery on all subordinates of the bridge instead - * of the bridge and clear the error status of the bridge. - */ - if (cb == report_error_detected) - dev->error_state = state; - pci_walk_bus(dev->subordinate, cb, &result_data); - if (cb == report_resume) { - pci_aer_clear_device_status(dev); - pci_cleanup_aer_uncorrect_error_status(dev); - dev->error_state = pci_channel_io_normal; - } - } else { - /* - * If the error is reported by an end point, we think this - * error is related to the upstream link of the end point. - * The error is non fatal so the bus is ok; just invoke - * the callback for the function that logged the error. - */ - cb(dev, &result_data); - } - + pci_walk_bus(dev->subordinate, cb, &result_data); return result_data.result; } @@ -276,6 +223,14 @@ void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, { pci_ers_result_t status; + /* + * Error recovery runs on all subordinates of the first downstream port. + * If the downstream port detected the error, it is cleared at the end. + */ + if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)) + dev = dev->bus->self; + status = broadcast_error_message(dev, state, "error_detected", @@ -311,6 +266,8 @@ void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, "resume", report_resume); + pci_aer_clear_device_status(dev); + pci_cleanup_aer_uncorrect_error_status(dev); pci_info(dev, "AER: Device recovery successful\n"); return; -- cgit v1.2.3 From 542aeb9c8f930e4099432cb0bec17b92c0175e08 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:14 -0600 Subject: PCI/ERR: Simplify broadcast callouts There is no point in having a generic broadcast function if it needs to have special cases for each callback it broadcasts. Abstract the error broadcast to only the necessary information and removes the now unnecessary helper to walk the bus. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pcie/err.c | 107 ++++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 69 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 0fa5e1417a4a..362a717c831a 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -19,11 +19,6 @@ #include "portdrv.h" #include "../pci.h" -struct aer_broadcast_data { - enum pci_channel_state state; - enum pci_ers_result result; -}; - static pci_ers_result_t merge_result(enum pci_ers_result orig, enum pci_ers_result new) { @@ -49,16 +44,15 @@ static pci_ers_result_t merge_result(enum pci_ers_result orig, return orig; } -static int report_error_detected(struct pci_dev *dev, void *data) +static int report_error_detected(struct pci_dev *dev, + enum pci_channel_state state, + enum pci_ers_result *result) { pci_ers_result_t vote; const struct pci_error_handlers *err_handler; - struct aer_broadcast_data *result_data; - - result_data = (struct aer_broadcast_data *) data; device_lock(&dev->dev); - dev->error_state = result_data->state; + dev->error_state = state; if (!dev->driver || !dev->driver->err_handler || @@ -75,22 +69,29 @@ static int report_error_detected(struct pci_dev *dev, void *data) vote = PCI_ERS_RESULT_NONE; } else { err_handler = dev->driver->err_handler; - vote = err_handler->error_detected(dev, result_data->state); + vote = err_handler->error_detected(dev, state); pci_uevent_ers(dev, PCI_ERS_RESULT_NONE); } - result_data->result = merge_result(result_data->result, vote); + *result = merge_result(*result, vote); device_unlock(&dev->dev); return 0; } +static int report_frozen_detected(struct pci_dev *dev, void *data) +{ + return report_error_detected(dev, pci_channel_io_frozen, data); +} + +static int report_normal_detected(struct pci_dev *dev, void *data) +{ + return report_error_detected(dev, pci_channel_io_normal, data); +} + static int report_mmio_enabled(struct pci_dev *dev, void *data) { - pci_ers_result_t vote; + pci_ers_result_t vote, *result = data; const struct pci_error_handlers *err_handler; - struct aer_broadcast_data *result_data; - - result_data = (struct aer_broadcast_data *) data; device_lock(&dev->dev); if (!dev->driver || @@ -100,7 +101,7 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data) err_handler = dev->driver->err_handler; vote = err_handler->mmio_enabled(dev); - result_data->result = merge_result(result_data->result, vote); + *result = merge_result(*result, vote); out: device_unlock(&dev->dev); return 0; @@ -108,11 +109,8 @@ out: static int report_slot_reset(struct pci_dev *dev, void *data) { - pci_ers_result_t vote; + pci_ers_result_t vote, *result = data; const struct pci_error_handlers *err_handler; - struct aer_broadcast_data *result_data; - - result_data = (struct aer_broadcast_data *) data; device_lock(&dev->dev); if (!dev->driver || @@ -122,7 +120,7 @@ static int report_slot_reset(struct pci_dev *dev, void *data) err_handler = dev->driver->err_handler; vote = err_handler->slot_reset(dev); - result_data->result = merge_result(result_data->result, vote); + *result = merge_result(*result, vote); out: device_unlock(&dev->dev); return 0; @@ -189,39 +187,11 @@ static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service) return status; } -/** - * broadcast_error_message - handle message broadcast to downstream drivers - * @dev: pointer to from where in a hierarchy message is broadcasted down - * @state: error state - * @error_mesg: message to print - * @cb: callback to be broadcasted - * - * Invoked during error recovery process. Once being invoked, the content - * of error severity will be broadcasted to all downstream drivers in a - * hierarchy in question. - */ -static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, - enum pci_channel_state state, - char *error_mesg, - int (*cb)(struct pci_dev *, void *)) -{ - struct aer_broadcast_data result_data; - - pci_printk(KERN_DEBUG, dev, "broadcast %s message\n", error_mesg); - result_data.state = state; - if (cb == report_error_detected) - result_data.result = PCI_ERS_RESULT_CAN_RECOVER; - else - result_data.result = PCI_ERS_RESULT_RECOVERED; - - pci_walk_bus(dev->subordinate, cb, &result_data); - return result_data.result; -} - void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, u32 service) { - pci_ers_result_t status; + pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER; + struct pci_bus *bus; /* * Error recovery runs on all subordinates of the first downstream port. @@ -230,21 +200,23 @@ void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)) dev = dev->bus->self; + bus = dev->subordinate; - status = broadcast_error_message(dev, - state, - "error_detected", - report_error_detected); + pci_dbg(dev, "broadcast error_detected message\n"); + if (state == pci_channel_io_frozen) + pci_walk_bus(bus, report_frozen_detected, &status); + else + pci_walk_bus(bus, report_normal_detected, &status); if (state == pci_channel_io_frozen && reset_link(dev, service) != PCI_ERS_RESULT_RECOVERED) goto failed; - if (status == PCI_ERS_RESULT_CAN_RECOVER) - status = broadcast_error_message(dev, - state, - "mmio_enabled", - report_mmio_enabled); + if (status == PCI_ERS_RESULT_CAN_RECOVER) { + status = PCI_ERS_RESULT_RECOVERED; + pci_dbg(dev, "broadcast mmio_enabled message\n"); + pci_walk_bus(bus, report_mmio_enabled, &status); + } if (status == PCI_ERS_RESULT_NEED_RESET) { /* @@ -252,19 +224,16 @@ void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, * functions to reset slot before calling * drivers' slot_reset callbacks? */ - status = broadcast_error_message(dev, - state, - "slot_reset", - report_slot_reset); + status = PCI_ERS_RESULT_RECOVERED; + pci_dbg(dev, "broadcast slot_reset message\n"); + pci_walk_bus(bus, report_slot_reset, &status); } if (status != PCI_ERS_RESULT_RECOVERED) goto failed; - broadcast_error_message(dev, - state, - "resume", - report_resume); + pci_dbg(dev, "broadcast resume message\n"); + pci_walk_bus(bus, report_resume, &status); pci_aer_clear_device_status(dev); pci_cleanup_aer_uncorrect_error_status(dev); -- cgit v1.2.3 From 7b42d97e99d3a2babffd1b3456ded08b54981538 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:15 -0600 Subject: PCI/ERR: Always report current recovery status for udev A device still participates in error recovery even if it doesn't have the error callbacks. Always provide the status for user event watchers. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pcie/err.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 362a717c831a..31e8a4314384 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -70,9 +70,8 @@ static int report_error_detected(struct pci_dev *dev, } else { err_handler = dev->driver->err_handler; vote = err_handler->error_detected(dev, state); - pci_uevent_ers(dev, PCI_ERS_RESULT_NONE); } - + pci_uevent_ers(dev, vote); *result = merge_result(*result, vote); device_unlock(&dev->dev); return 0; @@ -140,8 +139,8 @@ static int report_resume(struct pci_dev *dev, void *data) err_handler = dev->driver->err_handler; err_handler->resume(dev); - pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED); out: + pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED); device_unlock(&dev->dev); return 0; } -- cgit v1.2.3 From a6bd101b8f84f9b98768e9ab1e418c239e2e669f Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:16 -0600 Subject: PCI: Unify device inaccessible Bring surprise removals and permanent failures together so we no longer need separate flags. The implementation enforces that error handling will not be able to override a surprise removal's permanent channel failure. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pci.h | 60 +++++++++++++++++++++++++++++++++++++++++++++----- drivers/pci/pcie/err.c | 10 ++++----- 2 files changed, 59 insertions(+), 11 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9b279805489f..eb3125decffe 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -295,21 +295,71 @@ struct pci_sriov { bool drivers_autoprobe; /* Auto probing of VFs by driver */ }; -/* pci_dev priv_flags */ -#define PCI_DEV_DISCONNECTED 0 -#define PCI_DEV_ADDED 1 +/** + * pci_dev_set_io_state - Set the new error state if possible. + * + * @dev - pci device to set new error_state + * @new - the state we want dev to be in + * + * Must be called with device_lock held. + * + * Returns true if state has been changed to the requested state. + */ +static inline bool pci_dev_set_io_state(struct pci_dev *dev, + pci_channel_state_t new) +{ + bool changed = false; + + device_lock_assert(&dev->dev); + switch (new) { + case pci_channel_io_perm_failure: + switch (dev->error_state) { + case pci_channel_io_frozen: + case pci_channel_io_normal: + case pci_channel_io_perm_failure: + changed = true; + break; + } + break; + case pci_channel_io_frozen: + switch (dev->error_state) { + case pci_channel_io_frozen: + case pci_channel_io_normal: + changed = true; + break; + } + break; + case pci_channel_io_normal: + switch (dev->error_state) { + case pci_channel_io_frozen: + case pci_channel_io_normal: + changed = true; + break; + } + break; + } + if (changed) + dev->error_state = new; + return changed; +} static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused) { - set_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags); + device_lock(&dev->dev); + pci_dev_set_io_state(dev, pci_channel_io_perm_failure); + device_unlock(&dev->dev); + return 0; } static inline bool pci_dev_is_disconnected(const struct pci_dev *dev) { - return test_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags); + return dev->error_state == pci_channel_io_perm_failure; } +/* pci_dev priv_flags */ +#define PCI_DEV_ADDED 0 + static inline void pci_dev_assign_added(struct pci_dev *dev, bool added) { assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added); diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 31e8a4314384..4da2a62b4f77 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -52,9 +52,8 @@ static int report_error_detected(struct pci_dev *dev, const struct pci_error_handlers *err_handler; device_lock(&dev->dev); - dev->error_state = state; - - if (!dev->driver || + if (!pci_dev_set_io_state(dev, state) || + !dev->driver || !dev->driver->err_handler || !dev->driver->err_handler->error_detected) { /* @@ -130,9 +129,8 @@ static int report_resume(struct pci_dev *dev, void *data) const struct pci_error_handlers *err_handler; device_lock(&dev->dev); - dev->error_state = pci_channel_io_normal; - - if (!dev->driver || + if (!pci_dev_set_io_state(dev, pci_channel_io_normal) || + !dev->driver || !dev->driver->err_handler || !dev->driver->err_handler->resume) goto out; -- cgit v1.2.3 From f0157160b359b1d263ee9d4e0a435a7ad85bbcea Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 20 Sep 2018 10:27:17 -0600 Subject: PCI: Make link active reporting detection generic The spec has timing requirements when waiting for a link to become active after a conventional reset. Implement those hard delays when waiting for an active link so pciehp and dpc drivers don't need to duplicate this. For devices that don't support data link layer active reporting, wait the fixed time recommended by the PCIe spec. Signed-off-by: Keith Busch [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/hotplug/pciehp.h | 6 ------ drivers/pci/hotplug/pciehp_hpc.c | 22 ++-------------------- drivers/pci/pci.c | 33 +++++++++++++++++++++++++++------ drivers/pci/pcie/dpc.c | 4 +++- drivers/pci/probe.c | 1 + include/linux/pci.h | 1 + 6 files changed, 34 insertions(+), 33 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 3740f1a759c5..75fd52571107 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -62,11 +62,6 @@ do { \ * struct controller - PCIe hotplug controller * @pcie: pointer to the controller's PCIe port service device * @slot_cap: cached copy of the Slot Capabilities register - * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting - * Capable bit in Link Capabilities register; if this bit is zero, the - * Data Link Layer Link Active bit in the Link Status register will never - * be set and the driver is thus confined to wait 1 second before assuming - * the link to a hotplugged device is up and accessing it * @slot_ctrl: cached copy of the Slot Control register * @ctrl_lock: serializes writes to the Slot Control register * @cmd_started: jiffies when the Slot Control register was last written; @@ -103,7 +98,6 @@ struct controller { struct pcie_device *pcie; u32 slot_cap; /* capabilities and quirks */ - unsigned int link_active_reporting:1; u16 slot_ctrl; /* control register access */ struct mutex ctrl_lock; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7b5f9db60d9a..f0f3f4a3dac4 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -214,13 +214,6 @@ bool pciehp_check_link_active(struct controller *ctrl) return ret; } -static void pcie_wait_link_active(struct controller *ctrl) -{ - struct pci_dev *pdev = ctrl_dev(ctrl); - - pcie_wait_for_link(pdev, true); -} - static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) { u32 l; @@ -253,18 +246,9 @@ int pciehp_check_link_status(struct controller *ctrl) bool found; u16 lnk_status; - /* - * Data Link Layer Link Active Reporting must be capable for - * hot-plug capable downstream port. But old controller might - * not implement it. In this case, we wait for 1000 ms. - */ - if (ctrl->link_active_reporting) - pcie_wait_link_active(ctrl); - else - msleep(1000); + if (!pcie_wait_for_link(pdev, true)) + return -1; - /* wait 100ms before read pci conf, and try in 1s */ - msleep(100); found = pci_bus_check_dev(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0)); @@ -865,8 +849,6 @@ struct controller *pcie_init(struct pcie_device *dev) /* Check if Data Link Layer Link Active Reporting is implemented */ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); - if (link_cap & PCI_EXP_LNKCAP_DLLLARC) - ctrl->link_active_reporting = 1; /* Clear all remaining event bits in Slot Status register. */ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6916af269b19..4b0b1d0548f0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4489,21 +4489,42 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active) bool ret; u16 lnk_status; + /* + * Some controllers might not implement link active reporting. In this + * case, we wait for 1000 + 100 ms. + */ + if (!pdev->link_active_reporting) { + msleep(1100); + return true; + } + + /* + * PCIe r4.0 sec 6.6.1, a component must enter LTSSM Detect within 20ms, + * after which we should expect an link active if the reset was + * successful. If so, software must wait a minimum 100ms before sending + * configuration requests to devices downstream this port. + * + * If the link fails to activate, either the device was physically + * removed or the link is permanently failed. + */ + if (active) + msleep(20); for (;;) { pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); if (ret == active) - return true; + break; if (timeout <= 0) break; msleep(10); timeout -= 10; } - - pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n", - active ? "set" : "cleared"); - - return false; + if (active && ret) + msleep(100); + else if (ret != active) + pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n", + active ? "set" : "cleared"); + return ret == active; } void pci_reset_secondary_bus(struct pci_dev *dev) diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index 23e063aefddf..e435d12e61a0 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -140,10 +140,12 @@ static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev) pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, PCI_EXP_DPC_STATUS_TRIGGER); + if (!pcie_wait_for_link(pdev, true)) + return PCI_ERS_RESULT_DISCONNECT; + return PCI_ERS_RESULT_RECOVERED; } - static void dpc_process_rp_pio_error(struct dpc_dev *dpc) { struct device *dev = &dpc->dev->device; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 201f9e5ff55c..bb2999d1b199 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -713,6 +713,7 @@ static void pci_set_bus_speed(struct pci_bus *bus) pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap); bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS]; + bridge->link_active_reporting = !!(linkcap & PCI_EXP_LNKCAP_DLLLARC); pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); pcie_update_link_speed(bus, linksta); diff --git a/include/linux/pci.h b/include/linux/pci.h index 6925828f9f25..896b42032ec5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -402,6 +402,7 @@ struct pci_dev { unsigned int has_secondary_link:1; unsigned int non_compliant_bars:1; /* Broken BARs; ignore them */ unsigned int is_probed:1; /* Device probing in progress */ + unsigned int link_active_reporting:1;/* Device capable of reporting link active */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ -- cgit v1.2.3 From ac86e8eeb0542d1089d8cba55884eeeb9410f027 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:53:53 -0500 Subject: PCI: Do not skip power-managed bridges in pci_enable_wake() Commit baecc470d5fd ("PCI / PM: Skip bridges in pci_enable_wake()") changed pci_enable_wake() so that all bridges are skipped when wakeup is enabled (or disabled) with the reasoning that bridges can only signal wakeup on behalf of their subordinate devices. However, there are bridges that can signal wakeup themselves. For example PCIe downstream and root ports supporting hotplug may signal wakeup upon hotplug event. For this reason change pci_enable_wake() so that it skips all bridges except those that we power manage (->bridge_d3 is set). Those are the ones that can go into low power states and may need to signal wakeup. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 4b0b1d0548f0..4a1b1f76dc92 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2137,10 +2137,13 @@ static int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable int ret = 0; /* - * Bridges can only signal wakeup on behalf of subordinate devices, - * but that is set up elsewhere, so skip them. + * Bridges that are not power-manageable directly only signal + * wakeup on behalf of subordinate devices which is set up + * elsewhere, so skip them. However, bridges that are + * power-manageable may signal wakeup for themselves (for example, + * on a hotplug event) and they need to be covered here. */ - if (pci_has_subordinate(dev)) + if (!pci_power_manageable(dev)) return 0; /* Don't do the same thing twice in a row for one device. */ -- cgit v1.2.3 From 6299cf9ec3985cac70bede8a855b5087b81a6640 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:54:13 -0500 Subject: PCI / ACPI: Enable wake automatically for power managed bridges We enable power management automatically for bridges where pci_bridge_d3_possible() returns true. However, these bridges may have ACPI methods such as _DSW that need to be called before D3 entry. For example in Lenovo Thinkpad X1 Carbon 6th _DSW method is used to prepare D3cold for the PCIe root port hosting Thunderbolt chain. Because wake is not enabled _DSW method is never called and the port does not enter D3cold properly consuming more power than necessary. Users can work this around by writing "enabled" to "wakeup" sysfs file under the device in question but that is not something an ordinary user is expected to do. Since we already automatically enable power management for PCIe ports with ->bridge_d3 set extend that to enable wake for them as well, assuming the port has any ACPI wakeup related objects implemented in the namespace (adev->wakeup.flags.valid is true). This ensures the necessary ACPI methods get called at appropriate times and allows the root port in Thinkpad X1 Carbon 6th to go into D3cold. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci-acpi.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index c2ab57705043..f8436d1c4d45 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -762,19 +762,33 @@ static void pci_acpi_setup(struct device *dev) return; device_set_wakeup_capable(dev, true); + /* + * For bridges that can do D3 we enable wake automatically (as + * we do for the power management itself in that case). The + * reason is that the bridge may have additional methods such as + * _DSW that need to be called. + */ + if (pci_dev->bridge_d3) + device_wakeup_enable(dev); + acpi_pci_wakeup(pci_dev, false); } static void pci_acpi_cleanup(struct device *dev) { struct acpi_device *adev = ACPI_COMPANION(dev); + struct pci_dev *pci_dev = to_pci_dev(dev); if (!adev) return; pci_acpi_remove_pm_notifier(adev); - if (adev->wakeup.flags.valid) + if (adev->wakeup.flags.valid) { + if (pci_dev->bridge_d3) + device_wakeup_disable(dev); + device_set_wakeup_capable(dev, false); + } } static bool pci_acpi_bus_match(struct device *dev) -- cgit v1.2.3 From eb34da60edee8cf7bac691a406a1ddaa4175e5bc Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:38:19 -0500 Subject: PCI: pciehp: Disable hotplug interrupt during suspend When PCIe hotplug port is transitioned into D3hot, the link to the downstream component will go down. If hotplug interrupt generation is enabled when that happens, it will trigger immediately, waking up the system and bringing the link back up. To prevent this, disable hotplug interrupt generation when system suspend is entered. This does not prevent wakeup from low power states according to PCIe 4.0 spec section 6.7.3.4: Software enables a hot-plug event to generate a wakeup event by enabling software notification of the event as described in Section 6.7.3.1. Note that in order for software to disable interrupt generation while keeping wakeup generation enabled, the Hot-Plug Interrupt Enable bit must be cleared. So as long as we have set the slot event mask accordingly, wakeup should work even if slot interrupt is disabled. The port should trigger wake and then send PME to the root port when the PCIe hierarchy is brought back up. Limit this to systems using native PME mechanism to make sure older Apple systems depending on commit e3354628c376 ("PCI: pciehp: Support interrupts sent from D3hot") still continue working. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/hotplug/pciehp.h | 2 ++ drivers/pci/hotplug/pciehp_core.c | 18 ++++++++++++++++++ drivers/pci/hotplug/pciehp_hpc.c | 10 ++++++++++ 3 files changed, 30 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 75fd52571107..506e1d923a1f 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -176,6 +176,8 @@ struct controller *pcie_init(struct pcie_device *dev); int pcie_init_notification(struct controller *ctrl); void pcie_shutdown_notification(struct controller *ctrl); void pcie_clear_hotplug_events(struct controller *ctrl); +void pcie_enable_interrupt(struct controller *ctrl); +void pcie_disable_interrupt(struct controller *ctrl); int pciehp_power_on_slot(struct controller *ctrl); void pciehp_power_off_slot(struct controller *ctrl); void pciehp_get_power_status(struct controller *ctrl, u8 *status); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 944047976569..df5ae02f4e12 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -242,8 +242,23 @@ static void pciehp_remove(struct pcie_device *dev) } #ifdef CONFIG_PM +static bool pme_is_native(struct pcie_device *dev) +{ + const struct pci_host_bridge *host; + + host = pci_find_host_bridge(dev->port->bus); + return pcie_ports_native || host->native_pme; +} + static int pciehp_suspend(struct pcie_device *dev) { + /* + * Disable hotplug interrupt so that it does not trigger + * immediately when the downstream link goes down. + */ + if (pme_is_native(dev)) + pcie_disable_interrupt(get_service_data(dev)); + return 0; } @@ -266,6 +281,9 @@ static int pciehp_resume(struct pcie_device *dev) { struct controller *ctrl = get_service_data(dev); + if (pme_is_native(dev)) + pcie_enable_interrupt(ctrl); + pciehp_check_presence(ctrl); return 0; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index f0f3f4a3dac4..46598b4b0b92 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -732,6 +732,16 @@ void pcie_clear_hotplug_events(struct controller *ctrl) PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); } +void pcie_enable_interrupt(struct controller *ctrl) +{ + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE); +} + +void pcie_disable_interrupt(struct controller *ctrl) +{ + pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE); +} + /* * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary * bus reset of the bridge, but at the same time we want to ensure that it is -- cgit v1.2.3 From 720d6a671a6e2cf2b9ed032279b7c21c122bed7e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:41:46 -0500 Subject: PCI: pciehp: Do not handle events if interrupts are masked PCIe native hotplug shares MSI vector with native PME so the interrupt handler might get called even the hotplug interrupt is masked. In that case we should not handle any events because the interrupt was not meant for us. Modify the PCIe hotplug interrupt handler to check this accordingly and bail out if it finds out that the interrupt was not about hotplug. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner --- drivers/pci/hotplug/pciehp_hpc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 46598b4b0b92..7dd443aea5a5 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -518,9 +518,11 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id) u16 status, events; /* - * Interrupts only occur in D3hot or shallower (PCIe r4.0, sec 6.7.3.4). + * Interrupts only occur in D3hot or shallower and only if enabled + * in the Slot Control register (PCIe r4.0, sec 6.7.3.4). */ - if (pdev->current_state == PCI_D3cold) + if (pdev->current_state == PCI_D3cold || + (!(ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE) && !pciehp_poll_mode)) return IRQ_NONE; /* -- cgit v1.2.3 From 52be9464aa7edeeda35f7faecb162412ddb47e94 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:41:47 -0500 Subject: PCI/portdrv: Resume upon exit from system suspend if left runtime suspended Currently we try to keep PCIe ports runtime suspended over system suspend if possible. This mostly happens when entering suspend-to-idle because there is no need to re-configure wake settings. This causes problems if the parent port goes into D3cold and it gets resumed upon exit from system suspend. This may happen for example if the port is part of PCIe switch and the same switch is connected to a PCIe endpoint that needs to be resumed. The way exit from D3cold works according PCIe 4.0 spec 5.3.1.4.2 is that power is restored and cold reset is signaled. After this the device is in D0unitialized state keeping PME context if it supports wake from D3cold. The problem occurs when a PCIe hotplug port is left suspended and the parent port goes into D3cold and back to D0: the port keeps its PME context but since everything else is reset back to defaults (D0unitialized) it is not set to detect hotplug events anymore. For this reason change the PCIe portdrv power management logic so that it is fine to keep the port runtime suspended over system suspend but it needs to be resumed upon exit to make sure it gets properly re-initialized. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 17256733fa43..a1379151c565 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -109,8 +109,8 @@ static int pcie_portdrv_probe(struct pci_dev *dev, pci_save_state(dev); - dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_SMART_SUSPEND | - DPM_FLAG_LEAVE_SUSPENDED); + dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NEVER_SKIP | + DPM_FLAG_SMART_SUSPEND); if (pci_bridge_d3_possible(dev)) { /* -- cgit v1.2.3 From 94c7993fb5bd1e3c20f67a2d24ba05bbdc938340 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:41:48 -0500 Subject: PCI/portdrv: Add runtime PM hooks for port service drivers When PCIe port is runtime suspended/resumed some extra steps might be needed to be executed from the port service driver side. For instance we may need to disable PCIe hotplug interrupt to prevent it from triggering immediately when PCIe link to the downstream component goes down. To make the above possible add optional ->runtime_suspend() and ->runtime_resume() callbacks to struct pcie_port_service_driver and call them for each port service in runtime suspend/resume callbacks of portdrv. Signed-off-by: Mika Westerberg [bhelgaas: adjust "slot->state" for 5790a9c78e78 ("PCI: pciehp: Unify controller and slot structs")] Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pcie/portdrv.h | 4 ++++ drivers/pci/pcie/portdrv_core.c | 20 ++++++++++++++++++++ drivers/pci/pcie/portdrv_pci.c | 10 ++++------ 3 files changed, 28 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 2498b2d34009..abfdc2ae7979 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -76,6 +76,8 @@ struct pcie_port_service_driver { int (*suspend) (struct pcie_device *dev); int (*resume_noirq) (struct pcie_device *dev); int (*resume) (struct pcie_device *dev); + int (*runtime_suspend) (struct pcie_device *dev); + int (*runtime_resume) (struct pcie_device *dev); /* Device driver may resume normal operations */ void (*error_resume)(struct pci_dev *dev); @@ -109,6 +111,8 @@ int pcie_port_device_register(struct pci_dev *dev); int pcie_port_device_suspend(struct device *dev); int pcie_port_device_resume_noirq(struct device *dev); int pcie_port_device_resume(struct device *dev); +int pcie_port_device_runtime_suspend(struct device *dev); +int pcie_port_device_runtime_resume(struct device *dev); #endif void pcie_port_device_remove(struct pci_dev *dev); int __must_check pcie_port_bus_register(void); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 7c37d815229e..6542c48c7f59 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -395,6 +395,26 @@ int pcie_port_device_resume(struct device *dev) size_t off = offsetof(struct pcie_port_service_driver, resume); return device_for_each_child(dev, &off, pm_iter); } + +/** + * pcie_port_device_runtime_suspend - runtime suspend port services + * @dev: PCI Express port to handle + */ +int pcie_port_device_runtime_suspend(struct device *dev) +{ + size_t off = offsetof(struct pcie_port_service_driver, runtime_suspend); + return device_for_each_child(dev, &off, pm_iter); +} + +/** + * pcie_port_device_runtime_resume - runtime resume port services + * @dev: PCI Express port to handle + */ +int pcie_port_device_runtime_resume(struct device *dev) +{ + size_t off = offsetof(struct pcie_port_service_driver, runtime_resume); + return device_for_each_child(dev, &off, pm_iter); +} #endif /* PM */ static int remove_iter(struct device *dev, void *data) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index a1379151c565..0acca3596807 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -45,12 +45,10 @@ __setup("pcie_ports=", pcie_port_setup); #ifdef CONFIG_PM static int pcie_port_runtime_suspend(struct device *dev) { - return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY; -} + if (!to_pci_dev(dev)->bridge_d3) + return -EBUSY; -static int pcie_port_runtime_resume(struct device *dev) -{ - return 0; + return pcie_port_device_runtime_suspend(dev); } static int pcie_port_runtime_idle(struct device *dev) @@ -73,7 +71,7 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .restore_noirq = pcie_port_device_resume_noirq, .restore = pcie_port_device_resume, .runtime_suspend = pcie_port_runtime_suspend, - .runtime_resume = pcie_port_runtime_resume, + .runtime_resume = pcie_port_device_runtime_resume, .runtime_idle = pcie_port_runtime_idle, }; -- cgit v1.2.3 From 9c62f0bfb83260cb2469eb84d3185c9728e36900 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:41:49 -0500 Subject: PCI: pciehp: Implement runtime PM callbacks Basically we need to do the same thing when runtime suspending than with system sleep so re-use those operations here. This makes sure hotplug interrupt does not trigger immediately when the link goes down. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/hotplug/pciehp_core.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index df5ae02f4e12..fc5366b50e95 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -288,6 +288,22 @@ static int pciehp_resume(struct pcie_device *dev) return 0; } + +static int pciehp_runtime_resume(struct pcie_device *dev) +{ + struct controller *ctrl = get_service_data(dev); + + /* pci_restore_state() just wrote to the Slot Control register */ + ctrl->cmd_started = jiffies; + ctrl->cmd_busy = true; + + /* clear spurious events from rediscovery of inserted card */ + if ((ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) && + pme_is_native(dev)) + pcie_clear_hotplug_events(ctrl); + + return pciehp_resume(dev); +} #endif /* PM */ static struct pcie_port_service_driver hpdriver_portdrv = { @@ -302,6 +318,8 @@ static struct pcie_port_service_driver hpdriver_portdrv = { .suspend = pciehp_suspend, .resume_noirq = pciehp_resume_noirq, .resume = pciehp_resume, + .runtime_suspend = pciehp_suspend, + .runtime_resume = pciehp_runtime_resume, #endif /* PM */ }; -- cgit v1.2.3 From 0e157e52860441cb26051f131dd0b5ae3187a07b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:56:49 -0500 Subject: PCI/PME: Implement runtime PM callbacks Basically we need to do the same steps than what we do when system sleep is entered and disable PME interrupt when the root port is runtime suspended. This prevents spurious wakeups immediately when the port is transitioned into D3cold. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pcie/pme.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 1a8b85051b1b..0dbcf429089f 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -432,6 +432,31 @@ static void pcie_pme_remove(struct pcie_device *srv) kfree(get_service_data(srv)); } +static int pcie_pme_runtime_suspend(struct pcie_device *srv) +{ + struct pcie_pme_service_data *data = get_service_data(srv); + + spin_lock_irq(&data->lock); + pcie_pme_interrupt_enable(srv->port, false); + pcie_clear_root_pme_status(srv->port); + data->noirq = true; + spin_unlock_irq(&data->lock); + + return 0; +} + +static int pcie_pme_runtime_resume(struct pcie_device *srv) +{ + struct pcie_pme_service_data *data = get_service_data(srv); + + spin_lock_irq(&data->lock); + pcie_pme_interrupt_enable(srv->port, true); + data->noirq = false; + spin_unlock_irq(&data->lock); + + return 0; +} + static struct pcie_port_service_driver pcie_pme_driver = { .name = "pcie_pme", .port_type = PCI_EXP_TYPE_ROOT_PORT, @@ -439,6 +464,8 @@ static struct pcie_port_service_driver pcie_pme_driver = { .probe = pcie_pme_probe, .suspend = pcie_pme_suspend, + .runtime_suspend = pcie_pme_runtime_suspend, + .runtime_resume = pcie_pme_runtime_resume, .resume = pcie_pme_resume, .remove = pcie_pme_remove, }; -- cgit v1.2.3 From 26ad34d510a87fc65caeb48fa85cce58d2477a88 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:57:14 -0500 Subject: PCI / ACPI: Whitelist D3 for more PCIe hotplug ports In order to have better power management for Thunderbolt PCIe chains, Windows enables power management for native PCIe hotplug ports if there is the following ACPI _DSD attached to the root port: Name (_DSD, Package () { ToUUID ("6211e2c0-58a3-4af3-90e1-927a4e0c55a4"), Package () { Package () {"HotPlugSupportInD3", 1} } }) This is also documented in: https://docs.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports#identifying-pcie-root-ports-supporting-hot-plug-in-d3 Do the same in Linux by introducing new firmware PM callback (->bridge_d3()) and then implement it for ACPI based systems so that the above property is checked. There is one catch, though. The initial pci_dev->bridge_d3 is set before the root port has ACPI companion bound (the device is not added to the PCI bus either) so we need to look up the ACPI companion manually in that case in acpi_pci_bridge_d3(). Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/acpi/property.c | 3 +++ drivers/pci/pci-acpi.c | 41 +++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.c | 9 +++++++++ drivers/pci/pci.h | 3 +++ 4 files changed, 56 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 90ba9371bae6..8c7c4583b52d 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -28,6 +28,9 @@ static const guid_t prp_guids[] = { /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c, 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01), + /* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */ + GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3, + 0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4), }; static const guid_t ads_guid = diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index f8436d1c4d45..c8d0549580f4 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -519,6 +519,46 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) return PCI_POWER_ERROR; } +static struct acpi_device *acpi_pci_find_companion(struct device *dev); + +static bool acpi_pci_bridge_d3(struct pci_dev *dev) +{ + const struct fwnode_handle *fwnode; + struct acpi_device *adev; + struct pci_dev *root; + u8 val; + + if (!dev->is_hotplug_bridge) + return false; + + /* + * Look for a special _DSD property for the root port and if it + * is set we know the hierarchy behind it supports D3 just fine. + */ + root = pci_find_pcie_root_port(dev); + if (!root) + return false; + + adev = ACPI_COMPANION(&root->dev); + if (root == dev) { + /* + * It is possible that the ACPI companion is not yet bound + * for the root port so look it up manually here. + */ + if (!adev && !pci_dev_is_added(root)) + adev = acpi_pci_find_companion(&root->dev); + } + + if (!adev) + return false; + + fwnode = acpi_fwnode_handle(adev); + if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val)) + return false; + + return val == 1; +} + static bool acpi_pci_power_manageable(struct pci_dev *dev) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); @@ -635,6 +675,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev) } static const struct pci_platform_pm_ops acpi_pci_platform_pm = { + .bridge_d3 = acpi_pci_bridge_d3, .is_manageable = acpi_pci_power_manageable, .set_state = acpi_pci_set_power_state, .get_state = acpi_pci_get_power_state, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 4a1b1f76dc92..e6fcf11f5dcc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -793,6 +793,11 @@ static inline bool platform_pci_need_resume(struct pci_dev *dev) return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; } +static inline bool platform_pci_bridge_d3(struct pci_dev *dev) +{ + return pci_platform_pm ? pci_platform_pm->bridge_d3(dev) : false; +} + /** * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device @@ -2518,6 +2523,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) if (bridge->is_thunderbolt) return true; + /* Platform might know better if the bridge supports D3 */ + if (platform_pci_bridge_d3(bridge)) + return true; + /* * Hotplug ports handled natively by the OS were not validated * by vendors for runtime D3 at least until 2018 because there diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index eb3125decffe..672ba4d1659e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -40,6 +40,8 @@ int pci_bus_error_reset(struct pci_dev *dev); /** * struct pci_platform_pm_ops - Firmware PM callbacks * + * @bridge_d3: Does the bridge allow entering into D3 + * * @is_manageable: returns 'true' if given device is power manageable by the * platform firmware * @@ -61,6 +63,7 @@ int pci_bus_error_reset(struct pci_dev *dev); * these callbacks are mandatory. */ struct pci_platform_pm_ops { + bool (*bridge_d3)(struct pci_dev *dev); bool (*is_manageable)(struct pci_dev *dev); int (*set_state)(struct pci_dev *dev, pci_power_t state); pci_power_t (*get_state)(struct pci_dev *dev); -- cgit v1.2.3 From de3ffa301142bf8802a7b0de17f9985acde5c223 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Tue, 25 Sep 2018 12:39:06 -0600 Subject: PCI: Equalize hotplug memory and io for occupied and empty slots Currently, a hotplug bridge will be given hpmemsize additional memory and hpiosize additional io if available, in order to satisfy any future hotplug allocation requirements. These calculations don't consider the current memory/io size of the hotplug bridge/slot, so hotplug bridges/slots which have downstream devices will be allocated their current allocation in addition to the hpmemsize value. This makes for possibly undesirable results with a mix of unoccupied and occupied slots (ex, with hpmemsize=2M): 02:03.0 PCI bridge: <-- Occupied Memory behind bridge: d6200000-d64fffff [size=3M] 02:04.0 PCI bridge: <-- Unoccupied Memory behind bridge: d6500000-d66fffff [size=2M] This change considers the current allocation size when using the hpmemsize/hpiosize parameters to make the reservations predictable for the mix of unoccupied and occupied slots: 02:03.0 PCI bridge: <-- Occupied Memory behind bridge: d6200000-d63fffff [size=2M] 02:04.0 PCI bridge: <-- Unoccupied Memory behind bridge: d6400000-d65fffff [size=2M] Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 79b1824e83b4..ed960436df5e 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -811,6 +811,8 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, static resource_size_t calculate_iosize(resource_size_t size, resource_size_t min_size, resource_size_t size1, + resource_size_t add_size, + resource_size_t children_add_size, resource_size_t old_size, resource_size_t align) { @@ -823,15 +825,18 @@ static resource_size_t calculate_iosize(resource_size_t size, #if defined(CONFIG_ISA) || defined(CONFIG_EISA) size = (size & 0xff) + ((size & ~0xffUL) << 2); #endif - size = ALIGN(size + size1, align); + size = size + size1; if (size < old_size) size = old_size; + + size = ALIGN(max(size, add_size) + children_add_size, align); return size; } static resource_size_t calculate_memsize(resource_size_t size, resource_size_t min_size, - resource_size_t size1, + resource_size_t add_size, + resource_size_t children_add_size, resource_size_t old_size, resource_size_t align) { @@ -841,7 +846,8 @@ static resource_size_t calculate_memsize(resource_size_t size, old_size = 0; if (size < old_size) size = old_size; - size = ALIGN(size + size1, align); + + size = ALIGN(max(size, add_size) + children_add_size, align); return size; } @@ -930,12 +936,10 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, } } - size0 = calculate_iosize(size, min_size, size1, + size0 = calculate_iosize(size, min_size, size1, 0, 0, resource_size(b_res), min_align); - if (children_add_size > add_size) - add_size = children_add_size; - size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 : - calculate_iosize(size, min_size, add_size + size1, + size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 : + calculate_iosize(size, min_size, size1, add_size, children_add_size, resource_size(b_res), min_align); if (!size0 && !size1) { if (b_res->start || b_res->end) @@ -1079,12 +1083,10 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, min_align = calculate_mem_align(aligns, max_order); min_align = max(min_align, window_alignment(bus, b_res->flags)); - size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align); + size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align); add_align = max(min_align, add_align); - if (children_add_size > add_size) - add_size = children_add_size; - size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 : - calculate_memsize(size, min_size, add_size, + size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 : + calculate_memsize(size, min_size, add_size, children_add_size, resource_size(b_res), add_align); if (!size0 && !size1) { if (b_res->start || b_res->end) -- cgit v1.2.3 From 479e01a402f006746324a04a72bd949ceca5e73d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 26 Sep 2018 11:00:10 +0000 Subject: PCI/ERR: Remove duplicated include from err.c Remove duplicated include. Signed-off-by: YueHaibing Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/err.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 4da2a62b4f77..773197a12568 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From 37f1c5868e457386950e7e27212bfdcd9f77f302 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 26 Sep 2018 11:06:02 +0000 Subject: PCI: cpqphp: Remove set but not used variable 'physical_slot' Fixes gcc '-Wunused-but-set-variable' warning: drivers/pci/hotplug/cpqphp_core.c: In function 'init_SERR': drivers/pci/hotplug/cpqphp_core.c:124:5: warning: variable 'physical_slot' set but not used [-Wunused-but-set-variable] Signed-off-by: YueHaibing Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpqphp_core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 95b7d60cf119..16bbb183695a 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -121,7 +121,6 @@ static int init_SERR(struct controller *ctrl) { u32 tempdword; u32 number_of_slots; - u8 physical_slot; if (!ctrl) return 1; @@ -131,7 +130,6 @@ static int init_SERR(struct controller *ctrl) number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; /* Loop through slots */ while (number_of_slots) { - physical_slot = tempdword; writeb(0, ctrl->hpc_reg + SLOT_SERR); tempdword++; number_of_slots--; -- cgit v1.2.3 From 74171e9dab62bd9ab616c1a8f9f1c13af71f4f5d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 27 Sep 2018 06:52:21 +0000 Subject: PCI: pnv_php: Use kmemdup() Use kmemdup() rather than duplicating its implementation. Signed-off-by: YueHaibing Signed-off-by: Bjorn Helgaas Acked-by: Michael Ellerman --- drivers/pci/hotplug/pnv_php.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index 5070620a4f9f..ee54f5bacad1 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -275,14 +275,13 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) goto free_fdt1; } - fdt = kzalloc(fdt_totalsize(fdt1), GFP_KERNEL); + fdt = kmemdup(fdt1, fdt_totalsize(fdt1), GFP_KERNEL); if (!fdt) { ret = -ENOMEM; goto free_fdt1; } /* Unflatten device tree blob */ - memcpy(fdt, fdt1, fdt_totalsize(fdt1)); dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL); if (!dt) { ret = -EINVAL; -- cgit v1.2.3 From 5180fd913558825e910e255d879232c63aaa5c24 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 18 Sep 2018 17:58:37 -0600 Subject: PCI: Uninline PCI bus accessors for better ftracing The PCI bus config accessors could be inlined into other accessor functions, which makes it so they can't be traced. Force them to never be inlined so that ftrace can hook into these functions. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index a3ad2fe185b9..544922f097c0 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -33,7 +33,7 @@ DEFINE_RAW_SPINLOCK(pci_lock); #endif #define PCI_OP_READ(size, type, len) \ -int pci_bus_read_config_##size \ +int noinline pci_bus_read_config_##size \ (struct pci_bus *bus, unsigned int devfn, int pos, type *value) \ { \ int res; \ @@ -48,7 +48,7 @@ int pci_bus_read_config_##size \ } #define PCI_OP_WRITE(size, type, len) \ -int pci_bus_write_config_##size \ +int noinline pci_bus_write_config_##size \ (struct pci_bus *bus, unsigned int devfn, int pos, type value) \ { \ int res; \ -- cgit v1.2.3 From 3e41a317ae456bbd7ae08d03746024ec29a7bf31 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 18 Sep 2018 17:58:41 -0600 Subject: PCI/AER: Remove unused aer_error_resume() The error recovery callbacks are only run on child devices. A Root Port is never a child device, so this error resume callback was never invoked. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 0619ec5d7bb5..a41738ff33e1 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1547,18 +1547,6 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED; } -/** - * aer_error_resume - clean up corresponding error status bits - * @dev: pointer to Root Port's pci_dev data structure - * - * Invoked by Port Bus driver during nonfatal recovery. - */ -static void aer_error_resume(struct pci_dev *dev) -{ - pci_aer_clear_device_status(dev); - pci_cleanup_aer_uncorrect_error_status(dev); -} - static struct pcie_port_service_driver aerdriver = { .name = "aer", .port_type = PCI_EXP_TYPE_ROOT_PORT, @@ -1566,7 +1554,6 @@ static struct pcie_port_service_driver aerdriver = { .probe = aer_probe, .remove = aer_remove, - .error_resume = aer_error_resume, .reset_link = aer_root_reset, }; -- cgit v1.2.3 From fcd4d369034a819aa393f65c3a8f58db9ab5ed2a Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 18 Sep 2018 17:58:42 -0600 Subject: PCI/AER: Remove error source from AER struct aer_rpc The AER struct aer_rpc was carrying a copy of the error source simply as a temperary variable. Remove that from the structure and use a stack variable for the purpose. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index a41738ff33e1..85c713f6cfdb 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -44,7 +44,6 @@ struct aer_rpc { struct pci_dev *rpd; /* Root Port device */ struct work_struct dpc_handler; struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX]; - struct aer_err_info e_info; unsigned short prod_idx; /* Error Producer Index */ unsigned short cons_idx; /* Error Consumer Index */ int isr; @@ -1176,7 +1175,7 @@ static void aer_isr_one_error(struct aer_rpc *rpc, struct aer_err_source *e_src) { struct pci_dev *pdev = rpc->rpd; - struct aer_err_info *e_info = &rpc->e_info; + struct aer_err_info e_info; pci_rootport_aer_stats_incr(pdev, e_src); @@ -1185,36 +1184,36 @@ static void aer_isr_one_error(struct aer_rpc *rpc, * uncorrectable error being logged. Report correctable error first. */ if (e_src->status & PCI_ERR_ROOT_COR_RCV) { - e_info->id = ERR_COR_ID(e_src->id); - e_info->severity = AER_CORRECTABLE; + e_info.id = ERR_COR_ID(e_src->id); + e_info.severity = AER_CORRECTABLE; if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV) - e_info->multi_error_valid = 1; + e_info.multi_error_valid = 1; else - e_info->multi_error_valid = 0; - aer_print_port_info(pdev, e_info); + e_info.multi_error_valid = 0; + aer_print_port_info(pdev, &e_info); - if (find_source_device(pdev, e_info)) - aer_process_err_devices(e_info); + if (find_source_device(pdev, &e_info)) + aer_process_err_devices(&e_info); } if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) { - e_info->id = ERR_UNCOR_ID(e_src->id); + e_info.id = ERR_UNCOR_ID(e_src->id); if (e_src->status & PCI_ERR_ROOT_FATAL_RCV) - e_info->severity = AER_FATAL; + e_info.severity = AER_FATAL; else - e_info->severity = AER_NONFATAL; + e_info.severity = AER_NONFATAL; if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV) - e_info->multi_error_valid = 1; + e_info.multi_error_valid = 1; else - e_info->multi_error_valid = 0; + e_info.multi_error_valid = 0; - aer_print_port_info(pdev, e_info); + aer_print_port_info(pdev, &e_info); - if (find_source_device(pdev, e_info)) - aer_process_err_devices(e_info); + if (find_source_device(pdev, &e_info)) + aer_process_err_devices(&e_info); } } -- cgit v1.2.3 From 27c1ce8bbed7e7f0e4a87cf4a93f09be26d62ada Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 18 Sep 2018 17:58:43 -0600 Subject: PCI/AER: Use kfifo for tracking events instead of reimplementing it The kernel provides a generic FIFO implementation, so no need to reinvent that capability in a driver. Replace the AER-specific implementation with the kernel-provided kfifo. Since the interrupt handler producer and work queue consumer run single threaded, there is no need for additional locking, so remove that lock, too. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 88 +++++++------------------------------------------- 1 file changed, 11 insertions(+), 77 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 85c713f6cfdb..122a78197172 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -30,7 +30,7 @@ #include "../pci.h" #include "portdrv.h" -#define AER_ERROR_SOURCES_MAX 100 +#define AER_ERROR_SOURCES_MAX 128 #define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */ #define AER_MAX_TYPEOF_UNCOR_ERRS 26 /* as per PCI_ERR_UNCOR_STATUS*/ @@ -43,14 +43,8 @@ struct aer_err_source { struct aer_rpc { struct pci_dev *rpd; /* Root Port device */ struct work_struct dpc_handler; - struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX]; - unsigned short prod_idx; /* Error Producer Index */ - unsigned short cons_idx; /* Error Consumer Index */ + DECLARE_KFIFO(aer_fifo, struct aer_err_source, AER_ERROR_SOURCES_MAX); int isr; - spinlock_t e_lock; /* - * Lock access to Error Status/ID Regs - * and error producer/consumer index - */ struct mutex rpc_mutex; /* * only one thread could do * recovery on the same @@ -1217,35 +1211,6 @@ static void aer_isr_one_error(struct aer_rpc *rpc, } } -/** - * get_e_source - retrieve an error source - * @rpc: pointer to the root port which holds an error - * @e_src: pointer to store retrieved error source - * - * Return 1 if an error source is retrieved, otherwise 0. - * - * Invoked by DPC handler to consume an error. - */ -static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src) -{ - unsigned long flags; - - /* Lock access to Root error producer/consumer index */ - spin_lock_irqsave(&rpc->e_lock, flags); - if (rpc->prod_idx == rpc->cons_idx) { - spin_unlock_irqrestore(&rpc->e_lock, flags); - return 0; - } - - *e_src = rpc->e_sources[rpc->cons_idx]; - rpc->cons_idx++; - if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) - rpc->cons_idx = 0; - spin_unlock_irqrestore(&rpc->e_lock, flags); - - return 1; -} - /** * aer_isr - consume errors detected by root port * @work: definition of this work item @@ -1258,7 +1223,7 @@ static void aer_isr(struct work_struct *work) struct aer_err_source uninitialized_var(e_src); mutex_lock(&rpc->rpc_mutex); - while (get_e_source(rpc, &e_src)) + while (kfifo_get(&rpc->aer_fifo, &e_src)) aer_isr_one_error(rpc, &e_src); mutex_unlock(&rpc->rpc_mutex); } @@ -1272,51 +1237,23 @@ static void aer_isr(struct work_struct *work) */ irqreturn_t aer_irq(int irq, void *context) { - unsigned int status, id; struct pcie_device *pdev = (struct pcie_device *)context; struct aer_rpc *rpc = get_service_data(pdev); - int next_prod_idx; - unsigned long flags; - int pos; - - pos = pdev->port->aer_cap; - /* - * Must lock access to Root Error Status Reg, Root Error ID Reg, - * and Root error producer/consumer index - */ - spin_lock_irqsave(&rpc->e_lock, flags); + struct pci_dev *rp = rpc->rpd; + struct aer_err_source e_src = {}; + int pos = rp->aer_cap; - /* Read error status */ - pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); - if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) { - spin_unlock_irqrestore(&rpc->e_lock, flags); + pci_read_config_dword(rp, pos + PCI_ERR_ROOT_STATUS, &e_src.status); + if (!(e_src.status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) return IRQ_NONE; - } - /* Read error source and clear error status */ - pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id); - pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); + pci_read_config_dword(rp, pos + PCI_ERR_ROOT_ERR_SRC, &e_src.id); + pci_write_config_dword(rp, pos + PCI_ERR_ROOT_STATUS, e_src.status); - /* Store error source for later DPC handler */ - next_prod_idx = rpc->prod_idx + 1; - if (next_prod_idx == AER_ERROR_SOURCES_MAX) - next_prod_idx = 0; - if (next_prod_idx == rpc->cons_idx) { - /* - * Error Storm Condition - possibly the same error occurred. - * Drop the error. - */ - spin_unlock_irqrestore(&rpc->e_lock, flags); + if (!kfifo_put(&rpc->aer_fifo, e_src)) return IRQ_HANDLED; - } - rpc->e_sources[rpc->prod_idx].status = status; - rpc->e_sources[rpc->prod_idx].id = id; - rpc->prod_idx = next_prod_idx; - spin_unlock_irqrestore(&rpc->e_lock, flags); - /* Invoke DPC handler */ schedule_work(&rpc->dpc_handler); - return IRQ_HANDLED; } EXPORT_SYMBOL_GPL(aer_irq); @@ -1441,9 +1378,6 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) if (!rpc) return NULL; - /* Initialize Root lock access, e_lock, to Root Error Status Reg */ - spin_lock_init(&rpc->e_lock); - rpc->rpd = dev->port; INIT_WORK(&rpc->dpc_handler, aer_isr); mutex_init(&rpc->rpc_mutex); -- cgit v1.2.3 From ecae65e133f2e0647e6364d691130ff551382d91 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 18 Sep 2018 17:58:44 -0600 Subject: PCI/AER: Use kfifo_in_spinlocked() to insert locked elements Use the recommended kernel API for writing to a concurrently-accessed kfifo. No functional change here. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 122a78197172..f70ee6dfb79e 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1063,7 +1063,6 @@ static DECLARE_WORK(aer_recover_work, aer_recover_work_func); void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, int severity, struct aer_capability_regs *aer_regs) { - unsigned long flags; struct aer_recover_entry entry = { .bus = bus, .devfn = devfn, @@ -1072,13 +1071,12 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, .regs = aer_regs, }; - spin_lock_irqsave(&aer_recover_ring_lock, flags); - if (kfifo_put(&aer_recover_ring, entry)) + if (kfifo_in_spinlocked(&aer_recover_ring, &entry, sizeof(entry), + &aer_recover_ring_lock)) schedule_work(&aer_recover_work); else pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n", domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - spin_unlock_irqrestore(&aer_recover_ring_lock, flags); } EXPORT_SYMBOL_GPL(aer_recover_queue); #endif -- cgit v1.2.3 From 6200cc5ee2baa573e7ac4dbcfca750e0b777c37d Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 18 Sep 2018 17:58:46 -0600 Subject: PCI/AER: Use threaded IRQ for bottom half The threaded IRQ is naturally single threaded as desired, so use that to simplify the AER bottom half handler. Since the root port structure has much less to do now, remove the rpc construction helper routine. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 60 +++++++++++--------------------------------------- 1 file changed, 13 insertions(+), 47 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index f70ee6dfb79e..638d0cbc704e 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -42,14 +42,7 @@ struct aer_err_source { struct aer_rpc { struct pci_dev *rpd; /* Root Port device */ - struct work_struct dpc_handler; DECLARE_KFIFO(aer_fifo, struct aer_err_source, AER_ERROR_SOURCES_MAX); - int isr; - struct mutex rpc_mutex; /* - * only one thread could do - * recovery on the same - * root port hierarchy - */ }; /* AER stats for the device */ @@ -1215,15 +1208,18 @@ static void aer_isr_one_error(struct aer_rpc *rpc, * * Invoked, as DPC, when root port records new detected error */ -static void aer_isr(struct work_struct *work) +static irqreturn_t aer_isr(int irq, void *context) { - struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); + struct pcie_device *dev = (struct pcie_device *)context; + struct aer_rpc *rpc = get_service_data(dev); struct aer_err_source uninitialized_var(e_src); - mutex_lock(&rpc->rpc_mutex); + if (kfifo_is_empty(&rpc->aer_fifo)) + return IRQ_NONE; + while (kfifo_get(&rpc->aer_fifo, &e_src)) aer_isr_one_error(rpc, &e_src); - mutex_unlock(&rpc->rpc_mutex); + return IRQ_HANDLED; } /** @@ -1251,8 +1247,7 @@ irqreturn_t aer_irq(int irq, void *context) if (!kfifo_put(&rpc->aer_fifo, e_src)) return IRQ_HANDLED; - schedule_work(&rpc->dpc_handler); - return IRQ_HANDLED; + return IRQ_WAKE_THREAD; } EXPORT_SYMBOL_GPL(aer_irq); @@ -1362,30 +1357,6 @@ static void aer_disable_rootport(struct aer_rpc *rpc) pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); } -/** - * aer_alloc_rpc - allocate Root Port data structure - * @dev: pointer to the pcie_dev data structure - * - * Invoked when Root Port's AER service is loaded. - */ -static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) -{ - struct aer_rpc *rpc; - - rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL); - if (!rpc) - return NULL; - - rpc->rpd = dev->port; - INIT_WORK(&rpc->dpc_handler, aer_isr); - mutex_init(&rpc->rpc_mutex); - - /* Use PCIe bus function to store rpc into PCIe device */ - set_service_data(dev, rpc); - - return rpc; -} - /** * aer_remove - clean up resources * @dev: pointer to the pcie_dev data structure @@ -1397,11 +1368,6 @@ static void aer_remove(struct pcie_device *dev) struct aer_rpc *rpc = get_service_data(dev); if (rpc) { - /* If register interrupt service, it must be free. */ - if (rpc->isr) - free_irq(dev->irq, dev); - - flush_work(&rpc->dpc_handler); aer_disable_rootport(rpc); kfree(rpc); set_service_data(dev, NULL); @@ -1421,15 +1387,17 @@ static int aer_probe(struct pcie_device *dev) struct device *device = &dev->port->dev; /* Alloc rpc data structure */ - rpc = aer_alloc_rpc(dev); + rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL); if (!rpc) { dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n"); - aer_remove(dev); return -ENOMEM; } + rpc->rpd = dev->port; + set_service_data(dev, rpc); /* Request IRQ ISR */ - status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev); + status = request_threaded_irq(dev->irq, aer_irq, aer_isr, + IRQF_SHARED, "aerdrv", dev); if (status) { dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n", dev->irq); @@ -1437,8 +1405,6 @@ static int aer_probe(struct pcie_device *dev) return status; } - rpc->isr = 1; - aer_enable_rootport(rpc); dev_info(device, "AER enabled with IRQ %d\n", dev->irq); return 0; -- cgit v1.2.3 From 369fd7b00fce169570d6a74cb369e60dbfc95fb4 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 18 Sep 2018 17:58:47 -0600 Subject: PCI/AER: Use managed resource allocations Use the managed device resource allocations for the service data so the AER driver doesn't need to manage it, further simplifying this driver. Link: https://lore.kernel.org/linux-pci/20180918235848.26694-12-keith.busch@intel.com Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 638d0cbc704e..90b53abf621d 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1367,11 +1367,7 @@ static void aer_remove(struct pcie_device *dev) { struct aer_rpc *rpc = get_service_data(dev); - if (rpc) { - aer_disable_rootport(rpc); - kfree(rpc); - set_service_data(dev, NULL); - } + aer_disable_rootport(rpc); } /** @@ -1384,10 +1380,9 @@ static int aer_probe(struct pcie_device *dev) { int status; struct aer_rpc *rpc; - struct device *device = &dev->port->dev; + struct device *device = &dev->device; - /* Alloc rpc data structure */ - rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL); + rpc = devm_kzalloc(device, sizeof(struct aer_rpc), GFP_KERNEL); if (!rpc) { dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n"); return -ENOMEM; @@ -1395,13 +1390,11 @@ static int aer_probe(struct pcie_device *dev) rpc->rpd = dev->port; set_service_data(dev, rpc); - /* Request IRQ ISR */ - status = request_threaded_irq(dev->irq, aer_irq, aer_isr, - IRQF_SHARED, "aerdrv", dev); + status = devm_request_threaded_irq(device, dev->irq, aer_irq, aer_isr, + IRQF_SHARED, "aerdrv", dev); if (status) { dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n", dev->irq); - aer_remove(dev); return status; } -- cgit v1.2.3 From 0e98db259fd8760fde556e640b447dadeceefc96 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 11 Oct 2018 12:34:10 -0600 Subject: PCI/AER: Reuse existing pcie_port_find_device() interface The port services driver already provides a method to find the pcie_device for a service. Export that function, use it from the aer_inject module, and remove the duplicate functionality. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer_inject.c | 25 ++++--------------------- drivers/pci/pcie/portdrv_core.c | 1 + 2 files changed, 5 insertions(+), 21 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c index 0eb24346cad3..f40ed5867c89 100644 --- a/drivers/pci/pcie/aer_inject.c +++ b/drivers/pci/pcie/aer_inject.c @@ -303,32 +303,13 @@ out: return 0; } -static int find_aer_device_iter(struct device *device, void *data) -{ - struct pcie_device **result = data; - struct pcie_device *pcie_dev; - - if (device->bus == &pcie_port_bus_type) { - pcie_dev = to_pcie_device(device); - if (pcie_dev->service & PCIE_PORT_SERVICE_AER) { - *result = pcie_dev; - return 1; - } - } - return 0; -} - -static int find_aer_device(struct pci_dev *dev, struct pcie_device **result) -{ - return device_for_each_child(&dev->dev, result, find_aer_device_iter); -} - static int aer_inject(struct aer_error_inj *einj) { struct aer_error *err, *rperr; struct aer_error *err_alloc = NULL, *rperr_alloc = NULL; struct pci_dev *dev, *rpdev; struct pcie_device *edev; + struct device *device; unsigned long flags; unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn); int pos_cap_err, rp_pos_cap_err; @@ -464,7 +445,9 @@ static int aer_inject(struct aer_error_inj *einj) if (ret) goto out_put; - if (find_aer_device(rpdev, &edev)) { + device = pcie_port_find_device(rpdev, PCIE_PORT_SERVICE_AER); + if (device) { + edev = to_pcie_device(device); if (!get_service_data(edev)) { dev_warn(&edev->device, "aer_inject: AER service is not initialized\n"); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 6542c48c7f59..f458ac9cb70c 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -486,6 +486,7 @@ struct device *pcie_port_find_device(struct pci_dev *dev, device = pdrvs.dev; return device; } +EXPORT_SYMBOL_GPL(pcie_port_find_device); /** * pcie_port_device_remove - unregister PCI Express port service devices -- cgit v1.2.3 From 390e2db8248075ae2f31a7046a88eda0f9784310 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 11 Oct 2018 12:34:11 -0600 Subject: PCI/AER: Abstract AER interrupt handling The aer_inject module was directly calling aer_irq(). This required the AER driver export its private IRQ handler for no other reason than to support error injection. A driver should not have to expose its private interfaces, so use the IRQ subsystem to route injection to the AER driver, and make aer_irq() a private interface. This provides additional benefits: First, directly calling the IRQ handler bypassed the IRQ subsytem so the injection wasn't really synthesizing what happens if a shared AER interrupt occurs. The error injection had to provide the callback data directly, which may be racing with a removal that is freeing that structure. The IRQ subsystem can handle that race. Finally, using the IRQ subsystem automatically reacts to threaded IRQs, keeping the error injection abstracted from that implementation detail. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 3 +-- drivers/pci/pcie/aer_inject.c | 5 ++++- drivers/pci/pcie/portdrv.h | 4 ---- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 90b53abf621d..a90a9194ac4a 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1229,7 +1229,7 @@ static irqreturn_t aer_isr(int irq, void *context) * * Invoked when Root Port detects AER messages. */ -irqreturn_t aer_irq(int irq, void *context) +static irqreturn_t aer_irq(int irq, void *context) { struct pcie_device *pdev = (struct pcie_device *)context; struct aer_rpc *rpc = get_service_data(pdev); @@ -1249,7 +1249,6 @@ irqreturn_t aer_irq(int irq, void *context) return IRQ_WAKE_THREAD; } -EXPORT_SYMBOL_GPL(aer_irq); static int set_device_error_reporting(struct pci_dev *dev, void *data) { diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c index f40ed5867c89..726987f8d53c 100644 --- a/drivers/pci/pcie/aer_inject.c +++ b/drivers/pci/pcie/aer_inject.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -457,7 +458,9 @@ static int aer_inject(struct aer_error_inj *einj) dev_info(&edev->device, "aer_inject: Injecting errors %08x/%08x into device %s\n", einj->cor_status, einj->uncor_status, pci_name(dev)); - aer_irq(-1, edev); + local_irq_disable(); + generic_handle_irq(edev->irq); + local_irq_enable(); } else { pci_err(rpdev, "aer_inject: AER device not found\n"); ret = -ENODEV; diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index abfdc2ae7979..e495f04394d0 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -151,10 +151,6 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev) } #endif -#ifdef CONFIG_PCIEAER -irqreturn_t aer_irq(int irq, void *context); -#endif - struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev, u32 service); struct device *pcie_port_find_device(struct pci_dev *dev, u32 service); -- cgit v1.2.3 From e51cd9ce5dd3b10f9e67a30a4dc00fc1fa80c673 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Thu, 11 Oct 2018 12:34:12 -0600 Subject: PCI/AER: Refactor error injection fallbacks Move the bus ops fallback into separate functions. No functional change here. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer_inject.c | 66 +++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 28 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c index 726987f8d53c..95d4759664b3 100644 --- a/drivers/pci/pcie/aer_inject.c +++ b/drivers/pci/pcie/aer_inject.c @@ -176,14 +176,48 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where, return target; } +static int aer_inj_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *val) +{ + struct pci_ops *ops, *my_ops; + int rv; + + ops = __find_pci_bus_ops(bus); + if (!ops) + return -1; + + my_ops = bus->ops; + bus->ops = ops; + rv = ops->read(bus, devfn, where, size, val); + bus->ops = my_ops; + + return rv; +} + +static int aer_inj_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 val) +{ + struct pci_ops *ops, *my_ops; + int rv; + + ops = __find_pci_bus_ops(bus); + if (!ops) + return -1; + + my_ops = bus->ops; + bus->ops = ops; + rv = ops->write(bus, devfn, where, size, val); + bus->ops = my_ops; + + return rv; +} + static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { u32 *sim; struct aer_error *err; unsigned long flags; - struct pci_ops *ops; - struct pci_ops *my_ops; int domain; int rv; @@ -204,18 +238,7 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, return 0; } out: - ops = __find_pci_bus_ops(bus); - /* - * pci_lock must already be held, so we can directly - * manipulate bus->ops. Many config access functions, - * including pci_generic_config_read() require the original - * bus->ops be installed to function, so temporarily put them - * back. - */ - my_ops = bus->ops; - bus->ops = ops; - rv = ops->read(bus, devfn, where, size, val); - bus->ops = my_ops; + rv = aer_inj_read(bus, devfn, where, size, val); spin_unlock_irqrestore(&inject_lock, flags); return rv; } @@ -227,8 +250,6 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, struct aer_error *err; unsigned long flags; int rw1cs; - struct pci_ops *ops; - struct pci_ops *my_ops; int domain; int rv; @@ -252,18 +273,7 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, return 0; } out: - ops = __find_pci_bus_ops(bus); - /* - * pci_lock must already be held, so we can directly - * manipulate bus->ops. Many config access functions, - * including pci_generic_config_write() require the original - * bus->ops be installed to function, so temporarily put them - * back. - */ - my_ops = bus->ops; - bus->ops = ops; - rv = ops->write(bus, devfn, where, size, val); - bus->ops = my_ops; + rv = aer_inj_write(bus, devfn, where, size, val); spin_unlock_irqrestore(&inject_lock, flags); return rv; } -- cgit v1.2.3