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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright 2019 Joyent, Inc. 28 */ 29 #include <kstat.h> 30 #include <sun_sas.h> 31 32 /* 33 * Retrieves the statistics for a specified port.phy on an adapter 34 */ 35 HBA_STATUS 36 Sun_sasGetPhyStatistics(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 phy, 37 SMHBA_PHYSTATISTICS *pStatistics) 38 { 39 const char ROUTINE[] = "Sun_sasGetPhyStatistics"; 40 HBA_STATUS status = HBA_STATUS_OK; 41 struct sun_sas_hba *hba_ptr; 42 struct sun_sas_port *hba_port_ptr; 43 struct phy_info *phy_ptr; 44 PSMHBA_SASPHYSTATISTICS psas; 45 kstat_ctl_t *kc; 46 kstat_t *ksp; 47 kstat_named_t *kname; 48 char *charptr, path[MAXPATHLEN + 1]; 49 char *driver_name, kstat_name[256]; 50 di_node_t node; 51 int instance = 0; 52 int i; 53 uint64_t iport_wwn; 54 55 /* Validate the arguments */ 56 if (pStatistics == NULL) { 57 log(LOG_DEBUG, ROUTINE, 58 "NULL Phy Statistics buffer of phyIndex: %08lx", phy); 59 return (HBA_STATUS_ERROR_ARG); 60 } 61 psas = pStatistics->SASPhyStatistics; 62 if (psas == NULL) { 63 log(LOG_DEBUG, ROUTINE, 64 "NULL SAS Phy Statistics buffer of phyIndex: %08lx", phy); 65 return (HBA_STATUS_ERROR_ARG); 66 } 67 68 lock(&all_hbas_lock); 69 70 if ((hba_ptr = Retrieve_Sun_sasHandle(handle)) == NULL) { 71 log(LOG_DEBUG, ROUTINE, 72 "Invalid HBA handler %08lx of phyIndex: %08lx", 73 handle, phy); 74 unlock(&all_hbas_lock); 75 return (HBA_STATUS_ERROR_INVALID_HANDLE); 76 } 77 78 /* Check for stale data */ 79 status = verifyAdapter(hba_ptr); 80 if (status != HBA_STATUS_OK) { 81 log(LOG_DEBUG, ROUTINE, 82 "Verify Adapter failed for phyIndex: %08lx", phy); 83 unlock(&all_hbas_lock); 84 return (status); 85 } 86 87 for (hba_port_ptr = hba_ptr->first_port; 88 hba_port_ptr != NULL; 89 hba_port_ptr = hba_port_ptr->next) { 90 if (hba_port_ptr->index == port) { 91 break; 92 } 93 } 94 95 if (hba_port_ptr == NULL) { 96 log(LOG_DEBUG, ROUTINE, 97 "Invalid port index of phyIndex: %08lx", phy); 98 unlock(&all_hbas_lock); 99 return (HBA_STATUS_ERROR_ILLEGAL_INDEX); 100 } 101 102 if (phy >= hba_port_ptr->port_attributes.PortSpecificAttribute. 103 SASPort->NumberofPhys) { 104 log(LOG_DEBUG, ROUTINE, "Invalid phy index %08lx", phy); 105 unlock(&all_hbas_lock); 106 return (HBA_STATUS_ERROR_ILLEGAL_INDEX); 107 } 108 109 /* We need to find out the phy identifier. */ 110 for (phy_ptr = hba_port_ptr->first_phy; 111 phy_ptr != NULL; 112 phy_ptr = phy_ptr->next) { 113 if (phy == phy_ptr->index) 114 break; 115 } 116 117 if (phy_ptr == NULL) { 118 log(LOG_DEBUG, ROUTINE, "Invalid phy index %08lx", phy); 119 unlock(&all_hbas_lock); 120 return (HBA_STATUS_ERROR_ILLEGAL_INDEX); 121 } 122 123 /* 124 * for statistics that are not supported, its bits should all be 125 * set to -1 126 */ 127 (void) memset(pStatistics->SASPhyStatistics, 0xff, 128 sizeof (SMHBA_SASPHYSTATISTICS)); 129 130 131 /* First, we need the deivce path to locate the devinfo node. */ 132 (void) strlcpy(path, hba_port_ptr->device_path, 133 sizeof (path)); 134 charptr = strrchr(path, ':'); 135 if (charptr) { 136 *charptr = '\0'; 137 } 138 139 errno = 0; 140 141 (void *) memset(kstat_name, 0, sizeof (kstat_name)); 142 node = di_init(path, DINFOCPYONE); 143 if (node == DI_NODE_NIL) { 144 di_fini(node); 145 log(LOG_DEBUG, ROUTINE, 146 "Unable to take devinfo snapshot on HBA \"%s\" " 147 "for phyIndex: %08lx due to %s", 148 path, phy, strerror(errno)); 149 unlock(&all_hbas_lock); 150 return (HBA_STATUS_ERROR); 151 } 152 153 /* 154 * Then we could fetch the instance number and driver name of this 155 * device. 156 */ 157 instance = di_instance(node); 158 if (instance == -1) { 159 di_fini(node); 160 log(LOG_DEBUG, ROUTINE, 161 "An instance number has not been assigned to the " 162 "device \"%s\" when get phyIndex: %08lx", path, phy); 163 unlock(&all_hbas_lock); 164 return (HBA_STATUS_ERROR); 165 } 166 167 driver_name = di_driver_name(node); 168 if (driver_name == NULL) { 169 di_fini(node); 170 log(LOG_DEBUG, ROUTINE, 171 "No driver bound to this device \"%s\" " 172 "when get phyIndex: %08lx", 173 path, phy); 174 unlock(&all_hbas_lock); 175 return (HBA_STATUS_ERROR); 176 } 177 178 di_fini(node); 179 180 iport_wwn = wwnConversion(hba_port_ptr->port_attributes.\ 181 PortSpecificAttribute.SASPort->LocalSASAddress.wwn); 182 183 /* 184 * Construct the kstat name here. 185 */ 186 (void) snprintf(kstat_name, sizeof (kstat_name), "%s.%016llx.%u.%u", 187 driver_name, iport_wwn, instance, phy_ptr->phy.PhyIdentifier); 188 189 /* retrieve all the statistics from kstat. */ 190 kc = kstat_open(); 191 if (kc == NULL) { 192 log(LOG_DEBUG, ROUTINE, 193 "kstat_open failed due to \"%s\" of phyIndex: %08lx", 194 strerror(errno), phy); 195 unlock(&all_hbas_lock); 196 return (HBA_STATUS_ERROR); 197 } 198 ksp = kstat_lookup(kc, NULL, -1, kstat_name); 199 if (ksp == NULL) { 200 log(LOG_DEBUG, ROUTINE, 201 "No matching kstat name found for \"%s\" " 202 "of phyIndex: %08lx", 203 kstat_name, phy); 204 unlock(&all_hbas_lock); 205 (void) kstat_close(kc); 206 return (HBA_STATUS_ERROR); 207 } 208 /* Found the phy we're looking for. */ 209 if (kstat_read(kc, ksp, NULL) == -1) { 210 log(LOG_DEBUG, ROUTINE, 211 "error reading kstat data due to \"%s\" " 212 "of phyIndex: %08lx", 213 strerror(errno), phy); 214 unlock(&all_hbas_lock); 215 (void) kstat_close(kc); 216 return (HBA_STATUS_ERROR); 217 } 218 219 kname = (kstat_named_t *)ksp->ks_data; 220 for (i = 0; i < ksp->ks_ndata; i++, kname++) { 221 if (strcmp(kname->name, 222 "SecondsSinceLastReset") == 0) { 223 psas->SecondsSinceLastReset = kname->value.ull; 224 continue; 225 } 226 if (strcmp(kname->name, "TxFrames") == 0) { 227 psas->TxFrames = kname->value.ull; 228 continue; 229 } 230 if (strcmp(kname->name, "RxFrames") == 0) { 231 psas->RxFrames = kname->value.ull; 232 continue; 233 } 234 if (strcmp(kname->name, "TxWords") == 0) { 235 psas->TxWords = kname->value.ull; 236 continue; 237 } 238 if (strcmp(kname->name, "RxWords") == 0) { 239 psas->RxWords = kname->value.ull; 240 continue; 241 } 242 if (strcmp(kname->name, "InvalidDwordCount") == 0) { 243 psas->InvalidDwordCount = kname->value.ull; 244 continue; 245 } 246 if (strcmp(kname->name, "RunningDisparityErrorCount") == 0) { 247 psas->RunningDisparityErrorCount = kname->value.ull; 248 continue; 249 } 250 if (strcmp(kname->name, "LossofDwordSyncCount") == 0) { 251 psas->LossofDwordSyncCount = kname->value.ull; 252 continue; 253 } 254 if (strcmp(kname->name, "PhyResetProblemCount") == 0) { 255 psas->PhyResetProblemCount = kname->value.ull; 256 } 257 } 258 unlock(&all_hbas_lock); 259 (void) kstat_close(kc); 260 261 return (HBA_STATUS_OK); 262 } 263