124db4641Seschrock /*
224db4641Seschrock * CDDL HEADER START
324db4641Seschrock *
424db4641Seschrock * The contents of this file are subject to the terms of the
524db4641Seschrock * Common Development and Distribution License (the "License").
624db4641Seschrock * You may not use this file except in compliance with the License.
724db4641Seschrock *
824db4641Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
924db4641Seschrock * or http://www.opensolaris.org/os/licensing.
1024db4641Seschrock * See the License for the specific language governing permissions
1124db4641Seschrock * and limitations under the License.
1224db4641Seschrock *
1324db4641Seschrock * When distributing Covered Code, include this CDDL HEADER in each
1424db4641Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1524db4641Seschrock * If applicable, add the following below this CDDL HEADER, with the
1624db4641Seschrock * fields enclosed by brackets "[]" replaced with your own identifying
1724db4641Seschrock * information: Portions Copyright [yyyy] [name of copyright owner]
1824db4641Seschrock *
1924db4641Seschrock * CDDL HEADER END
2024db4641Seschrock */
2124db4641Seschrock /*
2224db4641Seschrock * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
2324db4641Seschrock * Use is subject to license terms.
240244979bSAlek Pinchuk * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
2524db4641Seschrock */
2624db4641Seschrock
2724db4641Seschrock #include <assert.h>
2824db4641Seschrock #include <errno.h>
2924db4641Seschrock #include <libdiskstatus.h>
3024db4641Seschrock #include <limits.h>
3124db4641Seschrock #include <stdlib.h>
3224db4641Seschrock #include <strings.h>
3324db4641Seschrock #include <sys/fm/io/scsi.h>
3424db4641Seschrock
3524db4641Seschrock #include "ds_scsi.h"
3624db4641Seschrock #include "ds_scsi_sim.h"
3724db4641Seschrock #include "ds_scsi_uscsi.h"
3824db4641Seschrock
3924db4641Seschrock typedef struct ds_scsi_info {
4024db4641Seschrock disk_status_t *si_dsp;
4124db4641Seschrock void *si_sim;
4224db4641Seschrock int si_cdblen;
4324db4641Seschrock int si_supp_mode;
4424db4641Seschrock int si_supp_log;
4524db4641Seschrock int si_extensions;
4624db4641Seschrock int si_reftemp;
4724db4641Seschrock scsi_ms_hdrs_t si_hdrs;
4824db4641Seschrock scsi_ie_page_t si_iec_current;
4924db4641Seschrock scsi_ie_page_t si_iec_changeable;
5024db4641Seschrock nvlist_t *si_state_modepage;
5124db4641Seschrock nvlist_t *si_state_logpage;
5224db4641Seschrock nvlist_t *si_state_iec;
5324db4641Seschrock } ds_scsi_info_t;
5424db4641Seschrock
5524db4641Seschrock #define scsi_set_errno(sip, errno) (ds_set_errno((sip)->si_dsp, (errno)))
5624db4641Seschrock
5724db4641Seschrock /*
5824db4641Seschrock * Table to validate log pages
5924db4641Seschrock */
6024db4641Seschrock typedef int (*logpage_validation_fn_t)(ds_scsi_info_t *,
6124db4641Seschrock scsi_log_parameter_header_t *, int, nvlist_t *);
6224db4641Seschrock typedef int (*logpage_analyze_fn_t)(ds_scsi_info_t *,
6324db4641Seschrock scsi_log_parameter_header_t *, int);
6424db4641Seschrock
6524db4641Seschrock typedef struct logpage_validation_entry {
6624db4641Seschrock uchar_t ve_code;
6724db4641Seschrock int ve_supported;
6824db4641Seschrock const char *ve_desc;
6924db4641Seschrock logpage_validation_fn_t ve_validate;
7024db4641Seschrock logpage_analyze_fn_t ve_analyze;
7124db4641Seschrock } logpage_validation_entry_t;
7224db4641Seschrock
7324db4641Seschrock static int logpage_ie_verify(ds_scsi_info_t *,
7424db4641Seschrock scsi_log_parameter_header_t *, int, nvlist_t *);
7524db4641Seschrock static int logpage_temp_verify(ds_scsi_info_t *,
7624db4641Seschrock scsi_log_parameter_header_t *, int, nvlist_t *);
7724db4641Seschrock static int logpage_selftest_verify(ds_scsi_info_t *,
7824db4641Seschrock scsi_log_parameter_header_t *, int, nvlist_t *);
790244979bSAlek Pinchuk static int logpage_ssm_verify(ds_scsi_info_t *,
800244979bSAlek Pinchuk scsi_log_parameter_header_t *, int, nvlist_t *);
8124db4641Seschrock
8224db4641Seschrock static int logpage_ie_analyze(ds_scsi_info_t *,
8324db4641Seschrock scsi_log_parameter_header_t *, int);
8424db4641Seschrock static int logpage_temp_analyze(ds_scsi_info_t *,
8524db4641Seschrock scsi_log_parameter_header_t *, int);
8624db4641Seschrock static int logpage_selftest_analyze(ds_scsi_info_t *,
8724db4641Seschrock scsi_log_parameter_header_t *, int);
880244979bSAlek Pinchuk static int logpage_ssm_analyze(ds_scsi_info_t *,
890244979bSAlek Pinchuk scsi_log_parameter_header_t *, int);
9024db4641Seschrock
9124db4641Seschrock static struct logpage_validation_entry log_validation[] = {
9224db4641Seschrock { LOGPAGE_IE, LOGPAGE_SUPP_IE,
9324db4641Seschrock "informational-exceptions",
9424db4641Seschrock logpage_ie_verify, logpage_ie_analyze },
9524db4641Seschrock { LOGPAGE_TEMP, LOGPAGE_SUPP_TEMP,
9624db4641Seschrock "temperature",
9724db4641Seschrock logpage_temp_verify, logpage_temp_analyze },
9824db4641Seschrock { LOGPAGE_SELFTEST, LOGPAGE_SUPP_SELFTEST,
9924db4641Seschrock "self-test",
1000244979bSAlek Pinchuk logpage_selftest_verify, logpage_selftest_analyze },
1010244979bSAlek Pinchuk { LOGPAGE_SSM, LOGPAGE_SUPP_SSM,
1020244979bSAlek Pinchuk FM_EREPORT_SCSI_SSMWEAROUT,
1030244979bSAlek Pinchuk logpage_ssm_verify, logpage_ssm_analyze }
10424db4641Seschrock };
10524db4641Seschrock
10624db4641Seschrock #define NLOG_VALIDATION (sizeof (log_validation) / sizeof (log_validation[0]))
10724db4641Seschrock
10824db4641Seschrock /*
10924db4641Seschrock * Given an extended sense page, retrieves the sense key, as well as the
11024db4641Seschrock * additional sense code information.
11124db4641Seschrock */
11224db4641Seschrock static void
scsi_translate_error(struct scsi_extended_sense * rq,uint_t * skeyp,uint_t * ascp,uint_t * ascqp)11324db4641Seschrock scsi_translate_error(struct scsi_extended_sense *rq, uint_t *skeyp,
11424db4641Seschrock uint_t *ascp, uint_t *ascqp)
11524db4641Seschrock {
11624db4641Seschrock struct scsi_descr_sense_hdr *sdsp =
11724db4641Seschrock (struct scsi_descr_sense_hdr *)rq;
11824db4641Seschrock
11924db4641Seschrock *skeyp = rq->es_key;
12024db4641Seschrock
12124db4641Seschrock /*
12224db4641Seschrock * Get asc, ascq and info field from sense data. There are two
12324db4641Seschrock * possible formats (fixed sense data and descriptor sense data)
12424db4641Seschrock * depending on the value of es_code.
12524db4641Seschrock */
12624db4641Seschrock switch (rq->es_code) {
12724db4641Seschrock case CODE_FMT_DESCR_CURRENT:
12824db4641Seschrock case CODE_FMT_DESCR_DEFERRED:
12924db4641Seschrock
13024db4641Seschrock *ascp = sdsp->ds_add_code;
13124db4641Seschrock *ascqp = sdsp->ds_qual_code;
13224db4641Seschrock break;
13324db4641Seschrock
13424db4641Seschrock case CODE_FMT_FIXED_CURRENT:
13524db4641Seschrock case CODE_FMT_FIXED_DEFERRED:
13624db4641Seschrock default:
13724db4641Seschrock
13824db4641Seschrock if (rq->es_add_len >= 6) {
13924db4641Seschrock *ascp = rq->es_add_code;
14024db4641Seschrock *ascqp = rq->es_qual_code;
14124db4641Seschrock } else {
14224db4641Seschrock *ascp = 0xff;
14324db4641Seschrock *ascqp = 0xff;
14424db4641Seschrock }
14524db4641Seschrock break;
14624db4641Seschrock }
14724db4641Seschrock }
14824db4641Seschrock
14924db4641Seschrock /*
15024db4641Seschrock * Routines built atop the bare uscsi commands, which take into account the
15124db4641Seschrock * command length, automatically translate any scsi errors, and transparently
15224db4641Seschrock * call into the simulator if active.
15324db4641Seschrock */
15424db4641Seschrock static int
scsi_mode_select(ds_scsi_info_t * sip,uchar_t page_code,int options,void * buf,uint_t buflen,scsi_ms_hdrs_t * headers,uint_t * skp,uint_t * ascp,uint_t * ascqp)15524db4641Seschrock scsi_mode_select(ds_scsi_info_t *sip, uchar_t page_code, int options,
15624db4641Seschrock void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp,
15724db4641Seschrock uint_t *ascp, uint_t *ascqp)
15824db4641Seschrock {
15924db4641Seschrock int result;
16024db4641Seschrock struct scsi_extended_sense sense;
16124db4641Seschrock int senselen = sizeof (struct scsi_extended_sense);
16224db4641Seschrock struct mode_page *mp = (struct mode_page *)buf;
16324db4641Seschrock
16424db4641Seschrock assert(sip->si_cdblen == MODE_CMD_LEN_6 ||
16524db4641Seschrock sip->si_cdblen == MODE_CMD_LEN_10);
16624db4641Seschrock assert(headers->ms_length == sip->si_cdblen);
16724db4641Seschrock
16824db4641Seschrock bzero(&sense, sizeof (struct scsi_extended_sense));
16924db4641Seschrock
17024db4641Seschrock if (mp->ps) {
17124db4641Seschrock options |= MODE_SELECT_SP;
17224db4641Seschrock mp->ps = 0;
17324db4641Seschrock } else {
17424db4641Seschrock options &= ~MODE_SELECT_SP;
17524db4641Seschrock }
17624db4641Seschrock
17724db4641Seschrock if (sip->si_cdblen == MODE_CMD_LEN_6) {
17824db4641Seschrock /* The following fields are reserved during mode select: */
17924db4641Seschrock headers->ms_hdr.g0.ms_header.length = 0;
18024db4641Seschrock headers->ms_hdr.g0.ms_header.device_specific = 0;
18124db4641Seschrock
18224db4641Seschrock if (sip->si_sim)
18324db4641Seschrock result = simscsi_mode_select(sip->si_sim,
18424db4641Seschrock page_code, options, buf, buflen,
18524db4641Seschrock &headers->ms_hdr.g0, &sense, &senselen);
18624db4641Seschrock else
18724db4641Seschrock result = uscsi_mode_select(sip->si_dsp->ds_fd,
18824db4641Seschrock page_code, options, buf, buflen,
18924db4641Seschrock &headers->ms_hdr.g0, &sense, &senselen);
19024db4641Seschrock } else {
19124db4641Seschrock /* The following fields are reserved during mode select: */
19224db4641Seschrock headers->ms_hdr.g1.ms_header.length = 0;
19324db4641Seschrock headers->ms_hdr.g1.ms_header.device_specific = 0;
19424db4641Seschrock
19524db4641Seschrock if (sip->si_sim)
19624db4641Seschrock result = simscsi_mode_select_10(sip->si_sim,
19724db4641Seschrock page_code, options, buf, buflen,
19824db4641Seschrock &headers->ms_hdr.g1, &sense, &senselen);
19924db4641Seschrock else
20024db4641Seschrock result = uscsi_mode_select_10(sip->si_dsp->ds_fd,
20124db4641Seschrock page_code, options, buf, buflen,
20224db4641Seschrock &headers->ms_hdr.g1, &sense, &senselen);
20324db4641Seschrock }
20424db4641Seschrock
20524db4641Seschrock if (result != 0)
20624db4641Seschrock scsi_translate_error(&sense, skp, ascp, ascqp);
20724db4641Seschrock
20824db4641Seschrock return (result);
20924db4641Seschrock }
21024db4641Seschrock
21124db4641Seschrock static int
scsi_mode_sense(ds_scsi_info_t * sip,uchar_t page_code,uchar_t pc,void * buf,uint_t buflen,scsi_ms_hdrs_t * headers,uint_t * skp,uint_t * ascp,uint_t * ascqp)21224db4641Seschrock scsi_mode_sense(ds_scsi_info_t *sip, uchar_t page_code, uchar_t pc,
21324db4641Seschrock void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp,
21424db4641Seschrock uint_t *ascp, uint_t *ascqp)
21524db4641Seschrock {
21624db4641Seschrock int result;
21724db4641Seschrock struct scsi_extended_sense sense;
21824db4641Seschrock int senselen = sizeof (struct scsi_extended_sense);
21924db4641Seschrock
22024db4641Seschrock assert(sip->si_cdblen == MODE_CMD_LEN_6 ||
22124db4641Seschrock sip->si_cdblen == MODE_CMD_LEN_10);
22224db4641Seschrock
22324db4641Seschrock bzero(&sense, sizeof (struct scsi_extended_sense));
22424db4641Seschrock
22524db4641Seschrock bzero(headers, sizeof (scsi_ms_hdrs_t));
22624db4641Seschrock headers->ms_length = sip->si_cdblen;
22724db4641Seschrock
22824db4641Seschrock if (sip->si_cdblen == MODE_CMD_LEN_6) {
22924db4641Seschrock if (sip->si_sim)
23024db4641Seschrock result = simscsi_mode_sense(sip->si_sim,
23124db4641Seschrock page_code, pc, buf, buflen, &headers->ms_hdr.g0,
23224db4641Seschrock &sense, &senselen);
23324db4641Seschrock else
23424db4641Seschrock result = uscsi_mode_sense(sip->si_dsp->ds_fd, page_code,
23524db4641Seschrock pc, buf, buflen, &headers->ms_hdr.g0, &sense,
23624db4641Seschrock &senselen);
23724db4641Seschrock } else {
23824db4641Seschrock if (sip->si_sim)
23924db4641Seschrock result = simscsi_mode_sense_10(sip->si_sim,
24024db4641Seschrock page_code, pc, buf, buflen, &headers->ms_hdr.g1,
24124db4641Seschrock &sense, &senselen);
24224db4641Seschrock else
24324db4641Seschrock result = uscsi_mode_sense_10(sip->si_dsp->ds_fd,
24424db4641Seschrock page_code, pc, buf, buflen, &headers->ms_hdr.g1,
24524db4641Seschrock &sense, &senselen);
24624db4641Seschrock }
24724db4641Seschrock
24824db4641Seschrock if (result != 0)
24924db4641Seschrock scsi_translate_error(&sense, skp, ascp, ascqp);
25024db4641Seschrock
25124db4641Seschrock return (result);
25224db4641Seschrock }
25324db4641Seschrock
25424db4641Seschrock static int
scsi_request_sense(ds_scsi_info_t * sip,uint_t * skp,uint_t * ascp,uint_t * ascqp)25524db4641Seschrock scsi_request_sense(ds_scsi_info_t *sip, uint_t *skp, uint_t *ascp,
25624db4641Seschrock uint_t *ascqp)
25724db4641Seschrock {
25824db4641Seschrock struct scsi_extended_sense sense, sensebuf;
25924db4641Seschrock int senselen = sizeof (struct scsi_extended_sense);
26024db4641Seschrock int sensebuflen = sizeof (struct scsi_extended_sense);
26124db4641Seschrock int result;
26224db4641Seschrock
26324db4641Seschrock bzero(&sense, sizeof (struct scsi_extended_sense));
26424db4641Seschrock bzero(&sensebuf, sizeof (struct scsi_extended_sense));
26524db4641Seschrock
26624db4641Seschrock if (sip->si_sim)
26724db4641Seschrock result = simscsi_request_sense(sip->si_sim,
26824db4641Seschrock (caddr_t)&sensebuf, sensebuflen, &sense, &senselen);
26924db4641Seschrock else
27024db4641Seschrock result = uscsi_request_sense(sip->si_dsp->ds_fd,
27124db4641Seschrock (caddr_t)&sensebuf, sensebuflen, &sense, &senselen);
27224db4641Seschrock
27324db4641Seschrock if (result == 0)
27424db4641Seschrock scsi_translate_error(&sensebuf, skp, ascp, ascqp);
27524db4641Seschrock else
27624db4641Seschrock scsi_translate_error(&sense, skp, ascp, ascqp);
27724db4641Seschrock
27824db4641Seschrock return (result);
27924db4641Seschrock }
28024db4641Seschrock
28124db4641Seschrock static int
scsi_log_sense(ds_scsi_info_t * sip,int page_code,int page_control,caddr_t page_data,int page_size,uint_t * skp,uint_t * ascp,uint_t * ascqp)28224db4641Seschrock scsi_log_sense(ds_scsi_info_t *sip, int page_code, int page_control,
28324db4641Seschrock caddr_t page_data, int page_size, uint_t *skp, uint_t *ascp, uint_t *ascqp)
28424db4641Seschrock {
28524db4641Seschrock int result;
28624db4641Seschrock struct scsi_extended_sense sense;
28724db4641Seschrock int senselen = sizeof (struct scsi_extended_sense);
28824db4641Seschrock
28924db4641Seschrock if (sip->si_sim)
29024db4641Seschrock result = simscsi_log_sense(sip->si_sim,
29124db4641Seschrock page_code, page_control, page_data, page_size, &sense,
29224db4641Seschrock &senselen);
29324db4641Seschrock else
29424db4641Seschrock result = uscsi_log_sense(sip->si_dsp->ds_fd,
29524db4641Seschrock page_code, page_control, page_data, page_size, &sense,
29624db4641Seschrock &senselen);
29724db4641Seschrock
29824db4641Seschrock if (result != 0)
29924db4641Seschrock scsi_translate_error(&sense, skp, ascp, ascqp);
30024db4641Seschrock
30124db4641Seschrock return (result);
30224db4641Seschrock }
30324db4641Seschrock
30424db4641Seschrock /*
30524db4641Seschrock * Given a list of supported mode pages, determine if the given page is present.
30624db4641Seschrock */
30724db4641Seschrock static boolean_t
mode_page_present(uchar_t * pgdata,uint_t pgdatalen,uchar_t pagecode)30824db4641Seschrock mode_page_present(uchar_t *pgdata, uint_t pgdatalen, uchar_t pagecode)
30924db4641Seschrock {
31024db4641Seschrock uint_t i = 0;
31124db4641Seschrock struct mode_page *pg;
31224db4641Seschrock boolean_t found = B_FALSE;
31324db4641Seschrock
31424db4641Seschrock /*
31524db4641Seschrock * The mode page list contains all mode pages supported by the device,
31624db4641Seschrock * one after the other.
31724db4641Seschrock */
31824db4641Seschrock while (i < pgdatalen) {
31924db4641Seschrock pg = (struct mode_page *)&pgdata[i];
32024db4641Seschrock
32124db4641Seschrock if (pg->code == pagecode) {
32224db4641Seschrock found = B_TRUE;
32324db4641Seschrock break;
32424db4641Seschrock }
32524db4641Seschrock
32624db4641Seschrock i += MODESENSE_PAGE_LEN(pg);
32724db4641Seschrock }
32824db4641Seschrock
32924db4641Seschrock return (found);
33024db4641Seschrock }
33124db4641Seschrock
33224db4641Seschrock /*
33324db4641Seschrock * Load mode pages and check that the appropriate pages are supported.
33424db4641Seschrock *
33524db4641Seschrock * As part of this process, we determine which form of the MODE SENSE / MODE
33624db4641Seschrock * SELECT command to use (the 6-byte or 10-byte version) by executing a MODE
33724db4641Seschrock * SENSE command for a page that should be implemented by the device.
33824db4641Seschrock */
33924db4641Seschrock static int
load_modepages(ds_scsi_info_t * sip)34024db4641Seschrock load_modepages(ds_scsi_info_t *sip)
34124db4641Seschrock {
34224db4641Seschrock int allpages_buflen;
34324db4641Seschrock uchar_t *allpages;
34424db4641Seschrock scsi_ms_hdrs_t headers;
34524db4641Seschrock int result;
34624db4641Seschrock uint_t skey, asc, ascq;
34724db4641Seschrock int datalength = 0;
34824db4641Seschrock scsi_ms_header_t *smh = &headers.ms_hdr.g0;
34924db4641Seschrock scsi_ms_header_g1_t *smh_g1 = &headers.ms_hdr.g1;
35024db4641Seschrock nvlist_t *nvl;
35124db4641Seschrock
35224db4641Seschrock allpages_buflen = MAX_BUFLEN(scsi_ms_header_g1_t);
35324db4641Seschrock if ((allpages = calloc(allpages_buflen, 1)) == NULL)
35424db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
35524db4641Seschrock
35624db4641Seschrock bzero(&headers, sizeof (headers));
35724db4641Seschrock
35824db4641Seschrock /*
35924db4641Seschrock * Attempt a mode sense(6). If that fails, try a mode sense(10)
36024db4641Seschrock *
36124db4641Seschrock * allpages is allocated to be of the maximum size for either a mode
36224db4641Seschrock * sense(6) or mode sense(10) MODEPAGE_ALLPAGES response.
36324db4641Seschrock *
36424db4641Seschrock * Note that the length passed into uscsi_mode_sense should be set to
36524db4641Seschrock * the maximum size of the parameter response, which in this case is
36624db4641Seschrock * UCHAR_MAX - the size of the headers/block descriptors.
36724db4641Seschrock */
36824db4641Seschrock sip->si_cdblen = MODE_CMD_LEN_6;
36924db4641Seschrock if ((result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES, PC_CURRENT,
37024db4641Seschrock (caddr_t)allpages, UCHAR_MAX - sizeof (scsi_ms_header_t),
37124db4641Seschrock &headers, &skey, &asc, &ascq)) == 0) {
37224db4641Seschrock /*
37324db4641Seschrock * Compute the data length of the page that contains all mode
37424db4641Seschrock * sense pages. This is a bit tricky because the format of the
37524db4641Seschrock * response from the lun is:
37624db4641Seschrock *
37724db4641Seschrock * header: <length> <medium type byte> <dev specific byte>
37824db4641Seschrock * <block descriptor length>
37924db4641Seschrock * [<optional block descriptor>]
38024db4641Seschrock * data: [<mode page data> <mode page data> ...]
38124db4641Seschrock *
38224db4641Seschrock * Since the length field in the header describes the length of
38324db4641Seschrock * the entire response. This includes the header, but NOT
38424db4641Seschrock * the length field itself, which is 1 or 2 bytes depending on
38524db4641Seschrock * which mode sense type (6- or 10- byte) is being executed.
38624db4641Seschrock *
38724db4641Seschrock * So, the data length equals the length value in the header
38824db4641Seschrock * plus 1 (because the length byte was not included in the
38924db4641Seschrock * length count), minus [[the sum of the length of the header
39024db4641Seschrock * and the length of the block descriptor]].
39124db4641Seschrock */
39224db4641Seschrock datalength = (smh->ms_header.length +
39324db4641Seschrock sizeof (smh->ms_header.length)) -
39424db4641Seschrock (sizeof (struct mode_header) +
39524db4641Seschrock smh->ms_header.bdesc_length);
39624db4641Seschrock } else if (SCSI_INVALID_OPCODE(skey, asc, ascq)) {
39724db4641Seschrock /*
39824db4641Seschrock * Fallback and try the 10-byte version of the command.
39924db4641Seschrock */
40024db4641Seschrock sip->si_cdblen = MODE_CMD_LEN_10;
40124db4641Seschrock result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES,
40224db4641Seschrock PC_CURRENT, (caddr_t)allpages, allpages_buflen,
40324db4641Seschrock &headers, &skey, &asc, &ascq);
40424db4641Seschrock
40524db4641Seschrock if (result == 0) {
40624db4641Seschrock datalength = (BE_16(smh_g1->ms_header.length) +
40724db4641Seschrock sizeof (smh_g1->ms_header.length)) -
40824db4641Seschrock (sizeof (struct mode_header_g1) +
40924db4641Seschrock BE_16(smh_g1->ms_header.bdesc_length));
41024db4641Seschrock
41124db4641Seschrock }
41224db4641Seschrock }
41324db4641Seschrock
41424db4641Seschrock if (result == 0 && datalength >= 0) {
41524db4641Seschrock if (nvlist_add_int8(sip->si_dsp->ds_state, "command-length",
41624db4641Seschrock sip->si_cdblen == MODE_CMD_LEN_6 ? 6 : 10) != 0) {
41724db4641Seschrock free(allpages);
41824db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
41924db4641Seschrock }
42024db4641Seschrock
42124db4641Seschrock nvl = NULL;
42224db4641Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
42324db4641Seschrock nvlist_add_nvlist(sip->si_dsp->ds_state, "modepages",
42424db4641Seschrock nvl) != 0) {
42524db4641Seschrock free(allpages);
42624db4641Seschrock nvlist_free(nvl);
42724db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
42824db4641Seschrock }
42924db4641Seschrock
43024db4641Seschrock nvlist_free(nvl);
43124db4641Seschrock result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
43224db4641Seschrock "modepages", &sip->si_state_modepage);
43324db4641Seschrock assert(result == 0);
43424db4641Seschrock
43524db4641Seschrock /*
43624db4641Seschrock * One of the sets of the commands (above) succeeded, so now
43724db4641Seschrock * look for the mode pages we need and record them appropriately
43824db4641Seschrock */
43924db4641Seschrock if (mode_page_present(allpages, datalength,
44024db4641Seschrock MODEPAGE_INFO_EXCPT)) {
44124db4641Seschrock
44224db4641Seschrock nvl = NULL;
44324db4641Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
44424db4641Seschrock nvlist_add_nvlist(sip->si_state_modepage,
44524db4641Seschrock "informational-exceptions", nvl) != 0) {
44624db4641Seschrock free(allpages);
44724db4641Seschrock nvlist_free(nvl);
44824db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
44924db4641Seschrock }
45024db4641Seschrock nvlist_free(nvl);
45124db4641Seschrock sip->si_supp_mode |= MODEPAGE_SUPP_IEC;
45224db4641Seschrock result = nvlist_lookup_nvlist(sip->si_state_modepage,
45324db4641Seschrock "informational-exceptions", &sip->si_state_iec);
45424db4641Seschrock assert(result == 0);
45524db4641Seschrock }
45624db4641Seschrock
45724db4641Seschrock } else {
45824db4641Seschrock /*
45924db4641Seschrock * If the device failed to respond to one of the basic commands,
46024db4641Seschrock * then assume it's not a SCSI device or otherwise doesn't
46124db4641Seschrock * support the necessary transport.
46224db4641Seschrock */
46324db4641Seschrock if (datalength < 0)
46424db4641Seschrock dprintf("command returned invalid data length (%d)\n",
46524db4641Seschrock datalength);
46624db4641Seschrock else
46724db4641Seschrock dprintf("failed to load modepages (KEY=0x%x "
46824db4641Seschrock "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
46924db4641Seschrock
47024db4641Seschrock result = scsi_set_errno(sip, EDS_NO_TRANSPORT);
47124db4641Seschrock }
47224db4641Seschrock
47324db4641Seschrock free(allpages);
47424db4641Seschrock return (result);
47524db4641Seschrock }
47624db4641Seschrock
47724db4641Seschrock /*
47824db4641Seschrock * Verify a single logpage. This will do some generic validation and then call
47924db4641Seschrock * the logpage-specific function for further verification.
48024db4641Seschrock */
48124db4641Seschrock static int
verify_logpage(ds_scsi_info_t * sip,logpage_validation_entry_t * lp)48224db4641Seschrock verify_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *lp)
48324db4641Seschrock {
48424db4641Seschrock scsi_log_header_t *lhp;
48524db4641Seschrock struct scsi_extended_sense sense;
48624db4641Seschrock int buflen;
48724db4641Seschrock int log_length;
48824db4641Seschrock int result = 0;
48924db4641Seschrock uint_t kp, asc, ascq;
49024db4641Seschrock nvlist_t *nvl;
49124db4641Seschrock
49224db4641Seschrock buflen = MAX_BUFLEN(scsi_log_header_t);
49324db4641Seschrock if ((lhp = calloc(buflen, 1)) == NULL)
49424db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
49524db4641Seschrock bzero(&sense, sizeof (struct scsi_extended_sense));
49624db4641Seschrock
49724db4641Seschrock nvl = NULL;
49824db4641Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
49924db4641Seschrock nvlist_add_nvlist(sip->si_state_logpage, lp->ve_desc, nvl) != 0) {
50024db4641Seschrock nvlist_free(nvl);
50124db4641Seschrock free(lhp);
50224db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
50324db4641Seschrock }
50424db4641Seschrock nvlist_free(nvl);
50524db4641Seschrock result = nvlist_lookup_nvlist(sip->si_state_logpage, lp->ve_desc, &nvl);
50624db4641Seschrock assert(result == 0);
50724db4641Seschrock
50824db4641Seschrock result = scsi_log_sense(sip, lp->ve_code,
50924db4641Seschrock PC_CUMULATIVE, (caddr_t)lhp, buflen, &kp, &asc, &ascq);
51024db4641Seschrock
51124db4641Seschrock if (result == 0) {
51224db4641Seschrock log_length = BE_16(lhp->lh_length);
51324db4641Seschrock if (nvlist_add_uint16(nvl, "length", log_length) != 0) {
51424db4641Seschrock free(lhp);
51524db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
51624db4641Seschrock }
51724db4641Seschrock
51824db4641Seschrock if (lp->ve_validate(sip, (scsi_log_parameter_header_t *)
51924db4641Seschrock (((char *)lhp) + sizeof (scsi_log_header_t)),
52024db4641Seschrock log_length, nvl) != 0) {
52124db4641Seschrock free(lhp);
52224db4641Seschrock return (-1);
52324db4641Seschrock }
52424db4641Seschrock } else {
52524db4641Seschrock dprintf("failed to load %s log page (KEY=0x%x "
52624db4641Seschrock "ASC=0x%x ASCQ=0x%x)\n", lp->ve_desc, kp, asc, ascq);
52724db4641Seschrock }
52824db4641Seschrock
52924db4641Seschrock free(lhp);
53024db4641Seschrock return (0);
53124db4641Seschrock }
53224db4641Seschrock
53324db4641Seschrock /*
53424db4641Seschrock * Load log pages and determine which pages are supported.
53524db4641Seschrock */
53624db4641Seschrock static int
load_logpages(ds_scsi_info_t * sip)53724db4641Seschrock load_logpages(ds_scsi_info_t *sip)
53824db4641Seschrock {
53924db4641Seschrock int buflen;
54024db4641Seschrock scsi_supported_log_pages_t *sp;
54124db4641Seschrock struct scsi_extended_sense sense;
54224db4641Seschrock int result;
54324db4641Seschrock uint_t sk, asc, ascq;
54424db4641Seschrock int i, j;
54524db4641Seschrock nvlist_t *nvl;
54624db4641Seschrock
54724db4641Seschrock buflen = MAX_BUFLEN(scsi_log_header_t);
54824db4641Seschrock if ((sp = calloc(buflen, 1)) == NULL)
54924db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
55024db4641Seschrock
55124db4641Seschrock bzero(&sense, sizeof (struct scsi_extended_sense));
55224db4641Seschrock
55324db4641Seschrock if ((result = scsi_log_sense(sip, LOGPAGE_SUPP_LIST,
55424db4641Seschrock PC_CUMULATIVE, (caddr_t)sp, buflen, &sk, &asc, &ascq)) == 0) {
55524db4641Seschrock int pagecount = BE_16(sp->slp_hdr.lh_length);
55624db4641Seschrock
55724db4641Seschrock for (i = 0; i < pagecount; i++) {
55824db4641Seschrock for (j = 0; j < NLOG_VALIDATION; j++) {
55924db4641Seschrock if (log_validation[j].ve_code ==
56024db4641Seschrock sp->slp_pages[i])
56124db4641Seschrock sip->si_supp_log |=
56224db4641Seschrock log_validation[j].ve_supported;
56324db4641Seschrock }
56424db4641Seschrock }
56524db4641Seschrock }
56624db4641Seschrock
56724db4641Seschrock free(sp);
56824db4641Seschrock if (result == 0) {
56924db4641Seschrock nvl = NULL;
57024db4641Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
57124db4641Seschrock nvlist_add_nvlist(sip->si_dsp->ds_state, "logpages",
57224db4641Seschrock nvl) != 0) {
57324db4641Seschrock nvlist_free(nvl);
57424db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
57524db4641Seschrock }
57624db4641Seschrock
57724db4641Seschrock nvlist_free(nvl);
57824db4641Seschrock result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
57924db4641Seschrock "logpages", &sip->si_state_logpage);
58024db4641Seschrock assert(result == 0);
58124db4641Seschrock
58224db4641Seschrock /*
58324db4641Seschrock * Validate the logpage contents.
58424db4641Seschrock */
58524db4641Seschrock for (i = 0; i < NLOG_VALIDATION; i++) {
58624db4641Seschrock if ((sip->si_supp_log &
58724db4641Seschrock log_validation[i].ve_supported) == 0)
58824db4641Seschrock continue;
58924db4641Seschrock
59024db4641Seschrock /*
59124db4641Seschrock * verify_logpage will clear the supported bit if
59224db4641Seschrock * verification fails.
59324db4641Seschrock */
59424db4641Seschrock if (verify_logpage(sip, &log_validation[i]) != 0)
59524db4641Seschrock return (-1);
59624db4641Seschrock }
59724db4641Seschrock
59824db4641Seschrock } else {
59924db4641Seschrock dprintf("failed to get log pages "
60024db4641Seschrock "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk, asc, ascq);
60124db4641Seschrock }
60224db4641Seschrock
60324db4641Seschrock /*
60424db4641Seschrock * We always return 0 here, even if the required log pages aren't
60524db4641Seschrock * supported.
60624db4641Seschrock */
60724db4641Seschrock return (0);
60824db4641Seschrock }
60924db4641Seschrock
61024db4641Seschrock /*
61124db4641Seschrock * Verify that the IE log page is sane. This log page is potentially chock-full
61224db4641Seschrock * of vendor specific information that we do not know how to access. All we can
61324db4641Seschrock * do is check for the generic predictive failure bit. If this log page is not
61424db4641Seschrock * well-formed, then bail out.
61524db4641Seschrock */
61624db4641Seschrock static int
logpage_ie_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)61724db4641Seschrock logpage_ie_verify(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
61824db4641Seschrock int log_length, nvlist_t *nvl)
61924db4641Seschrock {
62024db4641Seschrock int i, plen = 0;
62124db4641Seschrock boolean_t seen = B_FALSE;
62224db4641Seschrock scsi_ie_log_param_t *iep =
62324db4641Seschrock (scsi_ie_log_param_t *)lphp;
62424db4641Seschrock
62524db4641Seschrock for (i = 0; i < log_length; i += plen) {
62624db4641Seschrock iep = (scsi_ie_log_param_t *)((char *)iep + plen);
62724db4641Seschrock
62824db4641Seschrock if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE) {
62924db4641Seschrock if (nvlist_add_boolean_value(nvl, "general",
63024db4641Seschrock B_TRUE) != 0)
63124db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
63224db4641Seschrock
63324db4641Seschrock if (lphp->lph_length < LOGPARAM_IE_MIN_LEN) {
63424db4641Seschrock if (nvlist_add_uint8(nvl,
63524db4641Seschrock "invalid-length", lphp->lph_length) != 0)
63624db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
63724db4641Seschrock } else {
63824db4641Seschrock seen = B_TRUE;
63924db4641Seschrock }
64024db4641Seschrock break;
64124db4641Seschrock }
64224db4641Seschrock
64324db4641Seschrock plen = iep->ie_hdr.lph_length +
64424db4641Seschrock sizeof (scsi_log_parameter_header_t);
64524db4641Seschrock }
64624db4641Seschrock
64724db4641Seschrock if (!seen) {
64824db4641Seschrock sip->si_supp_log &= ~LOGPAGE_SUPP_IE;
64924db4641Seschrock dprintf("IE logpage validation failed\n");
65024db4641Seschrock }
65124db4641Seschrock
65224db4641Seschrock return (0);
65324db4641Seschrock }
65424db4641Seschrock
65524db4641Seschrock /*
65624db4641Seschrock * Verify the contents of the temperature log page. The temperature log page
65724db4641Seschrock * contains two log parameters: the current temperature, and (optionally) the
65824db4641Seschrock * reference temperature. For the verification phase, we check that the two
65924db4641Seschrock * parameters we care about are well-formed. If there is no reference
66024db4641Seschrock * temperature, then we cannot use the page for monitoring purposes.
66124db4641Seschrock */
66224db4641Seschrock static int
logpage_temp_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)66324db4641Seschrock logpage_temp_verify(ds_scsi_info_t *sip,
66424db4641Seschrock scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
66524db4641Seschrock {
66624db4641Seschrock int i, plen = 0;
66724db4641Seschrock boolean_t has_reftemp = B_FALSE;
66824db4641Seschrock boolean_t bad_length = B_FALSE;
66924db4641Seschrock ushort_t param_code;
67024db4641Seschrock
67124db4641Seschrock for (i = 0; i < log_length; i += plen) {
67224db4641Seschrock lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
67324db4641Seschrock param_code = BE_16(lphp->lph_param);
67424db4641Seschrock
67524db4641Seschrock switch (param_code) {
67624db4641Seschrock case LOGPARAM_TEMP_CURTEMP:
67724db4641Seschrock if (nvlist_add_boolean_value(nvl, "current-temperature",
67824db4641Seschrock B_TRUE) != 0)
67924db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
68024db4641Seschrock if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
68124db4641Seschrock if (nvlist_add_uint8(nvl,
68224db4641Seschrock "invalid-length", lphp->lph_length) != 0)
68324db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
68424db4641Seschrock bad_length = B_TRUE;
68524db4641Seschrock }
68624db4641Seschrock break;
68724db4641Seschrock
68824db4641Seschrock case LOGPARAM_TEMP_REFTEMP:
68924db4641Seschrock if (nvlist_add_boolean_value(nvl,
69024db4641Seschrock "reference-temperature", B_TRUE) != 0)
69124db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
69224db4641Seschrock if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
69324db4641Seschrock if (nvlist_add_uint8(nvl,
69424db4641Seschrock "invalid-length", lphp->lph_length) != 0)
69524db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
69624db4641Seschrock bad_length = B_TRUE;
69724db4641Seschrock }
69824db4641Seschrock has_reftemp = B_TRUE;
69924db4641Seschrock break;
70024db4641Seschrock }
70124db4641Seschrock
70224db4641Seschrock plen = lphp->lph_length +
70324db4641Seschrock sizeof (scsi_log_parameter_header_t);
70424db4641Seschrock }
70524db4641Seschrock
70624db4641Seschrock if (bad_length || !has_reftemp) {
70724db4641Seschrock sip->si_supp_log &= ~LOGPAGE_SUPP_TEMP;
70824db4641Seschrock dprintf("temperature logpage validation failed\n");
70924db4641Seschrock }
71024db4641Seschrock
71124db4641Seschrock return (0);
71224db4641Seschrock }
71324db4641Seschrock
71424db4641Seschrock /*
71524db4641Seschrock * Verify the contents of the self test log page. The log supports a maximum of
71624db4641Seschrock * 20 entries, where each entry's parameter code is its index in the log. We
71724db4641Seschrock * check that the parameter codes fall within this range, and that the size of
71824db4641Seschrock * each page is what we expect. It's perfectly acceptable for there to be no
71924db4641Seschrock * entries in this log, so we must also be sure to validate the contents as part
72024db4641Seschrock * of the analysis phase.
72124db4641Seschrock */
72224db4641Seschrock static int
logpage_selftest_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)72324db4641Seschrock logpage_selftest_verify(ds_scsi_info_t *sip,
72424db4641Seschrock scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
72524db4641Seschrock {
72624db4641Seschrock int i, plen = 0;
72724db4641Seschrock boolean_t bad = B_FALSE;
72824db4641Seschrock int entries = 0;
72924db4641Seschrock ushort_t param_code;
73024db4641Seschrock
73124db4641Seschrock for (i = 0; i < log_length; i += plen, entries++) {
73224db4641Seschrock lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
73324db4641Seschrock param_code = BE_16(lphp->lph_param);
73424db4641Seschrock
73524db4641Seschrock if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE ||
73624db4641Seschrock param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) {
73724db4641Seschrock if (nvlist_add_uint16(nvl, "invalid-param-code",
73824db4641Seschrock param_code) != 0)
73924db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
74024db4641Seschrock bad = B_TRUE;
74124db4641Seschrock break;
74224db4641Seschrock }
74324db4641Seschrock
74424db4641Seschrock if (lphp->lph_length != LOGPAGE_SELFTEST_PARAM_LEN) {
74524db4641Seschrock if (nvlist_add_uint8(nvl, "invalid-length",
74624db4641Seschrock lphp->lph_length) != 0)
74724db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
74824db4641Seschrock bad = B_TRUE;
74924db4641Seschrock break;
75024db4641Seschrock
75124db4641Seschrock }
75224db4641Seschrock
75324db4641Seschrock plen = lphp->lph_length +
75424db4641Seschrock sizeof (scsi_log_parameter_header_t);
75524db4641Seschrock }
75624db4641Seschrock
75724db4641Seschrock if (bad) {
75824db4641Seschrock sip->si_supp_log &= ~LOGPAGE_SUPP_SELFTEST;
75924db4641Seschrock dprintf("selftest logpage validation failed\n");
76024db4641Seschrock }
76124db4641Seschrock
76224db4641Seschrock return (0);
76324db4641Seschrock }
76424db4641Seschrock
7650244979bSAlek Pinchuk /*
7660244979bSAlek Pinchuk * Verify the contents of the Solid State Media (SSM) log page.
7670244979bSAlek Pinchuk * As of SBC3r36 SSM log page contains one log parameter:
7680244979bSAlek Pinchuk * "Percentage Used Endurance Indicator" which is mandatory.
7690244979bSAlek Pinchuk * For the verification phase, we sanity check this parameter
7700244979bSAlek Pinchuk * by making sure it's present and it's length is set to 0x04.
7710244979bSAlek Pinchuk */
7720244979bSAlek Pinchuk static int
logpage_ssm_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)7730244979bSAlek Pinchuk logpage_ssm_verify(ds_scsi_info_t *sip,
7740244979bSAlek Pinchuk scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
7750244979bSAlek Pinchuk {
7760244979bSAlek Pinchuk ushort_t param_code;
7770244979bSAlek Pinchuk int i, plen = 0;
7780244979bSAlek Pinchuk
7790244979bSAlek Pinchuk for (i = 0; i < log_length; i += plen) {
7800244979bSAlek Pinchuk lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
7810244979bSAlek Pinchuk param_code = BE_16(lphp->lph_param);
7820244979bSAlek Pinchuk
7830244979bSAlek Pinchuk switch (param_code) {
7840244979bSAlek Pinchuk case LOGPARAM_PRCNT_USED:
7850244979bSAlek Pinchuk if (nvlist_add_boolean_value(nvl,
7860244979bSAlek Pinchuk FM_EREPORT_SCSI_SSMWEAROUT, B_TRUE) != 0)
7870244979bSAlek Pinchuk return (scsi_set_errno(sip, EDS_NOMEM));
7880244979bSAlek Pinchuk if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN) {
7890244979bSAlek Pinchuk if (nvlist_add_uint8(nvl,
7900244979bSAlek Pinchuk "invalid-length", lphp->lph_length) != 0)
7910244979bSAlek Pinchuk return (scsi_set_errno(sip, EDS_NOMEM));
7920244979bSAlek Pinchuk
7930244979bSAlek Pinchuk dprintf("solid state media logpage bad len\n");
7940244979bSAlek Pinchuk break;
7950244979bSAlek Pinchuk }
7960244979bSAlek Pinchuk
7970244979bSAlek Pinchuk /* verification succeded */
7980244979bSAlek Pinchuk return (0);
7990244979bSAlek Pinchuk }
8000244979bSAlek Pinchuk
8010244979bSAlek Pinchuk plen = lphp->lph_length +
8020244979bSAlek Pinchuk sizeof (scsi_log_parameter_header_t);
8030244979bSAlek Pinchuk }
8040244979bSAlek Pinchuk
8050244979bSAlek Pinchuk /* verification failed */
8060244979bSAlek Pinchuk sip->si_supp_log &= ~LOGPAGE_SUPP_SSM;
8070244979bSAlek Pinchuk return (0);
8080244979bSAlek Pinchuk }
8090244979bSAlek Pinchuk
81024db4641Seschrock /*
81124db4641Seschrock * Load the current IE mode pages
81224db4641Seschrock */
81324db4641Seschrock static int
load_ie_modepage(ds_scsi_info_t * sip)81424db4641Seschrock load_ie_modepage(ds_scsi_info_t *sip)
81524db4641Seschrock {
81624db4641Seschrock struct scsi_ms_hdrs junk_hdrs;
81724db4641Seschrock int result;
81824db4641Seschrock uint_t skey, asc, ascq;
81924db4641Seschrock
82024db4641Seschrock if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
82124db4641Seschrock return (0);
82224db4641Seschrock
82324db4641Seschrock bzero(&sip->si_iec_current, sizeof (sip->si_iec_current));
82424db4641Seschrock bzero(&sip->si_iec_changeable, sizeof (sip->si_iec_changeable));
82524db4641Seschrock
82624db4641Seschrock if ((result = scsi_mode_sense(sip,
82724db4641Seschrock MODEPAGE_INFO_EXCPT, PC_CURRENT, &sip->si_iec_current,
82824db4641Seschrock MODEPAGE_INFO_EXCPT_LEN, &sip->si_hdrs, &skey, &asc,
82924db4641Seschrock &ascq)) == 0) {
83024db4641Seschrock result = scsi_mode_sense(sip,
83124db4641Seschrock MODEPAGE_INFO_EXCPT, PC_CHANGEABLE,
83224db4641Seschrock &sip->si_iec_changeable,
83324db4641Seschrock MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, &skey, &asc, &ascq);
83424db4641Seschrock }
83524db4641Seschrock
83624db4641Seschrock if (result != 0) {
83724db4641Seschrock dprintf("failed to get IEC modepage (KEY=0x%x "
83824db4641Seschrock "ASC=0x%x ASCQ=0x%x)", skey, asc, ascq);
83924db4641Seschrock sip->si_supp_mode &= ~MODEPAGE_SUPP_IEC;
84024db4641Seschrock } else {
84124db4641Seschrock if (nvlist_add_boolean_value(sip->si_state_iec,
84224db4641Seschrock "dexcpt", sip->si_iec_current.ie_dexcpt) != 0 ||
84324db4641Seschrock nvlist_add_boolean_value(sip->si_state_iec,
84424db4641Seschrock "logerr", sip->si_iec_current.ie_logerr) != 0 ||
84524db4641Seschrock nvlist_add_uint8(sip->si_state_iec,
84624db4641Seschrock "mrie", sip->si_iec_current.ie_mrie) != 0 ||
84724db4641Seschrock nvlist_add_boolean_value(sip->si_state_iec,
84824db4641Seschrock "test", sip->si_iec_current.ie_test) != 0 ||
84924db4641Seschrock nvlist_add_boolean_value(sip->si_state_iec,
85024db4641Seschrock "ewasc", sip->si_iec_current.ie_ewasc) != 0 ||
85124db4641Seschrock nvlist_add_boolean_value(sip->si_state_iec,
85224db4641Seschrock "perf", sip->si_iec_current.ie_perf) != 0 ||
85324db4641Seschrock nvlist_add_boolean_value(sip->si_state_iec,
85424db4641Seschrock "ebf", sip->si_iec_current.ie_ebf) != 0 ||
85524db4641Seschrock nvlist_add_uint32(sip->si_state_iec,
85624db4641Seschrock "interval-timer",
85724db4641Seschrock BE_32(sip->si_iec_current.ie_interval_timer)) != 0 ||
85824db4641Seschrock nvlist_add_uint32(sip->si_state_iec,
85924db4641Seschrock "report-count",
86024db4641Seschrock BE_32(sip->si_iec_current.ie_report_count)) != 0)
86124db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
86224db4641Seschrock }
86324db4641Seschrock
86424db4641Seschrock return (0);
86524db4641Seschrock }
86624db4641Seschrock
86724db4641Seschrock /*
86824db4641Seschrock * Enable IE reporting. We prefer the following settings:
86924db4641Seschrock *
87024db4641Seschrock * (1) DEXCPT = 0
87124db4641Seschrock * (3) MRIE = 6 (IE_REPORT_ON_REQUEST)
87224db4641Seschrock * (4) EWASC = 1
87324db4641Seschrock * (6) REPORT COUNT = 0x00000001
87424db4641Seschrock * (7) LOGERR = 1
87524db4641Seschrock *
87624db4641Seschrock * However, not all drives support changing these values, and the current state
87724db4641Seschrock * may be useful enough as-is. For example, some drives support IE logging, but
87824db4641Seschrock * don't support changing the MRIE. In this case, we can still use the
87924db4641Seschrock * information provided by the log page.
88024db4641Seschrock */
88124db4641Seschrock static int
scsi_enable_ie(ds_scsi_info_t * sip,boolean_t * changed)88224db4641Seschrock scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed)
88324db4641Seschrock {
88424db4641Seschrock scsi_ie_page_t new_iec_page;
88524db4641Seschrock scsi_ms_hdrs_t hdrs;
88624db4641Seschrock uint_t skey, asc, ascq;
88724db4641Seschrock
88824db4641Seschrock if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
88924db4641Seschrock return (0);
89024db4641Seschrock
89124db4641Seschrock bzero(&new_iec_page, sizeof (new_iec_page));
89224db4641Seschrock bzero(&hdrs, sizeof (hdrs));
89324db4641Seschrock
89424db4641Seschrock (void) memcpy(&new_iec_page, &sip->si_iec_current,
89524db4641Seschrock sizeof (new_iec_page));
89624db4641Seschrock
89724db4641Seschrock if (IEC_IE_CHANGEABLE(sip->si_iec_changeable))
89824db4641Seschrock new_iec_page.ie_dexcpt = 0;
89924db4641Seschrock
90024db4641Seschrock if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable))
90124db4641Seschrock new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST;
90224db4641Seschrock
90324db4641Seschrock /*
90424db4641Seschrock * We only want to enable warning reporting if we are able to change the
90524db4641Seschrock * mrie to report on request. Otherwise, we risk unnecessarily
90624db4641Seschrock * interrupting normal SCSI commands with a CHECK CONDITION code.
90724db4641Seschrock */
90824db4641Seschrock if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) {
90924db4641Seschrock if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST)
91024db4641Seschrock new_iec_page.ie_ewasc = 1;
91124db4641Seschrock else
91224db4641Seschrock new_iec_page.ie_ewasc = 0;
91324db4641Seschrock }
91424db4641Seschrock
91524db4641Seschrock if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable))
91624db4641Seschrock new_iec_page.ie_report_count = BE_32(1);
91724db4641Seschrock
91824db4641Seschrock if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable))
91924db4641Seschrock new_iec_page.ie_logerr = 1;
92024db4641Seschrock
92124db4641Seschrock /*
92224db4641Seschrock * Now compare the new mode page with the existing one.
92324db4641Seschrock * if there's no difference, there's no need for a mode select
92424db4641Seschrock */
92524db4641Seschrock if (memcmp(&new_iec_page, &sip->si_iec_current,
92624db4641Seschrock MODEPAGE_INFO_EXCPT_LEN) == 0) {
92724db4641Seschrock *changed = B_FALSE;
92824db4641Seschrock } else {
92924db4641Seschrock (void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs));
93024db4641Seschrock
93124db4641Seschrock if (scsi_mode_select(sip,
93224db4641Seschrock MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page,
93324db4641Seschrock MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) {
93424db4641Seschrock *changed = B_TRUE;
93524db4641Seschrock } else {
93624db4641Seschrock dprintf("failed to enable IE (KEY=0x%x "
93724db4641Seschrock "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
93824db4641Seschrock *changed = B_FALSE;
93924db4641Seschrock }
94024db4641Seschrock }
94124db4641Seschrock
94224db4641Seschrock if (nvlist_add_boolean_value(sip->si_state_iec, "changed",
94324db4641Seschrock *changed) != 0)
94424db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
94524db4641Seschrock
94624db4641Seschrock return (0);
94724db4641Seschrock }
94824db4641Seschrock
94924db4641Seschrock /*
95024db4641Seschrock * Clear the GLTSD bit, indicating log pages should be saved to non-volatile
95124db4641Seschrock * storage.
95224db4641Seschrock */
95324db4641Seschrock static int
clear_gltsd(ds_scsi_info_t * sip)95424db4641Seschrock clear_gltsd(ds_scsi_info_t *sip)
95524db4641Seschrock {
95624db4641Seschrock scsi_ms_hdrs_t hdrs, junk_hdrs;
95724db4641Seschrock struct mode_control_scsi3 control_pg_cur, control_pg_chg;
95824db4641Seschrock int result;
95924db4641Seschrock uint_t skey, asc, ascq;
96024db4641Seschrock
96124db4641Seschrock bzero(&hdrs, sizeof (hdrs));
96224db4641Seschrock bzero(&control_pg_cur, sizeof (control_pg_cur));
96324db4641Seschrock bzero(&control_pg_chg, sizeof (control_pg_chg));
96424db4641Seschrock
96524db4641Seschrock result = scsi_mode_sense(sip,
96624db4641Seschrock MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur,
96724db4641Seschrock MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
96824db4641Seschrock
96924db4641Seschrock if (result != 0) {
97024db4641Seschrock dprintf("failed to read Control mode page (KEY=0x%x "
97124db4641Seschrock "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
97224db4641Seschrock } else if (control_pg_cur.mode_page.length !=
97324db4641Seschrock PAGELENGTH_MODE_CONTROL_SCSI3) {
97424db4641Seschrock dprintf("SCSI-3 control mode page not supported\n");
97524db4641Seschrock } else if ((result = scsi_mode_sense(sip,
97624db4641Seschrock MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg,
97724db4641Seschrock MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq))
97824db4641Seschrock != 0) {
97924db4641Seschrock dprintf("failed to read changeable Control mode page (KEY=0x%x "
98024db4641Seschrock "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
98124db4641Seschrock } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) {
98224db4641Seschrock dprintf("gltsd is set and not changeable\n");
98324db4641Seschrock if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
98424db4641Seschrock "gltsd", control_pg_cur.gltsd) != 0)
98524db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
98624db4641Seschrock } else if (control_pg_cur.gltsd) {
98724db4641Seschrock control_pg_cur.gltsd = 0;
98824db4641Seschrock result = scsi_mode_select(sip,
98924db4641Seschrock MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur,
99024db4641Seschrock MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
99124db4641Seschrock if (result != 0)
99224db4641Seschrock dprintf("failed to enable GLTSD (KEY=0x%x "
99324db4641Seschrock "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq);
99424db4641Seschrock if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
99524db4641Seschrock "gltsd", control_pg_cur.gltsd) != 0)
99624db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
99724db4641Seschrock }
99824db4641Seschrock
99924db4641Seschrock return (0);
100024db4641Seschrock }
100124db4641Seschrock
100224db4641Seschrock /*
100324db4641Seschrock * Fetch the contents of the logpage, and then call the logpage-specific
100424db4641Seschrock * analysis function. The analysis function is responsible for detecting any
100524db4641Seschrock * faults and filling in the details.
100624db4641Seschrock */
100724db4641Seschrock static int
analyze_one_logpage(ds_scsi_info_t * sip,logpage_validation_entry_t * entry)100824db4641Seschrock analyze_one_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *entry)
100924db4641Seschrock {
101024db4641Seschrock scsi_log_header_t *lhp;
101124db4641Seschrock scsi_log_parameter_header_t *lphp;
101224db4641Seschrock int buflen;
101324db4641Seschrock int log_length;
101424db4641Seschrock uint_t skey, asc, ascq;
101524db4641Seschrock int result;
101624db4641Seschrock
101724db4641Seschrock buflen = MAX_BUFLEN(scsi_log_header_t);
101824db4641Seschrock if ((lhp = calloc(buflen, 1)) == NULL)
101924db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
102024db4641Seschrock
102124db4641Seschrock result = scsi_log_sense(sip, entry->ve_code,
102224db4641Seschrock PC_CUMULATIVE, (caddr_t)lhp, buflen, &skey, &asc, &ascq);
102324db4641Seschrock
102424db4641Seschrock if (result == 0) {
102524db4641Seschrock log_length = BE_16(lhp->lh_length);
102624db4641Seschrock lphp = (scsi_log_parameter_header_t *)(((uchar_t *)lhp) +
102724db4641Seschrock sizeof (scsi_log_header_t));
102824db4641Seschrock
102924db4641Seschrock result = entry->ve_analyze(sip, lphp, log_length);
103024db4641Seschrock } else {
103124db4641Seschrock result = scsi_set_errno(sip, EDS_IO);
103224db4641Seschrock }
103324db4641Seschrock
103424db4641Seschrock free(lhp);
103524db4641Seschrock return (result);
103624db4641Seschrock }
103724db4641Seschrock
103824db4641Seschrock /*
103924db4641Seschrock * Analyze the IE logpage. If we find an IE log record with a non-zero 'asc',
104024db4641Seschrock * then we have a fault.
104124db4641Seschrock */
104224db4641Seschrock static int
logpage_ie_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)104324db4641Seschrock logpage_ie_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
104424db4641Seschrock int log_length)
104524db4641Seschrock {
104624db4641Seschrock int i, plen = 0;
104724db4641Seschrock scsi_ie_log_param_t *iep = (scsi_ie_log_param_t *)lphp;
104824db4641Seschrock nvlist_t *nvl;
104924db4641Seschrock
105024db4641Seschrock assert(sip->si_dsp->ds_predfail == NULL);
105124db4641Seschrock if (nvlist_alloc(&sip->si_dsp->ds_predfail, NV_UNIQUE_NAME, 0) != 0)
105224db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
105324db4641Seschrock nvl = sip->si_dsp->ds_predfail;
105424db4641Seschrock
105524db4641Seschrock for (i = 0; i < log_length; i += plen) {
105624db4641Seschrock iep = (scsi_ie_log_param_t *)((char *)iep + plen);
105724db4641Seschrock
105824db4641Seschrock /*
105924db4641Seschrock * Even though we validated the length during the initial phase,
106024db4641Seschrock * never trust the device.
106124db4641Seschrock */
106224db4641Seschrock if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE &&
106324db4641Seschrock iep->ie_hdr.lph_length >= LOGPARAM_IE_MIN_LEN) {
106424db4641Seschrock if (nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASC,
106524db4641Seschrock iep->ie_asc) != 0 ||
106624db4641Seschrock nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASCQ,
106724db4641Seschrock iep->ie_ascq) != 0)
106824db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
106924db4641Seschrock
107024db4641Seschrock if (iep->ie_asc != 0)
107124db4641Seschrock sip->si_dsp->ds_faults |=
107224db4641Seschrock DS_FAULT_PREDFAIL;
107324db4641Seschrock break;
107424db4641Seschrock }
107524db4641Seschrock plen = iep->ie_hdr.lph_length +
107624db4641Seschrock sizeof (scsi_log_parameter_header_t);
107724db4641Seschrock }
107824db4641Seschrock
107924db4641Seschrock return (0);
108024db4641Seschrock }
108124db4641Seschrock
108224db4641Seschrock static int
logpage_temp_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)108324db4641Seschrock logpage_temp_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
108424db4641Seschrock int log_length)
108524db4641Seschrock {
108624db4641Seschrock int i, plen = 0;
108724db4641Seschrock uint8_t reftemp, curtemp;
108824db4641Seschrock ushort_t param_code;
108924db4641Seschrock scsi_temp_log_param_t *temp;
109024db4641Seschrock nvlist_t *nvl;
109124db4641Seschrock
109224db4641Seschrock assert(sip->si_dsp->ds_overtemp == NULL);
109324db4641Seschrock if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0)
109424db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
109524db4641Seschrock nvl = sip->si_dsp->ds_overtemp;
109624db4641Seschrock
109724db4641Seschrock reftemp = curtemp = INVALID_TEMPERATURE;
109824db4641Seschrock for (i = 0; i < log_length; i += plen) {
109924db4641Seschrock lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
110024db4641Seschrock param_code = BE_16(lphp->lph_param);
110124db4641Seschrock temp = (scsi_temp_log_param_t *)lphp;
110224db4641Seschrock
110324db4641Seschrock switch (param_code) {
110424db4641Seschrock case LOGPARAM_TEMP_CURTEMP:
110524db4641Seschrock if (lphp->lph_length != LOGPARAM_TEMP_LEN)
110624db4641Seschrock break;
110724db4641Seschrock
110824db4641Seschrock if (nvlist_add_uint8(nvl,
110924db4641Seschrock FM_EREPORT_PAYLOAD_SCSI_CURTEMP,
111024db4641Seschrock temp->t_temp) != 0)
111124db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
111224db4641Seschrock curtemp = temp->t_temp;
111324db4641Seschrock break;
111424db4641Seschrock
111524db4641Seschrock case LOGPARAM_TEMP_REFTEMP:
111624db4641Seschrock if (lphp->lph_length != LOGPARAM_TEMP_LEN)
111724db4641Seschrock break;
111824db4641Seschrock
111924db4641Seschrock if (nvlist_add_uint8(nvl,
112024db4641Seschrock FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP,
112124db4641Seschrock temp->t_temp) != 0)
112224db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
112324db4641Seschrock reftemp = temp->t_temp;
112424db4641Seschrock break;
112524db4641Seschrock }
112624db4641Seschrock
112724db4641Seschrock plen = lphp->lph_length +
112824db4641Seschrock sizeof (scsi_log_parameter_header_t);
112924db4641Seschrock }
113024db4641Seschrock
113124db4641Seschrock if (reftemp != INVALID_TEMPERATURE && curtemp != INVALID_TEMPERATURE &&
113224db4641Seschrock curtemp > reftemp)
113324db4641Seschrock sip->si_dsp->ds_faults |= DS_FAULT_OVERTEMP;
113424db4641Seschrock
113524db4641Seschrock return (0);
113624db4641Seschrock }
113724db4641Seschrock
113824db4641Seschrock static int
logpage_selftest_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)113924db4641Seschrock logpage_selftest_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
114024db4641Seschrock int log_length)
114124db4641Seschrock {
114224db4641Seschrock int i, plen = 0;
114324db4641Seschrock int entries = 0;
114424db4641Seschrock ushort_t param_code;
114524db4641Seschrock scsi_selftest_log_param_t *stp;
114624db4641Seschrock nvlist_t *nvl;
114724db4641Seschrock
114824db4641Seschrock assert(sip->si_dsp->ds_testfail == NULL);
114924db4641Seschrock if (nvlist_alloc(&sip->si_dsp->ds_testfail, NV_UNIQUE_NAME, 0) != 0)
115024db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
115124db4641Seschrock nvl = sip->si_dsp->ds_testfail;
115224db4641Seschrock
115324db4641Seschrock for (i = 0; i < log_length; i += plen, entries++) {
115424db4641Seschrock lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
115524db4641Seschrock param_code = BE_16(lphp->lph_param);
115624db4641Seschrock stp = (scsi_selftest_log_param_t *)lphp;
115724db4641Seschrock
115824db4641Seschrock if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE &&
115924db4641Seschrock param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE &&
116024db4641Seschrock lphp->lph_length >= LOGPAGE_SELFTEST_PARAM_LEN) {
116124db4641Seschrock /*
116224db4641Seschrock * We always log the last result, or the result of the
116324db4641Seschrock * last completed test.
116424db4641Seschrock */
116524db4641Seschrock if ((param_code == 1 ||
116624db4641Seschrock SELFTEST_COMPLETE(stp->st_results))) {
116724db4641Seschrock if (nvlist_add_uint8(nvl,
116824db4641Seschrock FM_EREPORT_PAYLOAD_SCSI_RESULTCODE,
116924db4641Seschrock stp->st_results) != 0 ||
117024db4641Seschrock nvlist_add_uint16(nvl,
117124db4641Seschrock FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP,
117224db4641Seschrock BE_16(stp->st_timestamp)) != 0 ||
117324db4641Seschrock nvlist_add_uint8(nvl,
117424db4641Seschrock FM_EREPORT_PAYLOAD_SCSI_SEGMENT,
117524db4641Seschrock stp->st_number) != 0 ||
117624db4641Seschrock nvlist_add_uint64(nvl,
117724db4641Seschrock FM_EREPORT_PAYLOAD_SCSI_ADDRESS,
117824db4641Seschrock BE_64(stp->st_lba)) != 0)
117924db4641Seschrock return (scsi_set_errno(sip,
118024db4641Seschrock EDS_NOMEM));
118124db4641Seschrock
118224db4641Seschrock if (SELFTEST_COMPLETE(stp->st_results)) {
118324db4641Seschrock if (stp->st_results != SELFTEST_OK)
118424db4641Seschrock sip->si_dsp->ds_faults |=
118524db4641Seschrock DS_FAULT_TESTFAIL;
118624db4641Seschrock return (0);
118724db4641Seschrock }
118824db4641Seschrock }
118924db4641Seschrock }
119024db4641Seschrock
119124db4641Seschrock plen = lphp->lph_length +
119224db4641Seschrock sizeof (scsi_log_parameter_header_t);
119324db4641Seschrock }
119424db4641Seschrock
119524db4641Seschrock return (0);
119624db4641Seschrock }
119724db4641Seschrock
11980244979bSAlek Pinchuk /*
11990244979bSAlek Pinchuk * Analyze the contents of the Solid State Media (SSM) log page's
12000244979bSAlek Pinchuk * "Percentage Used Endurance Indicator" log parameter.
12010244979bSAlek Pinchuk * We generate a fault if the percentage used is equal to or over
12020244979bSAlek Pinchuk * PRCNT_USED_FAULT_THRSH
12030244979bSAlek Pinchuk */
12040244979bSAlek Pinchuk static int
logpage_ssm_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)12050244979bSAlek Pinchuk logpage_ssm_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
12060244979bSAlek Pinchuk int log_length)
12070244979bSAlek Pinchuk {
12080244979bSAlek Pinchuk uint16_t param_code;
12090244979bSAlek Pinchuk scsi_ssm_log_param_t *ssm;
12100244979bSAlek Pinchuk nvlist_t *nvl;
12110244979bSAlek Pinchuk int i, plen = 0;
12120244979bSAlek Pinchuk
1213*a37235a3SAlek Pinchuk assert(sip->si_dsp->ds_ssmwearout == NULL);
1214*a37235a3SAlek Pinchuk if (nvlist_alloc(&sip->si_dsp->ds_ssmwearout, NV_UNIQUE_NAME, 0) != 0)
12150244979bSAlek Pinchuk return (scsi_set_errno(sip, EDS_NOMEM));
1216*a37235a3SAlek Pinchuk nvl = sip->si_dsp->ds_ssmwearout;
12170244979bSAlek Pinchuk
12180244979bSAlek Pinchuk for (i = 0; i < log_length; i += plen) {
12190244979bSAlek Pinchuk lphp = (scsi_log_parameter_header_t *)((uint8_t *)lphp + plen);
12200244979bSAlek Pinchuk param_code = BE_16(lphp->lph_param);
12210244979bSAlek Pinchuk ssm = (scsi_ssm_log_param_t *)lphp;
12220244979bSAlek Pinchuk
12230244979bSAlek Pinchuk switch (param_code) {
12240244979bSAlek Pinchuk case LOGPARAM_PRCNT_USED:
12250244979bSAlek Pinchuk if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN)
12260244979bSAlek Pinchuk break;
12270244979bSAlek Pinchuk
12280244979bSAlek Pinchuk if ((nvlist_add_uint8(nvl,
12290244979bSAlek Pinchuk FM_EREPORT_PAYLOAD_SCSI_CURSSMWEAROUT,
12300244979bSAlek Pinchuk ssm->ssm_prcnt_used) != 0) ||
12310244979bSAlek Pinchuk (nvlist_add_uint8(nvl,
12320244979bSAlek Pinchuk FM_EREPORT_PAYLOAD_SCSI_THRSHSSMWEAROUT,
12330244979bSAlek Pinchuk PRCNT_USED_FAULT_THRSH) != 0))
12340244979bSAlek Pinchuk return (scsi_set_errno(sip, EDS_NOMEM));
12350244979bSAlek Pinchuk
12360244979bSAlek Pinchuk if (ssm->ssm_prcnt_used >= PRCNT_USED_FAULT_THRSH)
12370244979bSAlek Pinchuk sip->si_dsp->ds_faults |= DS_FAULT_SSMWEAROUT;
12380244979bSAlek Pinchuk
12390244979bSAlek Pinchuk return (0);
12400244979bSAlek Pinchuk }
12410244979bSAlek Pinchuk
12420244979bSAlek Pinchuk plen = lphp->lph_length +
12430244979bSAlek Pinchuk sizeof (scsi_log_parameter_header_t);
12440244979bSAlek Pinchuk }
12450244979bSAlek Pinchuk
12460244979bSAlek Pinchuk /*
12470244979bSAlek Pinchuk * If we got this far we didn't see LOGPARAM_PRCNT_USED
12480244979bSAlek Pinchuk * which is strange since we verified that it's there
12490244979bSAlek Pinchuk */
12500244979bSAlek Pinchuk dprintf("solid state media logpage analyze failed\n");
12510244979bSAlek Pinchuk #if DEBUG
12520244979bSAlek Pinchuk abort();
12530244979bSAlek Pinchuk #endif
12540244979bSAlek Pinchuk return (scsi_set_errno(sip, EDS_NOT_SUPPORTED));
12550244979bSAlek Pinchuk }
12560244979bSAlek Pinchuk
125724db4641Seschrock /*
125824db4641Seschrock * Analyze the IE mode sense page explicitly. This is only needed if the IE log
125924db4641Seschrock * page is not supported.
126024db4641Seschrock */
126124db4641Seschrock static int
analyze_ie_sense(ds_scsi_info_t * sip)126224db4641Seschrock analyze_ie_sense(ds_scsi_info_t *sip)
126324db4641Seschrock {
126424db4641Seschrock uint_t skey, asc, ascq;
126524db4641Seschrock nvlist_t *nvl;
126624db4641Seschrock
126724db4641Seschrock /*
126824db4641Seschrock * Don't bother checking if we weren't able to set our MRIE correctly.
126924db4641Seschrock */
127024db4641Seschrock if (sip->si_iec_current.ie_mrie != IE_REPORT_ON_REQUEST)
127124db4641Seschrock return (0);
127224db4641Seschrock
127324db4641Seschrock if (scsi_request_sense(sip, &skey, &asc, &ascq) != 0) {
127424db4641Seschrock dprintf("failed to request IE page (KEY=0x%x ASC=0x%x "
127524db4641Seschrock "ASCQ=0x%x)\n", skey, asc, ascq);
127624db4641Seschrock return (scsi_set_errno(sip, EDS_IO));
127724db4641Seschrock } else if (skey == KEY_NO_SENSE) {
127824db4641Seschrock assert(sip->si_dsp->ds_predfail == NULL);
127924db4641Seschrock if (nvlist_alloc(&sip->si_dsp->ds_predfail,
128024db4641Seschrock NV_UNIQUE_NAME, 0) != 0)
128124db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
128224db4641Seschrock nvl = sip->si_dsp->ds_predfail;
128324db4641Seschrock
128424db4641Seschrock if (nvlist_add_uint8(nvl,
128524db4641Seschrock FM_EREPORT_PAYLOAD_SCSI_ASC, asc) != 0 ||
128624db4641Seschrock nvlist_add_uint8(nvl,
128724db4641Seschrock FM_EREPORT_PAYLOAD_SCSI_ASCQ, ascq) != 0) {
128824db4641Seschrock nvlist_free(nvl);
128924db4641Seschrock return (scsi_set_errno(sip, EDS_NOMEM));
129024db4641Seschrock }
129124db4641Seschrock
129224db4641Seschrock if (asc != 0)
129324db4641Seschrock sip->si_dsp->ds_faults |= DS_FAULT_PREDFAIL;
129424db4641Seschrock }
129524db4641Seschrock
129624db4641Seschrock return (0);
129724db4641Seschrock }
129824db4641Seschrock
129924db4641Seschrock /*
130024db4641Seschrock * Clean up the scsi-specific information structure.
130124db4641Seschrock */
130224db4641Seschrock static void
ds_scsi_close(void * arg)130324db4641Seschrock ds_scsi_close(void *arg)
130424db4641Seschrock {
130524db4641Seschrock ds_scsi_info_t *sip = arg;
130624db4641Seschrock if (sip->si_sim)
130724db4641Seschrock (void) dlclose(sip->si_sim);
130824db4641Seschrock
130924db4641Seschrock free(sip);
131024db4641Seschrock }
131124db4641Seschrock
131224db4641Seschrock /*
131324db4641Seschrock * Initialize a single disk. Initialization consists of:
131424db4641Seschrock *
131524db4641Seschrock * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE
131624db4641Seschrock * Control page (page 0x1C).
131724db4641Seschrock *
131824db4641Seschrock * 2. If the IE page is available, try to set the following parameters:
131924db4641Seschrock *
132024db4641Seschrock * DEXCPT 0 Enable exceptions
132124db4641Seschrock * MRIE 6 Only report IE information on request
132224db4641Seschrock * EWASC 1 Enable warning reporting
132324db4641Seschrock * REPORT COUNT 1 Only report an IE exception once
132424db4641Seschrock * LOGERR 1 Enable logging of errors
132524db4641Seschrock *
132624db4641Seschrock * The remaining fields are left as-is, preserving the current values. If we
132724db4641Seschrock * cannot set some of these fields, then we do our best. Some drives may
132824db4641Seschrock * have a static configuration which still allows for some monitoring.
132924db4641Seschrock *
133024db4641Seschrock * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a
133124db4641Seschrock * LOG SENSE command.
133224db4641Seschrock *
133324db4641Seschrock * 4. Check to see if the self-test log page (page 0x10) is supported.
133424db4641Seschrock *
133524db4641Seschrock * 5. Check to see if the temperature log page (page 0x0D) is supported, and
133624db4641Seschrock * contains a reference temperature.
133724db4641Seschrock *
133824db4641Seschrock * 6. Clear the GLTSD bit in control mode page 0xA. This will allow the drive
133924db4641Seschrock * to save each of the log pages described above to nonvolatile storage.
134024db4641Seschrock * This is essential if the drive is to remember its failures across
134124db4641Seschrock * loss of power.
134224db4641Seschrock */
134324db4641Seschrock static void *
ds_scsi_open_common(disk_status_t * dsp,ds_scsi_info_t * sip)134424db4641Seschrock ds_scsi_open_common(disk_status_t *dsp, ds_scsi_info_t *sip)
134524db4641Seschrock {
134624db4641Seschrock boolean_t changed;
134724db4641Seschrock
134824db4641Seschrock sip->si_dsp = dsp;
134924db4641Seschrock
135024db4641Seschrock /* Load and validate mode pages */
135124db4641Seschrock if (load_modepages(sip) != 0) {
135224db4641Seschrock ds_scsi_close(sip);
135324db4641Seschrock return (NULL);
135424db4641Seschrock }
135524db4641Seschrock
135624db4641Seschrock /* Load and validate log pages */
135724db4641Seschrock if (load_logpages(sip) != 0) {
135824db4641Seschrock ds_scsi_close(sip);
135924db4641Seschrock return (NULL);
136024db4641Seschrock }
136124db4641Seschrock
136224db4641Seschrock /* Load IE state */
136324db4641Seschrock if (load_ie_modepage(sip) != 0 ||
136424db4641Seschrock scsi_enable_ie(sip, &changed) != 0 ||
136524db4641Seschrock (changed && load_ie_modepage(sip) != 0)) {
136624db4641Seschrock ds_scsi_close(sip);
136724db4641Seschrock return (NULL);
136824db4641Seschrock }
136924db4641Seschrock
137024db4641Seschrock /* Clear the GLTSD bit in the control page */
137124db4641Seschrock if (sip->si_supp_log != 0 && clear_gltsd(sip) != 0) {
137224db4641Seschrock ds_scsi_close(sip);
137324db4641Seschrock return (NULL);
137424db4641Seschrock }
137524db4641Seschrock
137624db4641Seschrock return (sip);
137724db4641Seschrock }
137824db4641Seschrock
137924db4641Seschrock static void *
ds_scsi_open_uscsi(disk_status_t * dsp)138024db4641Seschrock ds_scsi_open_uscsi(disk_status_t *dsp)
138124db4641Seschrock {
138224db4641Seschrock ds_scsi_info_t *sip;
138324db4641Seschrock
138424db4641Seschrock if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
138524db4641Seschrock (void) ds_set_errno(dsp, EDS_NOMEM);
138624db4641Seschrock return (NULL);
138724db4641Seschrock }
138824db4641Seschrock
138924db4641Seschrock return (ds_scsi_open_common(dsp, sip));
139024db4641Seschrock }
139124db4641Seschrock
139224db4641Seschrock static void *
ds_scsi_open_sim(disk_status_t * dsp)139324db4641Seschrock ds_scsi_open_sim(disk_status_t *dsp)
139424db4641Seschrock {
139524db4641Seschrock ds_scsi_info_t *sip;
139624db4641Seschrock
139724db4641Seschrock if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
139824db4641Seschrock (void) ds_set_errno(dsp, EDS_NOMEM);
139924db4641Seschrock return (NULL);
140024db4641Seschrock }
140124db4641Seschrock
140224db4641Seschrock if ((sip->si_sim = dlopen(dsp->ds_path, RTLD_LAZY)) == NULL) {
140324db4641Seschrock (void) ds_set_errno(dsp, EDS_NO_TRANSPORT);
140424db4641Seschrock free(sip);
140524db4641Seschrock return (NULL);
140624db4641Seschrock }
140724db4641Seschrock
140824db4641Seschrock return (ds_scsi_open_common(dsp, sip));
140924db4641Seschrock }
141024db4641Seschrock
141124db4641Seschrock
141224db4641Seschrock /*
141324db4641Seschrock * Scan for any faults. The following steps are performed:
141424db4641Seschrock *
141524db4641Seschrock * 1. If the temperature log page is supported, check the current temperature
141624db4641Seschrock * and threshold. If the current temperature exceeds the threshold, report
141724db4641Seschrock * and overtemp fault.
141824db4641Seschrock *
141924db4641Seschrock * 2. If the selftest log page is supported, check to the last completed self
142024db4641Seschrock * test. If the last completed test resulted in failure, report a selftest
142124db4641Seschrock * fault.
142224db4641Seschrock *
142324db4641Seschrock * 3. If the IE log page is supported, check to see if failure is predicted. If
142424db4641Seschrock * so, indicate a predictive failure fault.
142524db4641Seschrock *
142624db4641Seschrock * 4. If the IE log page is not supported, but the mode page supports report on
142724db4641Seschrock * request mode, then issue a REQUEST SENSE for the mode page. Indicate a
142824db4641Seschrock * predictive failure fault if necessary.
142924db4641Seschrock */
143024db4641Seschrock static int
ds_scsi_scan(void * arg)143124db4641Seschrock ds_scsi_scan(void *arg)
143224db4641Seschrock {
143324db4641Seschrock ds_scsi_info_t *sip = arg;
143424db4641Seschrock int i;
143524db4641Seschrock
143624db4641Seschrock for (i = 0; i < NLOG_VALIDATION; i++) {
143724db4641Seschrock if ((sip->si_supp_log & log_validation[i].ve_supported) == 0)
143824db4641Seschrock continue;
143924db4641Seschrock
144024db4641Seschrock if (analyze_one_logpage(sip, &log_validation[i]) != 0)
144124db4641Seschrock return (-1);
144224db4641Seschrock }
144324db4641Seschrock
144424db4641Seschrock if (!(sip->si_supp_log & LOGPAGE_SUPP_IE) &&
144524db4641Seschrock (sip->si_supp_mode & MODEPAGE_SUPP_IEC) &&
144624db4641Seschrock analyze_ie_sense(sip) != 0)
144724db4641Seschrock return (-1);
144824db4641Seschrock
144924db4641Seschrock return (0);
145024db4641Seschrock }
145124db4641Seschrock
145224db4641Seschrock ds_transport_t ds_scsi_uscsi_transport = {
145324db4641Seschrock ds_scsi_open_uscsi,
145424db4641Seschrock ds_scsi_close,
145524db4641Seschrock ds_scsi_scan
145624db4641Seschrock };
145724db4641Seschrock
145824db4641Seschrock ds_transport_t ds_scsi_sim_transport = {
145924db4641Seschrock ds_scsi_open_sim,
146024db4641Seschrock ds_scsi_close,
146124db4641Seschrock ds_scsi_scan
146224db4641Seschrock };
1463