Commit 356156b6 authored by Julian Wiedmann's avatar Julian Wiedmann Committed by David S. Miller
Browse files

s390/qeth: speed up TSO transmission



Switch TSO over to the faster transmit path, and remove all the unused
old TSO code.

Signed-off-by: default avatarJulian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e517b649
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -1004,7 +1004,6 @@ int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
	int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),
	void *reply_param);
unsigned int qeth_count_elements(struct sk_buff *skb, unsigned int data_offset);
int qeth_get_elements_for_frags(struct sk_buff *);
int qeth_do_send_packet_fast(struct qeth_qdio_out_q *queue, struct sk_buff *skb,
			     struct qeth_hdr *hdr, unsigned int offset,
			     unsigned int hd_len);
+1 −2
Original line number Diff line number Diff line
@@ -3788,7 +3788,7 @@ EXPORT_SYMBOL_GPL(qeth_get_priority_queue);
 * Returns the number of pages, and thus QDIO buffer elements, needed to cover
 * fragmented part of the SKB. Returns zero for linear SKB.
 */
int qeth_get_elements_for_frags(struct sk_buff *skb)
static int qeth_get_elements_for_frags(struct sk_buff *skb)
{
	int cnt, elements = 0;

@@ -3801,7 +3801,6 @@ int qeth_get_elements_for_frags(struct sk_buff *skb)
	}
	return elements;
}
EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags);

/**
 * qeth_count_elements() -	Counts the number of QDIO buffer elements needed
+5 −146
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@
#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <net/ip6_fib.h>
#include <net/ip6_checksum.h>
#include <net/iucv/af_iucv.h>
#include <linux/hashtable.h>

@@ -2118,70 +2117,6 @@ static void qeth_l3_fill_tso_ext(struct qeth_hdr_tso *hdr,
	ext->dg_hdr_len = proto_len;
}

static void qeth_tso_fill_header(struct qeth_card *card,
		struct qeth_hdr *qhdr, struct sk_buff *skb)
{
	struct qeth_hdr_tso *hdr = (struct qeth_hdr_tso *)qhdr;
	struct tcphdr *tcph = tcp_hdr(skb);
	struct iphdr *iph = ip_hdr(skb);
	struct ipv6hdr *ip6h = ipv6_hdr(skb);

	/*set values which are fix for the first approach ...*/
	hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
	hdr->ext.imb_hdr_no  = 1;
	hdr->ext.hdr_type    = 1;
	hdr->ext.hdr_version = 1;
	hdr->ext.hdr_len     = 28;
	/*insert non-fix values */
	hdr->ext.mss = skb_shinfo(skb)->gso_size;
	hdr->ext.dg_hdr_len = (__u16)(ip_hdrlen(skb) + tcp_hdrlen(skb));
	hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
				       sizeof(struct qeth_hdr_tso));
	tcph->check = 0;
	if (be16_to_cpu(skb->protocol) == ETH_P_IPV6) {
		ip6h->payload_len = 0;
		tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
					       0, IPPROTO_TCP, 0);
	} else {
		/*OSA want us to set these values ...*/
		tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
					 0, IPPROTO_TCP, 0);
		iph->tot_len = 0;
		iph->check = 0;
	}
}

/**
 * qeth_l3_get_elements_no_tso() - find number of SBALEs for skb data for tso
 * @card:			   qeth card structure, to check max. elems.
 * @skb:			   SKB address
 * @extra_elems:		   extra elems needed, to check against max.
 *
 * Returns the number of pages, and thus QDIO buffer elements, needed to cover
 * skb data, including linear part and fragments, but excluding TCP header.
 * Checks if the result plus extra_elems fits under the limit for the card.
 * Returns 0 if it does not.
 * Note: extra_elems is not included in the returned result.
 */
static int qeth_l3_get_elements_no_tso(struct qeth_card *card,
			struct sk_buff *skb, int extra_elems)
{
	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,
	"Invalid size of TSO IP packet (Number=%d / Length=%d). Discarded.\n",
				elements + extra_elems, skb->len);
		return 0;
	}
	return elements;
}

static void qeth_l3_fixup_headers(struct sk_buff *skb)
{
	struct iphdr *iph = ip_hdr(skb);
@@ -2196,9 +2131,8 @@ static void qeth_l3_fixup_headers(struct sk_buff *skb)
	}
}

static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb,
				struct qeth_qdio_out_q *queue, int ipv,
				int cast_type)
static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb,
			struct qeth_qdio_out_q *queue, int ipv, int cast_type)
{
	unsigned int hw_hdr_len, proto_len, frame_len, elements;
	unsigned char eth_hdr[ETH_HLEN];
@@ -2282,81 +2216,6 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb,
	return rc;
}

static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb,
			struct qeth_qdio_out_q *queue, int ipv, int cast_type)
{
	struct qeth_hdr *hdr = NULL;
	struct sk_buff *new_skb = NULL;
	int tx_bytes = skb->len;
	unsigned int hd_len;
	int elements, rc;
	bool is_sg;

	/* create a clone with writeable headroom */
	new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso));
	if (!new_skb)
		return -ENOMEM;

	skb_pull(new_skb, ETH_HLEN);

	/* fix hardware limitation: as long as we do not have sbal
	 * chaining we can not send long frag lists
	 */
	if (!qeth_l3_get_elements_no_tso(card, new_skb, 1)) {
		rc = skb_linearize(new_skb);

		if (card->options.performance_stats) {
			if (rc)
				card->perf_stats.tx_linfail++;
			else
				card->perf_stats.tx_lin++;
		}
		if (rc)
			goto out;
	}

	hdr = skb_push(new_skb, sizeof(struct qeth_hdr_tso));
	memset(hdr, 0, sizeof(struct qeth_hdr_tso));
	qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type,
			    new_skb->len - sizeof(struct qeth_hdr_tso));
	qeth_tso_fill_header(card, hdr, new_skb);

	elements = qeth_l3_get_elements_no_tso(card, new_skb, 1);
	if (!elements) {
		rc = -E2BIG;
		goto out;
	}
	elements++;

	hd_len = sizeof(struct qeth_hdr_tso) + ip_hdrlen(new_skb) +
		 tcp_hdrlen(new_skb);

	if (qeth_hdr_chk_and_bounce(new_skb, &hdr, hd_len)) {
		rc = -EINVAL;
		goto out;
	}

	is_sg = skb_is_nonlinear(new_skb);
	rc = qeth_do_send_packet(card, queue, new_skb, hdr, hd_len, hd_len,
				 elements);
out:
	if (!rc) {
		if (new_skb != skb)
			dev_kfree_skb_any(skb);
		if (card->options.performance_stats) {
			card->perf_stats.buf_elements_sent += elements;
			if (is_sg)
				card->perf_stats.sg_skbs_sent++;
			card->perf_stats.large_send_bytes += tx_bytes;
			card->perf_stats.large_send_cnt++;
		}
	} else {
		if (new_skb != skb)
			dev_kfree_skb_any(new_skb);
	}
	return rc;
}

static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
					   struct net_device *dev)
{
@@ -2392,9 +2251,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
	}
	netif_stop_queue(dev);

	if (IS_IQD(card) || (!skb_is_gso(skb) && ipv == 4))
		rc = qeth_l3_xmit_offload(card, skb, queue, ipv, cast_type);
	else if (skb_is_gso(skb))
	if (ipv == 4 || IS_IQD(card))
		rc = qeth_l3_xmit(card, skb, queue, ipv, cast_type);
	else
		rc = qeth_xmit(card, skb, queue, ipv, cast_type,
@@ -2560,6 +2417,8 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
		card->dev->needed_headroom = sizeof(struct qeth_hdr);
		/* allow for de-acceleration of NETIF_F_HW_VLAN_CTAG_TX: */
		card->dev->needed_headroom += VLAN_HLEN;
		if (qeth_is_supported(card, IPA_OUTBOUND_TSO))
			card->dev->needed_headroom = sizeof(struct qeth_hdr_tso);

		/*IPv6 address autoconfiguration stuff*/
		qeth_l3_get_unique_id(card);