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 ds_dprintf(
465 "command returned invalid data length (%d)\n",
466 datalength);
467 else
468 ds_dprintf("failed to load modepages (KEY=0x%x "
469 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
470
471 result = scsi_set_errno(sip, EDS_NO_TRANSPORT);
472 }
473
474 free(allpages);
475 return (result);
476 }
477
478 /*
479 * Verify a single logpage. This will do some generic validation and then call
480 * the logpage-specific function for further verification.
481 */
482 static int
verify_logpage(ds_scsi_info_t * sip,logpage_validation_entry_t * lp)483 verify_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *lp)
484 {
485 scsi_log_header_t *lhp;
486 struct scsi_extended_sense sense;
487 int buflen;
488 int log_length;
489 int result = 0;
490 uint_t kp, asc, ascq;
491 nvlist_t *nvl;
492
493 buflen = MAX_BUFLEN(scsi_log_header_t);
494 if ((lhp = calloc(buflen, 1)) == NULL)
495 return (scsi_set_errno(sip, EDS_NOMEM));
496 bzero(&sense, sizeof (struct scsi_extended_sense));
497
498 nvl = NULL;
499 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
500 nvlist_add_nvlist(sip->si_state_logpage, lp->ve_desc, nvl) != 0) {
501 nvlist_free(nvl);
502 free(lhp);
503 return (scsi_set_errno(sip, EDS_NOMEM));
504 }
505 nvlist_free(nvl);
506 result = nvlist_lookup_nvlist(sip->si_state_logpage, lp->ve_desc, &nvl);
507 assert(result == 0);
508
509 result = scsi_log_sense(sip, lp->ve_code,
510 PC_CUMULATIVE, (caddr_t)lhp, buflen, &kp, &asc, &ascq);
511
512 if (result == 0) {
513 log_length = BE_16(lhp->lh_length);
514 if (nvlist_add_uint16(nvl, "length", log_length) != 0) {
515 free(lhp);
516 return (scsi_set_errno(sip, EDS_NOMEM));
517 }
518
519 if (lp->ve_validate(sip, (scsi_log_parameter_header_t *)
520 (((char *)lhp) + sizeof (scsi_log_header_t)),
521 log_length, nvl) != 0) {
522 free(lhp);
523 return (-1);
524 }
525 } else {
526 ds_dprintf("failed to load %s log page (KEY=0x%x "
527 "ASC=0x%x ASCQ=0x%x)\n", lp->ve_desc, kp, asc, ascq);
528 }
529
530 free(lhp);
531 return (0);
532 }
533
534 /*
535 * Load log pages and determine which pages are supported.
536 */
537 static int
load_logpages(ds_scsi_info_t * sip)538 load_logpages(ds_scsi_info_t *sip)
539 {
540 int buflen;
541 scsi_supported_log_pages_t *sp;
542 struct scsi_extended_sense sense;
543 int result;
544 uint_t sk, asc, ascq;
545 int i, j;
546 nvlist_t *nvl;
547
548 buflen = MAX_BUFLEN(scsi_log_header_t);
549 if ((sp = calloc(buflen, 1)) == NULL)
550 return (scsi_set_errno(sip, EDS_NOMEM));
551
552 bzero(&sense, sizeof (struct scsi_extended_sense));
553
554 if ((result = scsi_log_sense(sip, LOGPAGE_SUPP_LIST,
555 PC_CUMULATIVE, (caddr_t)sp, buflen, &sk, &asc, &ascq)) == 0) {
556 int pagecount = BE_16(sp->slp_hdr.lh_length);
557
558 for (i = 0; i < pagecount; i++) {
559 for (j = 0; j < NLOG_VALIDATION; j++) {
560 if (log_validation[j].ve_code ==
561 sp->slp_pages[i])
562 sip->si_supp_log |=
563 log_validation[j].ve_supported;
564 }
565 }
566 }
567
568 free(sp);
569 if (result == 0) {
570 nvl = NULL;
571 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
572 nvlist_add_nvlist(sip->si_dsp->ds_state, "logpages",
573 nvl) != 0) {
574 nvlist_free(nvl);
575 return (scsi_set_errno(sip, EDS_NOMEM));
576 }
577
578 nvlist_free(nvl);
579 result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
580 "logpages", &sip->si_state_logpage);
581 assert(result == 0);
582
583 /*
584 * Validate the logpage contents.
585 */
586 for (i = 0; i < NLOG_VALIDATION; i++) {
587 if ((sip->si_supp_log &
588 log_validation[i].ve_supported) == 0)
589 continue;
590
591 /*
592 * verify_logpage will clear the supported bit if
593 * verification fails.
594 */
595 if (verify_logpage(sip, &log_validation[i]) != 0)
596 return (-1);
597 }
598
599 } else {
600 ds_dprintf("failed to get log pages "
601 "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk, asc, ascq);
602 }
603
604 /*
605 * We always return 0 here, even if the required log pages aren't
606 * supported.
607 */
608 return (0);
609 }
610
611 /*
612 * Verify that the IE log page is sane. This log page is potentially chock-full
613 * of vendor specific information that we do not know how to access. All we can
614 * do is check for the generic predictive failure bit. If this log page is not
615 * well-formed, then bail out.
616 */
617 static int
logpage_ie_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)618 logpage_ie_verify(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
619 int log_length, nvlist_t *nvl)
620 {
621 int i, plen = 0;
622 boolean_t seen = B_FALSE;
623 scsi_ie_log_param_t *iep =
624 (scsi_ie_log_param_t *)lphp;
625
626 for (i = 0; i < log_length; i += plen) {
627 iep = (scsi_ie_log_param_t *)((char *)iep + plen);
628
629 if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE) {
630 if (nvlist_add_boolean_value(nvl, "general",
631 B_TRUE) != 0)
632 return (scsi_set_errno(sip, EDS_NOMEM));
633
634 if (lphp->lph_length < LOGPARAM_IE_MIN_LEN) {
635 if (nvlist_add_uint8(nvl,
636 "invalid-length", lphp->lph_length) != 0)
637 return (scsi_set_errno(sip, EDS_NOMEM));
638 } else {
639 seen = B_TRUE;
640 }
641 break;
642 }
643
644 plen = iep->ie_hdr.lph_length +
645 sizeof (scsi_log_parameter_header_t);
646 }
647
648 if (!seen) {
649 sip->si_supp_log &= ~LOGPAGE_SUPP_IE;
650 ds_dprintf("IE logpage validation failed\n");
651 }
652
653 return (0);
654 }
655
656 /*
657 * Verify the contents of the temperature log page. The temperature log page
658 * contains two log parameters: the current temperature, and (optionally) the
659 * reference temperature. For the verification phase, we check that the two
660 * parameters we care about are well-formed. If there is no reference
661 * temperature, then we cannot use the page for monitoring purposes.
662 */
663 static int
logpage_temp_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)664 logpage_temp_verify(ds_scsi_info_t *sip,
665 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
666 {
667 int i, plen = 0;
668 boolean_t has_reftemp = B_FALSE;
669 boolean_t bad_length = B_FALSE;
670 ushort_t param_code;
671
672 for (i = 0; i < log_length; i += plen) {
673 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
674 param_code = BE_16(lphp->lph_param);
675
676 switch (param_code) {
677 case LOGPARAM_TEMP_CURTEMP:
678 if (nvlist_add_boolean_value(nvl, "current-temperature",
679 B_TRUE) != 0)
680 return (scsi_set_errno(sip, EDS_NOMEM));
681 if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
682 if (nvlist_add_uint8(nvl,
683 "invalid-length", lphp->lph_length) != 0)
684 return (scsi_set_errno(sip, EDS_NOMEM));
685 bad_length = B_TRUE;
686 }
687 break;
688
689 case LOGPARAM_TEMP_REFTEMP:
690 if (nvlist_add_boolean_value(nvl,
691 "reference-temperature", B_TRUE) != 0)
692 return (scsi_set_errno(sip, EDS_NOMEM));
693 if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
694 if (nvlist_add_uint8(nvl,
695 "invalid-length", lphp->lph_length) != 0)
696 return (scsi_set_errno(sip, EDS_NOMEM));
697 bad_length = B_TRUE;
698 }
699 has_reftemp = B_TRUE;
700 break;
701 }
702
703 plen = lphp->lph_length +
704 sizeof (scsi_log_parameter_header_t);
705 }
706
707 if (bad_length || !has_reftemp) {
708 sip->si_supp_log &= ~LOGPAGE_SUPP_TEMP;
709 ds_dprintf("temperature logpage validation failed\n");
710 }
711
712 return (0);
713 }
714
715 /*
716 * Verify the contents of the self test log page. The log supports a maximum of
717 * 20 entries, where each entry's parameter code is its index in the log. We
718 * check that the parameter codes fall within this range, and that the size of
719 * each page is what we expect. It's perfectly acceptable for there to be no
720 * entries in this log, so we must also be sure to validate the contents as part
721 * of the analysis phase.
722 */
723 static int
logpage_selftest_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)724 logpage_selftest_verify(ds_scsi_info_t *sip,
725 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
726 {
727 int i, plen = 0;
728 boolean_t bad = B_FALSE;
729 int entries = 0;
730 ushort_t param_code;
731
732 for (i = 0; i < log_length; i += plen, entries++) {
733 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
734 param_code = BE_16(lphp->lph_param);
735
736 if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE ||
737 param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) {
738 if (nvlist_add_uint16(nvl, "invalid-param-code",
739 param_code) != 0)
740 return (scsi_set_errno(sip, EDS_NOMEM));
741 bad = B_TRUE;
742 break;
743 }
744
745 if (lphp->lph_length != LOGPAGE_SELFTEST_PARAM_LEN) {
746 if (nvlist_add_uint8(nvl, "invalid-length",
747 lphp->lph_length) != 0)
748 return (scsi_set_errno(sip, EDS_NOMEM));
749 bad = B_TRUE;
750 break;
751
752 }
753
754 plen = lphp->lph_length +
755 sizeof (scsi_log_parameter_header_t);
756 }
757
758 if (bad) {
759 sip->si_supp_log &= ~LOGPAGE_SUPP_SELFTEST;
760 ds_dprintf("selftest logpage validation failed\n");
761 }
762
763 return (0);
764 }
765
766 /*
767 * Verify the contents of the Solid State Media (SSM) log page.
768 * As of SBC3r36 SSM log page contains one log parameter:
769 * "Percentage Used Endurance Indicator" which is mandatory.
770 * For the verification phase, we sanity check this parameter
771 * by making sure it's present and it's length is set to 0x04.
772 */
773 static int
logpage_ssm_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)774 logpage_ssm_verify(ds_scsi_info_t *sip,
775 scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
776 {
777 ushort_t param_code;
778 int i, plen = 0;
779
780 for (i = 0; i < log_length; i += plen) {
781 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
782 param_code = BE_16(lphp->lph_param);
783
784 switch (param_code) {
785 case LOGPARAM_PRCNT_USED:
786 if (nvlist_add_boolean_value(nvl,
787 FM_EREPORT_SCSI_SSMWEAROUT, B_TRUE) != 0)
788 return (scsi_set_errno(sip, EDS_NOMEM));
789 if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN) {
790 if (nvlist_add_uint8(nvl,
791 "invalid-length", lphp->lph_length) != 0)
792 return (scsi_set_errno(sip, EDS_NOMEM));
793
794 ds_dprintf(
795 "solid state media logpage bad len\n");
796 break;
797 }
798
799 /* verification succeded */
800 return (0);
801 }
802
803 plen = lphp->lph_length +
804 sizeof (scsi_log_parameter_header_t);
805 }
806
807 /* verification failed */
808 sip->si_supp_log &= ~LOGPAGE_SUPP_SSM;
809 return (0);
810 }
811
812 /*
813 * Load the current IE mode pages
814 */
815 static int
load_ie_modepage(ds_scsi_info_t * sip)816 load_ie_modepage(ds_scsi_info_t *sip)
817 {
818 struct scsi_ms_hdrs junk_hdrs;
819 int result;
820 uint_t skey, asc, ascq;
821
822 if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
823 return (0);
824
825 bzero(&sip->si_iec_current, sizeof (sip->si_iec_current));
826 bzero(&sip->si_iec_changeable, sizeof (sip->si_iec_changeable));
827
828 if ((result = scsi_mode_sense(sip,
829 MODEPAGE_INFO_EXCPT, PC_CURRENT, &sip->si_iec_current,
830 MODEPAGE_INFO_EXCPT_LEN, &sip->si_hdrs, &skey, &asc,
831 &ascq)) == 0) {
832 result = scsi_mode_sense(sip,
833 MODEPAGE_INFO_EXCPT, PC_CHANGEABLE,
834 &sip->si_iec_changeable,
835 MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, &skey, &asc, &ascq);
836 }
837
838 if (result != 0) {
839 ds_dprintf("failed to get IEC modepage (KEY=0x%x "
840 "ASC=0x%x ASCQ=0x%x)", skey, asc, ascq);
841 sip->si_supp_mode &= ~MODEPAGE_SUPP_IEC;
842 } else {
843 if (nvlist_add_boolean_value(sip->si_state_iec,
844 "dexcpt", sip->si_iec_current.ie_dexcpt) != 0 ||
845 nvlist_add_boolean_value(sip->si_state_iec,
846 "logerr", sip->si_iec_current.ie_logerr) != 0 ||
847 nvlist_add_uint8(sip->si_state_iec,
848 "mrie", sip->si_iec_current.ie_mrie) != 0 ||
849 nvlist_add_boolean_value(sip->si_state_iec,
850 "test", sip->si_iec_current.ie_test) != 0 ||
851 nvlist_add_boolean_value(sip->si_state_iec,
852 "ewasc", sip->si_iec_current.ie_ewasc) != 0 ||
853 nvlist_add_boolean_value(sip->si_state_iec,
854 "perf", sip->si_iec_current.ie_perf) != 0 ||
855 nvlist_add_boolean_value(sip->si_state_iec,
856 "ebf", sip->si_iec_current.ie_ebf) != 0 ||
857 nvlist_add_uint32(sip->si_state_iec,
858 "interval-timer",
859 BE_32(sip->si_iec_current.ie_interval_timer)) != 0 ||
860 nvlist_add_uint32(sip->si_state_iec,
861 "report-count",
862 BE_32(sip->si_iec_current.ie_report_count)) != 0)
863 return (scsi_set_errno(sip, EDS_NOMEM));
864 }
865
866 return (0);
867 }
868
869 /*
870 * Enable IE reporting. We prefer the following settings:
871 *
872 * (1) DEXCPT = 0
873 * (3) MRIE = 6 (IE_REPORT_ON_REQUEST)
874 * (4) EWASC = 1
875 * (6) REPORT COUNT = 0x00000001
876 * (7) LOGERR = 1
877 *
878 * However, not all drives support changing these values, and the current state
879 * may be useful enough as-is. For example, some drives support IE logging, but
880 * don't support changing the MRIE. In this case, we can still use the
881 * information provided by the log page.
882 */
883 static int
scsi_enable_ie(ds_scsi_info_t * sip,boolean_t * changed)884 scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed)
885 {
886 scsi_ie_page_t new_iec_page;
887 scsi_ms_hdrs_t hdrs;
888 uint_t skey, asc, ascq;
889
890 if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
891 return (0);
892
893 bzero(&new_iec_page, sizeof (new_iec_page));
894 bzero(&hdrs, sizeof (hdrs));
895
896 (void) memcpy(&new_iec_page, &sip->si_iec_current,
897 sizeof (new_iec_page));
898
899 if (IEC_IE_CHANGEABLE(sip->si_iec_changeable))
900 new_iec_page.ie_dexcpt = 0;
901
902 if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable))
903 new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST;
904
905 /*
906 * We only want to enable warning reporting if we are able to change the
907 * mrie to report on request. Otherwise, we risk unnecessarily
908 * interrupting normal SCSI commands with a CHECK CONDITION code.
909 */
910 if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) {
911 if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST)
912 new_iec_page.ie_ewasc = 1;
913 else
914 new_iec_page.ie_ewasc = 0;
915 }
916
917 if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable))
918 new_iec_page.ie_report_count = BE_32(1);
919
920 if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable))
921 new_iec_page.ie_logerr = 1;
922
923 /*
924 * Now compare the new mode page with the existing one.
925 * if there's no difference, there's no need for a mode select
926 */
927 if (memcmp(&new_iec_page, &sip->si_iec_current,
928 MODEPAGE_INFO_EXCPT_LEN) == 0) {
929 *changed = B_FALSE;
930 } else {
931 (void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs));
932
933 if (scsi_mode_select(sip,
934 MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page,
935 MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) {
936 *changed = B_TRUE;
937 } else {
938 ds_dprintf("failed to enable IE (KEY=0x%x "
939 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
940 *changed = B_FALSE;
941 }
942 }
943
944 if (nvlist_add_boolean_value(sip->si_state_iec, "changed",
945 *changed) != 0)
946 return (scsi_set_errno(sip, EDS_NOMEM));
947
948 return (0);
949 }
950
951 /*
952 * Clear the GLTSD bit, indicating log pages should be saved to non-volatile
953 * storage.
954 */
955 static int
clear_gltsd(ds_scsi_info_t * sip)956 clear_gltsd(ds_scsi_info_t *sip)
957 {
958 scsi_ms_hdrs_t hdrs, junk_hdrs;
959 struct mode_control_scsi3 control_pg_cur, control_pg_chg;
960 int result;
961 uint_t skey, asc, ascq;
962
963 bzero(&hdrs, sizeof (hdrs));
964 bzero(&control_pg_cur, sizeof (control_pg_cur));
965 bzero(&control_pg_chg, sizeof (control_pg_chg));
966
967 result = scsi_mode_sense(sip,
968 MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur,
969 MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
970
971 if (result != 0) {
972 ds_dprintf("failed to read Control mode page (KEY=0x%x "
973 "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
974 } else if (control_pg_cur.mode_page.length !=
975 PAGELENGTH_MODE_CONTROL_SCSI3) {
976 ds_dprintf("SCSI-3 control mode page not supported\n");
977 } else if ((result = scsi_mode_sense(sip,
978 MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg,
979 MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq))
980 != 0) {
981 ds_dprintf("failed to read changeable Control mode page "
982 "(KEY=0x%x ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
983 } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) {
984 ds_dprintf("gltsd is set and not changeable\n");
985 if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
986 "gltsd", control_pg_cur.gltsd) != 0)
987 return (scsi_set_errno(sip, EDS_NOMEM));
988 } else if (control_pg_cur.gltsd) {
989 control_pg_cur.gltsd = 0;
990 result = scsi_mode_select(sip,
991 MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur,
992 MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
993 if (result != 0)
994 ds_dprintf("failed to enable GLTSD (KEY=0x%x "
995 "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq);
996 if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
997 "gltsd", control_pg_cur.gltsd) != 0)
998 return (scsi_set_errno(sip, EDS_NOMEM));
999 }
1000
1001 return (0);
1002 }
1003
1004 /*
1005 * Fetch the contents of the logpage, and then call the logpage-specific
1006 * analysis function. The analysis function is responsible for detecting any
1007 * faults and filling in the details.
1008 */
1009 static int
analyze_one_logpage(ds_scsi_info_t * sip,logpage_validation_entry_t * entry)1010 analyze_one_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *entry)
1011 {
1012 scsi_log_header_t *lhp;
1013 scsi_log_parameter_header_t *lphp;
1014 int buflen;
1015 int log_length;
1016 uint_t skey, asc, ascq;
1017 int result;
1018
1019 buflen = MAX_BUFLEN(scsi_log_header_t);
1020 if ((lhp = calloc(buflen, 1)) == NULL)
1021 return (scsi_set_errno(sip, EDS_NOMEM));
1022
1023 result = scsi_log_sense(sip, entry->ve_code,
1024 PC_CUMULATIVE, (caddr_t)lhp, buflen, &skey, &asc, &ascq);
1025
1026 if (result == 0) {
1027 log_length = BE_16(lhp->lh_length);
1028 lphp = (scsi_log_parameter_header_t *)(((uchar_t *)lhp) +
1029 sizeof (scsi_log_header_t));
1030
1031 result = entry->ve_analyze(sip, lphp, log_length);
1032 } else {
1033 result = scsi_set_errno(sip, EDS_IO);
1034 }
1035
1036 free(lhp);
1037 return (result);
1038 }
1039
1040 /*
1041 * Analyze the IE logpage. If we find an IE log record with a non-zero 'asc',
1042 * then we have a fault.
1043 */
1044 static int
logpage_ie_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)1045 logpage_ie_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
1046 int log_length)
1047 {
1048 int i, plen = 0;
1049 scsi_ie_log_param_t *iep = (scsi_ie_log_param_t *)lphp;
1050 nvlist_t *nvl;
1051
1052 assert(sip->si_dsp->ds_predfail == NULL);
1053 if (nvlist_alloc(&sip->si_dsp->ds_predfail, NV_UNIQUE_NAME, 0) != 0)
1054 return (scsi_set_errno(sip, EDS_NOMEM));
1055 nvl = sip->si_dsp->ds_predfail;
1056
1057 for (i = 0; i < log_length; i += plen) {
1058 iep = (scsi_ie_log_param_t *)((char *)iep + plen);
1059
1060 /*
1061 * Even though we validated the length during the initial phase,
1062 * never trust the device.
1063 */
1064 if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE &&
1065 iep->ie_hdr.lph_length >= LOGPARAM_IE_MIN_LEN) {
1066 if (nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASC,
1067 iep->ie_asc) != 0 ||
1068 nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASCQ,
1069 iep->ie_ascq) != 0)
1070 return (scsi_set_errno(sip, EDS_NOMEM));
1071
1072 if (iep->ie_asc != 0)
1073 sip->si_dsp->ds_faults |=
1074 DS_FAULT_PREDFAIL;
1075 break;
1076 }
1077 plen = iep->ie_hdr.lph_length +
1078 sizeof (scsi_log_parameter_header_t);
1079 }
1080
1081 return (0);
1082 }
1083
1084 static int
logpage_temp_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)1085 logpage_temp_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
1086 int log_length)
1087 {
1088 int i, plen = 0;
1089 uint8_t reftemp, curtemp;
1090 ushort_t param_code;
1091 scsi_temp_log_param_t *temp;
1092 nvlist_t *nvl;
1093
1094 assert(sip->si_dsp->ds_overtemp == NULL);
1095 if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0)
1096 return (scsi_set_errno(sip, EDS_NOMEM));
1097 nvl = sip->si_dsp->ds_overtemp;
1098
1099 reftemp = curtemp = INVALID_TEMPERATURE;
1100 for (i = 0; i < log_length; i += plen) {
1101 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
1102 param_code = BE_16(lphp->lph_param);
1103 temp = (scsi_temp_log_param_t *)lphp;
1104
1105 switch (param_code) {
1106 case LOGPARAM_TEMP_CURTEMP:
1107 if (lphp->lph_length != LOGPARAM_TEMP_LEN)
1108 break;
1109
1110 if (nvlist_add_uint8(nvl,
1111 FM_EREPORT_PAYLOAD_SCSI_CURTEMP,
1112 temp->t_temp) != 0)
1113 return (scsi_set_errno(sip, EDS_NOMEM));
1114 curtemp = temp->t_temp;
1115 break;
1116
1117 case LOGPARAM_TEMP_REFTEMP:
1118 if (lphp->lph_length != LOGPARAM_TEMP_LEN)
1119 break;
1120
1121 if (nvlist_add_uint8(nvl,
1122 FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP,
1123 temp->t_temp) != 0)
1124 return (scsi_set_errno(sip, EDS_NOMEM));
1125 reftemp = temp->t_temp;
1126 break;
1127 }
1128
1129 plen = lphp->lph_length +
1130 sizeof (scsi_log_parameter_header_t);
1131 }
1132
1133 if (reftemp != INVALID_TEMPERATURE && curtemp != INVALID_TEMPERATURE &&
1134 curtemp > reftemp)
1135 sip->si_dsp->ds_faults |= DS_FAULT_OVERTEMP;
1136
1137 return (0);
1138 }
1139
1140 static int
logpage_selftest_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)1141 logpage_selftest_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
1142 int log_length)
1143 {
1144 int i, plen = 0;
1145 int entries = 0;
1146 ushort_t param_code;
1147 scsi_selftest_log_param_t *stp;
1148 nvlist_t *nvl;
1149
1150 assert(sip->si_dsp->ds_testfail == NULL);
1151 if (nvlist_alloc(&sip->si_dsp->ds_testfail, NV_UNIQUE_NAME, 0) != 0)
1152 return (scsi_set_errno(sip, EDS_NOMEM));
1153 nvl = sip->si_dsp->ds_testfail;
1154
1155 for (i = 0; i < log_length; i += plen, entries++) {
1156 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
1157 param_code = BE_16(lphp->lph_param);
1158 stp = (scsi_selftest_log_param_t *)lphp;
1159
1160 if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE &&
1161 param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE &&
1162 lphp->lph_length >= LOGPAGE_SELFTEST_PARAM_LEN) {
1163 /*
1164 * We always log the last result, or the result of the
1165 * last completed test.
1166 */
1167 if ((param_code == 1 ||
1168 SELFTEST_COMPLETE(stp->st_results))) {
1169 if (nvlist_add_uint8(nvl,
1170 FM_EREPORT_PAYLOAD_SCSI_RESULTCODE,
1171 stp->st_results) != 0 ||
1172 nvlist_add_uint16(nvl,
1173 FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP,
1174 BE_16(stp->st_timestamp)) != 0 ||
1175 nvlist_add_uint8(nvl,
1176 FM_EREPORT_PAYLOAD_SCSI_SEGMENT,
1177 stp->st_number) != 0 ||
1178 nvlist_add_uint64(nvl,
1179 FM_EREPORT_PAYLOAD_SCSI_ADDRESS,
1180 BE_64(stp->st_lba)) != 0)
1181 return (scsi_set_errno(sip,
1182 EDS_NOMEM));
1183
1184 if (SELFTEST_COMPLETE(stp->st_results)) {
1185 if (stp->st_results != SELFTEST_OK)
1186 sip->si_dsp->ds_faults |=
1187 DS_FAULT_TESTFAIL;
1188 return (0);
1189 }
1190 }
1191 }
1192
1193 plen = lphp->lph_length +
1194 sizeof (scsi_log_parameter_header_t);
1195 }
1196
1197 return (0);
1198 }
1199
1200 /*
1201 * Analyze the contents of the Solid State Media (SSM) log page's
1202 * "Percentage Used Endurance Indicator" log parameter.
1203 * We generate a fault if the percentage used is equal to or over
1204 * PRCNT_USED_FAULT_THRSH
1205 */
1206 static int
logpage_ssm_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)1207 logpage_ssm_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
1208 int log_length)
1209 {
1210 uint16_t param_code;
1211 scsi_ssm_log_param_t *ssm;
1212 nvlist_t *nvl;
1213 int i, plen = 0;
1214
1215 assert(sip->si_dsp->ds_ssmwearout == NULL);
1216 if (nvlist_alloc(&sip->si_dsp->ds_ssmwearout, NV_UNIQUE_NAME, 0) != 0)
1217 return (scsi_set_errno(sip, EDS_NOMEM));
1218 nvl = sip->si_dsp->ds_ssmwearout;
1219
1220 for (i = 0; i < log_length; i += plen) {
1221 lphp = (scsi_log_parameter_header_t *)((uint8_t *)lphp + plen);
1222 param_code = BE_16(lphp->lph_param);
1223 ssm = (scsi_ssm_log_param_t *)lphp;
1224
1225 switch (param_code) {
1226 case LOGPARAM_PRCNT_USED:
1227 if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN)
1228 break;
1229
1230 if ((nvlist_add_uint8(nvl,
1231 FM_EREPORT_PAYLOAD_SCSI_CURSSMWEAROUT,
1232 ssm->ssm_prcnt_used) != 0) ||
1233 (nvlist_add_uint8(nvl,
1234 FM_EREPORT_PAYLOAD_SCSI_THRSHSSMWEAROUT,
1235 PRCNT_USED_FAULT_THRSH) != 0))
1236 return (scsi_set_errno(sip, EDS_NOMEM));
1237
1238 if (ssm->ssm_prcnt_used >= PRCNT_USED_FAULT_THRSH)
1239 sip->si_dsp->ds_faults |= DS_FAULT_SSMWEAROUT;
1240
1241 return (0);
1242 }
1243
1244 plen = lphp->lph_length +
1245 sizeof (scsi_log_parameter_header_t);
1246 }
1247
1248 /*
1249 * If we got this far we didn't see LOGPARAM_PRCNT_USED
1250 * which is strange since we verified that it's there
1251 */
1252 ds_dprintf("solid state media logpage analyze failed\n");
1253 #if DEBUG
1254 abort();
1255 #endif
1256 return (scsi_set_errno(sip, EDS_NOT_SUPPORTED));
1257 }
1258
1259 /*
1260 * Analyze the IE mode sense page explicitly. This is only needed if the IE log
1261 * page is not supported.
1262 */
1263 static int
analyze_ie_sense(ds_scsi_info_t * sip)1264 analyze_ie_sense(ds_scsi_info_t *sip)
1265 {
1266 uint_t skey, asc, ascq;
1267 nvlist_t *nvl;
1268
1269 /*
1270 * Don't bother checking if we weren't able to set our MRIE correctly.
1271 */
1272 if (sip->si_iec_current.ie_mrie != IE_REPORT_ON_REQUEST)
1273 return (0);
1274
1275 if (scsi_request_sense(sip, &skey, &asc, &ascq) != 0) {
1276 ds_dprintf("failed to request IE page (KEY=0x%x ASC=0x%x "
1277 "ASCQ=0x%x)\n", skey, asc, ascq);
1278 return (scsi_set_errno(sip, EDS_IO));
1279 } else if (skey == KEY_NO_SENSE) {
1280 assert(sip->si_dsp->ds_predfail == NULL);
1281 if (nvlist_alloc(&sip->si_dsp->ds_predfail,
1282 NV_UNIQUE_NAME, 0) != 0)
1283 return (scsi_set_errno(sip, EDS_NOMEM));
1284 nvl = sip->si_dsp->ds_predfail;
1285
1286 if (nvlist_add_uint8(nvl,
1287 FM_EREPORT_PAYLOAD_SCSI_ASC, asc) != 0 ||
1288 nvlist_add_uint8(nvl,
1289 FM_EREPORT_PAYLOAD_SCSI_ASCQ, ascq) != 0) {
1290 nvlist_free(nvl);
1291 return (scsi_set_errno(sip, EDS_NOMEM));
1292 }
1293
1294 if (asc != 0)
1295 sip->si_dsp->ds_faults |= DS_FAULT_PREDFAIL;
1296 }
1297
1298 return (0);
1299 }
1300
1301 /*
1302 * Clean up the scsi-specific information structure.
1303 */
1304 static void
ds_scsi_close(void * arg)1305 ds_scsi_close(void *arg)
1306 {
1307 ds_scsi_info_t *sip = arg;
1308 if (sip->si_sim)
1309 (void) dlclose(sip->si_sim);
1310
1311 free(sip);
1312 }
1313
1314 /*
1315 * Initialize a single disk. Initialization consists of:
1316 *
1317 * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE
1318 * Control page (page 0x1C).
1319 *
1320 * 2. If the IE page is available, try to set the following parameters:
1321 *
1322 * DEXCPT 0 Enable exceptions
1323 * MRIE 6 Only report IE information on request
1324 * EWASC 1 Enable warning reporting
1325 * REPORT COUNT 1 Only report an IE exception once
1326 * LOGERR 1 Enable logging of errors
1327 *
1328 * The remaining fields are left as-is, preserving the current values. If we
1329 * cannot set some of these fields, then we do our best. Some drives may
1330 * have a static configuration which still allows for some monitoring.
1331 *
1332 * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a
1333 * LOG SENSE command.
1334 *
1335 * 4. Check to see if the self-test log page (page 0x10) is supported.
1336 *
1337 * 5. Check to see if the temperature log page (page 0x0D) is supported, and
1338 * contains a reference temperature.
1339 *
1340 * 6. Clear the GLTSD bit in control mode page 0xA. This will allow the drive
1341 * to save each of the log pages described above to nonvolatile storage.
1342 * This is essential if the drive is to remember its failures across
1343 * loss of power.
1344 */
1345 static void *
ds_scsi_open_common(disk_status_t * dsp,ds_scsi_info_t * sip)1346 ds_scsi_open_common(disk_status_t *dsp, ds_scsi_info_t *sip)
1347 {
1348 boolean_t changed;
1349
1350 sip->si_dsp = dsp;
1351
1352 /* Load and validate mode pages */
1353 if (load_modepages(sip) != 0) {
1354 ds_scsi_close(sip);
1355 return (NULL);
1356 }
1357
1358 /* Load and validate log pages */
1359 if (load_logpages(sip) != 0) {
1360 ds_scsi_close(sip);
1361 return (NULL);
1362 }
1363
1364 /* Load IE state */
1365 if (load_ie_modepage(sip) != 0 ||
1366 scsi_enable_ie(sip, &changed) != 0 ||
1367 (changed && load_ie_modepage(sip) != 0)) {
1368 ds_scsi_close(sip);
1369 return (NULL);
1370 }
1371
1372 /* Clear the GLTSD bit in the control page */
1373 if (sip->si_supp_log != 0 && clear_gltsd(sip) != 0) {
1374 ds_scsi_close(sip);
1375 return (NULL);
1376 }
1377
1378 return (sip);
1379 }
1380
1381 static void *
ds_scsi_open_uscsi(disk_status_t * dsp)1382 ds_scsi_open_uscsi(disk_status_t *dsp)
1383 {
1384 ds_scsi_info_t *sip;
1385
1386 if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
1387 (void) ds_set_errno(dsp, EDS_NOMEM);
1388 return (NULL);
1389 }
1390
1391 return (ds_scsi_open_common(dsp, sip));
1392 }
1393
1394 static void *
ds_scsi_open_sim(disk_status_t * dsp)1395 ds_scsi_open_sim(disk_status_t *dsp)
1396 {
1397 ds_scsi_info_t *sip;
1398
1399 if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
1400 (void) ds_set_errno(dsp, EDS_NOMEM);
1401 return (NULL);
1402 }
1403
1404 if ((sip->si_sim = dlopen(dsp->ds_path, RTLD_LAZY)) == NULL) {
1405 (void) ds_set_errno(dsp, EDS_NO_TRANSPORT);
1406 free(sip);
1407 return (NULL);
1408 }
1409
1410 return (ds_scsi_open_common(dsp, sip));
1411 }
1412
1413
1414 /*
1415 * Scan for any faults. The following steps are performed:
1416 *
1417 * 1. If the temperature log page is supported, check the current temperature
1418 * and threshold. If the current temperature exceeds the threshold, report
1419 * and overtemp fault.
1420 *
1421 * 2. If the selftest log page is supported, check to the last completed self
1422 * test. If the last completed test resulted in failure, report a selftest
1423 * fault.
1424 *
1425 * 3. If the IE log page is supported, check to see if failure is predicted. If
1426 * so, indicate a predictive failure fault.
1427 *
1428 * 4. If the IE log page is not supported, but the mode page supports report on
1429 * request mode, then issue a REQUEST SENSE for the mode page. Indicate a
1430 * predictive failure fault if necessary.
1431 */
1432 static int
ds_scsi_scan(void * arg)1433 ds_scsi_scan(void *arg)
1434 {
1435 ds_scsi_info_t *sip = arg;
1436 int i;
1437
1438 for (i = 0; i < NLOG_VALIDATION; i++) {
1439 if ((sip->si_supp_log & log_validation[i].ve_supported) == 0)
1440 continue;
1441
1442 if (analyze_one_logpage(sip, &log_validation[i]) != 0)
1443 return (-1);
1444 }
1445
1446 if (!(sip->si_supp_log & LOGPAGE_SUPP_IE) &&
1447 (sip->si_supp_mode & MODEPAGE_SUPP_IEC) &&
1448 analyze_ie_sense(sip) != 0)
1449 return (-1);
1450
1451 return (0);
1452 }
1453
1454 ds_transport_t ds_scsi_uscsi_transport = {
1455 ds_scsi_open_uscsi,
1456 ds_scsi_close,
1457 ds_scsi_scan
1458 };
1459
1460 ds_transport_t ds_scsi_sim_transport = {
1461 ds_scsi_open_sim,
1462 ds_scsi_close,
1463 ds_scsi_scan
1464 };
1465