| From foo@baz Thu Dec 21 09:02:40 CET 2017 |
| From: Julian Wiedmann <jwi@linux.vnet.ibm.com> |
| Date: Thu, 23 Mar 2017 14:55:09 +0100 |
| Subject: s390/qeth: no ETH header for outbound AF_IUCV |
| |
| From: Julian Wiedmann <jwi@linux.vnet.ibm.com> |
| |
| |
| [ Upstream commit acd9776b5c45ef02d1a210969a6fcc058afb76e3 ] |
| |
| With AF_IUCV traffic, the skb passed to hard_start_xmit() has a 14 byte |
| slot at skb->data, intended for an ETH header. qeth_l3_fill_af_iucv_hdr() |
| fills this ETH header... and then immediately moves it to the |
| skb's headroom, where it disappears and is never seen again. |
| |
| But it's still possible for us to return NETDEV_TX_BUSY after the skb has |
| been modified. Since we didn't get a private copy of the skb, the next |
| time the skb is delivered to hard_start_xmit() it no longer has the |
| expected layout (we moved the ETH header to the headroom, so skb->data |
| now starts at the IUCV_TRANS header). So when qeth_l3_fill_af_iucv_hdr() |
| does another round of rebuilding, the resulting qeth header ends up |
| all wrong. On transmission, the buffer is then rejected by |
| the HiperSockets device with SBALF15 = x'04'. |
| When this error is passed back to af_iucv as TX_NOTIFY_UNREACHABLE, it |
| tears down the offending socket. |
| |
| As the ETH header for AF_IUCV serves no purpose, just align the code to |
| what we do for IP traffic on L3 HiperSockets: keep the ETH header at |
| skb->data, and pass down data_offset = ETH_HLEN to qeth_fill_buffer(). |
| When mapping the payload into the SBAL elements, the ETH header is then |
| stripped off. This avoids the skb manipulations in |
| qeth_l3_fill_af_iucv_hdr(), and any buffer re-entering hard_start_xmit() |
| after NETDEV_TX_BUSY is now processed properly. |
| |
| Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com> |
| Signed-off-by: Ursula Braun <ubraun@linux.vnet.ibm.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <alexander.levin@verizon.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/s390/net/qeth_l3_main.c | 15 ++++----------- |
| 1 file changed, 4 insertions(+), 11 deletions(-) |
| |
| --- a/drivers/s390/net/qeth_l3_main.c |
| +++ b/drivers/s390/net/qeth_l3_main.c |
| @@ -2612,17 +2612,13 @@ static void qeth_l3_fill_af_iucv_hdr(str |
| char daddr[16]; |
| struct af_iucv_trans_hdr *iucv_hdr; |
| |
| - skb_pull(skb, 14); |
| - card->dev->header_ops->create(skb, card->dev, 0, |
| - card->dev->dev_addr, card->dev->dev_addr, |
| - card->dev->addr_len); |
| - skb_pull(skb, 14); |
| - iucv_hdr = (struct af_iucv_trans_hdr *)skb->data; |
| memset(hdr, 0, sizeof(struct qeth_hdr)); |
| hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; |
| hdr->hdr.l3.ext_flags = 0; |
| - hdr->hdr.l3.length = skb->len; |
| + hdr->hdr.l3.length = skb->len - ETH_HLEN; |
| hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST; |
| + |
| + iucv_hdr = (struct af_iucv_trans_hdr *) (skb->data + ETH_HLEN); |
| memset(daddr, 0, sizeof(daddr)); |
| daddr[0] = 0xfe; |
| daddr[1] = 0x80; |
| @@ -2826,10 +2822,7 @@ static int qeth_l3_hard_start_xmit(struc |
| if ((card->info.type == QETH_CARD_TYPE_IQD) && |
| !skb_is_nonlinear(skb)) { |
| new_skb = skb; |
| - if (new_skb->protocol == ETH_P_AF_IUCV) |
| - data_offset = 0; |
| - else |
| - data_offset = ETH_HLEN; |
| + data_offset = ETH_HLEN; |
| hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); |
| if (!hdr) |
| goto tx_drop; |