• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Statistics for Ocelot switch family
3  *
4  * Copyright (c) 2017 Microsemi Corporation
5  * Copyright 2022 NXP
6  */
7 #include <linux/spinlock.h>
8 #include <linux/mutex.h>
9 #include <linux/workqueue.h>
10 #include "ocelot.h"
11 
12 /* Read the counters from hardware and keep them in region->buf.
13  * Caller must hold &ocelot->stat_view_lock.
14  */
ocelot_port_update_stats(struct ocelot * ocelot,int port)15 static int ocelot_port_update_stats(struct ocelot *ocelot, int port)
16 {
17 	struct ocelot_stats_region *region;
18 	int err;
19 
20 	/* Configure the port to read the stats from */
21 	ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG);
22 
23 	list_for_each_entry(region, &ocelot->stats_regions, node) {
24 		err = ocelot_bulk_read(ocelot, region->base, region->buf,
25 				       region->count);
26 		if (err)
27 			return err;
28 	}
29 
30 	return 0;
31 }
32 
33 /* Transfer the counters from region->buf to ocelot->stats.
34  * Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock.
35  */
ocelot_port_transfer_stats(struct ocelot * ocelot,int port)36 static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port)
37 {
38 	unsigned int idx = port * OCELOT_NUM_STATS;
39 	struct ocelot_stats_region *region;
40 	int j;
41 
42 	list_for_each_entry(region, &ocelot->stats_regions, node) {
43 		for (j = 0; j < region->count; j++) {
44 			u64 *stat = &ocelot->stats[idx + j];
45 			u64 val = region->buf[j];
46 
47 			if (val < (*stat & U32_MAX))
48 				*stat += (u64)1 << 32;
49 
50 			*stat = (*stat & ~(u64)U32_MAX) + val;
51 		}
52 
53 		idx += region->count;
54 	}
55 }
56 
ocelot_check_stats_work(struct work_struct * work)57 static void ocelot_check_stats_work(struct work_struct *work)
58 {
59 	struct delayed_work *del_work = to_delayed_work(work);
60 	struct ocelot *ocelot = container_of(del_work, struct ocelot,
61 					     stats_work);
62 	int port, err;
63 
64 	mutex_lock(&ocelot->stat_view_lock);
65 
66 	for (port = 0; port < ocelot->num_phys_ports; port++) {
67 		err = ocelot_port_update_stats(ocelot, port);
68 		if (err)
69 			break;
70 
71 		spin_lock(&ocelot->stats_lock);
72 		ocelot_port_transfer_stats(ocelot, port);
73 		spin_unlock(&ocelot->stats_lock);
74 	}
75 
76 	if (!err && ocelot->ops->update_stats)
77 		ocelot->ops->update_stats(ocelot);
78 
79 	mutex_unlock(&ocelot->stat_view_lock);
80 
81 	if (err)
82 		dev_err(ocelot->dev, "Error %d updating ethtool stats\n",  err);
83 
84 	queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
85 			   OCELOT_STATS_CHECK_DELAY);
86 }
87 
ocelot_get_strings(struct ocelot * ocelot,int port,u32 sset,u8 * data)88 void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
89 {
90 	int i;
91 
92 	if (sset != ETH_SS_STATS)
93 		return;
94 
95 	for (i = 0; i < OCELOT_NUM_STATS; i++) {
96 		if (ocelot->stats_layout[i].name[0] == '\0')
97 			continue;
98 
99 		memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name,
100 		       ETH_GSTRING_LEN);
101 	}
102 }
103 EXPORT_SYMBOL(ocelot_get_strings);
104 
105 /* Update ocelot->stats for the given port and run the given callback */
ocelot_port_stats_run(struct ocelot * ocelot,int port,void * priv,void (* cb)(struct ocelot * ocelot,int port,void * priv))106 static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv,
107 				  void (*cb)(struct ocelot *ocelot, int port,
108 					     void *priv))
109 {
110 	int err;
111 
112 	mutex_lock(&ocelot->stat_view_lock);
113 
114 	err = ocelot_port_update_stats(ocelot, port);
115 	if (err) {
116 		dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n",
117 			port, ERR_PTR(err));
118 		goto out_unlock;
119 	}
120 
121 	spin_lock(&ocelot->stats_lock);
122 
123 	ocelot_port_transfer_stats(ocelot, port);
124 	cb(ocelot, port, priv);
125 
126 	spin_unlock(&ocelot->stats_lock);
127 
128 out_unlock:
129 	mutex_unlock(&ocelot->stat_view_lock);
130 }
131 
ocelot_get_sset_count(struct ocelot * ocelot,int port,int sset)132 int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
133 {
134 	int i, num_stats = 0;
135 
136 	if (sset != ETH_SS_STATS)
137 		return -EOPNOTSUPP;
138 
139 	for (i = 0; i < OCELOT_NUM_STATS; i++)
140 		if (ocelot->stats_layout[i].name[0] != '\0')
141 			num_stats++;
142 
143 	return num_stats;
144 }
145 EXPORT_SYMBOL(ocelot_get_sset_count);
146 
ocelot_port_ethtool_stats_cb(struct ocelot * ocelot,int port,void * priv)147 static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port,
148 					 void *priv)
149 {
150 	u64 *data = priv;
151 	int i;
152 
153 	/* Copy all supported counters */
154 	for (i = 0; i < OCELOT_NUM_STATS; i++) {
155 		int index = port * OCELOT_NUM_STATS + i;
156 
157 		if (ocelot->stats_layout[i].name[0] == '\0')
158 			continue;
159 
160 		*data++ = ocelot->stats[index];
161 	}
162 }
163 
ocelot_get_ethtool_stats(struct ocelot * ocelot,int port,u64 * data)164 void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
165 {
166 	ocelot_port_stats_run(ocelot, port, data, ocelot_port_ethtool_stats_cb);
167 }
168 EXPORT_SYMBOL(ocelot_get_ethtool_stats);
169 
ocelot_port_pause_stats_cb(struct ocelot * ocelot,int port,void * priv)170 static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv)
171 {
172 	u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
173 	struct ethtool_pause_stats *pause_stats = priv;
174 
175 	pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE];
176 	pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE];
177 }
178 
ocelot_port_get_pause_stats(struct ocelot * ocelot,int port,struct ethtool_pause_stats * pause_stats)179 void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
180 				 struct ethtool_pause_stats *pause_stats)
181 {
182 	ocelot_port_stats_run(ocelot, port, pause_stats,
183 			      ocelot_port_pause_stats_cb);
184 }
185 EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats);
186 
187 static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = {
188 	{   64,    64 },
189 	{   65,   127 },
190 	{  128,   255 },
191 	{  256,   511 },
192 	{  512,  1023 },
193 	{ 1024,  1526 },
194 	{ 1527, 65535 },
195 	{},
196 };
197 
ocelot_port_rmon_stats_cb(struct ocelot * ocelot,int port,void * priv)198 static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv)
199 {
200 	u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
201 	struct ethtool_rmon_stats *rmon_stats = priv;
202 
203 	rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS];
204 	rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS];
205 	rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS];
206 	rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS];
207 
208 	rmon_stats->hist[0] = s[OCELOT_STAT_RX_64];
209 	rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127];
210 	rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255];
211 	rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511];
212 	rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023];
213 	rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526];
214 	rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX];
215 
216 	rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64];
217 	rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127];
218 	rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255];
219 	rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_256_511];
220 	rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_512_1023];
221 	rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_1024_1526];
222 	rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1527_MAX];
223 }
224 
ocelot_port_get_rmon_stats(struct ocelot * ocelot,int port,struct ethtool_rmon_stats * rmon_stats,const struct ethtool_rmon_hist_range ** ranges)225 void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
226 				struct ethtool_rmon_stats *rmon_stats,
227 				const struct ethtool_rmon_hist_range **ranges)
228 {
229 	*ranges = ocelot_rmon_ranges;
230 
231 	ocelot_port_stats_run(ocelot, port, rmon_stats,
232 			      ocelot_port_rmon_stats_cb);
233 }
234 EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats);
235 
ocelot_port_ctrl_stats_cb(struct ocelot * ocelot,int port,void * priv)236 static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv)
237 {
238 	u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
239 	struct ethtool_eth_ctrl_stats *ctrl_stats = priv;
240 
241 	ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL];
242 }
243 
ocelot_port_get_eth_ctrl_stats(struct ocelot * ocelot,int port,struct ethtool_eth_ctrl_stats * ctrl_stats)244 void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port,
245 				    struct ethtool_eth_ctrl_stats *ctrl_stats)
246 {
247 	ocelot_port_stats_run(ocelot, port, ctrl_stats,
248 			      ocelot_port_ctrl_stats_cb);
249 }
250 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats);
251 
ocelot_port_mac_stats_cb(struct ocelot * ocelot,int port,void * priv)252 static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv)
253 {
254 	u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
255 	struct ethtool_eth_mac_stats *mac_stats = priv;
256 
257 	mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS];
258 	mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] +
259 					 s[OCELOT_STAT_TX_65_127] +
260 					 s[OCELOT_STAT_TX_128_255] +
261 					 s[OCELOT_STAT_TX_256_511] +
262 					 s[OCELOT_STAT_TX_512_1023] +
263 					 s[OCELOT_STAT_TX_1024_1526] +
264 					 s[OCELOT_STAT_TX_1527_MAX];
265 	mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS];
266 	mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] +
267 				      s[OCELOT_STAT_RX_GREEN_PRIO_1] +
268 				      s[OCELOT_STAT_RX_GREEN_PRIO_2] +
269 				      s[OCELOT_STAT_RX_GREEN_PRIO_3] +
270 				      s[OCELOT_STAT_RX_GREEN_PRIO_4] +
271 				      s[OCELOT_STAT_RX_GREEN_PRIO_5] +
272 				      s[OCELOT_STAT_RX_GREEN_PRIO_6] +
273 				      s[OCELOT_STAT_RX_GREEN_PRIO_7] +
274 				      s[OCELOT_STAT_RX_YELLOW_PRIO_0] +
275 				      s[OCELOT_STAT_RX_YELLOW_PRIO_1] +
276 				      s[OCELOT_STAT_RX_YELLOW_PRIO_2] +
277 				      s[OCELOT_STAT_RX_YELLOW_PRIO_3] +
278 				      s[OCELOT_STAT_RX_YELLOW_PRIO_4] +
279 				      s[OCELOT_STAT_RX_YELLOW_PRIO_5] +
280 				      s[OCELOT_STAT_RX_YELLOW_PRIO_6] +
281 				      s[OCELOT_STAT_RX_YELLOW_PRIO_7];
282 	mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST];
283 	mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST];
284 	mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST];
285 	mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST];
286 	mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS];
287 	/* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not
288 	 * counted individually.
289 	 */
290 	mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
291 	mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
292 }
293 
ocelot_port_get_eth_mac_stats(struct ocelot * ocelot,int port,struct ethtool_eth_mac_stats * mac_stats)294 void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
295 				   struct ethtool_eth_mac_stats *mac_stats)
296 {
297 	ocelot_port_stats_run(ocelot, port, mac_stats,
298 			      ocelot_port_mac_stats_cb);
299 }
300 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats);
301 
ocelot_port_phy_stats_cb(struct ocelot * ocelot,int port,void * priv)302 static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv)
303 {
304 	u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
305 	struct ethtool_eth_phy_stats *phy_stats = priv;
306 
307 	phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS];
308 }
309 
ocelot_port_get_eth_phy_stats(struct ocelot * ocelot,int port,struct ethtool_eth_phy_stats * phy_stats)310 void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
311 				   struct ethtool_eth_phy_stats *phy_stats)
312 {
313 	ocelot_port_stats_run(ocelot, port, phy_stats,
314 			      ocelot_port_phy_stats_cb);
315 }
316 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats);
317 
ocelot_port_get_stats64(struct ocelot * ocelot,int port,struct rtnl_link_stats64 * stats)318 void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
319 			     struct rtnl_link_stats64 *stats)
320 {
321 	u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
322 
323 	spin_lock(&ocelot->stats_lock);
324 
325 	/* Get Rx stats */
326 	stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS];
327 	stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] +
328 			    s[OCELOT_STAT_RX_FRAGMENTS] +
329 			    s[OCELOT_STAT_RX_JABBERS] +
330 			    s[OCELOT_STAT_RX_LONGS] +
331 			    s[OCELOT_STAT_RX_64] +
332 			    s[OCELOT_STAT_RX_65_127] +
333 			    s[OCELOT_STAT_RX_128_255] +
334 			    s[OCELOT_STAT_RX_256_511] +
335 			    s[OCELOT_STAT_RX_512_1023] +
336 			    s[OCELOT_STAT_RX_1024_1526] +
337 			    s[OCELOT_STAT_RX_1527_MAX];
338 	stats->multicast = s[OCELOT_STAT_RX_MULTICAST];
339 	stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL];
340 	stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] +
341 			    s[OCELOT_STAT_RX_RED_PRIO_1] +
342 			    s[OCELOT_STAT_RX_RED_PRIO_2] +
343 			    s[OCELOT_STAT_RX_RED_PRIO_3] +
344 			    s[OCELOT_STAT_RX_RED_PRIO_4] +
345 			    s[OCELOT_STAT_RX_RED_PRIO_5] +
346 			    s[OCELOT_STAT_RX_RED_PRIO_6] +
347 			    s[OCELOT_STAT_RX_RED_PRIO_7] +
348 			    s[OCELOT_STAT_DROP_LOCAL] +
349 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_0] +
350 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_1] +
351 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_2] +
352 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_3] +
353 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_4] +
354 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_5] +
355 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_6] +
356 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_7] +
357 			    s[OCELOT_STAT_DROP_GREEN_PRIO_0] +
358 			    s[OCELOT_STAT_DROP_GREEN_PRIO_1] +
359 			    s[OCELOT_STAT_DROP_GREEN_PRIO_2] +
360 			    s[OCELOT_STAT_DROP_GREEN_PRIO_3] +
361 			    s[OCELOT_STAT_DROP_GREEN_PRIO_4] +
362 			    s[OCELOT_STAT_DROP_GREEN_PRIO_5] +
363 			    s[OCELOT_STAT_DROP_GREEN_PRIO_6] +
364 			    s[OCELOT_STAT_DROP_GREEN_PRIO_7];
365 
366 	/* Get Tx stats */
367 	stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS];
368 	stats->tx_packets = s[OCELOT_STAT_TX_64] +
369 			    s[OCELOT_STAT_TX_65_127] +
370 			    s[OCELOT_STAT_TX_128_255] +
371 			    s[OCELOT_STAT_TX_256_511] +
372 			    s[OCELOT_STAT_TX_512_1023] +
373 			    s[OCELOT_STAT_TX_1024_1526] +
374 			    s[OCELOT_STAT_TX_1527_MAX];
375 	stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] +
376 			    s[OCELOT_STAT_TX_AGED];
377 	stats->collisions = s[OCELOT_STAT_TX_COLLISION];
378 
379 	spin_unlock(&ocelot->stats_lock);
380 }
381 EXPORT_SYMBOL(ocelot_port_get_stats64);
382 
ocelot_prepare_stats_regions(struct ocelot * ocelot)383 static int ocelot_prepare_stats_regions(struct ocelot *ocelot)
384 {
385 	struct ocelot_stats_region *region = NULL;
386 	unsigned int last;
387 	int i;
388 
389 	INIT_LIST_HEAD(&ocelot->stats_regions);
390 
391 	for (i = 0; i < OCELOT_NUM_STATS; i++) {
392 		if (!ocelot->stats_layout[i].reg)
393 			continue;
394 
395 		if (region && ocelot->map[SYS][ocelot->stats_layout[i].reg & REG_MASK] ==
396 		    ocelot->map[SYS][last & REG_MASK] + 4) {
397 			region->count++;
398 		} else {
399 			region = devm_kzalloc(ocelot->dev, sizeof(*region),
400 					      GFP_KERNEL);
401 			if (!region)
402 				return -ENOMEM;
403 
404 			region->base = ocelot->stats_layout[i].reg;
405 			region->count = 1;
406 			list_add_tail(&region->node, &ocelot->stats_regions);
407 		}
408 
409 		last = ocelot->stats_layout[i].reg;
410 	}
411 
412 	list_for_each_entry(region, &ocelot->stats_regions, node) {
413 		region->buf = devm_kcalloc(ocelot->dev, region->count,
414 					   sizeof(*region->buf), GFP_KERNEL);
415 		if (!region->buf)
416 			return -ENOMEM;
417 	}
418 
419 	return 0;
420 }
421 
ocelot_stats_init(struct ocelot * ocelot)422 int ocelot_stats_init(struct ocelot *ocelot)
423 {
424 	char queue_name[32];
425 	int ret;
426 
427 	ocelot->stats = devm_kcalloc(ocelot->dev,
428 				     ocelot->num_phys_ports * OCELOT_NUM_STATS,
429 				     sizeof(u64), GFP_KERNEL);
430 	if (!ocelot->stats)
431 		return -ENOMEM;
432 
433 	snprintf(queue_name, sizeof(queue_name), "%s-stats",
434 		 dev_name(ocelot->dev));
435 	ocelot->stats_queue = create_singlethread_workqueue(queue_name);
436 	if (!ocelot->stats_queue)
437 		return -ENOMEM;
438 
439 	spin_lock_init(&ocelot->stats_lock);
440 	mutex_init(&ocelot->stat_view_lock);
441 
442 	ret = ocelot_prepare_stats_regions(ocelot);
443 	if (ret) {
444 		destroy_workqueue(ocelot->stats_queue);
445 		return ret;
446 	}
447 
448 	INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work);
449 	queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
450 			   OCELOT_STATS_CHECK_DELAY);
451 
452 	return 0;
453 }
454 
ocelot_stats_deinit(struct ocelot * ocelot)455 void ocelot_stats_deinit(struct ocelot *ocelot)
456 {
457 	cancel_delayed_work(&ocelot->stats_work);
458 	destroy_workqueue(ocelot->stats_queue);
459 }
460