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 }