xref: /illumos-gate/usr/src/lib/libdladm/common/libdlether.c (revision 8d0c3d29bb99f6521f2dc5058a7e4debebad7899)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdlib.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <sys/types.h>
30 #include <libdladm_impl.h>
31 #include <libdllink.h>
32 #include <libdlstat.h>
33 #include <libdlether.h>
34 
35 /*
36  * Ethernet administration library.
37  */
38 
39 /*
40  * kstat names for extracting attributes.
41  */
42 typedef struct ether_spdx_s {
43 	dladm_ether_spdx_t eth_spdx;
44 	char *eth_spdx_stat_name;
45 } ether_spdx_t;
46 
47 static ether_spdx_t cap_spdx[] = {
48 	{{1000, LINK_DUPLEX_FULL}, "cap_1000fdx"},
49 	{{1000, LINK_DUPLEX_HALF}, "cap_1000hdx"},
50 	{{100, LINK_DUPLEX_FULL}, "cap_100fdx"},
51 	{{100, LINK_DUPLEX_HALF}, "cap_100hdx"},
52 	{{10, LINK_DUPLEX_FULL}, "cap_10fdx"},
53 	{{10, LINK_DUPLEX_HALF}, "cap_10hdx"},
54 	{{0, LINK_DUPLEX_UNKNOWN}, NULL}
55 };
56 
57 static ether_spdx_t adv_cap_spdx[] = {
58 	{{1000, LINK_DUPLEX_FULL}, "adv_cap_1000fdx"},
59 	{{1000, LINK_DUPLEX_HALF}, "adv_cap_1000hdx"},
60 	{{100, LINK_DUPLEX_FULL}, "adv_cap_100fdx"},
61 	{{100, LINK_DUPLEX_HALF}, "adv_cap_100hdx"},
62 	{{10, LINK_DUPLEX_FULL}, "adv_cap_10fdx"},
63 	{{10, LINK_DUPLEX_HALF}, "adv_cap_10hdx"},
64 	{{0, LINK_DUPLEX_UNKNOWN}, NULL}
65 };
66 
67 static ether_spdx_t lp_cap_spdx[] = {
68 	{{1000, LINK_DUPLEX_FULL}, "lp_cap_1000fdx"},
69 	{{1000, LINK_DUPLEX_HALF}, "lp_cap_1000hdx"},
70 	{{100, LINK_DUPLEX_FULL}, "lp_cap_100fdx"},
71 	{{100, LINK_DUPLEX_HALF}, "lp_cap_100hdx"},
72 	{{10, LINK_DUPLEX_FULL}, "lp_cap_10fdx"},
73 	{{10, LINK_DUPLEX_HALF}, "lp_cap_10hdx"},
74 	{{0, LINK_DUPLEX_UNKNOWN}, NULL}
75 };
76 
77 typedef struct attr_kstat_s {
78 	char *autoneg_stat;
79 	char *pause_stat;
80 	char *asmpause_stat;
81 	char *fault_stat;
82 	ether_spdx_t *spdx_stat;
83 } attr_kstat_t;
84 
85 static attr_kstat_t attrstat[] =  {
86 	{"link_autoneg",	/* current */
87 	    "link_pause",	"link_asmpause",	NULL,
88 	    NULL},
89 
90 	{"cap_autoneg",		/* capable */
91 	    "cap_pause",	"cap_asmpause",		"cap_rem_fault",
92 	    cap_spdx},
93 
94 	{"adv_cap_autoneg",	/* advertised */
95 	    "adv_cap_pause",	"adv_cap_asmpause",	"adv_rem_fault",
96 	    adv_cap_spdx},
97 
98 	{"lp_cap_autoneg",	/* peer advertised */
99 	    "lp_cap_pause",	"lp_cap_asmpause",	"lp_rem_fault",
100 	    lp_cap_spdx}
101 };
102 
103 /*
104  * Get the speed-duplex stats specified in the ether_spdx_t table passed in
105  * by querying the appropriate kstat for each entry in the table.
106  */
107 static dladm_status_t
108 i_dladm_get_spdx(dladm_handle_t handle, datalink_id_t linkid,
109     dladm_ether_attr_t *eattr, ether_spdx_t *spdx_stat)
110 {
111 	int		i, nspdx = 0;
112 	uint32_t	speed;
113 	dladm_status_t	status;
114 	void		*ptr;
115 
116 	eattr->le_spdx = NULL;
117 	for (i = 0; spdx_stat[i].eth_spdx_stat_name != NULL; i++) {
118 		if ((status = dladm_get_single_mac_stat(handle, linkid,
119 		    spdx_stat[i].eth_spdx_stat_name,
120 		    KSTAT_DATA_UINT32, &speed)) != DLADM_STATUS_OK) {
121 
122 			if (status == DLADM_STATUS_NOTFOUND) {
123 				/*
124 				 * Missing statistic.
125 				 * Skip this one and try the rest.
126 				 */
127 				continue;
128 			} else {
129 				free(eattr->le_spdx);
130 				eattr->le_num_spdx = 0;
131 				return (status);
132 			}
133 		}
134 		if (speed == 0)
135 			continue;
136 		nspdx++;
137 		ptr = realloc(eattr->le_spdx,
138 		    nspdx * sizeof (dladm_ether_spdx_t));
139 		if (ptr != NULL) {
140 			eattr->le_spdx = ptr;
141 		} else {
142 			free(eattr->le_spdx);
143 			eattr->le_num_spdx = 0;
144 			return (DLADM_STATUS_NOMEM);
145 		}
146 		eattr->le_spdx[nspdx - 1] = spdx_stat[i].eth_spdx;
147 	}
148 	eattr->le_num_spdx = nspdx;
149 	return (DLADM_STATUS_OK);
150 }
151 
152 /*
153  * Returns "yes" or "no" based on the autonegotion capabilities
154  * for the parameter type indicated by ptype. The permissible
155  * values for ptype are CURRENT, CAPABLE, ADV, PEERADV.
156  */
157 char *
158 dladm_ether_autoneg2str(char *buf, size_t buflen, dladm_ether_info_t *eattr,
159     int ptype)
160 {
161 	boolean_t autoneg = eattr->lei_attr[ptype].le_autoneg;
162 
163 	(void) strlcpy(buf, (autoneg ? "yes" : "no"), buflen);
164 	return (buf);
165 }
166 
167 /*
168  * Returns {"bi", "tx", "none"} based on the flow-control capabilities
169  * for the parameter type indicated by ptype. The permissible
170  * values for ptype are CURRENT, CAPABLE, ADV, PEERADV.
171  */
172 char *
173 dladm_ether_pause2str(char *buf, size_t buflen, dladm_ether_info_t *eattr,
174     int ptype)
175 {
176 	boolean_t pause = eattr->lei_attr[ptype].le_pause;
177 	boolean_t asmpause = eattr->lei_attr[ptype].le_asmpause;
178 
179 	if (pause)
180 		(void) strlcpy(buf, "bi", buflen);
181 	else if (asmpause)
182 		(void) strlcpy(buf, "tx", buflen);
183 	else
184 		(void) strlcpy(buf, "none", buflen);
185 	return (buf);
186 }
187 
188 /*
189  * For a given param type, parse the list of speed-duplex pairs in
190  * the dladm_ether_info_t and return a  comma-separated string formatted
191  * as <speed><speed-unit-char>-<duplex-chars> where <speed> is the value of
192  * speed, in units specifid by the <speed-unit-char> which is one
193  * of 'M' (Mbits/sec) or 'G' (Gigabits/sec).  The permissible values of
194  * <duplex-chars> are 'u' (indicating duplex is "unknown") or one/both of
195  * 'f', 'h' (indicating full-duplex and half-duplex respectively)
196  */
197 extern char *
198 dladm_ether_spdx2str(char *buf, size_t buflen, dladm_ether_info_t *eattr,
199     int ptype)
200 {
201 	int		i, j;
202 	boolean_t	is_full, is_half;
203 	int		speed;
204 	char		speed_unit;
205 	char		tmpbuf[DLADM_STRSIZE];
206 	dladm_ether_spdx_t *spdx;
207 	uint32_t	nspdx;
208 
209 	spdx = eattr->lei_attr[ptype].le_spdx;
210 	nspdx = eattr->lei_attr[ptype].le_num_spdx;
211 	for (i = 0; i < nspdx; i++) {
212 
213 		speed = spdx[i].lesd_speed;
214 
215 		/*
216 		 * if we have already covered this speed for
217 		 * the <other>-duplex case before this, skip it
218 		 */
219 		for (j = 0; j < i; j++) {
220 			if (speed == spdx[j].lesd_speed)
221 				break;
222 		}
223 		if (j < i)
224 			continue;
225 
226 		if (speed >= 1000) {
227 			speed = speed/1000;
228 			speed_unit = 'G';
229 		} else {
230 			speed_unit = 'M';
231 		}
232 		(void) snprintf(tmpbuf, DLADM_STRSIZE, "%d%c",
233 		    speed, speed_unit);
234 		if (i > 0)
235 			(void) strncat(buf, ",", buflen);
236 		(void) strncat(buf, tmpbuf, buflen);
237 
238 		is_full = is_half = B_FALSE;
239 		/*
240 		 * Find all the supported duplex values for this speed.
241 		 */
242 		for (j = 0; j < nspdx; j++) {
243 			if (spdx[j].lesd_speed != spdx[i].lesd_speed)
244 				continue;
245 			if (spdx[j].lesd_duplex == LINK_DUPLEX_FULL)
246 				is_full = B_TRUE;
247 			if (spdx[j].lesd_duplex == LINK_DUPLEX_HALF)
248 				is_half = B_TRUE;
249 		}
250 		if (is_full && is_half)
251 			(void) strncat(buf, "-fh", buflen);
252 		else if (is_full)
253 			(void) strncat(buf, "-f", buflen);
254 		else if (is_half)
255 			(void) strncat(buf, "-h", buflen);
256 	}
257 	return (buf);
258 }
259 
260 /*
261  * Extract Ethernet attributes of the link specified by linkid.
262  * Information for the CURRENT, CAPABLE, ADV and PEERADV parameter
263  * types is extracted into the lei_attr[] entries in the dladm_ether_info_t.
264  * On succesful return, the memory allocated in this function should be
265  * freed by calling dladm_ether_info_done().
266  */
267 extern dladm_status_t
268 dladm_ether_info(dladm_handle_t handle, datalink_id_t linkid,
269     dladm_ether_info_t *eattr)
270 {
271 	uint32_t	autoneg, pause, asmpause, fault;
272 	uint64_t	sp64;
273 	dladm_status_t	status;
274 	int		i;
275 	link_duplex_t	link_duplex;
276 
277 	bzero(eattr, sizeof (*eattr));
278 	status = dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL,
279 	    eattr->lei_linkname, sizeof (eattr->lei_linkname));
280 	if (status != DLADM_STATUS_OK)
281 		goto bail;
282 
283 	/* get current values of speed, duplex, state of link */
284 	eattr->lei_attr[CURRENT].le_num_spdx = 1;
285 	eattr->lei_attr[CURRENT].le_spdx = malloc(sizeof (dladm_ether_spdx_t));
286 	if (eattr->lei_attr[CURRENT].le_spdx == NULL) {
287 		status = DLADM_STATUS_NOMEM;
288 		goto bail;
289 	}
290 
291 	if ((status = dladm_get_single_mac_stat(handle, linkid, "ifspeed",
292 	    KSTAT_DATA_UINT64, &sp64)) != DLADM_STATUS_OK)
293 		goto bail;
294 
295 	if ((status = dladm_get_single_mac_stat(handle, linkid, "link_duplex",
296 	    KSTAT_DATA_UINT32, &link_duplex)) != DLADM_STATUS_OK)
297 		goto bail;
298 
299 	eattr->lei_attr[CURRENT].le_spdx->lesd_speed = (int)(sp64/1000000ull);
300 	eattr->lei_attr[CURRENT].le_spdx->lesd_duplex = link_duplex;
301 
302 	status = dladm_get_state(handle, linkid, &eattr->lei_state);
303 	if (status != DLADM_STATUS_OK)
304 		goto bail;
305 
306 	/* get the auto, pause, asmpause, fault values */
307 	for (i = CURRENT; i <= PEERADV; i++)  {
308 
309 		status = dladm_get_single_mac_stat(handle, linkid,
310 		    attrstat[i].autoneg_stat, KSTAT_DATA_UINT32, &autoneg);
311 		if (status != DLADM_STATUS_OK)
312 			goto bail;
313 
314 		status = dladm_get_single_mac_stat(handle, linkid,
315 		    attrstat[i].pause_stat, KSTAT_DATA_UINT32, &pause);
316 		if (status != DLADM_STATUS_OK)
317 			goto bail;
318 
319 		status = dladm_get_single_mac_stat(handle, linkid,
320 		    attrstat[i].asmpause_stat, KSTAT_DATA_UINT32, &asmpause);
321 		if (status != DLADM_STATUS_OK)
322 			goto bail;
323 
324 		eattr->lei_attr[i].le_autoneg = (autoneg != 0);
325 		eattr->lei_attr[i].le_pause = (pause != 0);
326 		eattr->lei_attr[i].le_asmpause = (asmpause != 0);
327 
328 		if (i == CURRENT)
329 			continue;
330 		status = dladm_get_single_mac_stat(handle, linkid,
331 		    attrstat[i].fault_stat, KSTAT_DATA_UINT32, &fault);
332 		if (status != DLADM_STATUS_OK)
333 			goto bail;
334 		eattr->lei_attr[i].le_fault = (pause != 0);
335 
336 		/* get all the supported speed/duplex values */
337 		status = i_dladm_get_spdx(handle, linkid, &eattr->lei_attr[i],
338 		    attrstat[i].spdx_stat);
339 		if (status != DLADM_STATUS_OK)
340 			goto bail;
341 	}
342 	eattr->lei_attr[CURRENT].le_fault =
343 	    eattr->lei_attr[ADV].le_fault || eattr->lei_attr[PEERADV].le_fault;
344 bail:
345 	if (status != DLADM_STATUS_OK)
346 		dladm_ether_info_done(eattr);
347 	return (status);
348 }
349 
350 extern void
351 dladm_ether_info_done(dladm_ether_info_t *eattr)
352 {
353 	int i;
354 
355 	for (i = CURRENT; i <= PEERADV; i++)
356 		free(eattr->lei_attr[i].le_spdx);
357 }
358