6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / lib / libdladm / common / libdlether.c
blob6f9d1080f754743cfd434210761ec7a1f9f1cc61
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Copyright 2015 Garrett D'Amore <garrett@damore.org>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <sys/types.h>
32 #include <libdladm_impl.h>
33 #include <libdllink.h>
34 #include <libdlstat.h>
35 #include <libdlether.h>
38 * Ethernet administration library.
42 * kstat names for extracting attributes.
44 typedef struct ether_spdx_s {
45 dladm_ether_spdx_t eth_spdx;
46 char *eth_spdx_stat_name;
47 } ether_spdx_t;
49 static ether_spdx_t cap_spdx[] = {
50 {{1000, LINK_DUPLEX_FULL}, "cap_1000fdx"},
51 {{1000, LINK_DUPLEX_HALF}, "cap_1000hdx"},
52 {{100, LINK_DUPLEX_FULL}, "cap_100fdx"},
53 {{100, LINK_DUPLEX_HALF}, "cap_100hdx"},
54 {{10, LINK_DUPLEX_FULL}, "cap_10fdx"},
55 {{10, LINK_DUPLEX_HALF}, "cap_10hdx"},
56 {{0, LINK_DUPLEX_UNKNOWN}, NULL}
59 static ether_spdx_t adv_cap_spdx[] = {
60 {{1000, LINK_DUPLEX_FULL}, "adv_cap_1000fdx"},
61 {{1000, LINK_DUPLEX_HALF}, "adv_cap_1000hdx"},
62 {{100, LINK_DUPLEX_FULL}, "adv_cap_100fdx"},
63 {{100, LINK_DUPLEX_HALF}, "adv_cap_100hdx"},
64 {{10, LINK_DUPLEX_FULL}, "adv_cap_10fdx"},
65 {{10, LINK_DUPLEX_HALF}, "adv_cap_10hdx"},
66 {{0, LINK_DUPLEX_UNKNOWN}, NULL}
69 static ether_spdx_t lp_cap_spdx[] = {
70 {{1000, LINK_DUPLEX_FULL}, "lp_cap_1000fdx"},
71 {{1000, LINK_DUPLEX_HALF}, "lp_cap_1000hdx"},
72 {{100, LINK_DUPLEX_FULL}, "lp_cap_100fdx"},
73 {{100, LINK_DUPLEX_HALF}, "lp_cap_100hdx"},
74 {{10, LINK_DUPLEX_FULL}, "lp_cap_10fdx"},
75 {{10, LINK_DUPLEX_HALF}, "lp_cap_10hdx"},
76 {{0, LINK_DUPLEX_UNKNOWN}, NULL}
79 typedef struct attr_kstat_s {
80 char *autoneg_stat;
81 char *pause_stat;
82 char *asmpause_stat;
83 char *fault_stat;
84 ether_spdx_t *spdx_stat;
85 } attr_kstat_t;
87 static attr_kstat_t attrstat[] = {
88 {"link_autoneg", /* current */
89 "link_pause", "link_asmpause", NULL,
90 NULL},
92 {"cap_autoneg", /* capable */
93 "cap_pause", "cap_asmpause", "cap_rem_fault",
94 cap_spdx},
96 {"adv_cap_autoneg", /* advertised */
97 "adv_cap_pause", "adv_cap_asmpause", "adv_rem_fault",
98 adv_cap_spdx},
100 {"lp_cap_autoneg", /* peer advertised */
101 "lp_cap_pause", "lp_cap_asmpause", "lp_rem_fault",
102 lp_cap_spdx}
106 * Get the speed-duplex stats specified in the ether_spdx_t table passed in
107 * by querying the appropriate kstat for each entry in the table.
109 static dladm_status_t
110 i_dladm_get_spdx(dladm_handle_t handle, datalink_id_t linkid,
111 dladm_ether_attr_t *eattr, ether_spdx_t *spdx_stat)
113 int i, nspdx = 0;
114 uint32_t speed;
115 dladm_status_t status;
116 void *ptr;
118 eattr->le_spdx = NULL;
119 for (i = 0; spdx_stat[i].eth_spdx_stat_name != NULL; i++) {
120 if ((status = dladm_get_single_mac_stat(handle, linkid,
121 spdx_stat[i].eth_spdx_stat_name,
122 KSTAT_DATA_UINT32, &speed)) != DLADM_STATUS_OK) {
124 if (status == DLADM_STATUS_NOTFOUND) {
126 * Missing statistic.
127 * Skip this one and try the rest.
129 continue;
130 } else {
131 free(eattr->le_spdx);
132 eattr->le_num_spdx = 0;
133 return (status);
136 if (speed == 0)
137 continue;
138 nspdx++;
139 ptr = realloc(eattr->le_spdx,
140 nspdx * sizeof (dladm_ether_spdx_t));
141 if (ptr != NULL) {
142 eattr->le_spdx = ptr;
143 } else {
144 free(eattr->le_spdx);
145 eattr->le_num_spdx = 0;
146 return (DLADM_STATUS_NOMEM);
148 eattr->le_spdx[nspdx - 1] = spdx_stat[i].eth_spdx;
150 eattr->le_num_spdx = nspdx;
151 return (DLADM_STATUS_OK);
155 * Returns "yes" or "no" based on the autonegotion capabilities
156 * for the parameter type indicated by ptype. The permissible
157 * values for ptype are CURRENT, CAPABLE, ADV, PEERADV.
159 char *
160 dladm_ether_autoneg2str(char *buf, size_t buflen, dladm_ether_info_t *eattr,
161 int ptype)
163 boolean_t autoneg = eattr->lei_attr[ptype].le_autoneg;
165 (void) strlcpy(buf, (autoneg ? "yes" : "no"), buflen);
166 return (buf);
170 * Returns {"bi", "tx", "none"} based on the flow-control capabilities
171 * for the parameter type indicated by ptype. The permissible
172 * values for ptype are CURRENT, CAPABLE, ADV, PEERADV.
174 char *
175 dladm_ether_pause2str(char *buf, size_t buflen, dladm_ether_info_t *eattr,
176 int ptype)
178 boolean_t pause = eattr->lei_attr[ptype].le_pause;
179 boolean_t asmpause = eattr->lei_attr[ptype].le_asmpause;
181 if (pause)
182 (void) strlcpy(buf, "bi", buflen);
183 else if (asmpause)
184 (void) strlcpy(buf, "tx", buflen);
185 else
186 (void) strlcpy(buf, "none", buflen);
187 return (buf);
191 * For a given param type, parse the list of speed-duplex pairs in
192 * the dladm_ether_info_t and return a comma-separated string formatted
193 * as <speed><speed-unit-char>-<duplex-chars> where <speed> is the value of
194 * speed, in units specifid by the <speed-unit-char> which is one
195 * of 'M' (Mbits/sec) or 'G' (Gigabits/sec). The permissible values of
196 * <duplex-chars> are 'u' (indicating duplex is "unknown") or one/both of
197 * 'f', 'h' (indicating full-duplex and half-duplex respectively)
199 extern char *
200 dladm_ether_spdx2str(char *buf, size_t buflen, dladm_ether_info_t *eattr,
201 int ptype)
203 int i, j;
204 boolean_t is_full, is_half;
205 int speed;
206 char speed_unit;
207 char tmpbuf[DLADM_STRSIZE];
208 dladm_ether_spdx_t *spdx;
209 uint32_t nspdx;
211 spdx = eattr->lei_attr[ptype].le_spdx;
212 nspdx = eattr->lei_attr[ptype].le_num_spdx;
213 for (i = 0; i < nspdx; i++) {
215 speed = spdx[i].lesd_speed;
218 * if we have already covered this speed for
219 * the <other>-duplex case before this, skip it
221 for (j = 0; j < i; j++) {
222 if (speed == spdx[j].lesd_speed)
223 break;
225 if (j < i)
226 continue;
228 if ((speed % 1000) == 0) {
229 speed = speed/1000;
230 speed_unit = 'G';
231 } else {
232 speed_unit = 'M';
234 (void) snprintf(tmpbuf, DLADM_STRSIZE, "%d%c",
235 speed, speed_unit);
236 if (i > 0)
237 (void) strncat(buf, ",", buflen);
238 (void) strncat(buf, tmpbuf, buflen);
240 is_full = is_half = B_FALSE;
242 * Find all the supported duplex values for this speed.
244 for (j = 0; j < nspdx; j++) {
245 if (spdx[j].lesd_speed != spdx[i].lesd_speed)
246 continue;
247 if (spdx[j].lesd_duplex == LINK_DUPLEX_FULL)
248 is_full = B_TRUE;
249 if (spdx[j].lesd_duplex == LINK_DUPLEX_HALF)
250 is_half = B_TRUE;
252 if (is_full && is_half)
253 (void) strncat(buf, "-fh", buflen);
254 else if (is_full)
255 (void) strncat(buf, "-f", buflen);
256 else if (is_half)
257 (void) strncat(buf, "-h", buflen);
259 return (buf);
263 * Extract Ethernet attributes of the link specified by linkid.
264 * Information for the CURRENT, CAPABLE, ADV and PEERADV parameter
265 * types is extracted into the lei_attr[] entries in the dladm_ether_info_t.
266 * On succesful return, the memory allocated in this function should be
267 * freed by calling dladm_ether_info_done().
269 extern dladm_status_t
270 dladm_ether_info(dladm_handle_t handle, datalink_id_t linkid,
271 dladm_ether_info_t *eattr)
273 uint32_t autoneg, pause, asmpause, fault;
274 uint64_t sp64;
275 dladm_status_t status;
276 int i;
277 link_duplex_t link_duplex;
279 bzero(eattr, sizeof (*eattr));
280 status = dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL,
281 eattr->lei_linkname, sizeof (eattr->lei_linkname));
282 if (status != DLADM_STATUS_OK)
283 goto bail;
285 /* get current values of speed, duplex, state of link */
286 eattr->lei_attr[CURRENT].le_num_spdx = 1;
287 eattr->lei_attr[CURRENT].le_spdx = malloc(sizeof (dladm_ether_spdx_t));
288 if (eattr->lei_attr[CURRENT].le_spdx == NULL) {
289 status = DLADM_STATUS_NOMEM;
290 goto bail;
293 if ((status = dladm_get_single_mac_stat(handle, linkid, "ifspeed",
294 KSTAT_DATA_UINT64, &sp64)) != DLADM_STATUS_OK)
295 goto bail;
297 if ((status = dladm_get_single_mac_stat(handle, linkid, "link_duplex",
298 KSTAT_DATA_UINT32, &link_duplex)) != DLADM_STATUS_OK)
299 goto bail;
301 eattr->lei_attr[CURRENT].le_spdx->lesd_speed = (int)(sp64/1000000ull);
302 eattr->lei_attr[CURRENT].le_spdx->lesd_duplex = link_duplex;
304 status = dladm_get_state(handle, linkid, &eattr->lei_state);
305 if (status != DLADM_STATUS_OK)
306 goto bail;
308 /* get the auto, pause, asmpause, fault values */
309 for (i = CURRENT; i <= PEERADV; i++) {
311 status = dladm_get_single_mac_stat(handle, linkid,
312 attrstat[i].autoneg_stat, KSTAT_DATA_UINT32, &autoneg);
313 if (status != DLADM_STATUS_OK)
314 goto bail;
316 status = dladm_get_single_mac_stat(handle, linkid,
317 attrstat[i].pause_stat, KSTAT_DATA_UINT32, &pause);
318 if (status != DLADM_STATUS_OK)
319 goto bail;
321 status = dladm_get_single_mac_stat(handle, linkid,
322 attrstat[i].asmpause_stat, KSTAT_DATA_UINT32, &asmpause);
323 if (status != DLADM_STATUS_OK)
324 goto bail;
326 eattr->lei_attr[i].le_autoneg = (autoneg != 0);
327 eattr->lei_attr[i].le_pause = (pause != 0);
328 eattr->lei_attr[i].le_asmpause = (asmpause != 0);
330 if (i == CURRENT)
331 continue;
332 status = dladm_get_single_mac_stat(handle, linkid,
333 attrstat[i].fault_stat, KSTAT_DATA_UINT32, &fault);
334 if (status != DLADM_STATUS_OK)
335 goto bail;
336 eattr->lei_attr[i].le_fault = (pause != 0);
338 /* get all the supported speed/duplex values */
339 status = i_dladm_get_spdx(handle, linkid, &eattr->lei_attr[i],
340 attrstat[i].spdx_stat);
341 if (status != DLADM_STATUS_OK)
342 goto bail;
344 eattr->lei_attr[CURRENT].le_fault =
345 eattr->lei_attr[ADV].le_fault || eattr->lei_attr[PEERADV].le_fault;
346 bail:
347 if (status != DLADM_STATUS_OK)
348 dladm_ether_info_done(eattr);
349 return (status);
352 extern void
353 dladm_ether_info_done(dladm_ether_info_t *eattr)
355 int i;
357 for (i = CURRENT; i <= PEERADV; i++)
358 free(eattr->lei_attr[i].le_spdx);