Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ethtool: provide link state with LINKSTATE_GET request

Implement LINKSTATE_GET netlink request to get link state information.

At the moment, only link up flag as provided by ETHTOOL_GLINK ioctl command
is returned.

LINKSTATE_GET request can be used with NLM_F_DUMP (without device
identification) to request the information for all devices in current
network namespace providing the data.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Michal Kubecek and committed by
David S. Miller
3d2b847f 1b1b1847

+146 -6
+32 -1
Documentation/networking/ethtool-netlink.rst
··· 184 184 ``ETHTOOL_MSG_LINKINFO_SET`` set link settings 185 185 ``ETHTOOL_MSG_LINKMODES_GET`` get link modes info 186 186 ``ETHTOOL_MSG_LINKMODES_SET`` set link modes info 187 + ``ETHTOOL_MSG_LINKSTATE_GET`` get link state 187 188 ===================================== ================================ 188 189 189 190 Kernel to userspace: ··· 195 194 ``ETHTOOL_MSG_LINKINFO_NTF`` link settings notification 196 195 ``ETHTOOL_MSG_LINKMODES_GET_REPLY`` link modes info 197 196 ``ETHTOOL_MSG_LINKMODES_NTF`` link modes notification 197 + ``ETHTOOL_MSG_LINKSTATE_GET_REPLY`` link state info 198 198 ===================================== ================================ 199 199 200 200 ``GET`` requests are sent by userspace applications to retrieve device ··· 394 392 supports. 395 393 396 394 395 + LINKSTATE_GET 396 + ============= 397 + 398 + Requests link state information. At the moment, only link up/down flag (as 399 + provided by ``ETHTOOL_GLINK`` ioctl command) is provided but some future 400 + extensions are planned (e.g. link down reason). This request does not have any 401 + attributes. 402 + 403 + Request contents: 404 + 405 + ==================================== ====== ========================== 406 + ``ETHTOOL_A_LINKSTATE_HEADER`` nested request header 407 + ==================================== ====== ========================== 408 + 409 + Kernel response contents: 410 + 411 + ==================================== ====== ========================== 412 + ``ETHTOOL_A_LINKSTATE_HEADER`` nested reply header 413 + ``ETHTOOL_A_LINKSTATE_LINK`` bool link state (up/down) 414 + ==================================== ====== ========================== 415 + 416 + For most NIC drivers, the value of ``ETHTOOL_A_LINKSTATE_LINK`` returns 417 + carrier flag provided by ``netif_carrier_ok()`` but there are drivers which 418 + define their own handler. 419 + 420 + ``LINKSTATE_GET`` allows dump requests (kernel returns reply messages for all 421 + devices supporting the request). 422 + 423 + 397 424 Request translation 398 425 =================== 399 426 ··· 444 413 ``ETHTOOL_GMSGLVL`` n/a 445 414 ``ETHTOOL_SMSGLVL`` n/a 446 415 ``ETHTOOL_NWAY_RST`` n/a 447 - ``ETHTOOL_GLINK`` n/a 416 + ``ETHTOOL_GLINK`` ``ETHTOOL_MSG_LINKSTATE_GET`` 448 417 ``ETHTOOL_GEEPROM`` n/a 449 418 ``ETHTOOL_SEEPROM`` n/a 450 419 ``ETHTOOL_GCOALESCE`` n/a
+14
include/uapi/linux/ethtool_netlink.h
··· 19 19 ETHTOOL_MSG_LINKINFO_SET, 20 20 ETHTOOL_MSG_LINKMODES_GET, 21 21 ETHTOOL_MSG_LINKMODES_SET, 22 + ETHTOOL_MSG_LINKSTATE_GET, 22 23 23 24 /* add new constants above here */ 24 25 __ETHTOOL_MSG_USER_CNT, ··· 34 33 ETHTOOL_MSG_LINKINFO_NTF, 35 34 ETHTOOL_MSG_LINKMODES_GET_REPLY, 36 35 ETHTOOL_MSG_LINKMODES_NTF, 36 + ETHTOOL_MSG_LINKSTATE_GET_REPLY, 37 37 38 38 /* add new constants above here */ 39 39 __ETHTOOL_MSG_KERNEL_CNT, ··· 181 179 /* add new constants above here */ 182 180 __ETHTOOL_A_LINKMODES_CNT, 183 181 ETHTOOL_A_LINKMODES_MAX = __ETHTOOL_A_LINKMODES_CNT - 1 182 + }; 183 + 184 + /* LINKSTATE */ 185 + 186 + enum { 187 + ETHTOOL_A_LINKSTATE_UNSPEC, 188 + ETHTOOL_A_LINKSTATE_HEADER, /* nest - _A_HEADER_* */ 189 + ETHTOOL_A_LINKSTATE_LINK, /* u8 */ 190 + 191 + /* add new constants above here */ 192 + __ETHTOOL_A_LINKSTATE_CNT, 193 + ETHTOOL_A_LINKSTATE_MAX = __ETHTOOL_A_LINKSTATE_CNT - 1 184 194 }; 185 195 186 196 /* generic netlink info */
+2 -1
net/ethtool/Makefile
··· 4 4 5 5 obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o 6 6 7 - ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o 7 + ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ 8 + linkstate.o
+8
net/ethtool/common.c
··· 217 217 = legacy_settings->eth_tp_mdix_ctrl; 218 218 return retval; 219 219 } 220 + 221 + int __ethtool_get_link(struct net_device *dev) 222 + { 223 + if (!dev->ethtool_ops->get_link) 224 + return -EOPNOTSUPP; 225 + 226 + return netif_running(dev) && dev->ethtool_ops->get_link(dev); 227 + }
+3
net/ethtool/common.h
··· 3 3 #ifndef _ETHTOOL_COMMON_H 4 4 #define _ETHTOOL_COMMON_H 5 5 6 + #include <linux/netdevice.h> 6 7 #include <linux/ethtool.h> 7 8 8 9 /* compose link mode index from speed, type and duplex */ ··· 19 18 extern const char 20 19 phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN]; 21 20 extern const char link_mode_names[][ETH_GSTRING_LEN]; 21 + 22 + int __ethtool_get_link(struct net_device *dev); 22 23 23 24 bool convert_legacy_settings_to_link_ksettings( 24 25 struct ethtool_link_ksettings *link_ksettings,
+4 -4
net/ethtool/ioctl.c
··· 1365 1365 static int ethtool_get_link(struct net_device *dev, char __user *useraddr) 1366 1366 { 1367 1367 struct ethtool_value edata = { .cmd = ETHTOOL_GLINK }; 1368 + int link = __ethtool_get_link(dev); 1368 1369 1369 - if (!dev->ethtool_ops->get_link) 1370 - return -EOPNOTSUPP; 1370 + if (link < 0) 1371 + return link; 1371 1372 1372 - edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev); 1373 - 1373 + edata.data = link; 1374 1374 if (copy_to_user(useraddr, &edata, sizeof(edata))) 1375 1375 return -EFAULT; 1376 1376 return 0;
+74
net/ethtool/linkstate.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include "netlink.h" 4 + #include "common.h" 5 + 6 + struct linkstate_req_info { 7 + struct ethnl_req_info base; 8 + }; 9 + 10 + struct linkstate_reply_data { 11 + struct ethnl_reply_data base; 12 + int link; 13 + }; 14 + 15 + #define LINKSTATE_REPDATA(__reply_base) \ 16 + container_of(__reply_base, struct linkstate_reply_data, base) 17 + 18 + static const struct nla_policy 19 + linkstate_get_policy[ETHTOOL_A_LINKSTATE_MAX + 1] = { 20 + [ETHTOOL_A_LINKSTATE_UNSPEC] = { .type = NLA_REJECT }, 21 + [ETHTOOL_A_LINKSTATE_HEADER] = { .type = NLA_NESTED }, 22 + [ETHTOOL_A_LINKSTATE_LINK] = { .type = NLA_REJECT }, 23 + }; 24 + 25 + static int linkstate_prepare_data(const struct ethnl_req_info *req_base, 26 + struct ethnl_reply_data *reply_base, 27 + struct genl_info *info) 28 + { 29 + struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); 30 + struct net_device *dev = reply_base->dev; 31 + int ret; 32 + 33 + ret = ethnl_ops_begin(dev); 34 + if (ret < 0) 35 + return ret; 36 + data->link = __ethtool_get_link(dev); 37 + ethnl_ops_complete(dev); 38 + 39 + return 0; 40 + } 41 + 42 + static int linkstate_reply_size(const struct ethnl_req_info *req_base, 43 + const struct ethnl_reply_data *reply_base) 44 + { 45 + return nla_total_size(sizeof(u8)) /* LINKSTATE_LINK */ 46 + + 0; 47 + } 48 + 49 + static int linkstate_fill_reply(struct sk_buff *skb, 50 + const struct ethnl_req_info *req_base, 51 + const struct ethnl_reply_data *reply_base) 52 + { 53 + struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); 54 + 55 + if (data->link >= 0 && 56 + nla_put_u8(skb, ETHTOOL_A_LINKSTATE_LINK, !!data->link)) 57 + return -EMSGSIZE; 58 + 59 + return 0; 60 + } 61 + 62 + const struct ethnl_request_ops ethnl_linkstate_request_ops = { 63 + .request_cmd = ETHTOOL_MSG_LINKSTATE_GET, 64 + .reply_cmd = ETHTOOL_MSG_LINKSTATE_GET_REPLY, 65 + .hdr_attr = ETHTOOL_A_LINKSTATE_HEADER, 66 + .max_attr = ETHTOOL_A_LINKSTATE_MAX, 67 + .req_info_size = sizeof(struct linkstate_req_info), 68 + .reply_data_size = sizeof(struct linkstate_reply_data), 69 + .request_policy = linkstate_get_policy, 70 + 71 + .prepare_data = linkstate_prepare_data, 72 + .reply_size = linkstate_reply_size, 73 + .fill_reply = linkstate_fill_reply, 74 + };
+8
net/ethtool/netlink.c
··· 210 210 [ETHTOOL_MSG_STRSET_GET] = &ethnl_strset_request_ops, 211 211 [ETHTOOL_MSG_LINKINFO_GET] = &ethnl_linkinfo_request_ops, 212 212 [ETHTOOL_MSG_LINKMODES_GET] = &ethnl_linkmodes_request_ops, 213 + [ETHTOOL_MSG_LINKSTATE_GET] = &ethnl_linkstate_request_ops, 213 214 }; 214 215 215 216 static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) ··· 645 644 .cmd = ETHTOOL_MSG_LINKMODES_SET, 646 645 .flags = GENL_UNS_ADMIN_PERM, 647 646 .doit = ethnl_set_linkmodes, 647 + }, 648 + { 649 + .cmd = ETHTOOL_MSG_LINKSTATE_GET, 650 + .doit = ethnl_default_doit, 651 + .start = ethnl_default_start, 652 + .dumpit = ethnl_default_dumpit, 653 + .done = ethnl_default_done, 648 654 }, 649 655 }; 650 656
+1
net/ethtool/netlink.h
··· 333 333 extern const struct ethnl_request_ops ethnl_strset_request_ops; 334 334 extern const struct ethnl_request_ops ethnl_linkinfo_request_ops; 335 335 extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; 336 + extern const struct ethnl_request_ops ethnl_linkstate_request_ops; 336 337 337 338 int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); 338 339 int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);