From: Petr Uzel <petr.uzel@suse.cz>
Date: Mon, 16 Mar 2015 13:10:22 +0100
Subject: ethtool: set tx-nocache-copy
Patch-mainline: v3.4
Git-commit: 6042804cf6ecc229e600cf78124a152a40b0dc89 (partial)
References: bnc#845378

Implement getting/setting tx-nocache-copy feature. In upstream, this was
introduced by

  6042804cf  ("Change -k/-K options to use ETHTOOL_{G,S}FEATURES")

This patch is a minimal backport supporting only tx-nocache-copy and not
changing handling of other features.

Signed-off-by: Petr Uzel <petr.uzel@suse.cz>
Acked-by: Michal Kubecek <mkubecek@suse.cz>
---
 ethtool.8.in |   4 ++
 ethtool.c    | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 177 insertions(+), 5 deletions(-)

diff --git a/ethtool.8.in b/ethtool.8.in
index 9680336..6836ca1 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -215,6 +215,7 @@ ethtool \- query or control network driver and hardware settings
 .B2 rxvlan on off
 .B2 txvlan on off
 .B2 rxhash on off
+.B2 tx-nocache-copy on off
 .HP
 .B ethtool \-p|\-\-identify
 .I ethX
@@ -448,6 +449,9 @@ Specifies whether TX VLAN acceleration should be enabled
 .A2 rxhash on off
 Specifies whether receive hashing offload should be enabled
 .TP
+.A2 tx-nocache-copy on off
+Specifies whether no-cache copy from user on transmit should be enabled
+.TP
 .B \-p \-\-identify
 Initiates adapter-specific action intended to enable an operator to
 easily identify the adapter by sight.  Typically this involves
diff --git a/ethtool.c b/ethtool.c
index 3c73c33..e7216b2 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -24,6 +24,7 @@
  */
 
 #include "ethtool-util.h"
+#include "ethtool-bitops.h"
 #include <string.h>
 #include <stdlib.h>
 #include <sys/stat.h>
@@ -64,6 +65,14 @@ enum {
 };
 #endif
 
+#define FEATURE_BITS_TO_BLOCKS(n_bits)		DIV_ROUND_UP(n_bits, 32U)
+#define FEATURE_WORD(blocks, index, field)	((blocks)[(index) / 32U].field)
+#define FEATURE_FIELD_FLAG(index)		(1U << (index) % 32U)
+#define FEATURE_BIT_SET(blocks, index, field)			\
+	(FEATURE_WORD(blocks, index, field) |= FEATURE_FIELD_FLAG(index))
+#define FEATURE_BIT_IS_SET(blocks, index, field)		\
+	(FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
+
 static int parse_wolopts(char *optstr, u32 *data);
 static char *unparse_wolopts(int wolopts);
 static void get_mac_addr(char *src, unsigned char *dest);
@@ -205,6 +214,7 @@ static struct option {
 		"		[ txvlan on|off ]\n"
 		"		[ ntuple on|off ]\n"
 		"		[ rxhash on|off ]\n"
+		"		[ tx-nocache-copy on|off ]\n"
     },
     { "-i", "--driver", MODE_GDRV, "Show driver information" },
     { "-d", "--register-dump", MODE_GREGS, "Do a register dump",
@@ -327,6 +337,7 @@ static int off_gso_wanted = -1;
 static u32 off_flags_wanted = 0;
 static u32 off_flags_mask = 0;
 static int off_gro_wanted = -1;
+static int off_tnc_wanted = -1;
 
 static struct ethtool_pauseparam epause;
 static int gpause_changed = 0;
@@ -486,6 +497,7 @@ static struct cmdline_info cmdline_offload[] = {
 	{ "lro", CMDL_FLAG, &off_flags_wanted, NULL,
 	  ETH_FLAG_LRO, &off_flags_mask },
 	{ "gro", CMDL_BOOL, &off_gro_wanted, NULL },
+	{ "tx-nocache-copy", CMDL_BOOL, &off_tnc_wanted, NULL },
 	{ "rxvlan", CMDL_FLAG, &off_flags_wanted, NULL,
 	  ETH_FLAG_RXVLAN, &off_flags_mask },
 	{ "txvlan", CMDL_FLAG, &off_flags_wanted, NULL,
@@ -2017,7 +2029,7 @@ static int dump_coalesce(void)
 
 static int dump_offload(int rx, int tx, int sg, int tso, int ufo, int gso,
 			int gro, int lro, int rxvlan, int txvlan, int ntuple,
-			int rxhash)
+			int rxhash, int tnc)
 {
 	fprintf(stdout,
 		"rx-checksumming: %s\n"
@@ -2031,7 +2043,8 @@ static int dump_offload(int rx, int tx, int sg, int tso, int ufo, int gso,
 		"rx-vlan-offload: %s\n"
 		"tx-vlan-offload: %s\n"
 		"ntuple-filters: %s\n"
-		"receive-hashing: %s\n",
+		"receive-hashing: %s\n"
+		"tx-nocache-copy: %s\n",
 		rx ? "on" : "off",
 		tx ? "on" : "off",
 		sg ? "on" : "off",
@@ -2043,7 +2056,8 @@ static int dump_offload(int rx, int tx, int sg, int tso, int ufo, int gso,
 		rxvlan ? "on" : "off",
 		txvlan ? "on" : "off",
 		ntuple ? "on" : "off",
-		rxhash ? "on" : "off");
+		rxhash ? "on" : "off",
+		tnc ? "on" : "off");
 
 	return 0;
 }
@@ -2430,12 +2444,151 @@ static int do_scoalesce(int fd, struct ifreq *ifr)
 	return 0;
 }
 
+int get_off_features_count(int fd, struct ifreq *ifr)
+{
+	struct {
+		struct ethtool_sset_info hdr;
+		u32 buf[1];
+	} sset_info;
+
+	sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
+	sset_info.hdr.reserved = 0;
+	sset_info.hdr.sset_mask = 1ULL << ETH_SS_FEATURES;
+	ifr->ifr_data = (caddr_t)&sset_info;
+	if (send_ioctl(fd, ifr) == 0)
+		return(sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0);
+	else
+		return -1;
+}
+
+int get_tnc_index(int fd, struct ifreq *ifr, u32 len)
+{
+	int idx = -1;
+	u32 i = 0;
+
+	struct ethtool_gstrings *strings;
+	strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
+	if (!strings) {
+		perror("Cannot allocate memory for offload feature descriptions");
+		return -1;
+	}
+
+	strings->cmd = ETHTOOL_GSTRINGS;
+	strings->string_set = ETH_SS_FEATURES;
+	strings->len = len;
+	ifr->ifr_data = (caddr_t)strings;
+	if (len != 0 && send_ioctl(fd, ifr)) {
+		perror("Cannot get offload feature descriptions");
+		free(strings);
+		return -1;
+	}
+
+	/* Find index of tx-nocache-copy feature */
+	for (i = 0; i < len; i++)
+		if (!strncmp(strings->data + (i * ETH_GSTRING_LEN),
+			     "tx-nocache-copy", ETH_GSTRING_LEN))
+			idx = i;
+
+	free(strings);
+	return idx;
+}
+
+int get_tnc_value(int fd, struct ifreq *ifr)
+{
+	struct ethtool_gfeatures *features;
+	int err, ret, len, idx;
+
+	len = get_off_features_count(fd, ifr);
+	if (len == -1) {
+		fprintf(stderr, "Cannot get number of offload features\n");
+		return -1;
+	}
+
+	idx = get_tnc_index(fd, ifr, len);
+	if (idx == -1) {
+		fprintf(stderr, "tx-nocache-copy not found in supported offload features\n");
+		return -1;
+	}
+
+	features = malloc(sizeof(*features) +
+			   FEATURE_BITS_TO_BLOCKS(len) *
+			   sizeof(features->features[0]));
+	if (!features) {
+		perror("Cannot allocate memory for offload feature data");
+		return -1;
+	}
+
+	features->cmd = ETHTOOL_GFEATURES;
+	features->size = FEATURE_BITS_TO_BLOCKS(len);
+	ifr->ifr_data = (caddr_t)features;
+	err = send_ioctl(fd, ifr);
+	if (err) {
+		perror("Cannot get offload feature data");
+		free(features);
+		return -1;
+	}
+
+	ret = FEATURE_BIT_IS_SET(features->features, idx, active);
+	free(features);
+	return ret;
+}
+
+int set_tnc_value(int fd, struct ifreq *ifr, int enable)
+{
+	int len, idx, err;
+
+	len = get_off_features_count(fd, ifr);
+	if (len == -1) {
+		fprintf(stderr, "Cannot get number of offload features\n");
+		return -1;
+	}
+
+	idx = get_tnc_index(fd, ifr, len);
+	if (idx == -1) {
+		fprintf(stderr, "tx-nocache-copy not found in supported offload features\n");
+		return -1;
+	}
+
+	struct ethtool_sfeatures *efeatures;
+
+	efeatures = malloc(sizeof(*efeatures) +
+			   FEATURE_BITS_TO_BLOCKS(len) *
+			   sizeof(efeatures->features[0]));
+	if (!efeatures) {
+		perror("Cannot allocate memory for offload feature data");
+		return -1;
+	}
+	efeatures->cmd = ETHTOOL_SFEATURES;
+	efeatures->size = FEATURE_BITS_TO_BLOCKS(len);
+	memset(efeatures->features, 0,
+	       FEATURE_BITS_TO_BLOCKS(len) *
+	       sizeof(efeatures->features[0]));
+
+	/* Set appropriate bits in the efeatures bitfield */
+	FEATURE_BIT_SET(efeatures->features, idx, valid);
+
+	if (enable)
+		FEATURE_BIT_SET(efeatures->features, idx, requested);
+	ifr->ifr_data = (caddr_t)efeatures;
+
+	err = send_ioctl(fd, ifr);
+	if (err < 0) {
+		perror("Cannot change tx-nocache-copy settings");
+		free(efeatures);
+		return -1;
+	}
+
+	free(efeatures);
+	return 0;
+}
+
+
 static int do_goffload(int fd, struct ifreq *ifr)
 {
 	struct ethtool_value eval;
 	int err, allfail = 1, rx = 0, tx = 0, sg = 0;
 	int tso = 0, ufo = 0, gso = 0, gro = 0, lro = 0, rxvlan = 0, txvlan = 0,
-	    ntuple = 0, rxhash = 0;
+	    ntuple = 0, rxhash = 0, tnc = 0;
 
 	fprintf(stdout, "Offload parameters for %s:\n", devname);
 
@@ -2523,13 +2676,19 @@ static int do_goffload(int fd, struct ifreq *ifr)
 		allfail = 0;
 	}
 
+	tnc = get_tnc_value(fd, ifr);
+	if (tnc == -1)
+	    fprintf(stderr, "Can not get status of tx-nocache-copy\n");
+	else
+	    allfail = 0;
+
 	if (allfail) {
 		fprintf(stdout, "no offload info available\n");
 		return 83;
 	}
 
 	return dump_offload(rx, tx, sg, tso, ufo, gso, gro, lro, rxvlan, txvlan,
-			    ntuple, rxhash);
+			    ntuple, rxhash, tnc);
 }
 
 static int do_soffload(int fd, struct ifreq *ifr)
@@ -2639,6 +2798,15 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		}
 	}
 
+	if (off_tnc_wanted >= 0) {
+		changed = 1;
+		err = set_tnc_value(fd, ifr, (off_tnc_wanted == 1));
+		if (err == -1) {
+		    fprintf(stderr, "Cannot set device tx-nocache-copy settings\n");
+		    return 106;
+		}
+	}
+
 	if (!changed) {
 		fprintf(stdout, "no offload settings changed\n");
 	}
-- 
2.3.3

