aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/hotplug
diff options
context:
space:
mode:
authorLukas Wunner <lukas@wunner.de>2018-07-31 01:50:37 -0400
committerBjorn Helgaas <bhelgaas@google.com>2018-09-17 17:34:35 -0400
commit11e87702be65780be92fb1f0a5b7b293954185f7 (patch)
treeaef305785ff65cbad47c5323f97645b6fcd3e51e /drivers/pci/hotplug
parenta50ac6bfd6042b16e0de4ac3264c407e678c9b10 (diff)
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 <mr.nuke.me@gmail.com> Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Cc: Keith Busch <keith.busch@intel.com>
Diffstat (limited to 'drivers/pci/hotplug')
-rw-r--r--drivers/pci/hotplug/pciehp.h2
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c22
-rw-r--r--drivers/pci/hotplug/pciehp_pci.c23
3 files changed, 34 insertions, 13 deletions
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);
181void pciehp_handle_disable_request(struct slot *slot); 181void pciehp_handle_disable_request(struct slot *slot);
182void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events); 182void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events);
183int pciehp_configure_device(struct slot *p_slot); 183int pciehp_configure_device(struct slot *p_slot);
184void pciehp_unconfigure_device(struct slot *p_slot); 184void pciehp_unconfigure_device(struct slot *p_slot, bool presence);
185void pciehp_queue_pushbutton_work(struct work_struct *work); 185void pciehp_queue_pushbutton_work(struct work_struct *work);
186struct controller *pcie_init(struct pcie_device *dev); 186struct controller *pcie_init(struct pcie_device *dev);
187int pcie_init_notification(struct controller *ctrl); 187int 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 @@
26 hotplug controller logic 26 hotplug controller logic
27 */ 27 */
28 28
29#define SAFE_REMOVAL true
30#define SURPRISE_REMOVAL false
31
29static void set_slot_off(struct controller *ctrl, struct slot *pslot) 32static void set_slot_off(struct controller *ctrl, struct slot *pslot)
30{ 33{
31 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ 34 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
@@ -101,12 +104,13 @@ err_exit:
101/** 104/**
102 * remove_board - Turns off slot and LEDs 105 * remove_board - Turns off slot and LEDs
103 * @p_slot: slot where board is being removed 106 * @p_slot: slot where board is being removed
107 * @safe_removal: whether the board is safely removed (versus surprise removed)
104 */ 108 */
105static void remove_board(struct slot *p_slot) 109static void remove_board(struct slot *p_slot, bool safe_removal)
106{ 110{
107 struct controller *ctrl = p_slot->ctrl; 111 struct controller *ctrl = p_slot->ctrl;
108 112
109 pciehp_unconfigure_device(p_slot); 113 pciehp_unconfigure_device(p_slot, safe_removal);
110 114
111 if (POWER_CTRL(ctrl)) { 115 if (POWER_CTRL(ctrl)) {
112 pciehp_power_off_slot(p_slot); 116 pciehp_power_off_slot(p_slot);
@@ -124,7 +128,7 @@ static void remove_board(struct slot *p_slot)
124} 128}
125 129
126static int pciehp_enable_slot(struct slot *slot); 130static int pciehp_enable_slot(struct slot *slot);
127static int pciehp_disable_slot(struct slot *slot); 131static int pciehp_disable_slot(struct slot *slot, bool safe_removal);
128 132
129void pciehp_request(struct controller *ctrl, int action) 133void pciehp_request(struct controller *ctrl, int action)
130{ 134{
@@ -216,7 +220,7 @@ void pciehp_handle_disable_request(struct slot *slot)
216 slot->state = POWEROFF_STATE; 220 slot->state = POWEROFF_STATE;
217 mutex_unlock(&slot->lock); 221 mutex_unlock(&slot->lock);
218 222
219 ctrl->request_result = pciehp_disable_slot(slot); 223 ctrl->request_result = pciehp_disable_slot(slot, SAFE_REMOVAL);
220} 224}
221 225
222void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) 226void 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)
243 if (events & PCI_EXP_SLTSTA_PDC) 247 if (events & PCI_EXP_SLTSTA_PDC)
244 ctrl_info(ctrl, "Slot(%s): Card not present\n", 248 ctrl_info(ctrl, "Slot(%s): Card not present\n",
245 slot_name(slot)); 249 slot_name(slot));
246 pciehp_disable_slot(slot); 250 pciehp_disable_slot(slot, SURPRISE_REMOVAL);
247 break; 251 break;
248 default: 252 default:
249 mutex_unlock(&slot->lock); 253 mutex_unlock(&slot->lock);
@@ -329,7 +333,7 @@ static int pciehp_enable_slot(struct slot *slot)
329 return ret; 333 return ret;
330} 334}
331 335
332static int __pciehp_disable_slot(struct slot *p_slot) 336static int __pciehp_disable_slot(struct slot *p_slot, bool safe_removal)
333{ 337{
334 u8 getstatus = 0; 338 u8 getstatus = 0;
335 struct controller *ctrl = p_slot->ctrl; 339 struct controller *ctrl = p_slot->ctrl;
@@ -343,17 +347,17 @@ static int __pciehp_disable_slot(struct slot *p_slot)
343 } 347 }
344 } 348 }
345 349
346 remove_board(p_slot); 350 remove_board(p_slot, safe_removal);
347 return 0; 351 return 0;
348} 352}
349 353
350static int pciehp_disable_slot(struct slot *slot) 354static int pciehp_disable_slot(struct slot *slot, bool safe_removal)
351{ 355{
352 struct controller *ctrl = slot->ctrl; 356 struct controller *ctrl = slot->ctrl;
353 int ret; 357 int ret;
354 358
355 pm_runtime_get_sync(&ctrl->pcie->port->dev); 359 pm_runtime_get_sync(&ctrl->pcie->port->dev);
356 ret = __pciehp_disable_slot(slot); 360 ret = __pciehp_disable_slot(slot, safe_removal);
357 pm_runtime_put(&ctrl->pcie->port->dev); 361 pm_runtime_put(&ctrl->pcie->port->dev);
358 362
359 mutex_lock(&slot->lock); 363 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 @@
20#include "../pci.h" 20#include "../pci.h"
21#include "pciehp.h" 21#include "pciehp.h"
22 22
23/**
24 * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge
25 * @p_slot: PCIe hotplug slot
26 *
27 * Enumerate PCI devices below a hotplug bridge and add them to the system.
28 * Return 0 on success, %-EEXIST if the devices are already enumerated or
29 * %-ENODEV if enumeration failed.
30 */
23int pciehp_configure_device(struct slot *p_slot) 31int pciehp_configure_device(struct slot *p_slot)
24{ 32{
25 struct pci_dev *dev; 33 struct pci_dev *dev;
@@ -62,9 +70,19 @@ int pciehp_configure_device(struct slot *p_slot)
62 return ret; 70 return ret;
63} 71}
64 72
65void pciehp_unconfigure_device(struct slot *p_slot) 73/**
74 * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge
75 * @p_slot: PCIe hotplug slot
76 * @presence: whether the card is still present in the slot;
77 * true for safe removal via sysfs or an Attention Button press,
78 * false for surprise removal
79 *
80 * Unbind PCI devices below a hotplug bridge from their drivers and remove
81 * them from the system. Safely removed devices are quiesced. Surprise
82 * removed devices are marked as such to prevent further accesses.
83 */
84void pciehp_unconfigure_device(struct slot *p_slot, bool presence)
66{ 85{
67 u8 presence = 0;
68 struct pci_dev *dev, *temp; 86 struct pci_dev *dev, *temp;
69 struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; 87 struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
70 u16 command; 88 u16 command;
@@ -72,7 +90,6 @@ void pciehp_unconfigure_device(struct slot *p_slot)
72 90
73 ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", 91 ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
74 __func__, pci_domain_nr(parent), parent->number); 92 __func__, pci_domain_nr(parent), parent->number);
75 pciehp_get_adapter_status(p_slot, &presence);
76 93
77 if (!presence) 94 if (!presence)
78 pci_walk_bus(parent, pci_dev_set_disconnected, NULL); 95 pci_walk_bus(parent, pci_dev_set_disconnected, NULL);