File Index
Files and directories
Sign in
FILE

qeth_ethtool.c

File preview with syntax highlighting.

linux-6.19.14/drivers/s390/net/qeth_ethtool.c C
  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
 22struct qeth_stats {
 23	char name[ETH_GSTRING_LEN];
 24	unsigned int offset;
 25};
 26
 27static 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
 48static 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
 62static 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
 76static 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
 86static 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
 99static 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
111static 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
124static 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
145static 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
163static 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
185static 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
198static 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
213static 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
248static 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
259static 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
273static 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
288static 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
307static 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.							     */
329static 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
426static 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
446const 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};
~/ Read-only file preview.