Commit 841942bc authored by Maxime Chevallier's avatar Maxime Chevallier Committed by David S. Miller

net: ethtool: Allow passing a phy index for some commands

Some netlink commands are target towards ethernet PHYs, to control some
of their features. As there's several such commands, add the ability to
pass a PHY index in the ethnl request, which will populate the generic
ethnl_req_info with the relevant phydev when the command targets a PHY.
Signed-off-by: default avatarMaxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fdd35396
...@@ -57,6 +57,7 @@ Structure of this header is ...@@ -57,6 +57,7 @@ Structure of this header is
``ETHTOOL_A_HEADER_DEV_INDEX`` u32 device ifindex ``ETHTOOL_A_HEADER_DEV_INDEX`` u32 device ifindex
``ETHTOOL_A_HEADER_DEV_NAME`` string device name ``ETHTOOL_A_HEADER_DEV_NAME`` string device name
``ETHTOOL_A_HEADER_FLAGS`` u32 flags common for all requests ``ETHTOOL_A_HEADER_FLAGS`` u32 flags common for all requests
``ETHTOOL_A_HEADER_PHY_INDEX`` u32 phy device index
============================== ====== ============================= ============================== ====== =============================
``ETHTOOL_A_HEADER_DEV_INDEX`` and ``ETHTOOL_A_HEADER_DEV_NAME`` identify the ``ETHTOOL_A_HEADER_DEV_INDEX`` and ``ETHTOOL_A_HEADER_DEV_NAME`` identify the
...@@ -81,6 +82,12 @@ the behaviour is backward compatible, i.e. requests from old clients not aware ...@@ -81,6 +82,12 @@ the behaviour is backward compatible, i.e. requests from old clients not aware
of the flag should be interpreted the way the client expects. A client must of the flag should be interpreted the way the client expects. A client must
not set flags it does not understand. not set flags it does not understand.
``ETHTOOL_A_HEADER_PHY_INDEX`` identifies the Ethernet PHY the message relates to.
As there are numerous commands that are related to PHY configuration, and because
there may be more than one PHY on the link, the PHY index can be passed in the
request for the commands that needs it. It is, however, not mandatory, and if it
is not passed for commands that target a PHY, the net_device.phydev pointer
is used.
Bit sets Bit sets
======== ========
......
...@@ -132,6 +132,7 @@ enum { ...@@ -132,6 +132,7 @@ enum {
ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */ ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */
ETHTOOL_A_HEADER_DEV_NAME, /* string */ ETHTOOL_A_HEADER_DEV_NAME, /* string */
ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */ ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */
ETHTOOL_A_HEADER_PHY_INDEX, /* u32 */
/* add new constants above here */ /* add new constants above here */
__ETHTOOL_A_HEADER_CNT, __ETHTOOL_A_HEADER_CNT,
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/ethtool_netlink.h> #include <linux/ethtool_netlink.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include "netlink.h" #include "netlink.h"
#include <linux/phy_link_topology.h>
static struct genl_family ethtool_genl_family; static struct genl_family ethtool_genl_family;
...@@ -30,6 +31,24 @@ const struct nla_policy ethnl_header_policy_stats[] = { ...@@ -30,6 +31,24 @@ const struct nla_policy ethnl_header_policy_stats[] = {
ETHTOOL_FLAGS_STATS), ETHTOOL_FLAGS_STATS),
}; };
const struct nla_policy ethnl_header_policy_phy[] = {
[ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
[ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
.len = ALTIFNAMSIZ - 1 },
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
ETHTOOL_FLAGS_BASIC),
[ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
};
const struct nla_policy ethnl_header_policy_phy_stats[] = {
[ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
[ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
.len = ALTIFNAMSIZ - 1 },
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
ETHTOOL_FLAGS_STATS),
[ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
};
int ethnl_ops_begin(struct net_device *dev) int ethnl_ops_begin(struct net_device *dev)
{ {
int ret; int ret;
...@@ -89,8 +108,9 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, ...@@ -89,8 +108,9 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
const struct nlattr *header, struct net *net, const struct nlattr *header, struct net *net,
struct netlink_ext_ack *extack, bool require_dev) struct netlink_ext_ack *extack, bool require_dev)
{ {
struct nlattr *tb[ARRAY_SIZE(ethnl_header_policy)]; struct nlattr *tb[ARRAY_SIZE(ethnl_header_policy_phy)];
const struct nlattr *devname_attr; const struct nlattr *devname_attr;
struct phy_device *phydev = NULL;
struct net_device *dev = NULL; struct net_device *dev = NULL;
u32 flags = 0; u32 flags = 0;
int ret; int ret;
...@@ -104,7 +124,7 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, ...@@ -104,7 +124,7 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
/* No validation here, command policy should have a nested policy set /* No validation here, command policy should have a nested policy set
* for the header, therefore validation should have already been done. * for the header, therefore validation should have already been done.
*/ */
ret = nla_parse_nested(tb, ARRAY_SIZE(ethnl_header_policy) - 1, header, ret = nla_parse_nested(tb, ARRAY_SIZE(ethnl_header_policy_phy) - 1, header,
NULL, extack); NULL, extack);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -145,6 +165,30 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, ...@@ -145,6 +165,30 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
return -EINVAL; return -EINVAL;
} }
if (dev) {
if (tb[ETHTOOL_A_HEADER_PHY_INDEX]) {
struct nlattr *phy_id;
phy_id = tb[ETHTOOL_A_HEADER_PHY_INDEX];
phydev = phy_link_topo_get_phy(dev->link_topo,
nla_get_u32(phy_id));
if (!phydev) {
NL_SET_BAD_ATTR(extack, phy_id);
return -ENODEV;
}
} else {
/* If we need a PHY but no phy index is specified, fallback
* to dev->phydev
*/
phydev = dev->phydev;
}
} else if (tb[ETHTOOL_A_HEADER_PHY_INDEX]) {
NL_SET_ERR_MSG_ATTR(extack, header,
"can't target a PHY without a netdev");
return -EINVAL;
}
req_info->phydev = phydev;
req_info->dev = dev; req_info->dev = dev;
req_info->flags = flags; req_info->flags = flags;
return 0; return 0;
......
...@@ -250,6 +250,7 @@ static inline unsigned int ethnl_reply_header_size(void) ...@@ -250,6 +250,7 @@ static inline unsigned int ethnl_reply_header_size(void)
* @dev: network device the request is for (may be null) * @dev: network device the request is for (may be null)
* @dev_tracker: refcount tracker for @dev reference * @dev_tracker: refcount tracker for @dev reference
* @flags: request flags common for all request types * @flags: request flags common for all request types
* @phydev: phy_device connected to @dev this request is for (may be null)
* *
* This is a common base for request specific structures holding data from * This is a common base for request specific structures holding data from
* parsed userspace request. These always embed struct ethnl_req_info at * parsed userspace request. These always embed struct ethnl_req_info at
...@@ -259,6 +260,7 @@ struct ethnl_req_info { ...@@ -259,6 +260,7 @@ struct ethnl_req_info {
struct net_device *dev; struct net_device *dev;
netdevice_tracker dev_tracker; netdevice_tracker dev_tracker;
u32 flags; u32 flags;
struct phy_device *phydev;
}; };
static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info) static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info)
...@@ -395,9 +397,12 @@ extern const struct ethnl_request_ops ethnl_rss_request_ops; ...@@ -395,9 +397,12 @@ extern const struct ethnl_request_ops ethnl_rss_request_ops;
extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops; extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops;
extern const struct ethnl_request_ops ethnl_plca_status_request_ops; extern const struct ethnl_request_ops ethnl_plca_status_request_ops;
extern const struct ethnl_request_ops ethnl_mm_request_ops; extern const struct ethnl_request_ops ethnl_mm_request_ops;
extern const struct ethnl_request_ops ethnl_phy_request_ops;
extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1]; extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1]; extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_phy[ETHTOOL_A_HEADER_PHY_INDEX + 1];
extern const struct nla_policy ethnl_header_policy_phy_stats[ETHTOOL_A_HEADER_PHY_INDEX + 1];
extern const struct nla_policy ethnl_strset_get_policy[ETHTOOL_A_STRSET_COUNTS_ONLY + 1]; extern const struct nla_policy ethnl_strset_get_policy[ETHTOOL_A_STRSET_COUNTS_ONLY + 1];
extern const struct nla_policy ethnl_linkinfo_get_policy[ETHTOOL_A_LINKINFO_HEADER + 1]; extern const struct nla_policy ethnl_linkinfo_get_policy[ETHTOOL_A_LINKINFO_HEADER + 1];
extern const struct nla_policy ethnl_linkinfo_set_policy[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL + 1]; extern const struct nla_policy ethnl_linkinfo_set_policy[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL + 1];
......
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