Merge branch 's390-qeth-fixes'
Julian Wiedmann says: ==================== s390/qeth: fixes 2018-02-27 please apply some more qeth patches for -net and stable. One patch fixes a performance bug in the TSO path. Then there's several more fixes for IP management on L3 devices - including a revert, so that the subsequent fix cleanly applies to earlier kernels. The final patch takes care of a race in the control IO code that causes qeth to miss the cmd response, and subsequently trigger device recovery. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
c84316223b
@ -2134,24 +2134,25 @@ int qeth_send_control_data(struct qeth_card *card, int len,
|
||||
}
|
||||
reply->callback = reply_cb;
|
||||
reply->param = reply_param;
|
||||
if (card->state == CARD_STATE_DOWN)
|
||||
reply->seqno = QETH_IDX_COMMAND_SEQNO;
|
||||
else
|
||||
reply->seqno = card->seqno.ipa++;
|
||||
|
||||
init_waitqueue_head(&reply->wait_q);
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
list_add_tail(&reply->list, &card->cmd_waiter_list);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ;
|
||||
qeth_prepare_control_data(card, len, iob);
|
||||
|
||||
if (IS_IPA(iob->data)) {
|
||||
cmd = __ipa_cmd(iob);
|
||||
cmd->hdr.seqno = card->seqno.ipa++;
|
||||
reply->seqno = cmd->hdr.seqno;
|
||||
event_timeout = QETH_IPA_TIMEOUT;
|
||||
} else {
|
||||
reply->seqno = QETH_IDX_COMMAND_SEQNO;
|
||||
event_timeout = QETH_TIMEOUT;
|
||||
}
|
||||
qeth_prepare_control_data(card, len, iob);
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
list_add_tail(&reply->list, &card->cmd_waiter_list);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
timeout = jiffies + event_timeout;
|
||||
|
||||
@ -2933,7 +2934,7 @@ static void qeth_fill_ipacmd_header(struct qeth_card *card,
|
||||
memset(cmd, 0, sizeof(struct qeth_ipa_cmd));
|
||||
cmd->hdr.command = command;
|
||||
cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST;
|
||||
cmd->hdr.seqno = card->seqno.ipa;
|
||||
/* cmd->hdr.seqno is set by qeth_send_control_data() */
|
||||
cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type);
|
||||
cmd->hdr.rel_adapter_no = (__u8) card->info.portno;
|
||||
if (card->options.layer2)
|
||||
@ -3898,10 +3899,12 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags);
|
||||
int qeth_get_elements_no(struct qeth_card *card,
|
||||
struct sk_buff *skb, int extra_elems, int data_offset)
|
||||
{
|
||||
int elements = qeth_get_elements_for_range(
|
||||
(addr_t)skb->data + data_offset,
|
||||
(addr_t)skb->data + skb_headlen(skb)) +
|
||||
qeth_get_elements_for_frags(skb);
|
||||
addr_t end = (addr_t)skb->data + skb_headlen(skb);
|
||||
int elements = qeth_get_elements_for_frags(skb);
|
||||
addr_t start = (addr_t)skb->data + data_offset;
|
||||
|
||||
if (start != end)
|
||||
elements += qeth_get_elements_for_range(start, end);
|
||||
|
||||
if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
|
||||
QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
|
||||
|
@ -40,8 +40,40 @@ struct qeth_ipaddr {
|
||||
unsigned int pfxlen;
|
||||
} a6;
|
||||
} u;
|
||||
|
||||
};
|
||||
|
||||
static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1,
|
||||
struct qeth_ipaddr *a2)
|
||||
{
|
||||
if (a1->proto != a2->proto)
|
||||
return false;
|
||||
if (a1->proto == QETH_PROT_IPV6)
|
||||
return ipv6_addr_equal(&a1->u.a6.addr, &a2->u.a6.addr);
|
||||
return a1->u.a4.addr == a2->u.a4.addr;
|
||||
}
|
||||
|
||||
static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1,
|
||||
struct qeth_ipaddr *a2)
|
||||
{
|
||||
/* Assumes that the pair was obtained via qeth_l3_addr_find_by_ip(),
|
||||
* so 'proto' and 'addr' match for sure.
|
||||
*
|
||||
* For ucast:
|
||||
* - 'mac' is always 0.
|
||||
* - 'mask'/'pfxlen' for RXIP/VIPA is always 0. For NORMAL, matching
|
||||
* values are required to avoid mixups in takeover eligibility.
|
||||
*
|
||||
* For mcast,
|
||||
* - 'mac' is mapped from the IP, and thus always matches.
|
||||
* - 'mask'/'pfxlen' is always 0.
|
||||
*/
|
||||
if (a1->type != a2->type)
|
||||
return false;
|
||||
if (a1->proto == QETH_PROT_IPV6)
|
||||
return a1->u.a6.pfxlen == a2->u.a6.pfxlen;
|
||||
return a1->u.a4.mask == a2->u.a4.mask;
|
||||
}
|
||||
|
||||
static inline u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr)
|
||||
{
|
||||
u64 ret = 0;
|
||||
|
@ -67,6 +67,24 @@ void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
|
||||
qeth_l3_ipaddr6_to_string(addr, buf);
|
||||
}
|
||||
|
||||
static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
|
||||
struct qeth_ipaddr *query)
|
||||
{
|
||||
u64 key = qeth_l3_ipaddr_hash(query);
|
||||
struct qeth_ipaddr *addr;
|
||||
|
||||
if (query->is_multicast) {
|
||||
hash_for_each_possible(card->ip_mc_htable, addr, hnode, key)
|
||||
if (qeth_l3_addr_match_ip(addr, query))
|
||||
return addr;
|
||||
} else {
|
||||
hash_for_each_possible(card->ip_htable, addr, hnode, key)
|
||||
if (qeth_l3_addr_match_ip(addr, query))
|
||||
return addr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
|
||||
{
|
||||
int i, j;
|
||||
@ -120,34 +138,6 @@ static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
|
||||
return rc;
|
||||
}
|
||||
|
||||
inline int
|
||||
qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2)
|
||||
{
|
||||
return addr1->proto == addr2->proto &&
|
||||
!memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) &&
|
||||
ether_addr_equal_64bits(addr1->mac, addr2->mac);
|
||||
}
|
||||
|
||||
static struct qeth_ipaddr *
|
||||
qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
||||
{
|
||||
struct qeth_ipaddr *addr;
|
||||
|
||||
if (tmp_addr->is_multicast) {
|
||||
hash_for_each_possible(card->ip_mc_htable, addr,
|
||||
hnode, qeth_l3_ipaddr_hash(tmp_addr))
|
||||
if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
|
||||
return addr;
|
||||
} else {
|
||||
hash_for_each_possible(card->ip_htable, addr,
|
||||
hnode, qeth_l3_ipaddr_hash(tmp_addr))
|
||||
if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
|
||||
return addr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
||||
{
|
||||
int rc = 0;
|
||||
@ -162,23 +152,18 @@ int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
||||
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
|
||||
}
|
||||
|
||||
addr = qeth_l3_ip_from_hash(card, tmp_addr);
|
||||
if (!addr)
|
||||
addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
|
||||
if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr))
|
||||
return -ENOENT;
|
||||
|
||||
addr->ref_counter--;
|
||||
if (addr->ref_counter > 0 && (addr->type == QETH_IP_TYPE_NORMAL ||
|
||||
addr->type == QETH_IP_TYPE_RXIP))
|
||||
if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0)
|
||||
return rc;
|
||||
if (addr->in_progress)
|
||||
return -EINPROGRESS;
|
||||
|
||||
if (!qeth_card_hw_is_reachable(card)) {
|
||||
addr->disp_flag = QETH_DISP_ADDR_DELETE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = qeth_l3_deregister_addr_entry(card, addr);
|
||||
if (qeth_card_hw_is_reachable(card))
|
||||
rc = qeth_l3_deregister_addr_entry(card, addr);
|
||||
|
||||
hash_del(&addr->hnode);
|
||||
kfree(addr);
|
||||
@ -190,6 +175,7 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
||||
{
|
||||
int rc = 0;
|
||||
struct qeth_ipaddr *addr;
|
||||
char buf[40];
|
||||
|
||||
QETH_CARD_TEXT(card, 4, "addip");
|
||||
|
||||
@ -200,8 +186,20 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
||||
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
|
||||
}
|
||||
|
||||
addr = qeth_l3_ip_from_hash(card, tmp_addr);
|
||||
if (!addr) {
|
||||
addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
|
||||
if (addr) {
|
||||
if (tmp_addr->type != QETH_IP_TYPE_NORMAL)
|
||||
return -EADDRINUSE;
|
||||
if (qeth_l3_addr_match_all(addr, tmp_addr)) {
|
||||
addr->ref_counter++;
|
||||
return 0;
|
||||
}
|
||||
qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u,
|
||||
buf);
|
||||
dev_warn(&card->gdev->dev,
|
||||
"Registering IP address %s failed\n", buf);
|
||||
return -EADDRINUSE;
|
||||
} else {
|
||||
addr = qeth_l3_get_addr_buffer(tmp_addr->proto);
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
@ -241,19 +239,15 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
||||
(rc == IPA_RC_LAN_OFFLINE)) {
|
||||
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
||||
if (addr->ref_counter < 1) {
|
||||
qeth_l3_delete_ip(card, addr);
|
||||
qeth_l3_deregister_addr_entry(card, addr);
|
||||
hash_del(&addr->hnode);
|
||||
kfree(addr);
|
||||
}
|
||||
} else {
|
||||
hash_del(&addr->hnode);
|
||||
kfree(addr);
|
||||
}
|
||||
} else {
|
||||
if (addr->type == QETH_IP_TYPE_NORMAL ||
|
||||
addr->type == QETH_IP_TYPE_RXIP)
|
||||
addr->ref_counter++;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -321,11 +315,7 @@ static void qeth_l3_recover_ip(struct qeth_card *card)
|
||||
spin_lock_bh(&card->ip_lock);
|
||||
|
||||
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
|
||||
if (addr->disp_flag == QETH_DISP_ADDR_DELETE) {
|
||||
qeth_l3_deregister_addr_entry(card, addr);
|
||||
hash_del(&addr->hnode);
|
||||
kfree(addr);
|
||||
} else if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
|
||||
if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
|
||||
if (addr->proto == QETH_PROT_IPV4) {
|
||||
addr->in_progress = 1;
|
||||
spin_unlock_bh(&card->ip_lock);
|
||||
@ -643,12 +633,7 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&card->ip_lock);
|
||||
|
||||
if (qeth_l3_ip_from_hash(card, ipaddr))
|
||||
rc = -EEXIST;
|
||||
else
|
||||
rc = qeth_l3_add_ip(card, ipaddr);
|
||||
|
||||
rc = qeth_l3_add_ip(card, ipaddr);
|
||||
spin_unlock_bh(&card->ip_lock);
|
||||
|
||||
kfree(ipaddr);
|
||||
@ -713,12 +698,7 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&card->ip_lock);
|
||||
|
||||
if (qeth_l3_ip_from_hash(card, ipaddr))
|
||||
rc = -EEXIST;
|
||||
else
|
||||
rc = qeth_l3_add_ip(card, ipaddr);
|
||||
|
||||
rc = qeth_l3_add_ip(card, ipaddr);
|
||||
spin_unlock_bh(&card->ip_lock);
|
||||
|
||||
kfree(ipaddr);
|
||||
@ -1239,8 +1219,9 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev)
|
||||
tmp->u.a4.addr = be32_to_cpu(im4->multiaddr);
|
||||
tmp->is_multicast = 1;
|
||||
|
||||
ipm = qeth_l3_ip_from_hash(card, tmp);
|
||||
ipm = qeth_l3_find_addr_by_ip(card, tmp);
|
||||
if (ipm) {
|
||||
/* for mcast, by-IP match means full match */
|
||||
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
||||
} else {
|
||||
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
|
||||
@ -1319,8 +1300,9 @@ static void qeth_l3_add_mc6_to_hash(struct qeth_card *card,
|
||||
sizeof(struct in6_addr));
|
||||
tmp->is_multicast = 1;
|
||||
|
||||
ipm = qeth_l3_ip_from_hash(card, tmp);
|
||||
ipm = qeth_l3_find_addr_by_ip(card, tmp);
|
||||
if (ipm) {
|
||||
/* for mcast, by-IP match means full match */
|
||||
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
||||
continue;
|
||||
}
|
||||
@ -2450,11 +2432,12 @@ static void qeth_tso_fill_header(struct qeth_card *card,
|
||||
static int qeth_l3_get_elements_no_tso(struct qeth_card *card,
|
||||
struct sk_buff *skb, int extra_elems)
|
||||
{
|
||||
addr_t tcpdptr = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
|
||||
int elements = qeth_get_elements_for_range(
|
||||
tcpdptr,
|
||||
(addr_t)skb->data + skb_headlen(skb)) +
|
||||
qeth_get_elements_for_frags(skb);
|
||||
addr_t start = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
|
||||
addr_t end = (addr_t)skb->data + skb_headlen(skb);
|
||||
int elements = qeth_get_elements_for_frags(skb);
|
||||
|
||||
if (start != end)
|
||||
elements += qeth_get_elements_for_range(start, end);
|
||||
|
||||
if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
|
||||
QETH_DBF_MESSAGE(2,
|
||||
|
Loading…
x
Reference in New Issue
Block a user