@@ -44,6 +44,7 @@ void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type)
info = kmalloc(sizeof(*info), GFP_ATOMIC);
if (!info) {
ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type);
+ slot_being_removed_rescanned = 0;
return;
}
@@ -188,6 +189,7 @@ static void pciehp_power_thread(struct work_struct *work)
mutex_lock(&p_slot->lock);
p_slot->state = STATIC_STATE;
mutex_unlock(&p_slot->lock);
+ slot_being_removed_rescanned = 0;
break;
case ENABLE_REQ:
mutex_lock(&p_slot->hotplug_lock);
@@ -198,6 +200,7 @@ static void pciehp_power_thread(struct work_struct *work)
mutex_lock(&p_slot->lock);
p_slot->state = STATIC_STATE;
mutex_unlock(&p_slot->lock);
+ slot_being_removed_rescanned = 0;
break;
default:
break;
@@ -216,6 +219,7 @@ static void pciehp_queue_power_work(struct slot *p_slot, int req)
if (!info) {
ctrl_err(p_slot->ctrl, "no memory to queue %s request\n",
(req == ENABLE_REQ) ? "poweron" : "poweroff");
+ slot_being_removed_rescanned = 0;
return;
}
info->p_slot = p_slot;
@@ -284,6 +288,7 @@ static void handle_button_press_event(struct slot *p_slot)
ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
slot_name(p_slot));
p_slot->state = STATIC_STATE;
+ slot_being_removed_rescanned = 0;
break;
case POWEROFF_STATE:
case POWERON_STATE:
@@ -294,10 +299,12 @@ static void handle_button_press_event(struct slot *p_slot)
*/
ctrl_info(ctrl, "Slot(%s): Button ignored\n",
slot_name(p_slot));
+ slot_being_removed_rescanned = 0;
break;
default:
ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
slot_name(p_slot), p_slot->state);
+ slot_being_removed_rescanned = 0;
break;
}
}
@@ -622,7 +622,17 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
if (events & PCI_EXP_SLTSTA_ABP) {
ctrl_info(ctrl, "Slot(%s): Attention button pressed\n",
slot_name(slot));
- pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
+
+ if (!test_and_set_bit(0, &slot_being_removed_rescanned))
+ pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
+ else {
+ if (slot->state == BLINKINGOFF_STATE || slot->state == BLINKINGON_STATE)
+ pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
+ else
+ ctrl_info(ctrl, "Slot(%s): Slot operation failed because a remove or"
+ " rescan operation is under processing, please try later!\n",
+ slot_name(slot));
+ }
}
/*
@@ -489,8 +489,15 @@ static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
if (kstrtoul(buf, 0, &val) < 0)
return -EINVAL;
- if (val && device_remove_file_self(dev, attr))
- pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
+ if (val && device_remove_file_self(dev, attr)) {
+ if (!test_and_set_bit(0, &slot_being_removed_rescanned)) {
+ pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
+ slot_being_removed_rescanned = 0;
+ } else {
+ pr_info("Slot is being removed or rescanned, please try later!\n");
+ return -EPERM;
+ }
+ }
return count;
}
static struct device_attribute dev_remove_attr = __ATTR(remove,
@@ -3,6 +3,12 @@
#include <linux/pci-aspm.h>
#include "pci.h"
+/*
+ * When a slot is being hotplug through Attention Button or being
+ * removed/rescanned through sysfs, this flag is set.
+ */
+unsigned long slot_being_removed_rescanned;
+
static void pci_free_resources(struct pci_dev *dev)
{
int i;
@@ -854,6 +854,9 @@ enum pcie_bus_config_types {
/* Do NOT directly access these two variables, unless you are arch-specific PCI
* code, or PCI core code. */
extern struct list_head pci_root_buses; /* list of all known PCI buses */
+
+extern unsigned long slot_being_removed_rescanned;
+
/* Some device drivers need know if PCI is initiated */
int no_pci_devices(void);