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