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