|
|
1.1 root 1: /*
2: * AMD_SCSI.m - top-level module for AMD 53C974/79C974 PCI SCSI driver.
3: *
4: * HISTORY
5: * 21 Oct 94 Doug Mitchell at NeXT
6: * Created.
7: */
8:
9: #import "AMD_SCSI.h"
10: #import "AMD_Private.h"
11: #import "AMD_x86.h"
12: #import "AMD_Chip.h"
13: #import "AMD_ddm.h"
14: #import "AMD_Regs.h"
15: #import <mach/message.h>
16: #import <driverkit/generalFuncs.h>
17: #import <driverkit/interruptMsg.h>
18: #import <driverkit/align.h>
19: #import <driverkit/kernelDriver.h>
20: #import <kernserv/prototypes.h>
21:
22: static void AMDTimeout(void *arg);
23:
24: /*
25: * Template for command message sent to the I/O thread.
26: */
27: static msg_header_t cmdMessageTemplate = {
28: 0, // msg_unused
29: 1, // msg_simple
30: sizeof(msg_header_t), // msg_size
31: MSG_TYPE_NORMAL, // msg_type
32: PORT_NULL, // msg_local_port
33: PORT_NULL, // msg_remote_port - TO
34: // BE FILLED IN
35: IO_COMMAND_MSG // msg_id
36: };
37:
38: /*
39: * Template for timeout message.
40: */
41: static msg_header_t timeoutMsgTemplate = {
42: 0, // msg_unused
43: 1, // msg_simple
44: sizeof(msg_header_t), // msg_size
45: MSG_TYPE_NORMAL, // msg_type
46: PORT_NULL, // msg_local_port
47: PORT_NULL, // msg_remote_port - TO
48: // BE FILLED IN
49: IO_TIMEOUT_MSG // msg_id
50: };
51:
52:
53: @implementation AMD_SCSI
54:
55: /*
56: * Create and initialize one instance of AMD_SCSI. The work is done by
57: * architecture- and chip-specific modules.
58: */
59: + (BOOL)probe:deviceDescription
60: {
61: AMD_SCSI *inst = [self alloc];
62:
63:
64: if([inst archInit:deviceDescription] == nil) {
65: return NO;
66: }
67: else {
68: return YES;
69: }
70: }
71:
72: - free
73: {
74: commandBuf cmdBuf;
75:
76: /*
77: * First kill the I/O thread if running.
78: */
79: if(ioThreadRunning) {
80: cmdBuf.op = CO_Abort;
81: cmdBuf.scsiReq = NULL;
82: [self executeCmdBuf:&cmdBuf];
83: }
84:
85: if(commandLock) {
86: [commandLock free];
87: }
88: if(mdlFree) {
89: IOFree(mdlFree, MDL_SIZE * 2 * sizeof(vm_address_t));
90: }
91: return [super free];
92: }
93:
94: /*
95: * Our max DMA size is 64 K, derived from using 18 MDL entries (note the
96: * first and last entries can refer to chunks as small as 4 bytes).
97: */
98: - (unsigned)maxDmaSize
99: {
100: return AMD_DMA_PAGE_SIZE * (MDL_SIZE - 2);
101: }
102:
103: /*
104: * Return required DMA alignment for current architecture.
105: */
106: - (void)getDMAAlignment : (IODMAAlignment *)alignment;
107: {
108: alignment->readStart = AMD_READ_START_ALIGN;
109: alignment->writeStart = AMD_WRITE_START_ALIGN;
110: alignment->readLength = AMD_READ_LENGTH_ALIGN;
111: alignment->writeLength = AMD_WRITE_LENGTH_ALIGN;
112: }
113:
114: /*
115: * Statistics support.
116: */
117: - (unsigned int) numQueueSamples
118: {
119: return totalCommands;
120: }
121:
122:
123: - (unsigned int) sumQueueLengths
124: {
125: return queueLenTotal;
126: }
127:
128:
129: - (unsigned int) maxQueueLength
130: {
131: return maxQueueLen;
132: }
133:
134:
135: - (void)resetStats
136: {
137: totalCommands = 0;
138: queueLenTotal = 0;
139: maxQueueLen = 0;
140: }
141:
142: /*
143: * Do a SCSI command, as specified by an IOSCSIRequest. All the
144: * work is done by the I/O thread.
145: */
146: - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq
147: buffer : (void *)buffer
148: client : (vm_task_t)client
149: {
150: commandBuf cmdBuf;
151:
152: ddm_exp("executeRequest: cmdBuf 0x%x maxTransfer 0x%x\n",
153: &cmdBuf, scsiReq->maxTransfer,3,4,5);
154:
155: bzero(&cmdBuf, sizeof(commandBuf));
156: cmdBuf.op = CO_Execute;
157: cmdBuf.scsiReq = scsiReq;
158: cmdBuf.buffer = buffer;
159: cmdBuf.client = client;
160: scsiReq->driverStatus = SR_IOST_INVALID;
161:
162: [self executeCmdBuf:&cmdBuf];
163:
164: ddm_exp("executeRequest: cmdBuf 0x%x complete; driverStatus %s\n",
165: &cmdBuf, IOFindNameForValue(scsiReq->driverStatus,
166: IOScStatusStrings), 3,4,5);
167: return cmdBuf.scsiReq->driverStatus;
168: }
169:
170:
171: /*
172: * Reset the SCSI bus. All the work is done by the I/O thread.
173: */
174: - (sc_status_t)resetSCSIBus
175: {
176: commandBuf cmdBuf;
177:
178: ddm_exp("resetSCSIBus: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5);
179:
180: cmdBuf.op = CO_Reset;
181: cmdBuf.scsiReq = NULL;
182: [self executeCmdBuf:&cmdBuf];
183: ddm_exp("resetSCSIBus: cmdBuf 0x%x DONE\n", &cmdBuf, 2,3,4,5);
184: return SR_IOST_GOOD; // can not fail
185: }
186:
187: /*
188: * The following 6 methods are all called from the I/O thread in
189: * IODirectDevice.
190: */
191:
192: /*
193: * Called from the I/O thread when it receives an interrupt message.
194: * Currently all work is done by chip-specific module; maybe we should
195: * put this method there....
196: */
197: - (void)interruptOccurred
198: {
199: #if DDM_DEBUG
200: /*
201: * calculate interrupt service time if enabled.
202: */
203: ns_time_t startTime, endTime, elapsedNs;
204: unsigned elapsedUs = 0;
205:
206: if(IODDMMasks[AMD_DDM_INDEX] & DDM_INTR) {
207: IOGetTimestamp(&startTime);
208: }
209: ddm_thr("interruptOccurred: TOP\n", 1,2,3,4,5);
210: #endif DDM_DEBUG
211:
212: [self hwInterrupt];
213:
214: #if DDM_DEBUG
215: if(IODDMMasks[AMD_DDM_INDEX] & DDM_INTR) {
216: IOGetTimestamp(&endTime);
217: elapsedNs = endTime - startTime;
218: elapsedUs = (unsigned)((elapsedNs + 999ULL) / 1000ULL);
219: }
220: ddm_intr("interruptOccurred: DONE; elapsed time %d us\n",
221: elapsedUs, 2,3,4,5);
222: #endif DDM_DEBUG
223: }
224:
225: /*
226: * These three should not occur; they are here as error traps. All three are
227: * called out from the I/O thread upon receipt of messages which it should
228: * not be seeing.
229: */
230: - (void)interruptOccurredAt:(int)localNum
231: {
232: IOLog("%s: interruptOccurredAt:%d\n", [self name], localNum);
233: }
234:
235: - (void)otherOccurred:(int)id
236: {
237: IOLog("%s: otherOccurred:%d\n", [self name], id);
238: }
239:
240: - (void)receiveMsg
241: {
242: IOLog("%s: receiveMsg\n", [self name]);
243:
244: /*
245: * We have to let IODirectDevice take care of this (i.e., dequeue the
246: * bogus message).
247: */
248: [super receiveMsg];
249: }
250:
251: /*
252: * Used in -timeoutOccurred to determine if specified cmdBuf has timed out.
253: * Returns YES if timeout, else NO.
254: */
255: static inline BOOL
256: isCmdTimedOut(commandBuf *cmdBuf, ns_time_t now)
257: {
258: IOSCSIRequest *scsiReq;
259: ns_time_t expire;
260:
261: scsiReq = cmdBuf->scsiReq;
262: expire = cmdBuf->startTime +
263: (1000000000ULL * (unsigned long long)scsiReq->timeoutLength);
264: return ((now > expire) ? YES : NO);
265: }
266:
267: /*
268: * Called from the I/O thread when it receives a timeout
269: * message. We send these messages ourself from AMDTimeout().
270: */
271: - (void)timeoutOccurred
272: {
273: ns_time_t now;
274: BOOL cmdTimedOut = NO;
275: commandBuf *cmdBuf;
276: commandBuf *nextCmdBuf;
277:
278: ddm_thr("timeoutOccurred: TOP\n", 1,2,3,4,5);
279: IOGetTimestamp(&now);
280:
281: /*
282: * Scan activeCmd and disconnectQ looking for tardy I/Os.
283: */
284: if(activeCmd) {
285: cmdBuf = activeCmd;
286: if(isCmdTimedOut(cmdBuf, now)) {
287: ddm_thr("activeCmd TIMEOUT, cmd 0x%x\n",
288: cmdBuf, 2,3,4,5);
289: activeCmd = NULL;
290: ASSERT(cmdBuf->scsiReq != NULL);
291: cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO;
292: [self ioComplete:cmdBuf];
293: cmdTimedOut = YES;
294: }
295: }
296:
297: cmdBuf = (commandBuf *)queue_first(&disconnectQ);
298: while(!queue_end(&disconnectQ, (queue_entry_t)cmdBuf)) {
299: if(isCmdTimedOut(cmdBuf, now)) {
300: ddm_thr("disconnected cmd TIMEOUT, cmd 0x%x\n",
301: cmdBuf, 2,3,4,5);
302:
303: /*
304: * Remove cmdBuf from disconnectQ and
305: * complete it.
306: */
307: nextCmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
308: queue_remove(&disconnectQ, cmdBuf, commandBuf *, link);
309: ASSERT(cmdBuf->scsiReq != NULL);
310: cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO;
311: [self ioComplete:cmdBuf];
312: cmdBuf = nextCmdBuf;
313: cmdTimedOut = YES;
314: }
315: else {
316: cmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
317: }
318: }
319:
320: /*
321: * Reset bus. This also completes all I/Os in disconnectQ with
322: * status CS_Reset.
323: */
324: if(cmdTimedOut) {
325: [self logRegs];
326: [self threadResetBus:NULL];
327: }
328: ddm_thr("timeoutOccurred: DONE\n", 1,2,3,4,5);
329: }
330:
331: /*
332: * Process all commands in commandQ. At most one of these will become
333: * activeCmd. The remainder of CO_Execute commands go to pendingQ. Other
334: * types of commands are executed immediately.
335: */
336: - (void)commandRequestOccurred
337: {
338: commandBuf *cmdBuf;
339: commandBuf *pendCmd;
340:
341: ddm_thr("commandRequestOccurred: top\n", 1,2,3,4,5);
342: [commandLock lock];
343: while(!queue_empty(&commandQ)) {
344: cmdBuf = (commandBuf *) queue_first(&commandQ);
345: queue_remove(&commandQ, cmdBuf, commandBuf *, link);
346: [commandLock unlock];
347:
348: switch(cmdBuf->op) {
349: case CO_Reset:
350: /*
351: * Note all active and disconnected commands will
352: * be terminted.
353: */
354: [self threadResetBus:"Reset Command Received"];
355: [self ioComplete:cmdBuf];
356: break;
357:
358: case CO_Abort:
359: /*
360: * 1. Abort all active, pending, and disconnected
361: * commands.
362: * 2. Notify caller of completion.
363: * 3. Self-terminate.
364: */
365: [self swAbort:SR_IOST_INT];
366: pendCmd = (commandBuf *)queue_first(&pendingQ);
367: while(!queue_end(&pendingQ,
368: (queue_entry_t)pendCmd)) {
369: pendCmd->scsiReq->driverStatus = SR_IOST_INT;
370: [self ioComplete:pendCmd];
371: pendCmd = (commandBuf *)
372: queue_next(&pendCmd->link);
373: }
374: [cmdBuf->cmdLock lock];
375: [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
376: IOExitThread();
377: /* not reached */
378:
379: case CO_Execute:
380: [self threadExecuteRequest:cmdBuf];
381: break;
382:
383: }
384: [commandLock lock];
385: }
386: [commandLock unlock];
387: ddm_thr("commandRequestOccurred: DONE\n", 1,2,3,4,5);
388: return;
389: }
390:
391: /*
392: * Power management methods. All we care about is power off, when we must
393: * reset the SCSI bus due to the Compaq BIOS's lack of a SCSI reset, which
394: * causes a hang if we have set up targets for sync data transfer mode.
395: */
396: - (IOReturn)getPowerState:(PMPowerState *)state_p
397: {
398: ddm_exp("getPowerState called\n", 1,2,3,4,5);
399: return IO_R_UNSUPPORTED;
400: }
401:
402: - (IOReturn)setPowerState:(PMPowerState)state
403: {
404: #ifdef DEBUG
405: IOLog("%s: received setPowerState: with %x\n", [self name],
406: (unsigned)state);
407: #endif DEBUG
408: if (state == PM_OFF) {
409: [self scsiReset];
410: return IO_R_SUCCESS;
411: }
412: return IO_R_UNSUPPORTED;
413: }
414:
415: - (IOReturn)getPowerManagement:(PMPowerManagementState *)state_p
416: {
417: ddm_exp("getPowerManagement called\n", 1,2,3,4,5);
418: return IO_R_UNSUPPORTED;
419: }
420:
421: - (IOReturn)setPowerManagement:(PMPowerManagementState)state
422: {
423: ddm_exp("setPowerManagement called\n", 1,2,3,4,5);
424: return IO_R_UNSUPPORTED;
425: }
426:
427: #if AMD_ENABLE_GET_SET
428:
429: - (IOReturn)setIntValues:(unsigned *)parameterArray
430: forParameter:(IOParameterName)parameterName
431: count:(unsigned int)count
432: {
433: if(strcmp(parameterName, AMD_AUTOSENSE) == 0) {
434: if (count != 1) {
435: return IO_R_INVALID_ARG;
436: }
437: autoSenseEnable = (parameterArray[0] ? 1 : 0);
438: IOLog("%s: autoSense %s\n", [self name],
439: (autoSenseEnable ? "Enabled" : "Disabled"));
440: return IO_R_SUCCESS;
441: }
442: else if(strcmp(parameterName, AMD_CMD_QUEUE) == 0) {
443: if (count != 1) {
444: return IO_R_INVALID_ARG;
445: }
446: cmdQueueEnable = (parameterArray[0] ? 1 : 0);
447: IOLog("%s: cmdQueue %s\n", [self name],
448: (cmdQueueEnable ? "Enabled" : "Disabled"));
449: return IO_R_SUCCESS;
450: }
451: else if(strcmp(parameterName, AMD_SYNC) == 0) {
452: if (count != 1) {
453: return IO_R_INVALID_ARG;
454: }
455: syncModeEnable = (parameterArray[0] ? 1 : 0);
456: IOLog("%s: syncMode %s\n", [self name],
457: (syncModeEnable ? "Enabled" : "Disabled"));
458: return IO_R_SUCCESS;
459: }
460: else if(strcmp(parameterName, AMD_FAST_SCSI) == 0) {
461: if (count != 1) {
462: return IO_R_INVALID_ARG;
463: }
464: fastModeEnable = (parameterArray[0] ? 1 : 0);
465: IOLog("%s: fastMode %s\n", [self name],
466: (fastModeEnable ? "Enabled" : "Disabled"));
467: return IO_R_SUCCESS;
468: }
469: else if(strcmp(parameterName, AMD_RESET_TARGETS) == 0) {
470: int target;
471: perTargetData *perTargetPtr;
472:
473: if (count != 0) {
474: return IO_R_INVALID_ARG;
475: }
476:
477: /*
478: * Re-enable sync and command queueing. The
479: * disable bits persist after a reset.
480: */
481: for(target=0; target<SCSI_NTARGETS; target++) {
482: perTargetPtr = &perTarget[target];
483: perTargetPtr->cmdQueueDisable = 0;
484: perTargetPtr->syncDisable = 0;
485: perTargetPtr->maxQueue = 0;
486: }
487: IOLog("%s: Per Target disable flags cleared\n", [self name]);
488: return IO_R_SUCCESS;
489: }
490: else {
491: return [super setIntValues:parameterArray
492: forParameter:parameterName
493: count:count];
494: }
495: }
496:
497: - (IOReturn)getIntValues : (unsigned *)parameterArray
498: forParameter : (IOParameterName)parameterName
499: count : (unsigned *)count; // in/out
500: {
501: if(strcmp(parameterName, AMD_AUTOSENSE) == 0) {
502: if(*count != 1) {
503: return IO_R_INVALID_ARG;
504: }
505: parameterArray[0] = autoSenseEnable;
506: return IO_R_SUCCESS;
507: }
508: else if(strcmp(parameterName, AMD_CMD_QUEUE) == 0) {
509: if(*count != 1) {
510: return IO_R_INVALID_ARG;
511: }
512: parameterArray[0] = cmdQueueEnable;
513: return IO_R_SUCCESS;
514: }
515: else if(strcmp(parameterName, AMD_SYNC) == 0) {
516: if(*count != 1) {
517: return IO_R_INVALID_ARG;
518: }
519: parameterArray[0] = syncModeEnable;
520: return IO_R_SUCCESS;
521: }
522: else if(strcmp(parameterName, AMD_FAST_SCSI) == 0) {
523: if(*count != 1) {
524: return IO_R_INVALID_ARG;
525: }
526: parameterArray[0] = fastModeEnable;
527: return IO_R_SUCCESS;
528: }
529: else {
530: return [super getIntValues : parameterArray
531: forParameter : parameterName
532: count : count];
533:
534: }
535: }
536:
537:
538: #endif AMD_ENABLE_GET_SET
539:
540: @end /* AMD_SCSI */
541:
542: @implementation AMD_SCSI(Private)
543:
544: /*
545: * Private chip- and architecture-independent methods.
546: */
547:
548: /*
549: * Pass one commandBuf to the I/O thread; wait for completion.
550: * Normal completion status is in cmdBuf->scsiReq->driverStatus;
551: * a non-zero return from this function indicates a Mach IPC error.
552: *
553: * This method allocates and frees cmdBuf->cmdLock.
554: */
555: - (IOReturn)executeCmdBuf : (commandBuf *)cmdBuf
556: {
557: msg_header_t msg = cmdMessageTemplate;
558: kern_return_t krtn;
559: IOReturn rtn = IO_R_SUCCESS;
560:
561: cmdBuf->cmdPendingSense = NULL;
562: cmdBuf->active = 0;
563: cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING];
564: [commandLock lock];
565: queue_enter(&commandQ, cmdBuf, commandBuf *, link);
566: [commandLock unlock];
567:
568: /*
569: * Create a Mach message and send it in order to wake up the
570: * I/O thread.
571: */
572: msg.msg_remote_port = interruptPortKern;
573: krtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
574: if(krtn) {
575: IOLog("%s: msg_send_from_kernel() returned %d\n",
576: [self name], krtn);
577: rtn = IO_R_IPC_FAILURE;
578: goto out;
579: }
580:
581: /*
582: * Wait for I/O complete.
583: */
584: ddm_exp("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n",
585: cmdBuf, 2,3,4,5);
586: [cmdBuf->cmdLock lockWhen:CMD_COMPLETE];
587: out:
588: [cmdBuf->cmdLock free];
589: return rtn;
590: }
591:
592: /*
593: * Abort all active and disconnected commands with specified status. No
594: * hardware action. Currently used by threadResetBus and during processing
595: * of a CO_Abort command.
596: */
597: - (void)swAbort : (sc_status_t)status
598: {
599: commandBuf *cmdBuf;
600: commandBuf *nextCmdBuf;
601:
602: ddm_thr("swAbort\n", 1,2,3,4,5);
603: if(activeCmd) {
604: activeCmd->scsiReq->driverStatus = status;
605: [self ioComplete:activeCmd];
606: activeCmd = NULL;
607: }
608: cmdBuf = (commandBuf *)queue_first(&disconnectQ);
609: while(!queue_end(&disconnectQ, (queue_entry_t)cmdBuf)) {
610: queue_remove(&disconnectQ, cmdBuf, commandBuf *, link);
611: nextCmdBuf = (commandBuf *)
612: queue_next(&cmdBuf->link);
613: cmdBuf->scsiReq->driverStatus = status;
614: [self ioComplete:cmdBuf];
615: cmdBuf = nextCmdBuf;
616: }
617: #ifdef DEBUG
618: /*
619: * activeArray "should be" empty...if not, make sure it is for debug.
620: */
621: {
622: int target, lun;
623: int active;
624:
625: for(target=0; target<SCSI_NTARGETS; target++) {
626: for(lun=0; lun<SCSI_NLUNS; lun++) {
627: active = activeArray[target][lun];
628: if(active) {
629: IOLog("swAbort: activeArray[%d][%d] = %d\n",
630: target, lun, active);
631: activeCount -= active;
632: activeArray[target][lun] = 0;
633: }
634: }
635: }
636: if(activeCount != 0) {
637: IOLog("swAbort: activeCount = %d\n", activeCount);
638: activeCount = 0;
639: }
640: }
641: #endif DEBUG
642: }
643:
644: /*
645: * Abort all active and disconnected commands with status SR_IOST_RESET.
646: * Reset hardware and SCSI bus. If there is a command in pendingQ, start
647: * it up.
648: */
649: - (void)threadResetBus : (const char *)reason
650: {
651: [self swAbort:SR_IOST_RESET];
652: [self hwReset : reason];
653: [self busFree];
654: }
655:
656: /*
657: * Commence processing of the specified command.
658: *
659: * If activeCmd is non-NULL or cmdBufOK says we can't process this command,
660: * we just enqueue the command on the end of pendingQ.
661: */
662: - (void)threadExecuteRequest : (commandBuf *)cmdBuf
663: {
664: #if DDM_DEBUG
665: unsigned char target = cmdBuf->scsiReq->target;
666: unsigned char lun = cmdBuf->scsiReq->lun;
667: #endif DDM_DEBUG
668:
669: if(activeCmd != NULL) {
670: ddm_thr("threadExecuteRequest: ACTIVE; adding 0x%x to "
671: "pendingQ\n", cmdBuf, 2,3,4,5);
672: queue_enter(&pendingQ, cmdBuf, commandBuf *, link);
673: return;
674: }
675: else if([self cmdBufOK:cmdBuf] == NO) {
676: ddm_thr("threadExecuteRequest: !cmdBufOK; adding 0x%x to "
677: "pendingQ\n", cmdBuf, 2,3,4,5);
678: queue_enter(&pendingQ, cmdBuf, commandBuf *, link);
679: return;
680: }
681:
682: ddm_thr("calling hwStart: cmdBuf 0x%x activeArray[%d][%d] = %d\n",
683: cmdBuf, target, lun, activeArray[target][lun], 5);
684:
685: switch([self hwStart:cmdBuf]) {
686: case HWS_OK: // cool
687: case HWS_BUSY: // h/w can't take cmd now
688: break;
689: case HWS_REJECT: // hw ready for new cmd
690: ddm_thr("threadExecuteRequest: calling busFree\n", 1,2,3,4,5);
691: [self busFree];
692: }
693:
694: ddm_thr("threadExecuteRequest(0x%x): DONE\n", cmdBuf, 2,3,4,5);
695: }
696:
697: /*
698: * Methods called by hardware-dependent modules.
699: */
700:
701: #if TEST_QUEUE_FULL
702: int testQueueFull;
703: #endif TEST_QUEUE_FULL
704:
705: /*
706: * Called when a transaction associated with cmdBuf is complete. Notify
707: * waiting thread. If cmdBuf->scsiReq exists (i.e., this is not a reset
708: * or an abort), scsiReq->driverStatus must be valid. If cmdBuf is active,
709: * caller must remove from activeCmd. We decrement activeArray[][] counter
710: * if appropriate.
711: */
712: - (void)ioComplete:(commandBuf *)cmdBuf
713: {
714: ns_time_t currentTime;
715: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
716: int target;
717: int lun;
718:
719: if(cmdBuf->cmdPendingSense != NULL) {
720: /*
721: * This was an autosense request.
722: */
723:
724: commandBuf *origCmdBuf = cmdBuf->cmdPendingSense;
725: esense_reply_t *alignedSense = cmdBuf->buffer;
726:
727: ASSERT(cmdBuf->scsiReq != NULL);
728: ddm_thr("autosense buf 0x%x complete for cmd 0x%x sense "
729: "key 0x%x\n",
730: cmdBuf, origCmdBuf, alignedSense->er_sensekey, 4,5);
731: if(cmdBuf->scsiReq->driverStatus == SR_IOST_GOOD) {
732: /*
733: * Copy aligned sense data to caller's buffer.
734: */
735: alignedSense = cmdBuf->buffer;
736: origCmdBuf->scsiReq->senseData = *alignedSense;
737: origCmdBuf->scsiReq->driverStatus = SR_IOST_CHKSV;
738: }
739: else {
740: IOLog("AMD53C974: Autosense request for target %d"
741: " FAILED (%s)\n",
742: cmdBuf->scsiReq->target,
743: IOFindNameForValue(scsiReq->driverStatus,
744: IOScStatusStrings));
745: origCmdBuf->scsiReq->driverStatus = SR_IOST_CHKSNV;
746: }
747:
748: /*
749: * Free all of the allocated memory associated with
750: * this autosense request.
751: */
752: [self deactivateCmd:cmdBuf];
753: IOFree(cmdBuf->scsiReq, sizeof(*cmdBuf->scsiReq));
754: IOFree(cmdBuf->unalignedSense,
755: sizeof(esense_reply_t) + (2 * AMD_READ_START_ALIGN));
756: IOFree(cmdBuf, sizeof(commandBuf));
757:
758: /*
759: * Now complete the I/O for the original commandBuf.
760: */
761: [origCmdBuf->cmdLock lock];
762: [origCmdBuf->cmdLock unlockWith:YES];
763: return;
764: }
765: if(scsiReq != NULL) {
766: IOGetTimestamp(¤tTime);
767: scsiReq->totalTime = currentTime - cmdBuf->startTime;
768: scsiReq->bytesTransferred =
769: scsiReq->maxTransfer - cmdBuf->currentByteCount;
770:
771: /*
772: * Catch bad SCSI status now.
773: */
774: if(scsiReq->driverStatus == SR_IOST_GOOD) {
775: #if TEST_QUEUE_FULL
776: if(testQueueFull &&
777: (activeArray[scsiReq->target][scsiReq->lun] > 1)) {
778: scsiReq->scsiStatus = STAT_QUEUE_FULL;
779: testQueueFull = 0;
780: }
781: #endif TEST_QUEUE_FULL
782: switch(scsiReq->scsiStatus) {
783: case STAT_GOOD:
784: break;
785: case STAT_CHECK:
786: if(autoSenseEnable) {
787: /*
788: * Generate an autosense request, enqueue
789: * on pendingQ.
790: */
791: [self generateAutoSense:cmdBuf];
792: if(cmdBuf->active) {
793: [self deactivateCmd:cmdBuf];
794: }
795: return;
796: }
797: else {
798: scsiReq->driverStatus = SR_IOST_CHKSNV;
799: }
800: break;
801:
802: case STAT_QUEUE_FULL:
803: /*
804: * Avoid notifying client of this condition;
805: * update perTarget.maxQueue and place this
806: * request on pendingQ. We'll try this
807: * again when we ioComplete at least one
808: * command in this target's queue.
809: */
810: if(cmdBuf->queueTag == QUEUE_TAG_NONTAGGED) {
811: /*
812: * Huh? We're not doing command
813: * queueing...
814: */
815: scsiReq->driverStatus = SR_IOST_BADST;
816: break;
817: }
818: target = scsiReq->target;
819: lun = scsiReq->lun;
820: if(cmdBuf->active) {
821: [self deactivateCmd:cmdBuf];
822: }
823: perTarget[target].maxQueue =
824: activeArray[target][lun];
825: ddm_thr("Target %d QUEUE FULL, maxQueue %d\n",
826: target, perTarget[target].maxQueue,
827: 3,4,5);
828: queue_enter(&pendingQ, cmdBuf, commandBuf *,
829: link);
830: return;
831:
832: default:
833: scsiReq->driverStatus = SR_IOST_BADST;
834: break;
835: }
836: }
837: }
838: if(cmdBuf->active) {
839: /*
840: * Note that the active flag is false for non-CO_Execute
841: * commands and commands aborted from pendingQ.
842: */
843: [self deactivateCmd:cmdBuf];
844: }
845:
846: #if DDM_DEBUG
847: {
848: const char *status;
849: unsigned moved;
850:
851: if(scsiReq != NULL) {
852: status = IOFindNameForValue(scsiReq->driverStatus,
853: IOScStatusStrings);
854: moved = scsiReq->bytesTransferred;
855: }
856: else {
857: status = "Complete";
858: moved = 0;
859: }
860: ddm_thr("ioComplete: cmdBuf 0x%x status %s bytesXfr 0x%x\n",
861: cmdBuf, status, moved,4,5);
862: }
863: #endif DDM_DEBUG
864:
865: [cmdBuf->cmdLock lock];
866: [cmdBuf->cmdLock unlockWith:YES];
867: }
868:
869: /*
870: * Generate autosense request for specified cmdBuf, place it
871: * at head of pendingQ.
872: */
873: - (void)generateAutoSense : (commandBuf *)cmdBuf
874: {
875: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
876: commandBuf *senseCmdBuf;
877: IOSCSIRequest *senseScsiReq;
878: cdb_6_t *cdbp;
879:
880: senseCmdBuf = IOMalloc(sizeof(commandBuf));
881: senseScsiReq = IOMalloc(sizeof(IOSCSIRequest));
882: bzero(senseCmdBuf, sizeof(commandBuf));
883: bzero(senseScsiReq, sizeof(IOSCSIRequest));
884:
885: /*
886: * commandBuf fields....
887: */
888: senseCmdBuf->cmdPendingSense = cmdBuf;
889: senseCmdBuf->op = CO_Execute;
890: senseCmdBuf->scsiReq = senseScsiReq;
891:
892: /*
893: * Get aligned sense buffer.
894: */
895: senseCmdBuf->unalignedSense = IOMalloc(sizeof(esense_reply_t) +
896: (2 * AMD_READ_START_ALIGN));
897: senseCmdBuf->buffer = IOAlign(void *,
898: senseCmdBuf->unalignedSense,
899: AMD_READ_START_ALIGN);
900: senseCmdBuf->client = IOVmTaskSelf();
901:
902: /*
903: * Now IOSCSIRequest fields for request sense.
904: */
905: senseScsiReq->target = scsiReq->target;
906: senseScsiReq->lun = scsiReq->lun;
907: senseScsiReq->read = YES;
908: senseScsiReq->maxTransfer = sizeof(esense_reply_t);
909: senseScsiReq->timeoutLength = 10;
910: senseScsiReq->disconnect = 0;
911:
912: cdbp = &senseScsiReq->cdb.cdb_c6;
913: cdbp->c6_opcode = C6OP_REQSENSE;
914: cdbp->c6_lun = scsiReq->lun;
915: cdbp->c6_len = sizeof(esense_reply_t);
916: senseScsiReq->driverStatus = SR_IOST_INVALID;
917:
918: /*
919: * This goes at the head of pendingQ; hopefully it'll be the
920: * next command out to the bus.
921: */
922: ddm_thr("generateAutoSense: autosense buf 0x%x enqueued for "
923: "cmdBuf 0x%x\n", senseCmdBuf, cmdBuf, 3,4,5);
924: queue_enter_first(&pendingQ, senseCmdBuf, commandBuf *, link);
925:
926: }
927:
928: /*
929: * I/O associated with activeCmd has disconnected. Place it on disconnectQ
930: * and enable another transaction.
931: */
932: - (void)disconnect
933: {
934: ddm_thr("DISCONNECT: cmdBuf 0x%x target %d lun %d tag %d\n",
935: activeCmd, activeCmd->scsiReq->target,
936: activeCmd->scsiReq->lun, activeCmd->queueTag, 5);
937: queue_enter(&disconnectQ,
938: activeCmd,
939: commandBuf *,
940: link);
941: #if DDM_DEBUG
942: if((activeCmd->currentByteCount != activeCmd->scsiReq->maxTransfer) &&
943: (activeCmd->currentByteCount != 0)) {
944: ddm_thr("disconnect after partial DMA (max 0x%d curr 0x%x)\n",
945: activeCmd->scsiReq->maxTransfer,
946: activeCmd->currentByteCount, 3,4,5);
947: }
948: #endif DDM_DEBUG
949: /*
950: * Record this time so that activeCmd can be billed for
951: * disconnect latency at reselect time.
952: */
953: IOGetTimestamp(&activeCmd->disconnectTime);
954: activeCmd = NULL;
955: /* [self busFree]; NO! fsm does this at end of hwInterrupt! */
956: }
957:
958: /*
959: * Specified target, lun, and queueTag is trying to reselect. If we have
960: * a commandBuf for this TLQ nexus on disconnectQ, remove it, make it the
961: * current activeCmd, and return YES. Else return NO.
962: * A value of zero for queueTag indicates a nontagged command (zero is never
963: * used as the queue tag value for a tagged command).
964: */
965: - (BOOL)reselect : (unsigned char)target_id
966: lun : (unsigned char)lun
967: queueTag : (unsigned char)queueTag
968: {
969: commandBuf *cmdBuf;
970: IOSCSIRequest *scsiReq;
971: ns_time_t currentTime;
972:
973: cmdBuf = (commandBuf *)queue_first(&disconnectQ);
974: while(!queue_end(&disconnectQ, (queue_t)cmdBuf)) {
975:
976: scsiReq = cmdBuf->scsiReq;
977: if((scsiReq->target == target_id) &&
978: (scsiReq->lun == lun) &&
979: (cmdBuf->queueTag == queueTag)) {
980: ddm_thr("RESELECT: target %d lun %d tag %d FOUND;"
981: "cmdBuf 0x%x\n",
982: target_id, lun, queueTag, cmdBuf, 5);
983: queue_remove(&disconnectQ,
984: cmdBuf,
985: commandBuf *,
986: link);
987: activeCmd = cmdBuf;
988:
989: /*
990: * Bill this operation for latency time.
991: */
992: IOGetTimestamp(¤tTime);
993: scsiReq->latentTime +=
994: (currentTime - activeCmd->disconnectTime);
995: return(YES);
996: }
997: /*
998: * Try next element in queue.
999: */
1000: cmdBuf = (commandBuf *)cmdBuf->link.next;
1001: }
1002:
1003: /*
1004: * Hmm...this is not good! We don't want to talk to this target.
1005: */
1006: IOLog("%s: ILLEGAL RESELECT target %d lun %d tag %d\n",
1007: [self name], target_id, lun, queueTag);
1008: return(NO);
1009: }
1010:
1011: /*
1012: * Determine if activeArray[][], maxQueue, cmdQueueEnable, and a
1013: * command's target and lun show that it's OK to start processing cmdBuf.
1014: * Returns YES if copacetic.
1015: */
1016: - (BOOL)cmdBufOK : (commandBuf *)cmdBuf
1017: {
1018: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
1019: unsigned target = scsiReq->target;
1020: unsigned lun = scsiReq->lun;
1021: unsigned char active;
1022: unsigned char maxQ;
1023:
1024: active = activeArray[target][lun];
1025: if(active == 0) {
1026: /*
1027: * Trivial quiescent case, always OK.
1028: */
1029: return YES;
1030: }
1031: if((cmdQueueEnable == 0) ||
1032: (perTarget[target].cmdQueueDisable)) {
1033: /*
1034: * No command queueing (either globally or for this target),
1035: * only one at a time.
1036: */
1037: return NO;
1038: }
1039: maxQ = perTarget[target].maxQueue;
1040: if(maxQ == 0) {
1041: /*
1042: * We don't know what the target's limit is; go for it.
1043: */
1044: return YES;
1045: }
1046: if(active >= maxQ) {
1047: /*
1048: * T/L's queue full; hold off.
1049: */
1050: return NO;
1051: }
1052: else {
1053: return YES;
1054: }
1055: }
1056:
1057: /*
1058: * The bus has gone free. Start up a command from pendingQ, if any, and
1059: * if allowed by cmdQueueEnable and activeArray[][].
1060: */
1061: - (void)busFree
1062: {
1063: commandBuf *cmdBuf;
1064:
1065: ASSERT(activeCmd == NULL);
1066: if(queue_empty(&pendingQ)) {
1067: ddm_thr("busFree: pendingQ empty\n", 1,2,3,4,5);
1068: return;
1069: }
1070:
1071: /*
1072: * Attempt to find a commandBuf in pendingQ which we are in a position
1073: * to process.
1074: */
1075: cmdBuf = (commandBuf *)queue_first(&pendingQ);
1076: while(!queue_end(&pendingQ, (queue_entry_t)cmdBuf)) {
1077: if([self cmdBufOK:cmdBuf]) {
1078: queue_remove(&pendingQ, cmdBuf, commandBuf *, link);
1079: ddm_thr("busFree: starting pending cmd 0x%x\n", cmdBuf,
1080: 2,3,4,5);
1081: [self threadExecuteRequest:cmdBuf];
1082: return;
1083: }
1084: else {
1085: cmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
1086: }
1087: }
1088: ddm_thr("busFree: pendingQ non-empty, no commands available\n",
1089: 1,2,3,4,5);
1090: }
1091:
1092: /*
1093: * Abort activeCmd (if any) and any disconnected I/Os (if any) and reset
1094: * the bus due to gross hardware failure.
1095: * If activeCmd is valid, its scsiReq->driverStatus will be set to 'status'.
1096: */
1097: - (void)hwAbort : (sc_status_t)status
1098: reason : (const char *)reason
1099: {
1100: if(activeCmd) {
1101: activeCmd->scsiReq->driverStatus = status;
1102: [self ioComplete:activeCmd];
1103: activeCmd = NULL;
1104: }
1105: [self logRegs];
1106: [self threadResetBus:reason];
1107: }
1108:
1109: /*
1110: * Called by chip level to indicate that a command has gone out to the
1111: * hardware.
1112: */
1113: - (void)activateCommand : (commandBuf *)cmdBuf
1114: {
1115: unsigned char target;
1116: unsigned char lun;
1117:
1118: /*
1119: * Start timeout timer for this I/O. The timer request is cancelled
1120: * in ioComplete.
1121: */
1122: cmdBuf->timeoutPort = interruptPortKern;
1123: #if LONG_TIMEOUT
1124: cmdBuf->scsiReq->timeoutLength = OUR_TIMEOUT;
1125: #endif LONG_TIMEOUT
1126: IOScheduleFunc(AMDTimeout, cmdBuf, cmdBuf->scsiReq->timeoutLength);
1127:
1128: /*
1129: * This is the only place where an activeArray[][] counter is
1130: * incremented (and, hence, the only place where cmdBuf->active is
1131: * set). The only other place activeCmd is set to non-NULL
1132: * is in reselect:lun:queueTag.
1133: */
1134: activeCmd = cmdBuf;
1135: target = cmdBuf->scsiReq->target;
1136: lun = cmdBuf->scsiReq->lun;
1137: activeArray[target][lun]++;
1138: activeCount++;
1139: cmdBuf->active = 1;
1140:
1141: /*
1142: * Accumulate statistics.
1143: */
1144: maxQueueLen = MAX(maxQueueLen, activeCount);
1145: queueLenTotal += activeCount;
1146: totalCommands++;
1147: ddm_thr("activateCommand: cmdBuf 0x%x target %d lun %d\n",
1148: cmdBuf, target, lun, 4,5);
1149: }
1150:
1151: /*
1152: * Remove specified cmdBuf from "active" status. Update activeArray,
1153: * activeCount, and unschedule pending timer.
1154: */
1155: - (void)deactivateCmd : (commandBuf *)cmdBuf
1156: {
1157: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
1158: int target, lun;
1159:
1160: ASSERT(scsiReq != NULL);
1161: target = scsiReq->target;
1162: lun = scsiReq->lun;
1163: ddm_thr("deactivate cmdBuf 0x%x target %d lun %d activeArray %d\n",
1164: cmdBuf, target, lun, activeArray[target][lun], 5);
1165: ASSERT(activeArray[target][lun] != 0);
1166: activeArray[target][lun]--;
1167: ASSERT(activeCount != 0);
1168: activeCount--;
1169:
1170: /*
1171: * Cancel pending timeout request. Commands which timed out don't
1172: * have a timer request pending anymore.
1173: */
1174: if(scsiReq->driverStatus != SR_IOST_IOTO) {
1175: IOUnscheduleFunc(AMDTimeout, cmdBuf);
1176: }
1177: cmdBuf->active = 0;
1178: }
1179:
1180: @end /* AMD_SCSI(Private) */
1181:
1182: /*
1183: * Handle timeouts. We just send a timeout message to the I/O thread
1184: * so it wakes up.
1185: */
1186: static void AMDTimeout(void *arg)
1187: {
1188: commandBuf *cmdBuf = arg;
1189: msg_header_t msg = timeoutMsgTemplate;
1190:
1191: ddm_err("AMDTimeout: cmdBuf 0x%x target %d\n", cmdBuf,
1192: cmdBuf->scsiReq->target, 3,4,5);
1193: if(!cmdBuf->active) {
1194: /*
1195: * Should never happen...
1196: */
1197: IOLog("AMD53C974: Timeout on non-active cmdBuf\n");
1198: return;
1199: }
1200: msg.msg_remote_port = cmdBuf->timeoutPort;
1201: IOLog("AMD53C974: SCSI Timeout\n");
1202: (void) msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
1203: }
1204:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.