linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michal Kubecek <mkubecek@suse.cz>
To: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
	"John W. Linville" <linville@tuxdriver.com>
Subject: [RFC PATCH ethtool v2 11/23] netlink: add netlink handler for sfeatures (-K)
Date: Mon, 30 Jul 2018 14:56:39 +0200 (CEST)	[thread overview]
Message-ID: <72e0ca806f24935c068f0da8b0dcd909042285e2.1532954671.git.mkubecek@suse.cz> (raw)
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>

Implement "ethtool -K <dev>" subcommand using netlink interface command
ETHNL_CMD_SET_SETTINGS.

The implementation tries to emulate legacy flags but the result is not
exactly equal to the ioctl code. In particular, we map netdev features to
legacy flags based on name patterns in off_flag_def[] but this mapping is
slightly different from mapping used by kernel ioctl code. It also does not
try to emulate legacy flags in "actual changes" output show when actual
change of device features does not match userspace request exactly.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 ethtool.c          |   3 +-
 netlink/extapi.h   |   1 +
 netlink/settings.c | 292 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 295 insertions(+), 1 deletion(-)

diff --git a/ethtool.c b/ethtool.c
index b29086bbabde..b2f93a331098 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4878,6 +4878,7 @@ static int show_usage(struct cmd_context *ctx);
 #define nl_gset		NULL
 #define nl_sset		NULL
 #define nl_gfeatures	NULL
+#define nl_sfeatures	NULL
 #endif
 
 static const struct option {
@@ -4944,7 +4945,7 @@ static const struct option {
 	  "		[ tx N ]\n" },
 	{ "-k|--show-features|--show-offload", 1, do_gfeatures, nl_gfeatures,
 	  "Get state of protocol offload and other features" },
-	{ "-K|--features|--offload", 1, do_sfeatures, NULL,
+	{ "-K|--features|--offload", 1, do_sfeatures, nl_sfeatures,
 	  "Set protocol offload and other features",
 	  "		FEATURE on|off ...\n" },
 	{ "-i|--driver", 1, do_gdrv, nl_gdrv,
diff --git a/netlink/extapi.h b/netlink/extapi.h
index c656ff862687..ad67e81449ba 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -17,6 +17,7 @@ int nl_gdrv(struct cmd_context *ctx);
 int nl_gset(struct cmd_context *ctx);
 int nl_sset(struct cmd_context *ctx);
 int nl_gfeatures(struct cmd_context *ctx);
+int nl_sfeatures(struct cmd_context *ctx);
 int nl_monitor(struct cmd_context *ctx);
 
 void monitor_usage();
diff --git a/netlink/settings.c b/netlink/settings.c
index 51a01b400224..b7bfe14668df 100644
--- a/netlink/settings.c
+++ b/netlink/settings.c
@@ -741,3 +741,295 @@ int nl_sset(struct cmd_context *ctx)
 		return 0;
 	return nlctx->exit_code ?: 75;
 }
+
+/* features */
+
+struct sfeatures_context {
+	uint32_t		req_mask[0];
+};
+
+static void show_feature_changes(struct nl_context *nlctx,
+				 const struct nlattr *feat_attr)
+{
+	struct sfeatures_context *sfctx = nlctx->cmd_private;
+	const struct stringset *feature_names =
+		global_stringset(ETH_SS_FEATURES);
+	const unsigned int count = get_count(feature_names);
+	const unsigned int words = (count + 31) / 32;
+	const struct nlattr *tb[ETHA_FEATURES_MAX + 1] = {};
+	DECLARE_ATTR_TB_INFO(tb);
+	const uint32_t *wanted_val;
+	const uint32_t *wanted_mask;
+	const uint32_t *active_val;
+	const uint32_t *active_mask;
+	unsigned int i;
+	bool diff;
+	int ret;
+
+	ret = mnl_attr_parse_nested(feat_attr, attr_cb, &tb_info);
+	if (ret < 0)
+		goto err;
+	if (!tb[ETHA_FEATURES_WANTED] || !tb[ETHA_FEATURES_ACTIVE])
+		goto err;
+	if (bitset_get_count(tb[ETHA_FEATURES_WANTED], &ret) != count ||
+	    ret < 0)
+		goto err;
+	if (bitset_get_count(tb[ETHA_FEATURES_ACTIVE], &ret) != count ||
+	    ret < 0)
+		goto err;
+	wanted_val = get_compact_bitset_value(tb[ETHA_FEATURES_WANTED]);
+	wanted_mask = get_compact_bitset_mask(tb[ETHA_FEATURES_WANTED]);
+	active_val = get_compact_bitset_value(tb[ETHA_FEATURES_ACTIVE]);
+	active_mask = get_compact_bitset_mask(tb[ETHA_FEATURES_ACTIVE]);
+	if (!wanted_val || !wanted_mask || !active_val || !active_mask)
+		goto err;
+
+	diff = false;
+	for (i = 0; i < words; i++)
+		if (wanted_mask[i] || active_mask[i])
+			diff = true;
+	if (!diff)
+		return;
+
+	/* result is not exactly as requested, show differences */
+	printf("Actual changes:\n");
+	for (i = 0; i < count; i++) {
+		const char *name = get_string(feature_names, i);
+
+		if (!name)
+			continue;
+		if (!feature_on(wanted_mask, i) && !feature_on(active_mask, i))
+			continue;
+		printf("%s: ", name);
+		if (feature_on(wanted_mask, i))
+			/* we requested a value but result is different */
+			printf("%s [requested %s]",
+			       feature_on(wanted_val, i) ? "off" : "on",
+			       feature_on(wanted_val, i) ? "on" : "off");
+		else if (!feature_on(sfctx->req_mask, i))
+			/* not requested but changed anyway */
+			printf("%s [not requested]",
+			       feature_on(active_val, i) ? "on" : "off");
+		else
+			printf("%s", feature_on(active_val, i) ? "on" : "off");
+		fputc('\n', stdout);
+	}
+
+	return;
+err:
+	fprintf(stderr, "malformed diff info from kernel\n");
+}
+
+static int find_feature(const char *name)
+{
+	const struct stringset *feature_names =
+		global_stringset(ETH_SS_FEATURES);
+	const unsigned int count = get_count(feature_names);
+	unsigned int i;
+
+	for (i = 0; i < count; i++)
+		if (!strcmp(name, get_string(feature_names, i)))
+			return i;
+
+	return -1;
+}
+
+static int fill_feature(struct nl_context *nlctx, const char *name, bool val)
+{
+	struct nlattr *bit_attr;
+
+	bit_attr = ethnla_nest_start(nlctx, ETHA_BITS_BIT);
+	if (!bit_attr)
+		return -EMSGSIZE;
+	if (ethnla_put_strz(nlctx, ETHA_BIT_NAME, name))
+		return -EMSGSIZE;
+	if (ethnla_put_flag(nlctx, ETHA_BIT_VALUE, val))
+		return -EMSGSIZE;
+	mnl_attr_nest_end(nlctx->nlhdr, bit_attr);
+
+	return 0;
+}
+
+static void set_sf_req_mask(struct nl_context *nlctx, unsigned int idx)
+{
+	struct sfeatures_context *sfctx = nlctx->cmd_private;
+
+	sfctx->req_mask[idx / 32] |= (1 << (idx % 32));
+}
+
+static int fill_legacy_flag(struct nl_context *nlctx, const char *flag_name,
+			    bool val)
+{
+	const struct stringset *feature_names =
+		global_stringset(ETH_SS_FEATURES);
+	const unsigned int count = get_count(feature_names);
+	unsigned int i, j;
+	int ret;
+
+	for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+		const char *pattern;
+
+		if (strcmp(flag_name, off_flag_def[i].short_name) &&
+		    strcmp(flag_name, off_flag_def[i].long_name))
+			continue;
+		pattern = off_flag_def[i].kernel_name;
+
+		for (j = 0; j < count; j++) {
+			const char *name = get_string(feature_names, j);
+
+			if (flag_pattern_match(name, pattern)) {
+				ret = fill_feature(nlctx, name, val);
+				if (ret < 0)
+					return ret;
+				set_sf_req_mask(nlctx, j);
+			}
+		}
+
+		return 0;
+	}
+
+	return 1;
+}
+
+int sfeatures_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+	const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+	const struct nlattr *tb[ETHA_SETTINGS_MAX + 1] = {};
+	DECLARE_ATTR_TB_INFO(tb);
+	struct nl_context *nlctx = data;
+	const char *devname;
+	int ret;
+
+	if (ghdr->cmd != ETHNL_CMD_SET_SETTINGS) {
+		fprintf(stderr, "warning: unexpected reply message type %u\n",
+			ghdr->cmd);
+		return MNL_CB_OK;
+	}
+	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+	if (ret < 0)
+		return ret;
+	devname = get_dev_name(tb[ETHA_SETTINGS_DEV]);
+	if (strcmp(devname, nlctx->devname)) {
+		fprintf(stderr, "warning: unexpected message for device %s\n",
+			devname);
+		return MNL_CB_OK;
+	}
+	if (!tb[ETHA_SETTINGS_FEATURES])
+		return MNL_CB_OK;
+
+	show_feature_changes(nlctx, tb[ETHA_SETTINGS_FEATURES]);
+	return MNL_CB_OK;
+}
+
+int fill_sfeatures_bitmap(struct nl_context *nlctx)
+{
+	struct nlmsghdr *nlhdr = nlctx->nlhdr;
+	struct nlattr *bitset_attr;
+	struct nlattr *bits_attr;
+	int ret;
+
+	ret = -EMSGSIZE;
+	bitset_attr = ethnla_nest_start(nlctx, ETHA_FEATURES_WANTED);
+	if (!bitset_attr)
+		return ret;
+	bits_attr = ethnla_nest_start(nlctx, ETHA_BITSET_BITS);
+	if (!bits_attr)
+		goto err;
+
+	while (nlctx->argc > 0) {
+		bool val;
+
+		if (!strcmp(*nlctx->argp, "--")) {
+			nlctx->argp++;
+			nlctx->argc--;
+			break;
+		}
+		ret = -EINVAL;
+		if (nlctx->argc < 2 ||
+		    (strcmp(nlctx->argp[1], "on") &&
+		     strcmp(nlctx->argp[1], "off"))) {
+			fprintf(stderr,
+				"ethtool (%s): flag '%s' for parameter '%s' is"
+				" not followed by 'on' or 'off'\n",
+				nlctx->cmd, nlctx->argp[1], nlctx->param);
+			goto err;
+		}
+
+		val = !strcmp(nlctx->argp[1], "on");
+		ret = fill_legacy_flag(nlctx, nlctx->argp[0], val);
+		if (ret > 0) {
+			ret = fill_feature(nlctx, nlctx->argp[0], val);
+			if (ret == 0) {
+				int idx = find_feature(nlctx->argp[0]);
+
+				if (idx >= 0)
+					set_sf_req_mask(nlctx, idx);
+			}
+		}
+		if (ret < 0)
+			goto err;
+
+		nlctx->argp += 2;
+		nlctx->argc -= 2;
+	}
+
+	mnl_attr_nest_end(nlhdr, bits_attr);
+	mnl_attr_nest_end(nlhdr, bitset_attr);
+	return 0;
+err:
+	mnl_attr_nest_cancel(nlhdr, bitset_attr);
+	return ret;
+}
+
+int nl_sfeatures(struct cmd_context *ctx)
+{
+	const struct stringset *feature_names;
+	struct sfeatures_context *sfctx;
+	struct nl_context *nlctx = ctx->nlctx;
+	struct nlattr *feat_attr;
+	unsigned int words;
+	int ret;
+
+	nlctx->cmd = "-K";
+	nlctx->argp = ctx->argp;
+	nlctx->argc = ctx->argc;
+	nlctx->cmd_private = &sfctx;
+
+	load_global_strings(nlctx);
+	feature_names = global_stringset(ETH_SS_FEATURES);
+	words = (get_count(feature_names) + 31) / 32;
+	sfctx = malloc(sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
+	if (!sfctx)
+		return -ENOMEM;
+	memset(sfctx, '\0',
+	       sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
+	nlctx->cmd_private = sfctx;
+
+	nlctx->devname = ctx->devname;
+	ret = msg_init(nlctx, ETHNL_CMD_SET_SETTINGS,
+		       NLM_F_REQUEST | NLM_F_ACK);
+	if (ret < 0)
+		return 2;
+	if (ethnla_put_dev(nlctx, ETHA_SETTINGS_DEV, ctx->devname))
+		return -EMSGSIZE;
+	if (ethnla_put_flag(nlctx, ETHA_SETTINGS_COMPACT, true))
+		return -EMSGSIZE;
+
+	feat_attr = ethnla_nest_start(nlctx, ETHA_SETTINGS_FEATURES);
+	if (!feat_attr)
+		return -EMSGSIZE;
+	ret = fill_sfeatures_bitmap(nlctx);
+	if (ret < 0)
+		return ret;
+	if (ethnla_put_flag(nlctx, ETHA_FEATURES_WANT_DIFF, true))
+		return -EMSGSIZE;
+	mnl_attr_nest_end(nlctx->nlhdr, feat_attr);
+
+	ret = ethnl_sendmsg(nlctx);
+	if (ret < 0)
+		return 92;
+	ret = ethnl_process_reply(nlctx, sfeatures_reply_cb);
+	if (ret == 0)
+		return 0;
+	return nlctx->exit_code ?: 92;
+}
-- 
2.18.0


  parent reply	other threads:[~2018-07-30 12:56 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-30 12:55 [RFC PATCH ethtool v2 00/23] ethtool netlink interface (userspace side) (WiP) Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 01/23] move UAPI header copies to a separate directory Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 02/23] update UAPI header copies Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 03/23] netlink: add netlink interface Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 04/23] netlink: add support for string sets Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 05/23] netlink: add notification monitor Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 06/23] netlink: add netlink handler for gdrv (-i) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 07/23] netlink: add netlink handler for gset (no option) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 08/23] netlink: add helpers for command line parsing Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 09/23] netlink: add netlink handler for sset (-s) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 10/23] netlink: add netlink handler for gfeatures (-k) Michal Kubecek
2018-07-30 12:56 ` Michal Kubecek [this message]
2018-07-30 12:56 ` [RFC PATCH ethtool v2 12/23] netlink: add netlink handler for gcoalesce (-c) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 13/23] netlink: add netlink handler for gring (-g) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 14/23] netlink: add netlink handler for gpause (-a) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 15/23] netlink: add netlink handler for gchannels (-l) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 16/23] netlink: add netlink handler for geee (--show-eee) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 17/23] netlink: add netlink handler for gfec (--show-fec) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 18/23] netlink: add netlink handler for scoalesce (-C) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 19/23] netlink: add netlink handler for sring (-G) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 20/23] netlink: add netlink handler for spause (-A) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 21/23] netlink: add netlink handler for schannels (-L) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 22/23] netlink: add netlink handler for seee (--set-eee) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 23/23] netlink: add netlink handler for sfec (--set-fec) Michal Kubecek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=72e0ca806f24935c068f0da8b0dcd909042285e2.1532954671.git.mkubecek@suse.cz \
    --to=mkubecek@suse.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linville@tuxdriver.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).