linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [net-next PATCH v3] ibmvnic: map L2/L3/L4 header descriptors to firmware
@ 2016-02-25  3:34 Thomas Falcon
  2016-02-25  5:12 ` David Miller
  0 siblings, 1 reply; 2+ messages in thread
From: Thomas Falcon @ 2016-02-25  3:34 UTC (permalink / raw)
  To: netdev; +Cc: linuxppc-dev

Allow the VNIC driver to provide descriptors containing
L2/L3/L4 headers to firmware.  This feature is needed
for greater hardware compatibility and enablement of offloading
technologies for some backing hardware.

Signed-off-by: Thomas Falcon <tlfalcon@linux.vnet.ibm.com>
---
v2: Fixed typo error caught by kbuild test bot
v3: Fixed erroneous patch sender
---
 drivers/net/ethernet/ibm/ibmvnic.c | 238 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 235 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 7d657084..43c1df6 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -61,6 +61,7 @@
 #include <linux/proc_fs.h>
 #include <linux/in.h>
 #include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <linux/irq.h>
 #include <linux/kthread.h>
 #include <linux/seq_file.h>
@@ -94,6 +95,7 @@ static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *);
 static int ibmvnic_send_crq(struct ibmvnic_adapter *, union ibmvnic_crq *);
 static int send_subcrq(struct ibmvnic_adapter *adapter, u64 remote_handle,
 		       union sub_crq *sub_crq);
+static int send_subcrq_indirect(struct ibmvnic_adapter *, u64, u64, u64);
 static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance);
 static int enable_scrq_irq(struct ibmvnic_adapter *,
 			   struct ibmvnic_sub_crq_queue *);
@@ -561,12 +563,177 @@ static int ibmvnic_close(struct net_device *netdev)
 	return 0;
 }
 
+/**
+ * build_hdr_data - creates L2/L3/L4 header data buffer
+ * @hdr_field - bitfield determining needed headers
+ * @skb - socket buffer
+ * @hdr_len - array of header lengths
+ * @tot_len - total length of data
+ *
+ * Reads hdr_field to determine which headers are needed by firmware.
+ * Builds a buffer containing these headers.  Saves individual header
+ * lengths and total buffer length to be used to build descriptors.
+ */
+static unsigned char *build_hdr_data(u8 hdr_field, struct sk_buff *skb,
+				     int *hdr_len, int *tot_len)
+{
+	unsigned char *hdr_data;
+	unsigned char *hdrs[3];
+	u8 proto = 0;
+	int len = 0;
+	int i;
+
+	if ((hdr_field >> 6) & 1) {
+		hdrs[0] = skb_mac_header(skb);
+		hdr_len[0] = sizeof(struct ethhdr);
+	}
+
+	if ((hdr_field >> 5) & 1) {
+		hdrs[1] = skb_network_header(skb);
+		if (skb->protocol == htons(ETH_P_IP))
+			hdr_len[1] = ip_hdr(skb)->ihl * 4;
+		else if (skb->protocol == htons(ETH_P_IPV6))
+			hdr_len[1] = sizeof(struct ipv6hdr);
+	}
+
+	if ((hdr_field >> 4) & 1) {
+		hdrs[2] = skb_transport_header(skb);
+		if (skb->protocol == htons(ETH_P_IP))
+			proto = ip_hdr(skb)->protocol;
+		else if (skb->protocol == htons(ETH_P_IPV6))
+			proto = ipv6_hdr(skb)->nexthdr;
+
+		if (proto == IPPROTO_TCP)
+			hdr_len[2] = tcp_hdrlen(skb);
+		else if (proto == IPPROTO_UDP)
+			hdr_len[2] = sizeof(struct udphdr);
+	}
+
+	*tot_len = hdr_len[0] + hdr_len[1] + hdr_len[2];
+
+	hdr_data = kmalloc(*tot_len, GFP_KERNEL);
+	if (!hdr_data)
+		return NULL;
+
+	for (i = 0; i < 3; i++) {
+		if (hdrs[i])
+			memcpy(hdr_data, hdrs[i] + len, hdr_len[i]);
+		len += hdr_len[i];
+	}
+	return hdr_data;
+}
+
+/**
+ * create_hdr_descs - create header and header extension descriptors
+ * @hdr_field - bitfield determining needed headers
+ * @data - buffer containing header data
+ * @len - length of data buffer
+ * @hdr_len - array of individual header lengths
+ * @scrq_arr - descriptor array
+ *
+ * Creates header and, if needed, header extension descriptors and
+ * places them in a descriptor array, scrq_arr
+ */
+
+void create_hdr_descs(u8 hdr_field, unsigned char *data, int len, int *hdr_len,
+		      union sub_crq *scrq_arr)
+{
+	union sub_crq hdr_desc;
+	int tmp_len = len;
+	int tmp;
+
+	while (tmp_len > 0) {
+		unsigned char *cur = data + len - tmp_len;
+
+		memset(&hdr_desc, 0, sizeof(hdr_desc));
+		if (cur != data) {
+			tmp = tmp_len > 29 ? 29 : tmp_len;
+			hdr_desc.hdr_ext.first = IBMVNIC_CRQ_CMD;
+			hdr_desc.hdr_ext.type = IBMVNIC_HDR_EXT_DESC;
+			hdr_desc.hdr_ext.len = tmp;
+		} else {
+			tmp = tmp_len > 24 ? 24 : tmp_len;
+			hdr_desc.hdr.first = IBMVNIC_CRQ_CMD;
+			hdr_desc.hdr.type = IBMVNIC_HDR_DESC;
+			hdr_desc.hdr.len = tmp;
+			hdr_desc.hdr.l2_len = (u8)hdr_len[0];
+			hdr_desc.hdr.l3_len = (u16)hdr_len[1];
+			hdr_desc.hdr.l4_len = (u8)hdr_len[2];
+			hdr_desc.hdr.flag = hdr_field << 1;
+		}
+		memcpy(hdr_desc.hdr.data, cur, tmp);
+		tmp_len -= tmp;
+		*scrq_arr = hdr_desc;
+		scrq_arr++;
+	}
+}
+
+static int calc_num_hdr_descs(int len)
+{
+	int num_descs = 1;
+
+	len -= 24;
+	if (len > 0)
+		num_descs += len % 29 ? len / 29 + 1 : len / 29;
+	return num_descs;
+}
+
+static union sub_crq *alloc_scrq_array(int num, union sub_crq subcrq)
+{
+	union sub_crq *scrq_arr;
+
+	scrq_arr = kcalloc(num, sizeof(*scrq_arr), GFP_KERNEL);
+	if (!scrq_arr)
+		return NULL;
+	scrq_arr[0] = subcrq;
+	return scrq_arr;
+}
+
+/**
+ * build_hdr_descs_arr - build a header descriptor array
+ * @skb - socket buffer
+ * @num_entries - number of descriptors to be sent
+ * @subcrq - first TX descriptor
+ * @hdr_field - bit field determining which headers will be sent
+ *
+ * This function will build a TX with descriptor array with applicable
+ * L2/L3/L4 packet header descriptors to be sent by send_subcrq_indirect.
+ */
+
+static union sub_crq *build_hdr_descs_arr(struct sk_buff *skb,
+					  int *num_entries,
+					  union sub_crq subcrq, u8 hdr_field)
+{
+	unsigned char *hdr_data;
+	union sub_crq *entries;
+	int hdr_len[3] = {0};
+	int tot_len;
+
+	hdr_data = build_hdr_data(hdr_field, skb, hdr_len, &tot_len);
+	if (!hdr_data)
+		return NULL;
+
+	*num_entries += calc_num_hdr_descs(tot_len);
+
+	entries = alloc_scrq_array(*num_entries, subcrq);
+	if (!entries) {
+		kfree(hdr_data);
+		return NULL;
+	}
+
+	create_hdr_descs(hdr_field, hdr_data, tot_len, hdr_len, entries + 1);
+	kfree(hdr_data);
+	return entries;
+}
+
 static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
 	struct ibmvnic_adapter *adapter = netdev_priv(netdev);
 	int queue_num = skb_get_queue_mapping(skb);
+	u8 *hdrs = (u8 *)&adapter->tx_rx_desc_req;
 	struct device *dev = &adapter->vdev->dev;
 	struct ibmvnic_tx_buff *tx_buff = NULL;
+	union sub_crq *indir_arr = NULL;
 	struct ibmvnic_tx_pool *tx_pool;
 	unsigned int tx_send_failed = 0;
 	unsigned int tx_map_failed = 0;
@@ -576,9 +743,11 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
 	dma_addr_t data_dma_addr;
 	struct netdev_queue *txq;
 	bool used_bounce = false;
+	dma_addr_t indir_ioba;
 	unsigned long lpar_rc;
 	union sub_crq tx_crq;
 	unsigned int offset;
+	int num_entries = 1;
 	unsigned char *dst;
 	u64 *handle_array;
 	int index = 0;
@@ -644,10 +813,47 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
 			tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_UDP;
 	}
 
-	if (skb->ip_summed == CHECKSUM_PARTIAL)
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
 		tx_crq.v1.flags1 |= IBMVNIC_TX_CHKSUM_OFFLOAD;
+		hdrs += 2;
+	}
+
+	/* determine if l2/3/4 headers are sent to firmware */
+	if ((*hdrs >> 7) & 1 &&
+	    (skb->protocol == htons(ETH_P_IP) ||
+	     skb->protocol == htons(ETH_P_IPV6))) {
+		indir_arr = build_hdr_descs_arr(skb,
+						&num_entries,
+						tx_crq, *hdrs);
+		if (!indir_arr) {
+			dev_err(dev, "tx: unable to create descriptor array\n");
+			tx_send_failed++;
+			tx_dropped++;
+			ret = NETDEV_TX_BUSY;
+			goto out;
+		}
+		tx_crq.v1.n_crq_elem = num_entries;
+		indir_ioba = dma_map_single(dev, indir_arr,
+					    num_entries * sizeof(*indir_arr),
+					    DMA_TO_DEVICE);
+		if (dma_mapping_error(dev, indir_ioba)) {
+			if (!firmware_has_feature(FW_FEATURE_CMO))
+				dev_err(dev, "tx: unable to map descriptor array\n");
+			tx_map_failed++;
+			tx_dropped++;
+			ret = NETDEV_TX_BUSY;
+			goto out;
+		}
 
-	lpar_rc = send_subcrq(adapter, handle_array[0], &tx_crq);
+		lpar_rc = send_subcrq_indirect(adapter, handle_array[0],
+					       (u64)indir_ioba,
+					       (u64)num_entries);
+		dma_unmap_single(dev, indir_ioba,
+				 num_entries * sizeof(*indir_arr),
+				 DMA_TO_DEVICE);
+	} else {
+		lpar_rc = send_subcrq(adapter, handle_array[0], &tx_crq);
+	}
 
 	if (lpar_rc != H_SUCCESS) {
 		dev_err(dev, "tx failed with code %ld\n", lpar_rc);
@@ -674,6 +880,7 @@ out:
 	netdev->stats.tx_packets += tx_packets;
 	adapter->tx_send_failed += tx_send_failed;
 	adapter->tx_map_failed += tx_map_failed;
+	kfree(indir_arr);
 
 	return ret;
 }
@@ -1494,6 +1701,30 @@ static int send_subcrq(struct ibmvnic_adapter *adapter, u64 remote_handle,
 	return rc;
 }
 
+static int send_subcrq_indirect(struct ibmvnic_adapter *adapter,
+				u64 remote_handle, u64 ioba, u64 num_entries)
+{
+	unsigned int ua = adapter->vdev->unit_address;
+	struct device *dev = &adapter->vdev->dev;
+	int rc;
+
+	/* Make sure the hypervisor sees the complete request */
+	mb();
+
+	rc = plpar_hcall_norets(H_SEND_SUB_CRQ_INDIRECT, ua,
+				cpu_to_be64(remote_handle),
+				cpu_to_be64(ioba),
+				cpu_to_be64(num_entries));
+
+	if (rc) {
+		if (rc == H_CLOSED)
+			dev_warn(dev, "CRQ Queue closed\n");
+		dev_err(dev, "Send (indirect) error (rc=%d)\n", rc);
+	}
+
+	return rc;
+}
+
 static int ibmvnic_send_crq(struct ibmvnic_adapter *adapter,
 			    union ibmvnic_crq *crq)
 {
@@ -2447,7 +2678,8 @@ static void handle_query_cap_rsp(union ibmvnic_crq *crq,
 			   adapter->opt_rxba_entries_per_subcrq);
 		break;
 	case TX_RX_DESC_REQ:
-		adapter->tx_rx_desc_req = crq->query_capability.number;
+		adapter->tx_rx_desc_req =
+				be64_to_cpu(crq->query_capability.number);
 		netdev_dbg(netdev, "tx_rx_desc_req = %llx\n",
 			   adapter->tx_rx_desc_req);
 		break;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [net-next PATCH v3] ibmvnic: map L2/L3/L4 header descriptors to firmware
  2016-02-25  3:34 [net-next PATCH v3] ibmvnic: map L2/L3/L4 header descriptors to firmware Thomas Falcon
@ 2016-02-25  5:12 ` David Miller
  0 siblings, 0 replies; 2+ messages in thread
From: David Miller @ 2016-02-25  5:12 UTC (permalink / raw)
  To: tlfalcon; +Cc: netdev, linuxppc-dev

From: Thomas Falcon <tlfalcon@linux.vnet.ibm.com>
Date: Wed, 24 Feb 2016 21:34:43 -0600

> +
> +static union sub_crq *build_hdr_descs_arr(struct sk_buff *skb,
> +					  int *num_entries,
> +					  union sub_crq subcrq, u8 hdr_field)
> +{
> +	unsigned char *hdr_data;
> +	union sub_crq *entries;
> +	int hdr_len[3] = {0};
> +	int tot_len;
> +
> +	hdr_data = build_hdr_data(hdr_field, skb, hdr_len, &tot_len);
> +	if (!hdr_data)
> +		return NULL;
> +
> +	*num_entries += calc_num_hdr_descs(tot_len);
> +
> +	entries = alloc_scrq_array(*num_entries, subcrq);

Doing an extra memory allocation for every transmit packet is going
to kill performance.  We are spending enormous amounts of effort
trying to eliminate the bare minimum memory allocations that occur
as-is because that cost is what dominates and this patch is going
to make it even worse for this device.

I don't care what offloads this enables, the implementation is
unacceptable from a performance standpoint.

Also, you should only add this new code when the offloads that make
use of the feature are added as well, otherwise it is unused bloat.

I am not applying this patch, there are so many things wrong with it.

Sorry.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2016-02-25  5:12 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-25  3:34 [net-next PATCH v3] ibmvnic: map L2/L3/L4 header descriptors to firmware Thomas Falcon
2016-02-25  5:12 ` David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).