Commit 0524399d authored by Michal Kubecek's avatar Michal Kubecek Committed by David S. Miller

ethtool: provide netdev features with FEATURES_GET request

Implement FEATURES_GET request to get network device features. These are
traditionally available via ETHTOOL_GFEATURES ioctl request.

v2:
  - style cleanup suggested by Jakub Kicinski
Signed-off-by: default avatarMichal Kubecek <mkubecek@suse.cz>
Reviewed-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f70bb065
...@@ -189,6 +189,7 @@ Userspace to kernel: ...@@ -189,6 +189,7 @@ Userspace to kernel:
``ETHTOOL_MSG_DEBUG_SET`` set debugging settings ``ETHTOOL_MSG_DEBUG_SET`` set debugging settings
``ETHTOOL_MSG_WOL_GET`` get wake-on-lan settings ``ETHTOOL_MSG_WOL_GET`` get wake-on-lan settings
``ETHTOOL_MSG_WOL_SET`` set wake-on-lan settings ``ETHTOOL_MSG_WOL_SET`` set wake-on-lan settings
``ETHTOOL_MSG_FEATURES_GET`` get device features
===================================== ================================ ===================================== ================================
Kernel to userspace: Kernel to userspace:
...@@ -204,6 +205,7 @@ Kernel to userspace: ...@@ -204,6 +205,7 @@ Kernel to userspace:
``ETHTOOL_MSG_DEBUG_NTF`` debugging settings notification ``ETHTOOL_MSG_DEBUG_NTF`` debugging settings notification
``ETHTOOL_MSG_WOL_GET_REPLY`` wake-on-lan settings ``ETHTOOL_MSG_WOL_GET_REPLY`` wake-on-lan settings
``ETHTOOL_MSG_WOL_NTF`` wake-on-lan settings notification ``ETHTOOL_MSG_WOL_NTF`` wake-on-lan settings notification
``ETHTOOL_MSG_FEATURES_GET_REPLY`` device features
===================================== ================================= ===================================== =================================
``GET`` requests are sent by userspace applications to retrieve device ``GET`` requests are sent by userspace applications to retrieve device
...@@ -521,6 +523,37 @@ Request contents: ...@@ -521,6 +523,37 @@ Request contents:
``WAKE_MAGICSECURE`` mode. ``WAKE_MAGICSECURE`` mode.
FEATURES_GET
============
Gets netdev features like ``ETHTOOL_GFEATURES`` ioctl request.
Request contents:
==================================== ====== ==========================
``ETHTOOL_A_FEATURES_HEADER`` nested request header
==================================== ====== ==========================
Kernel response contents:
==================================== ====== ==========================
``ETHTOOL_A_FEATURES_HEADER`` nested reply header
``ETHTOOL_A_FEATURES_HW`` bitset dev->hw_features
``ETHTOOL_A_FEATURES_WANTED`` bitset dev->wanted_features
``ETHTOOL_A_FEATURES_ACTIVE`` bitset dev->features
``ETHTOOL_A_FEATURES_NOCHANGE`` bitset NETIF_F_NEVER_CHANGE
==================================== ====== ==========================
Bitmaps in kernel response have the same meaning as bitmaps used in ioctl
interference but attribute names are different (they are based on
corresponding members of struct net_device). Legacy "flags" are not provided,
if userspace needs them (most likely only ethtool for backward compatibility),
it can calculate their values from related feature bits itself.
ETHA_FEATURES_HW uses mask consisting of all features recognized by kernel (to
provide all names when using verbose bitmap format), the other three use no
mask (simple bit lists).
Request translation Request translation
=================== ===================
...@@ -551,30 +584,30 @@ have their netlink replacement yet. ...@@ -551,30 +584,30 @@ have their netlink replacement yet.
``ETHTOOL_SRINGPARAM`` n/a ``ETHTOOL_SRINGPARAM`` n/a
``ETHTOOL_GPAUSEPARAM`` n/a ``ETHTOOL_GPAUSEPARAM`` n/a
``ETHTOOL_SPAUSEPARAM`` n/a ``ETHTOOL_SPAUSEPARAM`` n/a
``ETHTOOL_GRXCSUM`` n/a ``ETHTOOL_GRXCSUM`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SRXCSUM`` n/a ``ETHTOOL_SRXCSUM`` n/a
``ETHTOOL_GTXCSUM`` n/a ``ETHTOOL_GTXCSUM`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_STXCSUM`` n/a ``ETHTOOL_STXCSUM`` n/a
``ETHTOOL_GSG`` n/a ``ETHTOOL_GSG`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SSG`` n/a ``ETHTOOL_SSG`` n/a
``ETHTOOL_TEST`` n/a ``ETHTOOL_TEST`` n/a
``ETHTOOL_GSTRINGS`` ``ETHTOOL_MSG_STRSET_GET`` ``ETHTOOL_GSTRINGS`` ``ETHTOOL_MSG_STRSET_GET``
``ETHTOOL_PHYS_ID`` n/a ``ETHTOOL_PHYS_ID`` n/a
``ETHTOOL_GSTATS`` n/a ``ETHTOOL_GSTATS`` n/a
``ETHTOOL_GTSO`` n/a ``ETHTOOL_GTSO`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_STSO`` n/a ``ETHTOOL_STSO`` n/a
``ETHTOOL_GPERMADDR`` rtnetlink ``RTM_GETLINK`` ``ETHTOOL_GPERMADDR`` rtnetlink ``RTM_GETLINK``
``ETHTOOL_GUFO`` n/a ``ETHTOOL_GUFO`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SUFO`` n/a ``ETHTOOL_SUFO`` n/a
``ETHTOOL_GGSO`` n/a ``ETHTOOL_GGSO`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SGSO`` n/a ``ETHTOOL_SGSO`` n/a
``ETHTOOL_GFLAGS`` n/a ``ETHTOOL_GFLAGS`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SFLAGS`` n/a ``ETHTOOL_SFLAGS`` n/a
``ETHTOOL_GPFLAGS`` n/a ``ETHTOOL_GPFLAGS`` n/a
``ETHTOOL_SPFLAGS`` n/a ``ETHTOOL_SPFLAGS`` n/a
``ETHTOOL_GRXFH`` n/a ``ETHTOOL_GRXFH`` n/a
``ETHTOOL_SRXFH`` n/a ``ETHTOOL_SRXFH`` n/a
``ETHTOOL_GGRO`` n/a ``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SGRO`` n/a ``ETHTOOL_SGRO`` n/a
``ETHTOOL_GRXRINGS`` n/a ``ETHTOOL_GRXRINGS`` n/a
``ETHTOOL_GRXCLSRLCNT`` n/a ``ETHTOOL_GRXCLSRLCNT`` n/a
...@@ -589,7 +622,7 @@ have their netlink replacement yet. ...@@ -589,7 +622,7 @@ have their netlink replacement yet.
``ETHTOOL_GSSET_INFO`` ``ETHTOOL_MSG_STRSET_GET`` ``ETHTOOL_GSSET_INFO`` ``ETHTOOL_MSG_STRSET_GET``
``ETHTOOL_GRXFHINDIR`` n/a ``ETHTOOL_GRXFHINDIR`` n/a
``ETHTOOL_SRXFHINDIR`` n/a ``ETHTOOL_SRXFHINDIR`` n/a
``ETHTOOL_GFEATURES`` n/a ``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SFEATURES`` n/a ``ETHTOOL_SFEATURES`` n/a
``ETHTOOL_GCHANNELS`` n/a ``ETHTOOL_GCHANNELS`` n/a
``ETHTOOL_SCHANNELS`` n/a ``ETHTOOL_SCHANNELS`` n/a
......
...@@ -24,6 +24,7 @@ enum { ...@@ -24,6 +24,7 @@ enum {
ETHTOOL_MSG_DEBUG_SET, ETHTOOL_MSG_DEBUG_SET,
ETHTOOL_MSG_WOL_GET, ETHTOOL_MSG_WOL_GET,
ETHTOOL_MSG_WOL_SET, ETHTOOL_MSG_WOL_SET,
ETHTOOL_MSG_FEATURES_GET,
/* add new constants above here */ /* add new constants above here */
__ETHTOOL_MSG_USER_CNT, __ETHTOOL_MSG_USER_CNT,
...@@ -43,6 +44,7 @@ enum { ...@@ -43,6 +44,7 @@ enum {
ETHTOOL_MSG_DEBUG_NTF, ETHTOOL_MSG_DEBUG_NTF,
ETHTOOL_MSG_WOL_GET_REPLY, ETHTOOL_MSG_WOL_GET_REPLY,
ETHTOOL_MSG_WOL_NTF, ETHTOOL_MSG_WOL_NTF,
ETHTOOL_MSG_FEATURES_GET_REPLY,
/* add new constants above here */ /* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT, __ETHTOOL_MSG_KERNEL_CNT,
...@@ -228,6 +230,21 @@ enum { ...@@ -228,6 +230,21 @@ enum {
ETHTOOL_A_WOL_MAX = __ETHTOOL_A_WOL_CNT - 1 ETHTOOL_A_WOL_MAX = __ETHTOOL_A_WOL_CNT - 1
}; };
/* FEATURES */
enum {
ETHTOOL_A_FEATURES_UNSPEC,
ETHTOOL_A_FEATURES_HEADER, /* nest - _A_HEADER_* */
ETHTOOL_A_FEATURES_HW, /* bitset */
ETHTOOL_A_FEATURES_WANTED, /* bitset */
ETHTOOL_A_FEATURES_ACTIVE, /* bitset */
ETHTOOL_A_FEATURES_NOCHANGE, /* bitset */
/* add new constants above here */
__ETHTOOL_A_FEATURES_CNT,
ETHTOOL_A_FEATURES_MAX = __ETHTOOL_A_FEATURES_CNT - 1
};
/* generic netlink info */ /* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1 #define ETHTOOL_GENL_VERSION 1
......
...@@ -5,4 +5,4 @@ obj-y += ioctl.o common.o ...@@ -5,4 +5,4 @@ obj-y += ioctl.o common.o
obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o
ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \
linkstate.o debug.o wol.o linkstate.o debug.o wol.o features.o
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#define ETHTOOL_DEV_FEATURE_WORDS DIV_ROUND_UP(NETDEV_FEATURE_COUNT, 32)
/* compose link mode index from speed, type and duplex */ /* compose link mode index from speed, type and duplex */
#define ETHTOOL_LINK_MODE(speed, type, duplex) \ #define ETHTOOL_LINK_MODE(speed, type, duplex) \
ETHTOOL_LINK_MODE_ ## speed ## base ## type ## _ ## duplex ## _BIT ETHTOOL_LINK_MODE_ ## speed ## base ## type ## _ ## duplex ## _BIT
......
// SPDX-License-Identifier: GPL-2.0-only
#include "netlink.h"
#include "common.h"
#include "bitset.h"
struct features_req_info {
struct ethnl_req_info base;
};
struct features_reply_data {
struct ethnl_reply_data base;
u32 hw[ETHTOOL_DEV_FEATURE_WORDS];
u32 wanted[ETHTOOL_DEV_FEATURE_WORDS];
u32 active[ETHTOOL_DEV_FEATURE_WORDS];
u32 nochange[ETHTOOL_DEV_FEATURE_WORDS];
u32 all[ETHTOOL_DEV_FEATURE_WORDS];
};
#define FEATURES_REPDATA(__reply_base) \
container_of(__reply_base, struct features_reply_data, base)
static const struct nla_policy
features_get_policy[ETHTOOL_A_FEATURES_MAX + 1] = {
[ETHTOOL_A_FEATURES_UNSPEC] = { .type = NLA_REJECT },
[ETHTOOL_A_FEATURES_HEADER] = { .type = NLA_NESTED },
[ETHTOOL_A_FEATURES_HW] = { .type = NLA_REJECT },
[ETHTOOL_A_FEATURES_WANTED] = { .type = NLA_REJECT },
[ETHTOOL_A_FEATURES_ACTIVE] = { .type = NLA_REJECT },
[ETHTOOL_A_FEATURES_NOCHANGE] = { .type = NLA_REJECT },
};
static void ethnl_features_to_bitmap32(u32 *dest, netdev_features_t src)
{
unsigned int i;
for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++)
dest[i] = src >> (32 * i);
}
static int features_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
struct features_reply_data *data = FEATURES_REPDATA(reply_base);
struct net_device *dev = reply_base->dev;
netdev_features_t all_features;
ethnl_features_to_bitmap32(data->hw, dev->hw_features);
ethnl_features_to_bitmap32(data->wanted, dev->wanted_features);
ethnl_features_to_bitmap32(data->active, dev->features);
ethnl_features_to_bitmap32(data->nochange, NETIF_F_NEVER_CHANGE);
all_features = GENMASK_ULL(NETDEV_FEATURE_COUNT - 1, 0);
ethnl_features_to_bitmap32(data->all, all_features);
return 0;
}
static int features_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct features_reply_data *data = FEATURES_REPDATA(reply_base);
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
unsigned int len = 0;
int ret;
ret = ethnl_bitset32_size(data->hw, data->all, NETDEV_FEATURE_COUNT,
netdev_features_strings, compact);
if (ret < 0)
return ret;
len += ret;
ret = ethnl_bitset32_size(data->wanted, NULL, NETDEV_FEATURE_COUNT,
netdev_features_strings, compact);
if (ret < 0)
return ret;
len += ret;
ret = ethnl_bitset32_size(data->active, NULL, NETDEV_FEATURE_COUNT,
netdev_features_strings, compact);
if (ret < 0)
return ret;
len += ret;
ret = ethnl_bitset32_size(data->nochange, NULL, NETDEV_FEATURE_COUNT,
netdev_features_strings, compact);
if (ret < 0)
return ret;
len += ret;
return len;
}
static int features_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct features_reply_data *data = FEATURES_REPDATA(reply_base);
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
int ret;
ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_HW, data->hw,
data->all, NETDEV_FEATURE_COUNT,
netdev_features_strings, compact);
if (ret < 0)
return ret;
ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_WANTED, data->wanted,
NULL, NETDEV_FEATURE_COUNT,
netdev_features_strings, compact);
if (ret < 0)
return ret;
ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_ACTIVE, data->active,
NULL, NETDEV_FEATURE_COUNT,
netdev_features_strings, compact);
if (ret < 0)
return ret;
return ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_NOCHANGE,
data->nochange, NULL, NETDEV_FEATURE_COUNT,
netdev_features_strings, compact);
}
const struct ethnl_request_ops ethnl_features_request_ops = {
.request_cmd = ETHTOOL_MSG_FEATURES_GET,
.reply_cmd = ETHTOOL_MSG_FEATURES_GET_REPLY,
.hdr_attr = ETHTOOL_A_FEATURES_HEADER,
.max_attr = ETHTOOL_A_FEATURES_MAX,
.req_info_size = sizeof(struct features_req_info),
.reply_data_size = sizeof(struct features_reply_data),
.request_policy = features_get_policy,
.prepare_data = features_prepare_data,
.reply_size = features_reply_size,
.fill_reply = features_fill_reply,
};
...@@ -56,8 +56,6 @@ EXPORT_SYMBOL(ethtool_op_get_ts_info); ...@@ -56,8 +56,6 @@ EXPORT_SYMBOL(ethtool_op_get_ts_info);
/* Handlers for each ethtool command */ /* Handlers for each ethtool command */
#define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32)
static int ethtool_get_features(struct net_device *dev, void __user *useraddr) static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
{ {
struct ethtool_gfeatures cmd = { struct ethtool_gfeatures cmd = {
......
...@@ -215,6 +215,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { ...@@ -215,6 +215,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_LINKSTATE_GET] = &ethnl_linkstate_request_ops, [ETHTOOL_MSG_LINKSTATE_GET] = &ethnl_linkstate_request_ops,
[ETHTOOL_MSG_DEBUG_GET] = &ethnl_debug_request_ops, [ETHTOOL_MSG_DEBUG_GET] = &ethnl_debug_request_ops,
[ETHTOOL_MSG_WOL_GET] = &ethnl_wol_request_ops, [ETHTOOL_MSG_WOL_GET] = &ethnl_wol_request_ops,
[ETHTOOL_MSG_FEATURES_GET] = &ethnl_features_request_ops,
}; };
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
...@@ -695,6 +696,13 @@ static const struct genl_ops ethtool_genl_ops[] = { ...@@ -695,6 +696,13 @@ static const struct genl_ops ethtool_genl_ops[] = {
.flags = GENL_UNS_ADMIN_PERM, .flags = GENL_UNS_ADMIN_PERM,
.doit = ethnl_set_wol, .doit = ethnl_set_wol,
}, },
{
.cmd = ETHTOOL_MSG_FEATURES_GET,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
},
}; };
static const struct genl_multicast_group ethtool_nl_mcgrps[] = { static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
......
...@@ -337,6 +337,7 @@ extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; ...@@ -337,6 +337,7 @@ extern const struct ethnl_request_ops ethnl_linkmodes_request_ops;
extern const struct ethnl_request_ops ethnl_linkstate_request_ops; extern const struct ethnl_request_ops ethnl_linkstate_request_ops;
extern const struct ethnl_request_ops ethnl_debug_request_ops; extern const struct ethnl_request_ops ethnl_debug_request_ops;
extern const struct ethnl_request_ops ethnl_wol_request_ops; extern const struct ethnl_request_ops ethnl_wol_request_ops;
extern const struct ethnl_request_ops ethnl_features_request_ops;
int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment