pciehp - add missing locking
authorKenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Fri, 22 Sep 2006 17:17:29 +0000 (10:17 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 18 Oct 2006 18:36:10 +0000 (11:36 -0700)
This patch fixes the problem that system will panic if multiple power
on/off operations are issued to the same slot in parallel. This
problem can be easily reproduced by commands below.

    # while true; do echo 1 > power; echo 0 > power; done &
    # while true; do echo 1 > power; echo 0 > power; done &

The cause is lack of locking for enable/disable operations. This patch
fixes this problem.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_ctrl.c
drivers/pci/hotplug/pciehp_hpc.c

index b71f774..30f021c 100644 (file)
@@ -92,6 +92,7 @@ struct php_ctlr_state_s {
 struct controller {
        struct controller *next;
        struct mutex crit_sect;         /* critical section mutex */
+       struct mutex ctrl_lock;         /* controller lock */
        struct php_ctlr_state_s *hpc_ctlr_handle; /* HPC controller handle */
        int num_slots;                  /* Number of slots on ctlr */
        int slot_num_inc;               /* 1 or -1 */
index c67b7c3..f93e81e 100644 (file)
@@ -448,7 +448,7 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
        }
 
        /* Wait for exclusive access to hardware */
-       mutex_lock(&ctrl->crit_sect);
+       mutex_lock(&ctrl->ctrl_lock);
 
        t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
        
@@ -456,7 +456,7 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
                rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
                if (rc) {
                        /* Done with exclusive hardware access */
-                       mutex_unlock(&ctrl->crit_sect);
+                       mutex_unlock(&ctrl->ctrl_lock);
                        goto err_out_free_ctrl_slot;
                } else
                        /* Wait for the command to complete */
@@ -464,7 +464,7 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
        }
 
        /* Done with exclusive hardware access */
-       mutex_unlock(&ctrl->crit_sect);
+       mutex_unlock(&ctrl->ctrl_lock);
 
        return 0;
 
index f602b04..c206a3d 100644 (file)
@@ -234,13 +234,13 @@ u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id)
 static void set_slot_off(struct controller *ctrl, struct slot * pslot)
 {
        /* Wait for exclusive access to hardware */
-       mutex_lock(&ctrl->crit_sect);
+       mutex_lock(&ctrl->ctrl_lock);
 
        /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
        if (POWER_CTRL(ctrl->ctrlcap)) {
                if (pslot->hpc_ops->power_off_slot(pslot)) {   
                        err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__);
-                       mutex_unlock(&ctrl->crit_sect);
+                       mutex_unlock(&ctrl->ctrl_lock);
                        return;
                }
                wait_for_ctrl_irq (ctrl);
@@ -254,14 +254,14 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot)
        if (ATTN_LED(ctrl->ctrlcap)) { 
                if (pslot->hpc_ops->set_attention_status(pslot, 1)) {   
                        err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__);
-                       mutex_unlock(&ctrl->crit_sect);
+                       mutex_unlock(&ctrl->ctrl_lock);
                        return;
                }
                wait_for_ctrl_irq (ctrl);
        }
 
        /* Done with exclusive hardware access */
-       mutex_unlock(&ctrl->crit_sect);
+       mutex_unlock(&ctrl->ctrl_lock);
 }
 
 /**
@@ -284,13 +284,13 @@ static int board_added(struct slot *p_slot)
                        ctrl->slot_device_offset, hp_slot);
 
        /* Wait for exclusive access to hardware */
-       mutex_lock(&ctrl->crit_sect);
+       mutex_lock(&ctrl->ctrl_lock);
 
        if (POWER_CTRL(ctrl->ctrlcap)) {
                /* Power on slot */
                rc = p_slot->hpc_ops->power_on_slot(p_slot);
                if (rc) {
-                       mutex_unlock(&ctrl->crit_sect);
+                       mutex_unlock(&ctrl->ctrl_lock);
                        return -1;
                }
 
@@ -306,7 +306,7 @@ static int board_added(struct slot *p_slot)
        }
 
        /* Done with exclusive hardware access */
-       mutex_unlock(&ctrl->crit_sect);
+       mutex_unlock(&ctrl->ctrl_lock);
 
        /* Wait for ~1 second */
        wait_for_ctrl_irq (ctrl);
@@ -340,7 +340,7 @@ static int board_added(struct slot *p_slot)
                pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
        if (PWR_LED(ctrl->ctrlcap)) {
                /* Wait for exclusive access to hardware */
-               mutex_lock(&ctrl->crit_sect);
+               mutex_lock(&ctrl->ctrl_lock);
 
                p_slot->hpc_ops->green_led_on(p_slot);
   
@@ -348,7 +348,7 @@ static int board_added(struct slot *p_slot)
                wait_for_ctrl_irq (ctrl);
        
                /* Done with exclusive hardware access */
-               mutex_unlock(&ctrl->crit_sect);
+               mutex_unlock(&ctrl->ctrl_lock);
        }
        return 0;
 
@@ -380,14 +380,14 @@ static int remove_board(struct slot *p_slot)
        dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
 
        /* Wait for exclusive access to hardware */
-       mutex_lock(&ctrl->crit_sect);
+       mutex_lock(&ctrl->ctrl_lock);
 
        if (POWER_CTRL(ctrl->ctrlcap)) {
                /* power off slot */
                rc = p_slot->hpc_ops->power_off_slot(p_slot);
                if (rc) {
                        err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
-                       mutex_unlock(&ctrl->crit_sect);
+                       mutex_unlock(&ctrl->ctrl_lock);
                        return rc;
                }
                /* Wait for the command to complete */
@@ -403,7 +403,7 @@ static int remove_board(struct slot *p_slot)
        }
 
        /* Done with exclusive hardware access */
-       mutex_unlock(&ctrl->crit_sect);
+       mutex_unlock(&ctrl->ctrl_lock);
 
        return 0;
 }
@@ -450,7 +450,7 @@ static void pciehp_pushbutton_thread(unsigned long slot)
 
                if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) {
                        /* Wait for exclusive access to hardware */
-                       mutex_lock(&p_slot->ctrl->crit_sect);
+                       mutex_lock(&p_slot->ctrl->ctrl_lock);
 
                        p_slot->hpc_ops->green_led_off(p_slot);
 
@@ -458,7 +458,7 @@ static void pciehp_pushbutton_thread(unsigned long slot)
                        wait_for_ctrl_irq (p_slot->ctrl);
 
                        /* Done with exclusive hardware access */
-                       mutex_unlock(&p_slot->ctrl->crit_sect);
+                       mutex_unlock(&p_slot->ctrl->ctrl_lock);
                }
                p_slot->state = STATIC_STATE;
        }
@@ -500,7 +500,7 @@ static void pciehp_surprise_rm_thread(unsigned long slot)
 
                if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) {
                        /* Wait for exclusive access to hardware */
-                       mutex_lock(&p_slot->ctrl->crit_sect);
+                       mutex_lock(&p_slot->ctrl->ctrl_lock);
 
                        p_slot->hpc_ops->green_led_off(p_slot);
 
@@ -508,7 +508,7 @@ static void pciehp_surprise_rm_thread(unsigned long slot)
                        wait_for_ctrl_irq (p_slot->ctrl);
 
                        /* Done with exclusive hardware access */
-                       mutex_unlock(&p_slot->ctrl->crit_sect);
+                       mutex_unlock(&p_slot->ctrl->ctrl_lock);
                }
                p_slot->state = STATIC_STATE;
        }
@@ -621,7 +621,7 @@ static void interrupt_event_handler(struct controller *ctrl)
                                        switch (p_slot->state) {
                                        case BLINKINGOFF_STATE:
                                                /* Wait for exclusive access to hardware */
-                                               mutex_lock(&ctrl->crit_sect);
+                                               mutex_lock(&ctrl->ctrl_lock);
                                                
                                                if (PWR_LED(ctrl->ctrlcap)) {
                                                        p_slot->hpc_ops->green_led_on(p_slot);
@@ -635,11 +635,11 @@ static void interrupt_event_handler(struct controller *ctrl)
                                                        wait_for_ctrl_irq (ctrl);
                                                }
                                                /* Done with exclusive hardware access */
-                                               mutex_unlock(&ctrl->crit_sect);
+                                               mutex_unlock(&ctrl->ctrl_lock);
                                                break;
                                        case BLINKINGON_STATE:
                                                /* Wait for exclusive access to hardware */
-                                               mutex_lock(&ctrl->crit_sect);
+                                               mutex_lock(&ctrl->ctrl_lock);
 
                                                if (PWR_LED(ctrl->ctrlcap)) {
                                                        p_slot->hpc_ops->green_led_off(p_slot);
@@ -652,7 +652,7 @@ static void interrupt_event_handler(struct controller *ctrl)
                                                        wait_for_ctrl_irq (ctrl);
                                                }
                                                /* Done with exclusive hardware access */
-                                               mutex_unlock(&ctrl->crit_sect);
+                                               mutex_unlock(&ctrl->ctrl_lock);
 
                                                break;
                                        default:
@@ -681,7 +681,7 @@ static void interrupt_event_handler(struct controller *ctrl)
                                                }
 
                                                /* Wait for exclusive access to hardware */
-                                               mutex_lock(&ctrl->crit_sect);
+                                               mutex_lock(&ctrl->ctrl_lock);
 
                                                /* blink green LED and turn off amber */
                                                if (PWR_LED(ctrl->ctrlcap)) {
@@ -698,7 +698,7 @@ static void interrupt_event_handler(struct controller *ctrl)
                                                }
 
                                                /* Done with exclusive hardware access */
-                                               mutex_unlock(&ctrl->crit_sect);
+                                               mutex_unlock(&ctrl->ctrl_lock);
 
                                                init_timer(&p_slot->task_event);
                                                p_slot->task_event.expires = jiffies + 5 * HZ;   /* 5 second delay */
@@ -713,7 +713,7 @@ static void interrupt_event_handler(struct controller *ctrl)
                                        if (POWER_CTRL(ctrl->ctrlcap)) {
                                                dbg("power fault\n");
                                                /* Wait for exclusive access to hardware */
-                                               mutex_lock(&ctrl->crit_sect);
+                                               mutex_lock(&ctrl->ctrl_lock);
 
                                                if (ATTN_LED(ctrl->ctrlcap)) {
                                                        p_slot->hpc_ops->set_attention_status(p_slot, 1);
@@ -726,7 +726,7 @@ static void interrupt_event_handler(struct controller *ctrl)
                                                }
 
                                                /* Done with exclusive hardware access */
-                                               mutex_unlock(&ctrl->crit_sect);
+                                               mutex_unlock(&ctrl->ctrl_lock);
                                        }
                                }
                                /***********SURPRISE REMOVAL********************/
@@ -789,7 +789,6 @@ int pciehp_enable_slot(struct slot *p_slot)
                        return -EINVAL;
                }
        }
-       mutex_unlock(&p_slot->ctrl->crit_sect);
 
        p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
 
@@ -801,6 +800,7 @@ int pciehp_enable_slot(struct slot *p_slot)
        if (p_slot)
                update_slot_info(p_slot);
 
+       mutex_unlock(&p_slot->ctrl->crit_sect);
        return rc;
 }
 
@@ -846,10 +846,10 @@ int pciehp_disable_slot(struct slot *p_slot)
                }
        }
 
-       mutex_unlock(&p_slot->ctrl->crit_sect);
-
        ret = remove_board(p_slot);
        update_slot_info(p_slot);
+
+       mutex_unlock(&p_slot->ctrl->crit_sect);
        return ret;
 }
 
index 703a64a..1c551c6 100644 (file)
@@ -1402,6 +1402,8 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
                pdev->subsystem_vendor, pdev->subsystem_device);
 
        mutex_init(&ctrl->crit_sect);
+       mutex_init(&ctrl->ctrl_lock);
+
        /* setup wait queue */
        init_waitqueue_head(&ctrl->queue);