IOBlockStorageDriver.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/storage/IOBlockStorageDevice.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#define super IOStorage
OSDefineMetaClassAndStructors(IOBlockStorageDriver, IOStorage)
const UInt32 kPollerInterval = 1000;
#define isMediaRemovable() _removable
#define kIOPropertyProtocolCharacteristicsKey "Protocol Characteristics"
#define kIOPropertyPhysicalInterconnectLocationKey "Physical Interconnect Location"
#define kIOPropertyExternalKey "External"
static char * strclean(char * s)
{
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
{
return (IOBlockStorageDevice *) IOService::getProvider();
}
bool IOBlockStorageDriver::init(OSDictionary * properties)
{
if (super::init(properties) == false) return false;
_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;
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)
{
if (provider->open(this) == false) return false;
if (handleStart(provider) == false)
{
provider->close(this);
return false;
}
if (isMediaRemovable() && isMediaPollRequired() && !isMediaPollExpensive())
{
lockForArbitration();
if (!isOpen() && !isInactive())
schedulePoller();
unlockForArbitration(); }
registerService();
return true;
}
bool IOBlockStorageDriver::yield(IOService * provider,
IOOptionBits options,
void * argument)
{
bool success;
lockForArbitration();
success = handleYield(provider, options, argument);
if (success)
{
provider->close(this);
}
unlockForArbitration();
return success;
}
void IOBlockStorageDriver::free()
{
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)
{
assert(client);
if (getMediaState() == kIOMediaStateOffline) return false;
if (isMediaRemovable() && _openClients->getCount() == 0)
{
if (isMediaPollRequired() && !isMediaPollExpensive())
unschedulePoller();
lockMedia(true);
}
_openClients->setObject(client);
return true;
}
bool IOBlockStorageDriver::handleIsOpen(const IOService * client) const
{
if (client)
return _openClients->containsObject(client);
else
return (_openClients->getCount() != 0);
}
void IOBlockStorageDriver::handleClose(IOService * client, IOOptionBits options)
{
assert(client);
_openClients->removeObject(client);
if (_openClients->getCount() == 0)
{
if (isInactive())
{
message(kIOMessageServiceIsRequestingClose, getProvider(), 0);
}
else
{
if (isMediaRemovable())
{
if (getMediaState() == kIOMediaStateOnline)
{
lockMedia(false);
}
if (isMediaPollRequired() && !isMediaPollExpensive())
schedulePoller(); }
}
}
}
void IOBlockStorageDriver::read(IOService * ,
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageCompletion completion)
{
assert(buffer->getDirection() == kIODirectionIn);
prepareRequest(byteStart, buffer, completion);
}
void IOBlockStorageDriver::write(IOService * ,
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageCompletion completion)
{
assert(buffer->getDirection() == kIODirectionOut);
prepareRequest(byteStart, buffer, completion);
}
void IOBlockStorageDriver::addToBytesTransferred(UInt64 bytesTransferred,
UInt64 totalTime, UInt64 latentTime, bool isWrite)
{
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)
{
if (isWrite)
_statistics[kStatisticsWriteRetries]->addValue(1);
else
_statistics[kStatisticsReadRetries]->addValue(1);
}
void IOBlockStorageDriver::incrementErrors(bool isWrite)
{
if (isWrite)
_statistics[kStatisticsWriteErrors]->addValue(1);
else
_statistics[kStatisticsReadErrors]->addValue(1);
}
UInt32 IOBlockStorageDriver::getStatistics(UInt64 * statistics,
UInt32 statisticsMaxCount) const
{
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
{
if ((UInt32) statistic >= kStatisticsCount) return 0;
return _statistics[statistic]->unsigned64BitValue();
}
IOBlockStorageDriver::Context * IOBlockStorageDriver::allocateContext()
{
Context * context = IONew(Context, 1);
if (context)
{
bzero(context, sizeof(Context));
}
return context;
}
void IOBlockStorageDriver::deleteContext(
IOBlockStorageDriver::Context * context)
{
IODelete(context, Context, 1);
}
void IOBlockStorageDriver::prepareRequest(UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageCompletion completion)
{
Context * context;
context = allocateContext();
if (context == 0)
{
complete(completion, kIOReturnNoMemory);
return;
}
context->block.size = getMediaBlockSize();
context->block.type = kBlockTypeStandard;
context->original.byteStart = byteStart;
context->original.buffer = buffer;
context->original.buffer->retain();
context->original.completion = completion;
clock_get_uptime(&context->timeStart);
completion.target = this;
completion.action = prepareRequestCompletion;
completion.parameter = context;
deblockRequest(byteStart, buffer, completion, context);
}
void IOBlockStorageDriver::prepareRequestCompletion(void * target,
void * parameter,
IOReturn status,
UInt64 actualByteCount)
{
Context * context = (Context *) parameter;
IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target;
bool isWrite;
AbsoluteTime time;
UInt64 timeInNanoseconds;
isWrite = (context->original.buffer->getDirection() == kIODirectionOut);
if (actualByteCount < context->original.buffer->getLength())
{
if (status == kIOReturnSuccess)
{
status = kIOReturnUnderrun;
}
}
if (actualByteCount && isWrite)
{
driver->_mediaDirtied = true;
}
clock_get_uptime(&time);
SUB_ABSOLUTETIME(&time, &context->timeStart);
absolutetime_to_nanoseconds(time, &timeInNanoseconds);
driver->addToBytesTransferred(actualByteCount, timeInNanoseconds, 0, isWrite);
if (status != kIOReturnSuccess)
{
driver->incrementErrors(isWrite);
}
IOStorage::complete(context->original.completion, status, actualByteCount);
context->original.buffer->release();
driver->deleteContext(context);
}
void IOBlockStorageDriver::schedulePoller()
{
AbsoluteTime deadline;
retain();
clock_interval_to_deadline(kPollerInterval, kMillisecondScale, &deadline);
thread_call_enter_delayed(_pollerCall, deadline);
}
void IOBlockStorageDriver::unschedulePoller()
{
if (thread_call_cancel(_pollerCall)) release();
}
void IOBlockStorageDriver::poller(void * target, void *)
{
IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target;
driver->pollMedia();
driver->lockForArbitration();
if (!driver->isOpen() && !driver->isInactive())
driver->schedulePoller();
driver->unlockForArbitration();
driver->release(); }
IOReturn IOBlockStorageDriver::message(UInt32 type,
IOService * provider,
void * argument)
{
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);
}
}
}
IOReturn
IOBlockStorageDriver::acceptNewMedia(void)
{
IOReturn result;
bool ok;
UInt64 nbytes;
char name[128];
bool nameSep;
if (_maxBlockNumber) {
nbytes = _mediaBlockSize * (_maxBlockNumber + 1);
} else {
nbytes = 0;
}
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);
if (ok) {
IOService *parent = this;
OSNumber *unit = NULL;
OSNumber *unitLUN = NULL;
OSString *unitName = NULL;
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();
} else {
_mediaObject->release();
_mediaObject = 0;
return(kIOReturnNoMemory);
}
}
return(result);
}
IOReturn
IOBlockStorageDriver::checkForMedia(void)
{
IOReturn result;
bool currentState;
bool changed;
IOLockLock(_mediaStateLock);
result = getProvider()->reportMediaState(¤tState,&changed);
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::checkForMedia; err '%s' from reportMediaState\n",
getName(),stringFromReturn(result));
} else {
changed = currentState ? !_mediaPresent : _mediaPresent;
if (changed) {
result = mediaStateHasChanged(currentState ? kIOMediaStateOnline
: kIOMediaStateOffline);
}
}
IOLockUnlock(_mediaStateLock);
return(result);
}
IOReturn
IOBlockStorageDriver::mediaStateHasChanged(IOMediaState state)
{
IOReturn result;
if (state == kIOMediaStateOnline) {
if (_mediaPresent) {
return(kIOReturnBadArgument);
}
if (validateNewMedia() == false) {
rejectMedia();
return(kIOReturnSuccess);
}
result = recordMediaParameters();
if (result != kIOReturnSuccess) {
initMediaState();
IOLog("%s[IOBlockStorageDriver]::checkForMedia: err '%s' from recordMediaParameters\n",
getName(),stringFromReturn(result));
return(result);
}
lockForArbitration();
result = acceptNewMedia();
if (result != kIOReturnSuccess) {
initMediaState();
IOLog("%s[IOBlockStorageDriver]::checkForMedia; err '%s' from acceptNewMedia\n",
getName(),stringFromReturn(result));
}
unlockForArbitration();
return(result);
} else {
lockForArbitration();
result = decommissionMedia(true);
unlockForArbitration();
if (result != kIOReturnSuccess && result != kIOReturnNoMedia) {
IOLog("%s[IOBlockStorageDriver]::checkForMedia; err '%s' from decommissionNewMedia\n",
getName(),stringFromReturn(result));
return(result);
}
return(kIOReturnSuccess);
}
}
UInt64
IOBlockStorageDriver::constrainByteCount(UInt64 ,bool isWrite)
{
if (isWrite) {
return(_maxWriteByteTransfer);
} else {
return(_maxReadByteTransfer);
}
}
IOReturn
IOBlockStorageDriver::decommissionMedia(bool forcible)
{
IOReturn result;
if (_mediaObject) {
if ((forcible || !_openClients->containsObject(_mediaObject)) &&
(isInactive() || _mediaObject->terminate() || forcible)) {
IORegistryEntry * parent;
if ( (parent = _mediaObject->getParentEntry(gIODTPlane)) ) {
_mediaObject->detachFromParent(parent, gIODTPlane);
}
_mediaObject->release();
_mediaObject = 0;
initMediaState();
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);
unlockForArbitration();
if (result == kIOReturnSuccess) {
if (mediaDirtied) {
synchronizeCache(this);
}
(void)lockMedia(false);
(void)getProvider()->doEjectMedia();
}
}
else
{
lockForArbitration();
if (_mediaObject) {
if (!_openClients->containsObject(_mediaObject)) {
result = kIOReturnSuccess;
} else {
result = kIOReturnBusy;
}
} else {
result = kIOReturnNoMedia;
}
if (result == kIOReturnSuccess) {
if (_mediaDirtied) {
_mediaDirtied = false;
synchronizeCache(this);
}
if (isMediaRemovable())
(void)lockMedia(false);
(void)getProvider()->doEjectMedia();
}
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) {
complete(completion, kIOReturnNoMedia,0);
return;
}
assert((byteStart % context->block.size) == 0);
assert((buffer->getLength() % context->block.size) == 0);
block = byteStart / context->block.size;
nblks = buffer->getLength() / context->block.size;
result = getProvider()->doAsyncReadWrite(buffer,block,nblks,completion);
if (result != kIOReturnSuccess) {
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);
unlockForArbitration();
if (result == kIOReturnSuccess) {
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;
result = getProvider()->reportRemovability(&_removable);
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportRemovability\n",
getName(),stringFromReturn(result));
return(false);
}
if (_removable) {
result = getProvider()->reportPollRequirements(&_pollIsRequired,&_pollIsExpensive);
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportPollRequirements\n",
getName(),stringFromReturn(result));
return(false);
}
result = getProvider()->reportEjectability(&_ejectable);
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportEjectability\n",
getName(),stringFromReturn(result));
return(false);
}
result = getProvider()->reportLockability(&_lockable);
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportLockability\n",
getName(),stringFromReturn(result));
return(false);
}
} else {
_ejectable = false;
_lockable = false;
_pollIsRequired = true;
}
maxBlockCountRead = OSDynamicCast(
OSNumber,
getProperty(
kIOMaximumBlockCountReadKey,
gIOServicePlane ) );
maxBlockCountWrite = OSDynamicCast(
OSNumber,
getProperty(
kIOMaximumBlockCountWriteKey,
gIOServicePlane ) );
maxByteCountRead = OSDynamicCast(
OSNumber,
getProperty(
kIOMaximumByteCountReadKey,
gIOServicePlane ) );
maxByteCountWrite = OSDynamicCast(
OSNumber,
getProperty(
kIOMaximumByteCountWriteKey,
gIOServicePlane ) );
maxSegmentCountRead = OSDynamicCast(
OSNumber,
getProperty(
kIOMaximumSegmentCountReadKey,
gIOServicePlane ) );
maxSegmentCountWrite = OSDynamicCast(
OSNumber,
getProperty(
kIOMaximumSegmentCountWriteKey,
gIOServicePlane ) );
maxSegmentByteCountRead = OSDynamicCast(
OSNumber,
getProperty(
kIOMaximumSegmentByteCountReadKey,
gIOServicePlane ) );
maxSegmentByteCountWrite = OSDynamicCast(
OSNumber,
getProperty(
kIOMaximumSegmentByteCountWriteKey,
gIOServicePlane ) );
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;
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();
}
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);
}
result = checkForMedia();
if (result != kIOReturnSuccess && !_removable) {
IOLog("%s[IOBlockStorageDriver]::handleStart: err '%s' from checkForMedia\n",
getName(),stringFromReturn(result));
return(false);
}
_powerEventNotifier = registerPrioritySleepWakeInterest(handlePowerEvent,this);
if (_powerEventNotifier) {
retain();
}
return(true);
}
bool
IOBlockStorageDriver::handleYield(IOService * provider,
IOOptionBits options,
void * argument)
{
if (isOpen())
{
return false;
}
if (isMediaRemovable() && isMediaPollRequired() && !isMediaPollExpensive())
{
unschedulePoller(); }
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,
byteSize,
blockSize,
attributes,
true,
!_writeProtected,
"");
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 {
m->release();
return(NULL);
}
}
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) {
return(kIOReturnUnsupported);
} else {
return(checkForMedia());
}
}
IOReturn
IOBlockStorageDriver::recordMediaParameters(void)
{
IOReturn result;
result = getProvider()->reportBlockSize(&_mediaBlockSize);
if (result != kIOReturnSuccess) {
goto err;
}
result = getProvider()->reportMaxValidBlock(&_maxBlockNumber);
if (result != kIOReturnSuccess) {
goto err;
}
result = getProvider()->reportWriteProtection(&_writeProtected);
if (result != kIOReturnSuccess) {
goto err;
}
return(kIOReturnSuccess);
err:
_mediaPresent = false;
_writeProtected = true;
return(result);
}
void
IOBlockStorageDriver::rejectMedia(void)
{
(void)getProvider()->doEjectMedia();
initMediaState();
}
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);
}
#include <IOKit/IOBufferMemoryDescriptor.h>
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,
IOByteCount withLength,
IODirection withDirection );
virtual bool initWithAddress( vm_address_t address,
IOByteCount withLength,
IODirection withDirection,
task_t withTask );
virtual bool initWithPhysicalAddress(
IOPhysicalAddress address,
IOByteCount withLength,
IODirection withDirection );
virtual bool initWithPhysicalRanges(
IOPhysicalRange * ranges,
UInt32 withCount,
IODirection withDirection,
bool asReference = false );
virtual bool initWithRanges( IOVirtualRange * ranges,
UInt32 withCount,
IODirection withDirection,
task_t withTask,
bool asReference = false );
virtual void * getVirtualSegment( IOByteCount offset,
IOByteCount * length );
IOMemoryDescriptor::withAddress;
IOMemoryDescriptor::withPhysicalAddress;
IOMemoryDescriptor::withPhysicalRanges;
IOMemoryDescriptor::withRanges;
IOMemoryDescriptor::withSubRange;
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 * ,
IOByteCount ,
IODirection )
{
return false;
}
bool IODeblocker::initWithAddress( vm_address_t ,
IOByteCount ,
IODirection ,
task_t )
{
return false;
}
bool IODeblocker::initWithPhysicalAddress(
IOPhysicalAddress ,
IOByteCount ,
IODirection )
{
return false;
}
bool IODeblocker::initWithPhysicalRanges(
IOPhysicalRange * ,
UInt32 ,
IODirection ,
bool )
{
return false;
}
bool IODeblocker::initWithRanges( IOVirtualRange * ,
UInt32 ,
IODirection ,
task_t ,
bool )
{
return false;
}
IODeblocker * IODeblocker::withBlockSize(
UInt64 blockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageCompletion withRequestCompletion,
void * withRequestContext )
{
IODeblocker * me = new IODeblocker;
if ( me && me->initWithBlockSize(
blockSize,
withRequestStart,
withRequestBuffer,
withRequestCompletion,
withRequestContext ) == false )
{
me->release();
me = 0;
}
return me;
}
bool IODeblocker::initWithBlockSize(
UInt64 blockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageCompletion withRequestCompletion,
void * withRequestContext )
{
UInt32 excessBufferSize = 0;
if ( super::init() == false ) return false;
_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);
switch ( _requestBuffer->getDirection() )
{
case kIODirectionIn: {
excessBufferSize = max(_excessCountStart, _excessCountFinal);
} break;
case kIODirectionOut: {
if ( _excessCountStart ) excessBufferSize += blockSize;
if ( _excessCountFinal ) excessBufferSize += blockSize;
if ( _excessCountStart && _excessCountFinal && _requestIsOneBlock )
{
excessBufferSize -= blockSize;
}
} break;
default:
{
assert(0);
} break;
}
if ( excessBufferSize )
{
_excessBuffer = IOBufferMemoryDescriptor::withCapacity(
excessBufferSize,
kIODirectionNone );
if ( _excessBuffer == 0 ) return false;
}
return true;
}
void IODeblocker::free()
{
if ( _requestBuffer ) _requestBuffer->release();
if ( _excessBuffer ) _excessBuffer->release();
super::free();
}
IOReturn IODeblocker::prepare(IODirection forDirection)
{
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)
{
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 )
{
assert(offset <= _length);
for ( unsigned index = 0; index < _chunksCount; index++ )
{
if ( offset < _chunks[index].length )
{
IOPhysicalAddress address;
address = _chunks[index].buffer->getPhysicalSegment(
offset + _chunks[index].offset,
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 ,
IOByteCount * )
{
return 0;
}
IOByteCount IODeblocker::readBytes( IOByteCount offset,
void * bytes,
IOByteCount withLength )
{
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 + _chunks[index].offset,
bytes,
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 )
{
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 + _chunks[index].offset,
bytes,
copy );
bytesCopied += copied;
if ( copied != copy ) break;
bytes = ((UInt8 *) bytes) + copied;
withLength -= copied;
offset = 0;
}
return bytesCopied;
}
bool IODeblocker::getNextStage(UInt64 * byteStart)
{
_chunksCount = 0;
_direction = kIODirectionNone;
_length = 0;
switch ( _requestBuffer->getDirection() )
{
case kIODirectionIn: {
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;
} } break;
case kIODirectionOut: {
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;
}
}
case kStagePrepareExcessStart:
{
if ( _excessCountFinal )
{
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;
}
}
}
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;
} } break;
default:
{
assert(0);
} break;
}
if ( _chunksCount == 0 ) return false;
for ( unsigned index = 0; index < _chunksCount; index++ )
{
_length += _chunks[index].length;
}
return true;
}
void IODeblocker::getRequestCompletion( IOStorageCompletion * completion,
IOReturn * status,
UInt64 * actualByteCount )
{
*completion = _requestCompletion;
switch ( _stage )
{
case kStageInit: {
*status = kIOReturnInternalError;
*actualByteCount = 0;
} break;
case kStagePrepareExcessStart: case kStagePrepareExcessFinal:
{
*actualByteCount = 0;
} break;
case kStageLast: 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()
{
return _requestBuffer;
}
void * IODeblocker::getRequestContext()
{
return _requestContext;
}
void IOBlockStorageDriver::deblockRequest(
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageCompletion completion,
IOBlockStorageDriver::Context * context )
{
IODeblocker * deblocker;
if ( (byteStart % context->block.size) == 0 &&
(buffer->getLength() % context->block.size) == 0 )
{
breakUpRequest(byteStart, buffer, completion, context);
return;
}
deblocker = IODeblocker::withBlockSize(
context->block.size,
byteStart,
buffer,
completion,
context );
if ( deblocker == 0 )
{
complete(completion, kIOReturnNoMemory);
return;
}
if ( buffer->getDirection() == kIODirectionOut )
{
IOLockLock(_deblockRequestWriteLock);
_deblockRequestWriteLockCount++;
if ( _deblockRequestWriteLockCount > 1 )
{
IOLockSleep( _deblockRequestWriteLock,
_deblockRequestWriteLock,
THREAD_UNINT );
}
IOLockUnlock(_deblockRequestWriteLock);
}
deblockRequestCompletion(this, deblocker, kIOReturnSuccess, 0);
return;
}
void IOBlockStorageDriver::deblockRequestCompletion( void * target,
void * parameter,
IOReturn status,
UInt64 actualByteCount )
{
UInt64 byteStart;
IOStorageCompletion completion;
Context * context;
IODeblocker * deblocker = (IODeblocker *) parameter;
IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target;
if ( actualByteCount < deblocker->getLength() ||
status != kIOReturnSuccess ||
deblocker->getNextStage(&byteStart) == false )
{
if ( deblocker->getRequestBuffer()->getDirection() == kIODirectionOut )
{
IOLockLock(driver->_deblockRequestWriteLock);
driver->_deblockRequestWriteLockCount--;
if ( driver->_deblockRequestWriteLockCount > 0 )
{
IOLockWakeup( driver->_deblockRequestWriteLock,
driver->_deblockRequestWriteLock,
true );
}
IOLockUnlock(driver->_deblockRequestWriteLock);
}
deblocker->getRequestCompletion(&completion, &status, &actualByteCount);
IOStorage::complete(completion, status, actualByteCount);
deblocker->release();
return;
}
completion.target = driver;
completion.action = deblockRequestCompletion;
completion.parameter = deblocker;
context = (Context *) deblocker->getRequestContext();
driver->breakUpRequest(byteStart, deblocker, completion, context);
return;
}
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 )
{
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;
if ( withMaximumBlockCount )
{
UInt64 blockCountInBytes;
blockCountInBytes = withMaximumBlockCount * withRequestBlockSize;
if ( withMaximumByteCount )
{
withMaximumByteCount = min(blockCountInBytes, withMaximumByteCount);
}
else
{
withMaximumByteCount = blockCountInBytes;
}
}
while ( segment || bufferOffset < bufferLength )
{
if ( segment == 0 )
{
segment = buffer->getPhysicalSegment(bufferOffset, &segmentSize);
assert(segment);
assert(segmentSize);
bufferOffset += segmentSize;
}
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 )
{
if ( withMaximumSegmentByteCount )
{
if ( chunkSize > withMaximumSegmentByteCount )
{
segment = chunk + withMaximumSegmentByteCount;
segmentSize = chunkSize - withMaximumSegmentByteCount;
breakSize -= segmentSize;
chunkSize -= segmentSize;
}
}
if ( withMaximumByteCount )
{
if ( breakSize >= withMaximumByteCount )
{
breakSize = withMaximumByteCount;
break;
}
}
}
if ( segment )
{
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 )
{
IOBreaker * me = new IOBreaker;
if ( me && me->initWithBreakSize(
breakSize,
withMaximumBlockCount,
withMaximumByteCount,
withMaximumSegmentCount,
withMaximumSegmentByteCount,
withRequestBlockSize,
withRequestStart,
withRequestBuffer,
withRequestCompletion,
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 )
{
if ( super::initSubRange(
withRequestBuffer,
0,
withRequestBuffer->getLength(),
withRequestBuffer->getDirection() ) == false )
{
return false;
}
_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()
{
if ( _requestBuffer ) _requestBuffer->release();
super::free();
}
bool IOBreaker::getNextStage(UInt64 * byteStart)
{
if ( _start + _length < _requestCount )
{
_start += _length;
_length = min(_breakSize, _requestCount - _start);
_breakSize = getBreakSize(
_maximumBlockCount,
_maximumByteCount,
_maximumSegmentCount,
_maximumSegmentByteCount,
_requestBlockSize,
_requestBuffer,
_start + _length );
}
else
{
return false;
}
*byteStart = _requestStart + _start;
return true;
}
void IOBreaker::getRequestCompletion( IOStorageCompletion * completion,
IOReturn * status,
UInt64 * actualByteCount )
{
*actualByteCount += _start;
*completion = _requestCompletion;
}
IOMemoryDescriptor * IOBreaker::getRequestBuffer()
{
return _requestBuffer;
}
void * IOBreaker::getRequestContext()
{
return _requestContext;
}
void IOBlockStorageDriver::breakUpRequest(
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageCompletion completion,
IOBlockStorageDriver::Context * context )
{
IOBreaker * breaker;
UInt64 breakSize;
assert((byteStart % context->block.size) == 0);
assert((buffer->getLength() % context->block.size) == 0);
if ( buffer->getDirection() == kIODirectionIn )
{
breakSize = IOBreaker::getBreakSize(
_maxReadBlockTransfer,
_maxReadByteTransfer,
_maxReadSegmentTransfer,
_maxReadSegmentByteTransfer,
context->block.size,
buffer,
0 );
}
else
{
breakSize = IOBreaker::getBreakSize(
_maxWriteBlockTransfer,
_maxWriteByteTransfer,
_maxWriteSegmentTransfer,
_maxWriteSegmentByteTransfer,
context->block.size,
buffer,
0 );
}
if ( buffer->getLength() <= breakSize )
{
executeRequest(byteStart, buffer, completion, context);
return;
}
breakSize = IOTrunc(breakSize, context->block.size);
if ( buffer->getDirection() == kIODirectionIn )
{
breaker = IOBreaker::withBreakSize(
breakSize,
_maxReadBlockTransfer,
_maxReadByteTransfer,
_maxReadSegmentTransfer,
_maxReadSegmentByteTransfer,
context->block.size,
byteStart,
buffer,
completion,
context );
}
else
{
breaker = IOBreaker::withBreakSize(
breakSize,
_maxWriteBlockTransfer,
_maxWriteByteTransfer,
_maxWriteSegmentTransfer,
_maxWriteSegmentByteTransfer,
context->block.size,
byteStart,
buffer,
completion,
context );
}
if ( breaker == 0 )
{
complete(completion, kIOReturnNoMemory);
return;
}
breakUpRequestCompletion(this, breaker, kIOReturnSuccess, 0);
return;
}
OSMetaClassDefineReservedUsed(IOBlockStorageDriver, 0);
void IOBlockStorageDriver::breakUpRequestCompletion( void * target,
void * parameter,
IOReturn status,
UInt64 actualByteCount )
{
UInt64 byteStart;
IOStorageCompletion completion;
Context * context;
IOBreaker * breaker = (IOBreaker *) parameter;
IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target;
if ( actualByteCount < breaker->getLength() ||
status != kIOReturnSuccess ||
breaker->getNextStage(&byteStart) == false )
{
breaker->getRequestCompletion(&completion, &status, &actualByteCount);
IOStorage::complete(completion, status, actualByteCount);
breaker->release();
return;
}
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);