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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2016 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 #include <assert.h> 28 #include <errno.h> 29 #include <libdiskstatus.h> 30 #include <limits.h> 31 #include <stdlib.h> 32 #include <strings.h> 33 #include <sys/fm/io/scsi.h> 34 35 #include "ds_scsi.h" 36 #include "ds_scsi_sim.h" 37 #include "ds_scsi_uscsi.h" 38 39 typedef struct ds_scsi_info { 40 disk_status_t *si_dsp; 41 void *si_sim; 42 int si_cdblen; 43 int si_supp_mode; 44 int si_supp_log; 45 int si_extensions; 46 int si_reftemp; 47 scsi_ms_hdrs_t si_hdrs; 48 scsi_ie_page_t si_iec_current; 49 scsi_ie_page_t si_iec_changeable; 50 nvlist_t *si_state_modepage; 51 nvlist_t *si_state_logpage; 52 nvlist_t *si_state_iec; 53 } ds_scsi_info_t; 54 55 #define scsi_set_errno(sip, errno) (ds_set_errno((sip)->si_dsp, (errno))) 56 57 /* 58 * Table to validate log pages 59 */ 60 typedef int (*logpage_validation_fn_t)(ds_scsi_info_t *, 61 scsi_log_parameter_header_t *, int, nvlist_t *); 62 typedef int (*logpage_analyze_fn_t)(ds_scsi_info_t *, 63 scsi_log_parameter_header_t *, int); 64 65 typedef struct logpage_validation_entry { 66 uchar_t ve_code; 67 int ve_supported; 68 const char *ve_desc; 69 logpage_validation_fn_t ve_validate; 70 logpage_analyze_fn_t ve_analyze; 71 } logpage_validation_entry_t; 72 73 static int logpage_ie_verify(ds_scsi_info_t *, 74 scsi_log_parameter_header_t *, int, nvlist_t *); 75 static int logpage_temp_verify(ds_scsi_info_t *, 76 scsi_log_parameter_header_t *, int, nvlist_t *); 77 static int logpage_selftest_verify(ds_scsi_info_t *, 78 scsi_log_parameter_header_t *, int, nvlist_t *); 79 static int logpage_ssm_verify(ds_scsi_info_t *, 80 scsi_log_parameter_header_t *, int, nvlist_t *); 81 82 static int logpage_ie_analyze(ds_scsi_info_t *, 83 scsi_log_parameter_header_t *, int); 84 static int logpage_temp_analyze(ds_scsi_info_t *, 85 scsi_log_parameter_header_t *, int); 86 static int logpage_selftest_analyze(ds_scsi_info_t *, 87 scsi_log_parameter_header_t *, int); 88 static int logpage_ssm_analyze(ds_scsi_info_t *, 89 scsi_log_parameter_header_t *, int); 90 91 static struct logpage_validation_entry log_validation[] = { 92 { LOGPAGE_IE, LOGPAGE_SUPP_IE, 93 "informational-exceptions", 94 logpage_ie_verify, logpage_ie_analyze }, 95 { LOGPAGE_TEMP, LOGPAGE_SUPP_TEMP, 96 "temperature", 97 logpage_temp_verify, logpage_temp_analyze }, 98 { LOGPAGE_SELFTEST, LOGPAGE_SUPP_SELFTEST, 99 "self-test", 100 logpage_selftest_verify, logpage_selftest_analyze }, 101 { LOGPAGE_SSM, LOGPAGE_SUPP_SSM, 102 FM_EREPORT_SCSI_SSMWEAROUT, 103 logpage_ssm_verify, logpage_ssm_analyze } 104 }; 105 106 #define NLOG_VALIDATION (sizeof (log_validation) / sizeof (log_validation[0])) 107 108 /* 109 * Given an extended sense page, retrieves the sense key, as well as the 110 * additional sense code information. 111 */ 112 static void 113 scsi_translate_error(struct scsi_extended_sense *rq, uint_t *skeyp, 114 uint_t *ascp, uint_t *ascqp) 115 { 116 struct scsi_descr_sense_hdr *sdsp = 117 (struct scsi_descr_sense_hdr *)rq; 118 119 *skeyp = rq->es_key; 120 121 /* 122 * Get asc, ascq and info field from sense data. There are two 123 * possible formats (fixed sense data and descriptor sense data) 124 * depending on the value of es_code. 125 */ 126 switch (rq->es_code) { 127 case CODE_FMT_DESCR_CURRENT: 128 case CODE_FMT_DESCR_DEFERRED: 129 130 *ascp = sdsp->ds_add_code; 131 *ascqp = sdsp->ds_qual_code; 132 break; 133 134 case CODE_FMT_FIXED_CURRENT: 135 case CODE_FMT_FIXED_DEFERRED: 136 default: 137 138 if (rq->es_add_len >= 6) { 139 *ascp = rq->es_add_code; 140 *ascqp = rq->es_qual_code; 141 } else { 142 *ascp = 0xff; 143 *ascqp = 0xff; 144 } 145 break; 146 } 147 } 148 149 /* 150 * Routines built atop the bare uscsi commands, which take into account the 151 * command length, automatically translate any scsi errors, and transparently 152 * call into the simulator if active. 153 */ 154 static int 155 scsi_mode_select(ds_scsi_info_t *sip, uchar_t page_code, int options, 156 void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp, 157 uint_t *ascp, uint_t *ascqp) 158 { 159 int result; 160 struct scsi_extended_sense sense; 161 int senselen = sizeof (struct scsi_extended_sense); 162 struct mode_page *mp = (struct mode_page *)buf; 163 164 assert(sip->si_cdblen == MODE_CMD_LEN_6 || 165 sip->si_cdblen == MODE_CMD_LEN_10); 166 assert(headers->ms_length == sip->si_cdblen); 167 168 bzero(&sense, sizeof (struct scsi_extended_sense)); 169 170 if (mp->ps) { 171 options |= MODE_SELECT_SP; 172 mp->ps = 0; 173 } else { 174 options &= ~MODE_SELECT_SP; 175 } 176 177 if (sip->si_cdblen == MODE_CMD_LEN_6) { 178 /* The following fields are reserved during mode select: */ 179 headers->ms_hdr.g0.ms_header.length = 0; 180 headers->ms_hdr.g0.ms_header.device_specific = 0; 181 182 if (sip->si_sim) 183 result = simscsi_mode_select(sip->si_sim, 184 page_code, options, buf, buflen, 185 &headers->ms_hdr.g0, &sense, &senselen); 186 else 187 result = uscsi_mode_select(sip->si_dsp->ds_fd, 188 page_code, options, buf, buflen, 189 &headers->ms_hdr.g0, &sense, &senselen); 190 } else { 191 /* The following fields are reserved during mode select: */ 192 headers->ms_hdr.g1.ms_header.length = 0; 193 headers->ms_hdr.g1.ms_header.device_specific = 0; 194 195 if (sip->si_sim) 196 result = simscsi_mode_select_10(sip->si_sim, 197 page_code, options, buf, buflen, 198 &headers->ms_hdr.g1, &sense, &senselen); 199 else 200 result = uscsi_mode_select_10(sip->si_dsp->ds_fd, 201 page_code, options, buf, buflen, 202 &headers->ms_hdr.g1, &sense, &senselen); 203 } 204 205 if (result != 0) 206 scsi_translate_error(&sense, skp, ascp, ascqp); 207 208 return (result); 209 } 210 211 static int 212 scsi_mode_sense(ds_scsi_info_t *sip, uchar_t page_code, uchar_t pc, 213 void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp, 214 uint_t *ascp, uint_t *ascqp) 215 { 216 int result; 217 struct scsi_extended_sense sense; 218 int senselen = sizeof (struct scsi_extended_sense); 219 220 assert(sip->si_cdblen == MODE_CMD_LEN_6 || 221 sip->si_cdblen == MODE_CMD_LEN_10); 222 223 bzero(&sense, sizeof (struct scsi_extended_sense)); 224 225 bzero(headers, sizeof (scsi_ms_hdrs_t)); 226 headers->ms_length = sip->si_cdblen; 227 228 if (sip->si_cdblen == MODE_CMD_LEN_6) { 229 if (sip->si_sim) 230 result = simscsi_mode_sense(sip->si_sim, 231 page_code, pc, buf, buflen, &headers->ms_hdr.g0, 232 &sense, &senselen); 233 else 234 result = uscsi_mode_sense(sip->si_dsp->ds_fd, page_code, 235 pc, buf, buflen, &headers->ms_hdr.g0, &sense, 236 &senselen); 237 } else { 238 if (sip->si_sim) 239 result = simscsi_mode_sense_10(sip->si_sim, 240 page_code, pc, buf, buflen, &headers->ms_hdr.g1, 241 &sense, &senselen); 242 else 243 result = uscsi_mode_sense_10(sip->si_dsp->ds_fd, 244 page_code, pc, buf, buflen, &headers->ms_hdr.g1, 245 &sense, &senselen); 246 } 247 248 if (result != 0) 249 scsi_translate_error(&sense, skp, ascp, ascqp); 250 251 return (result); 252 } 253 254 static int 255 scsi_request_sense(ds_scsi_info_t *sip, uint_t *skp, uint_t *ascp, 256 uint_t *ascqp) 257 { 258 struct scsi_extended_sense sense, sensebuf; 259 int senselen = sizeof (struct scsi_extended_sense); 260 int sensebuflen = sizeof (struct scsi_extended_sense); 261 int result; 262 263 bzero(&sense, sizeof (struct scsi_extended_sense)); 264 bzero(&sensebuf, sizeof (struct scsi_extended_sense)); 265 266 if (sip->si_sim) 267 result = simscsi_request_sense(sip->si_sim, 268 (caddr_t)&sensebuf, sensebuflen, &sense, &senselen); 269 else 270 result = uscsi_request_sense(sip->si_dsp->ds_fd, 271 (caddr_t)&sensebuf, sensebuflen, &sense, &senselen); 272 273 if (result == 0) 274 scsi_translate_error(&sensebuf, skp, ascp, ascqp); 275 else 276 scsi_translate_error(&sense, skp, ascp, ascqp); 277 278 return (result); 279 } 280 281 static int 282 scsi_log_sense(ds_scsi_info_t *sip, int page_code, int page_control, 283 caddr_t page_data, int page_size, uint_t *skp, uint_t *ascp, uint_t *ascqp) 284 { 285 int result; 286 struct scsi_extended_sense sense; 287 int senselen = sizeof (struct scsi_extended_sense); 288 289 if (sip->si_sim) 290 result = simscsi_log_sense(sip->si_sim, 291 page_code, page_control, page_data, page_size, &sense, 292 &senselen); 293 else 294 result = uscsi_log_sense(sip->si_dsp->ds_fd, 295 page_code, page_control, page_data, page_size, &sense, 296 &senselen); 297 298 if (result != 0) 299 scsi_translate_error(&sense, skp, ascp, ascqp); 300 301 return (result); 302 } 303 304 /* 305 * Given a list of supported mode pages, determine if the given page is present. 306 */ 307 static boolean_t 308 mode_page_present(uchar_t *pgdata, uint_t pgdatalen, uchar_t pagecode) 309 { 310 uint_t i = 0; 311 struct mode_page *pg; 312 boolean_t found = B_FALSE; 313 314 /* 315 * The mode page list contains all mode pages supported by the device, 316 * one after the other. 317 */ 318 while (i < pgdatalen) { 319 pg = (struct mode_page *)&pgdata[i]; 320 321 if (pg->code == pagecode) { 322 found = B_TRUE; 323 break; 324 } 325 326 i += MODESENSE_PAGE_LEN(pg); 327 } 328 329 return (found); 330 } 331 332 /* 333 * Load mode pages and check that the appropriate pages are supported. 334 * 335 * As part of this process, we determine which form of the MODE SENSE / MODE 336 * SELECT command to use (the 6-byte or 10-byte version) by executing a MODE 337 * SENSE command for a page that should be implemented by the device. 338 */ 339 static int 340 load_modepages(ds_scsi_info_t *sip) 341 { 342 int allpages_buflen; 343 uchar_t *allpages; 344 scsi_ms_hdrs_t headers; 345 int result; 346 uint_t skey, asc, ascq; 347 int datalength = 0; 348 scsi_ms_header_t *smh = &headers.ms_hdr.g0; 349 scsi_ms_header_g1_t *smh_g1 = &headers.ms_hdr.g1; 350 nvlist_t *nvl; 351 352 allpages_buflen = MAX_BUFLEN(scsi_ms_header_g1_t); 353 if ((allpages = calloc(allpages_buflen, 1)) == NULL) 354 return (scsi_set_errno(sip, EDS_NOMEM)); 355 356 bzero(&headers, sizeof (headers)); 357 358 /* 359 * Attempt a mode sense(6). If that fails, try a mode sense(10) 360 * 361 * allpages is allocated to be of the maximum size for either a mode 362 * sense(6) or mode sense(10) MODEPAGE_ALLPAGES response. 363 * 364 * Note that the length passed into uscsi_mode_sense should be set to 365 * the maximum size of the parameter response, which in this case is 366 * UCHAR_MAX - the size of the headers/block descriptors. 367 */ 368 sip->si_cdblen = MODE_CMD_LEN_6; 369 if ((result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES, PC_CURRENT, 370 (caddr_t)allpages, UCHAR_MAX - sizeof (scsi_ms_header_t), 371 &headers, &skey, &asc, &ascq)) == 0) { 372 /* 373 * Compute the data length of the page that contains all mode 374 * sense pages. This is a bit tricky because the format of the 375 * response from the lun is: 376 * 377 * header: <length> <medium type byte> <dev specific byte> 378 * <block descriptor length> 379 * [<optional block descriptor>] 380 * data: [<mode page data> <mode page data> ...] 381 * 382 * Since the length field in the header describes the length of 383 * the entire response. This includes the header, but NOT 384 * the length field itself, which is 1 or 2 bytes depending on 385 * which mode sense type (6- or 10- byte) is being executed. 386 * 387 * So, the data length equals the length value in the header 388 * plus 1 (because the length byte was not included in the 389 * length count), minus [[the sum of the length of the header 390 * and the length of the block descriptor]]. 391 */ 392 datalength = (smh->ms_header.length + 393 sizeof (smh->ms_header.length)) - 394 (sizeof (struct mode_header) + 395 smh->ms_header.bdesc_length); 396 } else if (SCSI_INVALID_OPCODE(skey, asc, ascq)) { 397 /* 398 * Fallback and try the 10-byte version of the command. 399 */ 400 sip->si_cdblen = MODE_CMD_LEN_10; 401 result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES, 402 PC_CURRENT, (caddr_t)allpages, allpages_buflen, 403 &headers, &skey, &asc, &ascq); 404 405 if (result == 0) { 406 datalength = (BE_16(smh_g1->ms_header.length) + 407 sizeof (smh_g1->ms_header.length)) - 408 (sizeof (struct mode_header_g1) + 409 BE_16(smh_g1->ms_header.bdesc_length)); 410 411 } 412 } 413 414 if (result == 0 && datalength >= 0) { 415 if (nvlist_add_int8(sip->si_dsp->ds_state, "command-length", 416 sip->si_cdblen == MODE_CMD_LEN_6 ? 6 : 10) != 0) { 417 free(allpages); 418 return (scsi_set_errno(sip, EDS_NOMEM)); 419 } 420 421 nvl = NULL; 422 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 423 nvlist_add_nvlist(sip->si_dsp->ds_state, "modepages", 424 nvl) != 0) { 425 free(allpages); 426 nvlist_free(nvl); 427 return (scsi_set_errno(sip, EDS_NOMEM)); 428 } 429 430 nvlist_free(nvl); 431 result = nvlist_lookup_nvlist(sip->si_dsp->ds_state, 432 "modepages", &sip->si_state_modepage); 433 assert(result == 0); 434 435 /* 436 * One of the sets of the commands (above) succeeded, so now 437 * look for the mode pages we need and record them appropriately 438 */ 439 if (mode_page_present(allpages, datalength, 440 MODEPAGE_INFO_EXCPT)) { 441 442 nvl = NULL; 443 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 444 nvlist_add_nvlist(sip->si_state_modepage, 445 "informational-exceptions", nvl) != 0) { 446 free(allpages); 447 nvlist_free(nvl); 448 return (scsi_set_errno(sip, EDS_NOMEM)); 449 } 450 nvlist_free(nvl); 451 sip->si_supp_mode |= MODEPAGE_SUPP_IEC; 452 result = nvlist_lookup_nvlist(sip->si_state_modepage, 453 "informational-exceptions", &sip->si_state_iec); 454 assert(result == 0); 455 } 456 457 } else { 458 /* 459 * If the device failed to respond to one of the basic commands, 460 * then assume it's not a SCSI device or otherwise doesn't 461 * support the necessary transport. 462 */ 463 if (datalength < 0) 464 ds_dprintf( 465 "command returned invalid data length (%d)\n", 466 datalength); 467 else 468 ds_dprintf("failed to load modepages (KEY=0x%x " 469 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); 470 471 result = scsi_set_errno(sip, EDS_NO_TRANSPORT); 472 } 473 474 free(allpages); 475 return (result); 476 } 477 478 /* 479 * Verify a single logpage. This will do some generic validation and then call 480 * the logpage-specific function for further verification. 481 */ 482 static int 483 verify_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *lp) 484 { 485 scsi_log_header_t *lhp; 486 struct scsi_extended_sense sense; 487 int buflen; 488 int log_length; 489 int result = 0; 490 uint_t kp, asc, ascq; 491 nvlist_t *nvl; 492 493 buflen = MAX_BUFLEN(scsi_log_header_t); 494 if ((lhp = calloc(buflen, 1)) == NULL) 495 return (scsi_set_errno(sip, EDS_NOMEM)); 496 bzero(&sense, sizeof (struct scsi_extended_sense)); 497 498 nvl = NULL; 499 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 500 nvlist_add_nvlist(sip->si_state_logpage, lp->ve_desc, nvl) != 0) { 501 nvlist_free(nvl); 502 free(lhp); 503 return (scsi_set_errno(sip, EDS_NOMEM)); 504 } 505 nvlist_free(nvl); 506 result = nvlist_lookup_nvlist(sip->si_state_logpage, lp->ve_desc, &nvl); 507 assert(result == 0); 508 509 result = scsi_log_sense(sip, lp->ve_code, 510 PC_CUMULATIVE, (caddr_t)lhp, buflen, &kp, &asc, &ascq); 511 512 if (result == 0) { 513 log_length = BE_16(lhp->lh_length); 514 if (nvlist_add_uint16(nvl, "length", log_length) != 0) { 515 free(lhp); 516 return (scsi_set_errno(sip, EDS_NOMEM)); 517 } 518 519 if (lp->ve_validate(sip, (scsi_log_parameter_header_t *) 520 (((char *)lhp) + sizeof (scsi_log_header_t)), 521 log_length, nvl) != 0) { 522 free(lhp); 523 return (-1); 524 } 525 } else { 526 ds_dprintf("failed to load %s log page (KEY=0x%x " 527 "ASC=0x%x ASCQ=0x%x)\n", lp->ve_desc, kp, asc, ascq); 528 } 529 530 free(lhp); 531 return (0); 532 } 533 534 /* 535 * Load log pages and determine which pages are supported. 536 */ 537 static int 538 load_logpages(ds_scsi_info_t *sip) 539 { 540 int buflen; 541 scsi_supported_log_pages_t *sp; 542 struct scsi_extended_sense sense; 543 int result; 544 uint_t sk, asc, ascq; 545 int i, j; 546 nvlist_t *nvl; 547 548 buflen = MAX_BUFLEN(scsi_log_header_t); 549 if ((sp = calloc(buflen, 1)) == NULL) 550 return (scsi_set_errno(sip, EDS_NOMEM)); 551 552 bzero(&sense, sizeof (struct scsi_extended_sense)); 553 554 if ((result = scsi_log_sense(sip, LOGPAGE_SUPP_LIST, 555 PC_CUMULATIVE, (caddr_t)sp, buflen, &sk, &asc, &ascq)) == 0) { 556 int pagecount = BE_16(sp->slp_hdr.lh_length); 557 558 for (i = 0; i < pagecount; i++) { 559 for (j = 0; j < NLOG_VALIDATION; j++) { 560 if (log_validation[j].ve_code == 561 sp->slp_pages[i]) 562 sip->si_supp_log |= 563 log_validation[j].ve_supported; 564 } 565 } 566 } 567 568 free(sp); 569 if (result == 0) { 570 nvl = NULL; 571 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 572 nvlist_add_nvlist(sip->si_dsp->ds_state, "logpages", 573 nvl) != 0) { 574 nvlist_free(nvl); 575 return (scsi_set_errno(sip, EDS_NOMEM)); 576 } 577 578 nvlist_free(nvl); 579 result = nvlist_lookup_nvlist(sip->si_dsp->ds_state, 580 "logpages", &sip->si_state_logpage); 581 assert(result == 0); 582 583 /* 584 * Validate the logpage contents. 585 */ 586 for (i = 0; i < NLOG_VALIDATION; i++) { 587 if ((sip->si_supp_log & 588 log_validation[i].ve_supported) == 0) 589 continue; 590 591 /* 592 * verify_logpage will clear the supported bit if 593 * verification fails. 594 */ 595 if (verify_logpage(sip, &log_validation[i]) != 0) 596 return (-1); 597 } 598 599 } else { 600 ds_dprintf("failed to get log pages " 601 "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk, asc, ascq); 602 } 603 604 /* 605 * We always return 0 here, even if the required log pages aren't 606 * supported. 607 */ 608 return (0); 609 } 610 611 /* 612 * Verify that the IE log page is sane. This log page is potentially chock-full 613 * of vendor specific information that we do not know how to access. All we can 614 * do is check for the generic predictive failure bit. If this log page is not 615 * well-formed, then bail out. 616 */ 617 static int 618 logpage_ie_verify(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 619 int log_length, nvlist_t *nvl) 620 { 621 int i, plen = 0; 622 boolean_t seen = B_FALSE; 623 scsi_ie_log_param_t *iep = 624 (scsi_ie_log_param_t *)lphp; 625 626 for (i = 0; i < log_length; i += plen) { 627 iep = (scsi_ie_log_param_t *)((char *)iep + plen); 628 629 if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE) { 630 if (nvlist_add_boolean_value(nvl, "general", 631 B_TRUE) != 0) 632 return (scsi_set_errno(sip, EDS_NOMEM)); 633 634 if (lphp->lph_length < LOGPARAM_IE_MIN_LEN) { 635 if (nvlist_add_uint8(nvl, 636 "invalid-length", lphp->lph_length) != 0) 637 return (scsi_set_errno(sip, EDS_NOMEM)); 638 } else { 639 seen = B_TRUE; 640 } 641 break; 642 } 643 644 plen = iep->ie_hdr.lph_length + 645 sizeof (scsi_log_parameter_header_t); 646 } 647 648 if (!seen) { 649 sip->si_supp_log &= ~LOGPAGE_SUPP_IE; 650 ds_dprintf("IE logpage validation failed\n"); 651 } 652 653 return (0); 654 } 655 656 /* 657 * Verify the contents of the temperature log page. The temperature log page 658 * contains two log parameters: the current temperature, and (optionally) the 659 * reference temperature. For the verification phase, we check that the two 660 * parameters we care about are well-formed. If there is no reference 661 * temperature, then we cannot use the page for monitoring purposes. 662 */ 663 static int 664 logpage_temp_verify(ds_scsi_info_t *sip, 665 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl) 666 { 667 int i, plen = 0; 668 boolean_t has_reftemp = B_FALSE; 669 boolean_t bad_length = B_FALSE; 670 ushort_t param_code; 671 672 for (i = 0; i < log_length; i += plen) { 673 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 674 param_code = BE_16(lphp->lph_param); 675 676 switch (param_code) { 677 case LOGPARAM_TEMP_CURTEMP: 678 if (nvlist_add_boolean_value(nvl, "current-temperature", 679 B_TRUE) != 0) 680 return (scsi_set_errno(sip, EDS_NOMEM)); 681 if (lphp->lph_length != LOGPARAM_TEMP_LEN) { 682 if (nvlist_add_uint8(nvl, 683 "invalid-length", lphp->lph_length) != 0) 684 return (scsi_set_errno(sip, EDS_NOMEM)); 685 bad_length = B_TRUE; 686 } 687 break; 688 689 case LOGPARAM_TEMP_REFTEMP: 690 if (nvlist_add_boolean_value(nvl, 691 "reference-temperature", B_TRUE) != 0) 692 return (scsi_set_errno(sip, EDS_NOMEM)); 693 if (lphp->lph_length != LOGPARAM_TEMP_LEN) { 694 if (nvlist_add_uint8(nvl, 695 "invalid-length", lphp->lph_length) != 0) 696 return (scsi_set_errno(sip, EDS_NOMEM)); 697 bad_length = B_TRUE; 698 } 699 has_reftemp = B_TRUE; 700 break; 701 } 702 703 plen = lphp->lph_length + 704 sizeof (scsi_log_parameter_header_t); 705 } 706 707 if (bad_length || !has_reftemp) { 708 sip->si_supp_log &= ~LOGPAGE_SUPP_TEMP; 709 ds_dprintf("temperature logpage validation failed\n"); 710 } 711 712 return (0); 713 } 714 715 /* 716 * Verify the contents of the self test log page. The log supports a maximum of 717 * 20 entries, where each entry's parameter code is its index in the log. We 718 * check that the parameter codes fall within this range, and that the size of 719 * each page is what we expect. It's perfectly acceptable for there to be no 720 * entries in this log, so we must also be sure to validate the contents as part 721 * of the analysis phase. 722 */ 723 static int 724 logpage_selftest_verify(ds_scsi_info_t *sip, 725 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl) 726 { 727 int i, plen = 0; 728 boolean_t bad = B_FALSE; 729 int entries = 0; 730 ushort_t param_code; 731 732 for (i = 0; i < log_length; i += plen, entries++) { 733 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 734 param_code = BE_16(lphp->lph_param); 735 736 if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE || 737 param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) { 738 if (nvlist_add_uint16(nvl, "invalid-param-code", 739 param_code) != 0) 740 return (scsi_set_errno(sip, EDS_NOMEM)); 741 bad = B_TRUE; 742 break; 743 } 744 745 if (lphp->lph_length != LOGPAGE_SELFTEST_PARAM_LEN) { 746 if (nvlist_add_uint8(nvl, "invalid-length", 747 lphp->lph_length) != 0) 748 return (scsi_set_errno(sip, EDS_NOMEM)); 749 bad = B_TRUE; 750 break; 751 752 } 753 754 plen = lphp->lph_length + 755 sizeof (scsi_log_parameter_header_t); 756 } 757 758 if (bad) { 759 sip->si_supp_log &= ~LOGPAGE_SUPP_SELFTEST; 760 ds_dprintf("selftest logpage validation failed\n"); 761 } 762 763 return (0); 764 } 765 766 /* 767 * Verify the contents of the Solid State Media (SSM) log page. 768 * As of SBC3r36 SSM log page contains one log parameter: 769 * "Percentage Used Endurance Indicator" which is mandatory. 770 * For the verification phase, we sanity check this parameter 771 * by making sure it's present and it's length is set to 0x04. 772 */ 773 static int 774 logpage_ssm_verify(ds_scsi_info_t *sip, 775 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl) 776 { 777 ushort_t param_code; 778 int i, plen = 0; 779 780 for (i = 0; i < log_length; i += plen) { 781 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 782 param_code = BE_16(lphp->lph_param); 783 784 switch (param_code) { 785 case LOGPARAM_PRCNT_USED: 786 if (nvlist_add_boolean_value(nvl, 787 FM_EREPORT_SCSI_SSMWEAROUT, B_TRUE) != 0) 788 return (scsi_set_errno(sip, EDS_NOMEM)); 789 if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN) { 790 if (nvlist_add_uint8(nvl, 791 "invalid-length", lphp->lph_length) != 0) 792 return (scsi_set_errno(sip, EDS_NOMEM)); 793 794 ds_dprintf( 795 "solid state media logpage bad len\n"); 796 break; 797 } 798 799 /* verification succeded */ 800 return (0); 801 } 802 803 plen = lphp->lph_length + 804 sizeof (scsi_log_parameter_header_t); 805 } 806 807 /* verification failed */ 808 sip->si_supp_log &= ~LOGPAGE_SUPP_SSM; 809 return (0); 810 } 811 812 /* 813 * Load the current IE mode pages 814 */ 815 static int 816 load_ie_modepage(ds_scsi_info_t *sip) 817 { 818 struct scsi_ms_hdrs junk_hdrs; 819 int result; 820 uint_t skey, asc, ascq; 821 822 if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC)) 823 return (0); 824 825 bzero(&sip->si_iec_current, sizeof (sip->si_iec_current)); 826 bzero(&sip->si_iec_changeable, sizeof (sip->si_iec_changeable)); 827 828 if ((result = scsi_mode_sense(sip, 829 MODEPAGE_INFO_EXCPT, PC_CURRENT, &sip->si_iec_current, 830 MODEPAGE_INFO_EXCPT_LEN, &sip->si_hdrs, &skey, &asc, 831 &ascq)) == 0) { 832 result = scsi_mode_sense(sip, 833 MODEPAGE_INFO_EXCPT, PC_CHANGEABLE, 834 &sip->si_iec_changeable, 835 MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, &skey, &asc, &ascq); 836 } 837 838 if (result != 0) { 839 ds_dprintf("failed to get IEC modepage (KEY=0x%x " 840 "ASC=0x%x ASCQ=0x%x)", skey, asc, ascq); 841 sip->si_supp_mode &= ~MODEPAGE_SUPP_IEC; 842 } else { 843 if (nvlist_add_boolean_value(sip->si_state_iec, 844 "dexcpt", sip->si_iec_current.ie_dexcpt) != 0 || 845 nvlist_add_boolean_value(sip->si_state_iec, 846 "logerr", sip->si_iec_current.ie_logerr) != 0 || 847 nvlist_add_uint8(sip->si_state_iec, 848 "mrie", sip->si_iec_current.ie_mrie) != 0 || 849 nvlist_add_boolean_value(sip->si_state_iec, 850 "test", sip->si_iec_current.ie_test) != 0 || 851 nvlist_add_boolean_value(sip->si_state_iec, 852 "ewasc", sip->si_iec_current.ie_ewasc) != 0 || 853 nvlist_add_boolean_value(sip->si_state_iec, 854 "perf", sip->si_iec_current.ie_perf) != 0 || 855 nvlist_add_boolean_value(sip->si_state_iec, 856 "ebf", sip->si_iec_current.ie_ebf) != 0 || 857 nvlist_add_uint32(sip->si_state_iec, 858 "interval-timer", 859 BE_32(sip->si_iec_current.ie_interval_timer)) != 0 || 860 nvlist_add_uint32(sip->si_state_iec, 861 "report-count", 862 BE_32(sip->si_iec_current.ie_report_count)) != 0) 863 return (scsi_set_errno(sip, EDS_NOMEM)); 864 } 865 866 return (0); 867 } 868 869 /* 870 * Enable IE reporting. We prefer the following settings: 871 * 872 * (1) DEXCPT = 0 873 * (3) MRIE = 6 (IE_REPORT_ON_REQUEST) 874 * (4) EWASC = 1 875 * (6) REPORT COUNT = 0x00000001 876 * (7) LOGERR = 1 877 * 878 * However, not all drives support changing these values, and the current state 879 * may be useful enough as-is. For example, some drives support IE logging, but 880 * don't support changing the MRIE. In this case, we can still use the 881 * information provided by the log page. 882 */ 883 static int 884 scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed) 885 { 886 scsi_ie_page_t new_iec_page; 887 scsi_ms_hdrs_t hdrs; 888 uint_t skey, asc, ascq; 889 890 if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC)) 891 return (0); 892 893 bzero(&new_iec_page, sizeof (new_iec_page)); 894 bzero(&hdrs, sizeof (hdrs)); 895 896 (void) memcpy(&new_iec_page, &sip->si_iec_current, 897 sizeof (new_iec_page)); 898 899 if (IEC_IE_CHANGEABLE(sip->si_iec_changeable)) 900 new_iec_page.ie_dexcpt = 0; 901 902 if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable)) 903 new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST; 904 905 /* 906 * We only want to enable warning reporting if we are able to change the 907 * mrie to report on request. Otherwise, we risk unnecessarily 908 * interrupting normal SCSI commands with a CHECK CONDITION code. 909 */ 910 if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) { 911 if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST) 912 new_iec_page.ie_ewasc = 1; 913 else 914 new_iec_page.ie_ewasc = 0; 915 } 916 917 if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable)) 918 new_iec_page.ie_report_count = BE_32(1); 919 920 if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable)) 921 new_iec_page.ie_logerr = 1; 922 923 /* 924 * Now compare the new mode page with the existing one. 925 * if there's no difference, there's no need for a mode select 926 */ 927 if (memcmp(&new_iec_page, &sip->si_iec_current, 928 MODEPAGE_INFO_EXCPT_LEN) == 0) { 929 *changed = B_FALSE; 930 } else { 931 (void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs)); 932 933 if (scsi_mode_select(sip, 934 MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page, 935 MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) { 936 *changed = B_TRUE; 937 } else { 938 ds_dprintf("failed to enable IE (KEY=0x%x " 939 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); 940 *changed = B_FALSE; 941 } 942 } 943 944 if (nvlist_add_boolean_value(sip->si_state_iec, "changed", 945 *changed) != 0) 946 return (scsi_set_errno(sip, EDS_NOMEM)); 947 948 return (0); 949 } 950 951 /* 952 * Clear the GLTSD bit, indicating log pages should be saved to non-volatile 953 * storage. 954 */ 955 static int 956 clear_gltsd(ds_scsi_info_t *sip) 957 { 958 scsi_ms_hdrs_t hdrs, junk_hdrs; 959 struct mode_control_scsi3 control_pg_cur, control_pg_chg; 960 int result; 961 uint_t skey, asc, ascq; 962 963 bzero(&hdrs, sizeof (hdrs)); 964 bzero(&control_pg_cur, sizeof (control_pg_cur)); 965 bzero(&control_pg_chg, sizeof (control_pg_chg)); 966 967 result = scsi_mode_sense(sip, 968 MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur, 969 MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq); 970 971 if (result != 0) { 972 ds_dprintf("failed to read Control mode page (KEY=0x%x " 973 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); 974 } else if (control_pg_cur.mode_page.length != 975 PAGELENGTH_MODE_CONTROL_SCSI3) { 976 ds_dprintf("SCSI-3 control mode page not supported\n"); 977 } else if ((result = scsi_mode_sense(sip, 978 MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg, 979 MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq)) 980 != 0) { 981 ds_dprintf("failed to read changeable Control mode page " 982 "(KEY=0x%x ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); 983 } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) { 984 ds_dprintf("gltsd is set and not changeable\n"); 985 if (nvlist_add_boolean_value(sip->si_dsp->ds_state, 986 "gltsd", control_pg_cur.gltsd) != 0) 987 return (scsi_set_errno(sip, EDS_NOMEM)); 988 } else if (control_pg_cur.gltsd) { 989 control_pg_cur.gltsd = 0; 990 result = scsi_mode_select(sip, 991 MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur, 992 MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq); 993 if (result != 0) 994 ds_dprintf("failed to enable GLTSD (KEY=0x%x " 995 "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq); 996 if (nvlist_add_boolean_value(sip->si_dsp->ds_state, 997 "gltsd", control_pg_cur.gltsd) != 0) 998 return (scsi_set_errno(sip, EDS_NOMEM)); 999 } 1000 1001 return (0); 1002 } 1003 1004 /* 1005 * Fetch the contents of the logpage, and then call the logpage-specific 1006 * analysis function. The analysis function is responsible for detecting any 1007 * faults and filling in the details. 1008 */ 1009 static int 1010 analyze_one_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *entry) 1011 { 1012 scsi_log_header_t *lhp; 1013 scsi_log_parameter_header_t *lphp; 1014 int buflen; 1015 int log_length; 1016 uint_t skey, asc, ascq; 1017 int result; 1018 1019 buflen = MAX_BUFLEN(scsi_log_header_t); 1020 if ((lhp = calloc(buflen, 1)) == NULL) 1021 return (scsi_set_errno(sip, EDS_NOMEM)); 1022 1023 result = scsi_log_sense(sip, entry->ve_code, 1024 PC_CUMULATIVE, (caddr_t)lhp, buflen, &skey, &asc, &ascq); 1025 1026 if (result == 0) { 1027 log_length = BE_16(lhp->lh_length); 1028 lphp = (scsi_log_parameter_header_t *)(((uchar_t *)lhp) + 1029 sizeof (scsi_log_header_t)); 1030 1031 result = entry->ve_analyze(sip, lphp, log_length); 1032 } else { 1033 result = scsi_set_errno(sip, EDS_IO); 1034 } 1035 1036 free(lhp); 1037 return (result); 1038 } 1039 1040 /* 1041 * Analyze the IE logpage. If we find an IE log record with a non-zero 'asc', 1042 * then we have a fault. 1043 */ 1044 static int 1045 logpage_ie_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 1046 int log_length) 1047 { 1048 int i, plen = 0; 1049 scsi_ie_log_param_t *iep = (scsi_ie_log_param_t *)lphp; 1050 nvlist_t *nvl; 1051 1052 assert(sip->si_dsp->ds_predfail == NULL); 1053 if (nvlist_alloc(&sip->si_dsp->ds_predfail, NV_UNIQUE_NAME, 0) != 0) 1054 return (scsi_set_errno(sip, EDS_NOMEM)); 1055 nvl = sip->si_dsp->ds_predfail; 1056 1057 for (i = 0; i < log_length; i += plen) { 1058 iep = (scsi_ie_log_param_t *)((char *)iep + plen); 1059 1060 /* 1061 * Even though we validated the length during the initial phase, 1062 * never trust the device. 1063 */ 1064 if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE && 1065 iep->ie_hdr.lph_length >= LOGPARAM_IE_MIN_LEN) { 1066 if (nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASC, 1067 iep->ie_asc) != 0 || 1068 nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASCQ, 1069 iep->ie_ascq) != 0) 1070 return (scsi_set_errno(sip, EDS_NOMEM)); 1071 1072 if (iep->ie_asc != 0) 1073 sip->si_dsp->ds_faults |= 1074 DS_FAULT_PREDFAIL; 1075 break; 1076 } 1077 plen = iep->ie_hdr.lph_length + 1078 sizeof (scsi_log_parameter_header_t); 1079 } 1080 1081 return (0); 1082 } 1083 1084 static int 1085 logpage_temp_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 1086 int log_length) 1087 { 1088 int i, plen = 0; 1089 uint8_t reftemp, curtemp; 1090 ushort_t param_code; 1091 scsi_temp_log_param_t *temp; 1092 nvlist_t *nvl; 1093 1094 assert(sip->si_dsp->ds_overtemp == NULL); 1095 if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0) 1096 return (scsi_set_errno(sip, EDS_NOMEM)); 1097 nvl = sip->si_dsp->ds_overtemp; 1098 1099 reftemp = curtemp = INVALID_TEMPERATURE; 1100 for (i = 0; i < log_length; i += plen) { 1101 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 1102 param_code = BE_16(lphp->lph_param); 1103 temp = (scsi_temp_log_param_t *)lphp; 1104 1105 switch (param_code) { 1106 case LOGPARAM_TEMP_CURTEMP: 1107 if (lphp->lph_length != LOGPARAM_TEMP_LEN) 1108 break; 1109 1110 if (nvlist_add_uint8(nvl, 1111 FM_EREPORT_PAYLOAD_SCSI_CURTEMP, 1112 temp->t_temp) != 0) 1113 return (scsi_set_errno(sip, EDS_NOMEM)); 1114 curtemp = temp->t_temp; 1115 break; 1116 1117 case LOGPARAM_TEMP_REFTEMP: 1118 if (lphp->lph_length != LOGPARAM_TEMP_LEN) 1119 break; 1120 1121 if (nvlist_add_uint8(nvl, 1122 FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP, 1123 temp->t_temp) != 0) 1124 return (scsi_set_errno(sip, EDS_NOMEM)); 1125 reftemp = temp->t_temp; 1126 break; 1127 } 1128 1129 plen = lphp->lph_length + 1130 sizeof (scsi_log_parameter_header_t); 1131 } 1132 1133 if (reftemp != INVALID_TEMPERATURE && curtemp != INVALID_TEMPERATURE && 1134 curtemp > reftemp) 1135 sip->si_dsp->ds_faults |= DS_FAULT_OVERTEMP; 1136 1137 return (0); 1138 } 1139 1140 static int 1141 logpage_selftest_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 1142 int log_length) 1143 { 1144 int i, plen = 0; 1145 int entries = 0; 1146 ushort_t param_code; 1147 scsi_selftest_log_param_t *stp; 1148 nvlist_t *nvl; 1149 1150 assert(sip->si_dsp->ds_testfail == NULL); 1151 if (nvlist_alloc(&sip->si_dsp->ds_testfail, NV_UNIQUE_NAME, 0) != 0) 1152 return (scsi_set_errno(sip, EDS_NOMEM)); 1153 nvl = sip->si_dsp->ds_testfail; 1154 1155 for (i = 0; i < log_length; i += plen, entries++) { 1156 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 1157 param_code = BE_16(lphp->lph_param); 1158 stp = (scsi_selftest_log_param_t *)lphp; 1159 1160 if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE && 1161 param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE && 1162 lphp->lph_length >= LOGPAGE_SELFTEST_PARAM_LEN) { 1163 /* 1164 * We always log the last result, or the result of the 1165 * last completed test. 1166 */ 1167 if ((param_code == 1 || 1168 SELFTEST_COMPLETE(stp->st_results))) { 1169 if (nvlist_add_uint8(nvl, 1170 FM_EREPORT_PAYLOAD_SCSI_RESULTCODE, 1171 stp->st_results) != 0 || 1172 nvlist_add_uint16(nvl, 1173 FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP, 1174 BE_16(stp->st_timestamp)) != 0 || 1175 nvlist_add_uint8(nvl, 1176 FM_EREPORT_PAYLOAD_SCSI_SEGMENT, 1177 stp->st_number) != 0 || 1178 nvlist_add_uint64(nvl, 1179 FM_EREPORT_PAYLOAD_SCSI_ADDRESS, 1180 BE_64(stp->st_lba)) != 0) 1181 return (scsi_set_errno(sip, 1182 EDS_NOMEM)); 1183 1184 if (SELFTEST_COMPLETE(stp->st_results)) { 1185 if (stp->st_results != SELFTEST_OK) 1186 sip->si_dsp->ds_faults |= 1187 DS_FAULT_TESTFAIL; 1188 return (0); 1189 } 1190 } 1191 } 1192 1193 plen = lphp->lph_length + 1194 sizeof (scsi_log_parameter_header_t); 1195 } 1196 1197 return (0); 1198 } 1199 1200 /* 1201 * Analyze the contents of the Solid State Media (SSM) log page's 1202 * "Percentage Used Endurance Indicator" log parameter. 1203 * We generate a fault if the percentage used is equal to or over 1204 * PRCNT_USED_FAULT_THRSH 1205 */ 1206 static int 1207 logpage_ssm_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 1208 int log_length) 1209 { 1210 uint16_t param_code; 1211 scsi_ssm_log_param_t *ssm; 1212 nvlist_t *nvl; 1213 int i, plen = 0; 1214 1215 assert(sip->si_dsp->ds_ssmwearout == NULL); 1216 if (nvlist_alloc(&sip->si_dsp->ds_ssmwearout, NV_UNIQUE_NAME, 0) != 0) 1217 return (scsi_set_errno(sip, EDS_NOMEM)); 1218 nvl = sip->si_dsp->ds_ssmwearout; 1219 1220 for (i = 0; i < log_length; i += plen) { 1221 lphp = (scsi_log_parameter_header_t *)((uint8_t *)lphp + plen); 1222 param_code = BE_16(lphp->lph_param); 1223 ssm = (scsi_ssm_log_param_t *)lphp; 1224 1225 switch (param_code) { 1226 case LOGPARAM_PRCNT_USED: 1227 if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN) 1228 break; 1229 1230 if ((nvlist_add_uint8(nvl, 1231 FM_EREPORT_PAYLOAD_SCSI_CURSSMWEAROUT, 1232 ssm->ssm_prcnt_used) != 0) || 1233 (nvlist_add_uint8(nvl, 1234 FM_EREPORT_PAYLOAD_SCSI_THRSHSSMWEAROUT, 1235 PRCNT_USED_FAULT_THRSH) != 0)) 1236 return (scsi_set_errno(sip, EDS_NOMEM)); 1237 1238 if (ssm->ssm_prcnt_used >= PRCNT_USED_FAULT_THRSH) 1239 sip->si_dsp->ds_faults |= DS_FAULT_SSMWEAROUT; 1240 1241 return (0); 1242 } 1243 1244 plen = lphp->lph_length + 1245 sizeof (scsi_log_parameter_header_t); 1246 } 1247 1248 /* 1249 * If we got this far we didn't see LOGPARAM_PRCNT_USED 1250 * which is strange since we verified that it's there 1251 */ 1252 ds_dprintf("solid state media logpage analyze failed\n"); 1253 #if DEBUG 1254 abort(); 1255 #endif 1256 return (scsi_set_errno(sip, EDS_NOT_SUPPORTED)); 1257 } 1258 1259 /* 1260 * Analyze the IE mode sense page explicitly. This is only needed if the IE log 1261 * page is not supported. 1262 */ 1263 static int 1264 analyze_ie_sense(ds_scsi_info_t *sip) 1265 { 1266 uint_t skey, asc, ascq; 1267 nvlist_t *nvl; 1268 1269 /* 1270 * Don't bother checking if we weren't able to set our MRIE correctly. 1271 */ 1272 if (sip->si_iec_current.ie_mrie != IE_REPORT_ON_REQUEST) 1273 return (0); 1274 1275 if (scsi_request_sense(sip, &skey, &asc, &ascq) != 0) { 1276 ds_dprintf("failed to request IE page (KEY=0x%x ASC=0x%x " 1277 "ASCQ=0x%x)\n", skey, asc, ascq); 1278 return (scsi_set_errno(sip, EDS_IO)); 1279 } else if (skey == KEY_NO_SENSE) { 1280 assert(sip->si_dsp->ds_predfail == NULL); 1281 if (nvlist_alloc(&sip->si_dsp->ds_predfail, 1282 NV_UNIQUE_NAME, 0) != 0) 1283 return (scsi_set_errno(sip, EDS_NOMEM)); 1284 nvl = sip->si_dsp->ds_predfail; 1285 1286 if (nvlist_add_uint8(nvl, 1287 FM_EREPORT_PAYLOAD_SCSI_ASC, asc) != 0 || 1288 nvlist_add_uint8(nvl, 1289 FM_EREPORT_PAYLOAD_SCSI_ASCQ, ascq) != 0) { 1290 nvlist_free(nvl); 1291 return (scsi_set_errno(sip, EDS_NOMEM)); 1292 } 1293 1294 if (asc != 0) 1295 sip->si_dsp->ds_faults |= DS_FAULT_PREDFAIL; 1296 } 1297 1298 return (0); 1299 } 1300 1301 /* 1302 * Clean up the scsi-specific information structure. 1303 */ 1304 static void 1305 ds_scsi_close(void *arg) 1306 { 1307 ds_scsi_info_t *sip = arg; 1308 if (sip->si_sim) 1309 (void) dlclose(sip->si_sim); 1310 1311 free(sip); 1312 } 1313 1314 /* 1315 * Initialize a single disk. Initialization consists of: 1316 * 1317 * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE 1318 * Control page (page 0x1C). 1319 * 1320 * 2. If the IE page is available, try to set the following parameters: 1321 * 1322 * DEXCPT 0 Enable exceptions 1323 * MRIE 6 Only report IE information on request 1324 * EWASC 1 Enable warning reporting 1325 * REPORT COUNT 1 Only report an IE exception once 1326 * LOGERR 1 Enable logging of errors 1327 * 1328 * The remaining fields are left as-is, preserving the current values. If we 1329 * cannot set some of these fields, then we do our best. Some drives may 1330 * have a static configuration which still allows for some monitoring. 1331 * 1332 * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a 1333 * LOG SENSE command. 1334 * 1335 * 4. Check to see if the self-test log page (page 0x10) is supported. 1336 * 1337 * 5. Check to see if the temperature log page (page 0x0D) is supported, and 1338 * contains a reference temperature. 1339 * 1340 * 6. Clear the GLTSD bit in control mode page 0xA. This will allow the drive 1341 * to save each of the log pages described above to nonvolatile storage. 1342 * This is essential if the drive is to remember its failures across 1343 * loss of power. 1344 */ 1345 static void * 1346 ds_scsi_open_common(disk_status_t *dsp, ds_scsi_info_t *sip) 1347 { 1348 boolean_t changed; 1349 1350 sip->si_dsp = dsp; 1351 1352 /* Load and validate mode pages */ 1353 if (load_modepages(sip) != 0) { 1354 ds_scsi_close(sip); 1355 return (NULL); 1356 } 1357 1358 /* Load and validate log pages */ 1359 if (load_logpages(sip) != 0) { 1360 ds_scsi_close(sip); 1361 return (NULL); 1362 } 1363 1364 /* Load IE state */ 1365 if (load_ie_modepage(sip) != 0 || 1366 scsi_enable_ie(sip, &changed) != 0 || 1367 (changed && load_ie_modepage(sip) != 0)) { 1368 ds_scsi_close(sip); 1369 return (NULL); 1370 } 1371 1372 /* Clear the GLTSD bit in the control page */ 1373 if (sip->si_supp_log != 0 && clear_gltsd(sip) != 0) { 1374 ds_scsi_close(sip); 1375 return (NULL); 1376 } 1377 1378 return (sip); 1379 } 1380 1381 static void * 1382 ds_scsi_open_uscsi(disk_status_t *dsp) 1383 { 1384 ds_scsi_info_t *sip; 1385 1386 if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) { 1387 (void) ds_set_errno(dsp, EDS_NOMEM); 1388 return (NULL); 1389 } 1390 1391 return (ds_scsi_open_common(dsp, sip)); 1392 } 1393 1394 static void * 1395 ds_scsi_open_sim(disk_status_t *dsp) 1396 { 1397 ds_scsi_info_t *sip; 1398 1399 if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) { 1400 (void) ds_set_errno(dsp, EDS_NOMEM); 1401 return (NULL); 1402 } 1403 1404 if ((sip->si_sim = dlopen(dsp->ds_path, RTLD_LAZY)) == NULL) { 1405 (void) ds_set_errno(dsp, EDS_NO_TRANSPORT); 1406 free(sip); 1407 return (NULL); 1408 } 1409 1410 return (ds_scsi_open_common(dsp, sip)); 1411 } 1412 1413 1414 /* 1415 * Scan for any faults. The following steps are performed: 1416 * 1417 * 1. If the temperature log page is supported, check the current temperature 1418 * and threshold. If the current temperature exceeds the threshold, report 1419 * and overtemp fault. 1420 * 1421 * 2. If the selftest log page is supported, check to the last completed self 1422 * test. If the last completed test resulted in failure, report a selftest 1423 * fault. 1424 * 1425 * 3. If the IE log page is supported, check to see if failure is predicted. If 1426 * so, indicate a predictive failure fault. 1427 * 1428 * 4. If the IE log page is not supported, but the mode page supports report on 1429 * request mode, then issue a REQUEST SENSE for the mode page. Indicate a 1430 * predictive failure fault if necessary. 1431 */ 1432 static int 1433 ds_scsi_scan(void *arg) 1434 { 1435 ds_scsi_info_t *sip = arg; 1436 int i; 1437 1438 for (i = 0; i < NLOG_VALIDATION; i++) { 1439 if ((sip->si_supp_log & log_validation[i].ve_supported) == 0) 1440 continue; 1441 1442 if (analyze_one_logpage(sip, &log_validation[i]) != 0) 1443 return (-1); 1444 } 1445 1446 if (!(sip->si_supp_log & LOGPAGE_SUPP_IE) && 1447 (sip->si_supp_mode & MODEPAGE_SUPP_IEC) && 1448 analyze_ie_sense(sip) != 0) 1449 return (-1); 1450 1451 return (0); 1452 } 1453 1454 ds_transport_t ds_scsi_uscsi_transport = { 1455 ds_scsi_open_uscsi, 1456 ds_scsi_close, 1457 ds_scsi_scan 1458 }; 1459 1460 ds_transport_t ds_scsi_sim_transport = { 1461 ds_scsi_open_sim, 1462 ds_scsi_close, 1463 ds_scsi_scan 1464 }; 1465