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