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 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 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 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 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