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