|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: ! 23: #include <IOKit/assert.h> ! 24: #include <IOKit/IOLib.h> ! 25: #include <IOKit/IOMemoryDescriptor.h> ! 26: #include <IOKit/storage/IODrive.h> ! 27: ! 28: #define super IOStorage ! 29: OSDefineMetaClass(IODrive, IOStorage) ! 30: OSDefineAbstractStructors(IODrive, IOStorage) ! 31: ! 32: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 33: ! 34: const UInt32 kPollerInterval = 1000; // (ms, 1 second) ! 35: ! 36: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 37: ! 38: bool IODrive::init(OSDictionary * properties = 0) ! 39: { ! 40: // ! 41: // Initialize this object's minimal state. ! 42: // ! 43: ! 44: if (super::init(properties) == false) return false; ! 45: ! 46: _deblockerLockForRMWs = IOLockAlloc(); ! 47: _openClients = OSSet::withCapacity(2); ! 48: ! 49: for (unsigned index = 0; index < kStatisticsCount; index++) ! 50: _statistics[index] = OSNumber::withNumber(0ULL, 64); ! 51: ! 52: if (_deblockerLockForRMWs == 0 || _openClients == 0) return false; ! 53: ! 54: for (unsigned index = 0; index < kStatisticsCount; index++) ! 55: if (_statistics[index] == 0) return false; ! 56: ! 57: IOLockInit(_deblockerLockForRMWs); ! 58: ! 59: // ! 60: // Create the standard drive registry properties. ! 61: // ! 62: ! 63: OSDictionary * statistics = OSDictionary::withCapacity(kStatisticsCount); ! 64: ! 65: if (statistics == 0) return false; ! 66: ! 67: statistics->setObject( kIODriveStatisticsBytesRead, ! 68: _statistics[kStatisticsBytesRead] ); ! 69: statistics->setObject( kIODriveStatisticsBytesWritten, ! 70: _statistics[kStatisticsBytesWritten] ); ! 71: statistics->setObject( kIODriveStatisticsReadErrors, ! 72: _statistics[kStatisticsReadErrors] ); ! 73: statistics->setObject( kIODriveStatisticsWriteErrors, ! 74: _statistics[kStatisticsWriteErrors] ); ! 75: statistics->setObject( kIODriveStatisticsLatentReadTime, ! 76: _statistics[kStatisticsLatentReadTime] ); ! 77: statistics->setObject( kIODriveStatisticsLatentWriteTime, ! 78: _statistics[kStatisticsLatentWriteTime] ); ! 79: statistics->setObject( kIODriveStatisticsReads, ! 80: _statistics[kStatisticsReads] ); ! 81: statistics->setObject( kIODriveStatisticsWrites, ! 82: _statistics[kStatisticsWrites] ); ! 83: statistics->setObject( kIODriveStatisticsReadRetries, ! 84: _statistics[kStatisticsReadRetries] ); ! 85: statistics->setObject( kIODriveStatisticsWriteRetries, ! 86: _statistics[kStatisticsWriteRetries] ); ! 87: statistics->setObject( kIODriveStatisticsTotalReadTime, ! 88: _statistics[kStatisticsTotalReadTime] ); ! 89: statistics->setObject( kIODriveStatisticsTotalWriteTime, ! 90: _statistics[kStatisticsTotalWriteTime] ); ! 91: ! 92: setProperty(kIODriveStatistics, statistics); ! 93: ! 94: return true; ! 95: } ! 96: ! 97: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 98: ! 99: bool IODrive::start(IOService * provider) ! 100: { ! 101: // ! 102: // This method is called once we have been attached to the provider object. ! 103: // ! 104: ! 105: // Instruct our subclass to prepare the drive for operation. ! 106: ! 107: if (handleStart(provider) == false) return false; ! 108: ! 109: // Initiate the poller mechanism if it is required. ! 110: ! 111: if (isMediaEjectable() && isMediaPollRequired() && !isMediaPollExpensive()) ! 112: { ! 113: lockForArbitration(); // (disable opens/closes; a recursive lock) ! 114: ! 115: if (!isOpen() && !isInactive()) ! 116: schedulePoller(); // (schedule the poller, increments retain) ! 117: ! 118: unlockForArbitration(); // (enable opens/closes; a recursive lock) ! 119: } ! 120: ! 121: // Register this object so it can be found via notification requests. It is ! 122: // not being registered to have I/O Kit attempt to have drivers match on it, ! 123: // which is the reason most other services are registered -- that's not the ! 124: // intention of this registerService call. ! 125: ! 126: registerService(); ! 127: ! 128: return true; ! 129: } ! 130: ! 131: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 132: ! 133: void IODrive::stop(IOService * provider) ! 134: { ! 135: // ! 136: // This method is called before we are detached from the provider object. ! 137: // ! 138: ! 139: // Halt the poller mechanism if it is required. ! 140: ! 141: if (isMediaEjectable() && isMediaPollRequired() && !isMediaPollExpensive()) ! 142: unschedulePoller(); // (unschedule the poller) ! 143: ! 144: // Instruct our subclass to stop the drive. ! 145: ! 146: handleStop(provider); ! 147: ! 148: super::stop(provider); ! 149: } ! 150: ! 151: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 152: ! 153: void IODrive::free() ! 154: { ! 155: // ! 156: // Free all of this object's outstanding resources. ! 157: // ! 158: ! 159: if (_deblockerLockForRMWs) IOLockFree(_deblockerLockForRMWs); ! 160: if (_openClients) _openClients->release(); ! 161: ! 162: for (unsigned index = 0; index < kStatisticsCount; index++) ! 163: if (_statistics[index]) _statistics[index]->release(); ! 164: ! 165: super::free(); ! 166: } ! 167: ! 168: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 169: ! 170: bool IODrive::handleOpen(IOService * client, ! 171: IOOptionBits options, ! 172: void * argument) ! 173: { ! 174: // ! 175: // The handleOpen method grants or denies permission to access this object ! 176: // to an interested client. The argument is an IOStorageAccess value that ! 177: // specifies the level of access desired -- reader or reader-writer. ! 178: // ! 179: // This method can be invoked to upgrade or downgrade the access level for ! 180: // an existing client as well. The previous access level will prevail for ! 181: // upgrades that fail, of course. A downgrade should never fail. If the ! 182: // new access level should be the same as the old for a given client, this ! 183: // method will do nothing and return success. In all cases, one, singular ! 184: // close-per-client is expected for all opens-per-client received. ! 185: // ! 186: // We are guaranteed that no other opens or closes will be processed until ! 187: // we make our decision, change our state, and return from this method. ! 188: // ! 189: ! 190: IOStorageAccess access = (IOStorageAccess) (int) argument; ! 191: ! 192: assert(client); ! 193: assert(access != kAccessNone); ! 194: ! 195: // Handle the first open on removable media in a special case. ! 196: ! 197: if (isMediaEjectable() && _openClients->getCount() == 0) ! 198: { ! 199: // Halt the poller if it is active and this is the drive's first open. ! 200: ! 201: if (isMediaPollRequired() && !isMediaPollExpensive()) ! 202: unschedulePoller(); // (unschedule the poller) ! 203: ! 204: // Lock down the media while we have opens on this drive object. The ! 205: // arbitration lock is held during the operation -- however we assume ! 206: // the extra length of time will not be an issue for now. ! 207: ! 208: if (lockMedia(true) != kIOReturnSuccess) ! 209: IOLog("%s: Unable to lock down removable media.\n", getName()); ! 210: } ! 211: ! 212: // Process the open. ! 213: ! 214: _openClients->setObject(client); // (works for up/downgrade case) ! 215: ! 216: return true; ! 217: } ! 218: ! 219: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 220: ! 221: bool IODrive::handleIsOpen(const IOService * client) const ! 222: { ! 223: // ! 224: // The handleIsOpen method determines whether the specified client, or any ! 225: // client if none is specificed, presently has an open on this object. ! 226: // ! 227: // We are guaranteed that no other opens or closes will be processed until ! 228: // we return from this method. ! 229: // ! 230: ! 231: if (client) ! 232: return _openClients->containsObject(client); ! 233: else ! 234: return (_openClients->getCount() != 0); ! 235: } ! 236: ! 237: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 238: ! 239: void IODrive::handleClose(IOService * client, IOOptionBits options) ! 240: { ! 241: // ! 242: // The handleClose method drops the incoming client's access to this object. ! 243: // ! 244: // We are guaranteed that no other opens or closes will be processed until ! 245: // we change our state and return from this method. ! 246: // ! 247: ! 248: assert(client); ! 249: ! 250: // Process the close. ! 251: ! 252: _openClients->removeObject(client); ! 253: ! 254: // Handle the last close on removable media in a special case. ! 255: ! 256: if (isMediaEjectable() && _openClients->getCount() == 0) ! 257: { ! 258: // Unlock the media in the drive. The arbitration lock is held during ! 259: // the operation -- however we assume the extra length of time will not ! 260: // be an issue for now. ! 261: ! 262: if (lockMedia(false) != kIOReturnSuccess) ! 263: IOLog("%s: Unable to unlock removable media.\n", getName()); ! 264: ! 265: // Reactivate the poller. ! 266: ! 267: if (isMediaPollRequired() && !isMediaPollExpensive() && !isInactive()) ! 268: schedulePoller(); // (schedule the poller, increments retain) ! 269: } ! 270: } ! 271: ! 272: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 273: ! 274: void IODrive::read(IOService * /* client */, ! 275: UInt64 byteStart, ! 276: IOMemoryDescriptor * buffer, ! 277: IOStorageCompletion completion) ! 278: { ! 279: // ! 280: // Read data from the storage object at the specified byte offset into the ! 281: // specified buffer, asynchronously. When the read completes, the caller ! 282: // will be notified via the specified completion action. ! 283: // ! 284: // The buffer will be retained for the duration of the read. ! 285: // ! 286: // Note that the read passes through several other methods before being ! 287: // passed to executeRequest. The first is deblockRequest, which aligns ! 288: // the request with the media's block boundaries; the second is prepare- ! 289: // Request which prepares the memory involved in the transfer (involves ! 290: // wiring, virtual-to-physical mapping, and breakup of the memory range ! 291: // based on the controller's and/or drive's constraints). ! 292: // ! 293: ! 294: UInt64 mediaBlockSize = getMediaBlockSize(); ! 295: ! 296: // State our assumptions. ! 297: ! 298: assert(buffer->getDirection() == kIODirectionIn); ! 299: ! 300: // If the request is aligned with the media's block boundaries, we can ! 301: // short-circuit the deblocker and call prepareRequest directly. ! 302: ! 303: if ( (byteStart % mediaBlockSize) == 0 && ! 304: (buffer->getLength() % mediaBlockSize) == 0 ) ! 305: { ! 306: prepareRequest(byteStart, buffer, completion); ! 307: return; ! 308: } ! 309: ! 310: deblockRequest(byteStart, buffer, completion); ! 311: } ! 312: ! 313: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 314: ! 315: void IODrive::write(IOService * /* client */, ! 316: UInt64 byteStart, ! 317: IOMemoryDescriptor * buffer, ! 318: IOStorageCompletion completion) ! 319: { ! 320: // ! 321: // Write data into the storage object at the specified byte offset from the ! 322: // specified buffer, asynchronously. When the write completes, the caller ! 323: // will be notified via the specified completion action. ! 324: // ! 325: // The buffer will be retained for the duration of the write. ! 326: // ! 327: // Note that the write passes through several other methods before being ! 328: // passed to executeRequest. The first is deblockRequest, which aligns ! 329: // the request with the media's block boundaries; the second is prepare- ! 330: // Request which prepares the memory involved in the transfer (involves ! 331: // wiring, virtual-to-physical mapping, and breakup of the memory range ! 332: // based on the controller's and/or drive's constraints). ! 333: // ! 334: ! 335: UInt64 mediaBlockSize = getMediaBlockSize(); ! 336: ! 337: // State our assumptions. ! 338: ! 339: assert(buffer->getDirection() == kIODirectionOut); ! 340: ! 341: // If the request is aligned with the media's block boundaries, we can ! 342: // short-circuit the deblocker and call prepareRequest directly. ! 343: ! 344: if ( (byteStart % mediaBlockSize) == 0 && ! 345: (buffer->getLength() % mediaBlockSize) == 0 ) ! 346: { ! 347: prepareRequest(byteStart, buffer, completion); ! 348: return; ! 349: } ! 350: ! 351: deblockRequest(byteStart, buffer, completion); ! 352: } ! 353: ! 354: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 355: ! 356: void IODrive::addToBytesTransferred(UInt64 bytesTransferred, ! 357: UInt64 totalTime, // (ns) ! 358: UInt64 latentTime, // (ns) ! 359: bool isWrite) ! 360: { ! 361: // ! 362: // Update the total number of bytes transferred, the total transfer time, ! 363: // and the total latency time for this drive -- used for statistics. ! 364: // ! 365: ! 366: if (isWrite) ! 367: { ! 368: _statistics[kStatisticsWrites]->addValue(1); ! 369: _statistics[kStatisticsBytesWritten]->addValue(bytesTransferred); ! 370: _statistics[kStatisticsTotalWriteTime]->addValue(totalTime); ! 371: _statistics[kStatisticsLatentWriteTime]->addValue(latentTime); ! 372: if (bytesTransferred <= getMediaBlockSize()) ! 373: _statistics[kStatisticsSingleBlockWrites]->addValue(1); ! 374: } ! 375: else ! 376: { ! 377: _statistics[kStatisticsReads]->addValue(1); ! 378: _statistics[kStatisticsBytesRead]->addValue(bytesTransferred); ! 379: _statistics[kStatisticsTotalReadTime]->addValue(totalTime); ! 380: _statistics[kStatisticsLatentReadTime]->addValue(latentTime); ! 381: if (bytesTransferred <= getMediaBlockSize()) ! 382: _statistics[kStatisticsSingleBlockReads]->addValue(1); ! 383: } ! 384: } ! 385: ! 386: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 387: ! 388: void IODrive::incrementRetries(bool isWrite) ! 389: { ! 390: // ! 391: // Update the total retry count -- used for statistics. ! 392: // ! 393: ! 394: if (isWrite) ! 395: _statistics[kStatisticsWriteRetries]->addValue(1); ! 396: else ! 397: _statistics[kStatisticsReadRetries]->addValue(1); ! 398: } ! 399: ! 400: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 401: ! 402: void IODrive::incrementErrors(bool isWrite) ! 403: { ! 404: // ! 405: // Update the total error count -- used for statistics. ! 406: // ! 407: ! 408: if (isWrite) ! 409: _statistics[kStatisticsWriteErrors]->addValue(1); ! 410: else ! 411: _statistics[kStatisticsReadErrors]->addValue(1); ! 412: } ! 413: ! 414: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 415: ! 416: UInt32 IODrive::getStatistics(UInt64 * statistics, ! 417: UInt32 statisticsMaxCount) const ! 418: { ! 419: // ! 420: // Ask the drive to report its operating statistics. The statistics are ! 421: // described by the IODrive::Statistics indices. This routine fills the ! 422: // caller's buffer, up to the maximum count specified if the real number ! 423: // of statistics would overflow the buffer. The return value indicates ! 424: // the actual number of statistics copied to the buffer. ! 425: // ! 426: // If the statistics buffer is not supplied or if the maximum count is ! 427: // zero, the routine returns the proposed count of statistics instead. ! 428: // ! 429: ! 430: if (statistics == 0) ! 431: return kStatisticsCount; ! 432: ! 433: UInt32 statisticsCount = min(kStatisticsCount, statisticsMaxCount); ! 434: ! 435: for (unsigned index = 0; index < statisticsCount; index++) ! 436: statistics[index] = _statistics[index]->unsigned64BitValue(); ! 437: ! 438: return statisticsCount; ! 439: } ! 440: ! 441: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 442: ! 443: UInt64 IODrive::getStatistic(Statistics statistic) const ! 444: { ! 445: // ! 446: // Ask the drive to report one of its operating statistics. ! 447: // ! 448: ! 449: if ((UInt32) statistic >= kStatisticsCount) return 0; ! 450: ! 451: return _statistics[statistic]->unsigned64BitValue(); ! 452: } ! 453: ! 454: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 455: ! 456: void IODrive::prepareRequest(UInt64 byteStart, ! 457: IOMemoryDescriptor * buffer, ! 458: IOStorageCompletion completion) ! 459: { ! 460: // ! 461: // The prepareRequest method prepares the memory involved in the transfer. ! 462: // The memory will be wired down, physically mapped, and broken up based ! 463: // on the controller's and drive's constraints. ! 464: // ! 465: // This method is part of a sequence of methods that are called for each ! 466: // read or write request. The first is deblockRequest, which aligns the ! 467: // request at the media's block boundaries; the second is prepareRequest, ! 468: // which prepares the buffer involved in the transfer; and the third is ! 469: // executeRequest, which implements the actual transfer from the drive. ! 470: // ! 471: ! 472: executeRequest(byteStart, buffer, completion); ! 473: return; ! 474: } ! 475: ! 476: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 477: ! 478: void IODrive::schedulePoller() ! 479: { ! 480: // ! 481: // Schedule the poller mechanism. ! 482: // ! 483: // This method assumes that the arbitration lock is held. ! 484: // ! 485: ! 486: AbsoluteTime deadline; ! 487: ! 488: retain(); ! 489: ! 490: clock_interval_to_deadline(kPollerInterval, kMillisecondScale, &deadline); ! 491: thread_call_func_delayed(poller, this, deadline); ! 492: } ! 493: ! 494: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 495: ! 496: void IODrive::unschedulePoller() ! 497: { ! 498: // ! 499: // Unschedule the poller mechanism. ! 500: // ! 501: // This method assumes that the arbitration lock is held. ! 502: // ! 503: ! 504: if (thread_call_func_cancel(poller, this, true)) release(); ! 505: } ! 506: ! 507: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 508: ! 509: void IODrive::poller(void * target, void *) ! 510: { ! 511: // ! 512: // This method is the timeout handler for the poller mechanism. It polls ! 513: // for media and reschedules another timeout if the drive is still closed. ! 514: // ! 515: ! 516: IODrive * drive = (IODrive *) target; ! 517: ! 518: drive->pollMedia(); ! 519: ! 520: drive->lockForArbitration(); // (disable opens/closes; a recursive lock) ! 521: ! 522: if (!drive->isOpen() && !drive->isInactive()) ! 523: drive->schedulePoller(); // (schedule the poller, increments retain) ! 524: ! 525: drive->unlockForArbitration(); // (enable opens/closes; a recursive lock) ! 526: ! 527: drive->release(); // (drop the retain associated with this poll) ! 528: } ! 529: ! 530: // ----------------------------------------------------------------------------- ! 531: // Deblocker Implementation ! 532: ! 533: IODrive::DeblockerContext * IODrive::deblockerContextAllocate() ! 534: { ! 535: // ! 536: // Allocate a deblocker context structure. It comes preinitialized with ! 537: // a block-sized scratch buffer and an associated memory descriptor, but ! 538: // is otherwise uninitialized. ! 539: // ! 540: // A future implementation may recycle deblocker context structures here ! 541: // as an optimization. ! 542: // ! 543: ! 544: DeblockerContext * deblockerContext = IONew(DeblockerContext, 1); ! 545: UInt64 mediaBlockSize = getMediaBlockSize(); ! 546: ! 547: if (deblockerContext) ! 548: { ! 549: deblockerContext->blockBufferPtr = IONew(UInt8, mediaBlockSize); ! 550: ! 551: if (deblockerContext->blockBufferPtr) ! 552: { ! 553: deblockerContext->blockBuffer = IOMemoryDescriptor::withAddress( ! 554: /* address */ deblockerContext->blockBufferPtr, ! 555: /* withLength */ (vm_size_t) mediaBlockSize, ! 556: /* withDirection */ kIODirectionNone ); ! 557: ! 558: if (deblockerContext->blockBuffer) return deblockerContext; ! 559: ! 560: IODelete(deblockerContext->blockBufferPtr, UInt8, mediaBlockSize); ! 561: } ! 562: IODelete(deblockerContext, DeblockerContext, 1); ! 563: } ! 564: ! 565: return 0; // (failure) ! 566: } ! 567: ! 568: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 569: ! 570: void IODrive::deblockerContextFree(IODrive::DeblockerContext * deblockerContext) ! 571: { ! 572: // ! 573: // Deallocate a deblocker context structure. The block-sized scratch ! 574: // buffer and memory descriptor is automatically deallocated as well. ! 575: // ! 576: ! 577: assert(deblockerContext->blockBuffer && deblockerContext->blockBufferPtr); ! 578: ! 579: if (deblockerContext->middleBuffer) ! 580: deblockerContext->middleBuffer->release(); ! 581: ! 582: deblockerContext->blockBuffer->release(); ! 583: IODelete(deblockerContext->blockBufferPtr, UInt8, getMediaBlockSize()); ! 584: IODelete(deblockerContext, DeblockerContext, 1); ! 585: } ! 586: ! 587: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 588: ! 589: void IODrive::deblockRequest(UInt64 byteStart, ! 590: IOMemoryDescriptor * buffer, ! 591: IOStorageCompletion completion) ! 592: { ! 593: // ! 594: // The deblockRequest method checks to see if the incoming request rests ! 595: // on the media's block boundaries, and if not, deblocks it. Deblocking ! 596: // involves breaking up the request into sub-requests that rest on block ! 597: // boundaries, and performing the appropriate unaligned byte copies from ! 598: // the scratch buffer into into the client's request buffer. ! 599: // ! 600: // This method is part of a sequence of methods that are called for each ! 601: // read or write request. The first is deblockRequest, which aligns the ! 602: // request at the media's block boundaries; the second is prepareRequest, ! 603: // which prepares the memory involved in the transfer; and the third is ! 604: // executeRequest, which implements the actual transfer from the drive. ! 605: // ! 606: // The current implementation of deblockRequest is asynchronous. ! 607: // ! 608: // A diagram and description of the key players: ! 609: // ____ _______________ ______ ______ _______________ ____ ! 610: // ... | | ... | | ... ! 611: // ____|_______________|______ ______|_______________|____ ! 612: // ^ ^ ! 613: // |\___/|\_______/|\_____________/|\_____/| ! 614: // | | | | | | | | | ! 615: // | | | | | | | | |_ byteStart + byteCount ! 616: // | | | | | | | |_ _ _ bytesFinal ! 617: // | | | | | | |_ _ _ _ _ BLOCK BOUNDARY ! 618: // | | | | | | ! 619: // | | | | | |_ _ _ _ _ _ _ _ _ bytesMiddle ! 620: // | | | | |_ _ _ _ _ _ _ _ _ _ _ _ _ BLOCK BOUNDARY ! 621: // | | | | ! 622: // | | | |__ _ _ _ _ _ _ _ _ _ _ _ _ _ _ bytesStart ! 623: // | | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ byteStart ! 624: // | |__ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ offsetStart ! 625: // |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ BLOCK BOUNDARY ! 626: // ! 627: // byteStart = the start of transfer, absolute position on media ! 628: // offsetStart = unaligned offset into the first block, zero if aligned ! 629: // bytesStart = unaligned bytecount for the first block, zero if aligned ! 630: // bytesMiddle = aligned byte count for the middle blocks, zero if none ! 631: // bytesFinal = unaligned byte count for the last block, zero if aligned ! 632: // ! 633: ! 634: UInt64 byteCount = buffer->getLength(); ! 635: DeblockerContext * context = deblockerContextAllocate(); ! 636: bool isWrite = (buffer->getDirection() == kIODirectionOut); ! 637: UInt64 mediaBlockSize = getMediaBlockSize(); ! 638: ! 639: if (context == 0) ! 640: { ! 641: complete(completion, kIOReturnNoMemory); ! 642: return; ! 643: } ! 644: ! 645: // ! 646: // This implementation of the deblocker permits only one read-modify-write ! 647: // at any given time. Note that other write requests can, and do, proceed ! 648: // simultaneously so long as they do not require the deblocker -- refer to ! 649: // the read() and the write() routines for the decision logic. ! 650: // ! 651: ! 652: if (isWrite) IOTakeLock(_deblockerLockForRMWs); ! 653: ! 654: // ! 655: // We split up our calculation logic into two distinct cases: ! 656: // (a) all one block transfers with an unaligned end; ! 657: // (b) all one block transfers with an aligned end and > 2 block transfers ! 658: // ! 659: ! 660: context->offsetStart = byteStart % mediaBlockSize; ! 661: ! 662: if (context->offsetStart + byteCount < mediaBlockSize) // (not '<=') ! 663: { ! 664: context->bytesStart = byteCount; ! 665: context->bytesFinal = 0; ! 666: context->bytesMiddle = 0; ! 667: } ! 668: else // (case b) ! 669: { ! 670: context->bytesStart = (context->offsetStart) ? ! 671: (mediaBlockSize - context->offsetStart) : 0; ! 672: context->bytesFinal = (context->offsetStart + byteCount) % ! 673: mediaBlockSize; ! 674: context->bytesMiddle = byteCount - context->bytesStart - ! 675: context->bytesFinal; ! 676: } ! 677: ! 678: assert(context->bytesMiddle % mediaBlockSize == 0); ! 679: ! 680: // ! 681: // We set the deblock phase to "begin" and pass control to an internal ! 682: // completion routine, which implements the deblocking state machine. ! 683: // ! 684: ! 685: context->phase = (isWrite) ? kPhaseBeginRMW : kPhaseBegin; ! 686: context->bytesTransferred = 0; ! 687: ! 688: context->originalRequest.byteStart = byteStart; ! 689: context->originalRequest.buffer = buffer; ! 690: context->originalRequest.buffer->retain(); // (retain the original buffer) ! 691: context->originalRequest.completion = completion; ! 692: ! 693: if (context->bytesMiddle) // (very bad things) ! 694: { ! 695: context->middleBuffer = IOMemoryDescriptor::withSubRange( ! 696: /* descriptor */ context->originalRequest.buffer, ! 697: /* withOffset */ context->bytesStart, ! 698: /* withLength */ context->bytesMiddle, ! 699: /* withDirection */ context->originalRequest.buffer->getDirection() ); ! 700: assert(context->middleBuffer); ! 701: } ! 702: else ! 703: { ! 704: context->middleBuffer = 0; ! 705: } ! 706: ! 707: context->subrequest.buffer = 0; ! 708: context->subrequest.byteStart = 0; ! 709: context->subrequest.completion.target = this; ! 710: context->subrequest.completion.action = deblockerCompletion; ! 711: context->subrequest.completion.parameter = context; ! 712: ! 713: deblockerCompletion(this, context, kIOReturnSuccess, 0); ! 714: return; ! 715: } ! 716: ! 717: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 718: ! 719: void IODrive::deblockerCompletion(void * target, ! 720: void * parameter, ! 721: IOReturn status, ! 722: UInt64 actualByteCount) ! 723: { ! 724: // ! 725: // This is the completion routine for the aligned deblocker subrequests. ! 726: // It performs work on the just-completed phase, if any, transitions to ! 727: // the next phase, then builds and issues a transfer for the next phase. ! 728: // ! 729: // ! 730: // For write storage requests, the state machine proceeds as follows: ! 731: // ! 732: // kPhaseBeginRMW --> { kPhaseStartRM --> kPhaseStartW } --> ! 733: // { kPhaseMiddleW } --> { kPhaseFinalRM --> kPhaseFinalW } --> kPhaseDone ! 734: // ! 735: // For read storage requests, the state machine proceeds as follows: ! 736: // ! 737: // kPhaseBegin --> { kPhaseStart } --> { kPhaseMiddle } --> { kPhaseFinal } ! 738: // --> kPhaseDone ! 739: // ! 740: // { } denotes skippable states ! 741: // ! 742: ! 743: DeblockerContext * context = (DeblockerContext *) parameter; ! 744: IODirection direction = kIODirectionIn; ! 745: IODrive * drive = (IODrive *) target; ! 746: UInt64 mediaBlockSize = drive->getMediaBlockSize(); ! 747: ! 748: // ! 749: // Complete the work necessary for the just-completed transfer, if any. ! 750: // Note that we go out of our way to squeeze every good byte through to ! 751: // the disk, even if the transfer failed half way through. ! 752: // ! 753: // A note for future developers: be mindful that we haven't checked the ! 754: // returned actual byte count and status yet. ! 755: // ! 756: ! 757: switch (context->phase) ! 758: { ! 759: case kPhaseStart: // - - - - - - - - - - - - - - - - - - - - - - - - - - ! 760: { ! 761: UInt64 bytesToCopy; ! 762: ! 763: // Ensure a sufficient amount of the block was read. ! 764: ! 765: if (actualByteCount > context->offsetStart) ! 766: { ! 767: // Copy the slice of data to be read from the scratch buffer, ! 768: // or the subset thereof if the actual transfer came up short. ! 769: ! 770: bytesToCopy = min(context->bytesStart, ! 771: actualByteCount - context->offsetStart); ! 772: ! 773: bytesToCopy = context->originalRequest.buffer->writeBytes( ! 774: context->bytesTransferred, ! 775: context->blockBufferPtr + context->offsetStart, ! 776: (UInt32)bytesToCopy); ! 777: ! 778: // Bring the total number of bytes transferred up to date. ! 779: ! 780: context->bytesTransferred += bytesToCopy; ! 781: ! 782: // If the actual transfer came up short (due to writeBytes), ! 783: // set an error status. ! 784: ! 785: if (bytesToCopy < context->bytesStart && ! 786: status == kIOReturnSuccess) ! 787: { ! 788: status = kIOReturnNoSpace; // (writeBytes failure) ! 789: } ! 790: } ! 791: } break; ! 792: ! 793: case kPhaseMiddle: // - - - - - - - - - - - - - - - - - - - - - - - - - ! 794: { ! 795: // Bring the total number of bytes transferred up to date. ! 796: ! 797: context->bytesTransferred += actualByteCount; ! 798: } break; ! 799: ! 800: case kPhaseFinal: // - - - - - - - - - - - - - - - - - - - - - - - - - - ! 801: { ! 802: UInt64 bytesToCopy = min(context->bytesFinal, actualByteCount); ! 803: ! 804: // Ensure at least some amount of the block was read. ! 805: ! 806: if (bytesToCopy) ! 807: { ! 808: // Copy the slice of data to be read from the scratch buffer, ! 809: // or the subset thereof if the actual transfer came up short. ! 810: ! 811: bytesToCopy = context->originalRequest.buffer->writeBytes( ! 812: context->bytesTransferred, ! 813: context->blockBufferPtr, ! 814: (UInt32)bytesToCopy); ! 815: ! 816: // Bring the total number of bytes transferred up to date. ! 817: ! 818: context->bytesTransferred += bytesToCopy; ! 819: ! 820: // If the actual transfer came up short (due to writeBytes), ! 821: // set an error status. ! 822: ! 823: if (bytesToCopy < context->bytesFinal && ! 824: status == kIOReturnSuccess) ! 825: { ! 826: status = kIOReturnNoSpace; // (writeBytes failure) ! 827: } ! 828: } ! 829: } break; ! 830: ! 831: case kPhaseStartRM: // - - - - - - - - - - - - - - - - - - - - - - - - - ! 832: { ! 833: // Ensure the entire block was read into the scratch buffer. ! 834: ! 835: if (actualByteCount == mediaBlockSize) ! 836: { ! 837: // Copy the slice of data to be written into the scratch buffer. ! 838: ! 839: if (context->originalRequest.buffer->readBytes( ! 840: context->bytesTransferred, ! 841: context->blockBufferPtr + context->offsetStart, ! 842: (UInt32)context->bytesStart) != (UInt32)context->bytesStart) ! 843: { ! 844: status = kIOReturnInternalError; // (readBytes failure) ! 845: } ! 846: } ! 847: } break; ! 848: ! 849: case kPhaseStartW: // - - - - - - - - - - - - - - - - - - - - - - - - - ! 850: { ! 851: // Ensure the entire block was written and bring total number of ! 852: // bytes transferred up to date. ! 853: ! 854: if (actualByteCount == mediaBlockSize) ! 855: { ! 856: // Bring the total number of bytes transferred up to date. ! 857: context->bytesTransferred += context->bytesStart; ! 858: } ! 859: } break; ! 860: ! 861: case kPhaseMiddleW: // - - - - - - - - - - - - - - - - - - - - - - - - - ! 862: { ! 863: // Bring the total number of bytes transferred up to date. ! 864: context->bytesTransferred += actualByteCount; ! 865: } break; ! 866: ! 867: case kPhaseFinalRM: // - - - - - - - - - - - - - - - - - - - - - - - - - ! 868: { ! 869: // Ensure the entire block was read into the scratch buffer. ! 870: ! 871: if (actualByteCount == mediaBlockSize) ! 872: { ! 873: // Copy the slice of data to be written into the scratch buffer. ! 874: ! 875: if (context->originalRequest.buffer->readBytes( ! 876: context->bytesTransferred, ! 877: context->blockBufferPtr, ! 878: (UInt32)context->bytesFinal) != (UInt32)context->bytesFinal) ! 879: { ! 880: status = kIOReturnInternalError; // (readBytes failure) ! 881: } ! 882: } ! 883: } break; ! 884: ! 885: case kPhaseFinalW: // - - - - - - - - - - - - - - - - - - - - - - - - - ! 886: { ! 887: // Ensure the entire block was written and bring total number of ! 888: // bytes transferred up to date. ! 889: ! 890: if (actualByteCount == mediaBlockSize) ! 891: { ! 892: // Bring the total number of bytes transferred up to date. ! 893: context->bytesTransferred += context->bytesFinal; ! 894: } ! 895: } break; ! 896: ! 897: default: ! 898: { ! 899: // Nothing to do. ! 900: } break; ! 901: } ! 902: ! 903: // ! 904: // Fail the original transfer if an error was detected. ! 905: // ! 906: ! 907: if (context->phase != kPhaseBegin && context->phase != kPhaseBeginRMW) ! 908: { ! 909: assert( status != kIOReturnSuccess || ! 910: actualByteCount == context->subrequest.buffer->getLength() ); ! 911: ! 912: if ( status != kIOReturnSuccess || ! 913: actualByteCount != context->subrequest.buffer->getLength() ) ! 914: { ! 915: // Unlock the RMW-lock, to allow the next RMW to proceed. ! 916: if (context->originalRequest.buffer->getDirection() == ! 917: kIODirectionOut) ! 918: IOUnlock(drive->_deblockerLockForRMWs); ! 919: ! 920: // Release the retain we placed on the request buffer. ! 921: context->originalRequest.buffer->release(); ! 922: ! 923: // Complete the request. ! 924: drive->complete(context->originalRequest.completion, ! 925: status, ! 926: context->bytesTransferred); ! 927: ! 928: // Release the deblocker context structure. ! 929: drive->deblockerContextFree(context); ! 930: return; ! 931: } ! 932: } ! 933: ! 934: // ! 935: // Transistion to the next phase of the unaligned transfer. ! 936: // ! 937: ! 938: switch (context->phase) ! 939: { ! 940: case kPhaseBegin: ! 941: if (context->bytesStart) { context->phase = kPhaseStart; break; } ! 942: case kPhaseStart: ! 943: if (context->bytesMiddle) { context->phase = kPhaseMiddle; break; } ! 944: case kPhaseMiddle: ! 945: if (context->bytesFinal) { context->phase = kPhaseFinal; break; } ! 946: case kPhaseFinal: ! 947: context->phase = kPhaseDone; ! 948: break; ! 949: ! 950: case kPhaseBeginRMW: ! 951: if (context->bytesStart) { context->phase = kPhaseStartRM; break; } ! 952: case kPhaseStartW: ! 953: if (context->bytesMiddle) ! 954: { ! 955: context->phase = kPhaseMiddleW; ! 956: direction = kIODirectionOut; ! 957: break; ! 958: } ! 959: case kPhaseMiddleW: ! 960: if (context->bytesFinal) { context->phase = kPhaseFinalRM; break; } ! 961: case kPhaseFinalW: ! 962: context->phase = kPhaseDone; ! 963: break; ! 964: ! 965: case kPhaseStartRM: ! 966: context->phase = kPhaseStartW; ! 967: direction = kIODirectionOut; ! 968: break; ! 969: case kPhaseFinalRM: ! 970: context->phase = kPhaseFinalW; ! 971: direction = kIODirectionOut; ! 972: break; ! 973: ! 974: default: ! 975: assert(0); ! 976: break; ! 977: } ! 978: ! 979: // ! 980: // Build and execute the transfer for the new phase. ! 981: // ! 982: ! 983: switch (context->phase) ! 984: { ! 985: case kPhaseStart: // - - - - - - - - - - - - - - - - - - - - - - - - - - ! 986: case kPhaseStartRM: ! 987: case kPhaseStartW: ! 988: context->subrequest.byteStart = context->originalRequest.byteStart - ! 989: context->offsetStart; ! 990: context->subrequest.buffer = context->blockBuffer; ! 991: ! 992: context->subrequest.buffer->initWithAddress( ! 993: /* address */ context->blockBufferPtr, ! 994: /* withLength */ mediaBlockSize, ! 995: /* withDirection */ direction ); ! 996: break; ! 997: ! 998: case kPhaseMiddle: // - - - - - - - - - - - - - - - - - - - - - - - - - ! 999: case kPhaseMiddleW: ! 1000: context->subrequest.byteStart = context->originalRequest.byteStart + ! 1001: context->bytesStart; ! 1002: context->subrequest.buffer = context->middleBuffer; ! 1003: ! 1004: assert(context->middleBuffer); // (to fix) ! 1005: break; ! 1006: ! 1007: case kPhaseFinal: // - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1008: case kPhaseFinalRM: ! 1009: case kPhaseFinalW: ! 1010: context->subrequest.byteStart = context->originalRequest.byteStart + ! 1011: context->bytesStart + ! 1012: context->bytesMiddle; ! 1013: context->subrequest.buffer = context->blockBuffer; ! 1014: ! 1015: context->subrequest.buffer->initWithAddress( ! 1016: /* address */ context->blockBufferPtr, ! 1017: /* withLength */ mediaBlockSize, ! 1018: /* withDirection */ direction ); ! 1019: break; ! 1020: ! 1021: case kPhaseDone: // - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1022: ! 1023: // Unlock the RMW-lock, to allow the next RMW to proceed. ! 1024: if (context->originalRequest.buffer->getDirection() == ! 1025: kIODirectionOut) ! 1026: IOUnlock(drive->_deblockerLockForRMWs); ! 1027: ! 1028: // Release the retain we placed on the request buffer. ! 1029: context->originalRequest.buffer->release(); ! 1030: ! 1031: // Complete the request. ! 1032: drive->complete(context->originalRequest.completion, ! 1033: kIOReturnSuccess, ! 1034: context->bytesTransferred); ! 1035: ! 1036: // Release the deblocker context structure. ! 1037: drive->deblockerContextFree(context); ! 1038: return; ! 1039: ! 1040: default: ! 1041: assert(0); ! 1042: break; ! 1043: } ! 1044: ! 1045: // (presuming completion.target/action/parameter are unchanged) ! 1046: ! 1047: drive->prepareRequest(context->subrequest.byteStart, ! 1048: context->subrequest.buffer, ! 1049: context->subrequest.completion); ! 1050: return; ! 1051: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.