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 dprintf("command returned invalid data length (%d)\n", 465 datalength); 466 else 467 dprintf("failed to load modepages (KEY=0x%x " 468 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); 469 470 result = scsi_set_errno(sip, EDS_NO_TRANSPORT); 471 } 472 473 free(allpages); 474 return (result); 475 } 476 477 /* 478 * Verify a single logpage. This will do some generic validation and then call 479 * the logpage-specific function for further verification. 480 */ 481 static int 482 verify_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *lp) 483 { 484 scsi_log_header_t *lhp; 485 struct scsi_extended_sense sense; 486 int buflen; 487 int log_length; 488 int result = 0; 489 uint_t kp, asc, ascq; 490 nvlist_t *nvl; 491 492 buflen = MAX_BUFLEN(scsi_log_header_t); 493 if ((lhp = calloc(buflen, 1)) == NULL) 494 return (scsi_set_errno(sip, EDS_NOMEM)); 495 bzero(&sense, sizeof (struct scsi_extended_sense)); 496 497 nvl = NULL; 498 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 499 nvlist_add_nvlist(sip->si_state_logpage, lp->ve_desc, nvl) != 0) { 500 nvlist_free(nvl); 501 free(lhp); 502 return (scsi_set_errno(sip, EDS_NOMEM)); 503 } 504 nvlist_free(nvl); 505 result = nvlist_lookup_nvlist(sip->si_state_logpage, lp->ve_desc, &nvl); 506 assert(result == 0); 507 508 result = scsi_log_sense(sip, lp->ve_code, 509 PC_CUMULATIVE, (caddr_t)lhp, buflen, &kp, &asc, &ascq); 510 511 if (result == 0) { 512 log_length = BE_16(lhp->lh_length); 513 if (nvlist_add_uint16(nvl, "length", log_length) != 0) { 514 free(lhp); 515 return (scsi_set_errno(sip, EDS_NOMEM)); 516 } 517 518 if (lp->ve_validate(sip, (scsi_log_parameter_header_t *) 519 (((char *)lhp) + sizeof (scsi_log_header_t)), 520 log_length, nvl) != 0) { 521 free(lhp); 522 return (-1); 523 } 524 } else { 525 dprintf("failed to load %s log page (KEY=0x%x " 526 "ASC=0x%x ASCQ=0x%x)\n", lp->ve_desc, kp, asc, ascq); 527 } 528 529 free(lhp); 530 return (0); 531 } 532 533 /* 534 * Load log pages and determine which pages are supported. 535 */ 536 static int 537 load_logpages(ds_scsi_info_t *sip) 538 { 539 int buflen; 540 scsi_supported_log_pages_t *sp; 541 struct scsi_extended_sense sense; 542 int result; 543 uint_t sk, asc, ascq; 544 int i, j; 545 nvlist_t *nvl; 546 547 buflen = MAX_BUFLEN(scsi_log_header_t); 548 if ((sp = calloc(buflen, 1)) == NULL) 549 return (scsi_set_errno(sip, EDS_NOMEM)); 550 551 bzero(&sense, sizeof (struct scsi_extended_sense)); 552 553 if ((result = scsi_log_sense(sip, LOGPAGE_SUPP_LIST, 554 PC_CUMULATIVE, (caddr_t)sp, buflen, &sk, &asc, &ascq)) == 0) { 555 int pagecount = BE_16(sp->slp_hdr.lh_length); 556 557 for (i = 0; i < pagecount; i++) { 558 for (j = 0; j < NLOG_VALIDATION; j++) { 559 if (log_validation[j].ve_code == 560 sp->slp_pages[i]) 561 sip->si_supp_log |= 562 log_validation[j].ve_supported; 563 } 564 } 565 } 566 567 free(sp); 568 if (result == 0) { 569 nvl = NULL; 570 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 571 nvlist_add_nvlist(sip->si_dsp->ds_state, "logpages", 572 nvl) != 0) { 573 nvlist_free(nvl); 574 return (scsi_set_errno(sip, EDS_NOMEM)); 575 } 576 577 nvlist_free(nvl); 578 result = nvlist_lookup_nvlist(sip->si_dsp->ds_state, 579 "logpages", &sip->si_state_logpage); 580 assert(result == 0); 581 582 /* 583 * Validate the logpage contents. 584 */ 585 for (i = 0; i < NLOG_VALIDATION; i++) { 586 if ((sip->si_supp_log & 587 log_validation[i].ve_supported) == 0) 588 continue; 589 590 /* 591 * verify_logpage will clear the supported bit if 592 * verification fails. 593 */ 594 if (verify_logpage(sip, &log_validation[i]) != 0) 595 return (-1); 596 } 597 598 } else { 599 dprintf("failed to get log pages " 600 "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk, asc, ascq); 601 } 602 603 /* 604 * We always return 0 here, even if the required log pages aren't 605 * supported. 606 */ 607 return (0); 608 } 609 610 /* 611 * Verify that the IE log page is sane. This log page is potentially chock-full 612 * of vendor specific information that we do not know how to access. All we can 613 * do is check for the generic predictive failure bit. If this log page is not 614 * well-formed, then bail out. 615 */ 616 static int 617 logpage_ie_verify(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 618 int log_length, nvlist_t *nvl) 619 { 620 int i, plen = 0; 621 boolean_t seen = B_FALSE; 622 scsi_ie_log_param_t *iep = 623 (scsi_ie_log_param_t *)lphp; 624 625 for (i = 0; i < log_length; i += plen) { 626 iep = (scsi_ie_log_param_t *)((char *)iep + plen); 627 628 if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE) { 629 if (nvlist_add_boolean_value(nvl, "general", 630 B_TRUE) != 0) 631 return (scsi_set_errno(sip, EDS_NOMEM)); 632 633 if (lphp->lph_length < LOGPARAM_IE_MIN_LEN) { 634 if (nvlist_add_uint8(nvl, 635 "invalid-length", lphp->lph_length) != 0) 636 return (scsi_set_errno(sip, EDS_NOMEM)); 637 } else { 638 seen = B_TRUE; 639 } 640 break; 641 } 642 643 plen = iep->ie_hdr.lph_length + 644 sizeof (scsi_log_parameter_header_t); 645 } 646 647 if (!seen) { 648 sip->si_supp_log &= ~LOGPAGE_SUPP_IE; 649 dprintf("IE logpage validation failed\n"); 650 } 651 652 return (0); 653 } 654 655 /* 656 * Verify the contents of the temperature log page. The temperature log page 657 * contains two log parameters: the current temperature, and (optionally) the 658 * reference temperature. For the verification phase, we check that the two 659 * parameters we care about are well-formed. If there is no reference 660 * temperature, then we cannot use the page for monitoring purposes. 661 */ 662 static int 663 logpage_temp_verify(ds_scsi_info_t *sip, 664 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl) 665 { 666 int i, plen = 0; 667 boolean_t has_reftemp = B_FALSE; 668 boolean_t bad_length = B_FALSE; 669 ushort_t param_code; 670 671 for (i = 0; i < log_length; i += plen) { 672 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 673 param_code = BE_16(lphp->lph_param); 674 675 switch (param_code) { 676 case LOGPARAM_TEMP_CURTEMP: 677 if (nvlist_add_boolean_value(nvl, "current-temperature", 678 B_TRUE) != 0) 679 return (scsi_set_errno(sip, EDS_NOMEM)); 680 if (lphp->lph_length != LOGPARAM_TEMP_LEN) { 681 if (nvlist_add_uint8(nvl, 682 "invalid-length", lphp->lph_length) != 0) 683 return (scsi_set_errno(sip, EDS_NOMEM)); 684 bad_length = B_TRUE; 685 } 686 break; 687 688 case LOGPARAM_TEMP_REFTEMP: 689 if (nvlist_add_boolean_value(nvl, 690 "reference-temperature", B_TRUE) != 0) 691 return (scsi_set_errno(sip, EDS_NOMEM)); 692 if (lphp->lph_length != LOGPARAM_TEMP_LEN) { 693 if (nvlist_add_uint8(nvl, 694 "invalid-length", lphp->lph_length) != 0) 695 return (scsi_set_errno(sip, EDS_NOMEM)); 696 bad_length = B_TRUE; 697 } 698 has_reftemp = B_TRUE; 699 break; 700 } 701 702 plen = lphp->lph_length + 703 sizeof (scsi_log_parameter_header_t); 704 } 705 706 if (bad_length || !has_reftemp) { 707 sip->si_supp_log &= ~LOGPAGE_SUPP_TEMP; 708 dprintf("temperature logpage validation failed\n"); 709 } 710 711 return (0); 712 } 713 714 /* 715 * Verify the contents of the self test log page. The log supports a maximum of 716 * 20 entries, where each entry's parameter code is its index in the log. We 717 * check that the parameter codes fall within this range, and that the size of 718 * each page is what we expect. It's perfectly acceptable for there to be no 719 * entries in this log, so we must also be sure to validate the contents as part 720 * of the analysis phase. 721 */ 722 static int 723 logpage_selftest_verify(ds_scsi_info_t *sip, 724 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl) 725 { 726 int i, plen = 0; 727 boolean_t bad = B_FALSE; 728 int entries = 0; 729 ushort_t param_code; 730 731 for (i = 0; i < log_length; i += plen, entries++) { 732 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 733 param_code = BE_16(lphp->lph_param); 734 735 if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE || 736 param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) { 737 if (nvlist_add_uint16(nvl, "invalid-param-code", 738 param_code) != 0) 739 return (scsi_set_errno(sip, EDS_NOMEM)); 740 bad = B_TRUE; 741 break; 742 } 743 744 if (lphp->lph_length != LOGPAGE_SELFTEST_PARAM_LEN) { 745 if (nvlist_add_uint8(nvl, "invalid-length", 746 lphp->lph_length) != 0) 747 return (scsi_set_errno(sip, EDS_NOMEM)); 748 bad = B_TRUE; 749 break; 750 751 } 752 753 plen = lphp->lph_length + 754 sizeof (scsi_log_parameter_header_t); 755 } 756 757 if (bad) { 758 sip->si_supp_log &= ~LOGPAGE_SUPP_SELFTEST; 759 dprintf("selftest logpage validation failed\n"); 760 } 761 762 return (0); 763 } 764 765 /* 766 * Verify the contents of the Solid State Media (SSM) log page. 767 * As of SBC3r36 SSM log page contains one log parameter: 768 * "Percentage Used Endurance Indicator" which is mandatory. 769 * For the verification phase, we sanity check this parameter 770 * by making sure it's present and it's length is set to 0x04. 771 */ 772 static int 773 logpage_ssm_verify(ds_scsi_info_t *sip, 774 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl) 775 { 776 ushort_t param_code; 777 int i, plen = 0; 778 779 for (i = 0; i < log_length; i += plen) { 780 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 781 param_code = BE_16(lphp->lph_param); 782 783 switch (param_code) { 784 case LOGPARAM_PRCNT_USED: 785 if (nvlist_add_boolean_value(nvl, 786 FM_EREPORT_SCSI_SSMWEAROUT, B_TRUE) != 0) 787 return (scsi_set_errno(sip, EDS_NOMEM)); 788 if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN) { 789 if (nvlist_add_uint8(nvl, 790 "invalid-length", lphp->lph_length) != 0) 791 return (scsi_set_errno(sip, EDS_NOMEM)); 792 793 dprintf("solid state media logpage bad len\n"); 794 break; 795 } 796 797 /* verification succeded */ 798 return (0); 799 } 800 801 plen = lphp->lph_length + 802 sizeof (scsi_log_parameter_header_t); 803 } 804 805 /* verification failed */ 806 sip->si_supp_log &= ~LOGPAGE_SUPP_SSM; 807 return (0); 808 } 809 810 /* 811 * Load the current IE mode pages 812 */ 813 static int 814 load_ie_modepage(ds_scsi_info_t *sip) 815 { 816 struct scsi_ms_hdrs junk_hdrs; 817 int result; 818 uint_t skey, asc, ascq; 819 820 if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC)) 821 return (0); 822 823 bzero(&sip->si_iec_current, sizeof (sip->si_iec_current)); 824 bzero(&sip->si_iec_changeable, sizeof (sip->si_iec_changeable)); 825 826 if ((result = scsi_mode_sense(sip, 827 MODEPAGE_INFO_EXCPT, PC_CURRENT, &sip->si_iec_current, 828 MODEPAGE_INFO_EXCPT_LEN, &sip->si_hdrs, &skey, &asc, 829 &ascq)) == 0) { 830 result = scsi_mode_sense(sip, 831 MODEPAGE_INFO_EXCPT, PC_CHANGEABLE, 832 &sip->si_iec_changeable, 833 MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, &skey, &asc, &ascq); 834 } 835 836 if (result != 0) { 837 dprintf("failed to get IEC modepage (KEY=0x%x " 838 "ASC=0x%x ASCQ=0x%x)", skey, asc, ascq); 839 sip->si_supp_mode &= ~MODEPAGE_SUPP_IEC; 840 } else { 841 if (nvlist_add_boolean_value(sip->si_state_iec, 842 "dexcpt", sip->si_iec_current.ie_dexcpt) != 0 || 843 nvlist_add_boolean_value(sip->si_state_iec, 844 "logerr", sip->si_iec_current.ie_logerr) != 0 || 845 nvlist_add_uint8(sip->si_state_iec, 846 "mrie", sip->si_iec_current.ie_mrie) != 0 || 847 nvlist_add_boolean_value(sip->si_state_iec, 848 "test", sip->si_iec_current.ie_test) != 0 || 849 nvlist_add_boolean_value(sip->si_state_iec, 850 "ewasc", sip->si_iec_current.ie_ewasc) != 0 || 851 nvlist_add_boolean_value(sip->si_state_iec, 852 "perf", sip->si_iec_current.ie_perf) != 0 || 853 nvlist_add_boolean_value(sip->si_state_iec, 854 "ebf", sip->si_iec_current.ie_ebf) != 0 || 855 nvlist_add_uint32(sip->si_state_iec, 856 "interval-timer", 857 BE_32(sip->si_iec_current.ie_interval_timer)) != 0 || 858 nvlist_add_uint32(sip->si_state_iec, 859 "report-count", 860 BE_32(sip->si_iec_current.ie_report_count)) != 0) 861 return (scsi_set_errno(sip, EDS_NOMEM)); 862 } 863 864 return (0); 865 } 866 867 /* 868 * Enable IE reporting. We prefer the following settings: 869 * 870 * (1) DEXCPT = 0 871 * (3) MRIE = 6 (IE_REPORT_ON_REQUEST) 872 * (4) EWASC = 1 873 * (6) REPORT COUNT = 0x00000001 874 * (7) LOGERR = 1 875 * 876 * However, not all drives support changing these values, and the current state 877 * may be useful enough as-is. For example, some drives support IE logging, but 878 * don't support changing the MRIE. In this case, we can still use the 879 * information provided by the log page. 880 */ 881 static int 882 scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed) 883 { 884 scsi_ie_page_t new_iec_page; 885 scsi_ms_hdrs_t hdrs; 886 uint_t skey, asc, ascq; 887 888 if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC)) 889 return (0); 890 891 bzero(&new_iec_page, sizeof (new_iec_page)); 892 bzero(&hdrs, sizeof (hdrs)); 893 894 (void) memcpy(&new_iec_page, &sip->si_iec_current, 895 sizeof (new_iec_page)); 896 897 if (IEC_IE_CHANGEABLE(sip->si_iec_changeable)) 898 new_iec_page.ie_dexcpt = 0; 899 900 if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable)) 901 new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST; 902 903 /* 904 * We only want to enable warning reporting if we are able to change the 905 * mrie to report on request. Otherwise, we risk unnecessarily 906 * interrupting normal SCSI commands with a CHECK CONDITION code. 907 */ 908 if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) { 909 if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST) 910 new_iec_page.ie_ewasc = 1; 911 else 912 new_iec_page.ie_ewasc = 0; 913 } 914 915 if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable)) 916 new_iec_page.ie_report_count = BE_32(1); 917 918 if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable)) 919 new_iec_page.ie_logerr = 1; 920 921 /* 922 * Now compare the new mode page with the existing one. 923 * if there's no difference, there's no need for a mode select 924 */ 925 if (memcmp(&new_iec_page, &sip->si_iec_current, 926 MODEPAGE_INFO_EXCPT_LEN) == 0) { 927 *changed = B_FALSE; 928 } else { 929 (void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs)); 930 931 if (scsi_mode_select(sip, 932 MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page, 933 MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) { 934 *changed = B_TRUE; 935 } else { 936 dprintf("failed to enable IE (KEY=0x%x " 937 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); 938 *changed = B_FALSE; 939 } 940 } 941 942 if (nvlist_add_boolean_value(sip->si_state_iec, "changed", 943 *changed) != 0) 944 return (scsi_set_errno(sip, EDS_NOMEM)); 945 946 return (0); 947 } 948 949 /* 950 * Clear the GLTSD bit, indicating log pages should be saved to non-volatile 951 * storage. 952 */ 953 static int 954 clear_gltsd(ds_scsi_info_t *sip) 955 { 956 scsi_ms_hdrs_t hdrs, junk_hdrs; 957 struct mode_control_scsi3 control_pg_cur, control_pg_chg; 958 int result; 959 uint_t skey, asc, ascq; 960 961 bzero(&hdrs, sizeof (hdrs)); 962 bzero(&control_pg_cur, sizeof (control_pg_cur)); 963 bzero(&control_pg_chg, sizeof (control_pg_chg)); 964 965 result = scsi_mode_sense(sip, 966 MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur, 967 MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq); 968 969 if (result != 0) { 970 dprintf("failed to read Control mode page (KEY=0x%x " 971 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); 972 } else if (control_pg_cur.mode_page.length != 973 PAGELENGTH_MODE_CONTROL_SCSI3) { 974 dprintf("SCSI-3 control mode page not supported\n"); 975 } else if ((result = scsi_mode_sense(sip, 976 MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg, 977 MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq)) 978 != 0) { 979 dprintf("failed to read changeable Control mode page (KEY=0x%x " 980 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); 981 } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) { 982 dprintf("gltsd is set and not changeable\n"); 983 if (nvlist_add_boolean_value(sip->si_dsp->ds_state, 984 "gltsd", control_pg_cur.gltsd) != 0) 985 return (scsi_set_errno(sip, EDS_NOMEM)); 986 } else if (control_pg_cur.gltsd) { 987 control_pg_cur.gltsd = 0; 988 result = scsi_mode_select(sip, 989 MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur, 990 MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq); 991 if (result != 0) 992 dprintf("failed to enable GLTSD (KEY=0x%x " 993 "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq); 994 if (nvlist_add_boolean_value(sip->si_dsp->ds_state, 995 "gltsd", control_pg_cur.gltsd) != 0) 996 return (scsi_set_errno(sip, EDS_NOMEM)); 997 } 998 999 return (0); 1000 } 1001 1002 /* 1003 * Fetch the contents of the logpage, and then call the logpage-specific 1004 * analysis function. The analysis function is responsible for detecting any 1005 * faults and filling in the details. 1006 */ 1007 static int 1008 analyze_one_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *entry) 1009 { 1010 scsi_log_header_t *lhp; 1011 scsi_log_parameter_header_t *lphp; 1012 int buflen; 1013 int log_length; 1014 uint_t skey, asc, ascq; 1015 int result; 1016 1017 buflen = MAX_BUFLEN(scsi_log_header_t); 1018 if ((lhp = calloc(buflen, 1)) == NULL) 1019 return (scsi_set_errno(sip, EDS_NOMEM)); 1020 1021 result = scsi_log_sense(sip, entry->ve_code, 1022 PC_CUMULATIVE, (caddr_t)lhp, buflen, &skey, &asc, &ascq); 1023 1024 if (result == 0) { 1025 log_length = BE_16(lhp->lh_length); 1026 lphp = (scsi_log_parameter_header_t *)(((uchar_t *)lhp) + 1027 sizeof (scsi_log_header_t)); 1028 1029 result = entry->ve_analyze(sip, lphp, log_length); 1030 } else { 1031 result = scsi_set_errno(sip, EDS_IO); 1032 } 1033 1034 free(lhp); 1035 return (result); 1036 } 1037 1038 /* 1039 * Analyze the IE logpage. If we find an IE log record with a non-zero 'asc', 1040 * then we have a fault. 1041 */ 1042 static int 1043 logpage_ie_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 1044 int log_length) 1045 { 1046 int i, plen = 0; 1047 scsi_ie_log_param_t *iep = (scsi_ie_log_param_t *)lphp; 1048 nvlist_t *nvl; 1049 1050 assert(sip->si_dsp->ds_predfail == NULL); 1051 if (nvlist_alloc(&sip->si_dsp->ds_predfail, NV_UNIQUE_NAME, 0) != 0) 1052 return (scsi_set_errno(sip, EDS_NOMEM)); 1053 nvl = sip->si_dsp->ds_predfail; 1054 1055 for (i = 0; i < log_length; i += plen) { 1056 iep = (scsi_ie_log_param_t *)((char *)iep + plen); 1057 1058 /* 1059 * Even though we validated the length during the initial phase, 1060 * never trust the device. 1061 */ 1062 if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE && 1063 iep->ie_hdr.lph_length >= LOGPARAM_IE_MIN_LEN) { 1064 if (nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASC, 1065 iep->ie_asc) != 0 || 1066 nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASCQ, 1067 iep->ie_ascq) != 0) 1068 return (scsi_set_errno(sip, EDS_NOMEM)); 1069 1070 if (iep->ie_asc != 0) 1071 sip->si_dsp->ds_faults |= 1072 DS_FAULT_PREDFAIL; 1073 break; 1074 } 1075 plen = iep->ie_hdr.lph_length + 1076 sizeof (scsi_log_parameter_header_t); 1077 } 1078 1079 return (0); 1080 } 1081 1082 static int 1083 logpage_temp_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 1084 int log_length) 1085 { 1086 int i, plen = 0; 1087 uint8_t reftemp, curtemp; 1088 ushort_t param_code; 1089 scsi_temp_log_param_t *temp; 1090 nvlist_t *nvl; 1091 1092 assert(sip->si_dsp->ds_overtemp == NULL); 1093 if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0) 1094 return (scsi_set_errno(sip, EDS_NOMEM)); 1095 nvl = sip->si_dsp->ds_overtemp; 1096 1097 reftemp = curtemp = INVALID_TEMPERATURE; 1098 for (i = 0; i < log_length; i += plen) { 1099 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 1100 param_code = BE_16(lphp->lph_param); 1101 temp = (scsi_temp_log_param_t *)lphp; 1102 1103 switch (param_code) { 1104 case LOGPARAM_TEMP_CURTEMP: 1105 if (lphp->lph_length != LOGPARAM_TEMP_LEN) 1106 break; 1107 1108 if (nvlist_add_uint8(nvl, 1109 FM_EREPORT_PAYLOAD_SCSI_CURTEMP, 1110 temp->t_temp) != 0) 1111 return (scsi_set_errno(sip, EDS_NOMEM)); 1112 curtemp = temp->t_temp; 1113 break; 1114 1115 case LOGPARAM_TEMP_REFTEMP: 1116 if (lphp->lph_length != LOGPARAM_TEMP_LEN) 1117 break; 1118 1119 if (nvlist_add_uint8(nvl, 1120 FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP, 1121 temp->t_temp) != 0) 1122 return (scsi_set_errno(sip, EDS_NOMEM)); 1123 reftemp = temp->t_temp; 1124 break; 1125 } 1126 1127 plen = lphp->lph_length + 1128 sizeof (scsi_log_parameter_header_t); 1129 } 1130 1131 if (reftemp != INVALID_TEMPERATURE && curtemp != INVALID_TEMPERATURE && 1132 curtemp > reftemp) 1133 sip->si_dsp->ds_faults |= DS_FAULT_OVERTEMP; 1134 1135 return (0); 1136 } 1137 1138 static int 1139 logpage_selftest_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 1140 int log_length) 1141 { 1142 int i, plen = 0; 1143 int entries = 0; 1144 ushort_t param_code; 1145 scsi_selftest_log_param_t *stp; 1146 nvlist_t *nvl; 1147 1148 assert(sip->si_dsp->ds_testfail == NULL); 1149 if (nvlist_alloc(&sip->si_dsp->ds_testfail, NV_UNIQUE_NAME, 0) != 0) 1150 return (scsi_set_errno(sip, EDS_NOMEM)); 1151 nvl = sip->si_dsp->ds_testfail; 1152 1153 for (i = 0; i < log_length; i += plen, entries++) { 1154 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); 1155 param_code = BE_16(lphp->lph_param); 1156 stp = (scsi_selftest_log_param_t *)lphp; 1157 1158 if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE && 1159 param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE && 1160 lphp->lph_length >= LOGPAGE_SELFTEST_PARAM_LEN) { 1161 /* 1162 * We always log the last result, or the result of the 1163 * last completed test. 1164 */ 1165 if ((param_code == 1 || 1166 SELFTEST_COMPLETE(stp->st_results))) { 1167 if (nvlist_add_uint8(nvl, 1168 FM_EREPORT_PAYLOAD_SCSI_RESULTCODE, 1169 stp->st_results) != 0 || 1170 nvlist_add_uint16(nvl, 1171 FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP, 1172 BE_16(stp->st_timestamp)) != 0 || 1173 nvlist_add_uint8(nvl, 1174 FM_EREPORT_PAYLOAD_SCSI_SEGMENT, 1175 stp->st_number) != 0 || 1176 nvlist_add_uint64(nvl, 1177 FM_EREPORT_PAYLOAD_SCSI_ADDRESS, 1178 BE_64(stp->st_lba)) != 0) 1179 return (scsi_set_errno(sip, 1180 EDS_NOMEM)); 1181 1182 if (SELFTEST_COMPLETE(stp->st_results)) { 1183 if (stp->st_results != SELFTEST_OK) 1184 sip->si_dsp->ds_faults |= 1185 DS_FAULT_TESTFAIL; 1186 return (0); 1187 } 1188 } 1189 } 1190 1191 plen = lphp->lph_length + 1192 sizeof (scsi_log_parameter_header_t); 1193 } 1194 1195 return (0); 1196 } 1197 1198 /* 1199 * Analyze the contents of the Solid State Media (SSM) log page's 1200 * "Percentage Used Endurance Indicator" log parameter. 1201 * We generate a fault if the percentage used is equal to or over 1202 * PRCNT_USED_FAULT_THRSH 1203 */ 1204 static int 1205 logpage_ssm_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, 1206 int log_length) 1207 { 1208 uint16_t param_code; 1209 scsi_ssm_log_param_t *ssm; 1210 nvlist_t *nvl; 1211 int i, plen = 0; 1212 1213 assert(sip->si_dsp->ds_overtemp == NULL); 1214 if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0) 1215 return (scsi_set_errno(sip, EDS_NOMEM)); 1216 nvl = sip->si_dsp->ds_overtemp; 1217 1218 for (i = 0; i < log_length; i += plen) { 1219 lphp = (scsi_log_parameter_header_t *)((uint8_t *)lphp + plen); 1220 param_code = BE_16(lphp->lph_param); 1221 ssm = (scsi_ssm_log_param_t *)lphp; 1222 1223 switch (param_code) { 1224 case LOGPARAM_PRCNT_USED: 1225 if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN) 1226 break; 1227 1228 if ((nvlist_add_uint8(nvl, 1229 FM_EREPORT_PAYLOAD_SCSI_CURSSMWEAROUT, 1230 ssm->ssm_prcnt_used) != 0) || 1231 (nvlist_add_uint8(nvl, 1232 FM_EREPORT_PAYLOAD_SCSI_THRSHSSMWEAROUT, 1233 PRCNT_USED_FAULT_THRSH) != 0)) 1234 return (scsi_set_errno(sip, EDS_NOMEM)); 1235 1236 if (ssm->ssm_prcnt_used >= PRCNT_USED_FAULT_THRSH) 1237 sip->si_dsp->ds_faults |= DS_FAULT_SSMWEAROUT; 1238 1239 return (0); 1240 } 1241 1242 plen = lphp->lph_length + 1243 sizeof (scsi_log_parameter_header_t); 1244 } 1245 1246 /* 1247 * If we got this far we didn't see LOGPARAM_PRCNT_USED 1248 * which is strange since we verified that it's there 1249 */ 1250 dprintf("solid state media logpage analyze failed\n"); 1251 #if DEBUG 1252 abort(); 1253 #endif 1254 return (scsi_set_errno(sip, EDS_NOT_SUPPORTED)); 1255 } 1256 1257 /* 1258 * Analyze the IE mode sense page explicitly. This is only needed if the IE log 1259 * page is not supported. 1260 */ 1261 static int 1262 analyze_ie_sense(ds_scsi_info_t *sip) 1263 { 1264 uint_t skey, asc, ascq; 1265 nvlist_t *nvl; 1266 1267 /* 1268 * Don't bother checking if we weren't able to set our MRIE correctly. 1269 */ 1270 if (sip->si_iec_current.ie_mrie != IE_REPORT_ON_REQUEST) 1271 return (0); 1272 1273 if (scsi_request_sense(sip, &skey, &asc, &ascq) != 0) { 1274 dprintf("failed to request IE page (KEY=0x%x ASC=0x%x " 1275 "ASCQ=0x%x)\n", skey, asc, ascq); 1276 return (scsi_set_errno(sip, EDS_IO)); 1277 } else if (skey == KEY_NO_SENSE) { 1278 assert(sip->si_dsp->ds_predfail == NULL); 1279 if (nvlist_alloc(&sip->si_dsp->ds_predfail, 1280 NV_UNIQUE_NAME, 0) != 0) 1281 return (scsi_set_errno(sip, EDS_NOMEM)); 1282 nvl = sip->si_dsp->ds_predfail; 1283 1284 if (nvlist_add_uint8(nvl, 1285 FM_EREPORT_PAYLOAD_SCSI_ASC, asc) != 0 || 1286 nvlist_add_uint8(nvl, 1287 FM_EREPORT_PAYLOAD_SCSI_ASCQ, ascq) != 0) { 1288 nvlist_free(nvl); 1289 return (scsi_set_errno(sip, EDS_NOMEM)); 1290 } 1291 1292 if (asc != 0) 1293 sip->si_dsp->ds_faults |= DS_FAULT_PREDFAIL; 1294 } 1295 1296 return (0); 1297 } 1298 1299 /* 1300 * Clean up the scsi-specific information structure. 1301 */ 1302 static void 1303 ds_scsi_close(void *arg) 1304 { 1305 ds_scsi_info_t *sip = arg; 1306 if (sip->si_sim) 1307 (void) dlclose(sip->si_sim); 1308 1309 free(sip); 1310 } 1311 1312 /* 1313 * Initialize a single disk. Initialization consists of: 1314 * 1315 * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE 1316 * Control page (page 0x1C). 1317 * 1318 * 2. If the IE page is available, try to set the following parameters: 1319 * 1320 * DEXCPT 0 Enable exceptions 1321 * MRIE 6 Only report IE information on request 1322 * EWASC 1 Enable warning reporting 1323 * REPORT COUNT 1 Only report an IE exception once 1324 * LOGERR 1 Enable logging of errors 1325 * 1326 * The remaining fields are left as-is, preserving the current values. If we 1327 * cannot set some of these fields, then we do our best. Some drives may 1328 * have a static configuration which still allows for some monitoring. 1329 * 1330 * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a 1331 * LOG SENSE command. 1332 * 1333 * 4. Check to see if the self-test log page (page 0x10) is supported. 1334 * 1335 * 5. Check to see if the temperature log page (page 0x0D) is supported, and 1336 * contains a reference temperature. 1337 * 1338 * 6. Clear the GLTSD bit in control mode page 0xA. This will allow the drive 1339 * to save each of the log pages described above to nonvolatile storage. 1340 * This is essential if the drive is to remember its failures across 1341 * loss of power. 1342 */ 1343 static void * 1344 ds_scsi_open_common(disk_status_t *dsp, ds_scsi_info_t *sip) 1345 { 1346 boolean_t changed; 1347 1348 sip->si_dsp = dsp; 1349 1350 /* Load and validate mode pages */ 1351 if (load_modepages(sip) != 0) { 1352 ds_scsi_close(sip); 1353 return (NULL); 1354 } 1355 1356 /* Load and validate log pages */ 1357 if (load_logpages(sip) != 0) { 1358 ds_scsi_close(sip); 1359 return (NULL); 1360 } 1361 1362 /* Load IE state */ 1363 if (load_ie_modepage(sip) != 0 || 1364 scsi_enable_ie(sip, &changed) != 0 || 1365 (changed && load_ie_modepage(sip) != 0)) { 1366 ds_scsi_close(sip); 1367 return (NULL); 1368 } 1369 1370 /* Clear the GLTSD bit in the control page */ 1371 if (sip->si_supp_log != 0 && clear_gltsd(sip) != 0) { 1372 ds_scsi_close(sip); 1373 return (NULL); 1374 } 1375 1376 return (sip); 1377 } 1378 1379 static void * 1380 ds_scsi_open_uscsi(disk_status_t *dsp) 1381 { 1382 ds_scsi_info_t *sip; 1383 1384 if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) { 1385 (void) ds_set_errno(dsp, EDS_NOMEM); 1386 return (NULL); 1387 } 1388 1389 return (ds_scsi_open_common(dsp, sip)); 1390 } 1391 1392 static void * 1393 ds_scsi_open_sim(disk_status_t *dsp) 1394 { 1395 ds_scsi_info_t *sip; 1396 1397 if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) { 1398 (void) ds_set_errno(dsp, EDS_NOMEM); 1399 return (NULL); 1400 } 1401 1402 if ((sip->si_sim = dlopen(dsp->ds_path, RTLD_LAZY)) == NULL) { 1403 (void) ds_set_errno(dsp, EDS_NO_TRANSPORT); 1404 free(sip); 1405 return (NULL); 1406 } 1407 1408 return (ds_scsi_open_common(dsp, sip)); 1409 } 1410 1411 1412 /* 1413 * Scan for any faults. The following steps are performed: 1414 * 1415 * 1. If the temperature log page is supported, check the current temperature 1416 * and threshold. If the current temperature exceeds the threshold, report 1417 * and overtemp fault. 1418 * 1419 * 2. If the selftest log page is supported, check to the last completed self 1420 * test. If the last completed test resulted in failure, report a selftest 1421 * fault. 1422 * 1423 * 3. If the IE log page is supported, check to see if failure is predicted. If 1424 * so, indicate a predictive failure fault. 1425 * 1426 * 4. If the IE log page is not supported, but the mode page supports report on 1427 * request mode, then issue a REQUEST SENSE for the mode page. Indicate a 1428 * predictive failure fault if necessary. 1429 */ 1430 static int 1431 ds_scsi_scan(void *arg) 1432 { 1433 ds_scsi_info_t *sip = arg; 1434 int i; 1435 1436 for (i = 0; i < NLOG_VALIDATION; i++) { 1437 if ((sip->si_supp_log & log_validation[i].ve_supported) == 0) 1438 continue; 1439 1440 if (analyze_one_logpage(sip, &log_validation[i]) != 0) 1441 return (-1); 1442 } 1443 1444 if (!(sip->si_supp_log & LOGPAGE_SUPP_IE) && 1445 (sip->si_supp_mode & MODEPAGE_SUPP_IEC) && 1446 analyze_ie_sense(sip) != 0) 1447 return (-1); 1448 1449 return (0); 1450 } 1451 1452 ds_transport_t ds_scsi_uscsi_transport = { 1453 ds_scsi_open_uscsi, 1454 ds_scsi_close, 1455 ds_scsi_scan 1456 }; 1457 1458 ds_transport_t ds_scsi_sim_transport = { 1459 ds_scsi_open_sim, 1460 ds_scsi_close, 1461 ds_scsi_scan 1462 }; 1463