Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/qeth: fix IPA command submission race

If multiple IPA commands are build & sent out concurrently,
fill_ipacmd_header() may assign a seqno value to a command that's
different from what send_control_data() later assigns to this command's
reply.
This is due to other commands passing through send_control_data(),
and incrementing card->seqno.ipa along the way.

So one IPA command has no reply that's waiting for its seqno, while some
other IPA command has multiple reply objects waiting for it.
Only one of those waiting replies wins, and the other(s) times out and
triggers a recovery via send_ipa_cmd().

Fix this by making sure that the same seqno value is assigned to
a command and its reply object.
Do so immediately before submitting the command & while holding the
irq_pending "lock", to produce nicely ascending seqnos.

As a side effect, *all* IPA commands now use a reply object that's
waiting for its actual seqno. Previously, early IPA commands that were
submitted while the card was still DOWN used the "catch-all" IDX seqno.

Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Julian Wiedmann and committed by
David S. Miller
d22ffb5a c5c48c58

+10 -9
+10 -9
drivers/s390/net/qeth_core_main.c
··· 2134 2134 } 2135 2135 reply->callback = reply_cb; 2136 2136 reply->param = reply_param; 2137 - if (card->state == CARD_STATE_DOWN) 2138 - reply->seqno = QETH_IDX_COMMAND_SEQNO; 2139 - else 2140 - reply->seqno = card->seqno.ipa++; 2137 + 2141 2138 init_waitqueue_head(&reply->wait_q); 2142 - spin_lock_irqsave(&card->lock, flags); 2143 - list_add_tail(&reply->list, &card->cmd_waiter_list); 2144 - spin_unlock_irqrestore(&card->lock, flags); 2145 2139 2146 2140 while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ; 2147 - qeth_prepare_control_data(card, len, iob); 2148 2141 2149 2142 if (IS_IPA(iob->data)) { 2150 2143 cmd = __ipa_cmd(iob); 2144 + cmd->hdr.seqno = card->seqno.ipa++; 2145 + reply->seqno = cmd->hdr.seqno; 2151 2146 event_timeout = QETH_IPA_TIMEOUT; 2152 2147 } else { 2148 + reply->seqno = QETH_IDX_COMMAND_SEQNO; 2153 2149 event_timeout = QETH_TIMEOUT; 2154 2150 } 2151 + qeth_prepare_control_data(card, len, iob); 2152 + 2153 + spin_lock_irqsave(&card->lock, flags); 2154 + list_add_tail(&reply->list, &card->cmd_waiter_list); 2155 + spin_unlock_irqrestore(&card->lock, flags); 2155 2156 2156 2157 timeout = jiffies + event_timeout; 2157 2158 ··· 2934 2933 memset(cmd, 0, sizeof(struct qeth_ipa_cmd)); 2935 2934 cmd->hdr.command = command; 2936 2935 cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST; 2937 - cmd->hdr.seqno = card->seqno.ipa; 2936 + /* cmd->hdr.seqno is set by qeth_send_control_data() */ 2938 2937 cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type); 2939 2938 cmd->hdr.rel_adapter_no = (__u8) card->info.portno; 2940 2939 if (card->options.layer2)