|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: ! 25: /** ! 26: * Copyright (c) 1994-1996 NeXT Software, Inc. All rights reserved. ! 27: * Copyright 1997 Apple Computer Inc. All Rights Reserved. ! 28: * @author Martin Minow mailto:[email protected] ! 29: * @revision 1997.02.18 Initial conversion from AMDPCSCSIDriver sources. ! 30: * ! 31: * Apple96SCSIPrivate.m - "Main" private methods for the 53C96 PCI SCSI interface. ! 32: * Apple96SCSI is closely based on Doug Mitchell's AMD 53C974/79C974 driver. ! 33: * ! 34: * Hmm, these methods are probably common to all Apple SCSI implementations. ! 35: * ! 36: * Edit History ! 37: * 1997.02.13 MM Initial conversion from AMDPCSCSIDriver sources. ! 38: */ ! 39: ! 40: #import "Apple96SCSI.h" ! 41: #import "Apple96SCSIPrivate.h" ! 42: #import "Apple96CurioPublic.h" ! 43: #import "Apple96Hardware.h" ! 44: #import "bringup.h" ! 45: #import "MacSCSICommand.h" ! 46: #import <mach/message.h> ! 47: #import <driverkit/generalFuncs.h> ! 48: #import <driverkit/interruptMsg.h> ! 49: #import <driverkit/align.h> ! 50: #import <driverkit/kernelDriver.h> ! 51: #import <kernserv/prototypes.h> ! 52: ! 53: /* ! 54: * Template for command message sent to the I/O thread. ! 55: */ ! 56: static const msg_header_t cmdMessageTemplate = { ! 57: 0, /* msg_unused */ ! 58: 1, /* msg_simple */ ! 59: sizeof(msg_header_t), /* msg_size */ ! 60: MSG_TYPE_NORMAL, /* msg_type */ ! 61: PORT_NULL, /* msg_local_port */ ! 62: PORT_NULL, /* msg_remote_port - filled in */ ! 63: IO_COMMAND_MSG /* msg_id */ ! 64: }; ! 65: ! 66: /* ! 67: * Template for timeout message. ! 68: */ ! 69: static const msg_header_t timeoutMsgTemplate = { ! 70: 0, /* msg_unused */ ! 71: 1, /* msg_simple */ ! 72: sizeof(msg_header_t), /* msg_size */ ! 73: MSG_TYPE_NORMAL, /* msg_type */ ! 74: PORT_NULL, /* msg_local_port */ ! 75: PORT_NULL, /* msg_remote_port - filled in */ ! 76: IO_TIMEOUT_MSG /* msg_id */ ! 77: }; ! 78: ! 79: static void serviceTimeoutInterrupt( ! 80: void *arg ! 81: ); ! 82: ! 83: @implementation Apple96_SCSI(Private) ! 84: ! 85: /* ! 86: * Private chip- and architecture-independent methods. ! 87: */ ! 88: /* ! 89: * Pass one CommandBuffer to the I/O thread; wait for completion. ! 90: * (We are called on the client's execution thread.) ! 91: * Normal completion status is in cmdBuf->scsiReq->driverStatus; ! 92: * a non-zero return from this function indicates a Mach IPC error. ! 93: * ! 94: * This method allocates and frees cmdBuf->cmdLock. ! 95: */ ! 96: - (IOReturn) executeCmdBuf ! 97: : (CommandBuffer *) cmdBuf ! 98: { ! 99: msg_header_t msg = cmdMessageTemplate; ! 100: kern_return_t kernelReturn; ! 101: IOReturn ioReturn = IO_R_SUCCESS; ! 102: ! 103: ENTRY("Sex executeCmdBuf"); ! 104: cmdBuf->flagActive = 0; ! 105: cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING]; ! 106: [gIncomingCommandLock lock]; ! 107: queue_enter(&gIncomingCommandQueue, cmdBuf, CommandBuffer *, link); ! 108: [gIncomingCommandLock unlock]; ! 109: ! 110: /* ! 111: * Create a Mach message and send it in order to wake up the ! 112: * I/O thread. ! 113: */ ! 114: msg.msg_remote_port = gKernelInterruptPort; ! 115: kernelReturn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0); ! 116: if (kernelReturn != KERN_SUCCESS) { ! 117: IOLog("%s: msg_send_from_kernel() error status %d\n", ! 118: [self name], ! 119: kernelReturn ! 120: ); ! 121: ioReturn = IO_R_IPC_FAILURE; ! 122: goto exit; ! 123: } ! 124: ! 125: /* ! 126: * Wait for I/O complete. ! 127: */ ! 128: ddmExported("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n", ! 129: cmdBuf, 2,3,4,5); ! 130: [cmdBuf->cmdLock lockWhen:CMD_COMPLETE]; ! 131: exit: RESULT(ioReturn); /* Log the completion before clearing the lock */ ! 132: [cmdBuf->cmdLock free]; ! 133: return (ioReturn); ! 134: } ! 135: ! 136: /* ! 137: * Abort all active and disconnected commands with specified status. No ! 138: * hardware action. Currently used by threadResetBus and during processing ! 139: * of a kCommandAbortRequest command. ! 140: */ ! 141: - (void) abortAllCommands : (sc_status_t) status ! 142: { ! 143: ENTRY("Sab abortAllCommands"); ! 144: ddmThread("abortAllCommands\n", 1,2,3,4,5); ! 145: [gIncomingCommandLock lock]; ! 146: [self killActiveCommand : status]; ! 147: [self killQueue : &gDisconnectedCommandQueue finalStatus : status]; ! 148: [self killQueue : &gPendingCommandQueue finalStatus : status]; ! 149: [self killQueue : &gIncomingCommandQueue finalStatus : status]; ! 150: #ifdef DEBUG ! 151: /* ! 152: * activeArray "should be" empty...if not, make sure it is for debug. ! 153: */ ! 154: do { ! 155: int target; ! 156: int lun; ! 157: int active; ! 158: ! 159: for (target = 0; target < SCSI_NTARGETS; target++) { ! 160: for (lun = 0; lun < SCSI_NLUNS; lun++) { ! 161: active = gActiveArray[target][lun]; ! 162: if (active > 0) { ! 163: IOLog("%s: (abortAllCommands) gActiveArray[%d][%d] = %d, should be zero\n", ! 164: [self name], ! 165: target, lun, active ! 166: ); ! 167: gActiveCount -= active; ! 168: gActiveArray[target][lun] = 0; ! 169: } ! 170: } ! 171: } ! 172: if (gActiveCount != 0) { ! 173: IOLog("%s: (abortAllCommands) activeCount = %d, should be zero\n", ! 174: [self name], ! 175: gActiveCount ! 176: ); ! 177: gActiveCount = 0; ! 178: } ! 179: } while (0); ! 180: #endif DEBUG ! 181: [gIncomingCommandLock unlock]; ! 182: EXIT(); ! 183: } ! 184: ! 185: /* ! 186: * Abort all active and disconnected commands with status SR_IOST_RESET. ! 187: * Reset hardware and SCSI bus. If there is a command in gPendingCommandQueue, ! 188: * start it up. ! 189: */ ! 190: - (void) threadResetBus ! 191: : (const char *) reason ! 192: { ! 193: ENTRY("Str threadResetBus"); ! 194: [self abortAllCommands : SR_IOST_RESET]; ! 195: [self hardwareReset : TRUE reason : reason]; /* Reset SCSI and chip */ ! 196: [self busFree]; /* Restart commands */ ! 197: EXIT(); ! 198: } ! 199: ! 200: /* ! 201: * Commence processing of the specified command. This is called by ! 202: * commandRequestOccurred when it receives a kCommandExecute message ! 203: * from IODirectDevice. There is a new SCSI request. Either start ! 204: * it now, or add it to the end of our pending request queue. ! 205: */ ! 206: - (void) threadExecuteRequest ! 207: : (CommandBuffer *) cmdBuf ! 208: { ! 209: ENTRY("Stx threadExecuteRequest"); ! 210: if (gActiveCommand != NULL) { ! 211: /* ! 212: * We are currently executing a request. ! 213: */ ! 214: ddmThread("threadExecuteRequest: ACTIVE; deferring 0x%x\n", ! 215: cmdBuf, ! 216: 2,3,4,5 ! 217: ); ! 218: queue_enter(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link); ! 219: } ! 220: else if ([self commandCanBeStarted:cmdBuf] == NO) { ! 221: /* ! 222: * This request can't be started right now (perhaps the ! 223: * target's tagged command limit has been reached). ! 224: */ ! 225: ddmThread("threadExecuteRequest: !commandCanBeStarted; deferring 0x%x\n", ! 226: cmdBuf, ! 227: 2,3,4,5 ! 228: ); ! 229: queue_enter(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link); ! 230: } ! 231: else { ! 232: /* ! 233: * Apparently, we can start this request. Call the hardware layer. ! 234: */ ! 235: ddmThread("calling hardwareStart: cmdBuf 0x%x activeArray[%d][%d] = %d\n", ! 236: cmdBuf, ! 237: cmdBuf->scsiReq->target, ! 238: cmdBuf->scsiReq->lun, ! 239: gActiveArray[cmdBuf->scsiReq->target][cmdBuf->scsiReq->lun], ! 240: 5 ! 241: ); ! 242: switch ([self hardwareStart : cmdBuf]) { ! 243: case kHardwareStartBusy: /* Hardware can't start now */ ! 244: ddmThread("threadExecuteRequest: hardware busy\n", 1, 2, 3, 4, 5); ! 245: break; ! 246: case kHardwareStartOK: /* Command started correctly */ ! 247: break; ! 248: case kHardwareStartRejected: /* Command rejected, try another */ ! 249: ddmThread("threadExecuteRequest: calling busFree\n", 1,2,3,4,5); ! 250: [self busFree]; /* Try another command */ ! 251: } ! 252: ddmThread("threadExecuteRequest(0x%x): DONE\n", cmdBuf, 2,3,4,5); ! 253: } ! 254: EXIT(); ! 255: } ! 256: ! 257: /* ! 258: * Methods called by hardware-dependent modules. ! 259: */ ! 260: ! 261: #if TEST_QUEUE_FULL ! 262: int testQueueFull; /* Set in the debugger to return queue full status */ ! 263: #endif TEST_QUEUE_FULL ! 264: ! 265: /* ! 266: * Called when a transaction associated with cmdBuf is complete. Notify ! 267: * waiting thread. If cmdBuf->scsiReq exists (i.e., this is not a reset ! 268: * or an abort), scsiReq->driverStatus must be valid. If cmdBuf is active, ! 269: * caller must remove from gActiveCommand. We decrement activeArray[][] counter ! 270: * if appropriate. ! 271: */ ! 272: - (void) ioComplete ! 273: : (CommandBuffer *) cmdBuf ! 274: finalStatus : (sc_status_t) finalStatus ! 275: { ! 276: ns_time_t currentTime; ! 277: IOSCSIRequest *scsiReq; ! 278: ! 279: ENTRY("Sic ioComplete"); ! 280: ASSERT(cmdBuf != NULL); ! 281: if (cmdBuf == gActiveCommand) { ! 282: [self deactivateCmd]; ! 283: } ! 284: scsiReq = cmdBuf->scsiReq; ! 285: if (scsiReq != NULL) { ! 286: if (scsiReq->driverStatus == SR_IOST_INVALID) { ! 287: scsiReq->driverStatus = finalStatus; ! 288: } ! 289: IOGetTimestamp(¤tTime); ! 290: scsiReq->totalTime = currentTime - cmdBuf->startTime; ! 291: scsiReq->bytesTransferred = cmdBuf->currentDataIndex; ! 292: #if SERIOUS_DEBUGGING || LOG_COMMAND_AND_RESULT ! 293: IOLog("%s: ioComplete %d.%d: Transferred %d bytes, status %d: %s\n", ! 294: [self name], ! 295: scsiReq->target, ! 296: scsiReq->lun, ! 297: scsiReq->bytesTransferred, ! 298: scsiReq->driverStatus, ! 299: IOFindNameForValue(scsiReq->driverStatus, IOScStatusStrings) ! 300: ); ! 301: #endif /* SERIOUS_DEBUGGING || LOG_COMMAND_AND_RESULT */ ! 302: #if (SERIOUS_DEBUGGING || DUMP_USER_BUFFER) && __IO_MEMORY_DESCRIPTOR_DEBUG__ ! 303: if (cmdBuf->mem != NULL) { ! 304: IOMemoryDescriptorState state; ! 305: LogicalRange range; ! 306: unsigned int newPosition = 0; ! 307: ! 308: [cmdbuf->mem state : &state]; ! 309: [cmdbuf->mem setPosition : 0]; ! 310: while (newPosition < scsiReq->bytesTransferred ! 311: && [cmdbuf->mem getLogicalRange : 1 ! 312: maxByteCount : scsiReq->bytesTransferred - newPosition ! 313: newPosition : &newPosition ! 314: actualRanges : NULL ! 315: logicalRanges : &range] != 0) { ! 316: [self logMemory : range.address ! 317: length : range.length ! 318: reason : "User buffer at I/O Complete" ! 319: ]; ! 320: } ! 321: [cmdbuf->mem setState : &state]; ! 322: } ! 323: #endif /* (SERIOUS_DEBUGGING || DUMP_USER_BUFFER) && __IO_MEMORY_DESCRIPTOR_DEBUG__ */ ! 324: /* ! 325: * Catch bad SCSI status now. ! 326: */ ! 327: if (scsiReq->driverStatus == SR_IOST_GOOD) { ! 328: #if TEST_QUEUE_FULL ! 329: if (testQueueFull ! 330: && (activeArray[scsiReq->target][scsiReq->lun] > 1)) { ! 331: scsiReq->scsiStatus = STAT_QUEUE_FULL; ! 332: testQueueFull = 0; ! 333: } ! 334: #endif TEST_QUEUE_FULL ! 335: if (cmdBuf->flagIsAutosense) { ! 336: /* ! 337: * We are completing an autosense command. Don't touch ! 338: * the request status (it should still be Check Condition). ! 339: * Queue full is a real problem. ! 340: */ ! 341: ASSERT(scsiReq->scsiStatus == kScsiStatusCheckCondition); ! 342: switch (cmdBuf->autosenseStatus) { ! 343: case kScsiStatusGood: ! 344: bcopy(gAutosenseArea, &scsiReq->senseData, sizeof (esense_reply_t)); ! 345: #if SERIOUS_DEBUGGING || DUMP_USER_BUFFER ! 346: [self logMemory : &scsiReq->senseData ! 347: length : sizeof (esense_reply_t) ! 348: reason : "Autosense at I/O completion" ! 349: ]; ! 350: #endif /* SERIOUS_DEBUGGING || DUMP_USER_BUFFER */ ! 351: scsiReq->driverStatus = SR_IOST_CHKSV; ! 352: break; ! 353: case kScsiStatusQueueFull: ! 354: if ([self pushbackFullTargetQueue : cmdBuf] == SR_IOST_GOOD) { ! 355: ddmThread("ioComplete: queue full after autosense\n", 1, 2, 3, 4, 5); ! 356: goto exit; ! 357: } ! 358: /* Fall through to failure */ ! 359: default: ! 360: scsiReq->driverStatus = SR_IOST_CHKSNV; ! 361: break; ! 362: } ! 363: } ! 364: else { /* Normal command: not autosense */ ! 365: switch (scsiReq->scsiStatus) { ! 366: case kScsiStatusGood: ! 367: ! 368: /* If this was an Inquiry, peek at the data */ ! 369: /* for Synchronous and Tagged Queuing support: */ ! 370: ! 371: if ( (*(UInt8*)&scsiReq->cdb == kScsiCmdInquiry) ! 372: && (cmdBuf->currentDataIndex > 7) ! 373: && (cmdBuf->mem != NULL) ) { ! 374: IOMemoryDescriptorState state; ! 375: UInt8 byte; ! 376: unsigned int count; ! 377: ! 378: [cmdBuf->mem state : &state]; ! 379: [cmdBuf->mem setPosition : 7]; ! 380: count = [cmdBuf->mem readFromClient : &byte count : 1]; ! 381: [cmdBuf->mem setState : &state]; ! 382: if (count == 1) { ! 383: gPerTargetData[ scsiReq->target ].inquiry_7 = byte; ! 384: if ( (byte & 0x02) == 0) { ! 385: gPerTargetData[ scsiReq->target ].cmdQueueDisable = TRUE; ! 386: } ! 387: } ! 388: } /* end if successful inquiry completion */ ! 389: break; ! 390: case kScsiStatusCheckCondition: ! 391: /* ! 392: * ** ** ** The 386 SIM supresses autosense for Test ! 393: * ** ** ** Unit Ready to avoid request sense when polling ! 394: * ** ** ** for removable devices. This should be the caller's ! 395: * ** ** ** decision. ! 396: */ ! 397: if ( gOptionAutoSenseEnable /* Supress for debugging */ ! 398: && (scsiReq->ignoreChkcond == FALSE) ) { ! 399: cmdBuf->flagIsAutosense = 1; /* We're doing autosense */ ! 400: queue_enter_first(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link); ! 401: ddmThread("ioComplete: queuing for autosense\n", 1, 2, 3, 4, 5); ! 402: goto exit; ! 403: } ! 404: else { ! 405: /* ! 406: * This command failed and we aren't doing autosense. ! 407: */ ! 408: scsiReq->driverStatus = SR_IOST_CHKSNV; ! 409: } ! 410: break; ! 411: case kScsiStatusQueueFull: ! 412: if ([self pushbackFullTargetQueue : cmdBuf] == SR_IOST_GOOD) { ! 413: /* ! 414: * ioComplete target tagged, queue full ! 415: */ ! 416: ddmThread("ioComplete: target tagged, queue full\n", 1, 2, 3, 4, 5); ! 417: goto exit; ! 418: } ! 419: /* Huh? we weren't doing tagged queuing, fall through */ ! 420: default: ! 421: scsiReq->driverStatus = SR_IOST_BADST; ! 422: break; ! 423: } /* switch */ ! 424: } /* If not autosense */ ! 425: } /* if scsiReq->driverStatus == SR_IOST_GOOD on entrance */ ! 426: } /* if (scsiReq != NULL) */ ! 427: #if DDM_DEBUG ! 428: do { ! 429: const char *status; ! 430: unsigned moved; ! 431: ! 432: if (scsiReq != NULL) { ! 433: status = IOFindNameForValue(scsiReq->driverStatus, ! 434: IOScStatusStrings); ! 435: moved = scsiReq->bytesTransferred; ! 436: } ! 437: else { ! 438: status = "Complete"; ! 439: moved = 0; ! 440: } ! 441: ddmThread("ioComplete: cmdBuf 0x%x status %s bytesXfr 0x%x\n", ! 442: cmdBuf, status, moved,4,5); ! 443: } while (0); ! 444: #if LOG_RESULT_ON_ERROR ! 445: if (scsiReq != NULL && scsiReq->driverStatus != SR_IOST_GOOD) { ! 446: [self logCommand ! 447: : cmdBuf ! 448: logMemory : TRUE ! 449: reason : "Unsuccessful result" ! 450: ]; ! 451: } ! 452: #endif /* LOG_RESULT_ON_ERROR */ ! 453: #endif DDM_DEBUG ! 454: [cmdBuf->cmdLock lock]; ! 455: [cmdBuf->cmdLock unlockWith:YES]; ! 456: exit: ; /* This semicolon is needed to prevent errors in the EXIT macro */ ! 457: EXIT(); ! 458: } ! 459: ! 460: /** ! 461: * A target reported a full queue. Push this command back on the pending ! 462: * queue and try it again, later. (Return SR_IOST_GOOD if successful, ! 463: * SR_IOST_BADST on failure. ! 464: */ ! 465: - (sc_status_t) pushbackFullTargetQueue ! 466: : (CommandBuffer *) cmdBuf ! 467: { ! 468: IOSCSIRequest *scsiReq; ! 469: int target; ! 470: int lun; ! 471: IOReturn ioReturn; ! 472: ! 473: ENTRY("Spf pushbackFullTargetQueue"); ! 474: ASSERT(cmdBuf != NULL && cmdBuf->scsiReq != NULL); ! 475: ASSERT(cmdBuf != gActiveCommand); ! 476: /* ! 477: * Avoid notifying client of this condition; update ! 478: * perTarget.maxQueue and place this request on gPendingCommandQueue. ! 479: * We'll try this again when we ioComplete at least one ! 480: * command in this target's queue. ! 481: ** ** ** Note that this can execute commands out of order. ! 482: ** ** ** This can be disasterous for directory commands. ! 483: ** ** ** In the long run, the client (disk/tape/whatever) ! 484: ** ** ** needs to tell us how to execute the command ! 485: ** ** ** (in-order, out-of-order, etc.) For example, ! 486: ** ** ** virtual-memory page faults can be executed ! 487: ** ** ** out of order, but directory and volume bitmap ! 488: ** ** ** updates must be executed in-order to preserve ! 489: ** ** ** volume integrity. ! 490: */ ! 491: if (cmdBuf->queueTag == QUEUE_TAG_NONTAGGED) { ! 492: /* ! 493: * Huh? We're not doing command queueing... ! 494: */ ! 495: ioReturn = SR_IOST_BADST; ! 496: } ! 497: else { ! 498: scsiReq = cmdBuf->scsiReq; ! 499: target = scsiReq->target; ! 500: lun = scsiReq->lun; ! 501: gPerTargetData[target].maxQueue = gActiveArray[target][lun]; ! 502: ddmThread("Target %d queue full, maxQueue set to %d\n", ! 503: target, ! 504: gPerTargetData[target].maxQueue, ! 505: 3,4,5 ! 506: ); ! 507: [self pushbackCurrentRequest : cmdBuf]; ! 508: ioReturn = SR_IOST_GOOD; ! 509: } ! 510: RESULT(ioReturn); ! 511: return (ioReturn); ! 512: } ! 513: ! 514: /** ! 515: * Push this request back on the pending queue. ! 516: */ ! 517: - (void) pushbackCurrentRequest ! 518: : (CommandBuffer *) cmdBuf ! 519: { ! 520: ENTRY("Spc pushbackCurrentRequest"); ! 521: if (gActiveCommand != NULL) { ! 522: [self deactivateCmd]; ! 523: } ! 524: queue_enter_first(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link); ! 525: EXIT(); ! 526: } ! 527: ! 528: #if 0 ! 529: /** ! 530: * Kill a request that can't be continued. ! 531: */ ! 532: - (void) killCurrentRequest ! 533: { ! 534: ENTRY("Skc killCurrentRequest"); ! 535: if (gActiveCommand != NULL) { ! 536: if (gActiveCommand->scsiReq != NULL ! 537: && gActiveCommand->scsiReq->driverStatus == SR_IOST_INVALID) { ! 538: if (gCurrentBusPhase == kBusPhaseBusFree) { ! 539: gActiveCommand->scsiReq->driverStatus = SR_IOST_SELTO; /* No such device */ ! 540: } ! 541: else { ! 542: gActiveCommand->scsiReq->driverStatus = SR_IOST_HW; /* Target went away */ ! 543: } ! 544: } ! 545: [self ioComplete ! 546: : gActiveCommand ! 547: finalStatus : SR_IOST_INVALID ! 548: ]; ! 549: } ! 550: EXIT(); ! 551: } ! 552: #endif ! 553: ! 554: /* ! 555: * I/O associated with gActiveCommand has disconnected. Place it on the ! 556: * disconnected command queue and enable another transaction. ! 557: */ ! 558: - (void) disconnect ! 559: { ! 560: ENTRY("Sdi disconnect"); ! 561: ASSERT(gActiveCommand != NULL); ! 562: ddmThread("DISCONNECT: cmdBuf 0x%x target %d lun %d tag %d\n", ! 563: gActiveCommand, gActiveCommand->scsiReq->target, ! 564: gActiveCommand->scsiReq->lun, gActiveCommand->queueTag, 5); ! 565: queue_enter( ! 566: &gDisconnectedCommandQueue, ! 567: gActiveCommand, ! 568: CommandBuffer *, ! 569: link ! 570: ); ! 571: #if DDM_DEBUG ! 572: if ((gActiveCommand->currentDataIndex != gActiveCommand->scsiReq->maxTransfer) ! 573: && (gActiveCommand->currentDataIndex != 0)) { ! 574: ddmThread("disconnect after partial DMA (max 0x%d curr 0x%x)\n", ! 575: gActiveCommand->scsiReq->maxTransfer, ! 576: gActiveCommand->currentDataIndex, 3,4,5); ! 577: } ! 578: #endif DDM_DEBUG ! 579: /* ! 580: * Record this time so that gActiveCommand can be billed for ! 581: * disconnect latency at reselect time. ! 582: */ ! 583: IOGetTimestamp(&gActiveCommand->disconnectTime); ! 584: gActiveCommand = NULL; ! 585: gCurrentTarget = kInvalidTarget; ! 586: gCurrentLUN = kInvalidLUN; ! 587: /* ! 588: * Since there is no active command, the caller must configure the ! 589: * bus interface to wait for bus free, then allow reselection. ! 590: */ ! 591: EXIT(); ! 592: } ! 593: ! 594: /* ! 595: * Specified target, lun is trying to reselect. If we have a CommandBuffer ! 596: * for this TL nexus and it is not tagged, remove it, make it the current ! 597: * gActiveCommand, and return IO_R_SUCCESS. If the first disconnected ! 598: * command is tagged, we need to read the queue tag message, return ! 599: * IO_R_INTERNAL to signal this (it is not an error). ! 600: */ ! 601: - (IOReturn) reselectNexusWithoutTag ! 602: { ! 603: CommandBuffer *cmdBuf; ! 604: IOSCSIRequest *scsiReq; ! 605: ns_time_t currentTime; ! 606: IOReturn ioReturn = SR_IOST_HW; /* Presume failure */ ! 607: ! 608: ENTRY("Srn reselectNexusWithoutTag"); ! 609: ASSERT(gActiveCommand == NULL); ! 610: cmdBuf = (CommandBuffer *) queue_first(&gDisconnectedCommandQueue); ! 611: while (queue_end(&gDisconnectedCommandQueue, (queue_t) cmdBuf) == FALSE) { ! 612: scsiReq = cmdBuf->scsiReq; ! 613: if (scsiReq->target == gCurrentTarget ! 614: && scsiReq->lun == gCurrentLUN ! 615: && cmdBuf->queueTag == QUEUE_TAG_NONTAGGED) { ! 616: /* ! 617: * We found the correct command. ! 618: */ ! 619: ddmThread("RESELECT: target %d.%d no tag %FOUND; cmdBuf 0x%x\n", ! 620: gCurrentTarget, ! 621: gCurrentLUN, ! 622: cmdBuf, ! 623: 4, 5 ! 624: ); ! 625: queue_remove( ! 626: &gDisconnectedCommandQueue, ! 627: cmdBuf, ! 628: CommandBuffer *, ! 629: link ! 630: ); ! 631: gActiveCommand = cmdBuf; ! 632: /* ! 633: * Bill this operation for latency time. ! 634: */ ! 635: IOGetTimestamp(¤tTime); ! 636: scsiReq->latentTime += ! 637: (currentTime - gActiveCommand->disconnectTime); ! 638: ioReturn = IO_R_SUCCESS; ! 639: break; ! 640: } ! 641: /* ! 642: * Try next element in queue. ! 643: */ ! 644: cmdBuf = (CommandBuffer *) cmdBuf->link.next; ! 645: } ! 646: RESULT(ioReturn); ! 647: return (ioReturn); ! 648: } ! 649: /* ! 650: * Specified target, lun, and queueTag is trying to reselect. If we have ! 651: * a CommandBuffer for this TLQ nexus on disconnectQ, remove it, make it the ! 652: * current activeCmd, and return IO_R_SUCCESS. Else return IO_R_HW. ! 653: */ ! 654: - (IOReturn) reselectNexusWithTag ! 655: : (UInt8) queueTag ! 656: { ! 657: CommandBuffer *cmdBuf; ! 658: IOSCSIRequest *scsiReq; ! 659: ns_time_t currentTime; ! 660: IOReturn ioReturn = SR_IOST_HW; /* Presume failure */ ! 661: ! 662: ENTRY("Srt reselectNexusWithTag"); ! 663: ASSERT(gActiveCommand == NULL); ! 664: ASSERT(queueTag != QUEUE_TAG_NONTAGGED); ! 665: /* ! 666: * Scan the disconnected queue looking for a command for this ! 667: * T/L/Q nexus. ! 668: */ ! 669: cmdBuf = (CommandBuffer *) queue_first(&gDisconnectedCommandQueue); ! 670: while (queue_end(&gDisconnectedCommandQueue, (queue_t) cmdBuf) == FALSE) { ! 671: scsiReq = cmdBuf->scsiReq; ! 672: if (scsiReq->target == gCurrentTarget ! 673: && scsiReq->lun == gCurrentLUN ! 674: && cmdBuf->queueTag == queueTag) { ! 675: /* ! 676: * We found the correct command. ! 677: */ ! 678: ddmThread("RESELECT: target %d.%d tag %d FOUND; cmdBuf 0x%x\n", ! 679: gCurrentTarget, ! 680: gCurrentLUN, ! 681: queueTag, ! 682: cmdBuf, ! 683: 5 ! 684: ); ! 685: queue_remove( ! 686: &gDisconnectedCommandQueue, ! 687: cmdBuf, ! 688: CommandBuffer *, ! 689: link ! 690: ); ! 691: gActiveCommand = cmdBuf; ! 692: ASSERT(scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN); ! 693: /* ! 694: * Bill this operation for latency time. ! 695: */ ! 696: IOGetTimestamp(¤tTime); ! 697: scsiReq->latentTime += ! 698: (currentTime - gActiveCommand->disconnectTime); ! 699: ioReturn = IO_R_SUCCESS; ! 700: break; ! 701: } ! 702: /* ! 703: * Try next element in queue. ! 704: */ ! 705: cmdBuf = (CommandBuffer *) cmdBuf->link.next; ! 706: } ! 707: if (ioReturn != IO_R_SUCCESS) { ! 708: /* ! 709: * Hmm...this is not good! We don't want to talk to this target. ! 710: * Perhaps it's reselecting after we have already aborted the ! 711: * original command request. ! 712: */ ! 713: IOLog("%s: Unexpected reselect from target %d lun %d tag %d\n", ! 714: [self name], ! 715: gCurrentTarget, ! 716: gCurrentLUN, ! 717: queueTag ! 718: ); ! 719: } ! 720: RESULT(ioReturn); ! 721: return (ioReturn); ! 722: } ! 723: ! 724: /* ! 725: * Determine if gActiveArray[][], maxQueue, cmdQueueEnable, and a ! 726: * command's target and lun show that it's OK to start processing cmdBuf. ! 727: * Returns YES if this command can be started. ! 728: * ** ** ** ! 729: * ** ** ** Here's where we can test for a frozen LUN queue. ! 730: * ** ** ** ! 731: */ ! 732: - (Boolean) commandCanBeStarted : (CommandBuffer *) cmdBuf ! 733: { ! 734: IOSCSIRequest *scsiReq; ! 735: unsigned target = scsiReq->target; ! 736: unsigned lun = scsiReq->lun; ! 737: Boolean result; ! 738: ! 739: ENTRY("Scs commandCanBeStarted"); ! 740: ASSERT(cmdBuf != NULL && cmdBuf->scsiReq != NULL); ! 741: scsiReq = cmdBuf->scsiReq; ! 742: target = scsiReq->target; ! 743: lun = scsiReq->lun; ! 744: if (gActiveArray[target][lun] == 0) { ! 745: /* ! 746: * No commands are active for this target, always ok. ! 747: */ ! 748: result = TRUE; ! 749: } ! 750: else if (gOptionCmdQueueEnable == FALSE ! 751: || gPerTargetData[target].cmdQueueDisable) { ! 752: /* ! 753: * Command queueing is disabled for this target ! 754: * (or disabled globally); only one command at a time ! 755: * can be issued. ! 756: */ ! 757: result = FALSE; ! 758: } ! 759: else { ! 760: if (gPerTargetData[target].maxQueue == 0 ! 761: || gActiveArray[target][lun] < gPerTargetData[target].maxQueue) { ! 762: /* ! 763: * If maxQueueDepth is zero, we haven't reached the ! 764: * target's limit. Otherwise, we're under the limit. In ! 765: * both cases, we can (presumably) start this command. ! 766: * ! 767: */ ! 768: result = TRUE; ! 769: } ! 770: else { ! 771: /* ! 772: * We're over the target limit. Wait on this one. ! 773: */ ! 774: result = FALSE; ! 775: } ! 776: } ! 777: RESULT(result); ! 778: return (result); ! 779: } ! 780: ! 781: /* ! 782: * The bus has gone free. Start up a command from gPendingCommandQueue, if any, and ! 783: * if allowed by cmdQueueEnable and gActiveArray[][]. ! 784: * ! 785: * This is called from the interrupt routine when it is about to exit (and the ! 786: * bus is free and there is no active command). It may also be called from ! 787: * threadExecuteRequest when the selected command couldn't be started. ! 788: * ! 789: */ ! 790: - (void) busFree ! 791: { ! 792: CommandBuffer *cmdBuf; ! 793: ! 794: ENTRY("Sbf busFree"); ! 795: cmdBuf = [self selectNextRequest]; ! 796: if (cmdBuf != NULL) { ! 797: [self threadExecuteRequest:cmdBuf]; ! 798: } ! 799: EXIT(); ! 800: } ! 801: ! 802: /* ! 803: * Scan through the pending queue for the first command that can be started. ! 804: * Return the command record, or NULL if there is no pending command. ! 805: */ ! 806: - (CommandBuffer *) selectNextRequest ! 807: { ! 808: CommandBuffer *cmdBuf = NULL; ! 809: Boolean foundRequest; ! 810: ! 811: ENTRY("Ssn selectNextRequest"); ! 812: ASSERT(gActiveCommand == NULL); ! 813: foundRequest = FALSE; ! 814: if (queue_empty(&gPendingCommandQueue) == FALSE) { ! 815: /* ! 816: * Attempt to find a CommandBuffer in gPendingCommandQueue which we are in a position ! 817: * to process. ! 818: */ ! 819: cmdBuf = (CommandBuffer *) queue_first(&gPendingCommandQueue); ! 820: while (queue_end(&gPendingCommandQueue, (queue_entry_t) cmdBuf) == FALSE) { ! 821: if ([self commandCanBeStarted:cmdBuf]) { ! 822: queue_remove(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link); ! 823: ddmThread("busFree: starting pending cmd 0x%x\n", cmdBuf, ! 824: 2,3,4,5); ! 825: foundRequest = TRUE; ! 826: break; ! 827: /* ! 828: * Note that threadExecuteRequest may call busFree if the command ! 829: * was rejected. If so, the rejected command will have been ! 830: * returned (with an error status) to its client, so there is ! 831: * no chance of an infinite loop here. ! 832: */ ! 833: } ! 834: else { ! 835: cmdBuf = (CommandBuffer *) queue_next(&cmdBuf->link); ! 836: } ! 837: } ! 838: } ! 839: if (foundRequest == FALSE) { ! 840: cmdBuf = NULL; ! 841: } ! 842: RESULT(cmdBuf); ! 843: return (cmdBuf); ! 844: } ! 845: ! 846: /* ! 847: * Abort gActiveCommand (if any) and any disconnected I/Os (if any) and reset ! 848: * the bus due to gross hardware failure. ! 849: * ! 850: * If gActiveCommand is valid, its scsiReq->driverStatus will be set to 'status'. ! 851: * This is called when the bus state machine detects an error that it can't ! 852: * recover from, such as a "gross error" in the chip. It is a very big hammer. ! 853: */ ! 854: - (void) killActiveCommandAndResetBus ! 855: : (sc_status_t) status ! 856: reason : (const char *) reason ! 857: { ! 858: ENTRY("Skr killActiveCommandAndResetBus"); ! 859: [self killActiveCommand : status]; ! 860: [self logRegisters : TRUE reason : "Killing Active Command and Resetting Bus"]; ! 861: [self threadResetBus : reason]; ! 862: EXIT(); ! 863: } ! 864: ! 865: - (void) killActiveCommand ! 866: : (sc_status_t) status ! 867: { ! 868: CommandBuffer *activeCmd; /* -> The currently executing command */ ! 869: ! 870: ENTRY("Ski killActiveCommand"); ! 871: if (gActiveCommand != NULL) { ! 872: activeCmd = gActiveCommand; ! 873: [self deactivateCmd]; ! 874: [self ioComplete ! 875: : activeCmd ! 876: finalStatus : status ! 877: ]; ! 878: } ! 879: EXIT(); ! 880: } ! 881: ! 882: /* ! 883: * Called by chip level to indicate that a command has gone out to the ! 884: * hardware. ! 885: */ ! 886: - (void) activateCommand ! 887: : (CommandBuffer *) cmdBuf ! 888: { ! 889: ENTRY("Sac activateCommand"); ! 890: ASSERT(gActiveCommand == NULL); ! 891: /* ! 892: * Start timeout timer for this I/O. The timer request is cancelled ! 893: * in ioComplete. ! 894: */ ! 895: cmdBuf->timeoutPort = gKernelInterruptPort; ! 896: #if LONG_TIMEOUT ! 897: cmdBuf->scsiReq->timeoutLength = OUR_TIMEOUT; ! 898: #endif LONG_TIMEOUT ! 899: IOScheduleFunc(serviceTimeoutInterrupt, cmdBuf, cmdBuf->scsiReq->timeoutLength); ! 900: ! 901: /* ! 902: * This is the only place where an gActiveArray[][] counter is ! 903: * incremented (and, hence, the only place where cmdBuf->active is ! 904: * set). The only other place gActiveCommand is set to non-NULL is ! 905: * in reselectNexux... (but those methods don't increment the ! 906: * active command counter) ! 907: */ ! 908: gActiveCommand = cmdBuf; ! 909: gCurrentTarget = cmdBuf->scsiReq->target; ! 910: gCurrentLUN = cmdBuf->scsiReq->lun; ! 911: TAG(__tag__, (gCurrentTarget << 4) | gCurrentLUN); ! 912: gActiveArray[gCurrentTarget][gCurrentLUN]++; ! 913: gActiveCount++; ! 914: cmdBuf->flagActive = TRUE; ! 915: /* ! 916: * Accumulate statistics. ! 917: */ ! 918: gMaxQueueLen = MAX(gMaxQueueLen, gActiveCount); ! 919: gQueueLenTotal += gActiveCount; ! 920: gTotalCommands++; ! 921: ddmThread("activateCommand: cmdBuf 0x%x target %d lun %d\n", ! 922: cmdBuf, gCurrentTarget, gCurrentLUN, ! 923: 4, 5 ! 924: ); ! 925: EXIT(); ! 926: } ! 927: ! 928: /* ! 929: * Remove the current command from "active" status. Update activeArray, ! 930: * activeCount, and unschedule pending timer. ! 931: */ ! 932: - (void) deactivateCmd ! 933: { ! 934: CommandBuffer *cmdBuf; ! 935: unsigned target, lun; ! 936: ! 937: ENTRY("Sdc deactivateCmd"); ! 938: /* ! 939: * If we are pushing back a full target queue, it's possible ! 940: * to be called twice on the same command: once from ioComplete ! 941: * and once from pushbackCurrentRequest. To prevent the second ! 942: * call from de-referencing NULL, we exit if gActiveCommand ! 943: * is already NULL. Radar 2206796. ! 944: */ ! 945: if (gActiveCommand != NULL) { ! 946: ASSERT(gActiveCommand->scsiReq != NULL); ! 947: TAG(__tag__, ! 948: (gActiveCommand->scsiReq->target << 4) ! 949: | gActiveCommand->scsiReq->lun ! 950: ); ! 951: ddmThread("deactivate cmd 0x%x target %d lun %d activeArray %d\n", ! 952: gActiveCommand, ! 953: gActiveCommand->scsiReq->target, ! 954: gActiveCommand->scsiReq->lun, ! 955: gActiveArray[gActiveCommand->scsiReq->target] ! 956: [gActiveCommand->scsiReq->lun], ! 957: 5 ! 958: ); ! 959: cmdBuf = gActiveCommand; ! 960: gActiveCommand = NULL; ! 961: gCurrentTarget = kInvalidTarget; ! 962: gCurrentLUN = kInvalidLUN; ! 963: target = cmdBuf->scsiReq->target; ! 964: lun = cmdBuf->scsiReq->lun; ! 965: ASSERT(gActiveArray[target][lun] > 0); ! 966: gActiveArray[target][lun]--; ! 967: ASSERT(gActiveCount > 0); ! 968: gActiveCount--; ! 969: /* ! 970: * Cancel pending timeout request. We don't have to do this ! 971: * for commands that have timed out as they won't have a ! 972: * timeout request in the schedular. ! 973: */ ! 974: if (cmdBuf->scsiReq->driverStatus != SR_IOST_IOTO) { ! 975: IOUnscheduleFunc(serviceTimeoutInterrupt, cmdBuf); ! 976: } ! 977: cmdBuf->flagActive = FALSE; ! 978: } ! 979: EXIT(); ! 980: } ! 981: ! 982: /** ! 983: * Kill everything in the indicated queue. Called after bus reset. ! 984: */ ! 985: - (void) killQueue : (queue_head_t *) queuePtr ! 986: finalStatus : (sc_status_t) scsiStatus ! 987: { ! 988: CommandBuffer *cmdBuf; ! 989: ! 990: ENTRY("Skq killQueue"); ! 991: while (queue_empty(queuePtr) == FALSE) { ! 992: cmdBuf = (CommandBuffer *) queue_first(queuePtr); ! 993: queue_remove(queuePtr, cmdBuf, CommandBuffer *, link); ! 994: cmdBuf->scsiReq->driverStatus = scsiStatus; ! 995: [self ioComplete ! 996: : cmdBuf ! 997: finalStatus : scsiStatus ! 998: ]; ! 999: } ! 1000: EXIT(); ! 1001: } ! 1002: ! 1003: @end /* Apple96_SCSI(Private) */ ! 1004: ! 1005: /* ! 1006: * Handle timeouts. We just send a timeout message to the I/O thread ! 1007: * so it wakes up. ! 1008: */ ! 1009: static void serviceTimeoutInterrupt(void *arg) ! 1010: { ! 1011: CommandBuffer *cmdBuf = arg; ! 1012: msg_header_t msg = timeoutMsgTemplate; ! 1013: ! 1014: ENTRY("Sto serviceTimeoutInterrupt"); ! 1015: ASSERT(cmdBuf != NULL && cmdBuf->scsiReq != NULL); ! 1016: ddmError("Apple96SCSI: timeout: cmdBuf 0x%x target %d\n", ! 1017: cmdBuf, ! 1018: cmdBuf->scsiReq->target, ! 1019: 3,4,5 ! 1020: ); ! 1021: if (cmdBuf->flagActive == FALSE) { ! 1022: /* ! 1023: * Should never happen... ! 1024: */ ! 1025: IOLog("Apple96SCSI: Timeout on inactive command\n"); ! 1026: } ! 1027: else { ! 1028: msg.msg_remote_port = cmdBuf->timeoutPort; ! 1029: IOLog("Apple96SCSI: SCSI Timeout on target %d, lun %d\n", ! 1030: cmdBuf->scsiReq->target, ! 1031: cmdBuf->scsiReq->lun ! 1032: ); ! 1033: (void) msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0); ! 1034: } ! 1035: EXIT(); ! 1036: } ! 1037:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.