1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright IBM Corp. 2018
6 #define KMSG_COMPONENT "qeth"
7 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
9 #include <linux/ethtool.h>
10 #include "qeth_core.h"
13 #define QETH_TXQ_STAT(_name, _stat) { \
15 .offset = offsetof(struct qeth_out_q_stats, _stat) \
18 #define QETH_CARD_STAT(_name, _stat) { \
20 .offset = offsetof(struct qeth_card_stats, _stat) \
24 char name
[ETH_GSTRING_LEN
];
28 static const struct qeth_stats txq_stats
[] = {
29 QETH_TXQ_STAT("IO buffers", bufs
),
30 QETH_TXQ_STAT("IO buffer elements", buf_elements
),
31 QETH_TXQ_STAT("packed IO buffers", bufs_pack
),
32 QETH_TXQ_STAT("skbs", tx_packets
),
33 QETH_TXQ_STAT("packed skbs", skbs_pack
),
34 QETH_TXQ_STAT("SG skbs", skbs_sg
),
35 QETH_TXQ_STAT("HW csum skbs", skbs_csum
),
36 QETH_TXQ_STAT("TSO skbs", skbs_tso
),
37 QETH_TXQ_STAT("linearized skbs", skbs_linearized
),
38 QETH_TXQ_STAT("linearized+error skbs", skbs_linearized_fail
),
39 QETH_TXQ_STAT("TSO bytes", tso_bytes
),
40 QETH_TXQ_STAT("Packing mode switches", packing_mode_switch
),
41 QETH_TXQ_STAT("Queue stopped", stopped
),
42 QETH_TXQ_STAT("Doorbell", doorbell
),
43 QETH_TXQ_STAT("IRQ for frames", coal_frames
),
44 QETH_TXQ_STAT("Completion yield", completion_yield
),
45 QETH_TXQ_STAT("Completion timer", completion_timer
),
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
),
59 #define TXQ_STATS_LEN ARRAY_SIZE(txq_stats)
60 #define CARD_STATS_LEN ARRAY_SIZE(card_stats)
62 static void qeth_add_stat_data(u64
**dst
, void *src
,
63 const struct qeth_stats stats
[],
69 for (i
= 0; i
< size
; i
++) {
70 stat
= (char *)src
+ stats
[i
].offset
;
76 static void qeth_add_stat_strings(u8
**data
, const char *prefix
,
77 const struct qeth_stats stats
[],
82 for (i
= 0; i
< size
; i
++) {
83 snprintf(*data
, ETH_GSTRING_LEN
, "%s%s", prefix
, stats
[i
].name
);
84 *data
+= ETH_GSTRING_LEN
;
88 static int qeth_get_sset_count(struct net_device
*dev
, int stringset
)
90 struct qeth_card
*card
= dev
->ml_priv
;
94 return CARD_STATS_LEN
+
95 card
->qdio
.no_out_queues
* TXQ_STATS_LEN
;
101 static void qeth_get_ethtool_stats(struct net_device
*dev
,
102 struct ethtool_stats
*stats
, u64
*data
)
104 struct qeth_card
*card
= dev
->ml_priv
;
107 qeth_add_stat_data(&data
, &card
->stats
, card_stats
, CARD_STATS_LEN
);
108 for (i
= 0; i
< card
->qdio
.no_out_queues
; i
++)
109 qeth_add_stat_data(&data
, &card
->qdio
.out_qs
[i
]->stats
,
110 txq_stats
, TXQ_STATS_LEN
);
113 static void __qeth_set_coalesce(struct net_device
*dev
,
114 struct qeth_qdio_out_q
*queue
,
115 struct ethtool_coalesce
*coal
)
117 WRITE_ONCE(queue
->coalesce_usecs
, coal
->tx_coalesce_usecs
);
118 WRITE_ONCE(queue
->max_coalesced_frames
, coal
->tx_max_coalesced_frames
);
120 if (coal
->tx_coalesce_usecs
&&
121 netif_running(dev
) &&
122 !qeth_out_queue_is_empty(queue
))
123 qeth_tx_arm_timer(queue
, coal
->tx_coalesce_usecs
);
126 static int qeth_set_coalesce(struct net_device
*dev
,
127 struct ethtool_coalesce
*coal
)
129 struct qeth_card
*card
= dev
->ml_priv
;
130 struct qeth_qdio_out_q
*queue
;
136 if (!coal
->tx_coalesce_usecs
&& !coal
->tx_max_coalesced_frames
)
139 qeth_for_each_output_queue(card
, queue
, i
)
140 __qeth_set_coalesce(dev
, queue
, coal
);
145 static void qeth_get_ringparam(struct net_device
*dev
,
146 struct ethtool_ringparam
*param
)
148 struct qeth_card
*card
= dev
->ml_priv
;
150 param
->rx_max_pending
= QDIO_MAX_BUFFERS_PER_Q
;
151 param
->rx_mini_max_pending
= 0;
152 param
->rx_jumbo_max_pending
= 0;
153 param
->tx_max_pending
= QDIO_MAX_BUFFERS_PER_Q
;
155 param
->rx_pending
= card
->qdio
.in_buf_pool
.buf_count
;
156 param
->rx_mini_pending
= 0;
157 param
->rx_jumbo_pending
= 0;
158 param
->tx_pending
= QDIO_MAX_BUFFERS_PER_Q
;
161 static void qeth_get_strings(struct net_device
*dev
, u32 stringset
, u8
*data
)
163 struct qeth_card
*card
= dev
->ml_priv
;
164 char prefix
[ETH_GSTRING_LEN
] = "";
169 qeth_add_stat_strings(&data
, prefix
, card_stats
,
171 for (i
= 0; i
< card
->qdio
.no_out_queues
; i
++) {
172 snprintf(prefix
, ETH_GSTRING_LEN
, "tx%u ", i
);
173 qeth_add_stat_strings(&data
, prefix
, txq_stats
,
183 static void qeth_get_drvinfo(struct net_device
*dev
,
184 struct ethtool_drvinfo
*info
)
186 struct qeth_card
*card
= dev
->ml_priv
;
188 strlcpy(info
->driver
, IS_LAYER2(card
) ? "qeth_l2" : "qeth_l3",
189 sizeof(info
->driver
));
190 strlcpy(info
->fw_version
, card
->info
.mcl_level
,
191 sizeof(info
->fw_version
));
192 snprintf(info
->bus_info
, sizeof(info
->bus_info
), "%s/%s/%s",
193 CARD_RDEV_ID(card
), CARD_WDEV_ID(card
), CARD_DDEV_ID(card
));
196 static void qeth_get_channels(struct net_device
*dev
,
197 struct ethtool_channels
*channels
)
199 struct qeth_card
*card
= dev
->ml_priv
;
201 channels
->max_rx
= dev
->num_rx_queues
;
202 channels
->max_tx
= card
->qdio
.no_out_queues
;
203 channels
->max_other
= 0;
204 channels
->max_combined
= 0;
205 channels
->rx_count
= dev
->real_num_rx_queues
;
206 channels
->tx_count
= dev
->real_num_tx_queues
;
207 channels
->other_count
= 0;
208 channels
->combined_count
= 0;
211 static int qeth_set_channels(struct net_device
*dev
,
212 struct ethtool_channels
*channels
)
214 struct qeth_priv
*priv
= netdev_priv(dev
);
215 struct qeth_card
*card
= dev
->ml_priv
;
218 if (channels
->rx_count
== 0 || channels
->tx_count
== 0)
220 if (channels
->tx_count
> card
->qdio
.no_out_queues
)
223 /* Prio-queueing needs all TX queues: */
224 if (qeth_uses_tx_prio_queueing(card
))
228 if (channels
->tx_count
< QETH_IQD_MIN_TXQ
)
231 /* Reject downgrade while running. It could push displaced
232 * ucast flows onto txq0, which is reserved for mcast.
234 if (netif_running(dev
) &&
235 channels
->tx_count
< dev
->real_num_tx_queues
)
239 rc
= qeth_set_real_num_tx_queues(card
, channels
->tx_count
);
241 priv
->tx_wanted_queues
= channels
->tx_count
;
246 static int qeth_get_ts_info(struct net_device
*dev
,
247 struct ethtool_ts_info
*info
)
249 struct qeth_card
*card
= dev
->ml_priv
;
254 return ethtool_op_get_ts_info(dev
, info
);
257 static int qeth_get_tunable(struct net_device
*dev
,
258 const struct ethtool_tunable
*tuna
, void *data
)
260 struct qeth_priv
*priv
= netdev_priv(dev
);
263 case ETHTOOL_RX_COPYBREAK
:
264 *(u32
*)data
= priv
->rx_copybreak
;
271 static int qeth_set_tunable(struct net_device
*dev
,
272 const struct ethtool_tunable
*tuna
,
275 struct qeth_priv
*priv
= netdev_priv(dev
);
278 case ETHTOOL_RX_COPYBREAK
:
279 WRITE_ONCE(priv
->rx_copybreak
, *(u32
*)data
);
286 static int qeth_get_per_queue_coalesce(struct net_device
*dev
, u32 __queue
,
287 struct ethtool_coalesce
*coal
)
289 struct qeth_card
*card
= dev
->ml_priv
;
290 struct qeth_qdio_out_q
*queue
;
295 if (__queue
>= card
->qdio
.no_out_queues
)
298 queue
= card
->qdio
.out_qs
[__queue
];
300 coal
->tx_coalesce_usecs
= queue
->coalesce_usecs
;
301 coal
->tx_max_coalesced_frames
= queue
->max_coalesced_frames
;
305 static int qeth_set_per_queue_coalesce(struct net_device
*dev
, u32 queue
,
306 struct ethtool_coalesce
*coal
)
308 struct qeth_card
*card
= dev
->ml_priv
;
313 if (queue
>= card
->qdio
.no_out_queues
)
316 if (!coal
->tx_coalesce_usecs
&& !coal
->tx_max_coalesced_frames
)
319 __qeth_set_coalesce(dev
, card
->qdio
.out_qs
[queue
], coal
);
323 /* Helper function to fill 'advertising' and 'supported' which are the same. */
324 /* Autoneg and full-duplex are supported and advertised unconditionally. */
325 /* Always advertise and support all speeds up to specified, and only one */
326 /* specified port type. */
327 static void qeth_set_ethtool_link_modes(struct ethtool_link_ksettings
*cmd
,
328 enum qeth_link_mode link_mode
)
330 ethtool_link_ksettings_zero_link_mode(cmd
, supported
);
331 ethtool_link_ksettings_zero_link_mode(cmd
, advertising
);
332 ethtool_link_ksettings_zero_link_mode(cmd
, lp_advertising
);
334 ethtool_link_ksettings_add_link_mode(cmd
, supported
, Autoneg
);
335 ethtool_link_ksettings_add_link_mode(cmd
, advertising
, Autoneg
);
337 switch (cmd
->base
.port
) {
339 ethtool_link_ksettings_add_link_mode(cmd
, supported
, TP
);
340 ethtool_link_ksettings_add_link_mode(cmd
, advertising
, TP
);
342 switch (cmd
->base
.speed
) {
344 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
346 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
350 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
352 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
354 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
356 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
360 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
362 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
364 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
366 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
370 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
372 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
374 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
376 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
385 ethtool_link_ksettings_add_link_mode(cmd
, supported
, FIBRE
);
386 ethtool_link_ksettings_add_link_mode(cmd
, advertising
, FIBRE
);
388 switch (cmd
->base
.speed
) {
390 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
392 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
396 if (link_mode
== QETH_LINK_MODE_FIBRE_LONG
) {
397 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
399 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
401 } else if (link_mode
== QETH_LINK_MODE_FIBRE_SHORT
) {
402 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
404 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
409 ethtool_link_ksettings_add_link_mode(cmd
, supported
,
411 ethtool_link_ksettings_add_link_mode(cmd
, advertising
,
424 static int qeth_get_link_ksettings(struct net_device
*netdev
,
425 struct ethtool_link_ksettings
*cmd
)
427 struct qeth_card
*card
= netdev
->ml_priv
;
428 struct qeth_link_info link_info
;
430 cmd
->base
.speed
= card
->info
.link_info
.speed
;
431 cmd
->base
.duplex
= card
->info
.link_info
.duplex
;
432 cmd
->base
.port
= card
->info
.link_info
.port
;
433 cmd
->base
.autoneg
= AUTONEG_ENABLE
;
434 cmd
->base
.phy_address
= 0;
435 cmd
->base
.mdio_support
= 0;
436 cmd
->base
.eth_tp_mdix
= ETH_TP_MDI_INVALID
;
437 cmd
->base
.eth_tp_mdix_ctrl
= ETH_TP_MDI_INVALID
;
439 /* Check if we can obtain more accurate information. */
440 if (!qeth_query_card_info(card
, &link_info
)) {
441 if (link_info
.speed
!= SPEED_UNKNOWN
)
442 cmd
->base
.speed
= link_info
.speed
;
443 if (link_info
.duplex
!= DUPLEX_UNKNOWN
)
444 cmd
->base
.duplex
= link_info
.duplex
;
445 if (link_info
.port
!= PORT_OTHER
)
446 cmd
->base
.port
= link_info
.port
;
449 qeth_set_ethtool_link_modes(cmd
, card
->info
.link_info
.link_mode
);
454 const struct ethtool_ops qeth_ethtool_ops
= {
455 .supported_coalesce_params
= ETHTOOL_COALESCE_TX_USECS
|
456 ETHTOOL_COALESCE_TX_MAX_FRAMES
,
457 .get_link
= ethtool_op_get_link
,
458 .set_coalesce
= qeth_set_coalesce
,
459 .get_ringparam
= qeth_get_ringparam
,
460 .get_strings
= qeth_get_strings
,
461 .get_ethtool_stats
= qeth_get_ethtool_stats
,
462 .get_sset_count
= qeth_get_sset_count
,
463 .get_drvinfo
= qeth_get_drvinfo
,
464 .get_channels
= qeth_get_channels
,
465 .set_channels
= qeth_set_channels
,
466 .get_ts_info
= qeth_get_ts_info
,
467 .get_tunable
= qeth_get_tunable
,
468 .set_tunable
= qeth_set_tunable
,
469 .get_per_queue_coalesce
= qeth_get_per_queue_coalesce
,
470 .set_per_queue_coalesce
= qeth_set_per_queue_coalesce
,
471 .get_link_ksettings
= qeth_get_link_ksettings
,
474 const struct ethtool_ops qeth_osn_ethtool_ops
= {
475 .get_strings
= qeth_get_strings
,
476 .get_ethtool_stats
= qeth_get_ethtool_stats
,
477 .get_sset_count
= qeth_get_sset_count
,
478 .get_drvinfo
= qeth_get_drvinfo
,