xref: /linux/drivers/s390/net/qeth_ethtool.c (revision 7fc2cd2e4b398c57c9cf961cfea05eadbf34c05c)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright IBM Corp. 2018
4  */
5 
6 #define pr_fmt(fmt) "qeth: " fmt
7 
8 #include <linux/ethtool.h>
9 #include "qeth_core.h"
10 
11 
12 #define QETH_TXQ_STAT(_name, _stat) { \
13 	.name = _name, \
14 	.offset = offsetof(struct qeth_out_q_stats, _stat) \
15 }
16 
17 #define QETH_CARD_STAT(_name, _stat) { \
18 	.name = _name, \
19 	.offset = offsetof(struct qeth_card_stats, _stat) \
20 }
21 
22 struct qeth_stats {
23 	char name[ETH_GSTRING_LEN];
24 	unsigned int offset;
25 };
26 
27 static const struct qeth_stats txq_stats[] = {
28 	QETH_TXQ_STAT("IO buffers", bufs),
29 	QETH_TXQ_STAT("IO buffer elements", buf_elements),
30 	QETH_TXQ_STAT("packed IO buffers", bufs_pack),
31 	QETH_TXQ_STAT("skbs", tx_packets),
32 	QETH_TXQ_STAT("packed skbs", skbs_pack),
33 	QETH_TXQ_STAT("SG skbs", skbs_sg),
34 	QETH_TXQ_STAT("HW csum skbs", skbs_csum),
35 	QETH_TXQ_STAT("TSO skbs", skbs_tso),
36 	QETH_TXQ_STAT("linearized skbs", skbs_linearized),
37 	QETH_TXQ_STAT("linearized+error skbs", skbs_linearized_fail),
38 	QETH_TXQ_STAT("TSO bytes", tso_bytes),
39 	QETH_TXQ_STAT("Packing mode switches", packing_mode_switch),
40 	QETH_TXQ_STAT("Queue stopped", stopped),
41 	QETH_TXQ_STAT("Doorbell", doorbell),
42 	QETH_TXQ_STAT("IRQ for frames", coal_frames),
43 	QETH_TXQ_STAT("Completion IRQ", completion_irq),
44 	QETH_TXQ_STAT("Completion yield", completion_yield),
45 	QETH_TXQ_STAT("Completion timer", completion_timer),
46 };
47 
48 static const struct qeth_stats card_stats[] = {
49 	QETH_CARD_STAT("rx0 IO buffers", rx_bufs),
50 	QETH_CARD_STAT("rx0 HW csum skbs", rx_skb_csum),
51 	QETH_CARD_STAT("rx0 SG skbs", rx_sg_skbs),
52 	QETH_CARD_STAT("rx0 SG page frags", rx_sg_frags),
53 	QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page),
54 	QETH_CARD_STAT("rx0 dropped, no memory", rx_dropped_nomem),
55 	QETH_CARD_STAT("rx0 dropped, bad format", rx_dropped_notsupp),
56 	QETH_CARD_STAT("rx0 dropped, runt", rx_dropped_runt),
57 };
58 
59 #define TXQ_STATS_LEN	ARRAY_SIZE(txq_stats)
60 #define CARD_STATS_LEN	ARRAY_SIZE(card_stats)
61 
62 static void qeth_add_stat_data(u64 **dst, void *src,
63 			       const struct qeth_stats stats[],
64 			       unsigned int size)
65 {
66 	unsigned int i;
67 	char *stat;
68 
69 	for (i = 0; i < size; i++) {
70 		stat = (char *)src + stats[i].offset;
71 		**dst = *(u64 *)stat;
72 		(*dst)++;
73 	}
74 }
75 
76 static void qeth_add_stat_strings(u8 **data, const char *prefix,
77 				  const struct qeth_stats stats[],
78 				  unsigned int size)
79 {
80 	unsigned int i;
81 
82 	for (i = 0; i < size; i++)
83 		ethtool_sprintf(data, "%s%s", prefix, stats[i].name);
84 }
85 
86 static int qeth_get_sset_count(struct net_device *dev, int stringset)
87 {
88 	struct qeth_card *card = dev->ml_priv;
89 
90 	switch (stringset) {
91 	case ETH_SS_STATS:
92 		return CARD_STATS_LEN +
93 		       card->qdio.no_out_queues * TXQ_STATS_LEN;
94 	default:
95 		return -EINVAL;
96 	}
97 }
98 
99 static void qeth_get_ethtool_stats(struct net_device *dev,
100 				   struct ethtool_stats *stats, u64 *data)
101 {
102 	struct qeth_card *card = dev->ml_priv;
103 	unsigned int i;
104 
105 	qeth_add_stat_data(&data, &card->stats, card_stats, CARD_STATS_LEN);
106 	for (i = 0; i < card->qdio.no_out_queues; i++)
107 		qeth_add_stat_data(&data, &card->qdio.out_qs[i]->stats,
108 				   txq_stats, TXQ_STATS_LEN);
109 }
110 
111 static void __qeth_set_coalesce(struct net_device *dev,
112 				struct qeth_qdio_out_q *queue,
113 				struct ethtool_coalesce *coal)
114 {
115 	WRITE_ONCE(queue->coalesce_usecs, coal->tx_coalesce_usecs);
116 	WRITE_ONCE(queue->max_coalesced_frames, coal->tx_max_coalesced_frames);
117 
118 	if (coal->tx_coalesce_usecs &&
119 	    netif_running(dev) &&
120 	    !qeth_out_queue_is_empty(queue))
121 		qeth_tx_arm_timer(queue, coal->tx_coalesce_usecs);
122 }
123 
124 static int qeth_set_coalesce(struct net_device *dev,
125 			     struct ethtool_coalesce *coal,
126 			     struct kernel_ethtool_coalesce *kernel_coal,
127 			     struct netlink_ext_ack *extack)
128 {
129 	struct qeth_card *card = dev->ml_priv;
130 	struct qeth_qdio_out_q *queue;
131 	unsigned int i;
132 
133 	if (!IS_IQD(card))
134 		return -EOPNOTSUPP;
135 
136 	if (!coal->tx_coalesce_usecs && !coal->tx_max_coalesced_frames)
137 		return -EINVAL;
138 
139 	qeth_for_each_output_queue(card, queue, i)
140 		__qeth_set_coalesce(dev, queue, coal);
141 
142 	return 0;
143 }
144 
145 static void qeth_get_ringparam(struct net_device *dev,
146 			       struct ethtool_ringparam *param,
147 			       struct kernel_ethtool_ringparam *kernel_param,
148 			       struct netlink_ext_ack *extack)
149 {
150 	struct qeth_card *card = dev->ml_priv;
151 
152 	param->rx_max_pending = QDIO_MAX_BUFFERS_PER_Q;
153 	param->rx_mini_max_pending = 0;
154 	param->rx_jumbo_max_pending = 0;
155 	param->tx_max_pending = QDIO_MAX_BUFFERS_PER_Q;
156 
157 	param->rx_pending = card->qdio.in_buf_pool.buf_count;
158 	param->rx_mini_pending = 0;
159 	param->rx_jumbo_pending = 0;
160 	param->tx_pending = QDIO_MAX_BUFFERS_PER_Q;
161 }
162 
163 static void qeth_get_strings(struct net_device *dev, u32 stringset, u8 *data)
164 {
165 	struct qeth_card *card = dev->ml_priv;
166 	char prefix[ETH_GSTRING_LEN] = "";
167 	unsigned int i;
168 
169 	switch (stringset) {
170 	case ETH_SS_STATS:
171 		qeth_add_stat_strings(&data, prefix, card_stats,
172 				      CARD_STATS_LEN);
173 		for (i = 0; i < card->qdio.no_out_queues; i++) {
174 			scnprintf(prefix, ETH_GSTRING_LEN, "tx%u ", i);
175 			qeth_add_stat_strings(&data, prefix, txq_stats,
176 					      TXQ_STATS_LEN);
177 		}
178 		break;
179 	default:
180 		WARN_ON(1);
181 		break;
182 	}
183 }
184 
185 static void qeth_get_drvinfo(struct net_device *dev,
186 			     struct ethtool_drvinfo *info)
187 {
188 	struct qeth_card *card = dev->ml_priv;
189 
190 	strscpy(info->driver, IS_LAYER2(card) ? "qeth_l2" : "qeth_l3",
191 		sizeof(info->driver));
192 	strscpy(info->fw_version, card->info.mcl_level,
193 		sizeof(info->fw_version));
194 	scnprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s",
195 		  CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card));
196 }
197 
198 static void qeth_get_channels(struct net_device *dev,
199 			      struct ethtool_channels *channels)
200 {
201 	struct qeth_card *card = dev->ml_priv;
202 
203 	channels->max_rx = dev->num_rx_queues;
204 	channels->max_tx = card->qdio.no_out_queues;
205 	channels->max_other = 0;
206 	channels->max_combined = 0;
207 	channels->rx_count = dev->real_num_rx_queues;
208 	channels->tx_count = dev->real_num_tx_queues;
209 	channels->other_count = 0;
210 	channels->combined_count = 0;
211 }
212 
213 static int qeth_set_channels(struct net_device *dev,
214 			     struct ethtool_channels *channels)
215 {
216 	struct qeth_priv *priv = netdev_priv(dev);
217 	struct qeth_card *card = dev->ml_priv;
218 	int rc;
219 
220 	if (channels->rx_count == 0 || channels->tx_count == 0)
221 		return -EINVAL;
222 	if (channels->tx_count > card->qdio.no_out_queues)
223 		return -EINVAL;
224 
225 	/* Prio-queueing needs all TX queues: */
226 	if (qeth_uses_tx_prio_queueing(card))
227 		return -EPERM;
228 
229 	if (IS_IQD(card)) {
230 		if (channels->tx_count < QETH_IQD_MIN_TXQ)
231 			return -EINVAL;
232 
233 		/* Reject downgrade while running. It could push displaced
234 		 * ucast flows onto txq0, which is reserved for mcast.
235 		 */
236 		if (netif_running(dev) &&
237 		    channels->tx_count < dev->real_num_tx_queues)
238 			return -EPERM;
239 	}
240 
241 	rc = qeth_set_real_num_tx_queues(card, channels->tx_count);
242 	if (!rc)
243 		priv->tx_wanted_queues = channels->tx_count;
244 
245 	return rc;
246 }
247 
248 static int qeth_get_ts_info(struct net_device *dev,
249 			    struct kernel_ethtool_ts_info *info)
250 {
251 	struct qeth_card *card = dev->ml_priv;
252 
253 	if (!IS_IQD(card))
254 		return -EOPNOTSUPP;
255 
256 	return ethtool_op_get_ts_info(dev, info);
257 }
258 
259 static int qeth_get_tunable(struct net_device *dev,
260 			    const struct ethtool_tunable *tuna, void *data)
261 {
262 	struct qeth_priv *priv = netdev_priv(dev);
263 
264 	switch (tuna->id) {
265 	case ETHTOOL_RX_COPYBREAK:
266 		*(u32 *)data = priv->rx_copybreak;
267 		return 0;
268 	default:
269 		return -EOPNOTSUPP;
270 	}
271 }
272 
273 static int qeth_set_tunable(struct net_device *dev,
274 			    const struct ethtool_tunable *tuna,
275 			    const void *data)
276 {
277 	struct qeth_priv *priv = netdev_priv(dev);
278 
279 	switch (tuna->id) {
280 	case ETHTOOL_RX_COPYBREAK:
281 		WRITE_ONCE(priv->rx_copybreak, *(u32 *)data);
282 		return 0;
283 	default:
284 		return -EOPNOTSUPP;
285 	}
286 }
287 
288 static int qeth_get_per_queue_coalesce(struct net_device *dev, u32 __queue,
289 				       struct ethtool_coalesce *coal)
290 {
291 	struct qeth_card *card = dev->ml_priv;
292 	struct qeth_qdio_out_q *queue;
293 
294 	if (!IS_IQD(card))
295 		return -EOPNOTSUPP;
296 
297 	if (__queue >= card->qdio.no_out_queues)
298 		return -EINVAL;
299 
300 	queue = card->qdio.out_qs[__queue];
301 
302 	coal->tx_coalesce_usecs = queue->coalesce_usecs;
303 	coal->tx_max_coalesced_frames = queue->max_coalesced_frames;
304 	return 0;
305 }
306 
307 static int qeth_set_per_queue_coalesce(struct net_device *dev, u32 queue,
308 				       struct ethtool_coalesce *coal)
309 {
310 	struct qeth_card *card = dev->ml_priv;
311 
312 	if (!IS_IQD(card))
313 		return -EOPNOTSUPP;
314 
315 	if (queue >= card->qdio.no_out_queues)
316 		return -EINVAL;
317 
318 	if (!coal->tx_coalesce_usecs && !coal->tx_max_coalesced_frames)
319 		return -EINVAL;
320 
321 	__qeth_set_coalesce(dev, card->qdio.out_qs[queue], coal);
322 	return 0;
323 }
324 
325 /* Helper function to fill 'advertising' and 'supported' which are the same. */
326 /* Autoneg and full-duplex are supported and advertised unconditionally.     */
327 /* Always advertise and support all speeds up to specified, and only one     */
328 /* specified port type.							     */
329 static void qeth_set_ethtool_link_modes(struct ethtool_link_ksettings *cmd,
330 					enum qeth_link_mode link_mode)
331 {
332 	ethtool_link_ksettings_zero_link_mode(cmd, supported);
333 	ethtool_link_ksettings_zero_link_mode(cmd, advertising);
334 	ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising);
335 
336 	ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
337 	ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
338 
339 	switch (cmd->base.port) {
340 	case PORT_TP:
341 		ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
342 		ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
343 
344 		switch (cmd->base.speed) {
345 		case SPEED_10000:
346 			ethtool_link_ksettings_add_link_mode(cmd, supported,
347 							     10000baseT_Full);
348 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
349 							     10000baseT_Full);
350 			fallthrough;
351 		case SPEED_1000:
352 			ethtool_link_ksettings_add_link_mode(cmd, supported,
353 							     1000baseT_Full);
354 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
355 							     1000baseT_Full);
356 			ethtool_link_ksettings_add_link_mode(cmd, supported,
357 							     1000baseT_Half);
358 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
359 							     1000baseT_Half);
360 			fallthrough;
361 		case SPEED_100:
362 			ethtool_link_ksettings_add_link_mode(cmd, supported,
363 							     100baseT_Full);
364 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
365 							     100baseT_Full);
366 			ethtool_link_ksettings_add_link_mode(cmd, supported,
367 							     100baseT_Half);
368 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
369 							     100baseT_Half);
370 			fallthrough;
371 		case SPEED_10:
372 			ethtool_link_ksettings_add_link_mode(cmd, supported,
373 							     10baseT_Full);
374 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
375 							     10baseT_Full);
376 			ethtool_link_ksettings_add_link_mode(cmd, supported,
377 							     10baseT_Half);
378 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
379 							     10baseT_Half);
380 			break;
381 		default:
382 			break;
383 		}
384 
385 		break;
386 	case PORT_FIBRE:
387 		ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
388 		ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);
389 
390 		switch (cmd->base.speed) {
391 		case SPEED_25000:
392 			ethtool_link_ksettings_add_link_mode(cmd, supported,
393 							     25000baseSR_Full);
394 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
395 							     25000baseSR_Full);
396 			break;
397 		case SPEED_10000:
398 			if (link_mode == QETH_LINK_MODE_FIBRE_LONG) {
399 				ethtool_link_ksettings_add_link_mode(cmd, supported,
400 								     10000baseLR_Full);
401 				ethtool_link_ksettings_add_link_mode(cmd, advertising,
402 								     10000baseLR_Full);
403 			} else if (link_mode == QETH_LINK_MODE_FIBRE_SHORT) {
404 				ethtool_link_ksettings_add_link_mode(cmd, supported,
405 								     10000baseSR_Full);
406 				ethtool_link_ksettings_add_link_mode(cmd, advertising,
407 								     10000baseSR_Full);
408 			}
409 			break;
410 		case SPEED_1000:
411 			ethtool_link_ksettings_add_link_mode(cmd, supported,
412 							     1000baseX_Full);
413 			ethtool_link_ksettings_add_link_mode(cmd, advertising,
414 							     1000baseX_Full);
415 			break;
416 		default:
417 			break;
418 		}
419 
420 		break;
421 	default:
422 		break;
423 	}
424 }
425 
426 static int qeth_get_link_ksettings(struct net_device *netdev,
427 				   struct ethtool_link_ksettings *cmd)
428 {
429 	struct qeth_card *card = netdev->ml_priv;
430 
431 	QETH_CARD_TEXT(card, 4, "ethtglks");
432 	cmd->base.speed = card->info.link_info.speed;
433 	cmd->base.duplex = card->info.link_info.duplex;
434 	cmd->base.port = card->info.link_info.port;
435 	cmd->base.autoneg = AUTONEG_ENABLE;
436 	cmd->base.phy_address = 0;
437 	cmd->base.mdio_support = 0;
438 	cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
439 	cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
440 
441 	qeth_set_ethtool_link_modes(cmd, card->info.link_info.link_mode);
442 
443 	return 0;
444 }
445 
446 const struct ethtool_ops qeth_ethtool_ops = {
447 	.supported_coalesce_params = ETHTOOL_COALESCE_TX_USECS |
448 				     ETHTOOL_COALESCE_TX_MAX_FRAMES,
449 	.get_link = ethtool_op_get_link,
450 	.set_coalesce = qeth_set_coalesce,
451 	.get_ringparam = qeth_get_ringparam,
452 	.get_strings = qeth_get_strings,
453 	.get_ethtool_stats = qeth_get_ethtool_stats,
454 	.get_sset_count = qeth_get_sset_count,
455 	.get_drvinfo = qeth_get_drvinfo,
456 	.get_channels = qeth_get_channels,
457 	.set_channels = qeth_set_channels,
458 	.get_ts_info = qeth_get_ts_info,
459 	.get_tunable = qeth_get_tunable,
460 	.set_tunable = qeth_set_tunable,
461 	.get_per_queue_coalesce = qeth_get_per_queue_coalesce,
462 	.set_per_queue_coalesce = qeth_set_per_queue_coalesce,
463 	.get_link_ksettings = qeth_get_link_ksettings,
464 };
465