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