From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.0 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 87512FD21E1 for ; Mon, 30 Jul 2018 12:54:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2F4D420892 for ; Mon, 30 Jul 2018 12:54:00 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2F4D420892 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=suse.cz Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732210AbeG3O2q (ORCPT ); Mon, 30 Jul 2018 10:28:46 -0400 Received: from mx2.suse.de ([195.135.220.15]:49480 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1730125AbeG3O2q (ORCPT ); Mon, 30 Jul 2018 10:28:46 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 1AF9CAE5B; Mon, 30 Jul 2018 12:53:53 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id B0409A0BE8; Mon, 30 Jul 2018 14:53:52 +0200 (CEST) Message-Id: <1127951c1ac8d2fb6fcb3d6caba6f45bcd74a37d.1532953989.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [RFC PATCH net-next v2 14/17] ethtool: implement SET_SETTINGS request for features To: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Jiri Pirko , David Miller , Florian Fainelli , Roopa Prabhu , Jakub Kicinski , "John W. Linville" Date: Mon, 30 Jul 2018 14:53:52 +0200 (CEST) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Using ETHNL_SETTINGS_FEATURES attribute, userspace can modify device features. Actual change is subject to netdev_change_features() sanity checks so that it can differ from what was requested. Unlike with most other requests, kernel can reply (if ETHA_FEATURES_WANT_DIFF flag is used) with a message in the same format but with different semantics: information about difference between user request and actual result and difference between old and new state of dev->features. Signed-off-by: Michal Kubecek --- Documentation/networking/ethtool-netlink.txt | 30 +++-- include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/settings.c | 127 +++++++++++++++++++ 3 files changed, 150 insertions(+), 8 deletions(-) diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt index c7fe4f518972..307d8c6c6c85 100644 --- a/Documentation/networking/ethtool-netlink.txt +++ b/Documentation/networking/ethtool-netlink.txt @@ -285,6 +285,9 @@ to be passed with SET_SETTINGS request: ETHA_SETTINGS_SOPASS (binary) SecureOn(tm) password ETHA_SETTINGS_MSGLVL (bitfield32) debug level ETHA_SETTINGS_LINK_MODES (bitset) device link modes + ETHA_SETTINGS_FEATURES (nested) device features + ETHA_FEATURES_WANTED (bitset) wanted features + ETHA_FEATURES_WANT_DIFF (flag) actual diff For both bitfield32 types, value and selector work the usual way, i.e. bits set in selector are set to corresponding bits from value and the rest is @@ -299,6 +302,17 @@ autoselection is done on ethtool side with ioctl interface, netlink interface is supposed to allow requesting changes without knowing what exactly kernel supports. +When changing device features, only ETHA_FEATURES_WANTED is passed. As usual, +mask defines which bits are to be set and value their values. If the request +has ETHA_FEATURES_WANT_DIFF flag set, reply will contain a message in the same +format as response to GET request, except only two bitsets are provided. +ETHA_FEATURES_WANTED shows difference between requested features and actual +result (dev->features after the operation); mask shows bits which differ and +value their values from the original request (new values are negated). Value +shows changes between old dev->features (before the operation) and new (after +the operation); mask shows bits which have been changed and value their new +values. + Request translation ------------------- @@ -328,30 +342,30 @@ ETHTOOL_SRINGPARAM n/a ETHTOOL_GPAUSEPARAM n/a ETHTOOL_SPAUSEPARAM n/a ETHTOOL_GRXCSUM ETHNL_CMD_GET_SETTINGS -ETHTOOL_SRXCSUM n/a +ETHTOOL_SRXCSUM ETHNL_CMD_SET_SETTINGS ETHTOOL_GTXCSUM ETHNL_CMD_GET_SETTINGS -ETHTOOL_STXCSUM n/a +ETHTOOL_STXCSUM ETHNL_CMD_SET_SETTINGS ETHTOOL_GSG ETHNL_CMD_GET_SETTINGS -ETHTOOL_SSG n/a +ETHTOOL_SSG ETHNL_CMD_SET_SETTINGS ETHTOOL_TEST n/a ETHTOOL_GSTRINGS ETHNL_CMD_GET_STRSET ETHTOOL_PHYS_ID n/a ETHTOOL_GSTATS n/a ETHTOOL_GTSO ETHNL_CMD_GET_SETTINGS -ETHTOOL_STSO n/a +ETHTOOL_STSO ETHNL_CMD_SET_SETTINGS ETHTOOL_GPERMADDR n/a ETHTOOL_GUFO ETHNL_CMD_GET_SETTINGS -ETHTOOL_SUFO n/a +ETHTOOL_SUFO ETHNL_CMD_SET_SETTINGS ETHTOOL_GGSO ETHNL_CMD_GET_SETTINGS -ETHTOOL_SGSO n/a +ETHTOOL_SGSO ETHNL_CMD_SET_SETTINGS ETHTOOL_GFLAGS ETHNL_CMD_GET_SETTINGS -ETHTOOL_SFLAGS n/a +ETHTOOL_SFLAGS ETHNL_CMD_SET_SETTINGS ETHTOOL_GPFLAGS n/a ETHTOOL_SPFLAGS n/a ETHTOOL_GRXFH n/a ETHTOOL_SRXFH n/a ETHTOOL_GGRO ETHNL_CMD_GET_SETTINGS -ETHTOOL_SGRO n/a +ETHTOOL_SGRO ETHNL_CMD_SET_SETTINGS ETHTOOL_GRXRINGS n/a ETHTOOL_GRXCLSRLCNT n/a ETHTOOL_GRXCLSRULE n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 06c78b281275..8dfcb9ef4009 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -191,6 +191,7 @@ enum { ETHA_FEATURES_WANTED, /* bitset */ ETHA_FEATURES_ACTIVE, /* bitset */ ETHA_FEATURES_NOCHANGE, /* bitset */ + ETHA_FEATURES_WANT_DIFF, /* flag */ __ETHA_FEATURES_MAX, ETHA_FEATURES_MAX = (__ETHA_FEATURES_MAX - 1) diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c index 678199e621a2..475582b7950c 100644 --- a/net/ethtool/settings.c +++ b/net/ethtool/settings.c @@ -1016,6 +1016,122 @@ static int ethnl_update_lsettings(struct genl_info *info, struct nlattr **tb, return 0; } +static const struct nla_policy features_policy[ETHA_FEATURES_MAX + 1] = { + [ETHA_FEATURES_UNSPEC] = { .type = NLA_UNSPEC }, + [ETHA_FEATURES_HW] = { .type = NLA_NESTED }, + [ETHA_FEATURES_WANTED] = { .type = NLA_NESTED }, + [ETHA_FEATURES_ACTIVE] = { .type = NLA_NESTED }, + [ETHA_FEATURES_NOCHANGE] = { .type = NLA_NESTED }, + [ETHA_FEATURES_WANT_DIFF] = { .type = NLA_FLAG }, +}; + +static void bitmap_from_features(unsigned long *bitmap, netdev_features_t val) +{ + const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT); + unsigned int i; + + bitmap_zero(bitmap, NETDEV_FEATURE_COUNT); + for (i = 0; i < words; i++) + bitmap[i] = (unsigned long)(val >> (i * BITS_PER_LONG)); +} + +static netdev_features_t features_from_bitmap(unsigned long *bitmap) +{ + const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT); + netdev_features_t ret = 0; + unsigned int i; + + for (i = 0; i < words; i++) + ret |= (netdev_features_t)(bitmap[i]) << (i * BITS_PER_LONG); + return ret; +} + +int update_features(struct genl_info *info, struct net_device *dev, + const struct nlattr *nest, bool compact) +{ + struct nlattr *tb[ETHA_FEATURES_MAX + 1]; + DECLARE_BITMAP(old_active, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(req_wanted, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(req_mask, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(new_active, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(wanted_diff_mask, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(active_diff_mask, NETDEV_FEATURE_COUNT); + struct nlattr *feat_attr; + unsigned int reply_len; + struct sk_buff *rskb; + bool mod = false; + void *ehdr; + int ret; + + ret = nla_parse_nested(tb, ETHA_FEATURES_MAX, nest, features_policy, + info->extack); + if (ret < 0) + return ret; + if (tb[ETHA_FEATURES_HW] || !tb[ETHA_FEATURES_WANTED] || + tb[ETHA_FEATURES_ACTIVE] || tb[ETHA_FEATURES_NOCHANGE]) + return -EINVAL; + + bitmap_from_features(old_active, dev->features); + bitmap_copy(req_wanted, old_active, NETDEV_FEATURE_COUNT); + bitmap_zero(req_mask, NETDEV_FEATURE_COUNT); + mod = ethnl_update_bitset(req_wanted, req_mask, NETDEV_FEATURE_COUNT, + tb[ETHA_FEATURES_WANTED], &ret, feature_names, + info); + if (ret < 0 || !mod) + return ret; + + dev->wanted_features = features_from_bitmap(req_wanted); + netdev_update_features(dev); + if (!tb[ETHA_FEATURES_WANT_DIFF]) + return 0; + bitmap_from_features(new_active, dev->features); + bitmap_xor(wanted_diff_mask, req_wanted, new_active, + NETDEV_FEATURE_COUNT); + bitmap_xor(active_diff_mask, old_active, new_active, + NETDEV_FEATURE_COUNT); + bitmap_and(wanted_diff_mask, wanted_diff_mask, req_mask, + NETDEV_FEATURE_COUNT); + bitmap_and(req_wanted, req_wanted, wanted_diff_mask, + NETDEV_FEATURE_COUNT); + bitmap_and(new_active, new_active, active_diff_mask, + NETDEV_FEATURE_COUNT); + + reply_len = ethnl_bitset_size(compact, NETDEV_FEATURE_COUNT, req_wanted, + wanted_diff_mask, feature_names) + + ethnl_bitset_size(compact, NETDEV_FEATURE_COUNT, new_active, + active_diff_mask, feature_names); + reply_len = nla_total_size(reply_len); + rskb = ethnl_reply_init(reply_len, dev, ETHNL_CMD_SET_SETTINGS, + ETHA_SETTINGS_DEV, info, &ehdr); + if (!rskb) + goto err; + ret = -EMSGSIZE; + + feat_attr = ethnl_nest_start(rskb, ETHA_SETTINGS_FEATURES); + if (!feat_attr) + goto err; + ret = ethnl_put_bitset(rskb, ETHA_FEATURES_WANTED, compact, + NETDEV_FEATURE_COUNT, req_wanted, + wanted_diff_mask, feature_names); + if (ret < 0) + goto err; + ret = ethnl_put_bitset(rskb, ETHA_FEATURES_ACTIVE, compact, + NETDEV_FEATURE_COUNT, new_active, + active_diff_mask, feature_names); + if (ret < 0) + goto err; + nla_nest_end(rskb, feat_attr); + + genlmsg_end(rskb, ehdr); + return genlmsg_reply(rskb, info); +err: + WARN_ONCE(ret == -EMSGSIZE, + "calculated message payload length (%d) not sufficient\n", + reply_len); + nlmsg_free(rskb); + return ret; +} + int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info) { struct nlattr *tb[ETHA_SETTINGS_MAX + 1]; @@ -1092,6 +1208,17 @@ int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info) req_mask |= ETH_SETTINGS_IM_MSGLEVEL; } } + if (tb[ETHA_SETTINGS_FEATURES]) { + bool compact = tb[ETHA_SETTINGS_COMPACT]; + + ret = update_features(info, dev, tb[ETHA_SETTINGS_FEATURES], + compact); + if (ret > 0) { + req_mask |= ETH_SETTINGS_IM_FEATURES; + ret = 0; + } + + } ret = 0; out_unlock: -- 2.18.0