Annotation of XNU/iokit/Families/IOStorage/IODrive.cpp, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.