1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
14 */
15
16 /*
17 * This File has Modules that handle the NOE functionality for
18 * this driver.
19 * It builds and submits the NOE command to the adapter. It also
20 * processes a completed NOE command.
21 * A study of the FirmWare specifications would be neccessary to relate
22 * coding in this module to the hardware functionality.
23 */
24
25 #include "cpqary3.h"
26
27 /*
28 * Local Functions Definitions
29 */
30
31 uint8_t cpqary3_disable_NOE_command(cpqary3_t *);
32
33 /*
34 * Last reason a drive at this position was failed by the
35 * controller firmware (saved in the RIS).
36 */
37
38 #define MAX_KNOWN_FAILURE_REASON 31
39
40 char *ascii_failure_reason[] = {
41 "NONE",
42 "TOO_SMALL_IN_LOAD_CONFIG",
43 "ERROR_ERASING_RIS",
44 "ERROR_SAVING_RIS",
45 "FAIL_DRIVE_COMMAND",
46 "MARK_BAD_FAILED",
47 "MARK_BAD_FAILED_IN_FINISH_REMAP",
48 "TIMEOUT",
49 "AUTOSENSE_FAILED",
50 "MEDIUM_ERROR_1",
51 "MEDIUM_ERROR_2",
52 "NOT_READY_BAD_SENSE",
53 "NOT_READY",
54 "HARDWARE_ERROR",
55 "ABORTED_COMMAND",
56 "WRITE_PROTECTED",
57 "SPIN_UP_FAILURE_IN_RECOVER",
58 "REBUILD_WRITE_ERROR",
59 "TOO_SMALL_IN_HOT_PLUG",
60 "RESET_RECOVERY_ABORT",
61 "REMOVED_IN_HOT_PLUG",
62 "INIT_REQUEST_SENSE_FAILED",
63 "INIT_START_UNIT_FAILED",
64 "GDP_INQUIRY_FAILED",
65 "GDP_NON_DISK_DEVICE",
66 "GDP_READ_CAPACITY_FAILED",
67 "GDP_INVALID_BLOCK_SIZE",
68 "HOTP_REQUEST_SENSE_FAILED",
69 "HOTP_START_UNIT_FAILED",
70 "WRITE_ERROR_AFTER_REMAP",
71 "INIT_RESET_RECOVERY_ABORTED"
72 };
73
74 /*
75 * All Possible Logical Volume Status
76 */
77
78 char *log_vol_status[] = {
79 "OK",
80 "Failed",
81 "Not Configured",
82 "Regenerating",
83 "Needs Rebuild Permission",
84 "Rebuilding",
85 "Wrong Drive Replaced",
86 "Bad Drive Connection",
87 "Box Overheating",
88 "Box Overheated",
89 "Volume Expanding",
90 "Not Yet Available",
91 "Volume Needs to Expand",
92 "Unknown"
93 };
94
95 /*
96 * Function : cpqary3_send_NOE_command
97 * Description : This routine builds and submits the NOE Command
98 * to the Controller.
99 * Called By : cpqary3_attach(), cpqary3_NOE_handler()
100 * Parameters : per-controller, per-command,
101 * Flag to signify first time or otherwise
102 * Calls : cpqary3_alloc_phyctgs_mem(), cpqary3_cmdlist_occupy(),
103 * cpqary3_submit(), cpqary3_add2submitted_cmdq(),
104 * cpqary3_free_phyctgs_mem()
105 * Return Values: SUCCESS / FAILURE
106 * [Shall fail only if memory allocation issues exist]
107 */
108 uint8_t
cpqary3_send_NOE_command(cpqary3_t * ctlr,cpqary3_cmdpvt_t * memp,uint8_t flag)109 cpqary3_send_NOE_command(cpqary3_t *ctlr, cpqary3_cmdpvt_t *memp, uint8_t flag)
110 {
111 uint32_t phys_addr = 0;
112 NoeBuffer *databuf;
113 CommandList_t *cmdlist;
114 cpqary3_phyctg_t *phys_handle;
115 int rv;
116
117 /*
118 * NOTE : DO NOT perform this operation for memp. Shall result in a
119 * failure of submission of the NOE command as it shall be NULL for
120 * the very first time
121 */
122 RETURN_FAILURE_IF_NULL(ctlr);
123
124 /*
125 * Allocate Memory for Return data
126 * if failure, RETURN.
127 * Allocate Memory for CommandList
128 * If error, RETURN.
129 * get the Request Block from the CommandList
130 * Fill in the Request Packet with the corresponding values
131 * Special Information can be filled in the "bno" field of
132 * the request structure.
133 * Here, the "bno" field is filled for Asynchronous Mode.
134 * Submit the Command.
135 * If Failure, WARN and RETURN.
136 */
137 if (CPQARY3_NOE_RESUBMIT == flag) {
138 if ((NULL == memp) || (NULL == memp->cmdlist_memaddr)) {
139 cmn_err(CE_WARN, " CPQary3 : _send_NOE_command : "
140 "Re-Use Not possible; CommandList NULL");
141 return (CPQARY3_FAILURE);
142 }
143
144 bzero(MEM2DRVPVT(memp)->sg, sizeof (NoeBuffer));
145 memp->cmdlist_memaddr->Header.Tag.drvinfo_n_err =
146 CPQARY3_NOECMD_SUCCESS;
147 } else if (CPQARY3_NOE_INIT == flag) {
148 phys_handle =
149 (cpqary3_phyctg_t *)MEM_ZALLOC(sizeof (cpqary3_phyctg_t));
150 if (!phys_handle)
151 return (CPQARY3_FAILURE);
152
153 databuf = (NoeBuffer *)cpqary3_alloc_phyctgs_mem(ctlr,
154 sizeof (NoeBuffer), &phys_addr, phys_handle);
155 if (!databuf) {
156 return (CPQARY3_FAILURE);
157 }
158 bzero(databuf, sizeof (NoeBuffer));
159
160 if (NULL == (memp = cpqary3_cmdlist_occupy(ctlr))) {
161 cpqary3_free_phyctgs_mem(phys_handle,
162 CPQARY3_FREE_PHYCTG_MEM);
163 return (CPQARY3_FAILURE);
164 }
165
166 memp->driverdata = (cpqary3_private_t *)
167 MEM_ZALLOC(sizeof (cpqary3_private_t));
168 if (NULL == memp->driverdata) {
169 cpqary3_free_phyctgs_mem(phys_handle,
170 CPQARY3_FREE_PHYCTG_MEM);
171 cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
172 return (CPQARY3_FAILURE);
173 }
174 memp->driverdata->sg = databuf;
175 memp->driverdata->phyctgp = phys_handle;
176
177 cmdlist = memp->cmdlist_memaddr;
178 cmdlist->Header.SGTotal = 1;
179 cmdlist->Header.SGList = 1;
180 cmdlist->Header.Tag.drvinfo_n_err = CPQARY3_NOECMD_SUCCESS;
181 cmdlist->Header.LUN.PhysDev.Mode = PERIPHERIAL_DEV_ADDR;
182
183 cmdlist->Request.CDBLen = CISS_NOE_CDB_LEN;
184 cmdlist->Request.Timeout = 0;
185 cmdlist->Request.Type.Type = CISS_TYPE_CMD;
186 cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
187 cmdlist->Request.Type.Direction = CISS_XFER_READ;
188 cmdlist->Request.CDB[0] = CISS_NEW_READ;
189 cmdlist->Request.CDB[1] = BMIC_NOTIFY_ON_EVENT;
190 cmdlist->Request.CDB[10] = (NOE_BUFFER_LENGTH >> 8) & 0xff;
191 cmdlist->Request.CDB[11] = NOE_BUFFER_LENGTH & 0xff;
192
193 cmdlist->SG[0].Addr = phys_addr;
194 cmdlist->SG[0].Len = NOE_BUFFER_LENGTH;
195 }
196
197 /* PERF */
198
199 memp->complete = cpqary3_noe_complete;
200
201 mutex_enter(&ctlr->hw_mutex);
202 rv = cpqary3_submit(ctlr, memp->cmdlist_phyaddr);
203 mutex_exit(&ctlr->hw_mutex);
204
205 if (rv != 0)
206 return (CPQARY3_FAILURE);
207
208 /* PERF */
209 return (CPQARY3_SUCCESS);
210 }
211
212 /*
213 * Function : cpqary3_disable_NOE_command
214 * Description : This routine disables the Event Notifier
215 * for the specified Controller.
216 * Called By : cpqary3_cleanup()
217 * Parameters : Per Controller Structure
218 * Calls : cpqary3_cmdlist_occupy(), cpqary3_submit(),
219 * cpqary3_add2submitted_cmdq()
220 * Return Values: SUCCESS / FAILURE
221 * [Shall fail only if Memory Constraints exist]
222 */
223 uint8_t
cpqary3_disable_NOE_command(cpqary3_t * ctlr)224 cpqary3_disable_NOE_command(cpqary3_t *ctlr)
225 {
226 CommandList_t *cmdlist;
227 cpqary3_cmdpvt_t *memp;
228 int rv;
229
230 RETURN_FAILURE_IF_NULL(ctlr);
231
232 /*
233 * Allocate Memory for CommandList
234 * If error, RETURN.
235 * get the Request Block from the CommandList
236 * Fill in the Request Packet with the corresponding values
237 * Submit the Command.
238 * If Failure, WARN and RETURN.
239 */
240
241 if (NULL == (memp = cpqary3_cmdlist_occupy(ctlr))) {
242 cmn_err(CE_WARN, "CPQary3 : _disable_NOE_command : Failed");
243 return (CPQARY3_FAILURE);
244 }
245
246 cmdlist = memp->cmdlist_memaddr;
247 cmdlist->Header.Tag.drvinfo_n_err = CPQARY3_NOECMD_SUCCESS;
248 cmdlist->Header.LUN.PhysDev.Mode = PERIPHERIAL_DEV_ADDR;
249
250 cmdlist->Request.CDBLen = CISS_CANCEL_NOE_CDB_LEN;
251 cmdlist->Request.Timeout = 0;
252 cmdlist->Request.Type.Type = CISS_TYPE_CMD;
253 cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
254 cmdlist->Request.Type.Direction = CISS_XFER_NONE;
255 cmdlist->Request.CDB[0] = ARRAY_WRITE; /* 0x27 */
256 cmdlist->Request.CDB[6] = BMIC_CANCEL_NOTIFY_ON_EVENT;
257
258 /* PERF */
259
260 memp->complete = cpqary3_noe_complete;
261
262 mutex_enter(&ctlr->hw_mutex);
263 rv = cpqary3_submit(ctlr, memp->cmdlist_phyaddr);
264 mutex_exit(&ctlr->hw_mutex);
265
266 if (rv != 0)
267 return (CPQARY3_FAILURE);
268
269 /* PERF */
270 return (CPQARY3_SUCCESS);
271 }
272
273 /*
274 * Function : cpqary3_NOE_handler
275 * Description : This routine handles all those NOEs tabulated at the
276 * begining of this code.
277 * Called By : cpqary3_process_pkt()
278 * Parameters : Pointer to the Command List
279 * Calls : cpqary3_send_NOE_command(),
280 * cpqary3_display_spare_status()
281 * cpqary3_free_phyctgs_mem(), cpqary3_cmdlist_release()
282 * Return Values: None
283 */
284 void
cpqary3_NOE_handler(cpqary3_cmdpvt_t * memp)285 cpqary3_NOE_handler(cpqary3_cmdpvt_t *memp)
286 {
287 uint16_t drive = 0;
288 NoeBuffer *evt;
289 cpqary3_t *ctlr;
290 cpqary3_phyctg_t *phys_handle;
291 uint8_t driveId = 0;
292
293 /*
294 * This should never happen....
295 * If the pointer passed as argument is NULL, Panic the System.
296 */
297 VERIFY(memp != NULL);
298
299 evt = (NoeBuffer *)MEM2DRVPVT(memp)->sg;
300 ctlr = (cpqary3_t *)memp->ctlr;
301 phys_handle = (cpqary3_phyctg_t *)MEM2DRVPVT(memp)->phyctgp;
302
303 /* Don't display more than 79 characters */
304 evt->ascii_message[79] = 0;
305
306
307 switch (evt->event_class_code) {
308 case CLASS_PROTOCOL:
309 /*
310 * the following cases are not handled:
311 * 000 : This is for Synchronous NOE.
312 * CPQary3 follows asynchronous NOE.
313 * 002 : Asynchronous NOE time out.
314 * CPQary3 does not implement time
315 * outs for NOE. It shall always reside in the HBA.
316 */
317
318 cmn_err(CE_NOTE, " %s", ctlr->hba_name);
319 if ((evt->event_subclass_code == SUB_CLASS_NON_EVENT) &&
320 (evt->event_detail_code == DETAIL_DISABLED)) {
321 cmn_err(CE_CONT, " %s", ctlr->hba_name);
322 cmn_err(CE_CONT,
323 "CPQary3 : Event Notifier Disabled \n");
324 MEM_SFREE(memp->driverdata, sizeof (cpqary3_private_t));
325 cpqary3_free_phyctgs_mem(phys_handle,
326 CPQARY3_FREE_PHYCTG_MEM);
327 cpqary3_cmdlist_release(memp, CPQARY3_NO_MUTEX);
328 return;
329 } else if ((evt->event_subclass_code ==
330 SUB_CLASS_PROTOCOL_ERR) &&
331 (evt->event_detail_code == DETAIL_EVENT_Q_OVERFLOW)) {
332 cmn_err(CE_CONT, " %s\n", evt->ascii_message);
333 }
334 cmn_err(CE_CONT, "\n");
335 break;
336
337 case CLASS_HOT_PLUG:
338 if (evt->event_subclass_code == SUB_CLASS_HP_CHANGE) {
339 cmn_err(CE_NOTE, " %s", ctlr->hba_name);
340 cmn_err(CE_CONT, " %s\n", evt->ascii_message);
341
342 /*
343 * Fix for QUIX 1000440284: Display the Physical
344 * Drive Num info only for CISS Controllers
345 */
346
347 if (!(ctlr->bddef->bd_flags & SA_BD_SAS)) {
348 driveId =
349 /* LINTED: alignment */
350 *(uint16_t *)(&evt->event_specific_data[0]);
351 if (driveId & 0x80) {
352 driveId -= 0x80;
353 cmn_err(CE_CONT, " Physical Drive Num "
354 "....... SCSI Port %u, "
355 "Drive Id %u\n",
356 (driveId / 16) + 1,
357 (driveId % 16));
358 } else {
359 cmn_err(CE_CONT, " Physical Drive Num "
360 "....... SCSI Port %u, "
361 "Drive Id %u\n",
362 (driveId / 16) + 1, (driveId % 16));
363 }
364 }
365
366 cmn_err(CE_CONT, " Configured Drive ? ....... %s\n",
367 evt->event_specific_data[2] ? "YES" : "NO");
368 if (evt->event_specific_data[3]) {
369 cmn_err(CE_CONT, " Spare Drive? "
370 "............. %s\n",
371 evt->event_specific_data[3] ? "YES" : "NO");
372 }
373 } else if (evt->event_subclass_code == SUB_CLASS_SB_HP_CHANGE) {
374 if (evt->event_detail_code == DETAIL_PATH_REMOVED) {
375 cmn_err(CE_WARN, " %s", ctlr->hba_name);
376 cmn_err(CE_CONT,
377 " Storage Enclosure cable or %s\n",
378 evt->ascii_message);
379 } else if (evt->event_detail_code ==
380 DETAIL_PATH_REPAIRED) {
381 cmn_err(CE_NOTE, " %s", ctlr->hba_name);
382 cmn_err(CE_CONT,
383 " Storage Enclosure Cable or %s\n",
384 evt->ascii_message);
385 } else {
386 cmn_err(CE_NOTE, " %s", ctlr->hba_name);
387 cmn_err(CE_CONT, " %s\n", evt->ascii_message);
388 }
389 } else {
390 cmn_err(CE_NOTE, " %s", ctlr->hba_name);
391 cmn_err(CE_CONT, " %s\n", evt->ascii_message);
392 }
393
394 cmn_err(CE_CONT, "\n");
395 break;
396
397 case CLASS_HARDWARE:
398 case CLASS_ENVIRONMENT:
399 cmn_err(CE_NOTE, " %s", ctlr->hba_name);
400 cmn_err(CE_CONT, " %s\n", evt->ascii_message);
401 cmn_err(CE_CONT, "\n");
402 break;
403
404 case CLASS_PHYSICAL_DRIVE:
405 cmn_err(CE_WARN, " %s", ctlr->hba_name);
406 cmn_err(CE_CONT, " %s\n", evt->ascii_message);
407
408 /*
409 * Fix for QUIX 1000440284: Display the Physical Drive
410 * Num info only for CISS Controllers
411 */
412
413 if (!(ctlr->bddef->bd_flags & SA_BD_SAS)) {
414 /* LINTED: alignment */
415 driveId = *(uint16_t *)(&evt->event_specific_data[0]);
416 if (driveId & 0x80) {
417 driveId -= 0x80;
418 cmn_err(CE_CONT, " Physical Drive Num ....... "
419 "SCSI Port %u, Drive Id %u\n",
420 (driveId / 16) + 1, (driveId % 16));
421 } else {
422 cmn_err(CE_CONT, " Physical Drive Num ....... "
423 "SCSI Port %u, Drive Id %u\n",
424 (driveId / 16) + 1, (driveId % 16));
425 }
426 }
427
428 if (evt->event_specific_data[2] < MAX_KNOWN_FAILURE_REASON) {
429 cmn_err(CE_CONT, " Failure Reason............ %s\n",
430 ascii_failure_reason[evt->event_specific_data[2]]);
431 } else {
432 cmn_err(CE_CONT,
433 " Failure Reason............ UNKNOWN \n");
434 }
435
436 cmn_err(CE_CONT, "\n");
437 break;
438
439 case CLASS_LOGICAL_DRIVE:
440 cmn_err(CE_NOTE, " %s", ctlr->hba_name);
441
442 /*
443 * Fix for QXCR1000717274 - We are appending the logical
444 * voulme number by one to be in sync with logical volume
445 * details given by HPQacucli
446 */
447
448 if ((evt->event_subclass_code == SUB_CLASS_STATUS) &&
449 (evt->event_detail_code == DETAIL_CHANGE)) {
450 cmn_err(CE_CONT, " State change, logical drive %u\n",
451 /* LINTED: alignment */
452 (*(uint16_t *)(&evt->event_specific_data[0]) + 1));
453 cmn_err(CE_CONT, " New Logical Drive State... %s\n",
454 log_vol_status[evt->event_specific_data[3]]);
455
456 /*
457 * If the Logical drive has FAILED or it was
458 * NOT CONFIGURED, in the corresponding target
459 * structure, set flag as NONE to suggest that no
460 * target exists at this id.
461 */
462
463 if ((evt->event_specific_data[3] == 1) ||
464 (evt->event_specific_data[3] == 2)) {
465 /* LINTED: alignment */
466 drive = *(uint16_t *)
467 (&evt->event_specific_data[0]);
468 drive = ((drive < CTLR_SCSI_ID)
469 ? drive : drive + CPQARY3_TGT_ALIGNMENT);
470 if (ctlr && ctlr->cpqary3_tgtp[drive]) {
471 ctlr->cpqary3_tgtp[drive]->type =
472 CPQARY3_TARGET_NONE;
473 }
474 }
475
476 if (evt->event_specific_data[4] & SPARE_REBUILDING) {
477 cmn_err(CE_CONT, " Logical Drive %d: "
478 "Data is rebuilding on spare drive\n",
479 /* LINTED: alignment */
480 (*(uint16_t *)
481 (&evt->event_specific_data[0]) + 1));
482 }
483
484 if (evt->event_specific_data[4] & SPARE_REBUILT) {
485 cmn_err(CE_CONT,
486 " Logical Drive %d: Rebuild complete. "
487 "Spare is now active\n",
488 /* LINTED: alignment */
489 (*(uint16_t *)
490 (&evt->event_specific_data[0]) + 1));
491 }
492 } else if ((evt->event_subclass_code == SUB_CLASS_STATUS) &&
493 (evt->event_detail_code == MEDIA_EXCHANGE)) {
494 cmn_err(CE_CONT, " Media exchange detected, "
495 "logical drive %u\n",
496 /* LINTED: alignment */
497 (*(uint16_t *)
498 (&evt->event_specific_data[0]) + 1));
499 } else {
500 cmn_err(CE_CONT, " %s\n", evt->ascii_message);
501 }
502
503 cmn_err(CE_CONT, "\n");
504 break;
505
506 default:
507 cmn_err(CE_NOTE, "%s", ctlr->hba_name);
508 cmn_err(CE_CONT, " %s\n", evt->ascii_message);
509 cmn_err(CE_CONT, "\n");
510 break;
511 }
512
513 /*
514 * Here, we reuse this command block to resubmit the NOE
515 * command.
516 * Ideally speaking, the resubmit should never fail
517 */
518 if (CPQARY3_FAILURE ==
519 cpqary3_send_NOE_command(ctlr, memp, CPQARY3_NOE_RESUBMIT)) {
520 cmn_err(CE_WARN, "CPQary3: Failed to ReInitialize "
521 "NOTIFY OF EVENT");
522 cpqary3_free_phyctgs_mem(MEM2DRVPVT(memp)->phyctgp,
523 CPQARY3_FREE_PHYCTG_MEM);
524 cpqary3_cmdlist_release(memp, CPQARY3_NO_MUTEX);
525 }
526 }
527
528 /* PERF */
529 /*
530 * Function : cpqary3_noe_complete
531 * Description : This routine processes the completed
532 * NOE commands and
533 * initiates any callback that is needed.
534 * Called By : cpqary3_send_NOE_command,
535 * cpqary3_disable_NOE_command
536 * Parameters : per-command
537 * Calls : cpqary3_NOE_handler, cpqary3_cmdlist_release
538 * Return Values: None
539 */
540 void
cpqary3_noe_complete(cpqary3_cmdpvt_t * cpqary3_cmdpvtp)541 cpqary3_noe_complete(cpqary3_cmdpvt_t *cpqary3_cmdpvtp)
542 {
543 ASSERT(cpqary3_cmdpvtp != NULL);
544
545 if (CPQARY3_TIMEOUT == cpqary3_cmdpvtp->cmdpvt_flag) {
546 cpqary3_cmdlist_release(cpqary3_cmdpvtp, CPQARY3_NO_MUTEX);
547 return;
548 }
549
550 if (cpqary3_cmdpvtp->cmdlist_memaddr->Request.CDB[6] ==
551 BMIC_CANCEL_NOTIFY_ON_EVENT) {
552 cv_signal(&cpqary3_cmdpvtp->ctlr->cv_noe_wait);
553 cpqary3_cmdlist_release(cpqary3_cmdpvtp, CPQARY3_NO_MUTEX);
554 } else {
555 cpqary3_NOE_handler(cpqary3_cmdpvtp);
556 }
557 }
558
559 /* PERF */
560