|
|
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: /* Copyright (c) 1991 NeXT Computer, Inc. All rights reserved.
25: *
26: * SCSIDiskThread.m - Implementation of SCSIDisk I/O thread and its associated
27: * methods. All communication with the SCSIController object
28: * is done here.
29: *
30: * HISTORY
31: * 04-Mar-91 Doug Mitchell at NeXT
32: * Created.
33: */
34:
35: /*
36: * Enforce clean driverkit implementation.
37: */
38: #define MACH_USER_API 1
39: #undef KERNEL_PRIVATE
40:
41: #import <sys/types.h>
42: #import <kernserv/prototypes.h>
43: #import <driverkit/SCSIDiskPrivate.h>
44: #import <driverkit/SCSIDiskTypes.h>
45: #import <driverkit/SCSIDiskThread.h>
46: #import <driverkit/SCSIStructInlines.h>
47: #import <bsd/dev/scsireg.h>
48: #import <driverkit/xpr_mi.h>
49: #import <driverkit/volCheck.h>
50: #import <driverkit/kernelDiskMethods.h>
51: #import <kern/assert.h>
52: #import <machkit/NXLock.h>
53: #import <driverkit/align.h>
54:
55: static void sdThreadDequeue(SCSIDisk *sdp,
56: BOOL needs_disk);
57:
58: @implementation SCSIDisk(Thread)
59:
60: /*
61: * Execute one I/O, as specified in sdBuf. Called out from an I/O thread.
62: *
63: * This is certainly the most complicated part of the SCSIDisk object; all
64: * retry logic is done here. It's kind of big. Maybe we can break this up
65: * a little bit.
66: */
67: - (void)doSdBuf : (sdBuf_t *)sdBuf
68: {
69: IOSCSIRequest scsiReq;
70: sc_status_t scrtn = SR_IOST_INVALID;
71: IOReturn iortn;
72: esense_reply_t senseReply;
73: const char *name = [self name];
74:
75: xpr_sd("sd%d doSdBuf: sdBuf 0x%x\n", [self unit], sdBuf, 3,4,5);
76:
77: /*
78: * Initialize retry counters.
79: */
80: sdBuf->busy_retry = SD_RETRY_BUSY;
81: sdBuf->norm_retry = SD_RETRY_NORM;
82: sdBuf->notRdy_retry = SD_RETRY_NOTRDY;
83:
84: while(sdBuf->busy_retry && sdBuf->norm_retry && sdBuf->notRdy_retry) {
85:
86: /*
87: * Set up a scsiReq to pass to the controller.
88: */
89: scrtn = [self setupScsiReq:sdBuf scsiReq:&scsiReq];
90: if(scrtn)
91: return;
92:
93:
94: /*
95: * Go for it.
96: */
97: senseReply.er_ibvalid = 0; // for error logging
98: scrtn = [_controller executeRequest:&scsiReq
99: buffer:sdBuf->buf
100: client:sdBuf->client];
101: xpr_sd("sd%d: CDB Complete: sdBuf 0x%x driverStatus "
102: "%s scsiStatus"
103: " 0x%x\n", [self unit], sdBuf,
104: IOFindNameForValue(scsiReq.driverStatus,
105: IOScStatusStrings),
106: scsiReq.scsiStatus, 5);
107: if(scrtn == SR_IOST_GOOD) {
108: if((sdBuf->command == SDOP_READ) ||
109: (sdBuf->command == SDOP_WRITE)) {
110: /*
111: * Make sure the we moved the number of
112: * bytes we wanted to. Note we skip this
113: * test for SDOP_CDB_xxx - that's the
114: * user's problem.
115: */
116: u_int block_size = [self blockSize];
117: if((sdBuf->blockCnt * block_size) !=
118: scsiReq.bytesTransferred) {
119: if(--sdBuf->norm_retry <= 0) {
120: IOLog("%s: TRANSFER COUNT ERROR; "
121: " FATAL.\n", name);
122: [self logOpInfo:sdBuf
123: sense:&senseReply];
124: scrtn = SR_IOST_BCOUNT;
125: goto abort;
126: }
127: IOLog("%s: TRANSFER COUNT ERROR. "
128: " Expected = %d Received %d; "
129: "Retrying.\n", name,
130: sdBuf->blockCnt * block_size,
131: sdBuf->bytesXfr);
132: [self logOpInfo:sdBuf sense:NULL];
133: goto do_retry;
134: }
135: }
136:
137: /*
138: * Success. Log statistics.
139: */
140: if(_isRegistered) {
141: switch(sdBuf->command) {
142: case SDOP_READ:
143: [self addToBytesRead:
144: scsiReq.bytesTransferred
145: totalTime:scsiReq.totalTime
146: latentTime:scsiReq.latentTime];
147: break;
148: case SDOP_WRITE:
149: [self addToBytesWritten:
150: scsiReq.bytesTransferred
151: totalTime:scsiReq.totalTime
152: latentTime:scsiReq.latentTime];
153: break;
154: }
155: }
156:
157: /*
158: * All right, we're done.
159: */
160: break;
161: }
162:
163: if(sdBuf->retryDisable)
164: goto abort;
165:
166: /*
167: * What seems to be the problem?
168: */
169: switch(scrtn) {
170: case SR_IOST_SELTO: /* selection timeout */
171: case SR_IOST_ALIGN: /* DMA alignment */
172: case SR_IOST_BCOUNT: /* DMA overrun */
173: case SR_IOST_CMDREJ: /* command reject */
174: case SR_IOST_MEMALL: /* malloc error */
175: case SR_IOST_MEMF: /* memory failure */
176: case SR_IOST_IPCFAIL: /* IPC failure */
177: case SR_IOST_INT: /* internal error */
178: case SR_IOST_VOLNA: /* volume not available */
179: case SR_IOST_INVALID: /* our mistake */
180: case SR_IOST_WP: /* write protect */
181:
182: /*
183: * These are not retriable.
184: */
185: IOLog("%s: %s : FATAL ERROR\n", name,
186: IOFindNameForValue(scrtn, IOScStatusStrings));
187: [self logOpInfo:sdBuf sense:&senseReply];
188:
189: goto abort;
190:
191: case SR_IOST_BADST:
192: case SR_IOST_CHKSV:
193: case SR_IOST_CHKSNV:
194: /*
195: * The only weird SCSI status values we know how to
196: * deal with are "Busy" and "Check Status".
197: */
198: switch(scsiReq.scsiStatus) {
199: case STAT_BUSY:
200: if(--sdBuf->busy_retry <= 0) {
201: IOLog("%s: BUSY STATUS; FATAL.\n",
202: name);
203: [self logOpInfo:sdBuf
204: sense:&senseReply];
205: goto abort;
206: }
207: IOLog("%s: BUSY STATUS; Retrying.\n", name);
208: IOSleep(SD_BUSY_SLEEP * 1000);
209: break;
210:
211: case STAT_CHECK:
212: /*
213: * Do a request sense to find out why.
214: */
215: if(scrtn == SR_IOST_CHKSV) {
216: /*
217: * We already have sense data.
218: */
219: senseReply = scsiReq.senseData;
220: }
221: else {
222: scrtn = [self reqSense:&senseReply];
223: if(scrtn) {
224: IOLog("%s: REQUEST SENSE ERROR; "
225: " FATAL.\n", name);
226: goto abort;
227: }
228: }
229:
230:
231: /*
232: * OK, we have valid sense data.
233: */
234: switch(senseReply.er_sensekey) {
235: case SENSE_NOTREADY:
236: if(--sdBuf->notRdy_retry <= 0) {
237: IOLog("%s: NOT READY; "
238: "FATAL.\n", name);
239: /*
240: * FIXME - do we tell volCheck
241: * about this??
242: */
243: [self logOpInfo:sdBuf
244: sense:&senseReply];
245: goto abort;
246: }
247: IOLog("%s: NOT READY; Retrying.\n",
248: name);
249: [self logOpInfo:sdBuf
250: sense:&senseReply];
251: IOSleep(SD_NOTRDY_SLEEP * 1000);
252: break;
253:
254: case SENSE_NOSENSE: // I don't
255: // trust this!
256: case SENSE_RECOVERED: // or this
257: case SENSE_MEDIA:
258: case SENSE_HARDWARE:
259: if(--sdBuf->norm_retry <= 0) {
260: IOLog("%s: %s; FATAL.\n", name,
261: IOFindNameForValue(senseReply.
262: er_sensekey,
263: IOSCSISenseStrings));
264: [self logOpInfo:sdBuf
265: sense:&senseReply];
266: goto abort;
267: }
268: IOLog("%s: %s; Retrying.\n", name,
269: IOFindNameForValue(senseReply.
270: er_sensekey,
271: IOSCSISenseStrings));
272: [self logOpInfo:sdBuf
273: sense:&senseReply];
274: break;
275:
276: case SENSE_UNITATTENTION:
277: /*
278: * TBD - when we figure out how
279: * to handle removable media...
280: */
281: if(--sdBuf->norm_retry <= 0) {
282: IOLog("%s: UNIT ATTENTION;"
283: " FATAL.\n", name);
284: [self logOpInfo:sdBuf
285: sense:&senseReply];
286: goto abort;
287: }
288: IOLog("%s: UNIT ATTENTION; "
289: "Retrying.\n", name);
290: [self logOpInfo:sdBuf
291: sense:&senseReply];
292: break;
293:
294: case SENSE_DATAPROTECT:
295: /*
296: * Not retryable, but at least we have
297: * a unique error code.
298: */
299: IOLog("%s: WRITE PROTECTED. FATAL"
300: ".\n", name);
301: [self logOpInfo:sdBuf
302: sense:&senseReply];
303: scrtn = SR_IOST_WP;
304: goto abort;
305:
306: default:
307: /*
308: * All others fatal.
309: */
310: IOLog("%s: %s; FATAL.\n", name,
311: IOFindNameForValue(senseReply.
312: er_sensekey,
313: IOSCSISenseStrings));
314: [self logOpInfo:sdBuf
315: sense:&senseReply];
316: scrtn = SR_IOST_CHKSV;
317: goto abort;
318: } /* switch sense key */
319: break; /* from case STAT_CHECK */
320:
321: default:
322: /*
323: * Sorry, no can do.
324: */
325: IOLog("%s: BOGUS SCSI STATUS (0x%x): "
326: "FATAL.\n", name, scsiReq.scsiStatus);
327: [self logOpInfo:sdBuf
328: sense:&senseReply];
329: scrtn = SR_IOST_BADST;
330: goto abort;
331:
332: } /* switch scsiStatus */
333: break; /* from case SR_IOST_BADST */
334:
335: default:
336: /*
337: * Well, what the hey. retry.
338: */
339: if(--sdBuf->norm_retry <= 0) {
340: IOLog("%s: %s; FATAL.\n", name,
341: IOFindNameForValue(scrtn, IOScStatusStrings));
342: [self logOpInfo:sdBuf
343: sense:&senseReply];
344: goto abort;
345: }
346: IOLog("%s: %s; Retrying.\n", name,
347: IOFindNameForValue(scrtn, IOScStatusStrings));
348: [self logOpInfo:sdBuf
349: sense:&senseReply];
350: break;
351: } /* switch scrtn */
352: do_retry:
353: /*
354: * Retrying. log this with IODisk.
355: */
356: if(_isRegistered) {
357: switch(sdBuf->command) {
358: case SDOP_READ:
359: [self incrementReadRetries];
360: break;
361: case SDOP_WRITE:
362: [self incrementWriteRetries];
363: break;
364: default:
365: [self incrementOtherRetries];
366: break;
367: }
368: }
369: } /* main retry loop */
370:
371: abort:
372:
373: /*
374: * We've either successfuly executed the command or have given up.
375: * Transfer appropriate return values to sdBuf and sdIoComplete the
376: * result.
377: */
378: if((sdBuf->busy_retry == 0) ||
379: (sdBuf->norm_retry == 0) ||
380: (sdBuf->notRdy_retry == 0)) {
381: iortn = IO_R_IO;
382: }
383: else if(scrtn) {
384: iortn = [_controller returnFromScStatus:scrtn];
385: }
386: else {
387: iortn = IO_R_SUCCESS;
388: }
389: if(iortn && _isRegistered) {
390: /*
391: * All fatal errors must be logged with IODisk.
392: */
393: switch(sdBuf->command) {
394: case SDOP_READ:
395: [self incrementReadErrors];
396: break;
397: case SDOP_WRITE:
398: [self incrementWriteErrors];
399: break;
400: default:
401: if(!sdBuf->retryDisable) {
402: [self incrementOtherErrors];
403: }
404: break;
405: }
406: }
407: if(sdBuf->scsiReq) {
408: /*
409: * This implies SDOP_CDB_{READ,WRITE}.
410: */
411: sdBuf->scsiReq->driverStatus = scsiReq.driverStatus;
412: sdBuf->scsiReq->scsiStatus = scsiReq.scsiStatus;
413: sdBuf->scsiReq->bytesTransferred = scsiReq.bytesTransferred;
414:
415: /*
416: * Don't forget to copy over the sense data if the controller
417: * has provided it to us. We do not copy sense data that we
418: * manually requested for our own purposes in SCSIDisk.
419: */
420: if (scsiReq.driverStatus == SR_IOST_CHKSV)
421: sdBuf->scsiReq->senseData = scsiReq.senseData;
422: }
423: #if 0
424: else if(iortn == IO_R_SUCCESS) {
425: /*
426: * We know we can read this thing.
427: */
428: [self setFormattedInternal:1];
429: }
430: #endif 0
431: sdBuf->bytesXfr = scsiReq.bytesTransferred;
432: sdBuf->status = iortn;
433: [self sdIoComplete:sdBuf];
434: return;
435: }
436:
437: /*
438: * Log info about current operation for retry/error reporting.
439: */
440: - (void)logOpInfo : (sdBuf_t *)sdBuf
441: sense : (esense_reply_t *)senseReply
442: {
443: char opString[40];
444: char blockString[80];
445:
446: blockString[0] = '\0';
447: switch(sdBuf->command) {
448: case SDOP_READ:
449: strcpy(opString, "Read");
450: goto logBlock;
451: case SDOP_WRITE:
452: strcpy(opString, "Write");
453: logBlock:
454: if(senseReply && senseReply->er_ibvalid) {
455: /*
456: * Get block # from sense data
457: */
458: unsigned blockInErr;
459:
460: blockInErr = scsi_error_info(senseReply);
461: sprintf(blockString, "block:%d", blockInErr);
462: }
463: else {
464: /*
465: * Just report starting block # and length.
466: */
467: sprintf(blockString, "block:%d blockCount:%d",
468: sdBuf->block, sdBuf->blockCnt);
469: }
470: break;
471:
472: case SDOP_CDB_READ:
473: case SDOP_CDB_WRITE:
474: strcpy(opString,
475: IOFindNameForValue(sdBuf->scsiReq->cdb.cdb_opcode,
476: IOSCSIOpcodeStrings));
477: break;
478:
479: case SDOP_EJECT:
480: strcpy(opString, "Eject");
481: break;
482:
483: default:
484: /*
485: * The remainder should not result in disk errors...
486: */
487: panic("Bogus op in logOpInfo");
488: }
489: IOLog(" target:%d lun:%d op:%s %s\n", _target, _lun,
490: opString, blockString);
491: return;
492: }
493:
494: /*
495: * generate a CDB for a read or write operation. Assumes that *cdbp has
496: * already been zero'd.
497: */
498: - (void)genRwCdb : (cdb_t *)cdbp
499: readFlag : (BOOL)readFlag
500: block : (u_int)block
501: blockCnt : (u_int)blockCnt
502: {
503: cdb_10_t *cdb = &cdbp->cdb_c10;
504: unsigned short blockCntS = blockCnt;
505:
506: if (readFlag)
507: scsi_readextended_setup(cdb, _lun, block, blockCntS);
508: else
509: scsi_writeextended_setup(cdb, _lun, block, blockCntS);
510: }
511:
512: /*
513: * Set up a scsiReq for a given sdBuf_t to pass to the controller. Only used
514: * inside of I/O thread.
515: */
516: - (sc_status_t)setupScsiReq:(sdBuf_t *)sdBuf
517: scsiReq:(IOSCSIRequest *)scsiReq
518: {
519: u_int block_size;
520:
521: block_size = [self blockSize];
522: bzero(scsiReq, sizeof(IOSCSIRequest));
523: scsiReq->target = _target;
524: scsiReq->lun = _lun;
525: switch(sdBuf->command) {
526: case SDOP_READ:
527: scsiReq->read = YES;
528: [self genRwCdb:&scsiReq->cdb
529: readFlag : YES
530: block : sdBuf->block
531: blockCnt : sdBuf->blockCnt];
532: goto rw_common;
533:
534: case SDOP_WRITE:
535: scsiReq->read = NO;
536: [self genRwCdb:&scsiReq->cdb
537: readFlag : NO
538: block : sdBuf->block
539: blockCnt : sdBuf->blockCnt];
540: rw_common:
541: sdBuf->scsiReq = NULL;
542: scsiReq->maxTransfer = sdBuf->blockCnt * block_size;
543: scsiReq->timeoutLength = SD_TIMEOUT_RW;
544: scsiReq->disconnect = 1;
545: break;
546:
547: case SDOP_EJECT:
548: case SDOP_CDB_READ:
549: case SDOP_CDB_WRITE:
550: if((sdBuf->scsiReq->target != _target) ||
551: (sdBuf->scsiReq->lun != _lun)) {
552: /*
553: * Ooops, can't allow this!
554: */
555: sdBuf->scsiReq->driverStatus = SR_IOST_CMDREJ;
556: sdBuf->status = IO_R_INVALID_ARG;
557: [self sdIoComplete:sdBuf];
558: return(SR_IOST_CMDREJ);
559: }
560: /*
561: * Otherwise, we'll allow just about anything.
562: */
563: *scsiReq = *sdBuf->scsiReq;
564: break;
565: default:
566: ASSERT(0);
567: break;
568: }
569: return(SR_IOST_GOOD);
570: }
571:
572: /*
573: * Execute a request sense command within the I/O thread. esenseReply
574: * does not have to be aligned.
575: */
576: - (sc_status_t)reqSense : (esense_reply_t *)esenseReply
577: {
578: IOSCSIRequest scsiReq;
579: sc_status_t rtn;
580: cdb_6_t *cdbp = &scsiReq.cdb.cdb_c6;
581: esense_reply_t *alignedReply;
582: void *freep;
583: int freeCnt;
584: IODMAAlignment dmaAlign;
585:
586: xpr_sd("sd%d reqSense\n", [self unit], 2,3,4,5);
587:
588: /*
589: * Get some well-aligned memory.
590: */
591: alignedReply = [_controller allocateBufferOfLength:
592: sizeof(esense_reply_t)
593: actualStart:&freep
594: actualLength:&freeCnt];
595: bzero(&scsiReq, sizeof(IOSCSIRequest));
596:
597: cdbp->c6_opcode = C6OP_REQSENSE;
598: cdbp->c6_lun = _lun;
599: cdbp->c6_len = sizeof(esense_reply_t);
600: scsiReq.target = _target;
601: scsiReq.lun = _lun;
602: scsiReq.read = YES;
603:
604: /*
605: * Get appropriate alignment from controller.
606: */
607: [_controller getDMAAlignment:&dmaAlign];
608: if(dmaAlign.readLength > 1) {
609: scsiReq.maxTransfer = IOAlign(int, sizeof(esense_reply_t),
610: dmaAlign.readLength);
611: }
612: else {
613: scsiReq.maxTransfer = sizeof(esense_reply_t);
614: }
615: scsiReq.timeoutLength = SD_TIMEOUT_SIMPLE;
616: scsiReq.disconnect = 1;
617: rtn = [_controller executeRequest:&scsiReq
618: buffer:alignedReply
619: client:IOVmTaskSelf()];
620: /*
621: * Copy data back to caller's struct.
622: */
623:
624: *esenseReply = *alignedReply;
625: IOFree(freep, freeCnt);
626: xpr_sd("sd%d reqSense: returning %d\n", [self unit], rtn, 3,4,5);
627:
628: return(rtn);
629: }
630:
631: /*
632: * Either wake up the thread which is waiting on the sdBuf, or send an
633: * ioComplete back to client. sdBuf->status must be valid.
634: */
635: - (void)sdIoComplete : (sdBuf_t *)sdBuf
636: {
637: xpr_sd("%s sdIoComplete: sdBuf 0x%x status %s\n",
638: [self name], sdBuf,
639: [self stringFromReturn:sdBuf->status], 4,5);
640: if(sdBuf->pending) {
641:
642: /*
643: * Async I/O complete. This will change with RO support.
644: */
645: int dirRead = 0;
646:
647: switch(sdBuf->command) {
648: case SDOP_READ:
649: case SDOP_CDB_READ:
650: dirRead = 1;
651: break;
652: default:
653: break;
654: }
655: [self completeTransfer : sdBuf->pending
656: withStatus : sdBuf->status
657: actualLength : sdBuf->bytesXfr];
658: [self freeSdBuf:sdBuf];
659: }
660: else {
661: /*
662: * Sync I/O. Just wake up the waiting thread.
663: */
664: [sdBuf->waitLock unlockWith:YES];
665: }
666: }
667:
668: /*
669: * Unlock ioQLock, updating condition variable as appropriate.
670: */
671: - (void)unlockIoQLock
672: {
673: int queue_state;
674: IODiskReadyState lastReady = [self lastReadyState];
675:
676: /*
677: * There's still work to do when:
678: * -- ioQueueNodisk non-empty, or
679: * -- ioQueueDisk non-empty and we "really" have a disk.
680: */
681:
682: if((!queue_empty(&_ioQueueNodisk)) ||
683: ((!queue_empty(&_ioQueueDisk)) &&
684: (lastReady != IO_NoDisk) &&
685: (lastReady != IO_Ejecting) &&
686: (!_ejectPending)
687: )
688: ) {
689: queue_state = WORK_AVAILABLE;
690: }
691: else
692: queue_state = NO_WORK_AVAILABLE;
693: [_ioQLock unlockWith:queue_state];
694:
695: if (queue_state == WORK_AVAILABLE)
696: (void) thread_block();
697: }
698:
699:
700: /*
701: * I/O thread. Each one of these sits around waiting for work to do on
702: * ioQueue; when something appears, the thread grabs it, creates an
703: * appropriate scsiReq, invokes the proper controller method, and
704: * handles a possible error condition (including retries).
705: */
706:
707: volatile void sdIoThread(id me)
708: {
709: SCSIDisk *sdp = (SCSIDisk *)me;
710: IODiskReadyState lastReady;
711:
712: xpr_sd("sdIoThread unit %d: STARTING\n", [sdp unit], 2,3,4,5);
713:
714: while(1) {
715: /*
716: * Wait for some work to do. Keep the ioQLock until we
717: * dequeue something.
718: */
719: xpr_sd("sdIoThread: waiting for work\n", 1,2,3,4,5);
720: [sdp->_ioQLock lockWhen:WORK_AVAILABLE];
721:
722: /*
723: * Service all requests which do not need a disk.
724: */
725: xpr_sd("sdIoThread: servicing q_nodisk\n", 1,2,3,4,5);
726: while(!queue_empty(&sdp->_ioQueueNodisk))
727: sdThreadDequeue(sdp, NO);
728:
729: /*
730: * Now service all requests which need a disk, if our disk
731: * is present. We still hold ioQLock.
732: */
733: xpr_sd("sdIoThread: servicing q_disk\n", 1,2,3,4,5);
734: while((!queue_empty(&sdp->_ioQueueDisk)) &&
735: ([sdp lastReadyState] != IO_NoDisk) &&
736: ([sdp lastReadyState] != IO_Ejecting) &&
737: (!sdp->_ejectPending)) {
738: sdThreadDequeue(sdp, YES);
739: }
740:
741: /*
742: * If we have work to do in ioQueueDisk but we don't have
743: * a disk, ask volCheck to put up a panel. In either case,
744: * when we unlock ioQueueLock for the last time, update its
745: * condition variable as appropriate so we and the other
746: * I/O threads know whether or not to sleep.
747: */
748:
749: lastReady = [sdp lastReadyState];
750: if((!queue_empty(&sdp->_ioQueueDisk)) &&
751: ((lastReady == IO_NotReady && [sdp isRemovable]) ||
752: (lastReady == IO_NoDisk) || sdp->_ejectPending)) {
753: [sdp unlockIoQLock];
754: xpr_sd("sdIoThread: volCheckRequest()\n", 1,2,3,4,5);
755: volCheckRequest(sdp, PR_DRIVE_SCSI);
756: }
757: else {
758: [sdp unlockIoQLock];
759: }
760: } /* while 1 */
761:
762: /* NOT REACHED */
763: }
764:
765: /*
766: * Process a request at the head of one of the IOQueues.
767: * ioQLock must be held on entry; it will still be held on exit.
768: */
769: static void sdThreadDequeue(SCSIDisk *sdp,
770: BOOL needs_disk)
771: {
772: queue_head_t *q;
773: sdBuf_t *sdBuf;
774:
775: if(needs_disk)
776: q = &sdp->_ioQueueDisk;
777: else
778: q = &sdp->_ioQueueNodisk;
779: if(queue_empty(q)) {
780: IOLog("sdThreadDequeue: Empty queue!\n");
781: return;
782: }
783: sdBuf = (sdBuf_t *)queue_first(q);
784: queue_remove(q,
785: sdBuf,
786: sdBuf_t *,
787: link);
788:
789: /*
790: * In case of an eject command, we have to ensure right now - while
791: * the queue is locked - that no other threads attempt any
792: * "needs disk" type of I/Os. That's what the ejectPending flag is
793: * for; we can't rely on the volCheck thread setting our
794: * lastReadyState to RS_EJECTING for another second or so...
795: *
796: * The numDiskIos counter allows the thread doing an eject command
797: * to wait for all other pending Disk I/Os to complete before doing
798: * the eject.
799: */
800: if(needs_disk) {
801: [sdp->_ejectLock lock];
802: sdp->_numDiskIos++;
803: if(sdBuf->command == SDOP_EJECT) {
804: sdp->_ejectPending = YES;
805: volCheckEjecting(sdp, PR_DRIVE_SCSI);
806: }
807: [sdp->_ejectLock unlockWith:sdp->_numDiskIos];
808: }
809:
810: /*
811: * Execute command specified in sdBuf. We hold _ioQLock.
812: */
813:
814: xpr_sd("sdThreadDequeue: sdBuf 0x%x received\n", sdBuf, 2,3,4,5);
815: switch(sdBuf->command) {
816: case SDOP_ABORT:
817: /*
818: * Ah hah, we have to ioComplete all of the sdBufs in
819: * ioQueueDisk.
820: */
821: {
822: sdBuf_t *abortBuf;
823: queue_head_t *q = &sdp->_ioQueueDisk;
824:
825: while(!queue_empty(q)) {
826: abortBuf = (sdBuf_t *)queue_first(q);
827: queue_remove(q, abortBuf, sdBuf_t *, link);
828: [sdp->_ioQLock unlock];
829: abortBuf->status = IO_R_NO_DISK;
830: if(abortBuf->scsiReq)
831: abortBuf->scsiReq->driverStatus = SR_IOST_VOLNA;
832: [sdp sdIoComplete:abortBuf];
833: [sdp->_ioQLock lock];
834: }
835:
836: /*
837: * and ioComplete the abort command itself.
838: */
839: sdBuf->status = IO_R_SUCCESS;
840: [sdp sdIoComplete:sdBuf];
841: }
842: break;
843:
844: case SDOP_PROBEDISK:
845: /*
846: * If we got this far, the disk is present. ioComplete
847: * immediately. (If disk was not present, we would have
848: * disposed of this in case SDOP_ABORT.)
849: */
850: sdBuf->status = IO_R_SUCCESS;
851: [sdp sdIoComplete:sdBuf];
852: break;
853:
854: case SDOP_EJECT:
855: /*
856: * Wait until we're the only thread executing.
857: */
858: [sdp->_ioQLock unlock];
859: [sdp->_ejectLock lockWhen:1];
860: [sdp->_ejectLock unlock];
861: [sdp doSdBuf:sdBuf];
862: [sdp->_ioQLock lock];
863: break;
864:
865: case SDOP_READ:
866: case SDOP_WRITE:
867: case SDOP_CDB_READ:
868: case SDOP_CDB_WRITE:
869: /*
870: * We don't want to hold this lock while executing this
871: * command...
872: */
873: [sdp->_ioQLock unlock];
874: [sdp doSdBuf:sdBuf];
875: [sdp->_ioQLock lock];
876: break;
877:
878: case SDOP_ABORT_THREAD:
879: /*
880: * I/O complete this before we die.
881: */
882: [sdp->_ioQLock unlock];
883: sdBuf->status = IO_R_SUCCESS;
884: [sdp sdIoComplete:sdBuf];
885: xpr_sd("%s: calling IOExitThread()\n", [sdp name],
886: 2,3,4,5);
887: IOExitThread();
888: break;
889: }
890:
891: if(needs_disk) {
892: /*
893: * Enable possible waiting eject command.
894: */
895: [sdp->_ejectLock lock];
896: sdp->_numDiskIos--;
897: [sdp->_ejectLock unlockWith:sdp->_numDiskIos];
898: }
899: }
900:
901: @end
902:
903:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.