d7cf3b
From 10b4bae4d7f2fa4768fbe90cbfa18ed6059e5a5b Mon Sep 17 00:00:00 2001
d7cf3b
From: Edward Cree <ecree@solarflare.com>
d7cf3b
Date: Fri, 9 Mar 2018 15:04:12 +0000
d7cf3b
Subject: [PATCH 16/18] ethtool: add support for extra RSS contexts and RSS
d7cf3b
 steering filters
d7cf3b
d7cf3b
RSS contexts can be created on a device with -X ... context new, modified
d7cf3b
 with -X ... context N, and deleted with -X ... context N delete.
d7cf3b
N-tuple filters can be directed at those contexts with -N ... context N.
d7cf3b
d7cf3b
Signed-off-by: Edward Cree <ecree@solarflare.com>
d7cf3b
Signed-off-by: John W. Linville <linville@tuxdriver.com>
d7cf3b
(cherry picked from commit f5d55b967e0c5757e423805a70d1a298e307e91e)
d7cf3b
---
d7cf3b
 ethtool.c  | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------
d7cf3b
 internal.h |   4 +-
d7cf3b
 rxclass.c  |  58 +++++++++++++++++++++----
d7cf3b
 3 files changed, 173 insertions(+), 30 deletions(-)
d7cf3b
d7cf3b
diff --git a/ethtool.c b/ethtool.c
d7cf3b
index 2e9ee2c..a276fdb 100644
d7cf3b
--- a/ethtool.c
d7cf3b
+++ b/ethtool.c
d7cf3b
@@ -1513,7 +1513,7 @@ static void dump_features(const struct feature_defs *defs,
d7cf3b
 
d7cf3b
 static int dump_rxfhash(int fhash, u64 val)
d7cf3b
 {
d7cf3b
-	switch (fhash) {
d7cf3b
+	switch (fhash & ~FLOW_RSS) {
d7cf3b
 	case TCP_V4_FLOW:
d7cf3b
 		fprintf(stdout, "TCP over IPV4 flows");
d7cf3b
 		break;
d7cf3b
@@ -3527,11 +3527,20 @@ static int do_srxclass(struct cmd_context *ctx)
d7cf3b
 	if (ctx->argc < 2)
d7cf3b
 		exit_bad_args();
d7cf3b
 
d7cf3b
-	if (ctx->argc == 3 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
d7cf3b
+	if (!strcmp(ctx->argp[0], "rx-flow-hash")) {
d7cf3b
 		int rx_fhash_set;
d7cf3b
 		u32 rx_fhash_val;
d7cf3b
 		struct ethtool_rxnfc nfccmd;
d7cf3b
+		bool flow_rss = false;
d7cf3b
 
d7cf3b
+		if (ctx->argc == 5) {
d7cf3b
+			if (strcmp(ctx->argp[3], "context"))
d7cf3b
+				exit_bad_args();
d7cf3b
+			flow_rss = true;
d7cf3b
+			nfccmd.rss_context = get_u32(ctx->argp[4], 0);
d7cf3b
+		} else if (ctx->argc != 3) {
d7cf3b
+			exit_bad_args();
d7cf3b
+		}
d7cf3b
 		rx_fhash_set = rxflow_str_to_type(ctx->argp[1]);
d7cf3b
 		if (!rx_fhash_set)
d7cf3b
 			exit_bad_args();
d7cf3b
@@ -3541,16 +3550,19 @@ static int do_srxclass(struct cmd_context *ctx)
d7cf3b
 		nfccmd.cmd = ETHTOOL_SRXFH;
d7cf3b
 		nfccmd.flow_type = rx_fhash_set;
d7cf3b
 		nfccmd.data = rx_fhash_val;
d7cf3b
+		if (flow_rss)
d7cf3b
+			nfccmd.flow_type |= FLOW_RSS;
d7cf3b
 
d7cf3b
 		err = send_ioctl(ctx, &nfccmd);
d7cf3b
 		if (err < 0)
d7cf3b
 			perror("Cannot change RX network flow hashing options");
d7cf3b
 	} else if (!strcmp(ctx->argp[0], "flow-type")) {	
d7cf3b
 		struct ethtool_rx_flow_spec rx_rule_fs;
d7cf3b
+		__u32 rss_context = 0;
d7cf3b
 
d7cf3b
 		ctx->argc--;
d7cf3b
 		ctx->argp++;
d7cf3b
-		if (rxclass_parse_ruleopts(ctx, &rx_rule_fs) < 0)
d7cf3b
+		if (rxclass_parse_ruleopts(ctx, &rx_rule_fs, &rss_context) < 0)
d7cf3b
 			exit_bad_args();
d7cf3b
 
d7cf3b
 		/* attempt to add rule via N-tuple specifier */
d7cf3b
@@ -3559,7 +3571,7 @@ static int do_srxclass(struct cmd_context *ctx)
d7cf3b
 			return 0;
d7cf3b
 
d7cf3b
 		/* attempt to add rule via network flow classifier */
d7cf3b
-		err = rxclass_rule_ins(ctx, &rx_rule_fs);
d7cf3b
+		err = rxclass_rule_ins(ctx, &rx_rule_fs, rss_context);
d7cf3b
 		if (err < 0) {
d7cf3b
 			fprintf(stderr, "Cannot insert"
d7cf3b
 				" classification rule\n");
d7cf3b
@@ -3588,8 +3600,18 @@ static int do_grxclass(struct cmd_context *ctx)
d7cf3b
 	struct ethtool_rxnfc nfccmd;
d7cf3b
 	int err;
d7cf3b
 
d7cf3b
-	if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
d7cf3b
+	if (ctx->argc > 0 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
d7cf3b
 		int rx_fhash_get;
d7cf3b
+		bool flow_rss = false;
d7cf3b
+
d7cf3b
+		if (ctx->argc == 4) {
d7cf3b
+			if (strcmp(ctx->argp[2], "context"))
d7cf3b
+				exit_bad_args();
d7cf3b
+			flow_rss = true;
d7cf3b
+			nfccmd.rss_context = get_u32(ctx->argp[3], 0);
d7cf3b
+		} else if (ctx->argc != 2) {
d7cf3b
+			exit_bad_args();
d7cf3b
+		}
d7cf3b
 
d7cf3b
 		rx_fhash_get = rxflow_str_to_type(ctx->argp[1]);
d7cf3b
 		if (!rx_fhash_get)
d7cf3b
@@ -3597,11 +3619,17 @@ static int do_grxclass(struct cmd_context *ctx)
d7cf3b
 
d7cf3b
 		nfccmd.cmd = ETHTOOL_GRXFH;
d7cf3b
 		nfccmd.flow_type = rx_fhash_get;
d7cf3b
+		if (flow_rss)
d7cf3b
+			nfccmd.flow_type |= FLOW_RSS;
d7cf3b
 		err = send_ioctl(ctx, &nfccmd);
d7cf3b
-		if (err < 0)
d7cf3b
+		if (err < 0) {
d7cf3b
 			perror("Cannot get RX network flow hashing options");
d7cf3b
-		else
d7cf3b
+		} else {
d7cf3b
+			if (flow_rss)
d7cf3b
+				fprintf(stdout, "For RSS context %u:\n",
d7cf3b
+					nfccmd.rss_context);
d7cf3b
 			dump_rxfhash(rx_fhash_get, nfccmd.data);
d7cf3b
+		}
d7cf3b
 	} else if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rule")) {
d7cf3b
 		int rx_class_rule_get =
d7cf3b
 			get_uint_range(ctx->argp[1], 0, INT_MAX);
d7cf3b
@@ -3693,10 +3721,23 @@ static int do_grxfh(struct cmd_context *ctx)
d7cf3b
 	struct ethtool_rxfh rss_head = {0};
d7cf3b
 	struct ethtool_rxnfc ring_count;
d7cf3b
 	struct ethtool_rxfh *rss;
d7cf3b
+	u32 rss_context = 0;
d7cf3b
 	u32 i, indir_bytes;
d7cf3b
+	int arg_num = 0;
d7cf3b
 	char *hkey;
d7cf3b
 	int err;
d7cf3b
 
d7cf3b
+	while (arg_num < ctx->argc) {
d7cf3b
+		if (!strcmp(ctx->argp[arg_num], "context")) {
d7cf3b
+			++arg_num;
d7cf3b
+			rss_context = get_int_range(ctx->argp[arg_num], 0, 1,
d7cf3b
+						    ETH_RXFH_CONTEXT_ALLOC - 1);
d7cf3b
+			++arg_num;
d7cf3b
+		} else {
d7cf3b
+			exit_bad_args();
d7cf3b
+		}
d7cf3b
+	}
d7cf3b
+
d7cf3b
 	ring_count.cmd = ETHTOOL_GRXRINGS;
d7cf3b
 	err = send_ioctl(ctx, &ring_count);
d7cf3b
 	if (err < 0) {
d7cf3b
@@ -3705,6 +3746,7 @@ static int do_grxfh(struct cmd_context *ctx)
d7cf3b
 	}
d7cf3b
 
d7cf3b
 	rss_head.cmd = ETHTOOL_GRSSH;
d7cf3b
+	rss_head.rss_context = rss_context;
d7cf3b
 	err = send_ioctl(ctx, &rss_head);
d7cf3b
 	if (err < 0 && errno == EOPNOTSUPP) {
d7cf3b
 		return do_grxfhindir(ctx, &ring_count);
d7cf3b
@@ -3722,6 +3764,7 @@ static int do_grxfh(struct cmd_context *ctx)
d7cf3b
 	}
d7cf3b
 
d7cf3b
 	rss->cmd = ETHTOOL_GRSSH;
d7cf3b
+	rss->rss_context = rss_context;
d7cf3b
 	rss->indir_size = rss_head.indir_size;
d7cf3b
 	rss->key_size = rss_head.key_size;
d7cf3b
 	err = send_ioctl(ctx, rss);
d7cf3b
@@ -3882,6 +3925,8 @@ static int do_srxfh(struct cmd_context *ctx)
d7cf3b
 	u32 req_hfunc = 0;
d7cf3b
 	u32 entry_size = sizeof(rss_head.rss_config[0]);
d7cf3b
 	u32 num_weights = 0;
d7cf3b
+	u32 rss_context = 0;
d7cf3b
+	int delete = 0;
d7cf3b
 
d7cf3b
 	if (ctx->argc < 1)
d7cf3b
 		exit_bad_args();
d7cf3b
@@ -3917,6 +3962,18 @@ static int do_srxfh(struct cmd_context *ctx)
d7cf3b
 			if (!req_hfunc_name)
d7cf3b
 				exit_bad_args();
d7cf3b
 			++arg_num;
d7cf3b
+		} else if (!strcmp(ctx->argp[arg_num], "context")) {
d7cf3b
+			++arg_num;
d7cf3b
+			if(!strcmp(ctx->argp[arg_num], "new"))
d7cf3b
+				rss_context = ETH_RXFH_CONTEXT_ALLOC;
d7cf3b
+			else
d7cf3b
+				rss_context = get_int_range(
d7cf3b
+						ctx->argp[arg_num], 0, 1,
d7cf3b
+						ETH_RXFH_CONTEXT_ALLOC - 1);
d7cf3b
+			++arg_num;
d7cf3b
+		} else if (!strcmp(ctx->argp[arg_num], "delete")) {
d7cf3b
+			++arg_num;
d7cf3b
+			delete = 1;
d7cf3b
 		} else {
d7cf3b
 			exit_bad_args();
d7cf3b
 		}
d7cf3b
@@ -3940,6 +3997,41 @@ static int do_srxfh(struct cmd_context *ctx)
d7cf3b
 		return 1;
d7cf3b
 	}
d7cf3b
 
d7cf3b
+	if (rxfhindir_default && rss_context) {
d7cf3b
+		fprintf(stderr,
d7cf3b
+			"Default and context options are mutually exclusive\n");
d7cf3b
+		return 1;
d7cf3b
+	}
d7cf3b
+
d7cf3b
+	if (delete && !rss_context) {
d7cf3b
+		fprintf(stderr, "Delete option requires context option\n");
d7cf3b
+		return 1;
d7cf3b
+	}
d7cf3b
+
d7cf3b
+	if (delete && rxfhindir_weight) {
d7cf3b
+		fprintf(stderr,
d7cf3b
+			"Delete and weight options are mutually exclusive\n");
d7cf3b
+		return 1;
d7cf3b
+	}
d7cf3b
+
d7cf3b
+	if (delete && rxfhindir_equal) {
d7cf3b
+		fprintf(stderr,
d7cf3b
+			"Delete and equal options are mutually exclusive\n");
d7cf3b
+		return 1;
d7cf3b
+	}
d7cf3b
+
d7cf3b
+	if (delete && rxfhindir_default) {
d7cf3b
+		fprintf(stderr,
d7cf3b
+			"Delete and default options are mutually exclusive\n");
d7cf3b
+		return 1;
d7cf3b
+	}
d7cf3b
+
d7cf3b
+	if (delete && rxfhindir_key) {
d7cf3b
+		fprintf(stderr,
d7cf3b
+			"Delete and hkey options are mutually exclusive\n");
d7cf3b
+		return 1;
d7cf3b
+	}
d7cf3b
+
d7cf3b
 	ring_count.cmd = ETHTOOL_GRXRINGS;
d7cf3b
 	err = send_ioctl(ctx, &ring_count);
d7cf3b
 	if (err < 0) {
d7cf3b
@@ -3950,7 +4042,7 @@ static int do_srxfh(struct cmd_context *ctx)
d7cf3b
 	rss_head.cmd = ETHTOOL_GRSSH;
d7cf3b
 	err = send_ioctl(ctx, &rss_head);
d7cf3b
 	if (err < 0 && errno == EOPNOTSUPP && !rxfhindir_key &&
d7cf3b
-	    !req_hfunc_name) {
d7cf3b
+	    !req_hfunc_name && !rss_context) {
d7cf3b
 		return do_srxfhindir(ctx, rxfhindir_default, rxfhindir_equal,
d7cf3b
 				     rxfhindir_weight, num_weights);
d7cf3b
 	} else if (err < 0) {
d7cf3b
@@ -3998,14 +4090,19 @@ static int do_srxfh(struct cmd_context *ctx)
d7cf3b
 		goto free;
d7cf3b
 	}
d7cf3b
 	rss->cmd = ETHTOOL_SRSSH;
d7cf3b
-	rss->indir_size = rss_head.indir_size;
d7cf3b
-	rss->key_size = rss_head.key_size;
d7cf3b
+	rss->rss_context = rss_context;
d7cf3b
 	rss->hfunc = req_hfunc;
d7cf3b
-
d7cf3b
-	if (fill_indir_table(&rss->indir_size, rss->rss_config, rxfhindir_default,
d7cf3b
-			     rxfhindir_equal, rxfhindir_weight, num_weights)) {
d7cf3b
-		err = 1;
d7cf3b
-		goto free;
d7cf3b
+	if (delete) {
d7cf3b
+		rss->indir_size = rss->key_size = 0;
d7cf3b
+	} else {
d7cf3b
+		rss->indir_size = rss_head.indir_size;
d7cf3b
+		rss->key_size = rss_head.key_size;
d7cf3b
+		if (fill_indir_table(&rss->indir_size, rss->rss_config,
d7cf3b
+				     rxfhindir_default, rxfhindir_equal,
d7cf3b
+				     rxfhindir_weight, num_weights)) {
d7cf3b
+			err = 1;
d7cf3b
+			goto free;
d7cf3b
+		}
d7cf3b
 	}
d7cf3b
 
d7cf3b
 	if (hkey)
d7cf3b
@@ -4018,6 +4115,8 @@ static int do_srxfh(struct cmd_context *ctx)
d7cf3b
 	if (err < 0) {
d7cf3b
 		perror("Cannot set RX flow hash configuration");
d7cf3b
 		err = 1;
d7cf3b
+	} else if (rss_context == ETH_RXFH_CONTEXT_ALLOC) {
d7cf3b
+		printf("New RSS context is %d\n", rss->rss_context);
d7cf3b
 	}
d7cf3b
 
d7cf3b
 free:
d7cf3b
@@ -4803,12 +4902,12 @@ static const struct option {
d7cf3b
 	{ "-n|-u|--show-nfc|--show-ntuple", 1, do_grxclass,
d7cf3b
 	  "Show Rx network flow classification options or rules",
d7cf3b
 	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
d7cf3b
-	  "tcp6|udp6|ah6|esp6|sctp6 |\n"
d7cf3b
+	  "tcp6|udp6|ah6|esp6|sctp6 [context %d] |\n"
d7cf3b
 	  "		  rule %d ]\n" },
d7cf3b
 	{ "-N|-U|--config-nfc|--config-ntuple", 1, do_srxclass,
d7cf3b
 	  "Configure Rx network flow classification options or rules",
d7cf3b
 	  "		rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
d7cf3b
-	  "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... |\n"
d7cf3b
+	  "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... [context %d] |\n"
d7cf3b
 	  "		flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4|"
d7cf3b
 	  "ip6|tcp6|udp6|ah6|esp6|sctp6\n"
d7cf3b
 	  "			[ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
d7cf3b
@@ -4827,17 +4926,21 @@ static const struct option {
d7cf3b
 	  "			[ user-def %x [m %x] ]\n"
d7cf3b
 	  "			[ dst-mac %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
d7cf3b
 	  "			[ action %d ]\n"
d7cf3b
+	  "			[ context %d ]\n"
d7cf3b
 	  "			[ loc %d]] |\n"
d7cf3b
 	  "		delete %d\n" },
d7cf3b
 	{ "-T|--show-time-stamping", 1, do_tsinfo,
d7cf3b
 	  "Show time stamping capabilities" },
d7cf3b
 	{ "-x|--show-rxfh-indir|--show-rxfh", 1, do_grxfh,
d7cf3b
-	  "Show Rx flow hash indirection table and/or RSS hash key" },
d7cf3b
+	  "Show Rx flow hash indirection table and/or RSS hash key",
d7cf3b
+	  "		[ context %d ]\n" },
d7cf3b
 	{ "-X|--set-rxfh-indir|--rxfh", 1, do_srxfh,
d7cf3b
 	  "Set Rx flow hash indirection table and/or RSS hash key",
d7cf3b
+	  "		[ context %d|new ]\n"
d7cf3b
 	  "		[ equal N | weight W0 W1 ... | default ]\n"
d7cf3b
 	  "		[ hkey %x:%x:%x:%x:%x:.... ]\n"
d7cf3b
-	  "		[ hfunc FUNC ]\n" },
d7cf3b
+	  "		[ hfunc FUNC ]\n"
d7cf3b
+	  "		[ delete ]\n" },
d7cf3b
 	{ "-f|--flash", 1, do_flash,
d7cf3b
 	  "Flash firmware image from the specified file to a region on the device",
d7cf3b
 	  "               FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
d7cf3b
diff --git a/internal.h b/internal.h
d7cf3b
index 4e658ea..913f4eb 100644
d7cf3b
--- a/internal.h
d7cf3b
+++ b/internal.h
d7cf3b
@@ -332,11 +332,11 @@ int vmxnet3_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
d7cf3b
 
d7cf3b
 /* Rx flow classification */
d7cf3b
 int rxclass_parse_ruleopts(struct cmd_context *ctx,
d7cf3b
-			   struct ethtool_rx_flow_spec *fsp);
d7cf3b
+			   struct ethtool_rx_flow_spec *fsp, __u32 *rss_context);
d7cf3b
 int rxclass_rule_getall(struct cmd_context *ctx);
d7cf3b
 int rxclass_rule_get(struct cmd_context *ctx, __u32 loc);
d7cf3b
 int rxclass_rule_ins(struct cmd_context *ctx,
d7cf3b
-		     struct ethtool_rx_flow_spec *fsp);
d7cf3b
+		     struct ethtool_rx_flow_spec *fsp, __u32 rss_context);
d7cf3b
 int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
d7cf3b
 
d7cf3b
 /* Module EEPROM parsing code */
d7cf3b
diff --git a/rxclass.c b/rxclass.c
d7cf3b
index c7bfeba..39c9eca 100644
d7cf3b
--- a/rxclass.c
d7cf3b
+++ b/rxclass.c
d7cf3b
@@ -94,14 +94,15 @@ static void rxclass_print_nfc_spec_ext(struct ethtool_rx_flow_spec *fsp)
d7cf3b
 	}
d7cf3b
 }
d7cf3b
 
d7cf3b
-static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp)
d7cf3b
+static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp,
d7cf3b
+				   __u32 rss_context)
d7cf3b
 {
d7cf3b
 	unsigned char	*smac, *smacm, *dmac, *dmacm;
d7cf3b
 	__u32		flow_type;
d7cf3b
 
d7cf3b
 	fprintf(stdout,	"Filter: %d\n", fsp->location);
d7cf3b
 
d7cf3b
-	flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
d7cf3b
+	flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
d7cf3b
 
d7cf3b
 	invert_flow_mask(fsp);
d7cf3b
 
d7cf3b
@@ -247,6 +248,9 @@ static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp)
d7cf3b
 
d7cf3b
 	rxclass_print_nfc_spec_ext(fsp);
d7cf3b
 
d7cf3b
+	if (fsp->flow_type & FLOW_RSS)
d7cf3b
+		fprintf(stdout, "\tRSS Context ID: %u\n", rss_context);
d7cf3b
+
d7cf3b
 	if (fsp->ring_cookie != RX_CLS_FLOW_DISC)
d7cf3b
 		fprintf(stdout, "\tAction: Direct to queue %llu\n",
d7cf3b
 			fsp->ring_cookie);
d7cf3b
@@ -256,10 +260,11 @@ static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp)
d7cf3b
 	fprintf(stdout, "\n");
d7cf3b
 }
d7cf3b
 
d7cf3b
-static void rxclass_print_rule(struct ethtool_rx_flow_spec *fsp)
d7cf3b
+static void rxclass_print_rule(struct ethtool_rx_flow_spec *fsp,
d7cf3b
+			       __u32 rss_context)
d7cf3b
 {
d7cf3b
 	/* print the rule in this location */
d7cf3b
-	switch (fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
d7cf3b
+	switch (fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
d7cf3b
 	case TCP_V4_FLOW:
d7cf3b
 	case UDP_V4_FLOW:
d7cf3b
 	case SCTP_V4_FLOW:
d7cf3b
@@ -272,11 +277,11 @@ static void rxclass_print_rule(struct ethtool_rx_flow_spec *fsp)
d7cf3b
 	case ESP_V6_FLOW:
d7cf3b
 	case IPV6_USER_FLOW:
d7cf3b
 	case ETHER_FLOW:
d7cf3b
-		rxclass_print_nfc_rule(fsp);
d7cf3b
+		rxclass_print_nfc_rule(fsp, rss_context);
d7cf3b
 		break;
d7cf3b
 	case IPV4_USER_FLOW:
d7cf3b
 		if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4)
d7cf3b
-			rxclass_print_nfc_rule(fsp);
d7cf3b
+			rxclass_print_nfc_rule(fsp, rss_context);
d7cf3b
 		else /* IPv6 uses IPV6_USER_FLOW */
d7cf3b
 			fprintf(stderr, "IPV4_USER_FLOW with wrong ip_ver\n");
d7cf3b
 		break;
d7cf3b
@@ -320,7 +325,7 @@ int rxclass_rule_get(struct cmd_context *ctx, __u32 loc)
d7cf3b
 	}
d7cf3b
 
d7cf3b
 	/* display rule */
d7cf3b
-	rxclass_print_rule(&nfccmd.fs);
d7cf3b
+	rxclass_print_rule(&nfccmd.fs, (__u32)nfccmd.rss_context);
d7cf3b
 	return err;
d7cf3b
 }
d7cf3b
 
d7cf3b
@@ -550,7 +555,7 @@ out:
d7cf3b
 }
d7cf3b
 
d7cf3b
 int rxclass_rule_ins(struct cmd_context *ctx,
d7cf3b
-		     struct ethtool_rx_flow_spec *fsp)
d7cf3b
+		     struct ethtool_rx_flow_spec *fsp, __u32 rss_context)
d7cf3b
 {
d7cf3b
 	struct ethtool_rxnfc nfccmd;
d7cf3b
 	__u32 loc = fsp->location;
d7cf3b
@@ -568,6 +573,7 @@ int rxclass_rule_ins(struct cmd_context *ctx,
d7cf3b
 
d7cf3b
 	/* notify netdev of new rule */
d7cf3b
 	nfccmd.cmd = ETHTOOL_SRXCLSRLINS;
d7cf3b
+	nfccmd.rss_context = rss_context;
d7cf3b
 	nfccmd.fs = *fsp;
d7cf3b
 	err = send_ioctl(ctx, &nfccmd);
d7cf3b
 	if (err < 0)
d7cf3b
@@ -1184,7 +1190,7 @@ static int rxclass_get_mask(char *str, unsigned char *p,
d7cf3b
 }
d7cf3b
 
d7cf3b
 int rxclass_parse_ruleopts(struct cmd_context *ctx,
d7cf3b
-			   struct ethtool_rx_flow_spec *fsp)
d7cf3b
+			   struct ethtool_rx_flow_spec *fsp, __u32 *rss_context)
d7cf3b
 {
d7cf3b
 	const struct rule_opts *options;
d7cf3b
 	unsigned char *p = (unsigned char *)fsp;
d7cf3b
@@ -1273,6 +1279,40 @@ int rxclass_parse_ruleopts(struct cmd_context *ctx,
d7cf3b
 	for (i = 1; i < argc;) {
d7cf3b
 		const struct rule_opts *opt;
d7cf3b
 		int idx;
d7cf3b
+
d7cf3b
+		/* special handling for 'context %d' as it doesn't go in
d7cf3b
+		 * the struct ethtool_rx_flow_spec
d7cf3b
+		 */
d7cf3b
+		if (!strcmp(argp[i], "context")) {
d7cf3b
+			unsigned long long val;
d7cf3b
+
d7cf3b
+			i++;
d7cf3b
+			if (i >= argc) {
d7cf3b
+				fprintf(stderr, "'context' missing value\n");
d7cf3b
+				return -1;
d7cf3b
+			}
d7cf3b
+
d7cf3b
+			if (rxclass_get_ulong(argp[i], &val, 32)) {
d7cf3b
+				fprintf(stderr, "Invalid context value[%s]\n",
d7cf3b
+					argp[i]);
d7cf3b
+				return -1;
d7cf3b
+			}
d7cf3b
+
d7cf3b
+			/* Can't use the ALLOC special value as the context ID
d7cf3b
+			 * of a filter to insert
d7cf3b
+			 */
d7cf3b
+			if ((__u32)val == ETH_RXFH_CONTEXT_ALLOC) {
d7cf3b
+				fprintf(stderr, "Bad context value %x\n",
d7cf3b
+					(__u32)val);
d7cf3b
+				return -1;
d7cf3b
+			}
d7cf3b
+
d7cf3b
+			*rss_context = (__u32)val;
d7cf3b
+			fsp->flow_type |= FLOW_RSS;
d7cf3b
+			i++;
d7cf3b
+			continue;
d7cf3b
+		}
d7cf3b
+
d7cf3b
 		for (opt = options, idx = 0; idx < n_opts; idx++, opt++) {
d7cf3b
 			char mask_name[16];
d7cf3b
 
d7cf3b
-- 
d7cf3b
1.8.3.1
d7cf3b