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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2015 Garrett D'Amore <garret@damore.org> 26 */ 27 28 #include <sys/types.h> 29 #include <sys/kstat.h> 30 #include <sys/mac.h> 31 #include <sys/dls.h> 32 #include <sys/softmac_impl.h> 33 34 typedef struct i_softmac_stat_info_s { 35 uint_t ssi_stat; 36 char *ssi_name; 37 char *ssi_alias; 38 } i_softmac_stat_info_t; 39 40 /* 41 * Must be the same order as mac_driver_stat. 42 */ 43 static i_softmac_stat_info_t i_softmac_driver_si[] = { 44 { MAC_STAT_IFSPEED, "ifspeed", "link_speed" }, 45 { MAC_STAT_MULTIRCV, "multircv", NULL }, 46 { MAC_STAT_BRDCSTRCV, "brdcstrcv", NULL }, 47 { MAC_STAT_MULTIXMT, "multixmt", NULL }, 48 { MAC_STAT_BRDCSTXMT, "brdcstxmt", NULL }, 49 { MAC_STAT_NORCVBUF, "norcvbuf", "rx_no_buf" }, 50 { MAC_STAT_IERRORS, "ierrors", NULL }, 51 { MAC_STAT_UNKNOWNS, "unknowns", NULL }, 52 { MAC_STAT_NOXMTBUF, "noxmtbuf", "No Txpkt " }, 53 { MAC_STAT_OERRORS, "oerrors", NULL }, 54 { MAC_STAT_COLLISIONS, "collisions", NULL }, 55 { MAC_STAT_RBYTES, "rbytes64", "rbytes" }, 56 { MAC_STAT_IPACKETS, "ipackets64", "ipackets" }, 57 { MAC_STAT_OBYTES, "obytes64", "obytes" }, 58 { MAC_STAT_OPACKETS, "opackets64", "opackets" }, 59 { MAC_STAT_UNDERFLOWS, "uflo", NULL }, 60 { MAC_STAT_OVERFLOWS, "oflo", NULL } 61 }; 62 63 #define SOFTMAC_DRIVER_SI_SZ \ 64 (sizeof (i_softmac_driver_si) / sizeof (i_softmac_driver_si[0])) 65 66 /* 67 * Must be the same order as ether_stat. 68 */ 69 static i_softmac_stat_info_t i_softmac_ether_si[] = { 70 { ETHER_STAT_ALIGN_ERRORS, "align_errors", 71 "alignment_err" }, 72 { ETHER_STAT_FCS_ERRORS, "fcs_errors", "crc_err" }, 73 { ETHER_STAT_FIRST_COLLISIONS, "first_collisions", NULL }, 74 { ETHER_STAT_MULTI_COLLISIONS, "multi_collisions", NULL }, 75 { ETHER_STAT_SQE_ERRORS, "sqe_errors", NULL }, 76 { ETHER_STAT_DEFER_XMTS, "defer_xmts", NULL }, 77 { ETHER_STAT_TX_LATE_COLLISIONS, "tx_late_collisions", 78 "late_collisions" }, 79 { ETHER_STAT_EX_COLLISIONS, "ex_collisions", 80 "excessive_collisions" }, 81 { ETHER_STAT_MACXMT_ERRORS, "macxmt_errors", NULL }, 82 { ETHER_STAT_CARRIER_ERRORS, "carrier_errors", NULL }, 83 { ETHER_STAT_TOOLONG_ERRORS, "toolong_errors", "length_err" }, 84 { ETHER_STAT_MACRCV_ERRORS, "macrcv_errors", 85 "Rx Error Count" }, 86 87 { ETHER_STAT_XCVR_ADDR, "xcvr_addr", NULL }, 88 { ETHER_STAT_XCVR_ID, "xcvr_id", NULL }, 89 { ETHER_STAT_XCVR_INUSE, "xcvr_inuse", NULL }, 90 91 { ETHER_STAT_CAP_1000FDX, "cap_1000fdx", NULL }, 92 { ETHER_STAT_CAP_1000HDX, "cap_1000hdx", NULL }, 93 { ETHER_STAT_CAP_100FDX, "cap_100fdx", NULL }, 94 { ETHER_STAT_CAP_100HDX, "cap_100hdx", NULL }, 95 { ETHER_STAT_CAP_10FDX, "cap_10fdx", NULL }, 96 { ETHER_STAT_CAP_10HDX, "cap_10hdx", NULL }, 97 { ETHER_STAT_CAP_ASMPAUSE, "cap_asmpause", NULL }, 98 { ETHER_STAT_CAP_PAUSE, "cap_pause", NULL }, 99 { ETHER_STAT_CAP_AUTONEG, "cap_autoneg", NULL }, 100 101 { ETHER_STAT_ADV_CAP_1000FDX, "adv_cap_1000fdx", NULL }, 102 { ETHER_STAT_ADV_CAP_1000HDX, "adv_cap_1000hdx", NULL }, 103 { ETHER_STAT_ADV_CAP_100FDX, "adv_cap_100fdx", NULL }, 104 { ETHER_STAT_ADV_CAP_100HDX, "adv_cap_100hdx", NULL }, 105 { ETHER_STAT_ADV_CAP_10FDX, "adv_cap_10fdx", NULL }, 106 { ETHER_STAT_ADV_CAP_10HDX, "adv_cap_10hdx", NULL }, 107 { ETHER_STAT_ADV_CAP_ASMPAUSE, "adv_cap_asmpause", NULL }, 108 { ETHER_STAT_ADV_CAP_PAUSE, "adv_cap_pause", NULL }, 109 { ETHER_STAT_ADV_CAP_AUTONEG, "adv_cap_autoneg", NULL }, 110 111 { ETHER_STAT_LP_CAP_1000FDX, "lp_cap_1000fdx", NULL }, 112 { ETHER_STAT_LP_CAP_1000HDX, "lp_cap_1000hdx", NULL }, 113 { ETHER_STAT_LP_CAP_100FDX, "lp_cap_100fdx", NULL }, 114 { ETHER_STAT_LP_CAP_100HDX, "lp_cap_100hdx", NULL }, 115 { ETHER_STAT_LP_CAP_10FDX, "lp_cap_10fdx", NULL }, 116 { ETHER_STAT_LP_CAP_10HDX, "lp_cap_10hdx", NULL }, 117 { ETHER_STAT_LP_CAP_ASMPAUSE, "lp_cap_asmpause", NULL }, 118 { ETHER_STAT_LP_CAP_PAUSE, "lp_cap_pause", NULL }, 119 { ETHER_STAT_LP_CAP_AUTONEG, "lp_cap_autoneg", NULL }, 120 121 { ETHER_STAT_LINK_ASMPAUSE, "link_asmpause", NULL }, 122 { ETHER_STAT_LINK_PAUSE, "link_pause", NULL }, 123 { ETHER_STAT_LINK_AUTONEG, "link_autoneg", NULL }, 124 { ETHER_STAT_LINK_DUPLEX, "link_duplex", "duplex" }, 125 126 { ETHER_STAT_TOOSHORT_ERRORS, "runt_errors", NULL }, 127 { ETHER_STAT_CAP_REMFAULT, "cap_rem_fault", NULL }, 128 { ETHER_STAT_ADV_REMFAULT, "adv_rem_fault", NULL }, 129 { ETHER_STAT_LP_REMFAULT, "lp_rem_fault", NULL }, 130 131 { ETHER_STAT_JABBER_ERRORS, "jabber_errors", NULL }, 132 { ETHER_STAT_CAP_100T4, "cap_100T4", NULL }, 133 { ETHER_STAT_ADV_CAP_100T4, "adv_cap_100T4", NULL }, 134 { ETHER_STAT_LP_CAP_100T4, "lp_cap_100T4", NULL }, 135 136 { ETHER_STAT_CAP_10GFDX, "cap_10gfdx", NULL }, 137 { ETHER_STAT_ADV_CAP_10GFDX, "adv_cap_10gfdx", NULL }, 138 { ETHER_STAT_LP_CAP_1000FDX, "lp_cap_10gfdx", NULL }, 139 140 { ETHER_STAT_CAP_40GFDX, "cap_40gfdx", NULL }, 141 { ETHER_STAT_ADV_CAP_40GFDX, "adv_cap_40gfdx", NULL }, 142 { ETHER_STAT_LP_CAP_40GFDX, "lp_cap_40gfdx", NULL }, 143 144 { ETHER_STAT_CAP_100GFDX, "cap_100gfdx", NULL }, 145 { ETHER_STAT_ADV_CAP_100GFDX, "adv_cap_100gfdx", NULL }, 146 { ETHER_STAT_LP_CAP_100GFDX, "lp_cap_100gfdx", NULL }, 147 148 { ETHER_STAT_CAP_2500FDX, "cap_2500fdx", NULL }, 149 { ETHER_STAT_ADV_CAP_2500FDX, "adv_cap_2500fdx", NULL }, 150 { ETHER_STAT_LP_CAP_2500FDX, "lp_cap_2500fdx", NULL }, 151 152 { ETHER_STAT_CAP_5000FDX, "cap_5000fdx", NULL }, 153 { ETHER_STAT_ADV_CAP_5000FDX, "adv_cap_5000fdx", NULL }, 154 { ETHER_STAT_LP_CAP_5000FDX, "lp_cap_5000fdx", NULL }, 155 }; 156 157 #define SOFTMAC_ETHER_SI_SZ \ 158 (sizeof (i_softmac_ether_si) / sizeof (i_softmac_ether_si[0])) 159 160 static kstat_t *softmac_hold_dev_kstat(softmac_t *); 161 static void softmac_rele_dev_kstat(kstat_t *); 162 static int softmac_get_kstat(kstat_t *, char *, uint64_t *); 163 164 static kstat_t * 165 softmac_hold_dev_kstat(softmac_t *softmac) 166 { 167 char drv[MAXLINKNAMELEN]; 168 uint_t ppa; 169 kstat_t *ksp; 170 171 if (ddi_parse(softmac->smac_devname, drv, &ppa) != DDI_SUCCESS) 172 return (NULL); 173 174 /* 175 * Find the kstat by the module name and the instance number. 176 */ 177 ksp = kstat_hold_byname(drv, ppa, softmac->smac_devname, ALL_ZONES); 178 if (ksp != NULL) { 179 KSTAT_ENTER(ksp); 180 181 if ((ksp->ks_data != NULL) && 182 (ksp->ks_type == KSTAT_TYPE_NAMED)) { 183 /* 184 * Update the kstat to get the latest statistics. 185 */ 186 if (KSTAT_UPDATE(ksp, KSTAT_READ) == 0) 187 return (ksp); 188 } 189 190 KSTAT_EXIT(ksp); 191 kstat_rele(ksp); 192 } 193 return (NULL); 194 } 195 196 static void 197 softmac_rele_dev_kstat(kstat_t *ksp) 198 { 199 KSTAT_EXIT(ksp); 200 kstat_rele(ksp); 201 } 202 203 /* 204 * The kstat needs to be held when calling this function. 205 */ 206 static int 207 softmac_get_kstat(kstat_t *ksp, char *name, uint64_t *valp) 208 { 209 kstat_named_t *knp; 210 int i; 211 int ret = ENOTSUP; 212 213 if (name == NULL) 214 return (ret); 215 216 /* 217 * Search the kstat with the given name. 218 */ 219 for (i = 0, knp = KSTAT_NAMED_PTR(ksp); i < ksp->ks_ndata; i++, knp++) { 220 if (strcmp(knp->name, name) == 0) { 221 switch (knp->data_type) { 222 case KSTAT_DATA_INT32: 223 case KSTAT_DATA_UINT32: 224 *valp = (uint64_t)(knp->value.ui32); 225 ret = 0; 226 break; 227 case KSTAT_DATA_INT64: 228 case KSTAT_DATA_UINT64: 229 *valp = knp->value.ui64; 230 ret = 0; 231 break; 232 #ifdef _LP64 233 case KSTAT_DATA_LONG: 234 case KSTAT_DATA_ULONG: 235 *valp = (uint64_t)knp->value.ul; 236 ret = 0; 237 break; 238 #endif 239 case KSTAT_DATA_CHAR: 240 if (strcmp(name, "duplex") != 0) 241 break; 242 if (strncmp(knp->value.c, "full", 4) == 0) 243 *valp = LINK_DUPLEX_FULL; 244 else if (strncmp(knp->value.c, "half", 4) == 0) 245 *valp = LINK_DUPLEX_HALF; 246 else 247 *valp = LINK_DUPLEX_UNKNOWN; 248 ret = 0; 249 break; 250 } 251 break; 252 } 253 } 254 255 return (ret); 256 } 257 258 int 259 softmac_m_stat(void *arg, uint_t stat, uint64_t *val) 260 { 261 softmac_t *softmac = arg; 262 kstat_t *ksp; 263 uint_t index; 264 int ret; 265 266 if ((ksp = softmac_hold_dev_kstat(softmac)) == NULL) 267 return (ENOTSUP); 268 269 if (IS_MAC_STAT(stat)) { 270 i_softmac_stat_info_t *ssip = NULL; 271 272 for (index = 0; index < SOFTMAC_DRIVER_SI_SZ; index++) { 273 if (stat == i_softmac_driver_si[index].ssi_stat) { 274 ssip = &i_softmac_driver_si[index]; 275 break; 276 } 277 } 278 279 if (ssip == NULL) { 280 ret = ENOTSUP; 281 } else { 282 if ((ret = softmac_get_kstat(ksp, ssip->ssi_name, 283 val)) != 0) 284 ret = softmac_get_kstat(ksp, ssip->ssi_alias, 285 val); 286 } 287 } else { 288 ASSERT(IS_MACTYPE_STAT(stat)); 289 290 switch (softmac->smac_media) { 291 case DL_ETHER: { 292 i_softmac_stat_info_t *ssip = NULL; 293 294 for (index = 0; index < SOFTMAC_ETHER_SI_SZ; index++) { 295 if (stat == 296 i_softmac_ether_si[index].ssi_stat) { 297 ssip = &i_softmac_ether_si[index]; 298 break; 299 } 300 } 301 302 if (ssip == NULL) { 303 ret = ENOTSUP; 304 } else { 305 if ((ret = softmac_get_kstat(ksp, 306 ssip->ssi_name, val)) != 0) 307 ret = softmac_get_kstat(ksp, 308 ssip->ssi_alias, val); 309 } 310 311 break; 312 } 313 default: 314 ret = ENOTSUP; 315 break; 316 } 317 } 318 319 softmac_rele_dev_kstat(ksp); 320 return (ret); 321 } 322