xref: /openbmc/linux/net/ethtool/mm.c (revision 2b30f829)
1*2b30f829SVladimir Oltean // SPDX-License-Identifier: GPL-2.0-only
2*2b30f829SVladimir Oltean /*
3*2b30f829SVladimir Oltean  * Copyright 2022-2023 NXP
4*2b30f829SVladimir Oltean  */
5*2b30f829SVladimir Oltean #include "common.h"
6*2b30f829SVladimir Oltean #include "netlink.h"
7*2b30f829SVladimir Oltean 
8*2b30f829SVladimir Oltean struct mm_req_info {
9*2b30f829SVladimir Oltean 	struct ethnl_req_info		base;
10*2b30f829SVladimir Oltean };
11*2b30f829SVladimir Oltean 
12*2b30f829SVladimir Oltean struct mm_reply_data {
13*2b30f829SVladimir Oltean 	struct ethnl_reply_data		base;
14*2b30f829SVladimir Oltean 	struct ethtool_mm_state		state;
15*2b30f829SVladimir Oltean 	struct ethtool_mm_stats		stats;
16*2b30f829SVladimir Oltean };
17*2b30f829SVladimir Oltean 
18*2b30f829SVladimir Oltean #define MM_REPDATA(__reply_base) \
19*2b30f829SVladimir Oltean 	container_of(__reply_base, struct mm_reply_data, base)
20*2b30f829SVladimir Oltean 
21*2b30f829SVladimir Oltean #define ETHTOOL_MM_STAT_CNT \
22*2b30f829SVladimir Oltean 	(__ETHTOOL_A_MM_STAT_CNT - (ETHTOOL_A_MM_STAT_PAD + 1))
23*2b30f829SVladimir Oltean 
24*2b30f829SVladimir Oltean const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1] = {
25*2b30f829SVladimir Oltean 	[ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_stats),
26*2b30f829SVladimir Oltean };
27*2b30f829SVladimir Oltean 
28*2b30f829SVladimir Oltean static int mm_prepare_data(const struct ethnl_req_info *req_base,
29*2b30f829SVladimir Oltean 			   struct ethnl_reply_data *reply_base,
30*2b30f829SVladimir Oltean 			   struct genl_info *info)
31*2b30f829SVladimir Oltean {
32*2b30f829SVladimir Oltean 	struct mm_reply_data *data = MM_REPDATA(reply_base);
33*2b30f829SVladimir Oltean 	struct net_device *dev = reply_base->dev;
34*2b30f829SVladimir Oltean 	const struct ethtool_ops *ops;
35*2b30f829SVladimir Oltean 	int ret;
36*2b30f829SVladimir Oltean 
37*2b30f829SVladimir Oltean 	ops = dev->ethtool_ops;
38*2b30f829SVladimir Oltean 
39*2b30f829SVladimir Oltean 	if (!ops->get_mm)
40*2b30f829SVladimir Oltean 		return -EOPNOTSUPP;
41*2b30f829SVladimir Oltean 
42*2b30f829SVladimir Oltean 	ethtool_stats_init((u64 *)&data->stats,
43*2b30f829SVladimir Oltean 			   sizeof(data->stats) / sizeof(u64));
44*2b30f829SVladimir Oltean 
45*2b30f829SVladimir Oltean 	ret = ethnl_ops_begin(dev);
46*2b30f829SVladimir Oltean 	if (ret < 0)
47*2b30f829SVladimir Oltean 		return ret;
48*2b30f829SVladimir Oltean 
49*2b30f829SVladimir Oltean 	ret = ops->get_mm(dev, &data->state);
50*2b30f829SVladimir Oltean 	if (ret)
51*2b30f829SVladimir Oltean 		goto out_complete;
52*2b30f829SVladimir Oltean 
53*2b30f829SVladimir Oltean 	if (ops->get_mm_stats && (req_base->flags & ETHTOOL_FLAG_STATS))
54*2b30f829SVladimir Oltean 		ops->get_mm_stats(dev, &data->stats);
55*2b30f829SVladimir Oltean 
56*2b30f829SVladimir Oltean out_complete:
57*2b30f829SVladimir Oltean 	ethnl_ops_complete(dev);
58*2b30f829SVladimir Oltean 
59*2b30f829SVladimir Oltean 	return 0;
60*2b30f829SVladimir Oltean }
61*2b30f829SVladimir Oltean 
62*2b30f829SVladimir Oltean static int mm_reply_size(const struct ethnl_req_info *req_base,
63*2b30f829SVladimir Oltean 			 const struct ethnl_reply_data *reply_base)
64*2b30f829SVladimir Oltean {
65*2b30f829SVladimir Oltean 	int len = 0;
66*2b30f829SVladimir Oltean 
67*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u8)); /* _MM_PMAC_ENABLED */
68*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u8)); /* _MM_TX_ENABLED */
69*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u8)); /* _MM_TX_ACTIVE */
70*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_ENABLED */
71*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_STATUS */
72*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u32)); /* _MM_VERIFY_TIME */
73*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u32)); /* _MM_MAX_VERIFY_TIME */
74*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u32)); /* _MM_TX_MIN_FRAG_SIZE */
75*2b30f829SVladimir Oltean 	len += nla_total_size(sizeof(u32)); /* _MM_RX_MIN_FRAG_SIZE */
76*2b30f829SVladimir Oltean 
77*2b30f829SVladimir Oltean 	if (req_base->flags & ETHTOOL_FLAG_STATS)
78*2b30f829SVladimir Oltean 		len += nla_total_size(0) + /* _MM_STATS */
79*2b30f829SVladimir Oltean 		       nla_total_size_64bit(sizeof(u64)) * ETHTOOL_MM_STAT_CNT;
80*2b30f829SVladimir Oltean 
81*2b30f829SVladimir Oltean 	return len;
82*2b30f829SVladimir Oltean }
83*2b30f829SVladimir Oltean 
84*2b30f829SVladimir Oltean static int mm_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)
85*2b30f829SVladimir Oltean {
86*2b30f829SVladimir Oltean 	if (val == ETHTOOL_STAT_NOT_SET)
87*2b30f829SVladimir Oltean 		return 0;
88*2b30f829SVladimir Oltean 	if (nla_put_u64_64bit(skb, attrtype, val, ETHTOOL_A_MM_STAT_PAD))
89*2b30f829SVladimir Oltean 		return -EMSGSIZE;
90*2b30f829SVladimir Oltean 	return 0;
91*2b30f829SVladimir Oltean }
92*2b30f829SVladimir Oltean 
93*2b30f829SVladimir Oltean static int mm_put_stats(struct sk_buff *skb,
94*2b30f829SVladimir Oltean 			const struct ethtool_mm_stats *stats)
95*2b30f829SVladimir Oltean {
96*2b30f829SVladimir Oltean 	struct nlattr *nest;
97*2b30f829SVladimir Oltean 
98*2b30f829SVladimir Oltean 	nest = nla_nest_start(skb, ETHTOOL_A_MM_STATS);
99*2b30f829SVladimir Oltean 	if (!nest)
100*2b30f829SVladimir Oltean 		return -EMSGSIZE;
101*2b30f829SVladimir Oltean 
102*2b30f829SVladimir Oltean 	if (mm_put_stat(skb, stats->MACMergeFrameAssErrorCount,
103*2b30f829SVladimir Oltean 			ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS) ||
104*2b30f829SVladimir Oltean 	    mm_put_stat(skb, stats->MACMergeFrameSmdErrorCount,
105*2b30f829SVladimir Oltean 			ETHTOOL_A_MM_STAT_SMD_ERRORS) ||
106*2b30f829SVladimir Oltean 	    mm_put_stat(skb, stats->MACMergeFrameAssOkCount,
107*2b30f829SVladimir Oltean 			ETHTOOL_A_MM_STAT_REASSEMBLY_OK) ||
108*2b30f829SVladimir Oltean 	    mm_put_stat(skb, stats->MACMergeFragCountRx,
109*2b30f829SVladimir Oltean 			ETHTOOL_A_MM_STAT_RX_FRAG_COUNT) ||
110*2b30f829SVladimir Oltean 	    mm_put_stat(skb, stats->MACMergeFragCountTx,
111*2b30f829SVladimir Oltean 			ETHTOOL_A_MM_STAT_TX_FRAG_COUNT) ||
112*2b30f829SVladimir Oltean 	    mm_put_stat(skb, stats->MACMergeHoldCount,
113*2b30f829SVladimir Oltean 			ETHTOOL_A_MM_STAT_HOLD_COUNT))
114*2b30f829SVladimir Oltean 		goto err_cancel;
115*2b30f829SVladimir Oltean 
116*2b30f829SVladimir Oltean 	nla_nest_end(skb, nest);
117*2b30f829SVladimir Oltean 	return 0;
118*2b30f829SVladimir Oltean 
119*2b30f829SVladimir Oltean err_cancel:
120*2b30f829SVladimir Oltean 	nla_nest_cancel(skb, nest);
121*2b30f829SVladimir Oltean 	return -EMSGSIZE;
122*2b30f829SVladimir Oltean }
123*2b30f829SVladimir Oltean 
124*2b30f829SVladimir Oltean static int mm_fill_reply(struct sk_buff *skb,
125*2b30f829SVladimir Oltean 			 const struct ethnl_req_info *req_base,
126*2b30f829SVladimir Oltean 			 const struct ethnl_reply_data *reply_base)
127*2b30f829SVladimir Oltean {
128*2b30f829SVladimir Oltean 	const struct mm_reply_data *data = MM_REPDATA(reply_base);
129*2b30f829SVladimir Oltean 	const struct ethtool_mm_state *state = &data->state;
130*2b30f829SVladimir Oltean 
131*2b30f829SVladimir Oltean 	if (nla_put_u8(skb, ETHTOOL_A_MM_TX_ENABLED, state->tx_enabled) ||
132*2b30f829SVladimir Oltean 	    nla_put_u8(skb, ETHTOOL_A_MM_TX_ACTIVE, state->tx_active) ||
133*2b30f829SVladimir Oltean 	    nla_put_u8(skb, ETHTOOL_A_MM_PMAC_ENABLED, state->pmac_enabled) ||
134*2b30f829SVladimir Oltean 	    nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_ENABLED, state->verify_enabled) ||
135*2b30f829SVladimir Oltean 	    nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_STATUS, state->verify_status) ||
136*2b30f829SVladimir Oltean 	    nla_put_u32(skb, ETHTOOL_A_MM_VERIFY_TIME, state->verify_time) ||
137*2b30f829SVladimir Oltean 	    nla_put_u32(skb, ETHTOOL_A_MM_MAX_VERIFY_TIME, state->max_verify_time) ||
138*2b30f829SVladimir Oltean 	    nla_put_u32(skb, ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, state->tx_min_frag_size) ||
139*2b30f829SVladimir Oltean 	    nla_put_u32(skb, ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, state->rx_min_frag_size))
140*2b30f829SVladimir Oltean 		return -EMSGSIZE;
141*2b30f829SVladimir Oltean 
142*2b30f829SVladimir Oltean 	if (req_base->flags & ETHTOOL_FLAG_STATS &&
143*2b30f829SVladimir Oltean 	    mm_put_stats(skb, &data->stats))
144*2b30f829SVladimir Oltean 		return -EMSGSIZE;
145*2b30f829SVladimir Oltean 
146*2b30f829SVladimir Oltean 	return 0;
147*2b30f829SVladimir Oltean }
148*2b30f829SVladimir Oltean 
149*2b30f829SVladimir Oltean const struct ethnl_request_ops ethnl_mm_request_ops = {
150*2b30f829SVladimir Oltean 	.request_cmd		= ETHTOOL_MSG_MM_GET,
151*2b30f829SVladimir Oltean 	.reply_cmd		= ETHTOOL_MSG_MM_GET_REPLY,
152*2b30f829SVladimir Oltean 	.hdr_attr		= ETHTOOL_A_MM_HEADER,
153*2b30f829SVladimir Oltean 	.req_info_size		= sizeof(struct mm_req_info),
154*2b30f829SVladimir Oltean 	.reply_data_size	= sizeof(struct mm_reply_data),
155*2b30f829SVladimir Oltean 
156*2b30f829SVladimir Oltean 	.prepare_data		= mm_prepare_data,
157*2b30f829SVladimir Oltean 	.reply_size		= mm_reply_size,
158*2b30f829SVladimir Oltean 	.fill_reply		= mm_fill_reply,
159*2b30f829SVladimir Oltean };
160*2b30f829SVladimir Oltean 
161*2b30f829SVladimir Oltean const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1] = {
162*2b30f829SVladimir Oltean 	[ETHTOOL_A_MM_HEADER]		= NLA_POLICY_NESTED(ethnl_header_policy),
163*2b30f829SVladimir Oltean 	[ETHTOOL_A_MM_VERIFY_ENABLED]	= NLA_POLICY_MAX(NLA_U8, 1),
164*2b30f829SVladimir Oltean 	[ETHTOOL_A_MM_VERIFY_TIME]	= NLA_POLICY_RANGE(NLA_U32, 1, 128),
165*2b30f829SVladimir Oltean 	[ETHTOOL_A_MM_TX_ENABLED]	= NLA_POLICY_MAX(NLA_U8, 1),
166*2b30f829SVladimir Oltean 	[ETHTOOL_A_MM_PMAC_ENABLED]	= NLA_POLICY_MAX(NLA_U8, 1),
167*2b30f829SVladimir Oltean 	[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE]	= NLA_POLICY_RANGE(NLA_U32, 60, 252),
168*2b30f829SVladimir Oltean };
169*2b30f829SVladimir Oltean 
170*2b30f829SVladimir Oltean static void mm_state_to_cfg(const struct ethtool_mm_state *state,
171*2b30f829SVladimir Oltean 			    struct ethtool_mm_cfg *cfg)
172*2b30f829SVladimir Oltean {
173*2b30f829SVladimir Oltean 	/* We could also compare state->verify_status against
174*2b30f829SVladimir Oltean 	 * ETHTOOL_MM_VERIFY_STATUS_DISABLED, but state->verify_enabled
175*2b30f829SVladimir Oltean 	 * is more like an administrative state which should be seen in
176*2b30f829SVladimir Oltean 	 * ETHTOOL_MSG_MM_GET replies. For example, a port with verification
177*2b30f829SVladimir Oltean 	 * disabled might be in the ETHTOOL_MM_VERIFY_STATUS_INITIAL
178*2b30f829SVladimir Oltean 	 * if it's down.
179*2b30f829SVladimir Oltean 	 */
180*2b30f829SVladimir Oltean 	cfg->verify_enabled = state->verify_enabled;
181*2b30f829SVladimir Oltean 	cfg->verify_time = state->verify_time;
182*2b30f829SVladimir Oltean 	cfg->tx_enabled = state->tx_enabled;
183*2b30f829SVladimir Oltean 	cfg->pmac_enabled = state->pmac_enabled;
184*2b30f829SVladimir Oltean 	cfg->tx_min_frag_size = state->tx_min_frag_size;
185*2b30f829SVladimir Oltean }
186*2b30f829SVladimir Oltean 
187*2b30f829SVladimir Oltean int ethnl_set_mm(struct sk_buff *skb, struct genl_info *info)
188*2b30f829SVladimir Oltean {
189*2b30f829SVladimir Oltean 	struct netlink_ext_ack *extack = info->extack;
190*2b30f829SVladimir Oltean 	struct ethnl_req_info req_info = {};
191*2b30f829SVladimir Oltean 	struct ethtool_mm_state state = {};
192*2b30f829SVladimir Oltean 	struct nlattr **tb = info->attrs;
193*2b30f829SVladimir Oltean 	struct ethtool_mm_cfg cfg = {};
194*2b30f829SVladimir Oltean 	const struct ethtool_ops *ops;
195*2b30f829SVladimir Oltean 	struct net_device *dev;
196*2b30f829SVladimir Oltean 	bool mod = false;
197*2b30f829SVladimir Oltean 	int ret;
198*2b30f829SVladimir Oltean 
199*2b30f829SVladimir Oltean 	ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_MM_HEADER],
200*2b30f829SVladimir Oltean 					 genl_info_net(info), extack, true);
201*2b30f829SVladimir Oltean 	if (ret)
202*2b30f829SVladimir Oltean 		return ret;
203*2b30f829SVladimir Oltean 
204*2b30f829SVladimir Oltean 	dev = req_info.dev;
205*2b30f829SVladimir Oltean 	ops = dev->ethtool_ops;
206*2b30f829SVladimir Oltean 
207*2b30f829SVladimir Oltean 	if (!ops->get_mm || !ops->set_mm) {
208*2b30f829SVladimir Oltean 		ret = -EOPNOTSUPP;
209*2b30f829SVladimir Oltean 		goto out_dev_put;
210*2b30f829SVladimir Oltean 	}
211*2b30f829SVladimir Oltean 
212*2b30f829SVladimir Oltean 	rtnl_lock();
213*2b30f829SVladimir Oltean 	ret = ethnl_ops_begin(dev);
214*2b30f829SVladimir Oltean 	if (ret < 0)
215*2b30f829SVladimir Oltean 		goto out_rtnl_unlock;
216*2b30f829SVladimir Oltean 
217*2b30f829SVladimir Oltean 	ret = ops->get_mm(dev, &state);
218*2b30f829SVladimir Oltean 	if (ret)
219*2b30f829SVladimir Oltean 		goto out_complete;
220*2b30f829SVladimir Oltean 
221*2b30f829SVladimir Oltean 	mm_state_to_cfg(&state, &cfg);
222*2b30f829SVladimir Oltean 
223*2b30f829SVladimir Oltean 	ethnl_update_bool(&cfg.verify_enabled, tb[ETHTOOL_A_MM_VERIFY_ENABLED],
224*2b30f829SVladimir Oltean 			  &mod);
225*2b30f829SVladimir Oltean 	ethnl_update_u32(&cfg.verify_time, tb[ETHTOOL_A_MM_VERIFY_TIME], &mod);
226*2b30f829SVladimir Oltean 	ethnl_update_bool(&cfg.tx_enabled, tb[ETHTOOL_A_MM_TX_ENABLED], &mod);
227*2b30f829SVladimir Oltean 	ethnl_update_bool(&cfg.pmac_enabled, tb[ETHTOOL_A_MM_PMAC_ENABLED],
228*2b30f829SVladimir Oltean 			  &mod);
229*2b30f829SVladimir Oltean 	ethnl_update_u32(&cfg.tx_min_frag_size,
230*2b30f829SVladimir Oltean 			 tb[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE], &mod);
231*2b30f829SVladimir Oltean 
232*2b30f829SVladimir Oltean 	if (!mod)
233*2b30f829SVladimir Oltean 		goto out_complete;
234*2b30f829SVladimir Oltean 
235*2b30f829SVladimir Oltean 	if (cfg.verify_time > state.max_verify_time) {
236*2b30f829SVladimir Oltean 		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MM_VERIFY_TIME],
237*2b30f829SVladimir Oltean 				    "verifyTime exceeds device maximum");
238*2b30f829SVladimir Oltean 		ret = -ERANGE;
239*2b30f829SVladimir Oltean 		goto out_complete;
240*2b30f829SVladimir Oltean 	}
241*2b30f829SVladimir Oltean 
242*2b30f829SVladimir Oltean 	ret = ops->set_mm(dev, &cfg, extack);
243*2b30f829SVladimir Oltean 	if (ret)
244*2b30f829SVladimir Oltean 		goto out_complete;
245*2b30f829SVladimir Oltean 
246*2b30f829SVladimir Oltean 	ethtool_notify(dev, ETHTOOL_MSG_MM_NTF, NULL);
247*2b30f829SVladimir Oltean 
248*2b30f829SVladimir Oltean out_complete:
249*2b30f829SVladimir Oltean 	ethnl_ops_complete(dev);
250*2b30f829SVladimir Oltean out_rtnl_unlock:
251*2b30f829SVladimir Oltean 	rtnl_unlock();
252*2b30f829SVladimir Oltean out_dev_put:
253*2b30f829SVladimir Oltean 	ethnl_parse_header_dev_put(&req_info);
254*2b30f829SVladimir Oltean 	return ret;
255*2b30f829SVladimir Oltean }
256