]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
PCI: pciehp: Track enable/disable status
authorLukas Wunner <lukas@wunner.de>
Thu, 19 Jul 2018 22:27:45 +0000 (17:27 -0500)
committerBjorn Helgaas <helgaas@kernel.org>
Mon, 23 Jul 2018 22:04:14 +0000 (17:04 -0500)
handle_button_press_event() currently determines whether the slot has
been turned on or off by looking at the Power Controller Control bit in
the Slot Control register.  This assumes that an attention button
implies presence of a power controller even though that's not mandated
by the spec.  Moreover the Power Controller Control bit is unreliable
when a power fault occurs (PCIe r4.0, sec 6.7.1.8).  This issue has
existed since the driver was introduced in 2004.

Fix by replacing STATIC_STATE with ON_STATE and OFF_STATE and tracking
whether the slot has been turned on or off.  This is also a required
ingredient to make pciehp resilient to missed events, which is the
object of an upcoming commit.

Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_ctrl.c
drivers/pci/hotplug/pciehp_hpc.c

index 0d005c5fabfaad8f6b6b116c08b2122f81d1092c..1ba335d6563a75df82dc10efad8959a055a3acdd 100644 (file)
@@ -122,11 +122,24 @@ struct controller {
        atomic_t pending_events;
 };
 
-#define STATIC_STATE                   0
+/**
+ * DOC: Slot state
+ *
+ * @OFF_STATE: slot is powered off, no subordinate devices are enumerated
+ * @BLINKINGON_STATE: slot will be powered on after the 5 second delay,
+ *     green led is blinking
+ * @BLINKINGOFF_STATE: slot will be powered off after the 5 second delay,
+ *     green led is blinking
+ * @POWERON_STATE: slot is currently powering on
+ * @POWEROFF_STATE: slot is currently powering off
+ * @ON_STATE: slot is powered on, subordinate devices have been enumerated
+ */
+#define OFF_STATE                      0
 #define BLINKINGON_STATE               1
 #define BLINKINGOFF_STATE              2
 #define POWERON_STATE                  3
 #define POWEROFF_STATE                 4
+#define ON_STATE                       5
 
 #define ATTN_BUTTN(ctrl)       ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP)
 #define POWER_CTRL(ctrl)       ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP)
index a4a8a5457aca87e0f15acb004f18f9153c8b24a0..627e846df8028f73b1fbdadc8dbbbe4e2cad0e61 100644 (file)
@@ -147,13 +147,12 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
 void pciehp_handle_button_press(struct slot *p_slot)
 {
        struct controller *ctrl = p_slot->ctrl;
-       u8 getstatus;
 
        mutex_lock(&p_slot->lock);
        switch (p_slot->state) {
-       case STATIC_STATE:
-               pciehp_get_power_status(p_slot, &getstatus);
-               if (getstatus) {
+       case OFF_STATE:
+       case ON_STATE:
+               if (p_slot->state == ON_STATE) {
                        p_slot->state = BLINKINGOFF_STATE;
                        ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
                                  slot_name(p_slot));
@@ -176,14 +175,16 @@ void pciehp_handle_button_press(struct slot *p_slot)
                 */
                ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
                cancel_delayed_work(&p_slot->work);
-               if (p_slot->state == BLINKINGOFF_STATE)
+               if (p_slot->state == BLINKINGOFF_STATE) {
+                       p_slot->state = ON_STATE;
                        pciehp_green_led_on(p_slot);
-               else
+               } else {
+                       p_slot->state = OFF_STATE;
                        pciehp_green_led_off(p_slot);
+               }
                pciehp_set_attention_status(p_slot, 0);
                ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
                          slot_name(p_slot));
-               p_slot->state = STATIC_STATE;
                break;
        case POWEROFF_STATE:
        case POWERON_STATE:
@@ -216,7 +217,8 @@ void pciehp_handle_link_change(struct slot *p_slot)
        case BLINKINGOFF_STATE:
                cancel_delayed_work(&p_slot->work);
                /* Fall through */
-       case STATIC_STATE:
+       case ON_STATE:
+       case OFF_STATE:
                if (link_active) {
                        p_slot->state = POWERON_STATE;
                        mutex_unlock(&p_slot->lock);
@@ -332,7 +334,7 @@ int pciehp_enable_slot(struct slot *slot)
                pciehp_green_led_off(slot); /* may be blinking */
 
        mutex_lock(&slot->lock);
-       slot->state = STATIC_STATE;
+       slot->state = ret ? OFF_STATE : ON_STATE;
        mutex_unlock(&slot->lock);
 
        return ret;
@@ -368,7 +370,7 @@ int pciehp_disable_slot(struct slot *slot)
        mutex_unlock(&slot->hotplug_lock);
 
        mutex_lock(&slot->lock);
-       slot->state = STATIC_STATE;
+       slot->state = OFF_STATE;
        mutex_unlock(&slot->lock);
 
        return ret;
@@ -382,7 +384,7 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
        switch (p_slot->state) {
        case BLINKINGON_STATE:
                cancel_delayed_work(&p_slot->work);
-       case STATIC_STATE:
+       case OFF_STATE:
                p_slot->state = POWERON_STATE;
                mutex_unlock(&p_slot->lock);
                return pciehp_enable_slot(p_slot);
@@ -391,6 +393,7 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
                          slot_name(p_slot));
                break;
        case BLINKINGOFF_STATE:
+       case ON_STATE:
        case POWEROFF_STATE:
                ctrl_info(ctrl, "Slot(%s): Already enabled\n",
                          slot_name(p_slot));
@@ -413,7 +416,7 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
        switch (p_slot->state) {
        case BLINKINGOFF_STATE:
                cancel_delayed_work(&p_slot->work);
-       case STATIC_STATE:
+       case ON_STATE:
                p_slot->state = POWEROFF_STATE;
                mutex_unlock(&p_slot->lock);
                return pciehp_disable_slot(p_slot);
@@ -422,6 +425,7 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
                          slot_name(p_slot));
                break;
        case BLINKINGON_STATE:
+       case OFF_STATE:
        case POWERON_STATE:
                ctrl_info(ctrl, "Slot(%s): Already disabled\n",
                          slot_name(p_slot));
index dcbdee50cd850f1754b7223583ba4d3697f4f5c7..9c6a18da1af51c533bf34b7785e72ea10de6c80f 100644 (file)
@@ -761,12 +761,17 @@ 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);
        mutex_init(&slot->hotplug_lock);