diff options
Diffstat (limited to 'drivers/s390/net/qeth_l2_main.c')
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 50 |
1 files changed, 39 insertions, 11 deletions
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index a8b069cd9a4c..b3cee032f578 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c | |||
@@ -243,8 +243,7 @@ static void qeth_l2_get_packet_type(struct qeth_card *card, | |||
243 | static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, | 243 | static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, |
244 | struct sk_buff *skb, int ipv, int cast_type) | 244 | struct sk_buff *skb, int ipv, int cast_type) |
245 | { | 245 | { |
246 | struct vlan_ethhdr *veth = (struct vlan_ethhdr *)((skb->data) + | 246 | struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb); |
247 | QETH_HEADER_SIZE); | ||
248 | 247 | ||
249 | memset(hdr, 0, sizeof(struct qeth_hdr)); | 248 | memset(hdr, 0, sizeof(struct qeth_hdr)); |
250 | hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2; | 249 | hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2; |
@@ -621,6 +620,9 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
621 | int tx_bytes = skb->len; | 620 | int tx_bytes = skb->len; |
622 | enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO; | 621 | enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO; |
623 | struct qeth_eddp_context *ctx = NULL; | 622 | struct qeth_eddp_context *ctx = NULL; |
623 | int data_offset = -1; | ||
624 | int elements_needed = 0; | ||
625 | int hd_len = 0; | ||
624 | 626 | ||
625 | if ((card->state != CARD_STATE_UP) || !card->lan_online) { | 627 | if ((card->state != CARD_STATE_UP) || !card->lan_online) { |
626 | card->stats.tx_carrier_errors++; | 628 | card->stats.tx_carrier_errors++; |
@@ -643,13 +645,32 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
643 | if (card->info.type == QETH_CARD_TYPE_OSN) | 645 | if (card->info.type == QETH_CARD_TYPE_OSN) |
644 | hdr = (struct qeth_hdr *)skb->data; | 646 | hdr = (struct qeth_hdr *)skb->data; |
645 | else { | 647 | else { |
646 | /* create a clone with writeable headroom */ | 648 | if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && |
647 | new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr)); | 649 | (skb_shinfo(skb)->nr_frags == 0)) { |
648 | if (!new_skb) | 650 | new_skb = skb; |
649 | goto tx_drop; | 651 | data_offset = ETH_HLEN; |
650 | hdr = (struct qeth_hdr *)skb_push(new_skb, | 652 | hd_len = ETH_HLEN; |
653 | hdr = kmem_cache_alloc(qeth_core_header_cache, | ||
654 | GFP_ATOMIC); | ||
655 | if (!hdr) | ||
656 | goto tx_drop; | ||
657 | elements_needed++; | ||
658 | skb_reset_mac_header(new_skb); | ||
659 | qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); | ||
660 | hdr->hdr.l2.pkt_length = new_skb->len; | ||
661 | memcpy(((char *)hdr) + sizeof(struct qeth_hdr), | ||
662 | skb_mac_header(new_skb), ETH_HLEN); | ||
663 | } else { | ||
664 | /* create a clone with writeable headroom */ | ||
665 | new_skb = skb_realloc_headroom(skb, | ||
666 | sizeof(struct qeth_hdr)); | ||
667 | if (!new_skb) | ||
668 | goto tx_drop; | ||
669 | hdr = (struct qeth_hdr *)skb_push(new_skb, | ||
651 | sizeof(struct qeth_hdr)); | 670 | sizeof(struct qeth_hdr)); |
652 | qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); | 671 | skb_set_mac_header(new_skb, sizeof(struct qeth_hdr)); |
672 | qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); | ||
673 | } | ||
653 | } | 674 | } |
654 | 675 | ||
655 | if (large_send == QETH_LARGE_SEND_EDDP) { | 676 | if (large_send == QETH_LARGE_SEND_EDDP) { |
@@ -660,9 +681,13 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
660 | goto tx_drop; | 681 | goto tx_drop; |
661 | } | 682 | } |
662 | } else { | 683 | } else { |
663 | elements = qeth_get_elements_no(card, (void *)hdr, new_skb, 0); | 684 | elements = qeth_get_elements_no(card, (void *)hdr, new_skb, |
664 | if (!elements) | 685 | elements_needed); |
686 | if (!elements) { | ||
687 | if (data_offset >= 0) | ||
688 | kmem_cache_free(qeth_core_header_cache, hdr); | ||
665 | goto tx_drop; | 689 | goto tx_drop; |
690 | } | ||
666 | } | 691 | } |
667 | 692 | ||
668 | if ((large_send == QETH_LARGE_SEND_NO) && | 693 | if ((large_send == QETH_LARGE_SEND_NO) && |
@@ -674,7 +699,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
674 | elements, ctx); | 699 | elements, ctx); |
675 | else | 700 | else |
676 | rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, | 701 | rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, |
677 | elements, ctx); | 702 | elements, ctx, data_offset, hd_len); |
678 | if (!rc) { | 703 | if (!rc) { |
679 | card->stats.tx_packets++; | 704 | card->stats.tx_packets++; |
680 | card->stats.tx_bytes += tx_bytes; | 705 | card->stats.tx_bytes += tx_bytes; |
@@ -701,6 +726,9 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
701 | if (ctx != NULL) | 726 | if (ctx != NULL) |
702 | qeth_eddp_put_context(ctx); | 727 | qeth_eddp_put_context(ctx); |
703 | 728 | ||
729 | if (data_offset >= 0) | ||
730 | kmem_cache_free(qeth_core_header_cache, hdr); | ||
731 | |||
704 | if (rc == -EBUSY) { | 732 | if (rc == -EBUSY) { |
705 | if (new_skb != skb) | 733 | if (new_skb != skb) |
706 | dev_kfree_skb_any(new_skb); | 734 | dev_kfree_skb_any(new_skb); |