File Index
Files and directories
Sign in
FILE

phy.c

File preview with syntax highlighting.

linux-6.19.14/net/ethtool/phy.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright 2023 Bootlin
  4 *
  5 */
  6#include "common.h"
  7#include "netlink.h"
  8
  9#include <linux/phy.h>
 10#include <linux/phy_link_topology.h>
 11#include <linux/sfp.h>
 12#include <net/netdev_lock.h>
 13
 14struct phy_req_info {
 15	struct ethnl_req_info base;
 16};
 17
 18struct phy_reply_data {
 19	struct ethnl_reply_data	base;
 20	u32 phyindex;
 21	char *drvname;
 22	char *name;
 23	unsigned int upstream_type;
 24	char *upstream_sfp_name;
 25	unsigned int upstream_index;
 26	char *downstream_sfp_name;
 27};
 28
 29#define PHY_REPDATA(__reply_base) \
 30	container_of(__reply_base, struct phy_reply_data, base)
 31
 32const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1] = {
 33	[ETHTOOL_A_PHY_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
 34};
 35
 36static int phy_reply_size(const struct ethnl_req_info *req_info,
 37			  const struct ethnl_reply_data *reply_data)
 38{
 39	struct phy_reply_data *rep_data = PHY_REPDATA(reply_data);
 40	size_t size = 0;
 41
 42	/* ETHTOOL_A_PHY_INDEX */
 43	size += nla_total_size(sizeof(u32));
 44
 45	/* ETHTOOL_A_DRVNAME */
 46	if (rep_data->drvname)
 47		size += nla_total_size(strlen(rep_data->drvname) + 1);
 48
 49	/* ETHTOOL_A_NAME */
 50	size += nla_total_size(strlen(rep_data->name) + 1);
 51
 52	/* ETHTOOL_A_PHY_UPSTREAM_TYPE */
 53	size += nla_total_size(sizeof(u32));
 54
 55	/* ETHTOOL_A_PHY_UPSTREAM_SFP_NAME */
 56	if (rep_data->upstream_sfp_name)
 57		size += nla_total_size(strlen(rep_data->upstream_sfp_name) + 1);
 58
 59	/* ETHTOOL_A_PHY_UPSTREAM_INDEX */
 60	if (rep_data->upstream_index)
 61		size += nla_total_size(sizeof(u32));
 62
 63	/* ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME */
 64	if (rep_data->downstream_sfp_name)
 65		size += nla_total_size(strlen(rep_data->downstream_sfp_name) + 1);
 66
 67	return size;
 68}
 69
 70static int phy_prepare_data(const struct ethnl_req_info *req_info,
 71			    struct ethnl_reply_data *reply_data,
 72			    const struct genl_info *info)
 73{
 74	struct phy_link_topology *topo = reply_data->dev->link_topo;
 75	struct phy_reply_data *rep_data = PHY_REPDATA(reply_data);
 76	struct nlattr **tb = info->attrs;
 77	struct phy_device_node *pdn;
 78	struct phy_device *phydev;
 79
 80	/* RTNL is held by the caller */
 81	phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PHY_HEADER,
 82				      info->extack);
 83	if (IS_ERR_OR_NULL(phydev))
 84		return -EOPNOTSUPP;
 85
 86	pdn = xa_load(&topo->phys, phydev->phyindex);
 87	if (!pdn)
 88		return -EOPNOTSUPP;
 89
 90	rep_data->phyindex = phydev->phyindex;
 91	rep_data->name = kstrdup(dev_name(&phydev->mdio.dev), GFP_KERNEL);
 92	rep_data->drvname = kstrdup(phydev->drv->name, GFP_KERNEL);
 93	rep_data->upstream_type = pdn->upstream_type;
 94
 95	if (pdn->upstream_type == PHY_UPSTREAM_PHY) {
 96		struct phy_device *upstream = pdn->upstream.phydev;
 97		rep_data->upstream_index = upstream->phyindex;
 98	}
 99
100	if (pdn->parent_sfp_bus)
101		rep_data->upstream_sfp_name = kstrdup(sfp_get_name(pdn->parent_sfp_bus),
102						      GFP_KERNEL);
103
104	if (phydev->sfp_bus)
105		rep_data->downstream_sfp_name = kstrdup(sfp_get_name(phydev->sfp_bus),
106							GFP_KERNEL);
107
108	return 0;
109}
110
111static int phy_fill_reply(struct sk_buff *skb,
112			  const struct ethnl_req_info *req_info,
113			  const struct ethnl_reply_data *reply_data)
114{
115	struct phy_reply_data *rep_data = PHY_REPDATA(reply_data);
116
117	if (nla_put_u32(skb, ETHTOOL_A_PHY_INDEX, rep_data->phyindex) ||
118	    nla_put_string(skb, ETHTOOL_A_PHY_NAME, rep_data->name) ||
119	    nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_TYPE, rep_data->upstream_type))
120		return -EMSGSIZE;
121
122	if (rep_data->drvname &&
123	    nla_put_string(skb, ETHTOOL_A_PHY_DRVNAME, rep_data->drvname))
124		return -EMSGSIZE;
125
126	if (rep_data->upstream_index &&
127	    nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_INDEX,
128			rep_data->upstream_index))
129		return -EMSGSIZE;
130
131	if (rep_data->upstream_sfp_name &&
132	    nla_put_string(skb, ETHTOOL_A_PHY_UPSTREAM_SFP_NAME,
133			   rep_data->upstream_sfp_name))
134		return -EMSGSIZE;
135
136	if (rep_data->downstream_sfp_name &&
137	    nla_put_string(skb, ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME,
138			   rep_data->downstream_sfp_name))
139		return -EMSGSIZE;
140
141	return 0;
142}
143
144static void phy_cleanup_data(struct ethnl_reply_data *reply_data)
145{
146	struct phy_reply_data *rep_data = PHY_REPDATA(reply_data);
147
148	kfree(rep_data->drvname);
149	kfree(rep_data->name);
150	kfree(rep_data->upstream_sfp_name);
151	kfree(rep_data->downstream_sfp_name);
152}
153
154const struct ethnl_request_ops ethnl_phy_request_ops = {
155	.request_cmd		= ETHTOOL_MSG_PHY_GET,
156	.reply_cmd		= ETHTOOL_MSG_PHY_GET_REPLY,
157	.hdr_attr		= ETHTOOL_A_PHY_HEADER,
158	.req_info_size		= sizeof(struct phy_req_info),
159	.reply_data_size	= sizeof(struct phy_reply_data),
160
161	.prepare_data		= phy_prepare_data,
162	.reply_size		= phy_reply_size,
163	.fill_reply		= phy_fill_reply,
164	.cleanup_data		= phy_cleanup_data,
165};
~/ Read-only file preview.