[S390] qeth: preallocated qeth header for hiper socket

For hiper socket devices this patch will economize the reallocation
of the tx skb data segment by allocating separate memory for the qdio
transport information (qeth header).

Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index a8b069c..b3cee03 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_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
 			struct sk_buff *skb, int ipv, int cast_type)
 {
-	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)((skb->data) +
-					QETH_HEADER_SIZE);
+	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
 
 	memset(hdr, 0, sizeof(struct qeth_hdr));
 	hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
@@ -621,6 +620,9 @@
 	int tx_bytes = skb->len;
 	enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
 	struct qeth_eddp_context *ctx = NULL;
+	int data_offset = -1;
+	int elements_needed = 0;
+	int hd_len = 0;
 
 	if ((card->state != CARD_STATE_UP) || !card->lan_online) {
 		card->stats.tx_carrier_errors++;
@@ -643,13 +645,32 @@
 	if (card->info.type == QETH_CARD_TYPE_OSN)
 		hdr = (struct qeth_hdr *)skb->data;
 	else {
-		/* create a clone with writeable headroom */
-		new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr));
-		if (!new_skb)
-			goto tx_drop;
-		hdr = (struct qeth_hdr *)skb_push(new_skb,
+		if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
+		    (skb_shinfo(skb)->nr_frags == 0)) {
+			new_skb = skb;
+			data_offset = ETH_HLEN;
+			hd_len = ETH_HLEN;
+			hdr = kmem_cache_alloc(qeth_core_header_cache,
+						GFP_ATOMIC);
+			if (!hdr)
+				goto tx_drop;
+			elements_needed++;
+			skb_reset_mac_header(new_skb);
+			qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+			hdr->hdr.l2.pkt_length = new_skb->len;
+			memcpy(((char *)hdr) + sizeof(struct qeth_hdr),
+				skb_mac_header(new_skb), ETH_HLEN);
+		} else {
+			/* create a clone with writeable headroom */
+			new_skb = skb_realloc_headroom(skb,
 						sizeof(struct qeth_hdr));
-		qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+			if (!new_skb)
+				goto tx_drop;
+			hdr = (struct qeth_hdr *)skb_push(new_skb,
+						sizeof(struct qeth_hdr));
+			skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
+			qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+		}
 	}
 
 	if (large_send == QETH_LARGE_SEND_EDDP) {
@@ -660,9 +681,13 @@
 			goto tx_drop;
 		}
 	} else {
-		elements = qeth_get_elements_no(card, (void *)hdr, new_skb, 0);
-		if (!elements)
+		elements = qeth_get_elements_no(card, (void *)hdr, new_skb,
+						elements_needed);
+		if (!elements) {
+			if (data_offset >= 0)
+				kmem_cache_free(qeth_core_header_cache, hdr);
 			goto tx_drop;
+		}
 	}
 
 	if ((large_send == QETH_LARGE_SEND_NO) &&
@@ -674,7 +699,7 @@
 					 elements, ctx);
 	else
 		rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
-					      elements, ctx);
+					elements, ctx, data_offset, hd_len);
 	if (!rc) {
 		card->stats.tx_packets++;
 		card->stats.tx_bytes += tx_bytes;
@@ -701,6 +726,9 @@
 		if (ctx != NULL)
 			qeth_eddp_put_context(ctx);
 
+		if (data_offset >= 0)
+			kmem_cache_free(qeth_core_header_cache, hdr);
+
 		if (rc == -EBUSY) {
 			if (new_skb != skb)
 				dev_kfree_skb_any(new_skb);