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