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