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