提交 f4778364 编写于 作者: K Kenji Kaneshige 提交者: Greg Kroah-Hartman

PCI: hotplug: pciehp: Fix possible race condition in writing slot

The slot control register is modified as follows:

    (1) Read the register value
    (2) Change the value
    (3) Write the value to the register

Those must be done atomically, otherwise writing to control register
would cause an unexpected result.
Signed-off-by: NKenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: NKristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 adf809d0
...@@ -103,6 +103,7 @@ struct controller { ...@@ -103,6 +103,7 @@ struct controller {
u8 cap_base; u8 cap_base;
struct timer_list poll_timer; struct timer_list poll_timer;
volatile int cmd_busy; volatile int cmd_busy;
spinlock_t lock;
}; };
#define INT_BUTTON_IGNORE 0 #define INT_BUTTON_IGNORE 0
......
...@@ -275,11 +275,19 @@ static inline int pcie_wait_cmd(struct controller *ctrl) ...@@ -275,11 +275,19 @@ static inline int pcie_wait_cmd(struct controller *ctrl)
return retval; return retval;
} }
static int pcie_write_cmd(struct slot *slot, u16 cmd) /**
* pcie_write_cmd - Issue controller command
* @slot: slot to which the command is issued
* @cmd: command value written to slot control register
* @mask: bitmask of slot control register to be modified
*/
static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
int retval = 0; int retval = 0;
u16 slot_status; u16 slot_status;
u16 slot_ctrl;
unsigned long flags;
DBG_ENTER_ROUTINE DBG_ENTER_ROUTINE
...@@ -299,16 +307,28 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd) ...@@ -299,16 +307,28 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd)
__FUNCTION__); __FUNCTION__);
} }
ctrl->cmd_busy = 1; spin_lock_irqsave(&ctrl->lock, flags);
retval = pciehp_writew(ctrl, SLOTCTRL, (cmd | CMD_CMPL_INTR_ENABLE)); retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
if (retval) { if (retval) {
err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
goto out; goto out_spin_unlock;
} }
slot_ctrl &= ~mask;
slot_ctrl |= ((cmd & mask) | CMD_CMPL_INTR_ENABLE);
ctrl->cmd_busy = 1;
retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl);
if (retval)
err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
out_spin_unlock:
spin_unlock_irqrestore(&ctrl->lock, flags);
/* /*
* Wait for command completion. * Wait for command completion.
*/ */
if (!retval)
retval = pcie_wait_cmd(ctrl); retval = pcie_wait_cmd(ctrl);
out: out:
mutex_unlock(&ctrl->ctrl_lock); mutex_unlock(&ctrl->ctrl_lock);
...@@ -502,25 +522,20 @@ static int hpc_get_emi_status(struct slot *slot, u8 *status) ...@@ -502,25 +522,20 @@ static int hpc_get_emi_status(struct slot *slot, u8 *status)
static int hpc_toggle_emi(struct slot *slot) static int hpc_toggle_emi(struct slot *slot)
{ {
struct controller *ctrl = slot->ctrl; u16 slot_cmd;
u16 slot_cmd = 0; u16 cmd_mask;
u16 slot_ctrl; int rc;
int rc = 0;
DBG_ENTER_ROUTINE DBG_ENTER_ROUTINE
rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); slot_cmd = EMI_CTRL;
if (rc) { cmd_mask = EMI_CTRL;
err("%s : hp_register_read_word SLOT_CTRL failed\n", if (!pciehp_poll_mode) {
__FUNCTION__);
return rc;
}
slot_cmd = (slot_ctrl | EMI_CTRL);
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE; slot_cmd = slot_cmd | HP_INTR_ENABLE;
cmd_mask = cmd_mask | HP_INTR_ENABLE;
}
pcie_write_cmd(slot, slot_cmd); rc = pcie_write_cmd(slot, slot_cmd, cmd_mask);
slot->last_emi_toggle = get_seconds(); slot->last_emi_toggle = get_seconds();
DBG_LEAVE_ROUTINE DBG_LEAVE_ROUTINE
return rc; return rc;
...@@ -529,35 +544,32 @@ static int hpc_toggle_emi(struct slot *slot) ...@@ -529,35 +544,32 @@ static int hpc_toggle_emi(struct slot *slot)
static int hpc_set_attention_status(struct slot *slot, u8 value) static int hpc_set_attention_status(struct slot *slot, u8 value)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
u16 slot_cmd = 0; u16 slot_cmd;
u16 slot_ctrl; u16 cmd_mask;
int rc = 0; int rc;
DBG_ENTER_ROUTINE DBG_ENTER_ROUTINE
rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); cmd_mask = ATTN_LED_CTRL;
if (rc) {
err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
return rc;
}
switch (value) { switch (value) {
case 0 : /* turn off */ case 0 : /* turn off */
slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x00C0; slot_cmd = 0x00C0;
break; break;
case 1: /* turn on */ case 1: /* turn on */
slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0040; slot_cmd = 0x0040;
break; break;
case 2: /* turn blink */ case 2: /* turn blink */
slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0080; slot_cmd = 0x0080;
break; break;
default: default:
return -1; return -1;
} }
if (!pciehp_poll_mode) if (!pciehp_poll_mode) {
slot_cmd = slot_cmd | HP_INTR_ENABLE; slot_cmd = slot_cmd | HP_INTR_ENABLE;
cmd_mask = cmd_mask | HP_INTR_ENABLE;
}
pcie_write_cmd(slot, slot_cmd); rc = pcie_write_cmd(slot, slot_cmd, cmd_mask);
dbg("%s: SLOTCTRL %x write cmd %x\n", dbg("%s: SLOTCTRL %x write cmd %x\n",
__FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
...@@ -570,21 +582,18 @@ static void hpc_set_green_led_on(struct slot *slot) ...@@ -570,21 +582,18 @@ static void hpc_set_green_led_on(struct slot *slot)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
u16 slot_cmd; u16 slot_cmd;
u16 slot_ctrl; u16 cmd_mask;
int rc = 0;
DBG_ENTER_ROUTINE DBG_ENTER_ROUTINE
rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); slot_cmd = 0x0100;
if (rc) { cmd_mask = PWR_LED_CTRL;
err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); if (!pciehp_poll_mode) {
return;
}
slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0100;
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE; slot_cmd = slot_cmd | HP_INTR_ENABLE;
cmd_mask = cmd_mask | HP_INTR_ENABLE;
}
pcie_write_cmd(slot, slot_cmd); pcie_write_cmd(slot, slot_cmd, cmd_mask);
dbg("%s: SLOTCTRL %x write cmd %x\n", dbg("%s: SLOTCTRL %x write cmd %x\n",
__FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
...@@ -596,22 +605,18 @@ static void hpc_set_green_led_off(struct slot *slot) ...@@ -596,22 +605,18 @@ static void hpc_set_green_led_off(struct slot *slot)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
u16 slot_cmd; u16 slot_cmd;
u16 slot_ctrl; u16 cmd_mask;
int rc = 0;
DBG_ENTER_ROUTINE DBG_ENTER_ROUTINE
rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); slot_cmd = 0x0300;
if (rc) { cmd_mask = PWR_LED_CTRL;
err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); if (!pciehp_poll_mode) {
return; slot_cmd = slot_cmd | HP_INTR_ENABLE;
cmd_mask = cmd_mask | HP_INTR_ENABLE;
} }
slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300; pcie_write_cmd(slot, slot_cmd, cmd_mask);
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE;
pcie_write_cmd(slot, slot_cmd);
dbg("%s: SLOTCTRL %x write cmd %x\n", dbg("%s: SLOTCTRL %x write cmd %x\n",
__FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
...@@ -623,22 +628,18 @@ static void hpc_set_green_led_blink(struct slot *slot) ...@@ -623,22 +628,18 @@ static void hpc_set_green_led_blink(struct slot *slot)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
u16 slot_cmd; u16 slot_cmd;
u16 slot_ctrl; u16 cmd_mask;
int rc = 0;
DBG_ENTER_ROUTINE DBG_ENTER_ROUTINE
rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); slot_cmd = 0x0200;
if (rc) { cmd_mask = PWR_LED_CTRL;
err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); if (!pciehp_poll_mode) {
return; slot_cmd = slot_cmd | HP_INTR_ENABLE;
cmd_mask = cmd_mask | HP_INTR_ENABLE;
} }
slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0200; pcie_write_cmd(slot, slot_cmd, cmd_mask);
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE;
pcie_write_cmd(slot, slot_cmd);
dbg("%s: SLOTCTRL %x write cmd %x\n", dbg("%s: SLOTCTRL %x write cmd %x\n",
__FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
...@@ -669,7 +670,8 @@ static int hpc_power_on_slot(struct slot * slot) ...@@ -669,7 +670,8 @@ static int hpc_power_on_slot(struct slot * slot)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
u16 slot_cmd; u16 slot_cmd;
u16 slot_ctrl, slot_status; u16 cmd_mask;
u16 slot_status;
int retval = 0; int retval = 0;
DBG_ENTER_ROUTINE DBG_ENTER_ROUTINE
...@@ -692,23 +694,23 @@ static int hpc_power_on_slot(struct slot * slot) ...@@ -692,23 +694,23 @@ static int hpc_power_on_slot(struct slot * slot)
} }
} }
retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); slot_cmd = POWER_ON;
if (retval) { cmd_mask = PWR_CTRL;
err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
return retval;
}
slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_ON;
/* Enable detection that we turned off at slot power-off time */ /* Enable detection that we turned off at slot power-off time */
if (!pciehp_poll_mode) if (!pciehp_poll_mode) {
slot_cmd = slot_cmd | slot_cmd = slot_cmd |
PWR_FAULT_DETECT_ENABLE | PWR_FAULT_DETECT_ENABLE |
MRL_DETECT_ENABLE | MRL_DETECT_ENABLE |
PRSN_DETECT_ENABLE | PRSN_DETECT_ENABLE |
HP_INTR_ENABLE; HP_INTR_ENABLE;
cmd_mask = cmd_mask |
PWR_FAULT_DETECT_ENABLE |
MRL_DETECT_ENABLE |
PRSN_DETECT_ENABLE |
HP_INTR_ENABLE;
}
retval = pcie_write_cmd(slot, slot_cmd); retval = pcie_write_cmd(slot, slot_cmd, cmd_mask);
if (retval) { if (retval) {
err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd); err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd);
...@@ -726,21 +728,15 @@ static int hpc_power_off_slot(struct slot * slot) ...@@ -726,21 +728,15 @@ static int hpc_power_off_slot(struct slot * slot)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
u16 slot_cmd; u16 slot_cmd;
u16 slot_ctrl; u16 cmd_mask;
int retval = 0; int retval = 0;
DBG_ENTER_ROUTINE DBG_ENTER_ROUTINE
dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); slot_cmd = POWER_OFF;
if (retval) { cmd_mask = PWR_CTRL;
err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
return retval;
}
slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_OFF;
/* /*
* If we get MRL or presence detect interrupts now, the isr * If we get MRL or presence detect interrupts now, the isr
* will notice the sticky power-fault bit too and issue power * will notice the sticky power-fault bit too and issue power
...@@ -748,14 +744,19 @@ static int hpc_power_off_slot(struct slot * slot) ...@@ -748,14 +744,19 @@ static int hpc_power_off_slot(struct slot * slot)
* of command completions, since the power-fault bit remains on * of command completions, since the power-fault bit remains on
* till the slot is powered on again. * till the slot is powered on again.
*/ */
if (!pciehp_poll_mode) if (!pciehp_poll_mode) {
slot_cmd = (slot_cmd & slot_cmd = (slot_cmd &
~PWR_FAULT_DETECT_ENABLE & ~PWR_FAULT_DETECT_ENABLE &
~MRL_DETECT_ENABLE & ~MRL_DETECT_ENABLE &
~PRSN_DETECT_ENABLE) | HP_INTR_ENABLE; ~PRSN_DETECT_ENABLE) | HP_INTR_ENABLE;
cmd_mask = cmd_mask |
PWR_FAULT_DETECT_ENABLE |
MRL_DETECT_ENABLE |
PRSN_DETECT_ENABLE |
HP_INTR_ENABLE;
}
retval = pcie_write_cmd(slot, slot_cmd); retval = pcie_write_cmd(slot, slot_cmd, cmd_mask);
if (retval) { if (retval) {
err("%s: Write command failed!\n", __FUNCTION__); err("%s: Write command failed!\n", __FUNCTION__);
return -1; return -1;
...@@ -775,6 +776,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ...@@ -775,6 +776,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
u16 temp_word; u16 temp_word;
int hp_slot = 0; /* only 1 slot per PCI Express port */ int hp_slot = 0; /* only 1 slot per PCI Express port */
int rc = 0; int rc = 0;
unsigned long flags;
rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
if (rc) { if (rc) {
...@@ -794,10 +796,12 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ...@@ -794,10 +796,12 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc); dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc);
/* Mask Hot-plug Interrupt Enable */ /* Mask Hot-plug Interrupt Enable */
if (!pciehp_poll_mode) { if (!pciehp_poll_mode) {
spin_lock_irqsave(&ctrl->lock, flags);
rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
if (rc) { if (rc) {
err("%s: Cannot read SLOT_CTRL register\n", err("%s: Cannot read SLOT_CTRL register\n",
__FUNCTION__); __FUNCTION__);
spin_unlock_irqrestore(&ctrl->lock, flags);
return IRQ_NONE; return IRQ_NONE;
} }
...@@ -808,8 +812,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ...@@ -808,8 +812,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
if (rc) { if (rc) {
err("%s: Cannot write to SLOTCTRL register\n", err("%s: Cannot write to SLOTCTRL register\n",
__FUNCTION__); __FUNCTION__);
spin_unlock_irqrestore(&ctrl->lock, flags);
return IRQ_NONE; return IRQ_NONE;
} }
spin_unlock_irqrestore(&ctrl->lock, flags);
rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
if (rc) { if (rc) {
...@@ -859,10 +865,12 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ...@@ -859,10 +865,12 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
} }
/* Unmask Hot-plug Interrupt Enable */ /* Unmask Hot-plug Interrupt Enable */
if (!pciehp_poll_mode) { if (!pciehp_poll_mode) {
spin_lock_irqsave(&ctrl->lock, flags);
rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
if (rc) { if (rc) {
err("%s: Cannot read SLOTCTRL register\n", err("%s: Cannot read SLOTCTRL register\n",
__FUNCTION__); __FUNCTION__);
spin_unlock_irqrestore(&ctrl->lock, flags);
return IRQ_NONE; return IRQ_NONE;
} }
...@@ -873,8 +881,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ...@@ -873,8 +881,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
if (rc) { if (rc) {
err("%s: Cannot write to SLOTCTRL register\n", err("%s: Cannot write to SLOTCTRL register\n",
__FUNCTION__); __FUNCTION__);
spin_unlock_irqrestore(&ctrl->lock, flags);
return IRQ_NONE; return IRQ_NONE;
} }
spin_unlock_irqrestore(&ctrl->lock, flags);
rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
if (rc) { if (rc) {
...@@ -1237,6 +1247,7 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) ...@@ -1237,6 +1247,7 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
mutex_init(&ctrl->crit_sect); mutex_init(&ctrl->crit_sect);
mutex_init(&ctrl->ctrl_lock); mutex_init(&ctrl->ctrl_lock);
spin_lock_init(&ctrl->lock);
/* setup wait queue */ /* setup wait queue */
init_waitqueue_head(&ctrl->queue); init_waitqueue_head(&ctrl->queue);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册