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