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
Sun_sasGetPhyStatistics(HBA_HANDLE handle,HBA_UINT32 port,HBA_UINT32 phy,SMHBA_PHYSTATISTICS * pStatistics)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