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.
26 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <libdiskstatus.h>
34 #include <sys/fm/io/scsi.h>
37 #include "ds_scsi_sim.h"
38 #include "ds_scsi_uscsi.h"
40 typedef struct ds_scsi_info
{
41 disk_status_t
*si_dsp
;
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
;
56 #define scsi_set_errno(sip, errno) (ds_set_errno((sip)->si_dsp, (errno)))
59 * Table to validate log pages
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);
66 typedef struct logpage_validation_entry
{
70 logpage_validation_fn_t ve_validate
;
71 logpage_analyze_fn_t ve_analyze
;
72 } logpage_validation_entry_t
;
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
*);
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);
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
,
94 logpage_temp_verify
, logpage_temp_analyze
},
95 { LOGPAGE_SELFTEST
, LOGPAGE_SUPP_SELFTEST
,
97 logpage_selftest_verify
, logpage_selftest_analyze
}
100 #define NLOG_VALIDATION (sizeof (log_validation) / sizeof (log_validation[0]))
103 * Given an extended sense page, retrieves the sense key, as well as the
104 * additional sense code information.
107 scsi_translate_error(struct scsi_extended_sense
*rq
, uint_t
*skeyp
,
108 uint_t
*ascp
, uint_t
*ascqp
)
110 struct scsi_descr_sense_hdr
*sdsp
=
111 (struct scsi_descr_sense_hdr
*)rq
;
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.
120 switch (rq
->es_code
) {
121 case CODE_FMT_DESCR_CURRENT
:
122 case CODE_FMT_DESCR_DEFERRED
:
124 *ascp
= sdsp
->ds_add_code
;
125 *ascqp
= sdsp
->ds_qual_code
;
128 case CODE_FMT_FIXED_CURRENT
:
129 case CODE_FMT_FIXED_DEFERRED
:
132 if (rq
->es_add_len
>= 6) {
133 *ascp
= rq
->es_add_code
;
134 *ascqp
= rq
->es_qual_code
;
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.
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
)
154 struct scsi_extended_sense sense
;
155 int senselen
= sizeof (struct scsi_extended_sense
);
156 struct mode_page
*mp
= (struct mode_page
*)buf
;
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
);
162 bzero(&sense
, sizeof (struct scsi_extended_sense
));
165 options
|= MODE_SELECT_SP
;
168 options
&= ~MODE_SELECT_SP
;
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;
177 result
= simscsi_mode_select(sip
->si_sim
,
178 page_code
, options
, buf
, buflen
,
179 &headers
->ms_hdr
.g0
, &sense
, &senselen
);
181 result
= uscsi_mode_select(sip
->si_dsp
->ds_fd
,
182 page_code
, options
, buf
, buflen
,
183 &headers
->ms_hdr
.g0
, &sense
, &senselen
);
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;
190 result
= simscsi_mode_select_10(sip
->si_sim
,
191 page_code
, options
, buf
, buflen
,
192 &headers
->ms_hdr
.g1
, &sense
, &senselen
);
194 result
= uscsi_mode_select_10(sip
->si_dsp
->ds_fd
,
195 page_code
, options
, buf
, buflen
,
196 &headers
->ms_hdr
.g1
, &sense
, &senselen
);
200 scsi_translate_error(&sense
, skp
, ascp
, ascqp
);
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
)
211 struct scsi_extended_sense sense
;
212 int senselen
= sizeof (struct scsi_extended_sense
);
214 assert(sip
->si_cdblen
== MODE_CMD_LEN_6
||
215 sip
->si_cdblen
== MODE_CMD_LEN_10
);
217 bzero(&sense
, sizeof (struct scsi_extended_sense
));
219 bzero(headers
, sizeof (scsi_ms_hdrs_t
));
220 headers
->ms_length
= sip
->si_cdblen
;
222 if (sip
->si_cdblen
== MODE_CMD_LEN_6
) {
224 result
= simscsi_mode_sense(sip
->si_sim
,
225 page_code
, pc
, buf
, buflen
, &headers
->ms_hdr
.g0
,
228 result
= uscsi_mode_sense(sip
->si_dsp
->ds_fd
, page_code
,
229 pc
, buf
, buflen
, &headers
->ms_hdr
.g0
, &sense
,
233 result
= simscsi_mode_sense_10(sip
->si_sim
,
234 page_code
, pc
, buf
, buflen
, &headers
->ms_hdr
.g1
,
237 result
= uscsi_mode_sense_10(sip
->si_dsp
->ds_fd
,
238 page_code
, pc
, buf
, buflen
, &headers
->ms_hdr
.g1
,
243 scsi_translate_error(&sense
, skp
, ascp
, ascqp
);
249 scsi_request_sense(ds_scsi_info_t
*sip
, uint_t
*skp
, uint_t
*ascp
,
252 struct scsi_extended_sense sense
, sensebuf
;
253 int senselen
= sizeof (struct scsi_extended_sense
);
254 int sensebuflen
= sizeof (struct scsi_extended_sense
);
257 bzero(&sense
, sizeof (struct scsi_extended_sense
));
258 bzero(&sensebuf
, sizeof (struct scsi_extended_sense
));
261 result
= simscsi_request_sense(sip
->si_sim
,
262 (caddr_t
)&sensebuf
, sensebuflen
, &sense
, &senselen
);
264 result
= uscsi_request_sense(sip
->si_dsp
->ds_fd
,
265 (caddr_t
)&sensebuf
, sensebuflen
, &sense
, &senselen
);
268 scsi_translate_error(&sensebuf
, skp
, ascp
, ascqp
);
270 scsi_translate_error(&sense
, skp
, ascp
, ascqp
);
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
)
280 struct scsi_extended_sense sense
;
281 int senselen
= sizeof (struct scsi_extended_sense
);
284 result
= simscsi_log_sense(sip
->si_sim
,
285 page_code
, page_control
, page_data
, page_size
, &sense
,
288 result
= uscsi_log_sense(sip
->si_dsp
->ds_fd
,
289 page_code
, page_control
, page_data
, page_size
, &sense
,
293 scsi_translate_error(&sense
, skp
, ascp
, ascqp
);
299 * Given a list of supported mode pages, determine if the given page is present.
302 mode_page_present(uchar_t
*pgdata
, uint_t pgdatalen
, uchar_t pagecode
)
305 struct mode_page
*pg
;
306 boolean_t found
= B_FALSE
;
309 * The mode page list contains all mode pages supported by the device,
310 * one after the other.
312 while (i
< pgdatalen
) {
313 pg
= (struct mode_page
*)&pgdata
[i
];
315 if (pg
->code
== pagecode
) {
320 i
+= MODESENSE_PAGE_LEN(pg
);
327 * Load mode pages and check that the appropriate pages are supported.
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.
334 load_modepages(ds_scsi_info_t
*sip
)
338 scsi_ms_hdrs_t headers
;
340 uint_t skey
, asc
, ascq
;
342 scsi_ms_header_t
*smh
= &headers
.ms_hdr
.g0
;
343 scsi_ms_header_g1_t
*smh_g1
= &headers
.ms_hdr
.g1
;
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
));
350 bzero(&headers
, sizeof (headers
));
353 * Attempt a mode sense(6). If that fails, try a mode sense(10)
355 * allpages is allocated to be of the maximum size for either a mode
356 * sense(6) or mode sense(10) MODEPAGE_ALLPAGES response.
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.
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) {
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:
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> ...]
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.
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]].
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
)) {
392 * Fallback and try the 10-byte version of the command.
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
);
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
));
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) {
412 return (scsi_set_errno(sip
, EDS_NOMEM
));
416 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
417 nvlist_add_nvlist(sip
->si_dsp
->ds_state
, "modepages",
421 return (scsi_set_errno(sip
, EDS_NOMEM
));
425 result
= nvlist_lookup_nvlist(sip
->si_dsp
->ds_state
,
426 "modepages", &sip
->si_state_modepage
);
430 * One of the sets of the commands (above) succeeded, so now
431 * look for the mode pages we need and record them appropriately
433 if (mode_page_present(allpages
, datalength
,
434 MODEPAGE_INFO_EXCPT
)) {
437 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
438 nvlist_add_nvlist(sip
->si_state_modepage
,
439 "informational-exceptions", nvl
) != 0) {
442 return (scsi_set_errno(sip
, EDS_NOMEM
));
445 sip
->si_supp_mode
|= MODEPAGE_SUPP_IEC
;
446 result
= nvlist_lookup_nvlist(sip
->si_state_modepage
,
447 "informational-exceptions", &sip
->si_state_iec
);
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.
458 dprintf("command returned invalid data length (%d)\n",
461 dprintf("failed to load modepages (KEY=0x%x "
462 "ASC=0x%x ASCQ=0x%x)\n", skey
, asc
, ascq
);
464 result
= scsi_set_errno(sip
, EDS_NO_TRANSPORT
);
472 * Verify a single logpage. This will do some generic validation and then call
473 * the logpage-specific function for further verification.
476 verify_logpage(ds_scsi_info_t
*sip
, logpage_validation_entry_t
*lp
)
478 scsi_log_header_t
*lhp
;
479 struct scsi_extended_sense sense
;
483 uint_t kp
, asc
, ascq
;
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
));
492 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
493 nvlist_add_nvlist(sip
->si_state_logpage
, lp
->ve_desc
, nvl
) != 0) {
496 return (scsi_set_errno(sip
, EDS_NOMEM
));
499 result
= nvlist_lookup_nvlist(sip
->si_state_logpage
, lp
->ve_desc
, &nvl
);
502 result
= scsi_log_sense(sip
, lp
->ve_code
,
503 PC_CUMULATIVE
, (caddr_t
)lhp
, buflen
, &kp
, &asc
, &ascq
);
506 log_length
= BE_16(lhp
->lh_length
);
507 if (nvlist_add_uint16(nvl
, "length", log_length
) != 0) {
509 return (scsi_set_errno(sip
, EDS_NOMEM
));
512 if (lp
->ve_validate(sip
, (scsi_log_parameter_header_t
*)
513 (((char *)lhp
) + sizeof (scsi_log_header_t
)),
514 log_length
, nvl
) != 0) {
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
);
528 * Load log pages and determine which pages are supported.
531 load_logpages(ds_scsi_info_t
*sip
)
534 scsi_supported_log_pages_t
*sp
;
535 struct scsi_extended_sense sense
;
537 uint_t sk
, asc
, ascq
;
541 buflen
= MAX_BUFLEN(scsi_log_header_t
);
542 if ((sp
= calloc(buflen
, 1)) == NULL
)
543 return (scsi_set_errno(sip
, EDS_NOMEM
));
545 bzero(&sense
, sizeof (struct scsi_extended_sense
));
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
);
551 for (i
= 0; i
< pagecount
; i
++) {
552 for (j
= 0; j
< NLOG_VALIDATION
; j
++) {
553 if (log_validation
[j
].ve_code
==
556 log_validation
[j
].ve_supported
;
564 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
565 nvlist_add_nvlist(sip
->si_dsp
->ds_state
, "logpages",
568 return (scsi_set_errno(sip
, EDS_NOMEM
));
572 result
= nvlist_lookup_nvlist(sip
->si_dsp
->ds_state
,
573 "logpages", &sip
->si_state_logpage
);
577 * Validate the logpage contents.
579 for (i
= 0; i
< NLOG_VALIDATION
; i
++) {
580 if ((sip
->si_supp_log
&
581 log_validation
[i
].ve_supported
) == 0)
585 * verify_logpage will clear the supported bit if
586 * verification fails.
588 if (verify_logpage(sip
, &log_validation
[i
]) != 0)
593 dprintf("failed to get log pages "
594 "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk
, asc
, ascq
);
598 * We always return 0 here, even if the required log pages aren't
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.
611 logpage_ie_verify(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
612 int log_length
, nvlist_t
*nvl
)
615 boolean_t seen
= B_FALSE
;
616 scsi_ie_log_param_t
*iep
=
617 (scsi_ie_log_param_t
*)lphp
;
619 for (i
= 0; i
< log_length
; i
+= plen
) {
620 iep
= (scsi_ie_log_param_t
*)((char *)iep
+ plen
);
622 if (BE_16(iep
->ie_hdr
.lph_param
) == LOGPARAM_IE
) {
623 if (nvlist_add_boolean_value(nvl
, "general",
625 return (scsi_set_errno(sip
, EDS_NOMEM
));
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
));
637 plen
= iep
->ie_hdr
.lph_length
+
638 sizeof (scsi_log_parameter_header_t
);
642 sip
->si_supp_log
&= ~LOGPAGE_SUPP_IE
;
643 dprintf("IE logpage validation failed\n");
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.
657 logpage_temp_verify(ds_scsi_info_t
*sip
,
658 scsi_log_parameter_header_t
*lphp
, int log_length
, nvlist_t
*nvl
)
661 boolean_t has_reftemp
= B_FALSE
;
662 boolean_t bad_length
= B_FALSE
;
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
);
669 switch (param_code
) {
670 case LOGPARAM_TEMP_CURTEMP
:
671 if (nvlist_add_boolean_value(nvl
, "current-temperature",
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
));
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
));
692 has_reftemp
= B_TRUE
;
696 plen
= lphp
->lph_length
+
697 sizeof (scsi_log_parameter_header_t
);
700 if (bad_length
|| !has_reftemp
) {
701 sip
->si_supp_log
&= ~LOGPAGE_SUPP_TEMP
;
702 dprintf("temperature logpage validation failed\n");
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.
717 logpage_selftest_verify(ds_scsi_info_t
*sip
,
718 scsi_log_parameter_header_t
*lphp
, int log_length
, nvlist_t
*nvl
)
721 boolean_t bad
= B_FALSE
;
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
);
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",
733 return (scsi_set_errno(sip
, EDS_NOMEM
));
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
));
747 plen
= lphp
->lph_length
+
748 sizeof (scsi_log_parameter_header_t
);
752 sip
->si_supp_log
&= ~LOGPAGE_SUPP_SELFTEST
;
753 dprintf("selftest logpage validation failed\n");
760 * Load the current IE mode pages
763 load_ie_modepage(ds_scsi_info_t
*sip
)
765 struct scsi_ms_hdrs junk_hdrs
;
767 uint_t skey
, asc
, ascq
;
769 if (!(sip
->si_supp_mode
& MODEPAGE_SUPP_IEC
))
772 bzero(&sip
->si_iec_current
, sizeof (sip
->si_iec_current
));
773 bzero(&sip
->si_iec_changeable
, sizeof (sip
->si_iec_changeable
));
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
,
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
);
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
;
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
,
806 BE_32(sip
->si_iec_current
.ie_interval_timer
)) != 0 ||
807 nvlist_add_uint32(sip
->si_state_iec
,
809 BE_32(sip
->si_iec_current
.ie_report_count
)) != 0)
810 return (scsi_set_errno(sip
, EDS_NOMEM
));
817 * Enable IE reporting. We prefer the following settings:
820 * (3) MRIE = 6 (IE_REPORT_ON_REQUEST)
822 * (6) REPORT COUNT = 0x00000001
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.
831 scsi_enable_ie(ds_scsi_info_t
*sip
, boolean_t
*changed
)
833 scsi_ie_page_t new_iec_page
;
835 uint_t skey
, asc
, ascq
;
837 if (!(sip
->si_supp_mode
& MODEPAGE_SUPP_IEC
))
840 bzero(&new_iec_page
, sizeof (new_iec_page
));
841 bzero(&hdrs
, sizeof (hdrs
));
843 (void) memcpy(&new_iec_page
, &sip
->si_iec_current
,
844 sizeof (new_iec_page
));
846 if (IEC_IE_CHANGEABLE(sip
->si_iec_changeable
))
847 new_iec_page
.ie_dexcpt
= 0;
849 if (IEC_MRIE_CHANGEABLE(sip
->si_iec_changeable
))
850 new_iec_page
.ie_mrie
= IE_REPORT_ON_REQUEST
;
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.
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;
861 new_iec_page
.ie_ewasc
= 0;
864 if (IEC_RPTCNT_CHANGEABLE(sip
->si_iec_changeable
))
865 new_iec_page
.ie_report_count
= BE_32(1);
867 if (IEC_LOGERR_CHANGEABLE(sip
->si_iec_changeable
))
868 new_iec_page
.ie_logerr
= 1;
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
874 if (memcmp(&new_iec_page
, &sip
->si_iec_current
,
875 MODEPAGE_INFO_EXCPT_LEN
) == 0) {
878 (void) memcpy(&hdrs
, &sip
->si_hdrs
, sizeof (sip
->si_hdrs
));
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) {
885 dprintf("failed to enable IE (KEY=0x%x "
886 "ASC=0x%x ASCQ=0x%x)\n", skey
, asc
, ascq
);
891 if (nvlist_add_boolean_value(sip
->si_state_iec
, "changed",
893 return (scsi_set_errno(sip
, EDS_NOMEM
));
899 * Clear the GLTSD bit, indicating log pages should be saved to non-volatile
903 clear_gltsd(ds_scsi_info_t
*sip
)
905 scsi_ms_hdrs_t hdrs
, junk_hdrs
;
906 struct mode_control_scsi3 control_pg_cur
, control_pg_chg
;
908 uint_t skey
, asc
, ascq
;
910 bzero(&hdrs
, sizeof (hdrs
));
911 bzero(&control_pg_cur
, sizeof (control_pg_cur
));
912 bzero(&control_pg_chg
, sizeof (control_pg_chg
));
914 result
= scsi_mode_sense(sip
,
915 MODEPAGE_CTRL_MODE
, PC_CURRENT
, &control_pg_cur
,
916 MODEPAGE_CTRL_MODE_LEN
, &hdrs
, &skey
, &asc
, &ascq
);
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
))
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
);
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
));
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.
957 analyze_one_logpage(ds_scsi_info_t
*sip
, logpage_validation_entry_t
*entry
)
959 scsi_log_header_t
*lhp
;
960 scsi_log_parameter_header_t
*lphp
;
963 uint_t skey
, asc
, ascq
;
966 buflen
= MAX_BUFLEN(scsi_log_header_t
);
967 if ((lhp
= calloc(buflen
, 1)) == NULL
)
968 return (scsi_set_errno(sip
, EDS_NOMEM
));
970 result
= scsi_log_sense(sip
, entry
->ve_code
,
971 PC_CUMULATIVE
, (caddr_t
)lhp
, buflen
, &skey
, &asc
, &ascq
);
974 log_length
= BE_16(lhp
->lh_length
);
975 lphp
= (scsi_log_parameter_header_t
*)(((uchar_t
*)lhp
) +
976 sizeof (scsi_log_header_t
));
978 result
= entry
->ve_analyze(sip
, lphp
, log_length
);
980 result
= scsi_set_errno(sip
, EDS_IO
);
988 * Analyze the IE logpage. If we find an IE log record with a non-zero 'asc',
989 * then we have a fault.
992 logpage_ie_analyze(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
996 scsi_ie_log_param_t
*iep
= (scsi_ie_log_param_t
*)lphp
;
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
;
1004 for (i
= 0; i
< log_length
; i
+= plen
) {
1005 iep
= (scsi_ie_log_param_t
*)((char *)iep
+ plen
);
1008 * Even though we validated the length during the initial phase,
1009 * never trust the device.
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
,
1017 return (scsi_set_errno(sip
, EDS_NOMEM
));
1019 if (iep
->ie_asc
!= 0)
1020 sip
->si_dsp
->ds_faults
|=
1024 plen
= iep
->ie_hdr
.lph_length
+
1025 sizeof (scsi_log_parameter_header_t
);
1032 logpage_temp_analyze(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
1036 uint8_t reftemp
, curtemp
;
1037 ushort_t param_code
;
1038 scsi_temp_log_param_t
*temp
;
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
;
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
;
1052 switch (param_code
) {
1053 case LOGPARAM_TEMP_CURTEMP
:
1054 if (lphp
->lph_length
!= LOGPARAM_TEMP_LEN
)
1057 if (nvlist_add_uint8(nvl
,
1058 FM_EREPORT_PAYLOAD_SCSI_CURTEMP
,
1060 return (scsi_set_errno(sip
, EDS_NOMEM
));
1061 curtemp
= temp
->t_temp
;
1064 case LOGPARAM_TEMP_REFTEMP
:
1065 if (lphp
->lph_length
!= LOGPARAM_TEMP_LEN
)
1068 if (nvlist_add_uint8(nvl
,
1069 FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP
,
1071 return (scsi_set_errno(sip
, EDS_NOMEM
));
1072 reftemp
= temp
->t_temp
;
1076 plen
= lphp
->lph_length
+
1077 sizeof (scsi_log_parameter_header_t
);
1080 if (reftemp
!= INVALID_TEMPERATURE
&& curtemp
!= INVALID_TEMPERATURE
&&
1082 sip
->si_dsp
->ds_faults
|= DS_FAULT_OVERTEMP
;
1088 logpage_selftest_analyze(ds_scsi_info_t
*sip
, scsi_log_parameter_header_t
*lphp
,
1093 ushort_t param_code
;
1094 scsi_selftest_log_param_t
*stp
;
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
;
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
;
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
) {
1111 * We always log the last result, or the result of the
1112 * last completed test.
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
,
1131 if (SELFTEST_COMPLETE(stp
->st_results
)) {
1132 if (stp
->st_results
!= SELFTEST_OK
)
1133 sip
->si_dsp
->ds_faults
|=
1140 plen
= lphp
->lph_length
+
1141 sizeof (scsi_log_parameter_header_t
);
1148 * Analyze the IE mode sense page explicitly. This is only needed if the IE log
1149 * page is not supported.
1152 analyze_ie_sense(ds_scsi_info_t
*sip
)
1154 uint_t skey
, asc
, ascq
;
1158 * Don't bother checking if we weren't able to set our MRIE correctly.
1160 if (sip
->si_iec_current
.ie_mrie
!= IE_REPORT_ON_REQUEST
)
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
;
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) {
1179 return (scsi_set_errno(sip
, EDS_NOMEM
));
1183 sip
->si_dsp
->ds_faults
|= DS_FAULT_PREDFAIL
;
1190 * Clean up the scsi-specific information structure.
1193 ds_scsi_close(void *arg
)
1195 ds_scsi_info_t
*sip
= arg
;
1197 (void) dlclose(sip
->si_sim
);
1203 * Initialize a single disk. Initialization consists of:
1205 * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE
1206 * Control page (page 0x1C).
1208 * 2. If the IE page is available, try to set the following parameters:
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
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.
1220 * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a
1221 * LOG SENSE command.
1223 * 4. Check to see if the self-test log page (page 0x10) is supported.
1225 * 5. Check to see if the temperature log page (page 0x0D) is supported, and
1226 * contains a reference temperature.
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
1234 ds_scsi_open_common(disk_status_t
*dsp
, ds_scsi_info_t
*sip
)
1240 /* Load and validate mode pages */
1241 if (load_modepages(sip
) != 0) {
1246 /* Load and validate log pages */
1247 if (load_logpages(sip
) != 0) {
1253 if (load_ie_modepage(sip
) != 0 ||
1254 scsi_enable_ie(sip
, &changed
) != 0 ||
1255 (changed
&& load_ie_modepage(sip
) != 0)) {
1260 /* Clear the GLTSD bit in the control page */
1261 if (sip
->si_supp_log
!= 0 && clear_gltsd(sip
) != 0) {
1270 ds_scsi_open_uscsi(disk_status_t
*dsp
)
1272 ds_scsi_info_t
*sip
;
1274 if ((sip
= calloc(sizeof (ds_scsi_info_t
), 1)) == NULL
) {
1275 (void) ds_set_errno(dsp
, EDS_NOMEM
);
1279 return (ds_scsi_open_common(dsp
, sip
));
1283 ds_scsi_open_sim(disk_status_t
*dsp
)
1285 ds_scsi_info_t
*sip
;
1287 if ((sip
= calloc(sizeof (ds_scsi_info_t
), 1)) == NULL
) {
1288 (void) ds_set_errno(dsp
, EDS_NOMEM
);
1292 if ((sip
->si_sim
= dlopen(dsp
->ds_path
, RTLD_LAZY
)) == NULL
) {
1293 (void) ds_set_errno(dsp
, EDS_NO_TRANSPORT
);
1298 return (ds_scsi_open_common(dsp
, sip
));
1303 * Scan for any faults. The following steps are performed:
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.
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
1313 * 3. If the IE log page is supported, check to see if failure is predicted. If
1314 * so, indicate a predictive failure fault.
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.
1321 ds_scsi_scan(void *arg
)
1323 ds_scsi_info_t
*sip
= arg
;
1326 for (i
= 0; i
< NLOG_VALIDATION
; i
++) {
1327 if ((sip
->si_supp_log
& log_validation
[i
].ve_supported
) == 0)
1330 if (analyze_one_logpage(sip
, &log_validation
[i
]) != 0)
1334 if (!(sip
->si_supp_log
& LOGPAGE_SUPP_IE
) &&
1335 (sip
->si_supp_mode
& MODEPAGE_SUPP_IEC
) &&
1336 analyze_ie_sense(sip
) != 0)
1342 ds_transport_t ds_scsi_uscsi_transport
= {
1348 ds_transport_t ds_scsi_sim_transport
= {