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