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.
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.
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]
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.
29 #include <libdiskstatus.h>
33 #include <sys/fm/io/scsi.h>
36 #include "ds_scsi_sim.h"
37 #include "ds_scsi_uscsi.h"
39 typedef struct ds_scsi_info
{
40 disk_status_t
*si_dsp
;
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
;
55 #define scsi_set_errno(sip, errno) (ds_set_errno((sip)->si_dsp, (errno)))
58 * Table to validate log pages
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);
65 typedef struct logpage_validation_entry
{
69 logpage_validation_fn_t ve_validate
;
70 logpage_analyze_fn_t ve_analyze
;
71 } logpage_validation_entry_t
;
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
*);
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);
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
,
97 logpage_temp_verify
, logpage_temp_analyze
},
98 { LOGPAGE_SELFTEST
, LOGPAGE_SUPP_SELFTEST
,
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
}
106 #define NLOG_VALIDATION (sizeof (log_validation) / sizeof (log_validation[0]))
109 * Given an extended sense page, retrieves the sense key, as well as the
110 * additional sense code information.
113 scsi_translate_error(struct scsi_extended_sense
*rq
, uint_t
*skeyp
,
114 uint_t
*ascp
, uint_t
*ascqp
)
116 struct scsi_descr_sense_hdr
*sdsp
=
117 (struct scsi_descr_sense_hdr
*)rq
;
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.
126 switch (rq
->es_code
) {
127 case CODE_FMT_DESCR_CURRENT
:
128 case CODE_FMT_DESCR_DEFERRED
:
130 *ascp
= sdsp
->ds_add_code
;
131 *ascqp
= sdsp
->ds_qual_code
;
134 case CODE_FMT_FIXED_CURRENT
:
135 case CODE_FMT_FIXED_DEFERRED
:
138 if (rq
->es_add_len
>= 6) {
139 *ascp
= rq
->es_add_code
;
140 *ascqp
= rq
->es_qual_code
;
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.
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
)
160 struct scsi_extended_sense sense
;
161 int senselen
= sizeof (struct scsi_extended_sense
);
162 struct mode_page
*mp
= (struct mode_page
*)buf
;
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
);
168 bzero(&sense
, sizeof (struct scsi_extended_sense
));
171 options
|= MODE_SELECT_SP
;
174 options
&= ~MODE_SELECT_SP
;
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;
183 result
= simscsi_mode_select(sip
->si_sim
,
184 page_code
, options
, buf
, buflen
,
185 &headers
->ms_hdr
.g0
, &sense
, &senselen
);
187 result
= uscsi_mode_select(sip
->si_dsp
->ds_fd
,
188 page_code
, options
, buf
, buflen
,
189 &headers
->ms_hdr
.g0
, &sense
, &senselen
);
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;
196 result
= simscsi_mode_select_10(sip
->si_sim
,
197 page_code
, options
, buf
, buflen
,
198 &headers
->ms_hdr
.g1
, &sense
, &senselen
);
200 result
= uscsi_mode_select_10(sip
->si_dsp
->ds_fd
,
201 page_code
, options
, buf
, buflen
,
202 &headers
->ms_hdr
.g1
, &sense
, &senselen
);
206 scsi_translate_error(&sense
, skp
, ascp
, ascqp
);
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
)
217 struct scsi_extended_sense sense
;
218 int senselen
= sizeof (struct scsi_extended_sense
);
220 assert(sip
->si_cdblen
== MODE_CMD_LEN_6
||
221 sip
->si_cdblen
== MODE_CMD_LEN_10
);
223 bzero(&sense
, sizeof (struct scsi_extended_sense
));
225 bzero(headers
, sizeof (scsi_ms_hdrs_t
));
226 headers
->ms_length
= sip
->si_cdblen
;
228 if (sip
->si_cdblen
== MODE_CMD_LEN_6
) {
230 result
= simscsi_mode_sense(sip
->si_sim
,
231 page_code
, pc
, buf
, buflen
, &headers
->ms_hdr
.g0
,
234 result
= uscsi_mode_sense(sip
->si_dsp
->ds_fd
, page_code
,
235 pc
, buf
, buflen
, &headers
->ms_hdr
.g0
, &sense
,
239 result
= simscsi_mode_sense_10(sip
->si_sim
,
240 page_code
, pc
, buf
, buflen
, &headers
->ms_hdr
.g1
,
243 result
= uscsi_mode_sense_10(sip
->si_dsp
->ds_fd
,
244 page_code
, pc
, buf
, buflen
, &headers
->ms_hdr
.g1
,
249 scsi_translate_error(&sense
, skp
, ascp
, ascqp
);
255 scsi_request_sense(ds_scsi_info_t
*sip
, uint_t
*skp
, uint_t
*ascp
,
258 struct scsi_extended_sense sense
, sensebuf
;
259 int senselen
= sizeof (struct scsi_extended_sense
);
260 int sensebuflen
= sizeof (struct scsi_extended_sense
);
263 bzero(&sense
, sizeof (struct scsi_extended_sense
));
264 bzero(&sensebuf
, sizeof (struct scsi_extended_sense
));
267 result
= simscsi_request_sense(sip
->si_sim
,
268 (caddr_t
)&sensebuf
, sensebuflen
, &sense
, &senselen
);
270 result
= uscsi_request_sense(sip
->si_dsp
->ds_fd
,
271 (caddr_t
)&sensebuf
, sensebuflen
, &sense
, &senselen
);
274 scsi_translate_error(&sensebuf
, skp
, ascp
, ascqp
);
276 scsi_translate_error(&sense
, skp
, ascp
, ascqp
);
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
)
286 struct scsi_extended_sense sense
;
287 int senselen
= sizeof (struct scsi_extended_sense
);
290 result
= simscsi_log_sense(sip
->si_sim
,
291 page_code
, page_control
, page_data
, page_size
, &sense
,
294 result
= uscsi_log_sense(sip
->si_dsp
->ds_fd
,
295 page_code
, page_control
, page_data
, page_size
, &sense
,
299 scsi_translate_error(&sense
, skp
, ascp
, ascqp
);
305 * Given a list of supported mode pages, determine if the given page is present.
308 mode_page_present(uchar_t
*pgdata
, uint_t pgdatalen
, uchar_t pagecode
)
311 struct mode_page
*pg
;
312 boolean_t found
= B_FALSE
;
315 * The mode page list contains all mode pages supported by the device,
316 * one after the other.
318 while (i
< pgdatalen
) {
319 pg
= (struct mode_page
*)&pgdata
[i
];
321 if (pg
->code
== pagecode
) {
326 i
+= MODESENSE_PAGE_LEN(pg
);
333 * Load mode pages and check that the appropriate pages are supported.
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.
340 load_modepages(ds_scsi_info_t
*sip
)
344 scsi_ms_hdrs_t headers
;
346 uint_t skey
, asc
, ascq
;
348 scsi_ms_header_t
*smh
= &headers
.ms_hdr
.g0
;
349 scsi_ms_header_g1_t
*smh_g1
= &headers
.ms_hdr
.g1
;
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
));
356 bzero(&headers
, sizeof (headers
));
359 * Attempt a mode sense(6). If that fails, try a mode sense(10)
361 * allpages is allocated to be of the maximum size for either a mode
362 * sense(6) or mode sense(10) MODEPAGE_ALLPAGES response.
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.
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) {
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:
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> ...]
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.
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]].
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
)) {
398 * Fallback and try the 10-byte version of the command.
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
);
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
));
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) {
418 return (scsi_set_errno(sip
, EDS_NOMEM
));
422 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
423 nvlist_add_nvlist(sip
->si_dsp
->ds_state
, "modepages",
427 return (scsi_set_errno(sip
, EDS_NOMEM
));
431 result
= nvlist_lookup_nvlist(sip
->si_dsp
->ds_state
,
432 "modepages", &sip
->si_state_modepage
);
436 * One of the sets of the commands (above) succeeded, so now
437 * look for the mode pages we need and record them appropriately
439 if (mode_page_present(allpages
, datalength
,
440 MODEPAGE_INFO_EXCPT
)) {
443 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
444 nvlist_add_nvlist(sip
->si_state_modepage
,
445 "informational-exceptions", nvl
) != 0) {
448 return (scsi_set_errno(sip
, EDS_NOMEM
));
451 sip
->si_supp_mode
|= MODEPAGE_SUPP_IEC
;
452 result
= nvlist_lookup_nvlist(sip
->si_state_modepage
,
453 "informational-exceptions", &sip
->si_state_iec
);
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.
464 dprintf("command returned invalid data length (%d)\n",
467 dprintf("failed to load modepages (KEY=0x%x "
468 "ASC=0x%x ASCQ=0x%x)\n", skey
, asc
, ascq
);
470 result
= scsi_set_errno(sip
, EDS_NO_TRANSPORT
);
478 * Verify a single logpage. This will do some generic validation and then call
479 * the logpage-specific function for further verification.
482 verify_logpage(ds_scsi_info_t
*sip
, logpage_validation_entry_t
*lp
)
484 scsi_log_header_t
*lhp
;
485 struct scsi_extended_sense sense
;
489 uint_t kp
, asc
, ascq
;
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
));
498 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
499 nvlist_add_nvlist(sip
->si_state_logpage
, lp
->ve_desc
, nvl
) != 0) {
502 return (scsi_set_errno(sip
, EDS_NOMEM
));
505 result
= nvlist_lookup_nvlist(sip
->si_state_logpage
, lp
->ve_desc
, &nvl
);
508 result
= scsi_log_sense(sip
, lp
->ve_code
,
509 PC_CUMULATIVE
, (caddr_t
)lhp
, buflen
, &kp
, &asc
, &ascq
);
512 log_length
= BE_16(lhp
->lh_length
);
513 if (nvlist_add_uint16(nvl
, "length", log_length
) != 0) {
515 return (scsi_set_errno(sip
, EDS_NOMEM
));
518 if (lp
->ve_validate(sip
, (scsi_log_parameter_header_t
*)
519 (((char *)lhp
) + sizeof (scsi_log_header_t
)),
520 log_length
, nvl
) != 0) {
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
);
534 * Load log pages and determine which pages are supported.
537 load_logpages(ds_scsi_info_t
*sip
)
540 scsi_supported_log_pages_t
*sp
;
541 struct scsi_extended_sense sense
;
543 uint_t sk
, asc
, ascq
;
547 buflen
= MAX_BUFLEN(scsi_log_header_t
);
548 if ((sp
= calloc(buflen
, 1)) == NULL
)
549 return (scsi_set_errno(sip
, EDS_NOMEM
));
551 bzero(&sense
, sizeof (struct scsi_extended_sense
));
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
);
557 for (i
= 0; i
< pagecount
; i
++) {
558 for (j
= 0; j
< NLOG_VALIDATION
; j
++) {
559 if (log_validation
[j
].ve_code
==
562 log_validation
[j
].ve_supported
;
570 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
571 nvlist_add_nvlist(sip
->si_dsp
->ds_state
, "logpages",
574 return (scsi_set_errno(sip
, EDS_NOMEM
));
578 result
= nvlist_lookup_nvlist(sip
->si_dsp
->ds_state
,
579 "logpages", &sip
->si_state_logpage
);
583 * Validate the logpage contents.
585 for (i
= 0; i
< NLOG_VALIDATION
; i
++) {
586 if ((sip
->si_supp_log
&
587 log_validation
[i
].ve_supported
) == 0)
591 * verify_logpage will clear the supported bit if
592 * verification fails.
594 if (verify_logpage(sip
, &log_validation
[i
]) != 0)
599 dprintf("failed to get log pages "
600 "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk
, asc
, ascq
);
604 * We always return 0 here, even if the required log pages aren't
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.
617 logpage_ie_verify(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
618 int log_length
, nvlist_t
*nvl
)
621 boolean_t seen
= B_FALSE
;
622 scsi_ie_log_param_t
*iep
=
623 (scsi_ie_log_param_t
*)lphp
;
625 for (i
= 0; i
< log_length
; i
+= plen
) {
626 iep
= (scsi_ie_log_param_t
*)((char *)iep
+ plen
);
628 if (BE_16(iep
->ie_hdr
.lph_param
) == LOGPARAM_IE
) {
629 if (nvlist_add_boolean_value(nvl
, "general",
631 return (scsi_set_errno(sip
, EDS_NOMEM
));
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
));
643 plen
= iep
->ie_hdr
.lph_length
+
644 sizeof (scsi_log_parameter_header_t
);
648 sip
->si_supp_log
&= ~LOGPAGE_SUPP_IE
;
649 dprintf("IE logpage validation failed\n");
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.
663 logpage_temp_verify(ds_scsi_info_t
*sip
,
664 scsi_log_parameter_header_t
*lphp
, int log_length
, nvlist_t
*nvl
)
667 boolean_t has_reftemp
= B_FALSE
;
668 boolean_t bad_length
= B_FALSE
;
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
);
675 switch (param_code
) {
676 case LOGPARAM_TEMP_CURTEMP
:
677 if (nvlist_add_boolean_value(nvl
, "current-temperature",
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
));
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
));
698 has_reftemp
= B_TRUE
;
702 plen
= lphp
->lph_length
+
703 sizeof (scsi_log_parameter_header_t
);
706 if (bad_length
|| !has_reftemp
) {
707 sip
->si_supp_log
&= ~LOGPAGE_SUPP_TEMP
;
708 dprintf("temperature logpage validation failed\n");
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.
723 logpage_selftest_verify(ds_scsi_info_t
*sip
,
724 scsi_log_parameter_header_t
*lphp
, int log_length
, nvlist_t
*nvl
)
727 boolean_t bad
= B_FALSE
;
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
);
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",
739 return (scsi_set_errno(sip
, EDS_NOMEM
));
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
));
753 plen
= lphp
->lph_length
+
754 sizeof (scsi_log_parameter_header_t
);
758 sip
->si_supp_log
&= ~LOGPAGE_SUPP_SELFTEST
;
759 dprintf("selftest logpage validation failed\n");
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.
773 logpage_ssm_verify(ds_scsi_info_t
*sip
,
774 scsi_log_parameter_header_t
*lphp
, int log_length
, nvlist_t
*nvl
)
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
);
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
));
793 dprintf("solid state media logpage bad len\n");
797 /* verification succeded */
801 plen
= lphp
->lph_length
+
802 sizeof (scsi_log_parameter_header_t
);
805 /* verification failed */
806 sip
->si_supp_log
&= ~LOGPAGE_SUPP_SSM
;
811 * Load the current IE mode pages
814 load_ie_modepage(ds_scsi_info_t
*sip
)
816 struct scsi_ms_hdrs junk_hdrs
;
818 uint_t skey
, asc
, ascq
;
820 if (!(sip
->si_supp_mode
& MODEPAGE_SUPP_IEC
))
823 bzero(&sip
->si_iec_current
, sizeof (sip
->si_iec_current
));
824 bzero(&sip
->si_iec_changeable
, sizeof (sip
->si_iec_changeable
));
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
,
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
);
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
;
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
,
857 BE_32(sip
->si_iec_current
.ie_interval_timer
)) != 0 ||
858 nvlist_add_uint32(sip
->si_state_iec
,
860 BE_32(sip
->si_iec_current
.ie_report_count
)) != 0)
861 return (scsi_set_errno(sip
, EDS_NOMEM
));
868 * Enable IE reporting. We prefer the following settings:
871 * (3) MRIE = 6 (IE_REPORT_ON_REQUEST)
873 * (6) REPORT COUNT = 0x00000001
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.
882 scsi_enable_ie(ds_scsi_info_t
*sip
, boolean_t
*changed
)
884 scsi_ie_page_t new_iec_page
;
886 uint_t skey
, asc
, ascq
;
888 if (!(sip
->si_supp_mode
& MODEPAGE_SUPP_IEC
))
891 bzero(&new_iec_page
, sizeof (new_iec_page
));
892 bzero(&hdrs
, sizeof (hdrs
));
894 (void) memcpy(&new_iec_page
, &sip
->si_iec_current
,
895 sizeof (new_iec_page
));
897 if (IEC_IE_CHANGEABLE(sip
->si_iec_changeable
))
898 new_iec_page
.ie_dexcpt
= 0;
900 if (IEC_MRIE_CHANGEABLE(sip
->si_iec_changeable
))
901 new_iec_page
.ie_mrie
= IE_REPORT_ON_REQUEST
;
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.
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;
912 new_iec_page
.ie_ewasc
= 0;
915 if (IEC_RPTCNT_CHANGEABLE(sip
->si_iec_changeable
))
916 new_iec_page
.ie_report_count
= BE_32(1);
918 if (IEC_LOGERR_CHANGEABLE(sip
->si_iec_changeable
))
919 new_iec_page
.ie_logerr
= 1;
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
925 if (memcmp(&new_iec_page
, &sip
->si_iec_current
,
926 MODEPAGE_INFO_EXCPT_LEN
) == 0) {
929 (void) memcpy(&hdrs
, &sip
->si_hdrs
, sizeof (sip
->si_hdrs
));
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) {
936 dprintf("failed to enable IE (KEY=0x%x "
937 "ASC=0x%x ASCQ=0x%x)\n", skey
, asc
, ascq
);
942 if (nvlist_add_boolean_value(sip
->si_state_iec
, "changed",
944 return (scsi_set_errno(sip
, EDS_NOMEM
));
950 * Clear the GLTSD bit, indicating log pages should be saved to non-volatile
954 clear_gltsd(ds_scsi_info_t
*sip
)
956 scsi_ms_hdrs_t hdrs
, junk_hdrs
;
957 struct mode_control_scsi3 control_pg_cur
, control_pg_chg
;
959 uint_t skey
, asc
, ascq
;
961 bzero(&hdrs
, sizeof (hdrs
));
962 bzero(&control_pg_cur
, sizeof (control_pg_cur
));
963 bzero(&control_pg_chg
, sizeof (control_pg_chg
));
965 result
= scsi_mode_sense(sip
,
966 MODEPAGE_CTRL_MODE
, PC_CURRENT
, &control_pg_cur
,
967 MODEPAGE_CTRL_MODE_LEN
, &hdrs
, &skey
, &asc
, &ascq
);
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
))
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
);
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
));
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.
1008 analyze_one_logpage(ds_scsi_info_t
*sip
, logpage_validation_entry_t
*entry
)
1010 scsi_log_header_t
*lhp
;
1011 scsi_log_parameter_header_t
*lphp
;
1014 uint_t skey
, asc
, ascq
;
1017 buflen
= MAX_BUFLEN(scsi_log_header_t
);
1018 if ((lhp
= calloc(buflen
, 1)) == NULL
)
1019 return (scsi_set_errno(sip
, EDS_NOMEM
));
1021 result
= scsi_log_sense(sip
, entry
->ve_code
,
1022 PC_CUMULATIVE
, (caddr_t
)lhp
, buflen
, &skey
, &asc
, &ascq
);
1025 log_length
= BE_16(lhp
->lh_length
);
1026 lphp
= (scsi_log_parameter_header_t
*)(((uchar_t
*)lhp
) +
1027 sizeof (scsi_log_header_t
));
1029 result
= entry
->ve_analyze(sip
, lphp
, log_length
);
1031 result
= scsi_set_errno(sip
, EDS_IO
);
1039 * Analyze the IE logpage. If we find an IE log record with a non-zero 'asc',
1040 * then we have a fault.
1043 logpage_ie_analyze(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
1047 scsi_ie_log_param_t
*iep
= (scsi_ie_log_param_t
*)lphp
;
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
;
1055 for (i
= 0; i
< log_length
; i
+= plen
) {
1056 iep
= (scsi_ie_log_param_t
*)((char *)iep
+ plen
);
1059 * Even though we validated the length during the initial phase,
1060 * never trust the device.
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
,
1068 return (scsi_set_errno(sip
, EDS_NOMEM
));
1070 if (iep
->ie_asc
!= 0)
1071 sip
->si_dsp
->ds_faults
|=
1075 plen
= iep
->ie_hdr
.lph_length
+
1076 sizeof (scsi_log_parameter_header_t
);
1083 logpage_temp_analyze(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
1087 uint8_t reftemp
, curtemp
;
1088 ushort_t param_code
;
1089 scsi_temp_log_param_t
*temp
;
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
;
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
;
1103 switch (param_code
) {
1104 case LOGPARAM_TEMP_CURTEMP
:
1105 if (lphp
->lph_length
!= LOGPARAM_TEMP_LEN
)
1108 if (nvlist_add_uint8(nvl
,
1109 FM_EREPORT_PAYLOAD_SCSI_CURTEMP
,
1111 return (scsi_set_errno(sip
, EDS_NOMEM
));
1112 curtemp
= temp
->t_temp
;
1115 case LOGPARAM_TEMP_REFTEMP
:
1116 if (lphp
->lph_length
!= LOGPARAM_TEMP_LEN
)
1119 if (nvlist_add_uint8(nvl
,
1120 FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP
,
1122 return (scsi_set_errno(sip
, EDS_NOMEM
));
1123 reftemp
= temp
->t_temp
;
1127 plen
= lphp
->lph_length
+
1128 sizeof (scsi_log_parameter_header_t
);
1131 if (reftemp
!= INVALID_TEMPERATURE
&& curtemp
!= INVALID_TEMPERATURE
&&
1133 sip
->si_dsp
->ds_faults
|= DS_FAULT_OVERTEMP
;
1139 logpage_selftest_analyze(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
1144 ushort_t param_code
;
1145 scsi_selftest_log_param_t
*stp
;
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
;
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
;
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
) {
1162 * We always log the last result, or the result of the
1163 * last completed test.
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
,
1182 if (SELFTEST_COMPLETE(stp
->st_results
)) {
1183 if (stp
->st_results
!= SELFTEST_OK
)
1184 sip
->si_dsp
->ds_faults
|=
1191 plen
= lphp
->lph_length
+
1192 sizeof (scsi_log_parameter_header_t
);
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
1205 logpage_ssm_analyze(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
1208 uint16_t param_code
;
1209 scsi_ssm_log_param_t
*ssm
;
1213 assert(sip
->si_dsp
->ds_ssmwearout
== NULL
);
1214 if (nvlist_alloc(&sip
->si_dsp
->ds_ssmwearout
, NV_UNIQUE_NAME
, 0) != 0)
1215 return (scsi_set_errno(sip
, EDS_NOMEM
));
1216 nvl
= sip
->si_dsp
->ds_ssmwearout
;
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
;
1223 switch (param_code
) {
1224 case LOGPARAM_PRCNT_USED
:
1225 if (lphp
->lph_length
!= LOGPARAM_PRCNT_USED_PARAM_LEN
)
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
));
1236 if (ssm
->ssm_prcnt_used
>= PRCNT_USED_FAULT_THRSH
)
1237 sip
->si_dsp
->ds_faults
|= DS_FAULT_SSMWEAROUT
;
1242 plen
= lphp
->lph_length
+
1243 sizeof (scsi_log_parameter_header_t
);
1247 * If we got this far we didn't see LOGPARAM_PRCNT_USED
1248 * which is strange since we verified that it's there
1250 dprintf("solid state media logpage analyze failed\n");
1254 return (scsi_set_errno(sip
, EDS_NOT_SUPPORTED
));
1258 * Analyze the IE mode sense page explicitly. This is only needed if the IE log
1259 * page is not supported.
1262 analyze_ie_sense(ds_scsi_info_t
*sip
)
1264 uint_t skey
, asc
, ascq
;
1268 * Don't bother checking if we weren't able to set our MRIE correctly.
1270 if (sip
->si_iec_current
.ie_mrie
!= IE_REPORT_ON_REQUEST
)
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
;
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) {
1289 return (scsi_set_errno(sip
, EDS_NOMEM
));
1293 sip
->si_dsp
->ds_faults
|= DS_FAULT_PREDFAIL
;
1300 * Clean up the scsi-specific information structure.
1303 ds_scsi_close(void *arg
)
1305 ds_scsi_info_t
*sip
= arg
;
1307 (void) dlclose(sip
->si_sim
);
1313 * Initialize a single disk. Initialization consists of:
1315 * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE
1316 * Control page (page 0x1C).
1318 * 2. If the IE page is available, try to set the following parameters:
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
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.
1330 * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a
1331 * LOG SENSE command.
1333 * 4. Check to see if the self-test log page (page 0x10) is supported.
1335 * 5. Check to see if the temperature log page (page 0x0D) is supported, and
1336 * contains a reference temperature.
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
1344 ds_scsi_open_common(disk_status_t
*dsp
, ds_scsi_info_t
*sip
)
1350 /* Load and validate mode pages */
1351 if (load_modepages(sip
) != 0) {
1356 /* Load and validate log pages */
1357 if (load_logpages(sip
) != 0) {
1363 if (load_ie_modepage(sip
) != 0 ||
1364 scsi_enable_ie(sip
, &changed
) != 0 ||
1365 (changed
&& load_ie_modepage(sip
) != 0)) {
1370 /* Clear the GLTSD bit in the control page */
1371 if (sip
->si_supp_log
!= 0 && clear_gltsd(sip
) != 0) {
1380 ds_scsi_open_uscsi(disk_status_t
*dsp
)
1382 ds_scsi_info_t
*sip
;
1384 if ((sip
= calloc(sizeof (ds_scsi_info_t
), 1)) == NULL
) {
1385 (void) ds_set_errno(dsp
, EDS_NOMEM
);
1389 return (ds_scsi_open_common(dsp
, sip
));
1393 ds_scsi_open_sim(disk_status_t
*dsp
)
1395 ds_scsi_info_t
*sip
;
1397 if ((sip
= calloc(sizeof (ds_scsi_info_t
), 1)) == NULL
) {
1398 (void) ds_set_errno(dsp
, EDS_NOMEM
);
1402 if ((sip
->si_sim
= dlopen(dsp
->ds_path
, RTLD_LAZY
)) == NULL
) {
1403 (void) ds_set_errno(dsp
, EDS_NO_TRANSPORT
);
1408 return (ds_scsi_open_common(dsp
, sip
));
1413 * Scan for any faults. The following steps are performed:
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.
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
1423 * 3. If the IE log page is supported, check to see if failure is predicted. If
1424 * so, indicate a predictive failure fault.
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.
1431 ds_scsi_scan(void *arg
)
1433 ds_scsi_info_t
*sip
= arg
;
1436 for (i
= 0; i
< NLOG_VALIDATION
; i
++) {
1437 if ((sip
->si_supp_log
& log_validation
[i
].ve_supported
) == 0)
1440 if (analyze_one_logpage(sip
, &log_validation
[i
]) != 0)
1444 if (!(sip
->si_supp_log
& LOGPAGE_SUPP_IE
) &&
1445 (sip
->si_supp_mode
& MODEPAGE_SUPP_IEC
) &&
1446 analyze_ie_sense(sip
) != 0)
1452 ds_transport_t ds_scsi_uscsi_transport
= {
1458 ds_transport_t ds_scsi_sim_transport
= {