1da14cebeSEric Cheng /*
2da14cebeSEric Cheng  * CDDL HEADER START
3da14cebeSEric Cheng  *
4da14cebeSEric Cheng  * The contents of this file are subject to the terms of the
5da14cebeSEric Cheng  * Common Development and Distribution License (the "License").
6da14cebeSEric Cheng  * You may not use this file except in compliance with the License.
7da14cebeSEric Cheng  *
8da14cebeSEric Cheng  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da14cebeSEric Cheng  * or http://www.opensolaris.org/os/licensing.
10da14cebeSEric Cheng  * See the License for the specific language governing permissions
11da14cebeSEric Cheng  * and limitations under the License.
12da14cebeSEric Cheng  *
13da14cebeSEric Cheng  * When distributing Covered Code, include this CDDL HEADER in each
14da14cebeSEric Cheng  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da14cebeSEric Cheng  * If applicable, add the following below this CDDL HEADER, with the
16da14cebeSEric Cheng  * fields enclosed by brackets "[]" replaced with your own identifying
17da14cebeSEric Cheng  * information: Portions Copyright [yyyy] [name of copyright owner]
18da14cebeSEric Cheng  *
19da14cebeSEric Cheng  * CDDL HEADER END
20da14cebeSEric Cheng  */
21da14cebeSEric Cheng /*
220dc2366fSVenugopal Iyer  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23da14cebeSEric Cheng  * Use is subject to license terms.
24da14cebeSEric Cheng  */
25da14cebeSEric Cheng 
26*399dcf08Scarlos antonio neira bustos /*
27*399dcf08Scarlos antonio neira bustos  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
28*399dcf08Scarlos antonio neira bustos  */
29*399dcf08Scarlos antonio neira bustos 
3062ef8476SYuri Pankov #include <stddef.h>
31da14cebeSEric Cheng #include <stdio.h>
32da14cebeSEric Cheng #include <stdlib.h>
33da14cebeSEric Cheng #include <strings.h>
34da14cebeSEric Cheng #include <err.h>
35da14cebeSEric Cheng #include <errno.h>
36c3affd82SMichael Lim #include <fcntl.h>
37da14cebeSEric Cheng #include <kstat.h>
38c3affd82SMichael Lim #include <limits.h>
39da14cebeSEric Cheng #include <unistd.h>
40da14cebeSEric Cheng #include <signal.h>
41da14cebeSEric Cheng #include <sys/dld.h>
4282a2fc47SJames Carlson #include <sys/ddi.h>
43da14cebeSEric Cheng 
44da14cebeSEric Cheng #include <libdllink.h>
45da14cebeSEric Cheng #include <libdlflow.h>
46da14cebeSEric Cheng #include <libdlstat.h>
470dc2366fSVenugopal Iyer #include <libdlaggr.h>
48da14cebeSEric Cheng 
49da14cebeSEric Cheng struct flowlist {
50da000602SGirish Moodalbail 	char		flowname[MAXFLOWNAMELEN];
51c3affd82SMichael Lim 	char		linkname[MAXLINKNAMELEN];
52da14cebeSEric Cheng 	datalink_id_t	linkid;
53c3affd82SMichael Lim 	int		fd;
54285e94f9SMichael Lim 	uint64_t	ifspeed;
55da14cebeSEric Cheng 	boolean_t	first;
56da14cebeSEric Cheng 	boolean_t	display;
57da14cebeSEric Cheng 	pktsum_t 	prevstats;
58da14cebeSEric Cheng 	pktsum_t	diffstats;
59da14cebeSEric Cheng };
60da14cebeSEric Cheng 
61da14cebeSEric Cheng pktsum_t		totalstats;
62da14cebeSEric Cheng struct flowlist		*stattable = NULL;
63da14cebeSEric Cheng 
64da14cebeSEric Cheng #define	STATGROWSIZE	16
65da14cebeSEric Cheng 
66da14cebeSEric Cheng /* Exported functions */
67da14cebeSEric Cheng 
68da14cebeSEric Cheng /*
69da14cebeSEric Cheng  * dladm_kstat_lookup() is a modified version of kstat_lookup which
70da14cebeSEric Cheng  * adds the class as a selector.
71da14cebeSEric Cheng  */
72da14cebeSEric Cheng kstat_t *
dladm_kstat_lookup(kstat_ctl_t * kcp,const char * module,int instance,const char * name,const char * class)73da14cebeSEric Cheng dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance,
74da14cebeSEric Cheng     const char *name, const char *class)
75da14cebeSEric Cheng {
76da14cebeSEric Cheng 	kstat_t *ksp = NULL;
77da14cebeSEric Cheng 
78da14cebeSEric Cheng 	for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
79da14cebeSEric Cheng 		if ((module == NULL || strcmp(ksp->ks_module, module) == 0) &&
80da14cebeSEric Cheng 		    (instance == -1 || ksp->ks_instance == instance) &&
81da14cebeSEric Cheng 		    (name == NULL || strcmp(ksp->ks_name, name) == 0) &&
82da14cebeSEric Cheng 		    (class == NULL || strcmp(ksp->ks_class, class) == 0))
83da14cebeSEric Cheng 			return (ksp);
84da14cebeSEric Cheng 	}
85da14cebeSEric Cheng 
86da14cebeSEric Cheng 	errno = ENOENT;
87da14cebeSEric Cheng 	return (NULL);
88da14cebeSEric Cheng }
89da14cebeSEric Cheng 
90da14cebeSEric Cheng /*
91da14cebeSEric Cheng  * dladm_get_stats() populates the supplied pktsum_t structure with
92da14cebeSEric Cheng  * the input and output  packet and byte kstats from the kstat_t
93da14cebeSEric Cheng  * found with dladm_kstat_lookup.
94da14cebeSEric Cheng  */
95da14cebeSEric Cheng void
dladm_get_stats(kstat_ctl_t * kcp,kstat_t * ksp,pktsum_t * stats)96da14cebeSEric Cheng dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats)
97da14cebeSEric Cheng {
98da14cebeSEric Cheng 
99da14cebeSEric Cheng 	if (kstat_read(kcp, ksp, NULL) == -1)
100da14cebeSEric Cheng 		return;
101da14cebeSEric Cheng 
102da14cebeSEric Cheng 	stats->snaptime = gethrtime();
103da14cebeSEric Cheng 
104da14cebeSEric Cheng 	if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
105da14cebeSEric Cheng 	    &stats->ipackets) < 0) {
106da14cebeSEric Cheng 		if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64,
107da14cebeSEric Cheng 		    &stats->ipackets) < 0)
108da14cebeSEric Cheng 			return;
109da14cebeSEric Cheng 	}
110da14cebeSEric Cheng 
111da14cebeSEric Cheng 	if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
112da14cebeSEric Cheng 	    &stats->opackets) < 0) {
113da14cebeSEric Cheng 		if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64,
114da14cebeSEric Cheng 		    &stats->opackets) < 0)
115da14cebeSEric Cheng 			return;
116da14cebeSEric Cheng 	}
117da14cebeSEric Cheng 
118da14cebeSEric Cheng 	if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
119da14cebeSEric Cheng 	    &stats->rbytes) < 0) {
120da14cebeSEric Cheng 		if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64,
121da14cebeSEric Cheng 		    &stats->rbytes) < 0)
122da14cebeSEric Cheng 			return;
123da14cebeSEric Cheng 	}
124da14cebeSEric Cheng 
125da14cebeSEric Cheng 	if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
126da14cebeSEric Cheng 	    &stats->obytes) < 0) {
127da14cebeSEric Cheng 		if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64,
128da14cebeSEric Cheng 		    &stats->obytes) < 0)
129da14cebeSEric Cheng 			return;
130da14cebeSEric Cheng 	}
131da14cebeSEric Cheng 
132da14cebeSEric Cheng 	if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
133da14cebeSEric Cheng 	    &stats->ierrors) < 0) {
134da14cebeSEric Cheng 		if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64,
135da14cebeSEric Cheng 		    &stats->ierrors) < 0)
136da14cebeSEric Cheng 		return;
137da14cebeSEric Cheng 	}
138da14cebeSEric Cheng 
139da14cebeSEric Cheng 	if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
140da14cebeSEric Cheng 	    &stats->oerrors) < 0) {
141da14cebeSEric Cheng 		if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64,
142da14cebeSEric Cheng 		    &stats->oerrors) < 0)
143da14cebeSEric Cheng 			return;
144da14cebeSEric Cheng 	}
145da14cebeSEric Cheng }
146da14cebeSEric Cheng 
147da14cebeSEric Cheng int
dladm_kstat_value(kstat_t * ksp,const char * name,uint8_t type,void * buf)148da14cebeSEric Cheng dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
149da14cebeSEric Cheng {
150da14cebeSEric Cheng 	kstat_named_t	*knp;
151da14cebeSEric Cheng 
152da14cebeSEric Cheng 	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
153da14cebeSEric Cheng 		return (-1);
154da14cebeSEric Cheng 
155da14cebeSEric Cheng 	if (knp->data_type != type)
156da14cebeSEric Cheng 		return (-1);
157da14cebeSEric Cheng 
158da14cebeSEric Cheng 	switch (type) {
159da14cebeSEric Cheng 	case KSTAT_DATA_UINT64:
160da14cebeSEric Cheng 		*(uint64_t *)buf = knp->value.ui64;
161da14cebeSEric Cheng 		break;
162da14cebeSEric Cheng 	case KSTAT_DATA_UINT32:
163da14cebeSEric Cheng 		*(uint32_t *)buf = knp->value.ui32;
164da14cebeSEric Cheng 		break;
165da14cebeSEric Cheng 	default:
166da14cebeSEric Cheng 		return (-1);
167da14cebeSEric Cheng 	}
168da14cebeSEric Cheng 
169da14cebeSEric Cheng 	return (0);
170da14cebeSEric Cheng }
171da14cebeSEric Cheng 
172da14cebeSEric Cheng dladm_status_t
dladm_get_single_mac_stat(dladm_handle_t handle,datalink_id_t linkid,const char * name,uint8_t type,void * val)1734ac67f02SAnurag S. Maskey dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid,
1744ac67f02SAnurag S. Maskey     const char *name, uint8_t type, void *val)
175da14cebeSEric Cheng {
176da14cebeSEric Cheng 	char		module[DLPI_LINKNAME_MAX];
177da14cebeSEric Cheng 	uint_t		instance;
178da14cebeSEric Cheng 	char 		link[DLPI_LINKNAME_MAX];
179da14cebeSEric Cheng 	dladm_status_t	status;
180da14cebeSEric Cheng 	uint32_t	flags, media;
181da14cebeSEric Cheng 	kstat_t		*ksp;
182da14cebeSEric Cheng 	dladm_phys_attr_t dpap;
183da14cebeSEric Cheng 
1844ac67f02SAnurag S. Maskey 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
1854ac67f02SAnurag S. Maskey 	    &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
186da14cebeSEric Cheng 		return (status);
187da14cebeSEric Cheng 
188da14cebeSEric Cheng 	if (media != DL_ETHER)
189da14cebeSEric Cheng 		return (DLADM_STATUS_LINKINVAL);
190da14cebeSEric Cheng 
1914ac67f02SAnurag S. Maskey 	status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST);
192da14cebeSEric Cheng 
193da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK)
194da14cebeSEric Cheng 		return (status);
195da14cebeSEric Cheng 
196da14cebeSEric Cheng 	status = dladm_parselink(dpap.dp_dev, module, &instance);
197da14cebeSEric Cheng 
198da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK)
199da14cebeSEric Cheng 		return (status);
200da14cebeSEric Cheng 
201da14cebeSEric Cheng 	/*
202da14cebeSEric Cheng 	 * The kstat query could fail if the underlying MAC
203da14cebeSEric Cheng 	 * driver was already detached.
204da14cebeSEric Cheng 	 */
205*399dcf08Scarlos antonio neira bustos 	if (dladm_dld_kcp(handle) == NULL) {
206*399dcf08Scarlos antonio neira bustos 		warn("kstat_open operation failed");
207*399dcf08Scarlos antonio neira bustos 		return (-1);
208*399dcf08Scarlos antonio neira bustos 	}
209*399dcf08Scarlos antonio neira bustos 
210*399dcf08Scarlos antonio neira bustos 	if ((ksp = kstat_lookup(dladm_dld_kcp(handle), module, instance,
211*399dcf08Scarlos antonio neira bustos 	    "mac")) == NULL &&
212*399dcf08Scarlos antonio neira bustos 	    (ksp = kstat_lookup(dladm_dld_kcp(handle), module, instance,
213*399dcf08Scarlos antonio neira bustos 	    NULL)) == NULL) {
214da14cebeSEric Cheng 		goto bail;
215*399dcf08Scarlos antonio neira bustos 	}
216da14cebeSEric Cheng 
217*399dcf08Scarlos antonio neira bustos 	if (kstat_read(dladm_dld_kcp(handle), ksp, NULL) == -1)
218da14cebeSEric Cheng 		goto bail;
219da14cebeSEric Cheng 
220da14cebeSEric Cheng 	if (dladm_kstat_value(ksp, name, type, val) < 0)
221da14cebeSEric Cheng 		goto bail;
222da14cebeSEric Cheng 
223da14cebeSEric Cheng 	return (DLADM_STATUS_OK);
224da14cebeSEric Cheng 
225da14cebeSEric Cheng bail:
226da14cebeSEric Cheng 	return (dladm_errno2status(errno));
227da14cebeSEric Cheng }
228da14cebeSEric Cheng 
229da14cebeSEric Cheng /* Compute sum of 2 pktsums (s1 = s2 + s3) */
230da14cebeSEric Cheng void
dladm_stats_total(pktsum_t * s1,pktsum_t * s2,pktsum_t * s3)231da14cebeSEric Cheng dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
232da14cebeSEric Cheng {
233da14cebeSEric Cheng 	s1->rbytes    = s2->rbytes    + s3->rbytes;
234da14cebeSEric Cheng 	s1->ipackets  = s2->ipackets  + s3->ipackets;
235da14cebeSEric Cheng 	s1->ierrors   = s2->ierrors   + s3->ierrors;
236da14cebeSEric Cheng 	s1->obytes    = s2->obytes    + s3->obytes;
237da14cebeSEric Cheng 	s1->opackets  = s2->opackets  + s3->opackets;
238da14cebeSEric Cheng 	s1->oerrors   = s2->oerrors   + s3->oerrors;
239da14cebeSEric Cheng 	s1->snaptime  = s2->snaptime;
240da14cebeSEric Cheng }
241da14cebeSEric Cheng 
2420dc2366fSVenugopal Iyer #define	DIFF_STAT(s2, s3) ((s2) > (s3) ? ((s2) - (s3)) : 0)
2432d40c3b2SPrakash Jalan 
2442d40c3b2SPrakash Jalan 
245da14cebeSEric Cheng /* Compute differences between 2 pktsums (s1 = s2 - s3) */
246da14cebeSEric Cheng void
dladm_stats_diff(pktsum_t * s1,pktsum_t * s2,pktsum_t * s3)247da14cebeSEric Cheng dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
248da14cebeSEric Cheng {
2492d40c3b2SPrakash Jalan 	s1->rbytes    = DIFF_STAT(s2->rbytes,   s3->rbytes);
2502d40c3b2SPrakash Jalan 	s1->ipackets  = DIFF_STAT(s2->ipackets, s3->ipackets);
2512d40c3b2SPrakash Jalan 	s1->ierrors   = DIFF_STAT(s2->ierrors,  s3->ierrors);
2522d40c3b2SPrakash Jalan 	s1->obytes    = DIFF_STAT(s2->obytes,   s3->obytes);
2532d40c3b2SPrakash Jalan 	s1->opackets  = DIFF_STAT(s2->opackets, s3->opackets);
2542d40c3b2SPrakash Jalan 	s1->oerrors   = DIFF_STAT(s2->oerrors,  s3->oerrors);
2552d40c3b2SPrakash Jalan 	s1->snaptime  = DIFF_STAT(s2->snaptime, s3->snaptime);
256da14cebeSEric Cheng }
2570dc2366fSVenugopal Iyer 
2580dc2366fSVenugopal Iyer #define	DLSTAT_MAC_RX_SWLANE	"mac_rx_swlane"
2590dc2366fSVenugopal Iyer #define	DLSTAT_MAC_RX_HWLANE	"mac_rx_hwlane"
2600dc2366fSVenugopal Iyer #define	DLSTAT_MAC_TX_SWLANE	"mac_tx_swlane"
2610dc2366fSVenugopal Iyer #define	DLSTAT_MAC_TX_HWLANE	"mac_tx_hwlane"
2620dc2366fSVenugopal Iyer #define	DLSTAT_MAC_MISC_STAT	"mac_misc_stat"
2630dc2366fSVenugopal Iyer #define	DLSTAT_MAC_RX_RING	"mac_rx_ring"
2640dc2366fSVenugopal Iyer #define	DLSTAT_MAC_TX_RING	"mac_tx_ring"
2650dc2366fSVenugopal Iyer #define	DLSTAT_MAC_FANOUT	"mac_rx_swlane0_fanout"
2660dc2366fSVenugopal Iyer 
2670dc2366fSVenugopal Iyer typedef struct {
2680dc2366fSVenugopal Iyer 	const char	*si_name;
2690dc2366fSVenugopal Iyer 	uint_t		si_offset;
2700dc2366fSVenugopal Iyer } stat_info_t;
2710dc2366fSVenugopal Iyer 
2720dc2366fSVenugopal Iyer #define	A_CNT(arr)	(sizeof (arr) / sizeof (arr[0]))
2730dc2366fSVenugopal Iyer 
2740dc2366fSVenugopal Iyer /* Definitions for rx lane stats */
2750dc2366fSVenugopal Iyer #define	RL_OFF(f)	(offsetof(rx_lane_stat_t, f))
2760dc2366fSVenugopal Iyer 
2770dc2366fSVenugopal Iyer static	stat_info_t	rx_hwlane_stats_list[] = {
2780dc2366fSVenugopal Iyer 	{"ipackets",		RL_OFF(rl_ipackets)},
2790dc2366fSVenugopal Iyer 	{"rbytes",		RL_OFF(rl_rbytes)},
2800dc2366fSVenugopal Iyer 	{"intrs",		RL_OFF(rl_intrs)},
2810dc2366fSVenugopal Iyer 	{"intrbytes",		RL_OFF(rl_intrbytes)},
2820dc2366fSVenugopal Iyer 	{"polls",		RL_OFF(rl_polls)},
2830dc2366fSVenugopal Iyer 	{"pollbytes",		RL_OFF(rl_pollbytes)},
2840dc2366fSVenugopal Iyer 	{"rxsdrops",		RL_OFF(rl_sdrops)},
2850dc2366fSVenugopal Iyer 	{"chainunder10",	RL_OFF(rl_chl10)},
2860dc2366fSVenugopal Iyer 	{"chain10to50", 	RL_OFF(rl_ch10_50)},
2870dc2366fSVenugopal Iyer 	{"chainover50", 	RL_OFF(rl_chg50)}
2880dc2366fSVenugopal Iyer };
2890dc2366fSVenugopal Iyer #define	RX_HWLANE_STAT_SIZE	A_CNT(rx_hwlane_stats_list)
2900dc2366fSVenugopal Iyer 
2910dc2366fSVenugopal Iyer static	stat_info_t	rx_swlane_stats_list[] = {
2920dc2366fSVenugopal Iyer 	{"ipackets",		RL_OFF(rl_ipackets)},
2930dc2366fSVenugopal Iyer 	{"rbytes",		RL_OFF(rl_rbytes)},
2940dc2366fSVenugopal Iyer 	{"local",		RL_OFF(rl_lclpackets)},
2950dc2366fSVenugopal Iyer 	{"localbytes",		RL_OFF(rl_lclbytes)},
2960dc2366fSVenugopal Iyer 	{"intrs",		RL_OFF(rl_intrs)},
2970dc2366fSVenugopal Iyer 	{"intrbytes",		RL_OFF(rl_intrbytes)},
2980dc2366fSVenugopal Iyer 	{"rxsdrops",		RL_OFF(rl_sdrops)}
2990dc2366fSVenugopal Iyer };
3000dc2366fSVenugopal Iyer #define	RX_SWLANE_STAT_SIZE	A_CNT(rx_swlane_stats_list)
3010dc2366fSVenugopal Iyer 
3020dc2366fSVenugopal Iyer static	stat_info_t	rx_lane_stats_list[] = {
3030dc2366fSVenugopal Iyer 	{"ipackets",		RL_OFF(rl_ipackets)},
3040dc2366fSVenugopal Iyer 	{"rbytes",		RL_OFF(rl_rbytes)},
305