Commit 7359393f authored by Julian Wiedmann's avatar Julian Wiedmann Committed by David S. Miller

s390/qeth: wake up all waiters from qeth_irq()

card->wait_q is shared by different users, for different wake-up
conditions. qeth_irq() can potentially trigger multiple of these
conditions:
1) A change to channel->irq_pending, which qeth_send_control_data() is
   waiting for.
2) A change to card->state, which qeth_clear_channel() and
   qeth_halt_channel() are waiting for.

As qeth_irq() does only a single wake_up(), we might miss to wake up
a second eligible waiter. Luckily all waiters are guarded with a
timeout, so this situation should recover on its own eventually.

To make things work robustly, add an additional wake_up() for changes
to channel->state. And extract a helper that updates
channel->irq_pending along with the needed wake_up().
Signed-off-by: default avatarJulian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 871602b1
...@@ -862,6 +862,13 @@ static inline bool qeth_card_hw_is_reachable(struct qeth_card *card) ...@@ -862,6 +862,13 @@ static inline bool qeth_card_hw_is_reachable(struct qeth_card *card)
return card->state == CARD_STATE_SOFTSETUP; return card->state == CARD_STATE_SOFTSETUP;
} }
static inline void qeth_unlock_channel(struct qeth_card *card,
struct qeth_channel *channel)
{
atomic_set(&channel->irq_pending, 0);
wake_up(&card->wait_q);
}
struct qeth_trap_id { struct qeth_trap_id {
__u16 lparnr; __u16 lparnr;
char vmname[8]; char vmname[8];
......
...@@ -520,11 +520,10 @@ static int __qeth_issue_next_read(struct qeth_card *card) ...@@ -520,11 +520,10 @@ static int __qeth_issue_next_read(struct qeth_card *card)
} else { } else {
QETH_DBF_MESSAGE(2, "error %i on device %x when starting next read ccw!\n", QETH_DBF_MESSAGE(2, "error %i on device %x when starting next read ccw!\n",
rc, CARD_DEVID(card)); rc, CARD_DEVID(card));
atomic_set(&channel->irq_pending, 0); qeth_unlock_channel(card, channel);
qeth_put_cmd(iob); qeth_put_cmd(iob);
card->read_or_write_problem = 1; card->read_or_write_problem = 1;
qeth_schedule_recovery(card); qeth_schedule_recovery(card);
wake_up(&card->wait_q);
} }
return rc; return rc;
} }
...@@ -1001,24 +1000,25 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, ...@@ -1001,24 +1000,25 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
} }
channel->active_cmd = NULL; channel->active_cmd = NULL;
qeth_unlock_channel(card, channel);
rc = qeth_check_irb_error(card, cdev, irb); rc = qeth_check_irb_error(card, cdev, irb);
if (rc) { if (rc) {
/* IO was terminated, free its resources. */ /* IO was terminated, free its resources. */
if (iob) if (iob)
qeth_cancel_cmd(iob, rc); qeth_cancel_cmd(iob, rc);
atomic_set(&channel->irq_pending, 0);
wake_up(&card->wait_q);
return; return;
} }
atomic_set(&channel->irq_pending, 0); if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC))
channel->state = CH_STATE_STOPPED; channel->state = CH_STATE_STOPPED;
wake_up(&card->wait_q);
}
if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
channel->state = CH_STATE_HALTED; channel->state = CH_STATE_HALTED;
wake_up(&card->wait_q);
}
if (iob && (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC | if (iob && (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC |
SCSW_FCTL_HALT_FUNC))) { SCSW_FCTL_HALT_FUNC))) {
...@@ -1052,7 +1052,7 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, ...@@ -1052,7 +1052,7 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
qeth_cancel_cmd(iob, rc); qeth_cancel_cmd(iob, rc);
qeth_clear_ipacmd_list(card); qeth_clear_ipacmd_list(card);
qeth_schedule_recovery(card); qeth_schedule_recovery(card);
goto out; return;
} }
} }
...@@ -1060,16 +1060,12 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, ...@@ -1060,16 +1060,12 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
/* sanity check: */ /* sanity check: */
if (irb->scsw.cmd.count > iob->length) { if (irb->scsw.cmd.count > iob->length) {
qeth_cancel_cmd(iob, -EIO); qeth_cancel_cmd(iob, -EIO);
goto out; return;
} }
if (iob->callback) if (iob->callback)
iob->callback(card, iob, iob->callback(card, iob,
iob->length - irb->scsw.cmd.count); iob->length - irb->scsw.cmd.count);
} }
out:
wake_up(&card->wait_q);
return;
} }
static void qeth_notify_skbs(struct qeth_qdio_out_q *q, static void qeth_notify_skbs(struct qeth_qdio_out_q *q,
...@@ -1780,8 +1776,7 @@ static int qeth_send_control_data(struct qeth_card *card, ...@@ -1780,8 +1776,7 @@ static int qeth_send_control_data(struct qeth_card *card,
QETH_CARD_TEXT_(card, 2, " err%d", rc); QETH_CARD_TEXT_(card, 2, " err%d", rc);
qeth_dequeue_cmd(card, iob); qeth_dequeue_cmd(card, iob);
qeth_put_cmd(iob); qeth_put_cmd(iob);
atomic_set(&channel->irq_pending, 0); qeth_unlock_channel(card, channel);
wake_up(&card->wait_q);
goto out; goto out;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment