1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <kstat.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #include <signal.h>
37 #include <sys/dld.h>
38 #include <sys/ddi.h>
39
40 #include <libdllink.h>
41 #include <libdlflow.h>
42 #include <libdlstat.h>
43 #include <libdlaggr.h>
44
45 struct flowlist {
46 char flowname[MAXFLOWNAMELEN];
47 char linkname[MAXLINKNAMELEN];
48 datalink_id_t linkid;
49 int fd;
50 uint64_t ifspeed;
51 boolean_t first;
52 boolean_t display;
53 pktsum_t prevstats;
54 pktsum_t diffstats;
55 };
56
57 pktsum_t totalstats;
58 struct flowlist *stattable = NULL;
59
60 #define STATGROWSIZE 16
61
62 /* Exported functions */
63
64 /*
65 * dladm_kstat_lookup() is a modified version of kstat_lookup which
66 * adds the class as a selector.
67 */
68 kstat_t *
dladm_kstat_lookup(kstat_ctl_t * kcp,const char * module,int instance,const char * name,const char * class)69 dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance,
70 const char *name, const char *class)
71 {
72 kstat_t *ksp = NULL;
73
74 for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
75 if ((module == NULL || strcmp(ksp->ks_module, module) == 0) &&
76 (instance == -1 || ksp->ks_instance == instance) &&
77 (name == NULL || strcmp(ksp->ks_name, name) == 0) &&
78 (class == NULL || strcmp(ksp->ks_class, class) == 0))
79 return (ksp);
80 }
81
82 errno = ENOENT;
83 return (NULL);
84 }
85
86 /*
87 * dladm_get_stats() populates the supplied pktsum_t structure with
88 * the input and output packet and byte kstats from the kstat_t
89 * found with dladm_kstat_lookup.
90 */
91 void
dladm_get_stats(kstat_ctl_t * kcp,kstat_t * ksp,pktsum_t * stats)92 dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats)
93 {
94
95 if (kstat_read(kcp, ksp, NULL) == -1)
96 return;
97
98 stats->snaptime = gethrtime();
99
100 if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
101 &stats->ipackets) < 0) {
102 if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64,
103 &stats->ipackets) < 0)
104 return;
105 }
106
107 if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
108 &stats->opackets) < 0) {
109 if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64,
110 &stats->opackets) < 0)
111 return;
112 }
113
114 if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
115 &stats->rbytes) < 0) {
116 if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64,
117 &stats->rbytes) < 0)
118 return;
119 }
120
121 if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
122 &stats->obytes) < 0) {
123 if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64,
124 &stats->obytes) < 0)
125 return;
126 }
127
128 if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
129 &stats->ierrors) < 0) {
130 if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64,
131 &stats->ierrors) < 0)
132 return;
133 }
134
135 if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
136 &stats->oerrors) < 0) {
137 if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64,
138 &stats->oerrors) < 0)
139 return;
140 }
141 }
142
143 int
dladm_kstat_value(kstat_t * ksp,const char * name,uint8_t type,void * buf)144 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
145 {
146 kstat_named_t *knp;
147
148 if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
149 return (-1);
150
151 if (knp->data_type != type)
152 return (-1);
153
154 switch (type) {
155 case KSTAT_DATA_UINT64:
156 *(uint64_t *)buf = knp->value.ui64;
157 break;
158 case KSTAT_DATA_UINT32:
159 *(uint32_t *)buf = knp->value.ui32;
160 break;
161 default:
162 return (-1);
163 }
164
165 return (0);
166 }
167
168 dladm_status_t
dladm_get_single_mac_stat(dladm_handle_t handle,datalink_id_t linkid,const char * name,uint8_t type,void * val)169 dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid,
170 const char *name, uint8_t type, void *val)
171 {
172 kstat_ctl_t *kcp;
173 char module[DLPI_LINKNAME_MAX];
174 uint_t instance;
175 char link[DLPI_LINKNAME_MAX];
176 dladm_status_t status;
177 uint32_t flags, media;
178 kstat_t *ksp;
179 dladm_phys_attr_t dpap;
180
181 if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
182 &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
183 return (status);
184
185 if (media != DL_ETHER)
186 return (DLADM_STATUS_LINKINVAL);
187
188 status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST);
189
190 if (status != DLADM_STATUS_OK)
191 return (status);
192
193 status = dladm_parselink(dpap.dp_dev, module, &instance);
194
195 if (status != DLADM_STATUS_OK)
196 return (status);
197
198 if ((kcp = kstat_open()) == NULL) {
199 warn("kstat_open operation failed");
200 return (-1);
201 }
202
203 /*
204 * The kstat query could fail if the underlying MAC
205 * driver was already detached.
206 */
207 if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
208 (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL)
209 goto bail;
210
211 if (kstat_read(kcp, ksp, NULL) == -1)
212 goto bail;
213
214 if (dladm_kstat_value(ksp, name, type, val) < 0)
215 goto bail;
216
217 (void) kstat_close(kcp);
218 return (DLADM_STATUS_OK);
219
220 bail:
221 (void) kstat_close(kcp);
222 return (dladm_errno2status(errno));
223 }
224
225 /* Compute sum of 2 pktsums (s1 = s2 + s3) */
226 void
dladm_stats_total(pktsum_t * s1,pktsum_t * s2,pktsum_t * s3)227 dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
228 {
229 s1->rbytes = s2->rbytes + s3->rbytes;
230 s1->ipackets = s2->ipackets + s3->ipackets;
231 s1->ierrors = s2->ierrors + s3->ierrors;
232 s1->obytes = s2->obytes + s3->obytes;
233 s1->opackets = s2->opackets + s3->opackets;
234 s1->oerrors = s2->oerrors + s3->oerrors;
235 s1->snaptime = s2->snaptime;
236 }
237
238 #define DIFF_STAT(s2, s3) ((s2) > (s3) ? ((s2) - (s3)) : 0)
239
240
241 /* Compute differences between 2 pktsums (s1 = s2 - s3) */
242 void
dladm_stats_diff(pktsum_t * s1,pktsum_t * s2,pktsum_t * s3)243 dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
244 {
245 s1->rbytes = DIFF_STAT(s2->rbytes, s3->rbytes);
246 s1->ipackets = DIFF_STAT(s2->ipackets, s3->ipackets);
247 s1->ierrors = DIFF_STAT(s2->ierrors, s3->ierrors);
248 s1->obytes = DIFF_STAT(s2->obytes, s3->obytes);
249 s1->opackets = DIFF_STAT(s2->opackets, s3->opackets);
250 s1->oerrors = DIFF_STAT(s2->oerrors, s3->oerrors);
251 s1->snaptime = DIFF_STAT(s2->snaptime, s3->snaptime);
252 }
253
254 #define DLSTAT_MAC_RX_SWLANE "mac_rx_swlane"
255 #define DLSTAT_MAC_RX_HWLANE "mac_rx_hwlane"
256 #define DLSTAT_MAC_TX_SWLANE "mac_tx_swlane"
257 #define DLSTAT_MAC_TX_HWLANE "mac_tx_hwlane"
258 #define DLSTAT_MAC_MISC_STAT "mac_misc_stat"
259 #define DLSTAT_MAC_RX_RING "mac_rx_ring"
260 #define DLSTAT_MAC_TX_RING "mac_tx_ring"
261 #define DLSTAT_MAC_FANOUT "mac_rx_swlane0_fanout"
262
263 typedef struct {
264 const char *si_name;
265 uint_t si_offset;
266 } stat_info_t;
267
268 #define A_CNT(arr) (sizeof (arr) / sizeof (arr[0]))
269
270 /* Definitions for rx lane stats */
271 #define RL_OFF(f) (offsetof(rx_lane_stat_t, f))
272
273 static stat_info_t rx_hwlane_stats_list[] = {
274 {"ipackets", RL_OFF(rl_ipackets)},
275 {"rbytes", RL_OFF(rl_rbytes)},
276 {"intrs", RL_OFF(rl_intrs)},
277 {"intrbytes", RL_OFF(rl_intrbytes)},
278 {"polls", RL_OFF(rl_polls)},
279 {"pollbytes", RL_OFF(rl_pollbytes)},
280 {"rxsdrops", RL_OFF(rl_sdrops)},
281 {"chainunder10", RL_OFF(rl_chl10)},
282 {"chain10to50", RL_OFF(rl_ch10_50)},
283 {"chainover50", RL_OFF(rl_chg50)}
284 };
285 #define RX_HWLANE_STAT_SIZE A_CNT(rx_hwlane_stats_list)
286
287 static stat_info_t rx_swlane_stats_list[] = {
288 {"ipackets", RL_OFF(rl_ipackets)},
289 {"rbytes", RL_OFF(rl_rbytes)},
290 {"local", RL_OFF(rl_lclpackets)},
291 {"localbytes", RL_OFF(rl_lclbytes)},
292 {"intrs", RL_OFF(rl_intrs)},
293 {"intrbytes", RL_OFF(rl_intrbytes)},
294 {"rxsdrops", RL_OFF(rl_sdrops)}
295 };
296 #define RX_SWLANE_STAT_SIZE A_CNT(rx_swlane_stats_list)
297
298 static stat_info_t rx_lane_stats_list[] = {
299 {"ipackets", RL_OFF(rl_ipackets)},
300 {"rbytes", RL_OFF(rl_rbytes)},
301 {"local", RL_OFF(rl_lclpackets)},
302 {"localbytes", RL_OFF(rl_lclbytes)},
303 {"intrs", RL_OFF(rl_intrs)},
304 {"intrbytes", RL_OFF(rl_intrbytes)},
305 {"polls", RL_OFF(rl_polls)},
306 {"rxsdrops", RL_OFF(rl_sdrops)},
307 {"pollbytes", RL_OFF(rl_pollbytes)},
308 {"chainunder10", RL_OFF(rl_chl10)},
309 {"chain10to50", RL_OFF(rl_ch10_50)},
310 {"chainover50", RL_OFF(rl_chg50)}
311 };
312 #define RX_LANE_STAT_SIZE A_CNT(rx_lane_stats_list)
313
314 /* Definitions for tx lane stats */
315 #define TL_OFF(f) (offsetof(tx_lane_stat_t, f))
316
317 static stat_info_t tx_lane_stats_list[] = {
318 {"opackets", TL_OFF(tl_opackets)},
319 {"obytes", TL_OFF(tl_obytes)},
320 {"blockcnt", TL_OFF(tl_blockcnt)},
321 {"unblockcnt", TL_OFF(tl_unblockcnt)},
322 {"txsdrops", TL_OFF(tl_sdrops)}
323 };
324 #define TX_LANE_STAT_SIZE A_CNT(tx_lane_stats_list)
325
326 /* Definitions for tx/rx misc stats */
327 #define M_OFF(f) (offsetof(misc_stat_t, f))
328
329 static stat_info_t misc_stats_list[] = {
330 {"multircv", M_OFF(ms_multircv)},
331 {"brdcstrcv", M_OFF(ms_brdcstrcv)},
332 {"multixmt", M_OFF(ms_multixmt)},
333 {"brdcstxmt", M_OFF(ms_brdcstxmt)},
334 {"multircvbytes", M_OFF(ms_multircvbytes)},
335 {"brdcstrcvbytes", M_OFF(ms_brdcstrcvbytes)},
336 {"multixmtbytes", M_OFF(ms_multixmtbytes)},
337 {"brdcstxmtbytes", M_OFF(ms_brdcstxmtbytes)},
338 {"txerrors", M_OFF(ms_txerrors)},
339 {"macspoofed", M_OFF(ms_macspoofed)},
340 {"ipspoofed", M_OFF(ms_ipspoofed)},
341 {"dhcpspoofed", M_OFF(ms_dhcpspoofed)},
342 {"restricted", M_OFF(ms_restricted)},
343 {"ipackets", M_OFF(ms_ipackets)},
344 {"rbytes", M_OFF(ms_rbytes)},
345 {"local", M_OFF(ms_local)},
346 {"localbytes", M_OFF(ms_localbytes)},
347 {"intrs", M_OFF(ms_intrs)},
348 {"intrbytes", M_OFF(ms_intrbytes)},
349 {"polls", M_OFF(ms_polls)},
350 {"pollbytes", M_OFF(ms_pollbytes)},
351 {"rxsdrops", M_OFF(ms_rxsdrops)},
352 {"chainunder10", M_OFF(ms_chainunder10)},
353 {"chain10to50", M_OFF(ms_chain10to50)},
354 {"chainover50", M_OFF(ms_chainover50)},
355 {"obytes", M_OFF(ms_obytes)},
356 {"opackets", M_OFF(ms_opackets)},
357 {"blockcnt", M_OFF(ms_blockcnt)},
358 {"unblockcnt", M_OFF(ms_unblockcnt)},
359 {"txsdrops", M_OFF(ms_txsdrops)}
360 };
361 #define MISC_STAT_SIZE A_CNT(misc_stats_list)
362
363 /* Definitions for rx ring stats */
364 #define R_OFF(f) (offsetof(ring_stat_t, f))
365
366 static stat_info_t rx_ring_stats_list[] = {
367 {"ipackets", R_OFF(r_packets)},
368 {"rbytes", R_OFF(r_bytes)}
369 };
370 #define RX_RING_STAT_SIZE A_CNT(rx_ring_stats_list)
371
372 /* Definitions for tx ring stats */
373 static stat_info_t tx_ring_stats_list[] = {
374 {"opackets", R_OFF(r_packets)},
375 {"obytes", R_OFF(r_bytes)}
376 };
377 #define TX_RING_STAT_SIZE A_CNT(tx_ring_stats_list)
378
379 /* Definitions for fanout stats */
380 #define F_OFF(f) (offsetof(fanout_stat_t, f))
381
382 static stat_info_t fanout_stats_list[] = {
383 {"ipackets", F_OFF(f_ipackets)},
384 {"rbytes", F_OFF(f_rbytes)},
385 };
386 #define FANOUT_STAT_SIZE A_CNT(fanout_stats_list)
387
388 /* Definitions for total stats */
389 #define T_OFF(f) (offsetof(total_stat_t, f))
390
391 static stat_info_t total_stats_list[] = {
392 {"ipackets", T_OFF(ts_ipackets)},
393 {"rbytes", T_OFF(ts_rbytes)},
394 {"opackets", T_OFF(ts_opackets)},
395 {"obytes", T_OFF(ts_obytes)}
396 };
397 #define TOTAL_STAT_SIZE A_CNT(total_stats_list)
398
399 /* Definitions for aggr stats */
400 #define AP_OFF(f) (offsetof(aggr_port_stat_t, f))
401
402 static stat_info_t aggr_port_stats_list[] = {
403 {"ipackets64", AP_OFF(ap_ipackets)},
404 {"rbytes64", AP_OFF(ap_rbytes)},
405 {"opackets64", AP_OFF(ap_opackets)},
406 {"obytes64", AP_OFF(ap_obytes)}
407 };
408 #define AGGR_PORT_STAT_SIZE A_CNT(aggr_port_stats_list)
409
410 /* Definitions for flow stats */
411 #define FL_OFF(f) (offsetof(flow_stat_t, f))
412
413 static stat_info_t flow_stats_list[] = {
414 {"ipackets", FL_OFF(fl_ipackets)},
415 {"rbytes", FL_OFF(fl_rbytes)},
416 {"opackets", FL_OFF(fl_opackets)},
417 {"obytes", FL_OFF(fl_obytes)}
418 };
419 #define FLOW_STAT_SIZE A_CNT(flow_stats_list)
420
421 /* Rx lane specific functions */
422 void * dlstat_rx_lane_stats(dladm_handle_t, datalink_id_t);
423 static boolean_t i_dlstat_rx_lane_match(void *, void *);
424 static void * i_dlstat_rx_lane_stat_entry_diff(void *, void *);
425
426 /* Tx lane specific functions */
427 void * dlstat_tx_lane_stats(dladm_handle_t, datalink_id_t);
428 static boolean_t i_dlstat_tx_lane_match(void *, void *);
429 static void * i_dlstat_tx_lane_stat_entry_diff(void *, void *);
430
431 /* Rx lane total specific functions */
432 void * dlstat_rx_lane_total_stats(dladm_handle_t,
433 datalink_id_t);
434
435 /* Tx lane total specific functions */
436 void * dlstat_tx_lane_total_stats(dladm_handle_t,
437 datalink_id_t);
438
439 /* Fanout specific functions */
440 void * dlstat_fanout_stats(dladm_handle_t, datalink_id_t);
441 static boolean_t i_dlstat_fanout_match(void *, void *);
442 static void * i_dlstat_fanout_stat_entry_diff(void *, void *);
443
444 /* Rx ring specific functions */
445 void * dlstat_rx_ring_stats(dladm_handle_t, datalink_id_t);
446 static boolean_t i_dlstat_rx_ring_match(void *, void *);
447 static void * i_dlstat_rx_ring_stat_entry_diff(void *, void *);
448
449 /* Tx ring specific functions */
450 void * dlstat_tx_ring_stats(dladm_handle_t, datalink_id_t);
451 static boolean_t i_dlstat_tx_ring_match(void *, void *);
452 static void * i_dlstat_tx_ring_stat_entry_diff(void *, void *);
453
454 /* Rx ring total specific functions */
455 void * dlstat_rx_ring_total_stats(dladm_handle_t,
456 datalink_id_t);
457
458 /* Tx ring total specific functions */
459 void * dlstat_tx_ring_total_stats(dladm_handle_t,
460 datalink_id_t);
461
462 /* Summary specific functions */
463 void * dlstat_total_stats(dladm_handle_t, datalink_id_t);
464 static boolean_t i_dlstat_total_match(void *, void *);
465 static void * i_dlstat_total_stat_entry_diff(void *, void *);
466
467 /* Aggr port specific functions */
468 void * dlstat_aggr_port_stats(dladm_handle_t, datalink_id_t);
469 static boolean_t i_dlstat_aggr_port_match(void *, void *);
470 static void * i_dlstat_aggr_port_stat_entry_diff(void *, void *);
471
472 /* Misc stat specific functions */
473 void * dlstat_misc_stats(dladm_handle_t, datalink_id_t);
474
475 typedef void * dladm_stat_query_t(dladm_handle_t, datalink_id_t);
476 typedef boolean_t dladm_stat_match_t(void *, void *);
477 typedef void * dladm_stat_diff_t(void *, void *);
478
479 typedef struct dladm_stat_desc_s {
480 dladm_stat_type_t ds_stattype;
481 dladm_stat_query_t *ds_querystat;
482 dladm_stat_match_t *ds_matchstat;
483 dladm_stat_diff_t *ds_diffstat;
484 uint_t ds_offset;
485 stat_info_t *ds_statlist;
486 uint_t ds_statsize;
487 } dladm_stat_desc_t;
488
489 /*
490 * dladm_stat_table has one entry for each supported stat. ds_querystat returns
491 * a chain of 'stat entries' for the queried stat.
492 * Each stat entry has set of identifiers (ids) and an object containing actual
493 * stat values. These stat entry objects are chained together in a linked list
494 * of datatype dladm_stat_chain_t. Head of this list is returned to the caller
495 * of dladm_link_stat_query.
496 *
497 * One node in the chain is shown below:
498 *
499 * -------------------------
500 * | dc_statentry |
501 * | -------------- |
502 * | | ids | |
503 * | -------------- |
504 * | | stat fields | |
505 * | -------------- |
506 * -------------------------
507 * | dc_next ---------|------> to next stat entry
508 * -------------------------
509 *
510 * In particular, for query DLADM_STAT_RX_LANE, dc_statentry carries pointer to
511 * object of type rx_lane_stat_entry_t.
512 *
513 * dladm_link_stat_query_all returns similar chain. However, instead of storing
514 * stat fields as raw numbers, it stores those as chain of <name, value> pairs.
515 * The resulting structure is depicted below:
516 *
517 * -------------------------
518 * | dc_statentry |
519 * | -------------- | ---------------
520 * | | nv_header | | | name, val |
521 * | -------------- | ---------------
522 * | | nve_stats---|----|-->| nv_nextstat--|---> to next name, val pair
523 * | -------------- | ---------------
524 * -------------------------
525 * | dc_next ---------|------> to next stat entry
526 * -------------------------
527 */
528 static dladm_stat_desc_t dladm_stat_table[] = {
529 { DLADM_STAT_RX_LANE, dlstat_rx_lane_stats,
530 i_dlstat_rx_lane_match, i_dlstat_rx_lane_stat_entry_diff,
531 offsetof(rx_lane_stat_entry_t, rle_stats),
532 rx_lane_stats_list, RX_LANE_STAT_SIZE},
533
534 { DLADM_STAT_TX_LANE, dlstat_tx_lane_stats,
535 i_dlstat_tx_lane_match, i_dlstat_tx_lane_stat_entry_diff,
536 offsetof(tx_lane_stat_entry_t, tle_stats),
537 tx_lane_stats_list, TX_LANE_STAT_SIZE},
538
539 { DLADM_STAT_RX_LANE_TOTAL, dlstat_rx_lane_total_stats,
540 i_dlstat_rx_lane_match, i_dlstat_rx_lane_stat_entry_diff,
541 offsetof(rx_lane_stat_entry_t, rle_stats),
542 rx_lane_stats_list, RX_LANE_STAT_SIZE},
543
544 { DLADM_STAT_TX_LANE_TOTAL, dlstat_tx_lane_total_stats,
545 i_dlstat_tx_lane_match, i_dlstat_tx_lane_stat_entry_diff,
546 offsetof(tx_lane_stat_entry_t, tle_stats),
547 tx_lane_stats_list, TX_LANE_STAT_SIZE},
548
549 { DLADM_STAT_RX_LANE_FOUT, dlstat_fanout_stats,
550 i_dlstat_fanout_match, i_dlstat_fanout_stat_entry_diff,
551 offsetof(fanout_stat_entry_t, fe_stats),
552 fanout_stats_list, FANOUT_STAT_SIZE},
553
554 { DLADM_STAT_RX_RING, dlstat_rx_ring_stats,
555 i_dlstat_rx_ring_match, i_dlstat_rx_ring_stat_entry_diff,
556 offsetof(ring_stat_entry_t, re_stats),
557 rx_ring_stats_list, RX_RING_STAT_SIZE},
558
559 { DLADM_STAT_TX_RING, dlstat_tx_ring_stats,
560 i_dlstat_tx_ring_match, i_dlstat_tx_ring_stat_entry_diff,
561 offsetof(ring_stat_entry_t, re_stats),
562 tx_ring_stats_list, TX_RING_STAT_SIZE},
563
564 { DLADM_STAT_RX_RING_TOTAL, dlstat_rx_ring_total_stats,
565 i_dlstat_rx_ring_match, i_dlstat_rx_ring_stat_entry_diff,
566 offsetof(ring_stat_entry_t, re_stats),
567 rx_ring_stats_list, RX_RING_STAT_SIZE},
568
569 { DLADM_STAT_TX_RING_TOTAL, dlstat_tx_ring_total_stats,
570 i_dlstat_tx_ring_match, i_dlstat_tx_ring_stat_entry_diff,
571 offsetof(ring_stat_entry_t, re_stats),
572 tx_ring_stats_list, TX_RING_STAT_SIZE},
573
574 { DLADM_STAT_TOTAL, dlstat_total_stats,
575 i_dlstat_total_match, i_dlstat_total_stat_entry_diff,
576 offsetof(total_stat_entry_t, tse_stats),
577 total_stats_list, TOTAL_STAT_SIZE},
578
579 { DLADM_STAT_AGGR_PORT, dlstat_aggr_port_stats,
580 i_dlstat_aggr_port_match, i_dlstat_aggr_port_stat_entry_diff,
581 offsetof(aggr_port_stat_entry_t, ape_stats),
582 aggr_port_stats_list, AGGR_PORT_STAT_SIZE},
583 /*
584 * We don't support -i <interval> query with misc stats. Several table fields
585 * are left uninitialized thus.
586 */
587 { DLADM_STAT_MISC, dlstat_misc_stats,
588 NULL, NULL,
589 0,
590 misc_stats_list, MISC_STAT_SIZE}
591 };
592
593 /* Internal functions */
594 static void *
dlstat_diff_stats(void * arg1,void * arg2,dladm_stat_type_t stattype)595 dlstat_diff_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
596 {
597 return (dladm_stat_table[stattype].ds_diffstat(arg1, arg2));
598 }
599
600 static boolean_t
dlstat_match_stats(void * arg1,void * arg2,dladm_stat_type_t stattype)601 dlstat_match_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
602 {
603 return (dladm_stat_table[stattype].ds_matchstat(arg1, arg2));
604 }
605
606 /* Diff between two stats */
607 static void
i_dlstat_diff_stats(void * diff,void * op1,void * op2,stat_info_t stats_list[],uint_t size)608 i_dlstat_diff_stats(void *diff, void *op1, void *op2,
609 stat_info_t stats_list[], uint_t size)
610 {
611 int i;
612
613 for (i = 0; i < size; i++) {
614 uint64_t *op1_val = (void *)
615 ((uchar_t *)op1 + stats_list[i].si_offset);
616 uint64_t *op2_val = (void *)
617 ((uchar_t *)op2 + stats_list[i].si_offset);
618 uint64_t *diff_val = (void *)
619 ((uchar_t *)diff + stats_list[i].si_offset);
620
621 *diff_val = DIFF_STAT(*op1_val, *op2_val);
622 }
623 }
624
625 /*
626 * Perform diff = s1 - s2, where diff, s1, s2 are structure objects of same
627 * datatype. slist is list of offsets of the fields within the structure.
628 */
629 #define DLSTAT_DIFF_STAT(s1, s2, diff, f, slist, sz) { \
630 if (s2 == NULL) { \
631 bcopy(&s1->f, &diff->f, sizeof (s1->f)); \
632 } else { \
633 i_dlstat_diff_stats(&diff->f, &s1->f, \
634 &s2->f, slist, sz); \
635 } \
636 }
637
638 /* Sum two stats */
639 static void
i_dlstat_sum_stats(void * sum,void * op1,void * op2,stat_info_t stats_list[],uint_t size)640 i_dlstat_sum_stats(void *sum, void *op1, void *op2,
641 stat_info_t stats_list[], uint_t size)
642 {
643 int i;
644
645 for (i = 0; i < size; i++) {
646 uint64_t *op1_val = (void *)
647 ((uchar_t *)op1 + stats_list[i].si_offset);
648 uint64_t *op2_val = (void *)
649 ((uchar_t *)op2 + stats_list[i].si_offset);
650 uint64_t *sum_val = (void *)
651 ((uchar_t *)sum + stats_list[i].si_offset);
652
653 *sum_val = *op1_val + *op2_val;
654 }
655 }
656
657 /* Look up kstat value */
658 static void
i_dlstat_get_stats(kstat_ctl_t * kcp,kstat_t * ksp,void * stats,stat_info_t stats_list[],uint_t size)659 i_dlstat_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, void *stats,
660 stat_info_t stats_list[], uint_t size)
661 {
662 int i;
663
664 if (kstat_read(kcp, ksp, NULL) == -1)
665 return;
666
667 for (i = 0; i < size; i++) {
668 uint64_t *val = (void *)
669 ((uchar_t *)stats + stats_list[i].si_offset);
670
671 if (dladm_kstat_value(ksp, stats_list[i].si_name,
672 KSTAT_DATA_UINT64, val) < 0)
673 return;
674 }
675 }
676
677 /* Append linked list list1 to linked list list2 and return resulting list */
678 static dladm_stat_chain_t *
i_dlstat_join_lists(dladm_stat_chain_t * list1,dladm_stat_chain_t * list2)679 i_dlstat_join_lists(dladm_stat_chain_t *list1, dladm_stat_chain_t *list2)
680 {
681 dladm_stat_chain_t *curr;
682
683 if (list1 == NULL)
684 return (list2);
685
686 /* list1 has at least one element, find last element in list1 */
687 curr = list1;
688 while (curr->dc_next != NULL)
689 curr = curr->dc_next;
690
691 curr->dc_next = list2;
692 return (list1);
693 }
694
695 uint_t default_idlist[] = {0};
696 uint_t default_idlist_size = 1;
697
698 typedef enum {
699 DLSTAT_RX_RING_IDLIST,
700 DLSTAT_TX_RING_IDLIST,
701 DLSTAT_RX_HWLANE_IDLIST,
702 DLSTAT_TX_HWLANE_IDLIST,
703 DLSTAT_FANOUT_IDLIST
704 } dlstat_idlist_type_t;
705
706 void
dladm_sort_index_list(uint_t idlist[],uint_t size)707 dladm_sort_index_list(uint_t idlist[], uint_t size)
708 {
709 int i, j;
710
711 for (j = 1; j < size; j++) {
712 int key = idlist[j];
713 for (i = j - 1; (i >= 0) && (idlist[i] > key); i--)
714 idlist[i + 1] = idlist[i];
715 idlist[i + 1] = key;
716 }
717 }
718
719 /* Support for legacy drivers */
720 void
i_query_legacy_stats(const char * linkname,pktsum_t * stats)721 i_query_legacy_stats(const char *linkname, pktsum_t *stats)
722 {
723 kstat_ctl_t *kcp;
724 kstat_t *ksp;
725
726 bzero(stats, sizeof (*stats));
727
728 if ((kcp = kstat_open()) == NULL)
729 return;
730
731 ksp = dladm_kstat_lookup(kcp, "link", 0, linkname, NULL);
732
733 if (ksp != NULL)
734 dladm_get_stats(kcp, ksp, stats);
735
736 (void) kstat_close(kcp);
737 }
738
739 void *
i_dlstat_legacy_rx_lane_stats(const char * linkname)740 i_dlstat_legacy_rx_lane_stats(const char *linkname)
741 {
742 dladm_stat_chain_t *head = NULL;
743 pktsum_t stats;
744 rx_lane_stat_entry_t *rx_lane_stat_entry;
745
746 bzero(&stats, sizeof (pktsum_t));
747
748 /* Query for dls stats */
749 i_query_legacy_stats(linkname, &stats);
750
751 /* Convert to desired data type */
752 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
753 if (rx_lane_stat_entry == NULL)
754 goto done;
755
756 rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
757 rx_lane_stat_entry->rle_id = L_SWLANE;
758
759 rx_lane_stat_entry->rle_stats.rl_ipackets = stats.ipackets;
760 rx_lane_stat_entry->rle_stats.rl_intrs = stats.ipackets;
761 rx_lane_stat_entry->rle_stats.rl_rbytes = stats.rbytes;
762
763 /* Allocate memory for wrapper */
764 head = malloc(sizeof (dladm_stat_chain_t));
765 if (head == NULL) {
766 free(rx_lane_stat_entry);
767 goto done;
768 }
769
770 head->dc_statentry = rx_lane_stat_entry;
771 head->dc_next = NULL;
772 done:
773 return (head);
774 }
775
776 void *
i_dlstat_legacy_tx_lane_stats(const char * linkname)777 i_dlstat_legacy_tx_lane_stats(const char *linkname)
778 {
779 dladm_stat_chain_t *head = NULL;
780 pktsum_t stats;
781 tx_lane_stat_entry_t *tx_lane_stat_entry;
782
783 bzero(&stats, sizeof (pktsum_t));
784
785 /* Query for dls stats */
786 i_query_legacy_stats(linkname, &stats);
787
788 /* Convert to desired data type */
789 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
790 if (tx_lane_stat_entry == NULL)
791 goto done;
792
793 tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
794 tx_lane_stat_entry->tle_id = L_SWLANE;
795
796 tx_lane_stat_entry->tle_stats.tl_opackets = stats.opackets;
797 tx_lane_stat_entry->tle_stats.tl_obytes = stats.obytes;
798
799 /* Allocate memory for wrapper */
800 head = malloc(sizeof (dladm_stat_chain_t));
801 if (head == NULL) {
802 free(tx_lane_stat_entry);
803 goto done;
804 }
805
806 head->dc_statentry = tx_lane_stat_entry;
807 head->dc_next = NULL;
808 done:
809 return (head);
810 }
811
812 /*
813 * Ideally, we would want an ioctl to return list of ring-ids (or lane-ids)
814 * for a given data-link (or mac client). We could then query for specific
815 * kstats based on these ring-ids (lane-ids).
816 * Ring-ids (or lane-ids) could be returned like any other link properties
817 * queried by dladm show-linkprop. However, non-global zones do not have
818 * access to this information today.
819 * We thus opt for an implementation that relies heavily on kstat internals:
820 * i_dlstat_*search routines and i_dlstat_get_idlist.
821 */
822 /* rx hwlane specific */
823 static boolean_t
i_dlstat_rx_hwlane_search(kstat_t * ksp)824 i_dlstat_rx_hwlane_search(kstat_t *ksp)
825 {
826 return (ksp->ks_instance == 0 &&
827 strstr(ksp->ks_name, "mac_rx") != 0 &&
828 strstr(ksp->ks_name, "hwlane") != 0 &&
829 strstr(ksp->ks_name, "fanout") == 0 &&
830 strcmp(ksp->ks_class, "net") == 0);
831 }
832
833 /* tx hwlane specific */
834 static boolean_t
i_dlstat_tx_hwlane_search(kstat_t * ksp)835 i_dlstat_tx_hwlane_search(kstat_t *ksp)
836 {
837 return (ksp->ks_instance == 0 &&
838 strstr(ksp->ks_name, "mac_tx") != 0 &&
839 strstr(ksp->ks_name, "hwlane") != 0 &&
840 strcmp(ksp->ks_class, "net") == 0);
841 }
842
843 /* rx fanout specific */
844 static boolean_t
i_dlstat_fanout_search(kstat_t * ksp)845 i_dlstat_fanout_search(kstat_t *ksp)
846 {
847 return (ksp->ks_instance == 0 &&
848 strstr(ksp->ks_name, "mac_rx") != 0 &&
849 strstr(ksp->ks_name, "swlane") != 0 &&
850 strstr(ksp->ks_name, "fanout") != 0 &&
851 strcmp(ksp->ks_class, "net") == 0);
852 }
853
854 /* rx ring specific */
855 static boolean_t
i_dlstat_rx_ring_search(kstat_t * ksp)856 i_dlstat_rx_ring_search(kstat_t *ksp)
857 {
858 return (ksp->ks_instance == 0 &&
859 strstr(ksp->ks_name, "mac_rx") != 0 &&
860 strstr(ksp->ks_name, "ring") != 0 &&
861 strcmp(ksp->ks_class, "net") == 0);
862 }
863
864 /* tx ring specific */
865 static boolean_t
i_dlstat_tx_ring_search(kstat_t * ksp)866 i_dlstat_tx_ring_search(kstat_t *ksp)
867 {
868 return (ksp->ks_instance == 0) &&
869 strstr(ksp->ks_name, "mac_tx") != 0 &&
870 strstr(ksp->ks_name, "ring") != 0 &&
871 strcmp(ksp->ks_class, "net") == 0;
872 }
873
874 typedef boolean_t dladm_search_kstat_t(kstat_t *);
875 typedef struct dladm_extract_idlist_s {
876 dlstat_idlist_type_t di_type;
877 char *di_prefix;
878 dladm_search_kstat_t *di_searchkstat;
879 } dladm_extract_idlist_t;
880
881 static dladm_extract_idlist_t dladm_extract_idlist[] = {
882 { DLSTAT_RX_RING_IDLIST, DLSTAT_MAC_RX_RING,
883 i_dlstat_rx_ring_search},
884 { DLSTAT_TX_RING_IDLIST, DLSTAT_MAC_TX_RING,
885 i_dlstat_tx_ring_search},
886 { DLSTAT_RX_HWLANE_IDLIST, DLSTAT_MAC_RX_HWLANE,
887 i_dlstat_rx_hwlane_search},
888 { DLSTAT_TX_HWLANE_IDLIST, DLSTAT_MAC_TX_HWLANE,
889 i_dlstat_tx_hwlane_search},
890 { DLSTAT_FANOUT_IDLIST, DLSTAT_MAC_FANOUT,
891 i_dlstat_fanout_search}
892 };
893
894 static void
i_dlstat_get_idlist(const char * modname,dlstat_idlist_type_t idlist_type,uint_t idlist[],uint_t * size)895 i_dlstat_get_idlist(const char *modname, dlstat_idlist_type_t idlist_type,
896 uint_t idlist[], uint_t *size)
897 {
898 kstat_ctl_t *kcp;
899 kstat_t *ksp;
900 char *prefix;
901 int prefixlen;
902 boolean_t (*fptr_searchkstat)(kstat_t *);
903
904 *size = 0;
905
906 if ((kcp = kstat_open()) == NULL) {
907 warn("kstat_open operation failed");
908 goto done;
909 }
910
911 prefix = dladm_extract_idlist[idlist_type].di_prefix;
912 fptr_searchkstat = dladm_extract_idlist[idlist_type].di_searchkstat;
913 prefixlen = strlen(prefix);
914 for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
915 if ((strcmp(ksp->ks_module, modname) == 0) &&
916 fptr_searchkstat(ksp)) {
917 idlist[(*size)++] = atoi(&ksp->ks_name[prefixlen]);
918 }
919 }
920 dladm_sort_index_list(idlist, *size);
921
922 done:
923 (void) kstat_close(kcp);
924 }
925
926 static dladm_stat_chain_t *
i_dlstat_query_stats(const char * modname,const char * prefix,uint_t idlist[],uint_t idlist_size,void * (* fn)(kstat_ctl_t *,kstat_t *,int))927 i_dlstat_query_stats(const char *modname, const char *prefix,
928 uint_t idlist[], uint_t idlist_size,
929 void * (*fn)(kstat_ctl_t *, kstat_t *, int))
930 {
931 kstat_ctl_t *kcp;
932 kstat_t *ksp;
933 char statname[MAXLINKNAMELEN];
934 int i = 0;
935 dladm_stat_chain_t *head = NULL, *prev = NULL;
936 dladm_stat_chain_t *curr;
937
938 if ((kcp = kstat_open()) == NULL) {
939 warn("kstat_open operation failed");
940 return (NULL);
941 }
942
943 for (i = 0; i < idlist_size; i++) {
944 uint_t index = idlist[i];
945
946 (void) snprintf(statname, sizeof (statname), "%s%d", prefix,
947 index);
948
949 ksp = dladm_kstat_lookup(kcp, modname, 0, statname, NULL);
950 if (ksp == NULL)
951 continue;
952
953 curr = malloc(sizeof (dladm_stat_chain_t));
954 if (curr == NULL)
955 break;
956
957 curr->dc_statentry = fn(kcp, ksp, index);
958 if (curr->dc_statentry == NULL) {
959 free(curr);
960 break;
961 }
962
963 (void) strlcpy(curr->dc_statheader, statname,
964 sizeof (curr->dc_statheader));
965 curr->dc_next = NULL;
966
967 if (head == NULL) /* First node */
968 head = curr;
969 else
970 prev->dc_next = curr;
971
972 prev = curr;
973 }
974 done:
975 (void) kstat_close(kcp);
976 return (head);
977 }
978
979 static misc_stat_entry_t *
i_dlstat_misc_stats(const char * linkname)980 i_dlstat_misc_stats(const char *linkname)
981 {
982 kstat_ctl_t *kcp;
983 kstat_t *ksp;
984 misc_stat_entry_t *misc_stat_entry = NULL;
985
986 if ((kcp = kstat_open()) == NULL)
987 return (NULL);
988
989 ksp = dladm_kstat_lookup(kcp, linkname, 0, DLSTAT_MAC_MISC_STAT, NULL);
990 if (ksp == NULL)
991 goto done;
992
993 misc_stat_entry = calloc(1, sizeof (misc_stat_entry_t));
994 if (misc_stat_entry == NULL)
995 goto done;
996
997 i_dlstat_get_stats(kcp, ksp, &misc_stat_entry->mse_stats,
998 misc_stats_list, MISC_STAT_SIZE);
999 done:
1000 (void) kstat_close(kcp);
1001 return (misc_stat_entry);
1002 }
1003
1004 /* Rx lane statistic specific functions */
1005 static boolean_t
i_dlstat_rx_lane_match(void * arg1,void * arg2)1006 i_dlstat_rx_lane_match(void *arg1, void *arg2)
1007 {
1008 rx_lane_stat_entry_t *s1 = arg1;
1009 rx_lane_stat_entry_t *s2 = arg2;
1010
1011 return (s1->rle_index == s2->rle_index &&
1012 s1->rle_id == s2->rle_id);
1013 }
1014
1015 static void *
i_dlstat_rx_lane_stat_entry_diff(void * arg1,void * arg2)1016 i_dlstat_rx_lane_stat_entry_diff(void *arg1, void *arg2)
1017 {
1018 rx_lane_stat_entry_t *s1 = arg1;
1019 rx_lane_stat_entry_t *s2 = arg2;
1020 rx_lane_stat_entry_t *diff_entry;
1021
1022 diff_entry = malloc(sizeof (rx_lane_stat_entry_t));
1023 if (diff_entry == NULL)
1024 goto done;
1025
1026 diff_entry->rle_index = s1->rle_index;
1027 diff_entry->rle_id = s1->rle_id;
1028
1029 DLSTAT_DIFF_STAT(s1, s2, diff_entry, rle_stats, rx_lane_stats_list,
1030 RX_LANE_STAT_SIZE);
1031
1032 done:
1033 return (diff_entry);
1034 }
1035
1036 static void *
i_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)1037 i_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1038 {
1039 rx_lane_stat_entry_t *rx_lane_stat_entry;
1040
1041 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1042 if (rx_lane_stat_entry == NULL)
1043 goto done;
1044
1045 rx_lane_stat_entry->rle_index = i;
1046 rx_lane_stat_entry->rle_id = L_HWLANE;
1047
1048 i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1049 rx_hwlane_stats_list, RX_HWLANE_STAT_SIZE);
1050
1051 done:
1052 return (rx_lane_stat_entry);
1053 }
1054
1055 /*ARGSUSED*/
1056 static void *
i_dlstat_rx_swlane_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)1057 i_dlstat_rx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1058 {
1059 rx_lane_stat_entry_t *rx_lane_stat_entry;
1060
1061 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1062 if (rx_lane_stat_entry == NULL)
1063 goto done;
1064
1065 rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1066 rx_lane_stat_entry->rle_id = L_SWLANE;
1067
1068 i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1069 rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
1070
1071 rx_lane_stat_entry->rle_stats.rl_ipackets =
1072 rx_lane_stat_entry->rle_stats.rl_intrs;
1073 rx_lane_stat_entry->rle_stats.rl_rbytes =
1074 rx_lane_stat_entry->rle_stats.rl_intrbytes;
1075 done:
1076 return (rx_lane_stat_entry);
1077 }
1078
1079 /*ARGSUSED*/
1080 static void *
i_dlstat_rx_local_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)1081 i_dlstat_rx_local_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1082 {
1083 rx_lane_stat_entry_t *local_stat_entry;
1084 rx_lane_stat_entry_t *rx_lane_stat_entry;
1085
1086 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1087 if (rx_lane_stat_entry == NULL)
1088 goto done;
1089
1090 local_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1091 if (local_stat_entry == NULL)
1092 goto done;
1093
1094 local_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1095 local_stat_entry->rle_id = L_LOCAL;
1096
1097 i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1098 rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
1099
1100 local_stat_entry->rle_stats.rl_ipackets =
1101 rx_lane_stat_entry->rle_stats.rl_lclpackets;
1102 local_stat_entry->rle_stats.rl_rbytes =
1103 rx_lane_stat_entry->rle_stats.rl_lclbytes;
1104
1105 done:
1106 free(rx_lane_stat_entry);
1107 return (local_stat_entry);
1108 }
1109
1110 static dladm_stat_chain_t *
i_dlstat_rx_local_stats(const char * linkname)1111 i_dlstat_rx_local_stats(const char *linkname)
1112 {
1113 dladm_stat_chain_t *local_stats = NULL;
1114
1115 local_stats = i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_SWLANE,
1116 default_idlist, default_idlist_size,
1117 i_dlstat_rx_local_retrieve_stat);
1118
1119 if (local_stats != NULL) {
1120 (void) strlcpy(local_stats->dc_statheader, "mac_rx_local",
1121 sizeof (local_stats->dc_statheader));
1122 }
1123 return (local_stats);
1124 }
1125
1126 static dladm_stat_chain_t *
i_dlstat_rx_bcast_stats(const char * linkname)1127 i_dlstat_rx_bcast_stats(const char *linkname)
1128 {
1129 misc_stat_entry_t *misc_stat_entry;
1130 dladm_stat_chain_t *head = NULL;
1131 rx_lane_stat_entry_t *rx_lane_stat_entry;
1132
1133 misc_stat_entry = i_dlstat_misc_stats(linkname);
1134 if (misc_stat_entry == NULL)
1135 goto done;
1136
1137 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1138 if (rx_lane_stat_entry == NULL)
1139 goto done;
1140
1141 rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1142 rx_lane_stat_entry->rle_id = L_BCAST;
1143
1144 rx_lane_stat_entry->rle_stats.rl_ipackets =
1145 misc_stat_entry->mse_stats.ms_brdcstrcv +
1146 misc_stat_entry->mse_stats.ms_multircv;
1147 rx_lane_stat_entry->rle_stats.rl_intrs =
1148 misc_stat_entry->mse_stats.ms_brdcstrcv +
1149 misc_stat_entry->mse_stats.ms_multircv;
1150 rx_lane_stat_entry->rle_stats.rl_rbytes =
1151 misc_stat_entry->mse_stats.ms_brdcstrcvbytes +
1152 misc_stat_entry->mse_stats.ms_multircvbytes;
1153
1154 head = malloc(sizeof (dladm_stat_chain_t));
1155 if (head == NULL) {
1156 free(rx_lane_stat_entry);
1157 goto done;
1158 }
1159
1160 head->dc_statentry = rx_lane_stat_entry;
1161 head->dc_next = NULL;
1162
1163 free(misc_stat_entry);
1164 done:
1165 return (head);
1166 }
1167
1168 static dladm_stat_chain_t *
i_dlstat_rx_defunctlane_stats(const char * linkname)1169 i_dlstat_rx_defunctlane_stats(const char *linkname)
1170 {
1171 misc_stat_entry_t *misc_stat_entry;
1172 dladm_stat_chain_t *head = NULL;
1173 rx_lane_stat_entry_t *rx_lane_stat_entry;
1174
1175 misc_stat_entry = i_dlstat_misc_stats(linkname);
1176 if (misc_stat_entry == NULL)
1177 goto done;
1178
1179 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1180 if (rx_lane_stat_entry == NULL)
1181 goto done;
1182
1183 rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1184 rx_lane_stat_entry->rle_id = L_DFNCT;
1185
1186 rx_lane_stat_entry->rle_stats.rl_ipackets =
1187 misc_stat_entry->mse_stats.ms_ipackets;
1188 rx_lane_stat_entry->rle_stats.rl_rbytes =
1189 misc_stat_entry->mse_stats.ms_rbytes;
1190 rx_lane_stat_entry->rle_stats.rl_intrs =
1191 misc_stat_entry->mse_stats.ms_intrs;
1192 rx_lane_stat_entry->rle_stats.rl_polls =
1193 misc_stat_entry->mse_stats.ms_polls;
1194 rx_lane_stat_entry->rle_stats.rl_sdrops =
1195 misc_stat_entry->mse_stats.ms_rxsdrops;
1196 rx_lane_stat_entry->rle_stats.rl_chl10 =
1197 misc_stat_entry->mse_stats.ms_chainunder10;
1198 rx_lane_stat_entry->rle_stats.rl_ch10_50 =
1199 misc_stat_entry->mse_stats.ms_chain10to50;
1200 rx_lane_stat_entry->rle_stats.rl_chg50 =
1201 misc_stat_entry->mse_stats.ms_chainover50;
1202
1203 head = malloc(sizeof (dladm_stat_chain_t));
1204 if (head == NULL) {
1205 free(rx_lane_stat_entry);
1206 goto done;
1207 }
1208
1209 head->dc_statentry = rx_lane_stat_entry;
1210 head->dc_next = NULL;
1211
1212 done:
1213 return (head);
1214 }
1215
1216 static dladm_stat_chain_t *
i_dlstat_rx_hwlane_stats(const char * linkname)1217 i_dlstat_rx_hwlane_stats(const char *linkname)
1218 {
1219 uint_t rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1220 uint_t rx_hwlane_idlist_size;
1221
1222 i_dlstat_get_idlist(linkname, DLSTAT_RX_HWLANE_IDLIST,
1223 rx_hwlane_idlist, &rx_hwlane_idlist_size);
1224
1225 return (i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_HWLANE,
1226 rx_hwlane_idlist, rx_hwlane_idlist_size,
1227 i_dlstat_rx_hwlane_retrieve_stat));
1228 }
1229
1230 /*ARGSUSED*/
1231 static dladm_stat_chain_t *
i_dlstat_rx_swlane_stats(dladm_handle_t dh,datalink_id_t linkid,const char * linkname)1232 i_dlstat_rx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1233 const char *linkname)
1234 {
1235 return (i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_SWLANE,
1236 default_idlist, default_idlist_size,
1237 i_dlstat_rx_swlane_retrieve_stat));
1238 }
1239
1240 void *
dlstat_rx_lane_stats(dladm_handle_t dh,datalink_id_t linkid)1241 dlstat_rx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
1242 {
1243 dladm_stat_chain_t *head = NULL;
1244 dladm_stat_chain_t *local_stats = NULL;
1245 dladm_stat_chain_t *bcast_stats = NULL;
1246 dladm_stat_chain_t *defunctlane_stats = NULL;
1247 dladm_stat_chain_t *lane_stats = NULL;
1248 char linkname[MAXLINKNAMELEN];
1249 boolean_t is_legacy_driver;
1250
1251 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1252 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1253 goto done;
1254 }
1255
1256 /* Check if it is legacy driver */
1257 if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
1258 "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
1259 goto done;
1260 }
1261
1262 if (is_legacy_driver) {
1263 head = i_dlstat_legacy_rx_lane_stats(linkname);
1264 goto done;
1265 }
1266
1267 local_stats = i_dlstat_rx_local_stats(linkname);
1268 bcast_stats = i_dlstat_rx_bcast_stats(linkname);
1269 defunctlane_stats = i_dlstat_rx_defunctlane_stats(linkname);
1270 lane_stats = i_dlstat_rx_hwlane_stats(linkname);
1271 if (lane_stats == NULL)
1272 lane_stats = i_dlstat_rx_swlane_stats(dh, linkid, linkname);
1273
1274 head = i_dlstat_join_lists(local_stats, bcast_stats);
1275 head = i_dlstat_join_lists(head, defunctlane_stats);
1276 head = i_dlstat_join_lists(head, lane_stats);
1277 done:
1278 return (head);
1279 }
1280
1281 /* Tx lane statistic specific functions */
1282 static boolean_t
i_dlstat_tx_lane_match(void * arg1,void * arg2)1283 i_dlstat_tx_lane_match(void *arg1, void *arg2)
1284 {
1285 tx_lane_stat_entry_t *s1 = arg1;
1286 tx_lane_stat_entry_t *s2 = arg2;
1287
1288 return (s1->tle_index == s2->tle_index &&
1289 s1->tle_id == s2->tle_id);
1290 }
1291
1292 static void *
i_dlstat_tx_lane_stat_entry_diff(void * arg1,void * arg2)1293 i_dlstat_tx_lane_stat_entry_diff(void *arg1, void *arg2)
1294 {
1295 tx_lane_stat_entry_t *s1 = arg1;
1296 tx_lane_stat_entry_t *s2 = arg2;
1297 tx_lane_stat_entry_t *diff_entry;
1298
1299 diff_entry = malloc(sizeof (tx_lane_stat_entry_t));
1300 if (diff_entry == NULL)
1301 goto done;
1302
1303 diff_entry->tle_index = s1->tle_index;
1304 diff_entry->tle_id = s1->tle_id;
1305
1306 DLSTAT_DIFF_STAT(s1, s2, diff_entry, tle_stats, tx_lane_stats_list,
1307 TX_LANE_STAT_SIZE);
1308
1309 done:
1310 return (diff_entry);
1311 }
1312
1313 static void *
i_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)1314 i_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1315 {
1316 tx_lane_stat_entry_t *tx_lane_stat_entry;
1317
1318 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1319 if (tx_lane_stat_entry == NULL)
1320 goto done;
1321
1322 tx_lane_stat_entry->tle_index = i;
1323 tx_lane_stat_entry->tle_id = L_HWLANE;
1324
1325 i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
1326 tx_lane_stats_list, TX_LANE_STAT_SIZE);
1327
1328 done:
1329 return (tx_lane_stat_entry);
1330 }
1331
1332 /*ARGSUSED*/
1333 static void *
i_dlstat_tx_swlane_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)1334 i_dlstat_tx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1335 {
1336 tx_lane_stat_entry_t *tx_lane_stat_entry;
1337
1338 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1339 if (tx_lane_stat_entry == NULL)
1340 goto done;
1341
1342 tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1343 tx_lane_stat_entry->tle_id = L_SWLANE;
1344
1345 i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
1346 tx_lane_stats_list, TX_LANE_STAT_SIZE);
1347
1348 done:
1349 return (tx_lane_stat_entry);
1350 }
1351
1352 static dladm_stat_chain_t *
i_dlstat_tx_bcast_stats(const char * linkname)1353 i_dlstat_tx_bcast_stats(const char *linkname)
1354 {
1355 misc_stat_entry_t *misc_stat_entry;
1356 dladm_stat_chain_t *head = NULL;
1357 tx_lane_stat_entry_t *tx_lane_stat_entry;
1358
1359 misc_stat_entry = i_dlstat_misc_stats(linkname);
1360 if (misc_stat_entry == NULL)
1361 goto done;
1362
1363 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1364 if (tx_lane_stat_entry == NULL)
1365 goto done;
1366
1367 tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1368 tx_lane_stat_entry->tle_id = L_BCAST;
1369
1370 tx_lane_stat_entry->tle_stats.tl_opackets =
1371 misc_stat_entry->mse_stats.ms_brdcstxmt +
1372 misc_stat_entry->mse_stats.ms_multixmt;
1373
1374 tx_lane_stat_entry->tle_stats.tl_obytes =
1375 misc_stat_entry->mse_stats.ms_brdcstxmtbytes +
1376 misc_stat_entry->mse_stats.ms_multixmtbytes;
1377
1378 head = malloc(sizeof (dladm_stat_chain_t));
1379 if (head == NULL) {
1380 free(tx_lane_stat_entry);
1381 goto done;
1382 }
1383
1384 head->dc_statentry = tx_lane_stat_entry;
1385 head->dc_next = NULL;
1386
1387 free(misc_stat_entry);
1388 done:
1389 return (head);
1390 }
1391
1392 static dladm_stat_chain_t *
i_dlstat_tx_defunctlane_stats(const char * linkname)1393 i_dlstat_tx_defunctlane_stats(const char *linkname)
1394 {
1395 misc_stat_entry_t *misc_stat_entry;
1396 dladm_stat_chain_t *head = NULL;
1397 tx_lane_stat_entry_t *tx_lane_stat_entry;
1398
1399 misc_stat_entry = i_dlstat_misc_stats(linkname);
1400 if (misc_stat_entry == NULL)
1401 goto done;
1402
1403 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1404 if (tx_lane_stat_entry == NULL)
1405 goto done;
1406
1407 tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1408 tx_lane_stat_entry->tle_id = L_DFNCT;
1409
1410 tx_lane_stat_entry->tle_stats.tl_opackets =
1411 misc_stat_entry->mse_stats.ms_opackets;
1412 tx_lane_stat_entry->tle_stats.tl_obytes =
1413 misc_stat_entry->mse_stats.ms_obytes;
1414 tx_lane_stat_entry->tle_stats.tl_sdrops =
1415 misc_stat_entry->mse_stats.ms_txsdrops;
1416
1417 head = malloc(sizeof (dladm_stat_chain_t));
1418 if (head == NULL) {
1419 free(tx_lane_stat_entry);
1420 goto done;
1421 }
1422
1423 head->dc_statentry = tx_lane_stat_entry;
1424 head->dc_next = NULL;
1425
1426 done:
1427 return (head);
1428 }
1429
1430 static dladm_stat_chain_t *
i_dlstat_tx_hwlane_stats(const char * linkname)1431 i_dlstat_tx_hwlane_stats(const char *linkname)
1432 {
1433 uint_t tx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1434 uint_t tx_hwlane_idlist_size;
1435
1436 i_dlstat_get_idlist(linkname, DLSTAT_TX_HWLANE_IDLIST,
1437 tx_hwlane_idlist, &tx_hwlane_idlist_size);
1438
1439 return (i_dlstat_query_stats(linkname, DLSTAT_MAC_TX_HWLANE,
1440 tx_hwlane_idlist, tx_hwlane_idlist_size,
1441 i_dlstat_tx_hwlane_retrieve_stat));
1442 }
1443
1444 /*ARGSUSED*/
1445 static dladm_stat_chain_t *
i_dlstat_tx_swlane_stats(dladm_handle_t dh,datalink_id_t linkid,const char * linkname)1446 i_dlstat_tx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1447 const char *linkname)
1448 {
1449 return (i_dlstat_query_stats(linkname, DLSTAT_MAC_TX_SWLANE,
1450 default_idlist, default_idlist_size,
1451 i_dlstat_tx_swlane_retrieve_stat));
1452 }
1453
1454 void *
dlstat_tx_lane_stats(dladm_handle_t dh,datalink_id_t linkid)1455 dlstat_tx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
1456 {
1457 dladm_stat_chain_t *head = NULL;
1458 dladm_stat_chain_t *bcast_stats = NULL;
1459 dladm_stat_chain_t *defunctlane_stats = NULL;
1460 dladm_stat_chain_t *lane_stats;
1461 char linkname[MAXLINKNAMELEN];
1462 boolean_t is_legacy_driver;
1463
1464 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1465 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1466 goto done;
1467 }
1468
1469 /* Check if it is legacy driver */
1470 if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
1471 "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
1472 goto done;
1473 }
1474
1475 if (is_legacy_driver) {
1476 head = i_dlstat_legacy_tx_lane_stats(linkname);
1477 goto done;
1478 }
1479
1480 bcast_stats = i_dlstat_tx_bcast_stats(linkname);
1481 defunctlane_stats = i_dlstat_tx_defunctlane_stats(linkname);
1482 lane_stats = i_dlstat_tx_hwlane_stats(linkname);
1483 if (lane_stats == NULL)
1484 lane_stats = i_dlstat_tx_swlane_stats(dh, linkid, linkname);
1485
1486 head = i_dlstat_join_lists(bcast_stats, defunctlane_stats);
1487 head = i_dlstat_join_lists(head, lane_stats);
1488
1489 done:
1490 return (head);
1491 }
1492
1493 /* Rx lane total statistic specific functions */
1494 void *
dlstat_rx_lane_total_stats(dladm_handle_t dh,datalink_id_t linkid)1495 dlstat_rx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1496 {
1497 dladm_stat_chain_t *total_head = NULL;
1498 dladm_stat_chain_t *rx_lane_head, *curr;
1499 rx_lane_stat_entry_t *total_stats;
1500
1501 /* Get per rx lane stats */
1502 rx_lane_head = dlstat_rx_lane_stats(dh, linkid);
1503 if (rx_lane_head == NULL)
1504 goto done;
1505
1506 total_stats = calloc(1, sizeof (rx_lane_stat_entry_t));
1507 if (total_stats == NULL)
1508 goto done;
1509
1510 total_stats->rle_index = DLSTAT_INVALID_ENTRY;
1511 total_stats->rle_id = DLSTAT_INVALID_ENTRY;
1512
1513 for (curr = rx_lane_head; curr != NULL; curr = curr->dc_next) {
1514 rx_lane_stat_entry_t *curr_lane_stats = curr->dc_statentry;
1515
1516 i_dlstat_sum_stats(&total_stats->rle_stats,
1517 &curr_lane_stats->rle_stats, &total_stats->rle_stats,
1518 rx_lane_stats_list, RX_LANE_STAT_SIZE);
1519 }
1520
1521 total_head = malloc(sizeof (dladm_stat_chain_t));
1522 if (total_head == NULL) {
1523 free(total_stats);
1524 goto done;
1525 }
1526
1527 total_head->dc_statentry = total_stats;
1528 (void) strlcpy(total_head->dc_statheader, "mac_rx_lane_total",
1529 sizeof (total_head->dc_statheader));
1530 total_head->dc_next = NULL;
1531 free(rx_lane_head);
1532
1533 done:
1534 return (total_head);
1535 }
1536
1537 /* Tx lane total statistic specific functions */
1538 void *
dlstat_tx_lane_total_stats(dladm_handle_t dh,datalink_id_t linkid)1539 dlstat_tx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1540 {
1541 dladm_stat_chain_t *total_head = NULL;
1542 dladm_stat_chain_t *tx_lane_head, *curr;
1543 tx_lane_stat_entry_t *total_stats;
1544
1545 /* Get per tx lane stats */
1546 tx_lane_head = dlstat_tx_lane_stats(dh, linkid);
1547 if (tx_lane_head == NULL)
1548 goto done;
1549
1550 total_stats = calloc(1, sizeof (tx_lane_stat_entry_t));
1551 if (total_stats == NULL)
1552 goto done;
1553
1554 total_stats->tle_index = DLSTAT_INVALID_ENTRY;
1555 total_stats->tle_id = DLSTAT_INVALID_ENTRY;
1556
1557 for (curr = tx_lane_head; curr != NULL; curr = curr->dc_next) {
1558 tx_lane_stat_entry_t *curr_lane_stats = curr->dc_statentry;
1559
1560 i_dlstat_sum_stats(&total_stats->tle_stats,
1561 &curr_lane_stats->tle_stats, &total_stats->tle_stats,
1562 tx_lane_stats_list, TX_LANE_STAT_SIZE);
1563 }
1564
1565 total_head = malloc(sizeof (dladm_stat_chain_t));
1566 if (total_head == NULL) {
1567 free(total_stats);
1568 goto done;
1569 }
1570
1571 total_head->dc_statentry = total_stats;
1572 (void) strlcpy(total_head->dc_statheader, "mac_tx_lane_total",
1573 sizeof (total_head->dc_statheader));
1574 total_head->dc_next = NULL;
1575 free(tx_lane_head);
1576
1577 done:
1578 return (total_head);
1579 }
1580
1581 /* Fanout specific functions */
1582 static boolean_t
i_dlstat_fanout_match(void * arg1,void * arg2)1583 i_dlstat_fanout_match(void *arg1, void *arg2)
1584 {
1585 fanout_stat_entry_t *s1 = arg1;
1586 fanout_stat_entry_t *s2 = arg2;
1587
1588 return (s1->fe_index == s2->fe_index &&
1589 s1->fe_id == s2->fe_id &&
1590 s1->fe_foutindex == s2->fe_foutindex);
1591 }
1592
1593 static void *
i_dlstat_fanout_stat_entry_diff(void * arg1,void * arg2)1594 i_dlstat_fanout_stat_entry_diff(void *arg1, void *arg2)
1595 {
1596 fanout_stat_entry_t *s1 = arg1;
1597 fanout_stat_entry_t *s2 = arg2;
1598 fanout_stat_entry_t *diff_entry;
1599
1600 diff_entry = malloc(sizeof (fanout_stat_entry_t));
1601 if (diff_entry == NULL)
1602 goto done;
1603
1604 diff_entry->fe_index = s1->fe_index;
1605 diff_entry->fe_id = s1->fe_id;
1606 diff_entry->fe_foutindex = s1->fe_foutindex;
1607
1608 DLSTAT_DIFF_STAT(s1, s2, diff_entry, fe_stats, fanout_stats_list,
1609 FANOUT_STAT_SIZE);
1610
1611 done:
1612 return (diff_entry);
1613 }
1614
1615 static void *
i_dlstat_fanout_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)1616 i_dlstat_fanout_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1617 {
1618 fanout_stat_entry_t *fanout_stat_entry;
1619
1620 fanout_stat_entry = calloc(1, sizeof (fanout_stat_entry_t));
1621 if (fanout_stat_entry == NULL)
1622 goto done;
1623
1624 /* Set by the caller later */
1625 fanout_stat_entry->fe_index = DLSTAT_INVALID_ENTRY;
1626 fanout_stat_entry->fe_id = DLSTAT_INVALID_ENTRY;
1627
1628 fanout_stat_entry->fe_foutindex = i;
1629
1630 i_dlstat_get_stats(kcp, ksp, &fanout_stat_entry->fe_stats,
1631 fanout_stats_list, FANOUT_STAT_SIZE);
1632
1633 done:
1634 return (fanout_stat_entry);
1635 }
1636
1637 static void *
i_dlstat_query_fanout_stats(dladm_handle_t dh,datalink_id_t linkid,uint_t idlist[],uint_t idlist_size,const char * modname,const char * prefix)1638 i_dlstat_query_fanout_stats(dladm_handle_t dh, datalink_id_t linkid,
1639 uint_t idlist[], uint_t idlist_size,
1640 const char *modname, const char *prefix)
1641 {
1642 int i;
1643 char statprefix[MAXLINKNAMELEN];
1644 char linkname[MAXLINKNAMELEN];
1645 dladm_stat_chain_t *curr, *curr_head;
1646 dladm_stat_chain_t *head = NULL, *prev = NULL;
1647 uint_t fanout_idlist[MAX_RINGS_PER_GROUP];
1648 uint_t fanout_idlist_size;
1649
1650 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1651 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1652 return (NULL);
1653 }
1654
1655 i_dlstat_get_idlist(linkname, DLSTAT_FANOUT_IDLIST,
1656 fanout_idlist, &fanout_idlist_size);
1657
1658 for (i = 0; i < idlist_size; i++) {
1659 uint_t index = idlist[i];
1660
1661 (void) snprintf(statprefix, sizeof (statprefix), "%s%d_fanout",
1662 prefix, index);
1663
1664 curr_head = i_dlstat_query_stats(modname, statprefix,
1665 fanout_idlist, fanout_idlist_size,
1666 i_dlstat_fanout_retrieve_stat);
1667
1668 if (curr_head == NULL) /* Last lane */
1669 break;
1670
1671 if (head == NULL) /* First lane */
1672 head = curr_head;
1673 else /* Link new lane list to end of previous lane list */
1674 prev->dc_next = curr_head;
1675
1676 /* Walk new lane list and set ids */
1677 for (curr = curr_head; curr != NULL; curr = curr->dc_next) {
1678 fanout_stat_entry_t *curr_stats = curr->dc_statentry;
1679
1680 curr_stats->fe_index = index;
1681 curr_stats->fe_id = L_HWLANE;
1682 /*
1683 * Save last pointer of previous linked list.
1684 * This pointer is used to chain linked lists
1685 * generated in each iteration.
1686 */
1687 prev = curr;
1688 }
1689 }
1690
1691 return (head);
1692 }
1693
1694 void *
dlstat_fanout_swlane_and_local_stats(dladm_handle_t dh,datalink_id_t linkid,const char * linkname)1695 dlstat_fanout_swlane_and_local_stats(dladm_handle_t dh, datalink_id_t linkid,
1696 const char *linkname)
1697 {
1698 return (i_dlstat_query_fanout_stats(dh, linkid,
1699 default_idlist, default_idlist_size, linkname,
1700 DLSTAT_MAC_RX_SWLANE));
1701 }
1702
1703 void *
dlstat_fanout_hwlane_stats(dladm_handle_t dh,datalink_id_t linkid,const char * linkname)1704 dlstat_fanout_hwlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1705 const char *linkname)
1706 {
1707 uint_t rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1708 uint_t rx_hwlane_idlist_size;
1709
1710 i_dlstat_get_idlist(linkname, DLSTAT_RX_HWLANE_IDLIST,
1711 rx_hwlane_idlist, &rx_hwlane_idlist_size);
1712
1713 return (i_dlstat_query_fanout_stats(dh, linkid, rx_hwlane_idlist,
1714 rx_hwlane_idlist_size, linkname, DLSTAT_MAC_RX_HWLANE));
1715 }
1716
1717 void *
dlstat_fanout_stats(dladm_handle_t dh,datalink_id_t linkid)1718 dlstat_fanout_stats(dladm_handle_t dh, datalink_id_t linkid)
1719 {
1720 dladm_stat_chain_t *head = NULL;
1721 dladm_stat_chain_t *fout_hwlane_stats;
1722 dladm_stat_chain_t *fout_swlane_and_local_stats;
1723 fanout_stat_entry_t *fout_stats;
1724 char linkname[MAXLINKNAMELEN];
1725
1726 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1727 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1728 goto done;
1729 }
1730
1731 fout_swlane_and_local_stats =
1732 dlstat_fanout_swlane_and_local_stats(dh, linkid, linkname);
1733 fout_hwlane_stats = dlstat_fanout_hwlane_stats(dh, linkid, linkname);
1734
1735 if (fout_swlane_and_local_stats == NULL) {
1736 head = fout_hwlane_stats;
1737 goto done;
1738 }
1739
1740 fout_stats = fout_swlane_and_local_stats->dc_statentry;
1741
1742 if (fout_hwlane_stats != NULL) { /* hwlane(s), only local traffic */
1743 fout_stats->fe_id = L_LOCAL;
1744 fout_stats->fe_index = DLSTAT_INVALID_ENTRY;
1745 } else { /* no hwlane, mix of local+sw classified */
1746 fout_stats->fe_id = L_LCLSWLANE;
1747 fout_stats->fe_index = DLSTAT_INVALID_ENTRY;
1748 }
1749
1750 fout_swlane_and_local_stats->dc_next = fout_hwlane_stats;
1751 head = fout_swlane_and_local_stats;
1752
1753 done:
1754 return (head);
1755 }
1756
1757 /* Rx ring statistic specific functions */
1758 static boolean_t
i_dlstat_rx_ring_match(void * arg1,void * arg2)1759 i_dlstat_rx_ring_match(void *arg1, void *arg2)
1760 {
1761 rx_lane_stat_entry_t *s1 = arg1;
1762 rx_lane_stat_entry_t *s2 = arg2;
1763
1764 return (s1->rle_index == s2->rle_index);
1765 }
1766
1767 static void *
i_dlstat_rx_ring_stat_entry_diff(void * arg1,void * arg2)1768 i_dlstat_rx_ring_stat_entry_diff(void *arg1, void *arg2)
1769 {
1770 ring_stat_entry_t *s1 = arg1;
1771 ring_stat_entry_t *s2 = arg2;
1772 ring_stat_entry_t *diff_entry;
1773
1774 diff_entry = malloc(sizeof (ring_stat_entry_t));
1775 if (diff_entry == NULL)
1776 goto done;
1777
1778 diff_entry->re_index = s1->re_index;
1779
1780 DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, rx_ring_stats_list,
1781 RX_RING_STAT_SIZE);
1782
1783 done:
1784 return (diff_entry);
1785 }
1786
1787 static void *
i_dlstat_rx_ring_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)1788 i_dlstat_rx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1789 {
1790 ring_stat_entry_t *rx_ring_stat_entry;
1791
1792 rx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
1793 if (rx_ring_stat_entry == NULL)
1794 goto done;
1795
1796 rx_ring_stat_entry->re_index = i;
1797
1798 i_dlstat_get_stats(kcp, ksp, &rx_ring_stat_entry->re_stats,
1799 rx_ring_stats_list, RX_RING_STAT_SIZE);
1800
1801 done:
1802 return (rx_ring_stat_entry);
1803 }
1804
1805 void *
dlstat_rx_ring_stats(dladm_handle_t dh,datalink_id_t linkid)1806 dlstat_rx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
1807 {
1808 uint_t rx_ring_idlist[MAX_RINGS_PER_GROUP];
1809 uint_t rx_ring_idlist_size;
1810 dladm_phys_attr_t dpa;
1811 char linkname[MAXLINKNAMELEN];
1812 char *modname;
1813 datalink_class_t class;
1814
1815 /*
1816 * kstats corresponding to physical device rings continue to use
1817 * device names even if the link is renamed using dladm rename-link.
1818 * Thus, given a linkid, we lookup the physical device name.
1819 * However, if an aggr is renamed, kstats corresponding to its
1820 * pseudo rings are renamed as well.
1821 */
1822 if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
1823 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1824 return (NULL);
1825 }
1826
1827 if (class != DATALINK_CLASS_AGGR) {
1828 if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
1829 DLADM_STATUS_OK) {
1830 return (NULL);
1831 }
1832 modname = dpa.dp_dev;
1833 } else
1834 modname = linkname;
1835
1836 i_dlstat_get_idlist(modname, DLSTAT_RX_RING_IDLIST,
1837 rx_ring_idlist, &rx_ring_idlist_size);
1838
1839 return (i_dlstat_query_stats(modname, DLSTAT_MAC_RX_RING,
1840 rx_ring_idlist, rx_ring_idlist_size,
1841 i_dlstat_rx_ring_retrieve_stat));
1842 }
1843
1844 /* Tx ring statistic specific functions */
1845 static boolean_t
i_dlstat_tx_ring_match(void * arg1,void * arg2)1846 i_dlstat_tx_ring_match(void *arg1, void *arg2)
1847 {
1848 tx_lane_stat_entry_t *s1 = arg1;
1849 tx_lane_stat_entry_t *s2 = arg2;
1850
1851 return (s1->tle_index == s2->tle_index);
1852 }
1853
1854 static void *
i_dlstat_tx_ring_stat_entry_diff(void * arg1,void * arg2)1855 i_dlstat_tx_ring_stat_entry_diff(void *arg1, void *arg2)
1856 {
1857 ring_stat_entry_t *s1 = arg1;
1858 ring_stat_entry_t *s2 = arg2;
1859 ring_stat_entry_t *diff_entry;
1860
1861 diff_entry = malloc(sizeof (ring_stat_entry_t));
1862 if (diff_entry == NULL)
1863 goto done;
1864
1865 diff_entry->re_index = s1->re_index;
1866
1867 DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, tx_ring_stats_list,
1868 TX_RING_STAT_SIZE);
1869
1870 done:
1871 return (diff_entry);
1872 }
1873
1874 static void *
i_dlstat_tx_ring_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)1875 i_dlstat_tx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1876 {
1877 ring_stat_entry_t *tx_ring_stat_entry;
1878
1879 tx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
1880 if (tx_ring_stat_entry == NULL)
1881 goto done;
1882
1883 tx_ring_stat_entry->re_index = i;
1884
1885 i_dlstat_get_stats(kcp, ksp, &tx_ring_stat_entry->re_stats,
1886 tx_ring_stats_list, TX_RING_STAT_SIZE);
1887
1888 done:
1889 return (tx_ring_stat_entry);
1890 }
1891
1892 void *
dlstat_tx_ring_stats(dladm_handle_t dh,datalink_id_t linkid)1893 dlstat_tx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
1894 {
1895 uint_t tx_ring_idlist[MAX_RINGS_PER_GROUP];
1896 uint_t tx_ring_idlist_size;
1897 dladm_phys_attr_t dpa;
1898 char linkname[MAXLINKNAMELEN];
1899 char *modname;
1900 datalink_class_t class;
1901
1902 /*
1903 * kstats corresponding to physical device rings continue to use
1904 * device names even if the link is renamed using dladm rename-link.
1905 * Thus, given a linkid, we lookup the physical device name.
1906 * However, if an aggr is renamed, kstats corresponding to its
1907 * pseudo rings are renamed as well.
1908 */
1909 if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
1910 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1911 return (NULL);
1912 }
1913
1914 if (class != DATALINK_CLASS_AGGR) {
1915 if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
1916 DLADM_STATUS_OK) {
1917 return (NULL);
1918 }
1919 modname = dpa.dp_dev;
1920 } else
1921 modname = linkname;
1922
1923 i_dlstat_get_idlist(modname, DLSTAT_TX_RING_IDLIST,
1924 tx_ring_idlist, &tx_ring_idlist_size);
1925
1926 return (i_dlstat_query_stats(modname, DLSTAT_MAC_TX_RING,
1927 tx_ring_idlist, tx_ring_idlist_size,
1928 i_dlstat_tx_ring_retrieve_stat));
1929 }
1930
1931 /* Rx ring total statistic specific functions */
1932 void *
dlstat_rx_ring_total_stats(dladm_handle_t dh,datalink_id_t linkid)1933 dlstat_rx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1934 {
1935 dladm_stat_chain_t *total_head = NULL;
1936 dladm_stat_chain_t *rx_ring_head, *curr;
1937 ring_stat_entry_t *total_stats;
1938
1939 /* Get per rx ring stats */
1940 rx_ring_head = dlstat_rx_ring_stats(dh, linkid);
1941 if (rx_ring_head == NULL)
1942 goto done;
1943
1944 total_stats = calloc(1, sizeof (ring_stat_entry_t));
1945 if (total_stats == NULL)
1946 goto done;
1947
1948 total_stats->re_index = DLSTAT_INVALID_ENTRY;
1949
1950 for (curr = rx_ring_head; curr != NULL; curr = curr->dc_next) {
1951 ring_stat_entry_t *curr_ring_stats = curr->dc_statentry;
1952
1953 i_dlstat_sum_stats(&total_stats->re_stats,
1954 &curr_ring_stats->re_stats, &total_stats->re_stats,
1955 rx_ring_stats_list, RX_RING_STAT_SIZE);
1956 }
1957
1958 total_head = malloc(sizeof (dladm_stat_chain_t));
1959 if (total_head == NULL) {
1960 free(total_stats);
1961 goto done;
1962 }
1963
1964 total_head->dc_statentry = total_stats;
1965 (void) strlcpy(total_head->dc_statheader, "mac_rx_ring_total",
1966 sizeof (total_head->dc_statheader));
1967 total_head->dc_next = NULL;
1968 free(rx_ring_head);
1969
1970 done:
1971 return (total_head);
1972 }
1973
1974 /* Tx ring total statistic specific functions */
1975 void *
dlstat_tx_ring_total_stats(dladm_handle_t dh,datalink_id_t linkid)1976 dlstat_tx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1977 {
1978 dladm_stat_chain_t *total_head = NULL;
1979 dladm_stat_chain_t *tx_ring_head, *curr;
1980 ring_stat_entry_t *total_stats;
1981
1982 /* Get per tx ring stats */
1983 tx_ring_head = dlstat_tx_ring_stats(dh, linkid);
1984 if (tx_ring_head == NULL)
1985 goto done;
1986
1987 total_stats = calloc(1, sizeof (ring_stat_entry_t));
1988 if (total_stats == NULL)
1989 goto done;
1990
1991 total_stats->re_index = DLSTAT_INVALID_ENTRY;
1992
1993 for (curr = tx_ring_head; curr != NULL; curr = curr->dc_next) {
1994 ring_stat_entry_t *curr_ring_stats = curr->dc_statentry;
1995
1996 i_dlstat_sum_stats(&total_stats->re_stats,
1997 &curr_ring_stats->re_stats, &total_stats->re_stats,
1998 tx_ring_stats_list, TX_RING_STAT_SIZE);
1999 }
2000
2001 total_head = malloc(sizeof (dladm_stat_chain_t));
2002 if (total_head == NULL) {
2003 free(total_stats);
2004 goto done;
2005 }
2006
2007 total_head->dc_statentry = total_stats;
2008 (void) strlcpy(total_head->dc_statheader, "mac_tx_ring_total",
2009 sizeof (total_head->dc_statheader));
2010 total_head->dc_next = NULL;
2011 free(tx_ring_head);
2012
2013 done:
2014 return (total_head);
2015 }
2016
2017 /* Summary statistic specific functions */
2018 /*ARGSUSED*/
2019 static boolean_t
i_dlstat_total_match(void * arg1,void * arg2)2020 i_dlstat_total_match(void *arg1, void *arg2)
2021 {
2022 /* Always single entry for total */
2023 return (B_TRUE);
2024 }
2025
2026 static void *
i_dlstat_total_stat_entry_diff(void * arg1,void * arg2)2027 i_dlstat_total_stat_entry_diff(void *arg1, void *arg2)
2028 {
2029 total_stat_entry_t *s1 = arg1;
2030 total_stat_entry_t *s2 = arg2;
2031 total_stat_entry_t *diff_entry;
2032
2033 diff_entry = malloc(sizeof (total_stat_entry_t));
2034 if (diff_entry == NULL)
2035 goto done;
2036
2037 DLSTAT_DIFF_STAT(s1, s2, diff_entry, tse_stats, total_stats_list,
2038 TOTAL_STAT_SIZE);
2039
2040 done:
2041 return (diff_entry);
2042 }
2043
2044 void *
dlstat_total_stats(dladm_handle_t dh,datalink_id_t linkid)2045 dlstat_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2046 {
2047 dladm_stat_chain_t *head = NULL;
2048 dladm_stat_chain_t *rx_total;
2049 dladm_stat_chain_t *tx_total;
2050 total_stat_entry_t *total_stat_entry;
2051 rx_lane_stat_entry_t *rx_lane_stat_entry;
2052 tx_lane_stat_entry_t *tx_lane_stat_entry;
2053
2054 /* Get total rx lane stats */
2055 rx_total = dlstat_rx_lane_total_stats(dh, linkid);
2056 if (rx_total == NULL)
2057 goto done;
2058
2059 /* Get total tx lane stats */
2060 tx_total = dlstat_tx_lane_total_stats(dh, linkid);
2061 if (tx_total == NULL)
2062 goto done;
2063
2064 /* Build total stat */
2065 total_stat_entry = calloc(1, sizeof (total_stat_entry_t));
2066 if (total_stat_entry == NULL)
2067 goto done;
2068
2069 rx_lane_stat_entry = rx_total->dc_statentry;
2070 tx_lane_stat_entry = tx_total->dc_statentry;
2071
2072 /* Extract total rx ipackets, rbytes */
2073 total_stat_entry->tse_stats.ts_ipackets =
2074 rx_lane_stat_entry->rle_stats.rl_ipackets;
2075 total_stat_entry->tse_stats.ts_rbytes =
2076 rx_lane_stat_entry->rle_stats.rl_rbytes;
2077
2078 /* Extract total tx opackets, obytes */
2079 total_stat_entry->tse_stats.ts_opackets =
2080 tx_lane_stat_entry->tle_stats.tl_opackets;
2081 total_stat_entry->tse_stats.ts_obytes =
2082 tx_lane_stat_entry->tle_stats.tl_obytes;
2083
2084 head = malloc(sizeof (dladm_stat_chain_t));
2085 if (head == NULL) {
2086 free(total_stat_entry);
2087 goto done;
2088 }
2089
2090 head->dc_statentry = total_stat_entry;
2091 (void) strlcpy(head->dc_statheader, "mac_lane_total",
2092 sizeof (head->dc_statheader));
2093 head->dc_next = NULL;
2094 free(rx_total);
2095 free(tx_total);
2096
2097 done:
2098 return (head);
2099 }
2100
2101 /* Aggr total statistic(summed across all component ports) specific functions */
2102 void *
dlstat_aggr_total_stats(dladm_stat_chain_t * head)2103 dlstat_aggr_total_stats(dladm_stat_chain_t *head)
2104 {
2105 dladm_stat_chain_t *curr;
2106 dladm_stat_chain_t *total_head;
2107 aggr_port_stat_entry_t *total_stats;
2108
2109 total_stats = calloc(1, sizeof (aggr_port_stat_entry_t));
2110 if (total_stats == NULL)
2111 goto done;
2112
2113 total_stats->ape_portlinkid = DATALINK_INVALID_LINKID;
2114
2115 for (curr = head; curr != NULL; curr = curr->dc_next) {
2116 aggr_port_stat_entry_t *curr_aggr_port_stats;
2117
2118 curr_aggr_port_stats = curr->dc_statentry;
2119
2120 i_dlstat_sum_stats(&total_stats->ape_stats,
2121 &curr_aggr_port_stats->ape_stats, &total_stats->ape_stats,
2122 aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
2123 }
2124
2125 total_head = malloc(sizeof (dladm_stat_chain_t));
2126 if (total_head == NULL) {
2127 free(total_stats);
2128 goto done;
2129 }
2130
2131 total_head->dc_statentry = total_stats;
2132 total_head->dc_next = NULL;
2133
2134 done:
2135 return (total_head);
2136 }
2137
2138 /* Aggr port statistic specific functions */
2139 static boolean_t
i_dlstat_aggr_port_match(void * arg1,void * arg2)2140 i_dlstat_aggr_port_match(void *arg1, void *arg2)
2141 {
2142 aggr_port_stat_entry_t *s1 = arg1;
2143 aggr_port_stat_entry_t *s2 = arg2;
2144
2145 return (s1->ape_portlinkid == s2->ape_portlinkid);
2146 }
2147
2148 static void *
i_dlstat_aggr_port_stat_entry_diff(void * arg1,void * arg2)2149 i_dlstat_aggr_port_stat_entry_diff(void *arg1, void *arg2)
2150 {
2151 aggr_port_stat_entry_t *s1 = arg1;
2152 aggr_port_stat_entry_t *s2 = arg2;
2153 aggr_port_stat_entry_t *diff_entry;
2154
2155 diff_entry = malloc(sizeof (aggr_port_stat_entry_t));
2156 if (diff_entry == NULL)
2157 goto done;
2158
2159 diff_entry->ape_portlinkid = s1->ape_portlinkid;
2160
2161 DLSTAT_DIFF_STAT(s1, s2, diff_entry, ape_stats, aggr_port_stats_list,
2162 AGGR_PORT_STAT_SIZE);
2163
2164 done:
2165 return (diff_entry);
2166 }
2167
2168 /*
2169 * Query dls stats for the aggr port. This results in query for stats into
2170 * the corresponding device driver.
2171 */
2172 static aggr_port_stat_entry_t *
i_dlstat_single_port_stats(const char * portname,datalink_id_t linkid)2173 i_dlstat_single_port_stats(const char *portname, datalink_id_t linkid)
2174 {
2175 kstat_ctl_t *kcp;
2176 kstat_t *ksp;
2177 char module[DLPI_LINKNAME_MAX];
2178 uint_t instance;
2179 aggr_port_stat_entry_t *aggr_port_stat_entry = NULL;
2180
2181 if (dladm_parselink(portname, module, &instance) != DLADM_STATUS_OK)
2182 goto done;
2183
2184 if ((kcp = kstat_open()) == NULL) {
2185 warn("kstat open operation failed");
2186 return (NULL);
2187 }
2188
2189 ksp = dladm_kstat_lookup(kcp, module, instance, "mac", NULL);
2190 if (ksp == NULL)
2191 goto done;
2192
2193 aggr_port_stat_entry = calloc(1, sizeof (aggr_port_stat_entry_t));
2194 if (aggr_port_stat_entry == NULL)
2195 goto done;
2196
2197 /* Save port's linkid */
2198 aggr_port_stat_entry->ape_portlinkid = linkid;
2199
2200 i_dlstat_get_stats(kcp, ksp, &aggr_port_stat_entry->ape_stats,
2201 aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
2202 done:
2203 (void) kstat_close(kcp);
2204 return (aggr_port_stat_entry);
2205 }
2206
2207 void *
dlstat_aggr_port_stats(dladm_handle_t dh,datalink_id_t linkid)2208 dlstat_aggr_port_stats(dladm_handle_t dh, datalink_id_t linkid)
2209 {
2210 dladm_aggr_grp_attr_t ginfo;
2211 int i;
2212 dladm_aggr_port_attr_t *portp;
2213 dladm_phys_attr_t dpa;
2214 aggr_port_stat_entry_t *aggr_port_stat_entry;
2215 dladm_stat_chain_t *head = NULL, *prev = NULL, *curr;
2216 dladm_stat_chain_t *total_stats;
2217
2218 /* Get aggr info */
2219 bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t));
2220 if (dladm_aggr_info(dh, linkid, &ginfo, DLADM_OPT_ACTIVE)
2221 != DLADM_STATUS_OK)
2222 goto done;
2223 /* For every port that is member of this aggr do */
2224 for (i = 0; i < ginfo.lg_nports; i++) {
2225 portp = &(ginfo.lg_ports[i]);
2226 if (dladm_phys_info(dh, portp->lp_linkid, &dpa,
2227 DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) {
2228 goto done;
2229 }
2230
2231 aggr_port_stat_entry = i_dlstat_single_port_stats(dpa.dp_dev,
2232 portp->lp_linkid);
2233
2234 /* Create dladm_stat_chain_t object for this stat */
2235 curr = malloc(sizeof (dladm_stat_chain_t));
2236 if (curr == NULL) {
2237 free(aggr_port_stat_entry);
2238 goto done;
2239 }
2240 (void) strlcpy(curr->dc_statheader, dpa.dp_dev,
2241 sizeof (curr->dc_statheader));
2242 curr->dc_statentry = aggr_port_stat_entry;
2243 curr->dc_next = NULL;
2244
2245 /* Chain this aggr port stat entry */
2246 /* head of the stat list */
2247 if (prev == NULL)
2248 head = curr;
2249 else
2250 prev->dc_next = curr;
2251 prev = curr;
2252 }
2253
2254 /*
2255 * Prepend the stat list with cumulative aggr stats i.e. summed over all
2256 * component ports
2257 */
2258 total_stats = dlstat_aggr_total_stats(head);
2259 if (total_stats != NULL) {
2260 total_stats->dc_next = head;
2261 head = total_stats;
2262 }
2263
2264 done:
2265 free(ginfo.lg_ports);
2266 return (head);
2267 }
2268
2269 /* Misc stat specific functions */
2270 void *
dlstat_misc_stats(dladm_handle_t dh,datalink_id_t linkid)2271 dlstat_misc_stats(dladm_handle_t dh, datalink_id_t linkid)
2272 {
2273 misc_stat_entry_t *misc_stat_entry;
2274 dladm_stat_chain_t *head = NULL;
2275 char linkname[MAXLINKNAMELEN];
2276
2277 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2278 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2279 goto done;
2280 }
2281
2282 misc_stat_entry = i_dlstat_misc_stats(linkname);
2283 if (misc_stat_entry == NULL)
2284 goto done;
2285
2286 head = malloc(sizeof (dladm_stat_chain_t));
2287 if (head == NULL) {
2288 free(misc_stat_entry);
2289 goto done;
2290 }
2291
2292 head->dc_statentry = misc_stat_entry;
2293 (void) strlcpy(head->dc_statheader, "mac_misc_stat",
2294 sizeof (head->dc_statheader));
2295 head->dc_next = NULL;
2296
2297 done:
2298 return (head);
2299 }
2300
2301 /* Exported functions */
2302 dladm_stat_chain_t *
dladm_link_stat_query(dladm_handle_t dh,datalink_id_t linkid,dladm_stat_type_t stattype)2303 dladm_link_stat_query(dladm_handle_t dh, datalink_id_t linkid,
2304 dladm_stat_type_t stattype)
2305 {
2306 return (dladm_stat_table[stattype].ds_querystat(dh, linkid));
2307 }
2308
2309 dladm_stat_chain_t *
dladm_link_stat_diffchain(dladm_stat_chain_t * op1,dladm_stat_chain_t * op2,dladm_stat_type_t stattype)2310 dladm_link_stat_diffchain(dladm_stat_chain_t *op1, dladm_stat_chain_t *op2,
2311 dladm_stat_type_t stattype)
2312 {
2313 dladm_stat_chain_t *op1_curr, *op2_curr;
2314 dladm_stat_chain_t *diff_curr;
2315 dladm_stat_chain_t *diff_prev = NULL, *diff_head = NULL;
2316
2317 /* Perform op1 - op2, store result in diff */
2318 for (op1_curr = op1; op1_curr != NULL; op1_curr = op1_curr->dc_next) {
2319 for (op2_curr = op2; op2_curr != NULL;
2320 op2_curr = op2_curr->dc_next) {
2321 if (dlstat_match_stats(op1_curr->dc_statentry,
2322 op2_curr->dc_statentry, stattype)) {
2323 break;
2324 }
2325 }
2326 diff_curr = malloc(sizeof (dladm_stat_chain_t));
2327 if (diff_curr == NULL)
2328 goto done;
2329
2330 diff_curr->dc_next = NULL;
2331
2332 if (op2_curr == NULL) {
2333 /* prev iteration did not have this stat entry */
2334 diff_curr->dc_statentry =
2335 dlstat_diff_stats(op1_curr->dc_statentry,
2336 NULL, stattype);
2337 } else {
2338 diff_curr->dc_statentry =
2339 dlstat_diff_stats(op1_curr->dc_statentry,
2340 op2_curr->dc_statentry, stattype);
2341 }
2342
2343 if (diff_curr->dc_statentry == NULL) {
2344 free(diff_curr);
2345 goto done;
2346 }
2347
2348 if (diff_prev == NULL) /* head of the diff stat list */
2349 diff_head = diff_curr;
2350 else
2351 diff_prev->dc_next = diff_curr;
2352 diff_prev = diff_curr;
2353 }
2354 done:
2355 return (diff_head);
2356 }
2357
2358 void
dladm_link_stat_free(dladm_stat_chain_t * curr)2359 dladm_link_stat_free(dladm_stat_chain_t *curr)
2360 {
2361 while (curr != NULL) {
2362 dladm_stat_chain_t *tofree = curr;
2363
2364 curr = curr->dc_next;
2365 free(tofree->dc_statentry);
2366 free(tofree);
2367 }
2368 }
2369
2370 /* Query all link stats */
2371 static name_value_stat_t *
i_dlstat_convert_stats(void * stats,stat_info_t stats_list[],uint_t size)2372 i_dlstat_convert_stats(void *stats, stat_info_t stats_list[], uint_t size)
2373 {
2374 int i;
2375 name_value_stat_t *head_stat = NULL, *prev_stat = NULL;
2376 name_value_stat_t *curr_stat;
2377
2378 for (i = 0; i < size; i++) {
2379 uint64_t *val = (void *)
2380 ((uchar_t *)stats + stats_list[i].si_offset);
2381
2382 curr_stat = calloc(1, sizeof (name_value_stat_t));
2383 if (curr_stat == NULL)
2384 break;
2385
2386 (void) strlcpy(curr_stat->nv_statname, stats_list[i].si_name,
2387 sizeof (curr_stat->nv_statname));
2388 curr_stat->nv_statval = *val;
2389 curr_stat->nv_nextstat = NULL;
2390
2391 if (head_stat == NULL) /* First node */
2392 head_stat = curr_stat;
2393 else
2394 prev_stat->nv_nextstat = curr_stat;
2395
2396 prev_stat = curr_stat;
2397 }
2398 return (head_stat);
2399 }
2400
2401 void *
build_nvs_entry(char * statheader,void * statentry,dladm_stat_type_t stattype)2402 build_nvs_entry(char *statheader, void *statentry, dladm_stat_type_t stattype)
2403 {
2404 name_value_stat_entry_t *name_value_stat_entry;
2405 dladm_stat_desc_t *stattbl_ptr;
2406 void *statfields;
2407
2408 stattbl_ptr = &dladm_stat_table[stattype];
2409
2410 /* Allocate memory for query all stat entry */
2411 name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
2412 if (name_value_stat_entry == NULL)
2413 goto done;
2414
2415 /* Header for these stat fields */
2416 (void) strlcpy(name_value_stat_entry->nve_header, statheader,
2417 sizeof (name_value_stat_entry->nve_header));
2418
2419 /* Extract stat fields from the statentry */
2420 statfields = (uchar_t *)statentry +
2421 dladm_stat_table[stattype].ds_offset;
2422
2423 /* Convert curr_stat to <statname, statval> pair */
2424 name_value_stat_entry->nve_stats =
2425 i_dlstat_convert_stats(statfields,
2426 stattbl_ptr->ds_statlist, stattbl_ptr->ds_statsize);
2427 done:
2428 return (name_value_stat_entry);
2429 }
2430
2431 void *
i_walk_dlstat_chain(dladm_stat_chain_t * stat_head,dladm_stat_type_t stattype)2432 i_walk_dlstat_chain(dladm_stat_chain_t *stat_head, dladm_stat_type_t stattype)
2433 {
2434 dladm_stat_chain_t *curr;
2435 dladm_stat_chain_t *nvstat_head = NULL, *nvstat_prev = NULL;
2436 dladm_stat_chain_t *nvstat_curr;
2437
2438 /*
2439 * For every stat in the chain, build header and convert all
2440 * its stat fields
2441 */
2442 for (curr = stat_head; curr != NULL; curr = curr->dc_next) {
2443 nvstat_curr = malloc(sizeof (dladm_stat_chain_t));
2444 if (nvstat_curr == NULL)
2445 break;
2446
2447 nvstat_curr->dc_statentry = build_nvs_entry(curr->dc_statheader,
2448 curr->dc_statentry, stattype);
2449
2450 if (nvstat_curr->dc_statentry == NULL) {
2451 free(nvstat_curr);
2452 break;
2453 }
2454
2455 nvstat_curr->dc_next = NULL;
2456
2457 if (nvstat_head == NULL) /* First node */
2458 nvstat_head = nvstat_curr;
2459 else
2460 nvstat_prev->dc_next = nvstat_curr;
2461
2462 nvstat_prev = nvstat_curr;
2463 }
2464 done:
2465 return (nvstat_head);
2466 }
2467
2468 dladm_stat_chain_t *
dladm_link_stat_query_all(dladm_handle_t dh,datalink_id_t linkid,dladm_stat_type_t stattype)2469 dladm_link_stat_query_all(dladm_handle_t dh, datalink_id_t linkid,
2470 dladm_stat_type_t stattype)
2471 {
2472 dladm_stat_chain_t *stat_head;
2473 dladm_stat_chain_t *nvstat_head = NULL;
2474
2475 /* Query the requested stat */
2476 stat_head = dladm_link_stat_query(dh, linkid, stattype);
2477 if (stat_head == NULL)
2478 goto done;
2479
2480 /*
2481 * Convert every statfield in every stat-entry of stat chain to
2482 * <statname, statval> pair
2483 */
2484 nvstat_head = i_walk_dlstat_chain(stat_head, stattype);
2485
2486 /* Free stat_head */
2487 dladm_link_stat_free(stat_head);
2488
2489 done:
2490 return (nvstat_head);
2491 }
2492
2493 void
dladm_link_stat_query_all_free(dladm_stat_chain_t * curr)2494 dladm_link_stat_query_all_free(dladm_stat_chain_t *curr)
2495 {
2496 while (curr != NULL) {
2497 dladm_stat_chain_t *tofree = curr;
2498 name_value_stat_entry_t *nv_entry = curr->dc_statentry;
2499 name_value_stat_t *nv_curr = nv_entry->nve_stats;
2500
2501 while (nv_curr != NULL) {
2502 name_value_stat_t *nv_tofree = nv_curr;
2503
2504 nv_curr = nv_curr->nv_nextstat;
2505 free(nv_tofree);
2506 }
2507
2508 curr = curr->dc_next;
2509 free(nv_entry);
2510 free(tofree);
2511 }
2512 }
2513
2514 /* flow stats specific routines */
2515 flow_stat_t *
dladm_flow_stat_query(const char * flowname)2516 dladm_flow_stat_query(const char *flowname)
2517 {
2518 kstat_ctl_t *kcp;
2519 kstat_t *ksp;
2520 flow_stat_t *flow_stat = NULL;
2521
2522 if ((kcp = kstat_open()) == NULL)
2523 return (NULL);
2524
2525 flow_stat = calloc(1, sizeof (flow_stat_t));
2526 if (flow_stat == NULL)
2527 goto done;
2528
2529 ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
2530
2531 if (ksp != NULL) {
2532 i_dlstat_get_stats(kcp, ksp, flow_stat, flow_stats_list,
2533 FLOW_STAT_SIZE);
2534 }
2535
2536 done:
2537 (void) kstat_close(kcp);
2538 return (flow_stat);
2539 }
2540
2541 flow_stat_t *
dladm_flow_stat_diff(flow_stat_t * op1,flow_stat_t * op2)2542 dladm_flow_stat_diff(flow_stat_t *op1, flow_stat_t *op2)
2543 {
2544 flow_stat_t *diff_stat;
2545
2546 diff_stat = calloc(1, sizeof (flow_stat_t));
2547 if (diff_stat == NULL)
2548 goto done;
2549
2550 if (op2 == NULL) {
2551 bcopy(op1, diff_stat, sizeof (flow_stat_t));
2552 } else {
2553 i_dlstat_diff_stats(diff_stat, op1, op2, flow_stats_list,
2554 FLOW_STAT_SIZE);
2555 }
2556 done:
2557 return (diff_stat);
2558 }
2559
2560 void
dladm_flow_stat_free(flow_stat_t * curr)2561 dladm_flow_stat_free(flow_stat_t *curr)
2562 {
2563 free(curr);
2564 }
2565
2566 /* Query all flow stats */
2567 name_value_stat_entry_t *
dladm_flow_stat_query_all(const char * flowname)2568 dladm_flow_stat_query_all(const char *flowname)
2569 {
2570 flow_stat_t *flow_stat;
2571 name_value_stat_entry_t *name_value_stat_entry = NULL;
2572
2573 /* Query flow stats */
2574 flow_stat = dladm_flow_stat_query(flowname);
2575 if (flow_stat == NULL)
2576 goto done;
2577
2578 /* Allocate memory for query all stat entry */
2579 name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
2580 if (name_value_stat_entry == NULL) {
2581 dladm_flow_stat_free(flow_stat);
2582 goto done;
2583 }
2584
2585 /* Header for these stat fields */
2586 (void) strncpy(name_value_stat_entry->nve_header, flowname,
2587 MAXFLOWNAMELEN);
2588
2589 /* Convert every statfield in flow_stat to <statname, statval> pair */
2590 name_value_stat_entry->nve_stats =
2591 i_dlstat_convert_stats(flow_stat, flow_stats_list, FLOW_STAT_SIZE);
2592
2593 /* Free flow_stat */
2594 dladm_flow_stat_free(flow_stat);
2595
2596 done:
2597 return (name_value_stat_entry);
2598 }
2599
2600 void
dladm_flow_stat_query_all_free(name_value_stat_entry_t * curr)2601 dladm_flow_stat_query_all_free(name_value_stat_entry_t *curr)
2602 {
2603 name_value_stat_t *nv_curr = curr->nve_stats;
2604
2605 while (nv_curr != NULL) {
2606 name_value_stat_t *nv_tofree = nv_curr;
2607
2608 nv_curr = nv_curr->nv_nextstat;
2609 free(nv_tofree);
2610 }
2611 }
2612