328 lines · 8.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only #include <linux/ethtool.h>#include <linux/phy.h>#include <linux/slab.h> #include "common.h"#include "netlink.h" /* Channels A-D only; WORST and LINK are exclusive alternatives */#define PHY_MSE_CHANNEL_COUNT 4 struct mse_req_info {	struct ethnl_req_info base;}; struct mse_snapshot_entry {	struct phy_mse_snapshot snapshot;	int channel;}; struct mse_reply_data {	struct ethnl_reply_data base;	struct phy_mse_capability capability;	struct mse_snapshot_entry *snapshots;	unsigned int num_snapshots;}; static struct mse_reply_data *mse_repdata(const struct ethnl_reply_data *reply_base){	return container_of(reply_base, struct mse_reply_data, base);} const struct nla_policy ethnl_mse_get_policy[] = {	[ETHTOOL_A_MSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_phy),}; static int get_snapshot_if_supported(struct phy_device *phydev,				     struct mse_reply_data *data,				     unsigned int *idx, u32 cap_bit,				     enum phy_mse_channel channel){	int ret; 	if (data->capability.supported_caps & cap_bit) {		ret = phydev->drv->get_mse_snapshot(phydev, channel,					&data->snapshots[*idx].snapshot);		if (ret)			return ret;		data->snapshots[*idx].channel = channel;		(*idx)++;	} 	return 0;} static int mse_get_channels(struct phy_device *phydev,			    struct mse_reply_data *data){	unsigned int i = 0;	int ret; 	if (!data->capability.supported_caps)		return 0; 	data->snapshots = kzalloc_objs(*data->snapshots, PHY_MSE_CHANNEL_COUNT);	if (!data->snapshots)		return -ENOMEM; 	/* Priority 1: Individual channels */	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_A,					PHY_MSE_CHANNEL_A);	if (ret)		return ret;	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_B,					PHY_MSE_CHANNEL_B);	if (ret)		return ret;	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_C,					PHY_MSE_CHANNEL_C);	if (ret)		return ret;	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_D,					PHY_MSE_CHANNEL_D);	if (ret)		return ret; 	/* If any individual channels were found, we are done. */	if (i > 0) {		data->num_snapshots = i;		return 0;	} 	/* Priority 2: Worst channel, if no individual channels supported. */	ret = get_snapshot_if_supported(phydev, data, &i,					PHY_MSE_CAP_WORST_CHANNEL,					PHY_MSE_CHANNEL_WORST);	if (ret)		return ret; 	/* If worst channel was found, we are done. */	if (i > 0) {		data->num_snapshots = i;		return 0;	} 	/* Priority 3: Link-wide, if nothing else is supported. */	ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_LINK,					PHY_MSE_CHANNEL_LINK);	if (ret)		return ret; 	data->num_snapshots = i;	return 0;} static int mse_prepare_data(const struct ethnl_req_info *req_base,			    struct ethnl_reply_data *reply_base,			    const struct genl_info *info){	struct mse_reply_data *data = mse_repdata(reply_base);	struct net_device *dev = reply_base->dev;	struct phy_device *phydev;	int ret; 	phydev = ethnl_req_get_phydev(req_base, info->attrs,				      ETHTOOL_A_MSE_HEADER, info->extack);	if (IS_ERR(phydev))		return PTR_ERR(phydev);	if (!phydev)		return -EOPNOTSUPP; 	ret = ethnl_ops_begin(dev);	if (ret)		return ret; 	mutex_lock(&phydev->lock); 	if (!phydev->drv || !phydev->drv->get_mse_capability ||	    !phydev->drv->get_mse_snapshot) {		ret = -EOPNOTSUPP;		goto out_unlock;	}	if (!phydev->link) {		ret = -ENETDOWN;		goto out_unlock;	} 	ret = phydev->drv->get_mse_capability(phydev, &data->capability);	if (ret)		goto out_unlock; 	ret = mse_get_channels(phydev, data); out_unlock:	mutex_unlock(&phydev->lock);	ethnl_ops_complete(dev);	if (ret)		kfree(data->snapshots);	return ret;} static void mse_cleanup_data(struct ethnl_reply_data *reply_base){	struct mse_reply_data *data = mse_repdata(reply_base); 	kfree(data->snapshots);} static int mse_reply_size(const struct ethnl_req_info *req_base,			  const struct ethnl_reply_data *reply_base){	const struct mse_reply_data *data = mse_repdata(reply_base);	size_t len = 0;	unsigned int i; 	/* ETHTOOL_A_MSE_CAPABILITIES */	len += nla_total_size(0);	if (data->capability.supported_caps & PHY_MSE_CAP_AVG)		/* ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE */		len += nla_total_size(sizeof(u64));	if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |					       PHY_MSE_CAP_WORST_PEAK))		/* ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE */		len += nla_total_size(sizeof(u64));	/* ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS */	len += nla_total_size(sizeof(u64));	/* ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS */	len += nla_total_size(sizeof(u64)); 	for (i = 0; i < data->num_snapshots; i++) {		size_t snapshot_len = 0; 		/* Per-channel nest (e.g., ETHTOOL_A_MSE_CHANNEL_A / _B / _C /		 * _D / _WORST_CHANNEL / _LINK)		 */		snapshot_len += nla_total_size(0); 		if (data->capability.supported_caps & PHY_MSE_CAP_AVG)			snapshot_len += nla_total_size(sizeof(u64));		if (data->capability.supported_caps & PHY_MSE_CAP_PEAK)			snapshot_len += nla_total_size(sizeof(u64));		if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK)			snapshot_len += nla_total_size(sizeof(u64)); 		len += snapshot_len;	} 	return len;} static int mse_channel_to_attr(int ch){	switch (ch) {	case PHY_MSE_CHANNEL_A:		return ETHTOOL_A_MSE_CHANNEL_A;	case PHY_MSE_CHANNEL_B:		return ETHTOOL_A_MSE_CHANNEL_B;	case PHY_MSE_CHANNEL_C:		return ETHTOOL_A_MSE_CHANNEL_C;	case PHY_MSE_CHANNEL_D:		return ETHTOOL_A_MSE_CHANNEL_D;	case PHY_MSE_CHANNEL_WORST:		return ETHTOOL_A_MSE_WORST_CHANNEL;	case PHY_MSE_CHANNEL_LINK:		return ETHTOOL_A_MSE_LINK;	default:		return -EINVAL;	}} static int mse_fill_reply(struct sk_buff *skb,			  const struct ethnl_req_info *req_base,			  const struct ethnl_reply_data *reply_base){	const struct mse_reply_data *data = mse_repdata(reply_base);	struct nlattr *nest;	unsigned int i;	int ret; 	nest = nla_nest_start(skb, ETHTOOL_A_MSE_CAPABILITIES);	if (!nest)		return -EMSGSIZE; 	if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {		ret = nla_put_uint(skb,				   ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE,				   data->capability.max_average_mse);		if (ret < 0)			goto nla_put_nest_failure;	} 	if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |					       PHY_MSE_CAP_WORST_PEAK)) {		ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE,				   data->capability.max_peak_mse);		if (ret < 0)			goto nla_put_nest_failure;	} 	ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS,			   data->capability.refresh_rate_ps);	if (ret < 0)		goto nla_put_nest_failure; 	ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS,			   data->capability.num_symbols);	if (ret < 0)		goto nla_put_nest_failure; 	nla_nest_end(skb, nest); 	for (i = 0; i < data->num_snapshots; i++) {		const struct mse_snapshot_entry *s = &data->snapshots[i];		int chan_attr; 		chan_attr = mse_channel_to_attr(s->channel);		if (chan_attr < 0)			return chan_attr; 		nest = nla_nest_start(skb, chan_attr);		if (!nest)			return -EMSGSIZE; 		if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {			ret = nla_put_uint(skb,					   ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE,					   s->snapshot.average_mse);			if (ret)				goto nla_put_nest_failure;		}		if (data->capability.supported_caps & PHY_MSE_CAP_PEAK) {			ret = nla_put_uint(skb, ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE,					   s->snapshot.peak_mse);			if (ret)				goto nla_put_nest_failure;		}		if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK) {			ret = nla_put_uint(skb,					   ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE,					   s->snapshot.worst_peak_mse);			if (ret)				goto nla_put_nest_failure;		} 		nla_nest_end(skb, nest);	} 	return 0; nla_put_nest_failure:	nla_nest_cancel(skb, nest);	return ret;} const struct ethnl_request_ops ethnl_mse_request_ops = {	.request_cmd = ETHTOOL_MSG_MSE_GET,	.reply_cmd = ETHTOOL_MSG_MSE_GET_REPLY,	.hdr_attr = ETHTOOL_A_MSE_HEADER,	.req_info_size = sizeof(struct mse_req_info),	.reply_data_size = sizeof(struct mse_reply_data), 	.prepare_data = mse_prepare_data,	.cleanup_data = mse_cleanup_data,	.reply_size = mse_reply_size,	.fill_reply = mse_fill_reply,};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t