File Index
Files and directories
Sign in
FILE

mse.c

File preview with syntax highlighting.

linux-6.19.14/net/ethtool/mse.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3#include <linux/ethtool.h>
  4#include <linux/phy.h>
  5#include <linux/slab.h>
  6
  7#include "netlink.h"
  8#include "common.h"
  9
 10/* Channels A-D only; WORST and LINK are exclusive alternatives */
 11#define PHY_MSE_CHANNEL_COUNT 4
 12
 13struct mse_req_info {
 14	struct ethnl_req_info base;
 15};
 16
 17struct mse_snapshot_entry {
 18	struct phy_mse_snapshot snapshot;
 19	int channel;
 20};
 21
 22struct mse_reply_data {
 23	struct ethnl_reply_data base;
 24	struct phy_mse_capability capability;
 25	struct mse_snapshot_entry *snapshots;
 26	unsigned int num_snapshots;
 27};
 28
 29static struct mse_reply_data *
 30mse_repdata(const struct ethnl_reply_data *reply_base)
 31{
 32	return container_of(reply_base, struct mse_reply_data, base);
 33}
 34
 35const struct nla_policy ethnl_mse_get_policy[] = {
 36	[ETHTOOL_A_MSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_phy),
 37};
 38
 39static int get_snapshot_if_supported(struct phy_device *phydev,
 40				     struct mse_reply_data *data,
 41				     unsigned int *idx, u32 cap_bit,
 42				     enum phy_mse_channel channel)
 43{
 44	int ret;
 45
 46	if (data->capability.supported_caps & cap_bit) {
 47		ret = phydev->drv->get_mse_snapshot(phydev, channel,
 48					&data->snapshots[*idx].snapshot);
 49		if (ret)
 50			return ret;
 51		data->snapshots[*idx].channel = channel;
 52		(*idx)++;
 53	}
 54
 55	return 0;
 56}
 57
 58static int mse_get_channels(struct phy_device *phydev,
 59			    struct mse_reply_data *data)
 60{
 61	unsigned int i = 0;
 62	int ret;
 63
 64	if (!data->capability.supported_caps)
 65		return 0;
 66
 67	data->snapshots = kcalloc(PHY_MSE_CHANNEL_COUNT,
 68				  sizeof(*data->snapshots), GFP_KERNEL);
 69	if (!data->snapshots)
 70		return -ENOMEM;
 71
 72	/* Priority 1: Individual channels */
 73	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_A,
 74					PHY_MSE_CHANNEL_A);
 75	if (ret)
 76		return ret;
 77	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_B,
 78					PHY_MSE_CHANNEL_B);
 79	if (ret)
 80		return ret;
 81	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_C,
 82					PHY_MSE_CHANNEL_C);
 83	if (ret)
 84		return ret;
 85	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_D,
 86					PHY_MSE_CHANNEL_D);
 87	if (ret)
 88		return ret;
 89
 90	/* If any individual channels were found, we are done. */
 91	if (i > 0) {
 92		data->num_snapshots = i;
 93		return 0;
 94	}
 95
 96	/* Priority 2: Worst channel, if no individual channels supported. */
 97	ret = get_snapshot_if_supported(phydev, data, &i,
 98					PHY_MSE_CAP_WORST_CHANNEL,
 99					PHY_MSE_CHANNEL_WORST);
100	if (ret)
101		return ret;
102
103	/* If worst channel was found, we are done. */
104	if (i > 0) {
105		data->num_snapshots = i;
106		return 0;
107	}
108
109	/* Priority 3: Link-wide, if nothing else is supported. */
110	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_LINK,
111					PHY_MSE_CHANNEL_LINK);
112	if (ret)
113		return ret;
114
115	data->num_snapshots = i;
116	return 0;
117}
118
119static int mse_prepare_data(const struct ethnl_req_info *req_base,
120			    struct ethnl_reply_data *reply_base,
121			    const struct genl_info *info)
122{
123	struct mse_reply_data *data = mse_repdata(reply_base);
124	struct net_device *dev = reply_base->dev;
125	struct phy_device *phydev;
126	int ret;
127
128	phydev = ethnl_req_get_phydev(req_base, info->attrs,
129				      ETHTOOL_A_MSE_HEADER, info->extack);
130	if (IS_ERR(phydev))
131		return PTR_ERR(phydev);
132	if (!phydev)
133		return -EOPNOTSUPP;
134
135	ret = ethnl_ops_begin(dev);
136	if (ret)
137		return ret;
138
139	mutex_lock(&phydev->lock);
140
141	if (!phydev->drv || !phydev->drv->get_mse_capability ||
142	    !phydev->drv->get_mse_snapshot) {
143		ret = -EOPNOTSUPP;
144		goto out_unlock;
145	}
146	if (!phydev->link) {
147		ret = -ENETDOWN;
148		goto out_unlock;
149	}
150
151	ret = phydev->drv->get_mse_capability(phydev, &data->capability);
152	if (ret)
153		goto out_unlock;
154
155	ret = mse_get_channels(phydev, data);
156
157out_unlock:
158	mutex_unlock(&phydev->lock);
159	ethnl_ops_complete(dev);
160	if (ret)
161		kfree(data->snapshots);
162	return ret;
163}
164
165static void mse_cleanup_data(struct ethnl_reply_data *reply_base)
166{
167	struct mse_reply_data *data = mse_repdata(reply_base);
168
169	kfree(data->snapshots);
170}
171
172static int mse_reply_size(const struct ethnl_req_info *req_base,
173			  const struct ethnl_reply_data *reply_base)
174{
175	const struct mse_reply_data *data = mse_repdata(reply_base);
176	size_t len = 0;
177	unsigned int i;
178
179	/* ETHTOOL_A_MSE_CAPABILITIES */
180	len += nla_total_size(0);
181	if (data->capability.supported_caps & PHY_MSE_CAP_AVG)
182		/* ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE */
183		len += nla_total_size(sizeof(u64));
184	if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |
185					       PHY_MSE_CAP_WORST_PEAK))
186		/* ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE */
187		len += nla_total_size(sizeof(u64));
188	/* ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS */
189	len += nla_total_size(sizeof(u64));
190	/* ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS */
191	len += nla_total_size(sizeof(u64));
192
193	for (i = 0; i < data->num_snapshots; i++) {
194		size_t snapshot_len = 0;
195
196		/* Per-channel nest (e.g., ETHTOOL_A_MSE_CHANNEL_A / _B / _C /
197		 * _D / _WORST_CHANNEL / _LINK)
198		 */
199		snapshot_len += nla_total_size(0);
200
201		if (data->capability.supported_caps & PHY_MSE_CAP_AVG)
202			snapshot_len += nla_total_size(sizeof(u64));
203		if (data->capability.supported_caps & PHY_MSE_CAP_PEAK)
204			snapshot_len += nla_total_size(sizeof(u64));
205		if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK)
206			snapshot_len += nla_total_size(sizeof(u64));
207
208		len += snapshot_len;
209	}
210
211	return len;
212}
213
214static int mse_channel_to_attr(int ch)
215{
216	switch (ch) {
217	case PHY_MSE_CHANNEL_A:
218		return ETHTOOL_A_MSE_CHANNEL_A;
219	case PHY_MSE_CHANNEL_B:
220		return ETHTOOL_A_MSE_CHANNEL_B;
221	case PHY_MSE_CHANNEL_C:
222		return ETHTOOL_A_MSE_CHANNEL_C;
223	case PHY_MSE_CHANNEL_D:
224		return ETHTOOL_A_MSE_CHANNEL_D;
225	case PHY_MSE_CHANNEL_WORST:
226		return ETHTOOL_A_MSE_WORST_CHANNEL;
227	case PHY_MSE_CHANNEL_LINK:
228		return ETHTOOL_A_MSE_LINK;
229	default:
230		return -EINVAL;
231	}
232}
233
234static int mse_fill_reply(struct sk_buff *skb,
235			  const struct ethnl_req_info *req_base,
236			  const struct ethnl_reply_data *reply_base)
237{
238	const struct mse_reply_data *data = mse_repdata(reply_base);
239	struct nlattr *nest;
240	unsigned int i;
241	int ret;
242
243	nest = nla_nest_start(skb, ETHTOOL_A_MSE_CAPABILITIES);
244	if (!nest)
245		return -EMSGSIZE;
246
247	if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {
248		ret = nla_put_uint(skb,
249				   ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE,
250				   data->capability.max_average_mse);
251		if (ret < 0)
252			goto nla_put_nest_failure;
253	}
254
255	if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |
256					       PHY_MSE_CAP_WORST_PEAK)) {
257		ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE,
258				   data->capability.max_peak_mse);
259		if (ret < 0)
260			goto nla_put_nest_failure;
261	}
262
263	ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS,
264			   data->capability.refresh_rate_ps);
265	if (ret < 0)
266		goto nla_put_nest_failure;
267
268	ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS,
269			   data->capability.num_symbols);
270	if (ret < 0)
271		goto nla_put_nest_failure;
272
273	nla_nest_end(skb, nest);
274
275	for (i = 0; i < data->num_snapshots; i++) {
276		const struct mse_snapshot_entry *s = &data->snapshots[i];
277		int chan_attr;
278
279		chan_attr = mse_channel_to_attr(s->channel);
280		if (chan_attr < 0)
281			return chan_attr;
282
283		nest = nla_nest_start(skb, chan_attr);
284		if (!nest)
285			return -EMSGSIZE;
286
287		if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {
288			ret = nla_put_uint(skb,
289					   ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE,
290					   s->snapshot.average_mse);
291			if (ret)
292				goto nla_put_nest_failure;
293		}
294		if (data->capability.supported_caps & PHY_MSE_CAP_PEAK) {
295			ret = nla_put_uint(skb, ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE,
296					   s->snapshot.peak_mse);
297			if (ret)
298				goto nla_put_nest_failure;
299		}
300		if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK) {
301			ret = nla_put_uint(skb,
302					   ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE,
303					   s->snapshot.worst_peak_mse);
304			if (ret)
305				goto nla_put_nest_failure;
306		}
307
308		nla_nest_end(skb, nest);
309	}
310
311	return 0;
312
313nla_put_nest_failure:
314	nla_nest_cancel(skb, nest);
315	return ret;
316}
317
318const struct ethnl_request_ops ethnl_mse_request_ops = {
319	.request_cmd = ETHTOOL_MSG_MSE_GET,
320	.reply_cmd = ETHTOOL_MSG_MSE_GET_REPLY,
321	.hdr_attr = ETHTOOL_A_MSE_HEADER,
322	.req_info_size = sizeof(struct mse_req_info),
323	.reply_data_size = sizeof(struct mse_reply_data),
324
325	.prepare_data = mse_prepare_data,
326	.cleanup_data = mse_cleanup_data,
327	.reply_size = mse_reply_size,
328	.fill_reply = mse_fill_reply,
329};
~/ Read-only file preview.