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