xref: /linux/net/ethtool/linkmodes.c (revision f72aa1b276281b4e4f75261af8425bc99d903f3e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include "netlink.h"
4 #include "common.h"
5 #include "bitset.h"
6 
7 /* LINKMODES_GET */
8 
9 struct linkmodes_req_info {
10 	struct ethnl_req_info		base;
11 };
12 
13 struct linkmodes_reply_data {
14 	struct ethnl_reply_data			base;
15 	struct ethtool_link_ksettings		ksettings;
16 	struct ethtool_link_settings_hdr	*lsettings;
17 	bool					peer_empty;
18 };
19 
20 #define LINKMODES_REPDATA(__reply_base) \
21 	container_of(__reply_base, struct linkmodes_reply_data, base)
22 
23 const struct nla_policy ethnl_linkmodes_get_policy[] = {
24 	[ETHTOOL_A_LINKMODES_HEADER]		=
25 		NLA_POLICY_NESTED(ethnl_header_policy),
26 };
27 
28 static int linkmodes_prepare_data(const struct ethnl_req_info *req_base,
29 				  struct ethnl_reply_data *reply_base,
30 				  const struct genl_info *info)
31 {
32 	struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
33 	struct net_device *dev = reply_base->dev;
34 	int ret;
35 
36 	data->lsettings = &data->ksettings.base;
37 
38 	ret = ethnl_ops_begin(dev);
39 	if (ret < 0)
40 		return ret;
41 
42 	ret = __ethtool_get_link_ksettings(dev, &data->ksettings);
43 	if (ret < 0) {
44 		GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
45 		goto out;
46 	}
47 
48 	if (!dev->ethtool_ops->cap_link_lanes_supported)
49 		data->ksettings.lanes = 0;
50 
51 	data->peer_empty =
52 		bitmap_empty(data->ksettings.link_modes.lp_advertising,
53 			     __ETHTOOL_LINK_MODE_MASK_NBITS);
54 
55 out:
56 	ethnl_ops_complete(dev);
57 	return ret;
58 }
59 
60 static int linkmodes_reply_size(const struct ethnl_req_info *req_base,
61 				const struct ethnl_reply_data *reply_base)
62 {
63 	const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
64 	const struct ethtool_link_ksettings *ksettings = &data->ksettings;
65 	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
66 	const struct ethtool_link_settings_hdr *lsettings;
67 	int len, ret;
68 
69 	lsettings = &ksettings->base;
70 
71 	len = nla_total_size(sizeof(u8)) /* LINKMODES_AUTONEG */
72 		+ nla_total_size(sizeof(u32)) /* LINKMODES_SPEED */
73 		+ nla_total_size(sizeof(u32)) /* LINKMODES_LANES */
74 		+ nla_total_size(sizeof(u8)) /* LINKMODES_DUPLEX */
75 		+ nla_total_size(sizeof(u8)) /* LINKMODES_RATE_MATCHING */
76 		+ 0;
77 	ret = ethnl_bitset_size(ksettings->link_modes.advertising,
78 				ksettings->link_modes.supported,
79 				__ETHTOOL_LINK_MODE_MASK_NBITS,
80 				link_mode_names, compact);
81 	if (ret < 0)
82 		return ret;
83 	len += ret;
84 	if (!data->peer_empty) {
85 		ret = ethnl_bitset_size(ksettings->link_modes.lp_advertising,
86 					NULL, __ETHTOOL_LINK_MODE_MASK_NBITS,
87 					link_mode_names, compact);
88 		if (ret < 0)
89 			return ret;
90 		len += ret;
91 	}
92 
93 	if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED)
94 		len += nla_total_size(sizeof(u8));
95 
96 	if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED)
97 		len += nla_total_size(sizeof(u8));
98 
99 	return len;
100 }
101 
102 static int linkmodes_fill_reply(struct sk_buff *skb,
103 				const struct ethnl_req_info *req_base,
104 				const struct ethnl_reply_data *reply_base)
105 {
106 	const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
107 	const struct ethtool_link_ksettings *ksettings = &data->ksettings;
108 	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
109 	const struct ethtool_link_settings_hdr *lsettings;
110 	int ret;
111 
112 	lsettings = &ksettings->base;
113 
114 	if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_AUTONEG, lsettings->autoneg))
115 		return -EMSGSIZE;
116 
117 	ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_OURS,
118 			       ksettings->link_modes.advertising,
119 			       ksettings->link_modes.supported,
120 			       __ETHTOOL_LINK_MODE_MASK_NBITS, link_mode_names,
121 			       compact);
122 	if (ret < 0)
123 		return -EMSGSIZE;
124 	if (!data->peer_empty) {
125 		ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_PEER,
126 				       ksettings->link_modes.lp_advertising,
127 				       NULL, __ETHTOOL_LINK_MODE_MASK_NBITS,
128 				       link_mode_names, compact);
129 		if (ret < 0)
130 			return -EMSGSIZE;
131 	}
132 
133 	if (nla_put_u32(skb, ETHTOOL_A_LINKMODES_SPEED, lsettings->speed) ||
134 	    nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex))
135 		return -EMSGSIZE;
136 
137 	if (ksettings->lanes &&
138 	    nla_put_u32(skb, ETHTOOL_A_LINKMODES_LANES, ksettings->lanes))
139 		return -EMSGSIZE;
140 
141 	if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED &&
142 	    nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,
143 		       lsettings->master_slave_cfg))
144 		return -EMSGSIZE;
145 
146 	if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED &&
147 	    nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE,
148 		       lsettings->master_slave_state))
149 		return -EMSGSIZE;
150 
151 	if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_RATE_MATCHING,
152 		       lsettings->rate_matching))
153 		return -EMSGSIZE;
154 
155 	return 0;
156 }
157 
158 /* LINKMODES_SET */
159 
160 const struct nla_policy ethnl_linkmodes_set_policy[] = {
161 	[ETHTOOL_A_LINKMODES_HEADER]		=
162 		NLA_POLICY_NESTED(ethnl_header_policy),
163 	[ETHTOOL_A_LINKMODES_AUTONEG]		= { .type = NLA_U8 },
164 	[ETHTOOL_A_LINKMODES_OURS]		= { .type = NLA_NESTED },
165 	[ETHTOOL_A_LINKMODES_SPEED]		= { .type = NLA_U32 },
166 	[ETHTOOL_A_LINKMODES_DUPLEX]		= { .type = NLA_U8 },
167 	[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]	= { .type = NLA_U8 },
168 	[ETHTOOL_A_LINKMODES_LANES]		= NLA_POLICY_RANGE(NLA_U32, 1, 8),
169 };
170 
171 /* Set advertised link modes to all supported modes matching requested speed,
172  * lanes and duplex values. Called when autonegotiation is on, speed, lanes or
173  * duplex is requested but no link mode change. This is done in userspace with
174  * ioctl() interface, move it into kernel for netlink.
175  * Returns true if advertised modes bitmap was modified.
176  */
177 static bool ethnl_auto_linkmodes(struct ethtool_link_ksettings *ksettings,
178 				 bool req_speed, bool req_lanes, bool req_duplex)
179 {
180 	unsigned long *advertising = ksettings->link_modes.advertising;
181 	unsigned long *supported = ksettings->link_modes.supported;
182 	DECLARE_BITMAP(old_adv, __ETHTOOL_LINK_MODE_MASK_NBITS);
183 	unsigned int i;
184 
185 	bitmap_copy(old_adv, advertising, __ETHTOOL_LINK_MODE_MASK_NBITS);
186 
187 	for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) {
188 		const struct link_mode_info *info = &link_mode_params[i];
189 
190 		if (info->speed == SPEED_UNKNOWN)
191 			continue;
192 		if (test_bit(i, supported) &&
193 		    (!req_speed || info->speed == ksettings->base.speed) &&
194 		    (!req_lanes || info->lanes == ksettings->lanes) &&
195 		    (!req_duplex || info->duplex == ksettings->base.duplex))
196 			set_bit(i, advertising);
197 		else
198 			clear_bit(i, advertising);
199 	}
200 
201 	return !bitmap_equal(old_adv, advertising,
202 			     __ETHTOOL_LINK_MODE_MASK_NBITS);
203 }
204 
205 static bool ethnl_validate_master_slave_cfg(u8 cfg)
206 {
207 	switch (cfg) {
208 	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
209 	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
210 	case MASTER_SLAVE_CFG_MASTER_FORCE:
211 	case MASTER_SLAVE_CFG_SLAVE_FORCE:
212 		return true;
213 	}
214 
215 	return false;
216 }
217 
218 static int ethnl_check_linkmodes(struct genl_info *info, struct nlattr **tb)
219 {
220 	const struct nlattr *master_slave_cfg, *lanes_cfg;
221 
222 	master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG];
223 	if (master_slave_cfg &&
224 	    !ethnl_validate_master_slave_cfg(nla_get_u8(master_slave_cfg))) {
225 		NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg,
226 				    "master/slave value is invalid");
227 		return -EOPNOTSUPP;
228 	}
229 
230 	lanes_cfg = tb[ETHTOOL_A_LINKMODES_LANES];
231 	if (lanes_cfg && !is_power_of_2(nla_get_u32(lanes_cfg))) {
232 		NL_SET_ERR_MSG_ATTR(info->extack, lanes_cfg,
233 				    "lanes value is invalid");
234 		return -EINVAL;
235 	}
236 
237 	return 0;
238 }
239 
240 static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb,
241 				  struct ethtool_link_ksettings *ksettings,
242 				  bool *mod, const struct net_device *dev)
243 {
244 	struct ethtool_link_settings_hdr *lsettings = &ksettings->base;
245 	bool req_speed, req_lanes, req_duplex;
246 	const struct nlattr *master_slave_cfg, *lanes_cfg;
247 	int ret;
248 
249 	master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG];
250 	if (master_slave_cfg) {
251 		if (lsettings->master_slave_cfg == MASTER_SLAVE_CFG_UNSUPPORTED) {
252 			NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg,
253 					    "master/slave configuration not supported by device");
254 			return -EOPNOTSUPP;
255 		}
256 	}
257 
258 	*mod = false;
259 	req_speed = tb[ETHTOOL_A_LINKMODES_SPEED];
260 	req_lanes = tb[ETHTOOL_A_LINKMODES_LANES];
261 	req_duplex = tb[ETHTOOL_A_LINKMODES_DUPLEX];
262 
263 	ethnl_update_u8(&lsettings->autoneg, tb[ETHTOOL_A_LINKMODES_AUTONEG],
264 			mod);
265 
266 	lanes_cfg = tb[ETHTOOL_A_LINKMODES_LANES];
267 	if (lanes_cfg) {
268 		/* If autoneg is off and lanes parameter is not supported by the
269 		 * driver, return an error.
270 		 */
271 		if (!lsettings->autoneg &&
272 		    !dev->ethtool_ops->cap_link_lanes_supported) {
273 			NL_SET_ERR_MSG_ATTR(info->extack, lanes_cfg,
274 					    "lanes configuration not supported by device");
275 			return -EOPNOTSUPP;
276 		}
277 	} else if (!lsettings->autoneg && ksettings->lanes) {
278 		/* If autoneg is off and lanes parameter is not passed from user but
279 		 * it was defined previously then set the lanes parameter to 0.
280 		 */
281 		ksettings->lanes = 0;
282 		*mod = true;
283 	}
284 
285 	ret = ethnl_update_bitset(ksettings->link_modes.advertising,
286 				  __ETHTOOL_LINK_MODE_MASK_NBITS,
287 				  tb[ETHTOOL_A_LINKMODES_OURS], link_mode_names,
288 				  info->extack, mod);
289 	if (ret < 0)
290 		return ret;
291 	ethnl_update_u32(&lsettings->speed, tb[ETHTOOL_A_LINKMODES_SPEED],
292 			 mod);
293 	ethnl_update_u32(&ksettings->lanes, lanes_cfg, mod);
294 	ethnl_update_u8(&lsettings->duplex, tb[ETHTOOL_A_LINKMODES_DUPLEX],
295 			mod);
296 	ethnl_update_u8(&lsettings->master_slave_cfg, master_slave_cfg, mod);
297 
298 	if (!tb[ETHTOOL_A_LINKMODES_OURS] && lsettings->autoneg &&
299 	    (req_speed || req_lanes || req_duplex) &&
300 	    ethnl_auto_linkmodes(ksettings, req_speed, req_lanes, req_duplex))
301 		*mod = true;
302 
303 	return 0;
304 }
305 
306 static int
307 ethnl_set_linkmodes_validate(struct ethnl_req_info *req_info,
308 			     struct genl_info *info)
309 {
310 	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
311 	int ret;
312 
313 	ret = ethnl_check_linkmodes(info, info->attrs);
314 	if (ret < 0)
315 		return ret;
316 
317 	if (!ops->get_link_ksettings || !ops->set_link_ksettings)
318 		return -EOPNOTSUPP;
319 	return 1;
320 }
321 
322 static int
323 ethnl_set_linkmodes(struct ethnl_req_info *req_info, struct genl_info *info)
324 {
325 	struct ethtool_link_ksettings ksettings = {};
326 	struct net_device *dev = req_info->dev;
327 	struct nlattr **tb = info->attrs;
328 	bool mod = false;
329 	int ret;
330 
331 	ret = __ethtool_get_link_ksettings(dev, &ksettings);
332 	if (ret < 0) {
333 		GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
334 		return ret;
335 	}
336 
337 	ret = ethnl_update_linkmodes(info, tb, &ksettings, &mod, dev);
338 	if (ret < 0)
339 		return ret;
340 	if (!mod)
341 		return 0;
342 
343 	ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings);
344 	if (ret < 0) {
345 		GENL_SET_ERR_MSG(info, "link settings update failed");
346 		return ret;
347 	}
348 
349 	return 1;
350 }
351 
352 const struct ethnl_request_ops ethnl_linkmodes_request_ops = {
353 	.request_cmd		= ETHTOOL_MSG_LINKMODES_GET,
354 	.reply_cmd		= ETHTOOL_MSG_LINKMODES_GET_REPLY,
355 	.hdr_attr		= ETHTOOL_A_LINKMODES_HEADER,
356 	.req_info_size		= sizeof(struct linkmodes_req_info),
357 	.reply_data_size	= sizeof(struct linkmodes_reply_data),
358 
359 	.prepare_data		= linkmodes_prepare_data,
360 	.reply_size		= linkmodes_reply_size,
361 	.fill_reply		= linkmodes_fill_reply,
362 
363 	.set_validate		= ethnl_set_linkmodes_validate,
364 	.set			= ethnl_set_linkmodes,
365 	.set_ntf_cmd		= ETHTOOL_MSG_LINKMODES_NTF,
366 };
367