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 2008 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 = i_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