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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 /*
26  * This file contains SM-HBA support for MPT SAS driver
27  */
28 
29 #if defined(lint) || defined(DEBUG)
30 #define	MPTSAS_DEBUG
31 #endif
32 
33 /*
34  * standard header files
35  */
36 #include <sys/note.h>
37 #include <sys/scsi/scsi.h>
38 #include <sys/pci.h>
39 
40 #pragma pack(1)
41 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_type.h>
42 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2.h>
43 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_cnfg.h>
44 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_init.h>
45 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_ioc.h>
46 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_sas.h>
47 #pragma pack()
48 
49 /*
50  * private header files.
51  */
52 #include <sys/scsi/adapters/mpt_sas/mptsas_var.h>
53 #include <sys/scsi/adapters/mpt_sas/mptsas_smhba.h>
54 
55 void
56 mptsas_smhba_add_hba_prop(mptsas_t *mpt, data_type_t dt,
57     char *prop_name, void *prop_val);
58 
59 void
60 mptsas_smhba_show_phy_info(mptsas_t *mpt);
61 
62 void
63 mptsas_smhba_add_hba_prop(mptsas_t *mpt, data_type_t dt,
64     char *prop_name, void *prop_val)
65 {
66 	ASSERT(mpt != NULL);
67 
68 	switch (dt) {
69 	case DATA_TYPE_INT32:
70 		if (ddi_prop_update_int(DDI_DEV_T_NONE, mpt->m_dip,
71 		    prop_name, *(int *)prop_val)) {
72 			mptsas_log(mpt, CE_WARN,
73 			    "%s: %s prop update failed", __func__, prop_name);
74 		}
75 		break;
76 	case DATA_TYPE_STRING:
77 		if (ddi_prop_update_string(DDI_DEV_T_NONE, mpt->m_dip,
78 		    prop_name, (char *)prop_val)) {
79 			mptsas_log(mpt, CE_WARN,
80 			    "%s: %s prop update failed", __func__, prop_name);
81 		}
82 		break;
83 	default:
84 		mptsas_log(mpt, CE_WARN, "%s: "
85 		    "Unhandled datatype(%d) for (%s). Skipping prop update.",
86 		    __func__, dt, prop_name);
87 	}
88 }
89 
90 void
91 mptsas_smhba_show_phy_info(mptsas_t *mpt)
92 {
93 	int i;
94 
95 	ASSERT(mpt != NULL);
96 
97 	for (i = 0; i < MPTSAS_MAX_PHYS; i++) {
98 		mptsas_log(mpt, CE_WARN,
99 		    "phy %d, Owner hdl:0x%x, attached hdl: 0x%x,"
100 		    "attached phy identifier %d,Program link rate 0x%x,"
101 		    "hw link rate 0x%x, negotiator link rate 0x%x, path %s",
102 		    i, mpt->m_phy_info[i].smhba_info.owner_devhdl,
103 		    mpt->m_phy_info[i].smhba_info.attached_devhdl,
104 		    mpt->m_phy_info[i].smhba_info.attached_phy_identify,
105 		    mpt->m_phy_info[i].smhba_info.programmed_link_rate,
106 		    mpt->m_phy_info[i].smhba_info.hw_link_rate,
107 		    mpt->m_phy_info[i].smhba_info.negotiated_link_rate,
108 		    mpt->m_phy_info[i].smhba_info.path);
109 	}
110 }
111 
112 void
113 mptsas_smhba_set_phy_props(mptsas_t *mpt, char *iport, dev_info_t *dip,
114     uint8_t phy_nums, uint16_t *attached_devhdl)
115 {
116 	int		i;
117 	int		j = 0;
118 	int		rval;
119 	size_t		packed_size;
120 	char		*packed_data = NULL;
121 	char		phymask[MPTSAS_MAX_PHYS];
122 	nvlist_t	**phy_props;
123 	nvlist_t	*nvl;
124 	smhba_info_t	*pSmhba = NULL;
125 
126 	if (phy_nums == 0) {
127 		return;
128 	}
129 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
130 		mptsas_log(mpt, CE_WARN, "%s: nvlist_alloc() failed", __func__);
131 	}
132 
133 	phy_props = kmem_zalloc(sizeof (nvlist_t *) * phy_nums,
134 	    KM_SLEEP);
135 
136 	for (i = 0; i < mpt->m_num_phys; i++) {
137 
138 		bzero(phymask, sizeof (phymask));
139 		(void) sprintf(phymask, "%x", mpt->m_phy_info[i].phy_mask);
140 		if (strcmp(phymask, iport) == 0) {
141 			pSmhba = &mpt->m_phy_info[i].smhba_info;
142 			(void) nvlist_alloc(&phy_props[j], NV_UNIQUE_NAME, 0);
143 			(void) nvlist_add_uint8(phy_props[j], SAS_PHY_ID, i);
144 			(void) nvlist_add_uint8(phy_props[j],
145 			    "phyState",
146 			    (pSmhba->negotiated_link_rate
147 			    & 0x0f));
148 			(void) nvlist_add_int8(phy_props[j],
149 			    SAS_NEG_LINK_RATE,
150 			    (pSmhba->negotiated_link_rate
151 			    & 0x0f));
152 			(void) nvlist_add_int8(phy_props[j],
153 			    SAS_PROG_MIN_LINK_RATE,
154 			    (pSmhba->programmed_link_rate
155 			    & 0x0f));
156 			(void) nvlist_add_int8(phy_props[j],
157 			    SAS_HW_MIN_LINK_RATE,
158 			    (pSmhba->hw_link_rate
159 			    & 0x0f));
160 			(void) nvlist_add_int8(phy_props[j],
161 			    SAS_PROG_MAX_LINK_RATE,
162 			    ((pSmhba->programmed_link_rate
163 			    & 0xf0) >> 4));
164 			(void) nvlist_add_int8(phy_props[j],
165 			    SAS_HW_MAX_LINK_RATE,
166 			    ((pSmhba->hw_link_rate
167 			    & 0xf0) >> 4));
168 
169 			j++;
170 
171 			if (pSmhba->attached_devhdl &&
172 			    (attached_devhdl != NULL)) {
173 				*attached_devhdl =
174 				    pSmhba->attached_devhdl;
175 			}
176 		}
177 	}
178 
179 	rval = nvlist_add_nvlist_array(nvl, SAS_PHY_INFO_NVL, phy_props,
180 	    phy_nums);
181 	if (rval) {
182 		mptsas_log(mpt, CE_WARN,
183 		    " nv list array add failed, return value %d.",
184 		    rval);
185 		goto exit;
186 	}
187 	(void) nvlist_size(nvl, &packed_size, NV_ENCODE_NATIVE);
188 	packed_data = kmem_zalloc(packed_size, KM_SLEEP);
189 	(void) nvlist_pack(nvl, &packed_data, &packed_size,
190 	    NV_ENCODE_NATIVE, 0);
191 
192 	(void) ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
193 	    SAS_PHY_INFO, (uchar_t *)packed_data, packed_size);
194 
195 exit:
196 	for (i = 0; i < phy_nums && phy_props[i] != NULL; i++) {
197 		nvlist_free(phy_props[i]);
198 	}
199 	nvlist_free(nvl);
200 	kmem_free(phy_props, sizeof (nvlist_t *) * phy_nums);
201 
202 	if (packed_data != NULL) {
203 		kmem_free(packed_data, packed_size);
204 	}
205 }
206 
207 /*
208  * Called with PHY lock held on phyp
209  */
210 void
211 mptsas_smhba_log_sysevent(mptsas_t *mpt, char *subclass, char *etype,
212     smhba_info_t *phyp)
213 {
214 	nvlist_t	*attr_list;
215 	char		*pname;
216 	char		sas_addr[MPTSAS_WWN_STRLEN];
217 	uint8_t		phynum = 0;
218 	uint8_t		lrate = 0;
219 
220 	if (mpt->m_dip == NULL)
221 		return;
222 	if (phyp == NULL)
223 		return;
224 
225 	pname = kmem_zalloc(MAXPATHLEN, KM_NOSLEEP);
226 	if (pname == NULL)
227 		return;
228 
229 	if ((strcmp(subclass, ESC_SAS_PHY_EVENT) == 0) ||
230 	    (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0)) {
231 		ASSERT(phyp != NULL);
232 		(void) strncpy(pname, phyp->path, strlen(phyp->path));
233 		phynum = phyp->phy_id;
234 		bzero(sas_addr, sizeof (sas_addr));
235 		(void) sprintf(sas_addr, "w%016"PRIx64, phyp->sas_addr);
236 		if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
237 			lrate = phyp->negotiated_link_rate;
238 		}
239 	}
240 	if (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0) {
241 		(void) ddi_pathname(mpt->m_dip, pname);
242 	}
243 
244 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, 0) != 0) {
245 		mptsas_log(mpt, CE_WARN,
246 		    "%s: Failed to post sysevent", __func__);
247 		kmem_free(pname, MAXPATHLEN);
248 		return;
249 	}
250 
251 	if (nvlist_add_int32(attr_list, SAS_DRV_INST,
252 	    ddi_get_instance(mpt->m_dip)) != 0)
253 		goto fail;
254 
255 	if (nvlist_add_string(attr_list, SAS_PORT_ADDR, sas_addr) != 0)
256 		goto fail;
257 
258 	if (nvlist_add_string(attr_list, SAS_DEVFS_PATH, pname) != 0)
259 		goto fail;
260 
261 	if (nvlist_add_uint8(attr_list, SAS_PHY_ID, phynum) != 0)
262 		goto fail;
263 
264 	if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
265 		if (nvlist_add_uint8(attr_list, SAS_LINK_RATE, lrate) != 0)
266 			goto fail;
267 	}
268 
269 	if (nvlist_add_string(attr_list, SAS_EVENT_TYPE, etype) != 0)
270 		goto fail;
271 
272 	(void) ddi_log_sysevent(mpt->m_dip, DDI_VENDOR_SUNW, EC_HBA, subclass,
273 	    attr_list, NULL, DDI_NOSLEEP);
274 
275 fail:
276 	kmem_free(pname, MAXPATHLEN);
277 	nvlist_free(attr_list);
278 }
279 
280 void
281 mptsas_create_phy_stats(mptsas_t *mpt, char *iport, dev_info_t *dip)
282 {
283 	sas_phy_stats_t		*ps;
284 	smhba_info_t		*phyp;
285 	int			ndata;
286 	char			ks_name[KSTAT_STRLEN];
287 	char			phymask[MPTSAS_MAX_PHYS];
288 	int			i;
289 
290 	ASSERT(iport != NULL);
291 	ASSERT(mpt != NULL);
292 
293 	for (i = 0; i < mpt->m_num_phys; i++) {
294 
295 		bzero(phymask, sizeof (phymask));
296 		(void) sprintf(phymask, "%x", mpt->m_phy_info[i].phy_mask);
297 		if (strcmp(phymask, iport) == 0) {
298 
299 			phyp = &mpt->m_phy_info[i].smhba_info;
300 			mutex_enter(&phyp->phy_mutex);
301 
302 			if (phyp->phy_stats != NULL) {
303 				mutex_exit(&phyp->phy_mutex);
304 				/* We've already created this kstat instance */
305 				continue;
306 			}
307 
308 			ndata = (sizeof (sas_phy_stats_t)/
309 			    sizeof (kstat_named_t));
310 			(void) snprintf(ks_name, sizeof (ks_name),
311 			    "%s.%llx.%d.%d", ddi_driver_name(dip),
312 			    (longlong_t)mpt->un.m_base_wwid,
313 			    ddi_get_instance(dip), i);
314 
315 			phyp->phy_stats = kstat_create("mptsas",
316 			    ddi_get_instance(dip), ks_name, KSTAT_SAS_PHY_CLASS,
317 			    KSTAT_TYPE_NAMED, ndata, 0);
318 
319 			if (phyp->phy_stats == NULL) {
320 				mutex_exit(&phyp->phy_mutex);
321 				mptsas_log(mpt, CE_WARN,
322 				    "%s: Failed to create %s kstats", __func__,
323 				    ks_name);
324 				continue;
325 			}
326 
327 			ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data;
328 
329 			kstat_named_init(&ps->seconds_since_last_reset,
330 			    "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG);
331 			kstat_named_init(&ps->tx_frames,
332 			    "TxFrames", KSTAT_DATA_ULONGLONG);
333 			kstat_named_init(&ps->rx_frames,
334 			    "RxFrames", KSTAT_DATA_ULONGLONG);
335 			kstat_named_init(&ps->tx_words,
336 			    "TxWords", KSTAT_DATA_ULONGLONG);
337 			kstat_named_init(&ps->rx_words,
338 			    "RxWords", KSTAT_DATA_ULONGLONG);
339 			kstat_named_init(&ps->invalid_dword_count,
340 			    "InvalidDwordCount", KSTAT_DATA_ULONGLONG);
341 			kstat_named_init(&ps->running_disparity_error_count,
342 			    "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG);
343 			kstat_named_init(&ps->loss_of_dword_sync_count,
344 			    "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG);
345 			kstat_named_init(&ps->phy_reset_problem_count,
346 			    "PhyResetProblemCount", KSTAT_DATA_ULONGLONG);
347 
348 			phyp->phy_stats->ks_private = phyp;
349 			phyp->phy_stats->ks_update = mptsas_update_phy_stats;
350 			kstat_install(phyp->phy_stats);
351 			mutex_exit(&phyp->phy_mutex);
352 		}
353 	}
354 }
355 
356 int
357 mptsas_update_phy_stats(kstat_t *ks, int rw)
358 {
359 	int			ret = DDI_FAILURE;
360 	smhba_info_t		*pptr = NULL;
361 	sas_phy_stats_t		*ps = ks->ks_data;
362 	uint32_t 		page_address;
363 	mptsas_t 		*mpt;
364 
365 	_NOTE(ARGUNUSED(rw));
366 
367 	pptr = (smhba_info_t *)ks->ks_private;
368 	ASSERT((pptr != NULL));
369 	mpt = (mptsas_t *)pptr->mpt;
370 	ASSERT((mpt != NULL));
371 	page_address = (MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | pptr->phy_id);
372 
373 	/*
374 	 * We just want to lock against other invocations of kstat;
375 	 * we don't need to pmcs_lock_phy() for this.
376 	 */
377 	mutex_enter(&mpt->m_mutex);
378 
379 	ret = mptsas_get_sas_phy_page1(pptr->mpt, page_address, pptr);
380 
381 	if (ret == DDI_FAILURE)
382 		goto fail;
383 
384 	ps->invalid_dword_count.value.ull =
385 	    (unsigned long long)pptr->invalid_dword_count;
386 
387 	ps->running_disparity_error_count.value.ull =
388 	    (unsigned long long)pptr->running_disparity_error_count;
389 
390 	ps->loss_of_dword_sync_count.value.ull =
391 	    (unsigned long long)pptr->loss_of_dword_sync_count;
392 
393 	ps->phy_reset_problem_count.value.ull =
394 	    (unsigned long long)pptr->phy_reset_problem_count;
395 
396 	ret = DDI_SUCCESS;
397 fail:
398 	mutex_exit(&mpt->m_mutex);
399 
400 	return (ret);
401 }
402 
403 void
404 mptsas_destroy_phy_stats(mptsas_t *mpt)
405 {
406 	smhba_info_t	*phyp;
407 	int			i = 0;
408 
409 	ASSERT(mpt != NULL);
410 
411 	for (i = 0; i < mpt->m_num_phys; i++) {
412 		phyp = &mpt->m_phy_info[i].smhba_info;
413 		if (phyp == NULL) {
414 			continue;
415 		}
416 
417 		mutex_enter(&phyp->phy_mutex);
418 		if (phyp->phy_stats != NULL) {
419 			kstat_delete(phyp->phy_stats);
420 			phyp->phy_stats = NULL;
421 		}
422 		mutex_exit(&phyp->phy_mutex);
423 	}
424 }
425 
426 int
427 mptsas_smhba_phy_init(mptsas_t *mpt)
428 {
429 	int		i = 0;
430 	int		rval = DDI_SUCCESS;
431 	uint32_t	page_address;
432 
433 	ASSERT(mutex_owned(&mpt->m_mutex));
434 
435 	for (i = 0; i < mpt->m_num_phys; i++) {
436 		page_address =
437 		    (MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER |
438 		    (MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK & i));
439 		rval = mptsas_get_sas_phy_page0(mpt,
440 		    page_address, &mpt->m_phy_info[i].smhba_info);
441 		if (rval != DDI_SUCCESS) {
442 			mptsas_log(mpt, CE_WARN,
443 			    "Failed to get sas phy page 0"
444 			    " for each phy");
445 			return (DDI_FAILURE);
446 		}
447 		mpt->m_phy_info[i].smhba_info.phy_id = (uint8_t)i;
448 		mpt->m_phy_info[i].smhba_info.sas_addr =
449 		    mpt->un.m_base_wwid + i;
450 		mpt->m_phy_info[i].smhba_info.mpt = mpt;
451 	}
452 
453 	return (DDI_SUCCESS);
454 }
455