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]
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>
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
;
49 static ether_spdx_t cap_spdx
[] = {
50 {{100000, LINK_DUPLEX_FULL
}, "cap_100gfdx"},
51 {{50000, LINK_DUPLEX_FULL
}, "cap_50gfdx"},
52 {{40000, LINK_DUPLEX_FULL
}, "cap_40gfdx"},
53 {{25000, LINK_DUPLEX_FULL
}, "cap_25gfdx"},
54 {{10000, LINK_DUPLEX_FULL
}, "cap_10gfdx"},
55 {{5000, LINK_DUPLEX_FULL
}, "cap_5000fdx"},
56 {{2500, LINK_DUPLEX_FULL
}, "cap_2500fdx"},
57 {{1000, LINK_DUPLEX_FULL
}, "cap_1000fdx"},
58 {{1000, LINK_DUPLEX_HALF
}, "cap_1000hdx"},
59 {{100, LINK_DUPLEX_FULL
}, "cap_100fdx"},
60 {{100, LINK_DUPLEX_HALF
}, "cap_100hdx"},
61 {{10, LINK_DUPLEX_FULL
}, "cap_10fdx"},
62 {{10, LINK_DUPLEX_HALF
}, "cap_10hdx"},
63 {{0, LINK_DUPLEX_UNKNOWN
}, NULL
}
66 static ether_spdx_t adv_cap_spdx
[] = {
67 {{100000, LINK_DUPLEX_FULL
}, "adv_cap_100gfdx"},
68 {{50000, LINK_DUPLEX_FULL
}, "adv_cap_50gfdx"},
69 {{40000, LINK_DUPLEX_FULL
}, "adv_cap_40gfdx"},
70 {{25000, LINK_DUPLEX_FULL
}, "adv_cap_25gfdx"},
71 {{10000, LINK_DUPLEX_FULL
}, "adv_cap_10gfdx"},
72 {{5000, LINK_DUPLEX_FULL
}, "adv_cap_5000fdx"},
73 {{2500, LINK_DUPLEX_FULL
}, "adv_cap_2500fdx"},
74 {{1000, LINK_DUPLEX_FULL
}, "adv_cap_1000fdx"},
75 {{1000, LINK_DUPLEX_HALF
}, "adv_cap_1000hdx"},
76 {{100, LINK_DUPLEX_FULL
}, "adv_cap_100fdx"},
77 {{100, LINK_DUPLEX_HALF
}, "adv_cap_100hdx"},
78 {{10, LINK_DUPLEX_FULL
}, "adv_cap_10fdx"},
79 {{10, LINK_DUPLEX_HALF
}, "adv_cap_10hdx"},
80 {{0, LINK_DUPLEX_UNKNOWN
}, NULL
}
83 static ether_spdx_t lp_cap_spdx
[] = {
84 {{100000, LINK_DUPLEX_FULL
}, "lp_cap_100gfdx"},
85 {{50000, LINK_DUPLEX_FULL
}, "lp_cap_50gfdx"},
86 {{40000, LINK_DUPLEX_FULL
}, "lp_cap_40gfdx"},
87 {{25000, LINK_DUPLEX_FULL
}, "lp_cap_25gfdx"},
88 {{10000, LINK_DUPLEX_FULL
}, "lp_cap_10gfdx"},
89 {{5000, LINK_DUPLEX_FULL
}, "lp_cap_5000fdx"},
90 {{2500, LINK_DUPLEX_FULL
}, "lp_cap_2500fdx"},
91 {{1000, LINK_DUPLEX_FULL
}, "lp_cap_1000fdx"},
92 {{1000, LINK_DUPLEX_HALF
}, "lp_cap_1000hdx"},
93 {{100, LINK_DUPLEX_FULL
}, "lp_cap_100fdx"},
94 {{100, LINK_DUPLEX_HALF
}, "lp_cap_100hdx"},
95 {{10, LINK_DUPLEX_FULL
}, "lp_cap_10fdx"},
96 {{10, LINK_DUPLEX_HALF
}, "lp_cap_10hdx"},
97 {{0, LINK_DUPLEX_UNKNOWN
}, NULL
}
100 typedef struct attr_kstat_s
{
105 ether_spdx_t
*spdx_stat
;
108 static attr_kstat_t attrstat
[] = {
109 {"link_autoneg", /* current */
110 "link_pause", "link_asmpause", NULL
,
113 {"cap_autoneg", /* capable */
114 "cap_pause", "cap_asmpause", "cap_rem_fault",
117 {"adv_cap_autoneg", /* advertised */
118 "adv_cap_pause", "adv_cap_asmpause", "adv_rem_fault",
121 {"lp_cap_autoneg", /* peer advertised */
122 "lp_cap_pause", "lp_cap_asmpause", "lp_rem_fault",
127 * Get the speed-duplex stats specified in the ether_spdx_t table passed in
128 * by querying the appropriate kstat for each entry in the table.
130 static dladm_status_t
131 i_dladm_get_spdx(dladm_handle_t handle
, datalink_id_t linkid
,
132 dladm_ether_attr_t
*eattr
, ether_spdx_t
*spdx_stat
)
136 dladm_status_t status
;
139 eattr
->le_spdx
= NULL
;
140 for (i
= 0; spdx_stat
[i
].eth_spdx_stat_name
!= NULL
; i
++) {
141 if ((status
= dladm_get_single_mac_stat(handle
, linkid
,
142 spdx_stat
[i
].eth_spdx_stat_name
,
143 KSTAT_DATA_UINT32
, &speed
)) != DLADM_STATUS_OK
) {
145 if (status
== DLADM_STATUS_NOTFOUND
) {
148 * Skip this one and try the rest.
152 free(eattr
->le_spdx
);
153 eattr
->le_num_spdx
= 0;
160 ptr
= reallocarray(eattr
->le_spdx
, nspdx
,
161 sizeof (dladm_ether_spdx_t
));
163 eattr
->le_spdx
= ptr
;
165 free(eattr
->le_spdx
);
166 eattr
->le_num_spdx
= 0;
167 return (DLADM_STATUS_NOMEM
);
169 eattr
->le_spdx
[nspdx
- 1] = spdx_stat
[i
].eth_spdx
;
171 eattr
->le_num_spdx
= nspdx
;
172 return (DLADM_STATUS_OK
);
176 * Returns "yes" or "no" based on the autonegotion capabilities
177 * for the parameter type indicated by ptype. The permissible
178 * values for ptype are CURRENT, CAPABLE, ADV, PEERADV.
181 dladm_ether_autoneg2str(char *buf
, size_t buflen
, dladm_ether_info_t
*eattr
,
184 boolean_t autoneg
= eattr
->lei_attr
[ptype
].le_autoneg
;
186 (void) strlcpy(buf
, (autoneg
? "yes" : "no"), buflen
);
191 * Returns {"bi", "tx", "none"} based on the flow-control capabilities
192 * for the parameter type indicated by ptype. The permissible
193 * values for ptype are CURRENT, CAPABLE, ADV, PEERADV.
196 dladm_ether_pause2str(char *buf
, size_t buflen
, dladm_ether_info_t
*eattr
,
199 boolean_t pause
= eattr
->lei_attr
[ptype
].le_pause
;
200 boolean_t asmpause
= eattr
->lei_attr
[ptype
].le_asmpause
;
203 (void) strlcpy(buf
, "bi", buflen
);
205 (void) strlcpy(buf
, "tx", buflen
);
207 (void) strlcpy(buf
, "none", buflen
);
212 * For a given param type, parse the list of speed-duplex pairs in
213 * the dladm_ether_info_t and return a comma-separated string formatted
214 * as <speed><speed-unit-char>-<duplex-chars> where <speed> is the value of
215 * speed, in units specifid by the <speed-unit-char> which is one
216 * of 'M' (Mbits/sec) or 'G' (Gigabits/sec). The permissible values of
217 * <duplex-chars> are 'u' (indicating duplex is "unknown") or one/both of
218 * 'f', 'h' (indicating full-duplex and half-duplex respectively)
221 dladm_ether_spdx2str(char *buf
, size_t buflen
, dladm_ether_info_t
*eattr
,
225 boolean_t is_full
, is_half
;
228 char tmpbuf
[DLADM_STRSIZE
];
229 dladm_ether_spdx_t
*spdx
;
232 spdx
= eattr
->lei_attr
[ptype
].le_spdx
;
233 nspdx
= eattr
->lei_attr
[ptype
].le_num_spdx
;
234 for (i
= 0; i
< nspdx
; i
++) {
236 speed
= spdx
[i
].lesd_speed
;
239 * if we have already covered this speed for
240 * the <other>-duplex case before this, skip it
242 for (j
= 0; j
< i
; j
++) {
243 if (speed
== spdx
[j
].lesd_speed
)
249 if ((speed
% 1000) == 0) {
255 (void) snprintf(tmpbuf
, DLADM_STRSIZE
, "%d%c",
258 (void) strncat(buf
, ",", buflen
);
259 (void) strncat(buf
, tmpbuf
, buflen
);
261 is_full
= is_half
= B_FALSE
;
263 * Find all the supported duplex values for this speed.
265 for (j
= 0; j
< nspdx
; j
++) {
266 if (spdx
[j
].lesd_speed
!= spdx
[i
].lesd_speed
)
268 if (spdx
[j
].lesd_duplex
== LINK_DUPLEX_FULL
)
270 if (spdx
[j
].lesd_duplex
== LINK_DUPLEX_HALF
)
273 if (is_full
&& is_half
)
274 (void) strncat(buf
, "-fh", buflen
);
276 (void) strncat(buf
, "-f", buflen
);
278 (void) strncat(buf
, "-h", buflen
);
284 * Extract Ethernet attributes of the link specified by linkid.
285 * Information for the CURRENT, CAPABLE, ADV and PEERADV parameter
286 * types is extracted into the lei_attr[] entries in the dladm_ether_info_t.
287 * On succesful return, the memory allocated in this function should be
288 * freed by calling dladm_ether_info_done().
290 extern dladm_status_t
291 dladm_ether_info(dladm_handle_t handle
, datalink_id_t linkid
,
292 dladm_ether_info_t
*eattr
)
294 uint32_t autoneg
, pause
, asmpause
, fault
;
296 dladm_status_t status
;
298 link_duplex_t link_duplex
;
300 bzero(eattr
, sizeof (*eattr
));
301 status
= dladm_datalink_id2info(handle
, linkid
, NULL
, NULL
, NULL
,
302 eattr
->lei_linkname
, sizeof (eattr
->lei_linkname
));
303 if (status
!= DLADM_STATUS_OK
)
306 /* get current values of speed, duplex, state of link */
307 eattr
->lei_attr
[CURRENT
].le_num_spdx
= 1;
308 eattr
->lei_attr
[CURRENT
].le_spdx
= malloc(sizeof (dladm_ether_spdx_t
));
309 if (eattr
->lei_attr
[CURRENT
].le_spdx
== NULL
) {
310 status
= DLADM_STATUS_NOMEM
;
314 if ((status
= dladm_get_single_mac_stat(handle
, linkid
, "ifspeed",
315 KSTAT_DATA_UINT64
, &sp64
)) != DLADM_STATUS_OK
)
318 if ((status
= dladm_get_single_mac_stat(handle
, linkid
, "link_duplex",
319 KSTAT_DATA_UINT32
, &link_duplex
)) != DLADM_STATUS_OK
)
322 eattr
->lei_attr
[CURRENT
].le_spdx
->lesd_speed
= (int)(sp64
/1000000ull);
323 eattr
->lei_attr
[CURRENT
].le_spdx
->lesd_duplex
= link_duplex
;
325 status
= dladm_get_state(handle
, linkid
, &eattr
->lei_state
);
326 if (status
!= DLADM_STATUS_OK
)
329 /* get the auto, pause, asmpause, fault values */
330 for (i
= CURRENT
; i
<= PEERADV
; i
++) {
332 status
= dladm_get_single_mac_stat(handle
, linkid
,
333 attrstat
[i
].autoneg_stat
, KSTAT_DATA_UINT32
, &autoneg
);
334 if (status
!= DLADM_STATUS_OK
)
337 status
= dladm_get_single_mac_stat(handle
, linkid
,
338 attrstat
[i
].pause_stat
, KSTAT_DATA_UINT32
, &pause
);
339 if (status
!= DLADM_STATUS_OK
)
342 status
= dladm_get_single_mac_stat(handle
, linkid
,
343 attrstat
[i
].asmpause_stat
, KSTAT_DATA_UINT32
, &asmpause
);
344 if (status
!= DLADM_STATUS_OK
)
347 eattr
->lei_attr
[i
].le_autoneg
= (autoneg
!= 0);
348 eattr
->lei_attr
[i
].le_pause
= (pause
!= 0);
349 eattr
->lei_attr
[i
].le_asmpause
= (asmpause
!= 0);
353 status
= dladm_get_single_mac_stat(handle
, linkid
,
354 attrstat
[i
].fault_stat
, KSTAT_DATA_UINT32
, &fault
);
355 if (status
!= DLADM_STATUS_OK
)
357 eattr
->lei_attr
[i
].le_fault
= (pause
!= 0);
359 /* get all the supported speed/duplex values */
360 status
= i_dladm_get_spdx(handle
, linkid
, &eattr
->lei_attr
[i
],
361 attrstat
[i
].spdx_stat
);
362 if (status
!= DLADM_STATUS_OK
)
365 eattr
->lei_attr
[CURRENT
].le_fault
=
366 eattr
->lei_attr
[ADV
].le_fault
|| eattr
->lei_attr
[PEERADV
].le_fault
;
368 if (status
!= DLADM_STATUS_OK
)
369 dladm_ether_info_done(eattr
);
374 dladm_ether_info_done(dladm_ether_info_t
*eattr
)
378 for (i
= CURRENT
; i
<= PEERADV
; i
++)
379 free(eattr
->lei_attr
[i
].le_spdx
);