/* * Copyright (c) 1998-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #define super IOStorage OSDefineMetaClassAndStructors(IOBlockStorageDriver, IOStorage) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const UInt32 kPollerInterval = 1000; // (ms, 1 second) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #define isMediaRemovable() _removable #define kIOPropertyProtocolCharacteristicsKey "Protocol Characteristics" #define kIOPropertyPhysicalInterconnectLocationKey "Physical Interconnect Location" #define kIOPropertyExternalKey "External" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static char * strclean(char * s) { // // strclean() trims any spaces at either end of the string, strips any // control characters within the string, and collapses any sequence of // spaces within the string into a single space. // int sourceIndex = 0, targetIndex = 0, targetLength = 0; for ( ; s[sourceIndex] > '\0' && s[sourceIndex] <= ' '; sourceIndex++ ); for ( ; s[sourceIndex]; sourceIndex++ ) { if ( s[sourceIndex] < '\0' || s[sourceIndex] >= ' ' ) { if ( s[sourceIndex] != ' ' ) { if ( targetLength < targetIndex ) { targetIndex = targetLength + 1; } targetLength = targetIndex + 1; } s[targetIndex++] = s[sourceIndex]; } } s[targetLength] = '\0'; return s; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOBlockStorageDevice * IOBlockStorageDriver::getProvider() const { // // Obtain this object's provider. We override the superclass's method to // return a more specific subclass of IOService -- IOBlockStorageDevice. // This method serves simply as a convenience to subclass developers. // return (IOBlockStorageDevice *) IOService::getProvider(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IOBlockStorageDriver::init(OSDictionary * properties) { // // Initialize this object's minimal state. // // Ask our superclass' opinion. if (super::init(properties) == false) return false; // Initialize our state. _expansionData = IONew(ExpansionData, 1); if (_expansionData == 0) return false; initMediaState(); _ejectable = false; _lockable = false; _pollIsExpensive = false; _pollIsRequired = false; _removable = false; _mediaBlockSize = 0; _maxBlockNumber = 0; _maxReadBlockTransfer = 256; _maxWriteBlockTransfer = 256; _maxReadByteTransfer = 131072; _maxWriteByteTransfer = 131072; _maxReadSegmentTransfer = 0; _maxWriteSegmentTransfer = 0; _maxReadSegmentByteTransfer = 0; _maxWriteSegmentByteTransfer = 0; _mediaStateLock = IOLockAlloc(); if (_mediaStateLock == 0) return false; _deblockRequestWriteLock = IOLockAlloc(); _deblockRequestWriteLockCount = 0; _openClients = OSSet::withCapacity(2); _pollerCall = thread_call_allocate(poller, this); _powerEventNotifier = 0; for (unsigned index = 0; index < kStatisticsCount; index++) _statistics[index] = OSNumber::withNumber(0ULL, 64); if (_deblockRequestWriteLock == 0 || _openClients == 0 || _pollerCall == 0) return false; for (unsigned index = 0; index < kStatisticsCount; index++) if (_statistics[index] == 0) return false; // Create our registry properties. OSDictionary * statistics = OSDictionary::withCapacity(kStatisticsCount); if (statistics == 0) return false; statistics->setObject( kIOBlockStorageDriverStatisticsBytesReadKey, _statistics[kStatisticsBytesRead] ); statistics->setObject( kIOBlockStorageDriverStatisticsBytesWrittenKey, _statistics[kStatisticsBytesWritten] ); statistics->setObject( kIOBlockStorageDriverStatisticsReadErrorsKey, _statistics[kStatisticsReadErrors] ); statistics->setObject( kIOBlockStorageDriverStatisticsWriteErrorsKey, _statistics[kStatisticsWriteErrors] ); statistics->setObject( kIOBlockStorageDriverStatisticsLatentReadTimeKey, _statistics[kStatisticsLatentReadTime] ); statistics->setObject( kIOBlockStorageDriverStatisticsLatentWriteTimeKey, _statistics[kStatisticsLatentWriteTime] ); statistics->setObject( kIOBlockStorageDriverStatisticsReadsKey, _statistics[kStatisticsReads] ); statistics->setObject( kIOBlockStorageDriverStatisticsWritesKey, _statistics[kStatisticsWrites] ); statistics->setObject( kIOBlockStorageDriverStatisticsReadRetriesKey, _statistics[kStatisticsReadRetries] ); statistics->setObject( kIOBlockStorageDriverStatisticsWriteRetriesKey, _statistics[kStatisticsWriteRetries] ); statistics->setObject( kIOBlockStorageDriverStatisticsTotalReadTimeKey, _statistics[kStatisticsTotalReadTime] ); statistics->setObject( kIOBlockStorageDriverStatisticsTotalWriteTimeKey, _statistics[kStatisticsTotalWriteTime] ); setProperty(kIOBlockStorageDriverStatisticsKey, statistics); statistics->release(); return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IOBlockStorageDriver::start(IOService * provider) { // // This method is called once we have been attached to the provider object. // // Open the block storage device. if (provider->open(this) == false) return false; // Prepare the block storage driver for operation. if (handleStart(provider) == false) { provider->close(this); return false; } // Initiate the poller mechanism if it is required. if (isMediaRemovable() && isMediaPollRequired() && !isMediaPollExpensive()) { lockForArbitration(); // (disable opens/closes; a recursive lock) if (!isOpen() && !isInactive()) schedulePoller(); // (schedule the poller) unlockForArbitration(); // (enable opens/closes; a recursive lock) } // Register this object so it can be found via notification requests. It is // not being registered to have I/O Kit attempt to have drivers match on it, // which is the reason most other services are registered -- that's not the // intention of this registerService call. registerService(); return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IOBlockStorageDriver::yield(IOService * provider, IOOptionBits options, void * argument) { // // This method is called as a result of a kIOMessageServiceIsRequestingClose // provider message. The argument is passed in as-is from the message. The // options are unused. // bool success; lockForArbitration(); // Yield the block storage device. success = handleYield(provider, options, argument); if (success) { // Close the block storage device. provider->close(this); } unlockForArbitration(); return success; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::free() { // // Free all of this object's outstanding resources. // if (_mediaStateLock) IOLockFree(_mediaStateLock); if (_deblockRequestWriteLock) IOLockFree(_deblockRequestWriteLock); if (_openClients) _openClients->release(); if (_pollerCall) thread_call_free(_pollerCall); for (unsigned index = 0; index < kStatisticsCount; index++) if (_statistics[index]) _statistics[index]->release(); if (_expansionData) IODelete(_expansionData, ExpansionData, 1); super::free(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IOBlockStorageDriver::handleOpen(IOService * client, IOOptionBits options, void * argument) { // // The handleOpen method grants or denies permission to access this object // to an interested client. The argument is an IOStorageAccess value that // specifies the level of access desired -- reader or reader-writer. // // This method can be invoked to upgrade or downgrade the access level for // an existing client as well. The previous access level will prevail for // upgrades that fail, of course. A downgrade should never fail. If the // new access level should be the same as the old for a given client, this // method will do nothing and return success. In all cases, one, singular // close-per-client is expected for all opens-per-client received. // // This method assumes that the arbitration lock is held. // assert(client); // Ensure there is media in the block storage device. if (getMediaState() == kIOMediaStateOffline) return false; // Handle the first open on removable media in a special case. if (isMediaRemovable() && _openClients->getCount() == 0) { // Halt the poller if it is active and this is the first open. if (isMediaPollRequired() && !isMediaPollExpensive()) unschedulePoller(); // (unschedule the poller) // Lock down the media while we have opens on this driver object. lockMedia(true); } // Process the open. _openClients->setObject(client); // (works for up/downgrade case) return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IOBlockStorageDriver::handleIsOpen(const IOService * client) const { // // The handleIsOpen method determines whether the specified client, or any // client if none is specificed, presently has an open on this object. // // This method assumes that the arbitration lock is held. // if (client) return _openClients->containsObject(client); else return (_openClients->getCount() != 0); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::handleClose(IOService * client, IOOptionBits options) { // // The handleClose method drops the incoming client's access to this object. // // This method assumes that the arbitration lock is held. // assert(client); // Process the close. _openClients->removeObject(client); // Handle the last close in a special case. if (_openClients->getCount() == 0) { if (isInactive()) { // Yield the block storage device. message(kIOMessageServiceIsRequestingClose, getProvider(), 0); } else { if (isMediaRemovable()) { // Unlock the removable media. if (getMediaState() == kIOMediaStateOnline) { lockMedia(false); } // Reactivate the poller. if (isMediaPollRequired() && !isMediaPollExpensive()) schedulePoller(); // (schedule the poller) } } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::read(IOService * /* client */, UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion) { // // The read method is the receiving end for all read requests from the // storage framework, ie. via the media object created by this driver. // // This method initiates a sequence of methods (stages) for each read/write // request. The first is prepareRequest, which allocates and prepares some // context for the transfer; the second is deblockRequest, which aligns the // transfer at the media's block boundaries; third is breakUpRequest, which // breaks up the transfer into multiple sub-transfers when certain hardware // constraints are exceeded; fourth is executeRequest, which implements the // actual transfer from the block storage device. // // State our assumptions. assert(buffer->getDirection() == kIODirectionIn); // Prepare the transfer. prepareRequest(byteStart, buffer, completion); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::write(IOService * /* client */, UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion) { // // The write method is the receiving end for all write requests from the // storage framework, ie. via the media object created by this driver. // // This method initiates a sequence of methods (stages) for each read/write // request. The first is prepareRequest, which allocates and prepares some // context for the transfer; the second is deblockRequest, which aligns the // transfer at the media's block boundaries; third is breakUpRequest, which // breaks up the transfer into multiple sub-transfers when certain hardware // constraints are exceeded; fourth is executeRequest, which implements the // actual transfer from the block storage device. // // State our assumptions. assert(buffer->getDirection() == kIODirectionOut); // Prepare the transfer. prepareRequest(byteStart, buffer, completion); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::addToBytesTransferred(UInt64 bytesTransferred, UInt64 totalTime, // (ns) UInt64 latentTime, // (ns) bool isWrite) { // // Update the total number of bytes transferred, the total transfer time, // and the total latency time -- used for statistics. // if (isWrite) { _statistics[kStatisticsWrites]->addValue(1); _statistics[kStatisticsBytesWritten]->addValue(bytesTransferred); _statistics[kStatisticsTotalWriteTime]->addValue(totalTime); _statistics[kStatisticsLatentWriteTime]->addValue(latentTime); if (bytesTransferred <= getMediaBlockSize()) _statistics[kStatisticsSingleBlockWrites]->addValue(1); } else { _statistics[kStatisticsReads]->addValue(1); _statistics[kStatisticsBytesRead]->addValue(bytesTransferred); _statistics[kStatisticsTotalReadTime]->addValue(totalTime); _statistics[kStatisticsLatentReadTime]->addValue(latentTime); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::incrementRetries(bool isWrite) { // // Update the total retry count -- used for statistics. // if (isWrite) _statistics[kStatisticsWriteRetries]->addValue(1); else _statistics[kStatisticsReadRetries]->addValue(1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::incrementErrors(bool isWrite) { // // Update the total error count -- used for statistics. // if (isWrite) _statistics[kStatisticsWriteErrors]->addValue(1); else _statistics[kStatisticsReadErrors]->addValue(1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UInt32 IOBlockStorageDriver::getStatistics(UInt64 * statistics, UInt32 statisticsMaxCount) const { // // Ask the driver to report its operating statistics. // // The statistics are each indexed by IOBlockStorageDriver::Statistics // indices. This routine fills the caller's buffer, up to the maximum // count specified if the real number of statistics would overflow the // buffer. The return value indicates the actual number of statistics // copied to the buffer. // // If the statistics buffer is not supplied or if the maximum count is // zero, the routine returns the proposed count of statistics instead. // if (statistics == 0) return kStatisticsCount; UInt32 statisticsCount = min(kStatisticsCount, statisticsMaxCount); for (unsigned index = 0; index < statisticsCount; index++) statistics[index] = _statistics[index]->unsigned64BitValue(); return statisticsCount; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UInt64 IOBlockStorageDriver::getStatistic(Statistics statistic) const { // // Ask the driver to report one of its operating statistics. // if ((UInt32) statistic >= kStatisticsCount) return 0; return _statistics[statistic]->unsigned64BitValue(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOBlockStorageDriver::Context * IOBlockStorageDriver::allocateContext() { // // Allocate a context structure for a read/write operation. // Context * context = IONew(Context, 1); if (context) { bzero(context, sizeof(Context)); } return context; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::deleteContext( IOBlockStorageDriver::Context * context) { // // Delete a context structure from a read/write operation. // IODelete(context, Context, 1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::prepareRequest(UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion) { // // The prepareRequest method allocates and prepares state for the transfer. // // This method is part of a sequence of methods invoked for each read/write // request. The first is prepareRequest, which allocates and prepares some // context for the transfer; the second is deblockRequest, which aligns the // transfer at the media's block boundaries; third is breakUpRequest, which // breaks up the transfer into multiple sub-transfers when certain hardware // constraints are exceeded; fourth is executeRequest, which implements the // actual transfer from the block storage device. // Context * context; // Allocate a context structure to hold some of our state. context = allocateContext(); if (context == 0) { complete(completion, kIOReturnNoMemory); return; } // Fill in the context structure with some of our state. context->block.size = getMediaBlockSize(); context->block.type = kBlockTypeStandard; context->original.byteStart = byteStart; context->original.buffer = buffer; context->original.buffer->retain(); context->original.completion = completion; completion.target = this; completion.action = prepareRequestCompletion; completion.parameter = context; // Deblock the transfer. deblockRequest(byteStart, buffer, completion, context); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::prepareRequestCompletion(void * target, void * parameter, IOReturn status, UInt64 actualByteCount) { // // This is the completion routine for the prepared request. It updates // the driver's statistics, performs some clean up work, then calls the // original request's completion routine. // Context * context = (Context *) parameter; IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target; bool isWrite; isWrite = (context->original.buffer->getDirection() == kIODirectionOut); // Update the error state, on a short transfer. if (actualByteCount < context->original.buffer->getLength()) { if (status == kIOReturnSuccess) { status = kIOReturnUnderrun; } } // Update the dirtied state. if (actualByteCount && isWrite) { driver->_mediaDirtied = true; } // Update the total number of bytes transferred. driver->addToBytesTransferred(actualByteCount, 0, 0, isWrite); // Update the total error count. if (status != kIOReturnSuccess) { driver->incrementErrors(isWrite); } // Complete the transfer request. IOStorage::complete(context->original.completion, status, actualByteCount); // Release our resources. context->original.buffer->release(); driver->deleteContext(context); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::schedulePoller() { // // Schedule the poller mechanism. // // This method assumes that the arbitration lock is held. // AbsoluteTime deadline; retain(); clock_interval_to_deadline(kPollerInterval, kMillisecondScale, &deadline); thread_call_enter_delayed(_pollerCall, deadline); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::unschedulePoller() { // // Unschedule the poller mechanism. // // This method assumes that the arbitration lock is held. // if (thread_call_cancel(_pollerCall)) release(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::poller(void * target, void *) { // // This method is the timeout handler for the poller mechanism. It polls // for media and reschedules another timeout if there are still no opens. // IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target; driver->pollMedia(); driver->lockForArbitration(); // (disable opens/closes; a recursive lock) if (!driver->isOpen() && !driver->isInactive()) driver->schedulePoller(); // (schedule the poller) driver->unlockForArbitration(); // (enable opens/closes; a recursive lock) driver->release(); // (drop the retain associated with this poll) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOReturn IOBlockStorageDriver::message(UInt32 type, IOService * provider, void * argument) { // // Generic entry point for calls from the provider. A return value of // kIOReturnSuccess indicates that the message was received, and where // applicable, that it was successful. // switch (type) { case kIOMessageMediaStateHasChanged: { IOReturn status; IOLockLock(_mediaStateLock); status = mediaStateHasChanged((IOMediaState) argument); IOLockUnlock(_mediaStateLock); return status; } case kIOMessageServiceIsRequestingClose: { bool success; success = yield(provider, 0, argument); return success ? kIOReturnSuccess : kIOReturnBusy; } default: { return super::message(type, provider, argument); } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* Accept a new piece of media, doing whatever's necessary to make it * show up properly to the system. The arbitration lock is assumed to * be held during the call. */ IOReturn IOBlockStorageDriver::acceptNewMedia(void) { IOReturn result; bool ok; UInt64 nbytes; char name[128]; bool nameSep; if (_maxBlockNumber) { nbytes = _mediaBlockSize * (_maxBlockNumber + 1); } else { nbytes = 0; } /* Instantiate a media object and attach it to ourselves. */ name[0] = 0; nameSep = false; if (getProvider()->getVendorString()) { strcat(name, getProvider()->getVendorString()); nameSep = true; } if (getProvider()->getProductString()) { if (nameSep == true) strcat(name, " "); strcat(name, getProvider()->getProductString()); nameSep = true; } if (nameSep == true) strcat(name, " "); strcat(name, "Media"); strclean(name); _mediaObject = instantiateMediaObject(0,nbytes,_mediaBlockSize,name); result = (_mediaObject) ? kIOReturnSuccess : kIOReturnBadArgument; if (result == kIOReturnSuccess) { if (getProperty(kIOMediaIconKey, gIOServicePlane)) { _mediaObject->removeProperty(kIOMediaIconKey); } ok = _mediaObject->attach(this); /* attach media object above us */ if (ok) { IOService *parent = this; OSNumber *unit = NULL; OSNumber *unitLUN = NULL; OSString *unitName = NULL; /* Wire the media object to the device tree. */ while ((parent = parent->getProvider())) { if (!unit) { unit = OSDynamicCast(OSNumber, parent->getProperty("IOUnit")); } if (!unitLUN) { unitLUN = OSDynamicCast(OSNumber, parent->getProperty("IOUnitLUN")); } if (!unitName) { unitName = OSDynamicCast(OSString, parent->getProperty("IOUnitName")); } if (parent->inPlane(gIODTPlane)) { IORegistryEntry *child; IORegistryIterator *children; if (!unit || !parent->getProvider()) { break; } children = IORegistryIterator::iterateOver(parent, gIODTPlane); if (!children) { break; } while ((child = children->getNextObject())) { if (!OSDynamicCast(IOMedia, child)) { child->detachAll(gIODTPlane); } } children->release(); if (_mediaObject->attachToParent(parent, gIODTPlane)) { char location[ 32 ]; if (unitLUN && unitLUN->unsigned32BitValue()) { sprintf(location, "%x,%x:0", unit->unsigned32BitValue(), unitLUN->unsigned32BitValue()); } else { sprintf(location, "%x:0", unit->unsigned32BitValue()); } _mediaObject->setLocation(location, gIODTPlane); _mediaObject->setName(unitName ? unitName->getCStringNoCopy() : "", gIODTPlane); } break; } } _mediaPresent = true; _mediaObject->registerService(); /* enable matching */ } else { _mediaObject->release(); _mediaObject = 0; return(kIOReturnNoMemory); /* give up now */ } } return(result); } IOReturn IOBlockStorageDriver::checkForMedia(void) { IOReturn result; bool currentState; bool changed; IOLockLock(_mediaStateLock); result = getProvider()->reportMediaState(¤tState,&changed); if (result != kIOReturnSuccess) { /* the poll operation failed */ IOLog("%s[IOBlockStorageDriver]::checkForMedia; err '%s' from reportMediaState\n", getName(),stringFromReturn(result)); } else { changed = currentState ? !_mediaPresent : _mediaPresent; if (changed) { /* the poll succeeded, media state has changed */ result = mediaStateHasChanged(currentState ? kIOMediaStateOnline : kIOMediaStateOffline); } } IOLockUnlock(_mediaStateLock); return(result); } IOReturn IOBlockStorageDriver::mediaStateHasChanged(IOMediaState state) { IOReturn result; /* The media has changed state. See if it's just inserted or removed. */ if (state == kIOMediaStateOnline) { /* media is now present */ if (_mediaPresent) { return(kIOReturnBadArgument); } /* Allow a subclass to decide whether we accept the media. Such a * decision might be based on things like password-protection, etc. */ if (validateNewMedia() == false) { /* we're told to reject it */ rejectMedia(); /* so let subclass do whatever it wants */ return(kIOReturnSuccess); /* pretend nothing happened */ } result = recordMediaParameters(); /* learn about media */ if (result != kIOReturnSuccess) { /* couldn't record params */ initMediaState(); /* deny existence of new media */ IOLog("%s[IOBlockStorageDriver]::checkForMedia: err '%s' from recordMediaParameters\n", getName(),stringFromReturn(result)); return(result); } /* Now we do what's necessary to make the new media * show up properly in the system. */ lockForArbitration(); result = acceptNewMedia(); if (result != kIOReturnSuccess) { initMediaState(); /* deny existence of new media */ IOLog("%s[IOBlockStorageDriver]::checkForMedia; err '%s' from acceptNewMedia\n", getName(),stringFromReturn(result)); } unlockForArbitration(); return(result); /* all done, new media is ready */ } else { /* media is now absent */ lockForArbitration(); result = decommissionMedia(true); /* force a teardown */ unlockForArbitration(); if (result != kIOReturnSuccess && result != kIOReturnNoMedia) { IOLog("%s[IOBlockStorageDriver]::checkForMedia; err '%s' from decommissionNewMedia\n", getName(),stringFromReturn(result)); return(result); } return(kIOReturnSuccess); /* all done; media is gone */ } } UInt64 IOBlockStorageDriver::constrainByteCount(UInt64 /* requestedCount */ ,bool isWrite) { if (isWrite) { return(_maxWriteByteTransfer); } else { return(_maxReadByteTransfer); } } /* Decommission a piece of media that has become unavailable either due to * ejection or some outside force (e.g. the Giant Hand of the User). * (I prefer the term "decommission" rather than "abandon." The former implies * a well-determined procedure, whereas the latter implies leaving the media * in an orphaned state.) */ /* Tear down the stack above the specified object. Usually these objects will * be of type IOMedia, but they could be any IOService. The arbitration lock is * assumed to be held during the call. */ IOReturn IOBlockStorageDriver::decommissionMedia(bool forcible) { IOReturn result; if (_mediaObject) { /* If this is a forcible decommission (i.e. media is gone), we don't * care whether the teardown worked; we forget about the media. */ if ((forcible || !_openClients->containsObject(_mediaObject)) && (isInactive() || _mediaObject->terminate() || forcible)) { IORegistryEntry * parent; /* Unwire the media object from the device tree. */ if ( (parent = _mediaObject->getParentEntry(gIODTPlane)) ) { _mediaObject->detachFromParent(parent, gIODTPlane); } _mediaObject->release(); _mediaObject = 0; initMediaState(); /* clear all knowledge of the media */ result = kIOReturnSuccess; } else { result = kIOReturnBusy; } } else { result = kIOReturnNoMedia; } return(result); } IOReturn IOBlockStorageDriver::ejectMedia(void) { IOReturn result; IOLockLock(_mediaStateLock); if (isMediaEjectable()) { bool mediaDirtied = _mediaDirtied; lockForArbitration(); result = decommissionMedia(false); /* try to teardown */ unlockForArbitration(); if (result == kIOReturnSuccess) { /* eject */ if (mediaDirtied) { synchronizeCache(this); } (void)lockMedia(false); (void)getProvider()->doEjectMedia(); /* ignore any error */ } } else /* if (!isMediaEjectable()) */ { lockForArbitration(); if (_mediaObject) { if (!_openClients->containsObject(_mediaObject)) { result = kIOReturnSuccess; } else { result = kIOReturnBusy; } } else { result = kIOReturnNoMedia; } if (result == kIOReturnSuccess) { /* eject */ if (_mediaDirtied) { _mediaDirtied = false; synchronizeCache(this); } if (isMediaRemovable()) (void)lockMedia(false); (void)getProvider()->doEjectMedia(); /* ignore any error */ } unlockForArbitration(); } IOLockUnlock(_mediaStateLock); return(result); } void IOBlockStorageDriver::executeRequest(UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion, IOBlockStorageDriver::Context * context) { UInt64 block; UInt64 nblks; IOReturn result; if (!_mediaPresent) { /* no media? you lose */ complete(completion, kIOReturnNoMedia,0); return; } /* We know that we are never called with a request too large, * nor one that is misaligned with a block. */ assert((byteStart % context->block.size) == 0); assert((buffer->getLength() % context->block.size) == 0); block = byteStart / context->block.size; nblks = buffer->getLength() / context->block.size; /* Now the protocol-specific provider implements the actual * start of the data transfer: */ result = getProvider()->doAsyncReadWrite(buffer,block,nblks,completion); if (result != kIOReturnSuccess) { /* it failed to start */ IOLog("%s[IOBlockStorageDriver]; executeRequest: request failed to start!\n",getName()); complete(completion,result); return; } } IOReturn IOBlockStorageDriver::formatMedia(UInt64 byteCapacity) { IOReturn result; IOLockLock(_mediaStateLock); lockForArbitration(); result = decommissionMedia(false); /* try to teardown */ unlockForArbitration(); if (result == kIOReturnSuccess) { /* format */ result = getProvider()->doFormatMedia(byteCapacity); if (result == kIOReturnSuccess) { result = mediaStateHasChanged(kIOMediaStateOnline); } else { (void)mediaStateHasChanged(kIOMediaStateOnline); } } IOLockUnlock(_mediaStateLock); return(result); } const char * IOBlockStorageDriver::getDeviceTypeName(void) { return(kIOBlockStorageDeviceTypeGeneric); } UInt32 IOBlockStorageDriver::getFormatCapacities(UInt64 * capacities, UInt32 capacitiesMaxCount) const { return(getProvider()->doGetFormatCapacities(capacities,capacitiesMaxCount)); } UInt64 IOBlockStorageDriver::getMediaBlockSize() const { return(_mediaBlockSize); } IOMediaState IOBlockStorageDriver::getMediaState() const { if (_mediaPresent) { return(kIOMediaStateOnline); } else { return(kIOMediaStateOffline); } } IOReturn IOBlockStorageDriver::handlePowerEvent(void *target,void *refCon, UInt32 messageType,IOService *provider, void *messageArgument,vm_size_t argSize) { IOBlockStorageDriver *driver = (IOBlockStorageDriver *)target; IOReturn result; switch (messageType) { case kIOMessageSystemWillPowerOff: case kIOMessageSystemWillRestart: if (!driver->isInactive()) { if (driver->_mediaPresent) { if (driver->_mediaDirtied) { driver->synchronizeCache(driver); } if (!driver->isMediaRemovable() && (messageType == kIOMessageSystemWillPowerOff)) { driver->getProvider()->doEjectMedia(); } } } result = kIOReturnSuccess; break; default: result = kIOReturnUnsupported; break; } return(result); } bool IOBlockStorageDriver::handleStart(IOService * provider) { IOReturn result; OSNumber * maxBlockCountRead; OSNumber * maxBlockCountWrite; OSNumber * maxByteCountRead; OSNumber * maxByteCountWrite; OSNumber * maxSegmentCountRead; OSNumber * maxSegmentCountWrite; OSNumber * maxSegmentByteCountRead; OSNumber * maxSegmentByteCountWrite; /* The protocol-specific provider determines whether the media is removable. */ result = getProvider()->reportRemovability(&_removable); if (result != kIOReturnSuccess) { IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportRemovability\n", getName(),stringFromReturn(result)); return(false); } if (_removable) { /* The protocol-specific provider determines whether we must poll to detect * media insertion. Nonremovable devices never need polling. */ result = getProvider()->reportPollRequirements(&_pollIsRequired,&_pollIsExpensive); if (result != kIOReturnSuccess) { IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportPollRequirements\n", getName(),stringFromReturn(result)); return(false); } /* The protocol-specific provider determines whether the media is ejectable * under software control. */ result = getProvider()->reportEjectability(&_ejectable); if (result != kIOReturnSuccess) { IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportEjectability\n", getName(),stringFromReturn(result)); return(false); } /* The protocol-specific provider determines whether the media is lockable * under software control. */ result = getProvider()->reportLockability(&_lockable); if (result != kIOReturnSuccess) { IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportLockability\n", getName(),stringFromReturn(result)); return(false); } } else { /* fixed disk: not ejectable, not lockable */ _ejectable = false; _lockable = false; _pollIsRequired = true; /* polling detects device disappearance */ } /* Obtain the constraint values for reads and writes. */ maxBlockCountRead = OSDynamicCast( /* class */ OSNumber, /* object */ getProperty( /* key */ kIOMaximumBlockCountReadKey, /* plane */ gIOServicePlane ) ); maxBlockCountWrite = OSDynamicCast( /* class */ OSNumber, /* object */ getProperty( /* key */ kIOMaximumBlockCountWriteKey, /* plane */ gIOServicePlane ) ); maxByteCountRead = OSDynamicCast( /* class */ OSNumber, /* object */ getProperty( /* key */ kIOMaximumByteCountReadKey, /* plane */ gIOServicePlane ) ); maxByteCountWrite = OSDynamicCast( /* class */ OSNumber, /* object */ getProperty( /* key */ kIOMaximumByteCountWriteKey, /* plane */ gIOServicePlane ) ); maxSegmentCountRead = OSDynamicCast( /* class */ OSNumber, /* object */ getProperty( /* key */ kIOMaximumSegmentCountReadKey, /* plane */ gIOServicePlane ) ); maxSegmentCountWrite = OSDynamicCast( /* class */ OSNumber, /* object */ getProperty( /* key */ kIOMaximumSegmentCountWriteKey, /* plane */ gIOServicePlane ) ); maxSegmentByteCountRead = OSDynamicCast( /* class */ OSNumber, /* object */ getProperty( /* key */ kIOMaximumSegmentByteCountReadKey, /* plane */ gIOServicePlane ) ); maxSegmentByteCountWrite = OSDynamicCast( /* class */ OSNumber, /* object */ getProperty( /* key */ kIOMaximumSegmentByteCountWriteKey, /* plane */ gIOServicePlane ) ); /* Obtain the constraint values for reads and writes (old method). */ if ( maxBlockCountRead == 0 || maxBlockCountWrite == 0 ) { UInt64 maxReadTransfer; UInt64 maxWriteTransfer; result = getProvider()->reportMaxReadTransfer(512, &maxReadTransfer); if (result == kIOReturnSuccess) { _maxReadBlockTransfer = maxReadTransfer / 512; } result = getProvider()->reportMaxWriteTransfer(512, &maxWriteTransfer); if (result == kIOReturnSuccess) { _maxWriteBlockTransfer = maxWriteTransfer / 512; } } _maxReadSegmentTransfer = _maxReadByteTransfer / page_size; _maxWriteSegmentTransfer = _maxWriteByteTransfer / page_size; _maxReadByteTransfer = 0; _maxWriteByteTransfer = 0; /* Process the constraint values: */ if ( maxBlockCountRead ) { _maxReadBlockTransfer = maxBlockCountRead->unsigned64BitValue(); } if ( maxBlockCountWrite ) { _maxWriteBlockTransfer = maxBlockCountWrite->unsigned64BitValue(); } if ( maxByteCountRead ) { _maxReadByteTransfer = maxByteCountRead->unsigned64BitValue(); } if ( maxByteCountWrite ) { _maxWriteByteTransfer = maxByteCountWrite->unsigned64BitValue(); } if ( maxSegmentCountRead ) { _maxReadSegmentTransfer = maxSegmentCountRead->unsigned64BitValue(); } if ( maxSegmentCountWrite ) { _maxWriteSegmentTransfer = maxSegmentCountWrite->unsigned64BitValue(); } if ( maxSegmentByteCountRead ) { _maxReadSegmentByteTransfer = maxSegmentByteCountRead->unsigned64BitValue(); } if ( maxSegmentByteCountWrite ) { _maxWriteSegmentByteTransfer = maxSegmentByteCountWrite->unsigned64BitValue(); } /* Publish the default constraint values: */ if ( maxBlockCountRead == 0 ) { getProvider()->setProperty(kIOMaximumBlockCountReadKey, _maxReadBlockTransfer, 64); } if ( maxBlockCountWrite == 0 ) { getProvider()->setProperty(kIOMaximumBlockCountWriteKey, _maxWriteBlockTransfer, 64); } if ( maxSegmentCountRead == 0 ) { getProvider()->setProperty(kIOMaximumSegmentCountReadKey, _maxReadSegmentTransfer, 64); } if ( maxSegmentCountWrite == 0 ) { getProvider()->setProperty(kIOMaximumSegmentCountWriteKey, _maxWriteSegmentTransfer, 64); } /* Check for the device being ready with media inserted: */ result = checkForMedia(); /* The poll should never fail for nonremovable media: */ if (result != kIOReturnSuccess && !_removable) { IOLog("%s[IOBlockStorageDriver]::handleStart: err '%s' from checkForMedia\n", getName(),stringFromReturn(result)); return(false); } /* Set up the power event handler for restarts and shutdowns: */ _powerEventNotifier = registerPrioritySleepWakeInterest(handlePowerEvent,this); if (_powerEventNotifier) { retain(); } return(true); } /* The driver has been instructed to yield. The arbitration lock is assumed to * be held during the call. */ bool IOBlockStorageDriver::handleYield(IOService * provider, IOOptionBits options, void * argument) { // Determine whether there are outstanding opens on this driver object. if (isOpen()) { return false; } // Halt the poller mechanism. if (isMediaRemovable() && isMediaPollRequired() && !isMediaPollExpensive()) { unschedulePoller(); // (unschedule the poller) } // Force a teardown. decommissionMedia(true); return true; } void IOBlockStorageDriver::initMediaState(void) { _mediaDirtied = false; _mediaPresent = false; _mediaType = 0; _writeProtected = false; } IOMedia * IOBlockStorageDriver::instantiateDesiredMediaObject(void) { return(new IOMedia); } IOMedia * IOBlockStorageDriver::instantiateMediaObject(UInt64 base,UInt64 byteSize, UInt32 blockSize,char *mediaName) { IOMediaAttributeMask attributes = 0; IOMedia *m; bool result; m = instantiateDesiredMediaObject(); if (m == NULL) { return(NULL); } attributes |= _ejectable ? kIOMediaAttributeEjectableMask : 0; attributes |= _removable ? kIOMediaAttributeRemovableMask : 0; result = m->init( base, /* base byte offset */ byteSize, /* byte size */ blockSize, /* preferred block size */ attributes, /* attributes */ true, /* TRUE if whole physical media */ !_writeProtected, /* TRUE if writable */ ""); /* content hint */ if (result) { char *picture = "External.icns"; if (_removable) { picture = "Removable.icns"; } else { OSDictionary *dictionary = OSDynamicCast(OSDictionary, getProvider()->getProperty(kIOPropertyProtocolCharacteristicsKey)); if (dictionary) { OSString *string = OSDynamicCast(OSString, dictionary->getObject(kIOPropertyPhysicalInterconnectLocationKey)); if (string) { const char *value = string->getCStringNoCopy(); if (value && !strcmp(value, kIOPropertyExternalKey)) { picture = "External.icns"; } else { picture = "Internal.icns"; } } } } if (picture) { OSDictionary *dictionary = OSDictionary::withCapacity(2); OSString *identifier = OSString::withCString("com.apple.iokit.IOStorageFamily"); OSString *resourceFile = OSString::withCString(picture); if (dictionary && identifier && resourceFile) { dictionary->setObject("CFBundleIdentifier", identifier); dictionary->setObject("IOBundleResourceFile", resourceFile); } m->setProperty(kIOMediaIconKey, dictionary); if (resourceFile) { resourceFile->release(); } if (identifier) { identifier->release(); } if (dictionary) { dictionary->release(); } } m->setName(mediaName); return(m); } else { /* some init error */ m->release(); return(NULL); /* beats me...call it this error */ } } bool IOBlockStorageDriver::isMediaEjectable(void) const { return(_ejectable); } bool IOBlockStorageDriver::isMediaPollExpensive(void) const { return(_pollIsExpensive); } bool IOBlockStorageDriver::isMediaPollRequired(void) const { return(_pollIsRequired); } bool IOBlockStorageDriver::isMediaWritable(void) const { return(!_writeProtected); } IOReturn IOBlockStorageDriver::lockMedia(bool locked) { if (_lockable) { return(getProvider()->doLockUnlockMedia(locked)); } else { return(kIOReturnUnsupported); } } IOReturn IOBlockStorageDriver::pollMedia(void) { if (!_pollIsRequired) { /* shouldn't poll; it's an error */ return(kIOReturnUnsupported); } else { /* poll is required...do it */ return(checkForMedia()); } } IOReturn IOBlockStorageDriver::recordMediaParameters(void) { IOReturn result; /* Determine the device's block size and max block number. * What should an unformatted device report? All zeroes. */ result = getProvider()->reportBlockSize(&_mediaBlockSize); if (result != kIOReturnSuccess) { goto err; } result = getProvider()->reportMaxValidBlock(&_maxBlockNumber); if (result != kIOReturnSuccess) { goto err; } /* Is the media write-protected? */ result = getProvider()->reportWriteProtection(&_writeProtected); if (result != kIOReturnSuccess) { goto err; } return(kIOReturnSuccess); /* everything was successful */ /* If we fall thru to here, we had some kind of error. Set everything to * a reasonable state since we haven't got any real information. */ err: _mediaPresent = false; _writeProtected = true; return(result); } void IOBlockStorageDriver::rejectMedia(void) { (void)getProvider()->doEjectMedia(); /* eject it, ignoring any error */ initMediaState(); /* deny existence of new media */ } void IOBlockStorageDriver::stop(IOService * provider) { if (_powerEventNotifier) { _powerEventNotifier->remove(); _powerEventNotifier = NULL; release(); } super::stop(provider); } IOReturn IOBlockStorageDriver::synchronizeCache(IOService *client) { return(getProvider()->doSynchronizeCache()); } bool IOBlockStorageDriver::validateNewMedia(void) { return(true); } // ----------------------------------------------------------------------------- // Deblocker Implementation #include class IODeblocker : public IOMemoryDescriptor { OSDeclareDefaultStructors(IODeblocker); protected: UInt64 _blockSize; struct { IOMemoryDescriptor * buffer; UInt32 offset; UInt32 length; } _chunks[3]; UInt32 _chunksCount; IOBufferMemoryDescriptor * _excessBuffer; UInt64 _excessCountFinal; UInt64 _excessCountStart; IOMemoryDescriptor * _requestBuffer; IOStorageCompletion _requestCompletion; void * _requestContext; UInt64 _requestCount; bool _requestIsOneBlock; UInt64 _requestStart; enum { kStageInit, kStagePrepareExcessStart, kStagePrepareExcessFinal, kStageLast, kStageDone } _stage; virtual void free(); virtual bool initWithAddress( void * address, /* not supported */ IOByteCount withLength, IODirection withDirection ); virtual bool initWithAddress( vm_address_t address, /* not supported */ IOByteCount withLength, IODirection withDirection, task_t withTask ); virtual bool initWithPhysicalAddress( IOPhysicalAddress address, /* not supported */ IOByteCount withLength, IODirection withDirection ); virtual bool initWithPhysicalRanges( IOPhysicalRange * ranges, /* not supproted */ UInt32 withCount, IODirection withDirection, bool asReference = false ); virtual bool initWithRanges( IOVirtualRange * ranges, /* not supported */ UInt32 withCount, IODirection withDirection, task_t withTask, bool asReference = false ); virtual void * getVirtualSegment( IOByteCount offset, /* not supported */ IOByteCount * length ); IOMemoryDescriptor::withAddress; /* not supported */ IOMemoryDescriptor::withPhysicalAddress; /* not supported */ IOMemoryDescriptor::withPhysicalRanges; /* not supported */ IOMemoryDescriptor::withRanges; /* not supported */ IOMemoryDescriptor::withSubRange; /* not supported */ public: static IODeblocker * withBlockSize( UInt64 blockSize, UInt64 withRequestStart, IOMemoryDescriptor * withRequestBuffer, IOStorageCompletion withRequestCompletion, void * withRequestContext ); virtual bool initWithBlockSize( UInt64 blockSize, UInt64 withRequestStart, IOMemoryDescriptor * withRequestBuffer, IOStorageCompletion withRequestCompletion, void * withRequestContext ); virtual IOPhysicalAddress getPhysicalSegment( IOByteCount offset, IOByteCount * length ); virtual IOReturn prepare(IODirection forDirection = kIODirectionNone); virtual IOReturn complete(IODirection forDirection = kIODirectionNone); virtual IOByteCount readBytes( IOByteCount offset, void * bytes, IOByteCount withLength ); virtual IOByteCount writeBytes( IOByteCount offset, const void * bytes, IOByteCount withLength ); virtual bool getNextStage(UInt64 * byteStart); virtual void getRequestCompletion( IOStorageCompletion * completion, IOReturn * status, UInt64 * actualByteCount ); virtual IOMemoryDescriptor * getRequestBuffer(); virtual void * getRequestContext(); }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #undef super #define super IOMemoryDescriptor OSDefineMetaClassAndStructors(IODeblocker, IOMemoryDescriptor) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IODeblocker::initWithAddress( void * /* address */ , IOByteCount /* withLength */ , IODirection /* withDirection */ ) { return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IODeblocker::initWithAddress( vm_address_t /* address */ , IOByteCount /* withLength */ , IODirection /* withDirection */ , task_t /* withTask */ ) { return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IODeblocker::initWithPhysicalAddress( IOPhysicalAddress /* address */ , IOByteCount /* withLength */ , IODirection /* withDirection */ ) { return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IODeblocker::initWithPhysicalRanges( IOPhysicalRange * /* ranges */ , UInt32 /* withCount */ , IODirection /* withDirection */ , bool /* asReference */ ) { return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IODeblocker::initWithRanges( IOVirtualRange * /* ranges */ , UInt32 /* withCount */ , IODirection /* withDirection */ , task_t /* withTask */ , bool /* asReference */ ) { return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IODeblocker * IODeblocker::withBlockSize( UInt64 blockSize, UInt64 withRequestStart, IOMemoryDescriptor * withRequestBuffer, IOStorageCompletion withRequestCompletion, void * withRequestContext ) { // // Create a new IODeblocker. // IODeblocker * me = new IODeblocker; if ( me && me->initWithBlockSize( /* blockSize */ blockSize, /* withRequestStart */ withRequestStart, /* withRequestBuffer */ withRequestBuffer, /* withRequestCompletion */ withRequestCompletion, /* withRequestContext */ withRequestContext ) == false ) { me->release(); me = 0; } return me; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IODeblocker::initWithBlockSize( UInt64 blockSize, UInt64 withRequestStart, IOMemoryDescriptor * withRequestBuffer, IOStorageCompletion withRequestCompletion, void * withRequestContext ) { // // Initialize an IODeblocker. // // _excessCountStart = byte count from media boundary to start of request // _excessCountFinal = byte count from end of request to a media boundary // UInt32 excessBufferSize = 0; // Ask our superclass' opinion. if ( super::init() == false ) return false; // Initialize our minimal state. _blockSize = blockSize; _chunksCount = 0; _direction = kIODirectionNone; _length = 0; _requestBuffer = withRequestBuffer; _requestBuffer->retain(); _requestCompletion = withRequestCompletion; _requestContext = withRequestContext; _requestCount = withRequestBuffer->getLength(); _requestStart = withRequestStart; _excessCountStart = (withRequestStart ) % blockSize; _excessCountFinal = (withRequestStart + _requestCount) % blockSize; if ( _excessCountFinal ) _excessCountFinal = blockSize - _excessCountFinal; _requestIsOneBlock = (_excessCountStart + _requestCount <= blockSize); // Determine the necessary size for our scratch buffer. switch ( _requestBuffer->getDirection() ) { case kIODirectionIn: // (read) { excessBufferSize = max(_excessCountStart, _excessCountFinal); } break; case kIODirectionOut: // (write) { if ( _excessCountStart ) excessBufferSize += blockSize; if ( _excessCountFinal ) excessBufferSize += blockSize; // If there is excess both ends of the original request, but both // ends reside within the same media block, then we could shorten // our buffer size to just one block. if ( _excessCountStart && _excessCountFinal && _requestIsOneBlock ) { excessBufferSize -= blockSize; } } break; default: { assert(0); } break; } // Allocate our scratch buffer. if ( excessBufferSize ) { _excessBuffer = IOBufferMemoryDescriptor::withCapacity( /* capacity */ excessBufferSize, /* withDirection */ kIODirectionNone ); if ( _excessBuffer == 0 ) return false; } return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IODeblocker::free() { // // Free all of this object's outstanding resources. // if ( _requestBuffer ) _requestBuffer->release(); if ( _excessBuffer ) _excessBuffer->release(); super::free(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOReturn IODeblocker::prepare(IODirection forDirection) { // // Prepare the memory for an I/O transfer. // // This involves paging in the memory and wiring it down for the duration // of the transfer. The complete() method finishes the processing of the // memory after the I/O transfer finishes. // unsigned index; IOReturn status = kIOReturnInternalError; IOReturn statusUndo; if ( forDirection == kIODirectionNone ) { forDirection = _direction; } for ( index = 0; index < _chunksCount; index++ ) { status = _chunks[index].buffer->prepare(forDirection); if ( status != kIOReturnSuccess ) break; } if ( status != kIOReturnSuccess ) { for ( unsigned indexUndo = 0; indexUndo <= index; indexUndo++ ) { statusUndo = _chunks[index].buffer->complete(forDirection); assert(statusUndo == kIOReturnSuccess); } } return status; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOReturn IODeblocker::complete(IODirection forDirection) { // // Complete processing of the memory after an I/O transfer finishes. // // This method shouldn't be called unless a prepare() was previously issued; // the prepare() and complete() must occur in pairs, before and after an I/O // transfer. // IOReturn status; IOReturn statusFinal = kIOReturnSuccess; if ( forDirection == kIODirectionNone ) { forDirection = _direction; } for ( unsigned index = 0; index < _chunksCount; index++ ) { status = _chunks[index].buffer->complete(forDirection); if ( status != kIOReturnSuccess ) statusFinal = status; assert(status == kIOReturnSuccess); } return statusFinal; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOPhysicalAddress IODeblocker::getPhysicalSegment( IOByteCount offset, IOByteCount * length ) { // // This method returns the physical address of the byte at the given offset // into the memory, and optionally the length of the physically contiguous // segment from that offset. // assert(offset <= _length); for ( unsigned index = 0; index < _chunksCount; index++ ) { if ( offset < _chunks[index].length ) { IOPhysicalAddress address; address = _chunks[index].buffer->getPhysicalSegment( /* offset */ offset + _chunks[index].offset, /* length */ length ); if ( length ) *length = min(*length, _chunks[index].length); return address; } offset -= _chunks[index].length; } if ( length ) *length = 0; return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void * IODeblocker::getVirtualSegment( IOByteCount /* offset */ , IOByteCount * /* length */ ) { return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOByteCount IODeblocker::readBytes( IOByteCount offset, void * bytes, IOByteCount withLength ) { // // Copies data from the memory descriptor's buffer at the given offset, to // the specified buffer. Returns the number of bytes copied. // IOByteCount bytesCopied = 0; unsigned index; for ( index = 0; index < _chunksCount; index++ ) { if ( offset < _chunks[index].length ) break; offset -= _chunks[index].length; } for ( ; index < _chunksCount && withLength; index++) { IOByteCount copy = min(_chunks[index].length - offset, withLength); IOByteCount copied = _chunks[index].buffer->readBytes( /* offset */ offset + _chunks[index].offset, /* bytes */ bytes, /* length */ copy ); bytesCopied += copied; if ( copied != copy ) break; bytes = ((UInt8 *) bytes) + copied; withLength -= copied; offset = 0; } return bytesCopied; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOByteCount IODeblocker::writeBytes( IOByteCount offset, const void * bytes, IOByteCount withLength ) { // // Copies data to the memory descriptor's buffer at the given offset, from // the specified buffer. Returns the number of bytes copied. // IOByteCount bytesCopied = 0; unsigned index; for ( index = 0; index < _chunksCount; index++ ) { if ( offset < _chunks[index].length ) break; offset -= _chunks[index].length; } for ( ; index < _chunksCount && withLength; index++) { IOByteCount copy = min(_chunks[index].length - offset, withLength); IOByteCount copied = _chunks[index].buffer->writeBytes( /* offset */ offset + _chunks[index].offset, /* bytes */ bytes, /* length */ copy ); bytesCopied += copied; if ( copied != copy ) break; bytes = ((UInt8 *) bytes) + copied; withLength -= copied; offset = 0; } return bytesCopied; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IODeblocker::getNextStage(UInt64 * byteStart) { // // Obtain the next stage of the transfer. The transfer buffer will be the // deblocker object itself and the byte start will be returned in byteStart. // // This method must not be called if the current stage failed with an error // or a short byte count, but instead getRequestCompletion() must be called // to adjust the status and actual byte count (with respect to the original // request) and return the original request's completion routine. The same // call to getRequestCompletion() should also be done if the getNextStage() // method returns false. // _chunksCount = 0; _direction = kIODirectionNone; _length = 0; switch ( _requestBuffer->getDirection() ) { case kIODirectionIn: // (read) { switch ( _stage ) { case kStageInit: { _stage = kStageLast; _excessBuffer->setDirection(kIODirectionIn); _direction = kIODirectionIn; *byteStart = _requestStart - _excessCountStart; if ( _excessCountStart ) { _chunks[_chunksCount].buffer = _excessBuffer; _chunks[_chunksCount].offset = 0; _chunks[_chunksCount].length = _excessCountStart; _chunksCount++; } _chunks[_chunksCount].buffer = _requestBuffer; _chunks[_chunksCount].offset = 0; _chunks[_chunksCount].length = _requestBuffer->getLength(); _chunksCount++; if ( _excessCountFinal ) { _chunks[_chunksCount].buffer = _excessBuffer; _chunks[_chunksCount].offset = 0; _chunks[_chunksCount].length = _excessCountFinal; _chunksCount++; } } break; case kStageLast: { _stage = kStageDone; } break; default: { assert(0); } break; } // (switch) } break; case kIODirectionOut: // (write) { switch ( _stage ) { case kStageInit: { if ( _excessCountStart ) { _stage = kStagePrepareExcessStart; _excessBuffer->setDirection(kIODirectionIn); _direction = kIODirectionIn; *byteStart = _requestStart - _excessCountStart; _chunks[_chunksCount].buffer = _excessBuffer; _chunks[_chunksCount].offset = 0; _chunks[_chunksCount].length = _blockSize; _chunksCount++; break; } } // (fall thru) case kStagePrepareExcessStart: { if ( _excessCountFinal ) { // We do not issue this stage if the original transfer // resides within one media block, and we already read // that block into our buffer in the previous stage. if ( !_excessCountStart || !_requestIsOneBlock ) { _stage = kStagePrepareExcessFinal; _excessBuffer->setDirection(kIODirectionIn); _direction = kIODirectionIn; *byteStart = _requestStart + _requestCount + _excessCountFinal - _blockSize; _chunks[_chunksCount].buffer = _excessBuffer; _chunks[_chunksCount].offset = (_requestIsOneBlock) ? 0 : (_excessCountStart) ? _blockSize : 0; _chunks[_chunksCount].length = _blockSize; _chunksCount++; break; } } } // (fall thru) case kStagePrepareExcessFinal: { _stage = kStageLast; _excessBuffer->setDirection(kIODirectionOut); _direction = kIODirectionOut; *byteStart = _requestStart - _excessCountStart; if ( _excessCountStart ) { _chunks[_chunksCount].buffer = _excessBuffer; _chunks[_chunksCount].offset = 0; _chunks[_chunksCount].length = _excessCountStart; _chunksCount++; } _chunks[_chunksCount].buffer = _requestBuffer; _chunks[_chunksCount].offset = 0; _chunks[_chunksCount].length = _requestBuffer->getLength(); _chunksCount++; if ( _excessCountFinal ) { _chunks[_chunksCount].buffer = _excessBuffer; _chunks[_chunksCount].offset = (_requestIsOneBlock) ? 0 : (_excessCountStart) ? _blockSize : 0; _chunks[_chunksCount].offset += ( _blockSize - _excessCountFinal ); _chunks[_chunksCount].length = _excessCountFinal; _chunksCount++; } } break; case kStageLast: { _stage = kStageDone; } break; default: { assert(0); } break; } // (switch) } break; default: { assert(0); } break; } // (switch) // Determine whether we have an abort or completion condition. if ( _chunksCount == 0 ) return false; // Compute the total length of the descriptor over all chunks. for ( unsigned index = 0; index < _chunksCount; index++ ) { _length += _chunks[index].length; } return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IODeblocker::getRequestCompletion( IOStorageCompletion * completion, IOReturn * status, UInt64 * actualByteCount ) { // // Obtain the completion information for the original request, taking // into account the status and actual byte count of the current stage. // *completion = _requestCompletion; switch ( _stage ) { case kStageInit: // (inital stage) { *status = kIOReturnInternalError; *actualByteCount = 0; } break; case kStagePrepareExcessStart: // (write preparation stage) case kStagePrepareExcessFinal: { *actualByteCount = 0; } break; case kStageLast: // (last stage) case kStageDone: { if ( *actualByteCount > _excessCountStart ) *actualByteCount -= _excessCountStart; else *actualByteCount = 0; if ( *actualByteCount > _requestBuffer->getLength() ) *actualByteCount = _requestBuffer->getLength(); } break; default: { assert(0); } break; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOMemoryDescriptor * IODeblocker::getRequestBuffer() { // // Obtain the buffer for the original request. // return _requestBuffer; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void * IODeblocker::getRequestContext() { // // Obtain the context for the original request. // return _requestContext; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::deblockRequest( UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion, IOBlockStorageDriver::Context * context ) { // // The deblockRequest method checks to see if the incoming request rests // on the media's block boundaries, and if not, deblocks it. Deblocking // involves rounding out the request to the nearest block boundaries and // transferring the excess bytes into a scratch buffer. // // This method is part of a sequence of methods invoked for each read/write // request. The first is prepareRequest, which allocates and prepares some // context for the transfer; the second is deblockRequest, which aligns the // transfer at the media's block boundaries; third is breakUpRequest, which // breaks up the transfer into multiple sub-transfers when certain hardware // constraints are exceeded; fourth is executeRequest, which implements the // actual transfer from the block storage device. // // The current implementation of deblockRequest is asynchronous. // IODeblocker * deblocker; // If the request is aligned with the media's block boundaries, we // do short-circuit the deblocker and call breakUpRequest directly. if ( (byteStart % context->block.size) == 0 && (buffer->getLength() % context->block.size) == 0 ) { breakUpRequest(byteStart, buffer, completion, context); return; } // Build a deblocker object. deblocker = IODeblocker::withBlockSize( /* blockSize */ context->block.size, /* withRequestStart */ byteStart, /* withRequestBuffer */ buffer, /* withRequestCompletion */ completion, /* withRequestContext */ context ); if ( deblocker == 0 ) { complete(completion, kIOReturnNoMemory); return; } // This implementation of the deblocker permits only one read-modify-write // at any given time. Note that other write requests can, and do, proceed // simultaneously so long as they do not require the deblocker. if ( buffer->getDirection() == kIODirectionOut ) { IOLockLock(_deblockRequestWriteLock); _deblockRequestWriteLockCount++; if ( _deblockRequestWriteLockCount > 1 ) { IOLockSleep( /* lock */ _deblockRequestWriteLock, /* event */ _deblockRequestWriteLock, /* interruptible */ THREAD_UNINT ); } IOLockUnlock(_deblockRequestWriteLock); } // Execute the transfer (for the next stage). deblockRequestCompletion(this, deblocker, kIOReturnSuccess, 0); return; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::deblockRequestCompletion( void * target, void * parameter, IOReturn status, UInt64 actualByteCount ) { // // This is the completion routine for the aligned deblocker subrequests. // It verifies the success of the just-completed stage, transitions to // the next stage, then builds and issues a transfer for the next stage. // UInt64 byteStart; IOStorageCompletion completion; Context * context; IODeblocker * deblocker = (IODeblocker *) parameter; IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target; // Determine whether an error occurred or whether there are no more stages. if ( actualByteCount < deblocker->getLength() || status != kIOReturnSuccess || deblocker->getNextStage(&byteStart) == false ) { // Unlock the write-lock in order to allow the next write to proceed. if ( deblocker->getRequestBuffer()->getDirection() == kIODirectionOut ) { IOLockLock(driver->_deblockRequestWriteLock); driver->_deblockRequestWriteLockCount--; if ( driver->_deblockRequestWriteLockCount > 0 ) { IOLockWakeup( /* lock */ driver->_deblockRequestWriteLock, /* event */ driver->_deblockRequestWriteLock, /* one */ true ); } IOLockUnlock(driver->_deblockRequestWriteLock); } // Obtain the completion information for the original request, taking // into account the status and actual byte count of the current stage. deblocker->getRequestCompletion(&completion, &status, &actualByteCount); // Complete the original request. IOStorage::complete(completion, status, actualByteCount); // Release our resources. deblocker->release(); return; } // Execute the transfer (for the next stage). completion.target = driver; completion.action = deblockRequestCompletion; completion.parameter = deblocker; context = (Context *) deblocker->getRequestContext(); driver->breakUpRequest(byteStart, deblocker, completion, context); return; } // ----------------------------------------------------------------------------- // Breaker Implementation class IOBreaker : public IOSubMemoryDescriptor { OSDeclareDefaultStructors(IOBreaker); protected: UInt64 _breakSize; UInt64 _maximumBlockCount; UInt64 _maximumByteCount; UInt64 _maximumSegmentCount; UInt64 _maximumSegmentByteCount; UInt64 _requestBlockSize; IOMemoryDescriptor * _requestBuffer; IOStorageCompletion _requestCompletion; void * _requestContext; UInt64 _requestCount; UInt64 _requestStart; virtual void free(); public: static UInt64 getBreakSize( UInt64 withMaximumBlockCount, UInt64 withMaximumByteCount, UInt64 withMaximumSegmentCount, UInt64 withMaximumSegmentByteCount, UInt64 withRequestBlockSize, IOMemoryDescriptor * withRequestBuffer, UInt64 withRequestBufferOffset ); static IOBreaker * withBreakSize( UInt64 breakSize, UInt64 withMaximumBlockCount, UInt64 withMaximumByteCount, UInt64 withMaximumSegmentCount, UInt64 withMaximumSegmentByteCount, UInt64 withRequestBlockSize, UInt64 withRequestStart, IOMemoryDescriptor * withRequestBuffer, IOStorageCompletion withRequestCompletion, void * withRequestContext ); virtual bool initWithBreakSize( UInt64 breakSize, UInt64 withMaximumBlockCount, UInt64 withMaximumByteCount, UInt64 withMaximumSegmentCount, UInt64 withMaximumSegmentByteCount, UInt64 withRequestBlockSize, UInt64 withRequestStart, IOMemoryDescriptor * withRequestBuffer, IOStorageCompletion withRequestCompletion, void * withRequestContext ); virtual bool getNextStage(UInt64 * byteStart); virtual void getRequestCompletion( IOStorageCompletion * completion, IOReturn * status, UInt64 * actualByteCount ); virtual IOMemoryDescriptor * getRequestBuffer(); virtual void * getRequestContext(); }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #undef super #define super IOSubMemoryDescriptor OSDefineMetaClassAndStructors(IOBreaker, IOSubMemoryDescriptor) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UInt64 IOBreaker::getBreakSize( UInt64 withMaximumBlockCount, UInt64 withMaximumByteCount, UInt64 withMaximumSegmentCount, UInt64 withMaximumSegmentByteCount, UInt64 withRequestBlockSize, IOMemoryDescriptor * withRequestBuffer, UInt64 withRequestBufferOffset ) { // // Determine where the next break point is given our constraints. // UInt64 breakSize = 0; IOMemoryDescriptor * buffer = withRequestBuffer; IOByteCount bufferLength = withRequestBuffer->getLength(); IOByteCount bufferOffset = withRequestBufferOffset; IOPhysicalAddress chunk = 0; UInt32 chunkSize = 0; IOPhysicalAddress segment = 0; UInt32 segmentCount = 0; UInt32 segmentSize = 0; // Constrain block count. if ( withMaximumBlockCount ) { UInt64 blockCountInBytes; blockCountInBytes = withMaximumBlockCount * withRequestBlockSize; if ( withMaximumByteCount ) { withMaximumByteCount = min(blockCountInBytes, withMaximumByteCount); } else { withMaximumByteCount = blockCountInBytes; } } // Scan the buffer's segments. while ( segment || bufferOffset < bufferLength ) { // Obtain an individual segment. if ( segment == 0 ) { segment = buffer->getPhysicalSegment(bufferOffset, &segmentSize); assert(segment); assert(segmentSize); bufferOffset += segmentSize; } // Process an individual segment. if ( chunk == 0 ) { breakSize += segmentSize; chunk = segment; chunkSize = segmentSize; segment = 0; segmentSize = 0; segmentCount++; } else if ( chunk + chunkSize == segment ) { breakSize += segmentSize; chunkSize += segmentSize; segment = 0; segmentSize = 0; } if ( segment == 0 ) { // Constrain segment byte count. if ( withMaximumSegmentByteCount ) { if ( chunkSize > withMaximumSegmentByteCount ) { segment = chunk + withMaximumSegmentByteCount; segmentSize = chunkSize - withMaximumSegmentByteCount; breakSize -= segmentSize; chunkSize -= segmentSize; } } // Constrain byte count. if ( withMaximumByteCount ) { if ( breakSize >= withMaximumByteCount ) { breakSize = withMaximumByteCount; break; } } } // Process a coalesced segment. if ( segment ) { // Constrain segment count. if ( withMaximumSegmentCount ) { if ( segmentCount == withMaximumSegmentCount ) { break; } } chunk = 0; chunkSize = 0; } } breakSize = IOTrunc(breakSize, withRequestBlockSize); return breakSize; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOBreaker * IOBreaker::withBreakSize( UInt64 breakSize, UInt64 withMaximumBlockCount, UInt64 withMaximumByteCount, UInt64 withMaximumSegmentCount, UInt64 withMaximumSegmentByteCount, UInt64 withRequestBlockSize, UInt64 withRequestStart, IOMemoryDescriptor * withRequestBuffer, IOStorageCompletion withRequestCompletion, void * withRequestContext ) { // // Create a new IOBreaker. // IOBreaker * me = new IOBreaker; if ( me && me->initWithBreakSize( /* breakSize */ breakSize, /* withMaximumBlockCount */ withMaximumBlockCount, /* withMaximumByteCount */ withMaximumByteCount, /* withMaximumSegmentCount */ withMaximumSegmentCount, /* withMaximumSegmentByteCount */ withMaximumSegmentByteCount, /* withRequestBlockSize */ withRequestBlockSize, /* withRequestStart */ withRequestStart, /* withRequestBuffer */ withRequestBuffer, /* withRequestCompletion */ withRequestCompletion, /* withRequestContext */ withRequestContext ) == false ) { me->release(); me = 0; } return me; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IOBreaker::initWithBreakSize( UInt64 breakSize, UInt64 withMaximumBlockCount, UInt64 withMaximumByteCount, UInt64 withMaximumSegmentCount, UInt64 withMaximumSegmentByteCount, UInt64 withRequestBlockSize, UInt64 withRequestStart, IOMemoryDescriptor * withRequestBuffer, IOStorageCompletion withRequestCompletion, void * withRequestContext ) { // // Initialize an IOBreaker. // // Ask our superclass' opinion. if ( super::initSubRange( /* parent */ withRequestBuffer, /* withOffset */ 0, /* withLength */ withRequestBuffer->getLength(), /* withDirection */ withRequestBuffer->getDirection() ) == false ) { return false; } // Initialize our minimal state. _breakSize = breakSize; _length = 0; _maximumBlockCount = withMaximumBlockCount; _maximumByteCount = withMaximumByteCount; _maximumSegmentCount = withMaximumSegmentCount; _maximumSegmentByteCount = withMaximumSegmentByteCount; _requestBlockSize = withRequestBlockSize; _requestBuffer = withRequestBuffer; _requestBuffer->retain(); _requestCompletion = withRequestCompletion; _requestContext = withRequestContext; _requestCount = withRequestBuffer->getLength(); _requestStart = withRequestStart; return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBreaker::free() { // // Free all of this object's outstanding resources. // if ( _requestBuffer ) _requestBuffer->release(); super::free(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool IOBreaker::getNextStage(UInt64 * byteStart) { // // Obtain the next stage of the transfer. The transfer buffer will be the // breaker object itself and the byte start will be returned in byteStart. // // This method must not be called if the current stage failed with an error // or a short byte count, but instead getRequestCompletion() must be called // to adjust the status and actual byte count (with respect to the original // request) and return the original request's completion routine. The same // call to getRequestCompletion() should also be done if the getNextStage() // method returns false. // if ( _start + _length < _requestCount ) { _start += _length; _length = min(_breakSize, _requestCount - _start); _breakSize = getBreakSize( /* withMaximumBlockCount */ _maximumBlockCount, /* withMaximumByteCount */ _maximumByteCount, /* withMaximumSegmentCount */ _maximumSegmentCount, /* withMaximumSegmentByteCount */ _maximumSegmentByteCount, /* withRequestBlockSize */ _requestBlockSize, /* withRequestBuffer */ _requestBuffer, /* withRequestBufferOffset */ _start + _length ); } else { return false; } *byteStart = _requestStart + _start; return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBreaker::getRequestCompletion( IOStorageCompletion * completion, IOReturn * status, UInt64 * actualByteCount ) { // // Obtain the completion information for the original request, taking // into account the status and actual byte count of the current stage. // *actualByteCount += _start; *completion = _requestCompletion; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IOMemoryDescriptor * IOBreaker::getRequestBuffer() { // // Obtain the buffer for the original request. // return _requestBuffer; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void * IOBreaker::getRequestContext() { // // Obtain the context for the original request. // return _requestContext; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::breakUpRequest( UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion, IOBlockStorageDriver::Context * context ) { // // The breakUpRequest method checks to see if the incoming request exceeds // our transfer constraints, and if so, breaks up the request into smaller // sub-requests. // // This method is part of a sequence of methods invoked for each read/write // request. The first is prepareRequest, which allocates and prepares some // context for the transfer; the second is deblockRequest, which aligns the // transfer at the media's block boundaries; third is breakUpRequest, which // breaks up the transfer into multiple sub-transfers when certain hardware // constraints are exceeded; fourth is executeRequest, which implements the // actual transfer from the block storage device. // // The current implementation of breakUpRequest is asynchronous. // IOBreaker * breaker; UInt64 breakSize; // State our assumptions. assert((byteStart % context->block.size) == 0); assert((buffer->getLength() % context->block.size) == 0); // Determine the transfer constraint, based on direction. if ( buffer->getDirection() == kIODirectionIn ) { breakSize = IOBreaker::getBreakSize( /* withMaximumBlockCount */ _maxReadBlockTransfer, /* withMaximumByteCount */ _maxReadByteTransfer, /* withMaximumSegmentCount */ _maxReadSegmentTransfer, /* withMaximumSegmentByteCount */ _maxReadSegmentByteTransfer, /* withRequestBlockSize */ context->block.size, /* withRequestBuffer */ buffer, /* withRequestBufferOffset */ 0 ); } else { breakSize = IOBreaker::getBreakSize( /* withMaximumBlockCount */ _maxWriteBlockTransfer, /* withMaximumByteCount */ _maxWriteByteTransfer, /* withMaximumSegmentCount */ _maxWriteSegmentTransfer, /* withMaximumSegmentByteCount */ _maxWriteSegmentByteTransfer, /* withRequestBlockSize */ context->block.size, /* withRequestBuffer */ buffer, /* withRequestBufferOffset */ 0 ); } // If the request doesn't exceed our transfer constaints, we do // short-circuit the break-up and call executeRequest directly. if ( buffer->getLength() <= breakSize ) { executeRequest(byteStart, buffer, completion, context); return; } // Round the sub-transfer size to a block boundary. breakSize = IOTrunc(breakSize, context->block.size); // Build a breaker object. if ( buffer->getDirection() == kIODirectionIn ) { breaker = IOBreaker::withBreakSize( /* breakSize */ breakSize, /* withMaximumBlockCount */ _maxReadBlockTransfer, /* withMaximumByteCount */ _maxReadByteTransfer, /* withMaximumSegmentCount */ _maxReadSegmentTransfer, /* withMaximumSegmentByteCount */ _maxReadSegmentByteTransfer, /* withRequestBlockSize */ context->block.size, /* withRequestStart */ byteStart, /* withRequestBuffer */ buffer, /* withRequestCompletion */ completion, /* withRequestContext */ context ); } else { breaker = IOBreaker::withBreakSize( /* breakSize */ breakSize, /* withMaximumBlockCount */ _maxWriteBlockTransfer, /* withMaximumByteCount */ _maxWriteByteTransfer, /* withMaximumSegmentCount */ _maxWriteSegmentTransfer, /* withMaximumSegmentByteCount */ _maxWriteSegmentByteTransfer, /* withRequestBlockSize */ context->block.size, /* withRequestStart */ byteStart, /* withRequestBuffer */ buffer, /* withRequestCompletion */ completion, /* withRequestContext */ context ); } if ( breaker == 0 ) { complete(completion, kIOReturnNoMemory); return; } // Execute the transfer (for the next stage). breakUpRequestCompletion(this, breaker, kIOReturnSuccess, 0); return; } OSMetaClassDefineReservedUsed(IOBlockStorageDriver, 0); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void IOBlockStorageDriver::breakUpRequestCompletion( void * target, void * parameter, IOReturn status, UInt64 actualByteCount ) { // // This is the completion routine for the broken-up breaker subrequests. // It verifies the success of the just-completed stage, transitions to // the next stage, then builds and issues a transfer for the next stage. // UInt64 byteStart; IOStorageCompletion completion; Context * context; IOBreaker * breaker = (IOBreaker *) parameter; IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target; // Determine whether an error occurred or whether there are no more stages. if ( actualByteCount < breaker->getLength() || status != kIOReturnSuccess || breaker->getNextStage(&byteStart) == false ) { // Obtain the completion information for the original request, taking // into account the status and actual byte count of the current stage. breaker->getRequestCompletion(&completion, &status, &actualByteCount); // Complete the original request. IOStorage::complete(completion, status, actualByteCount); // Release our resources. breaker->release(); return; } // Execute the transfer (for the next stage). completion.target = driver; completion.action = breakUpRequestCompletion; completion.parameter = breaker; context = (Context *) breaker->getRequestContext(); driver->executeRequest(byteStart, breaker, completion, context); return; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 1); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 2); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 3); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 4); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 5); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 6); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 7); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 8); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 9); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 10); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 11); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 12); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 13); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 14); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 15); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 16); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 17); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 18); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 19); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 20); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 21); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 22); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 23); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 24); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 25); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 26); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 27); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 28); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 29); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 30); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 31);