IOBlockStorageDriver.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOMapper.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOSubMemoryDescriptor.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/storage/IOBlockStorageDevice.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <kern/thread_call.h>
#define super IOStorage
OSDefineMetaClassAndStructors(IOBlockStorageDriver, IOStorage)
#ifdef __LP64__
#define original request
#else
#define isMediaRemovable() _removable
#endif
#ifndef __LP64__
#define kIOBlockStorageDriverAttributesUnsupported ( ( IOStorage::ExpansionData * ) 2 )
extern IOStorageAttributes gIOStorageAttributesUnsupported;
extern "C" void _ZN20IOBlockStorageDriver14prepareRequestEyP18IOMemoryDescriptor19IOStorageCompletion( IOBlockStorageDriver *, UInt64, IOMemoryDescriptor *, IOStorageCompletion );
#define prepareRequestAttributes( driver ) ( OSMemberFunctionCast( void *, driver, ( void ( IOBlockStorageDriver::* )( UInt64, IOMemoryDescriptor *, IOStorageCompletion ) ) &IOBlockStorageDriver::prepareRequest ) == _ZN20IOBlockStorageDriver14prepareRequestEyP18IOMemoryDescriptor19IOStorageCompletion )
#endif
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)
{
#ifndef __LP64__
if (prepareRequestAttributes(this) == false)
IOStorage::_expansionData = kIOBlockStorageDriverAttributesUnsupported;
#endif
if (super::init(properties) == false) return false;
_expansionData = IONew(ExpansionData, 1);
if (_expansionData == 0) return false;
initMediaState();
_ejectable = false;
_removable = false;
_mediaBlockSize = 0;
_maxBlockNumber = 0;
_writeProtected = false;
#ifdef __LP64__
_maxReadBlockTransfer = 0;
_maxWriteBlockTransfer = 0;
_maxReadByteTransfer = 0;
_maxWriteByteTransfer = 0;
#else
_maxReadBlockTransfer = 256;
_maxWriteBlockTransfer = 256;
_maxReadByteTransfer = 131072;
_maxWriteByteTransfer = 131072;
#endif
_maxReadSegmentTransfer = 0;
_maxWriteSegmentTransfer = 0;
_maxReadSegmentByteTransfer = 0;
_maxWriteSegmentByteTransfer = 0;
_minSegmentAlignmentByteTransfer = 4;
_maxSegmentWidthByteTransfer = 0;
_contexts = NULL;
_contextsLock = IOSimpleLockAlloc();
_contextsCount = 0;
_contextsMaxCount = 32;
if (_contextsLock == 0)
return false;
_deblockRequestWriteLock = IOLockAlloc();
_deblockRequestWriteLockCount = 0;
_openAssertions = 0;
_openClients = OSSet::withCapacity(2);
_powerEventNotifier = 0;
for (unsigned index = 0; index < kStatisticsCount; index++)
_statistics[index] = OSNumber::withNumber(0ULL, 64);
if (_deblockRequestWriteLock == 0 || _openClients == 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)
{
bool success;
success = open(this);
if (success)
{
success = handleStart(provider);
close(this);
}
if (success)
{
registerService();
}
return success;
}
bool IOBlockStorageDriver::didTerminate(IOService * provider,
IOOptionBits options,
bool * defer)
{
decommissionMedia(false);
return super::didTerminate(provider, options, defer);
}
bool IOBlockStorageDriver::yield(IOService * provider,
IOOptionBits options,
void * argument)
{
return false;
}
void IOBlockStorageDriver::free()
{
while (_contexts)
{
Context * context = _contexts;
_contexts = context->next;
_contextsCount--;
IODelete(context, Context, 1);
}
if (_contextsLock) IOSimpleLockFree(_contextsLock);
if (_deblockRequestWriteLock) IOLockFree(_deblockRequestWriteLock);
if (_openClients) _openClients->release();
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 (client != this)
{
if (_mediaObject == NULL)
{
return false;
}
}
if (_openClients->getCount() == 0)
{
if (getProvider()->open(this) == false)
{
return false;
}
}
if (client != this)
{
_openClients->setObject(client);
}
else
{
if (_openAssertions == 0)
{
_openClients->setObject(client);
}
_openAssertions++;
}
return true;
}
bool IOBlockStorageDriver::handleIsOpen(const IOService * client) const
{
if (client)
{
return _openClients->containsObject(client);
}
else
{
return _openClients->getCount() ? true : false;
}
}
void IOBlockStorageDriver::handleClose(IOService * client, IOOptionBits options)
{
assert(client);
if (client != this)
{
_openClients->removeObject(client);
}
else
{
_openAssertions--;
if (_openAssertions == 0)
{
_openClients->removeObject(client);
}
}
if (_openClients->getCount() == 0)
{
if (_mediaObject)
{
if (_mediaObject->isInactive())
{
message(kIOMessageServiceIsRequestingClose, getProvider(), 0);
}
}
getProvider()->close(this);
}
}
void IOBlockStorageDriver::read(IOService * client,
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageAttributes * attributes,
IOStorageCompletion * completion)
{
assert( buffer->getDirection( ) == kIODirectionIn );
#ifndef __LP64__
if ( IOStorage::_expansionData )
{
if ( attributes == &gIOStorageAttributesUnsupported )
{
attributes = NULL;
if ( IOStorage::_expansionData == kIOBlockStorageDriverAttributesUnsupported )
{
prepareRequest( byteStart, buffer, completion ? *completion : ( IOStorageCompletion ) { 0 } );
return;
}
}
else
{
if ( attributes && attributes->options )
{
complete( completion, kIOReturnUnsupported );
}
else
{
if ( IOStorage::_expansionData == kIOBlockStorageDriverAttributesUnsupported )
{
prepareRequest( byteStart, buffer, completion ? *completion : ( IOStorageCompletion ) { 0 } );
}
else
{
read( client, byteStart, buffer, completion ? *completion : ( IOStorageCompletion ) { 0 } );
}
}
return;
}
}
#endif
prepareRequest( byteStart, buffer, attributes, completion );
}
void IOBlockStorageDriver::write(IOService * client,
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageAttributes * attributes,
IOStorageCompletion * completion)
{
assert( buffer->getDirection( ) == kIODirectionOut );
#ifndef __LP64__
if ( IOStorage::_expansionData )
{
if ( attributes == &gIOStorageAttributesUnsupported )
{
attributes = NULL;
if ( IOStorage::_expansionData == kIOBlockStorageDriverAttributesUnsupported )
{
prepareRequest( byteStart, buffer, completion ? *completion : ( IOStorageCompletion ) { 0 } );
return;
}
}
else
{
if ( attributes && attributes->options )
{
complete( completion, kIOReturnUnsupported );
}
else
{
if ( IOStorage::_expansionData == kIOBlockStorageDriverAttributesUnsupported )
{
prepareRequest( byteStart, buffer, completion ? *completion : ( IOStorageCompletion ) { 0 } );
}
else
{
write( client, byteStart, buffer, completion ? *completion : ( IOStorageCompletion ) { 0 } );
}
}
return;
}
}
#endif
prepareRequest( byteStart, buffer, attributes, 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);
}
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;
IOSimpleLockLock(_contextsLock);
context = _contexts;
if (context)
{
_contexts = context->next;
_contextsCount--;
}
IOSimpleLockUnlock(_contextsLock);
if (context == 0)
{
context = IONew(Context, 1);
}
if (context)
{
bzero(context, sizeof(Context));
}
return context;
}
void IOBlockStorageDriver::deleteContext(
IOBlockStorageDriver::Context * context)
{
IOSimpleLockLock(_contextsLock);
if (_contextsCount < _contextsMaxCount)
{
context->next = _contexts;
_contexts = context;
_contextsCount++;
context = 0;
}
IOSimpleLockUnlock(_contextsLock);
if (context)
{
IODelete(context, Context, 1);
}
}
#ifndef __LP64__
void IOBlockStorageDriver::prepareRequest(UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageCompletion completion)
{
prepareRequest( byteStart, buffer, NULL, &completion );
}
#endif
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;
}
}
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()
{
}
void IOBlockStorageDriver::unschedulePoller()
{
}
IOReturn IOBlockStorageDriver::message(UInt32 type,
IOService * provider,
void * argument)
{
switch (type)
{
case kIOMessageMediaParametersHaveChanged:
{
IOReturn status;
if (open(this)) {
status = recordMediaParameters();
if (status == kIOReturnSuccess) {
UInt64 nbytes;
IOMedia *m;
if (_maxBlockNumber) {
nbytes = _mediaBlockSize * (_maxBlockNumber + 1);
} else {
nbytes = 0;
}
m = instantiateMediaObject(0,nbytes,_mediaBlockSize,NULL);
if (m) {
lockForArbitration();
if (_mediaObject) {
_mediaObject->init( m->getBase(),
m->getSize(),
m->getPreferredBlockSize(),
m->getAttributes(),
m->isWhole(),
m->isWritable(),
m->getContentHint() );
_mediaObject->messageClients(kIOMessageServicePropertyChange);
} else {
status = kIOReturnNoMedia;
}
unlockForArbitration();
m->release();
} else {
status = kIOReturnBadArgument;
}
}
close(this);
} else {
status = kIOReturnNotAttached;
}
return status;
}
case kIOMessageMediaStateHasChanged:
{
IOReturn status;
if (open(this)) {
status = mediaStateHasChanged((uintptr_t) argument);
close(this);
} else {
status = kIOReturnNotAttached;
}
return status;
}
case kIOMessageServiceIsRequestingClose:
{
IOReturn status;
status = decommissionMedia(false);
return status;
}
default:
{
return super::message(type, provider, argument);
}
}
}
IOReturn
IOBlockStorageDriver::acceptNewMedia(void)
{
IOReturn result;
bool ok;
UInt64 nbytes;
char name[128];
bool nameSep;
IOMedia *m;
if (_maxBlockNumber) {
nbytes = _mediaBlockSize * (_maxBlockNumber + 1);
} else {
nbytes = 0;
}
name[0] = 0;
nameSep = false;
if (getProvider()->getVendorString()) {
strlcat(name, getProvider()->getVendorString(), sizeof(name) - strlen(name));
nameSep = true;
}
if (getProvider()->getProductString()) {
if (nameSep == true) strlcat(name, " ", sizeof(name) - strlen(name));
strlcat(name, getProvider()->getProductString(), sizeof(name) - strlen(name));
nameSep = true;
}
if (nameSep == true) strlcat(name, " ", sizeof(name) - strlen(name));
strlcat(name, "Media", sizeof(name) - strlen(name));
strclean(name);
m = instantiateMediaObject(0,nbytes,_mediaBlockSize,name);
result = (m) ? kIOReturnSuccess : kIOReturnBadArgument;
if (result == kIOReturnSuccess) {
if (getProperty(kIOMediaIconKey, gIOServicePlane)) {
m->removeProperty(kIOMediaIconKey);
}
ok = m->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 (m->attachToParent(parent, gIODTPlane)) {
char location[ 32 ];
if (unitLUN && unitLUN->unsigned32BitValue()) {
snprintf(location, sizeof(location), "%x,%x:0", unit->unsigned32BitValue(), unitLUN->unsigned32BitValue());
} else {
snprintf(location, sizeof(location), "%x:0", unit->unsigned32BitValue());
}
m->setLocation(location, gIODTPlane);
m->setName(unitName ? unitName->getCStringNoCopy() : "", gIODTPlane);
}
break;
}
}
lockForArbitration();
_mediaObject = m;
_mediaObject->retain();
unlockForArbitration();
m->registerService(kIOServiceAsynchronous);
} else {
result = kIOReturnNoMemory;
}
m->release();
}
return(result);
}
IOReturn
IOBlockStorageDriver::checkForMedia(void)
{
IOReturn result;
bool currentState;
bool changed;
result = getProvider()->reportMediaState(¤tState,&changed);
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::checkForMedia; err '%s' from reportMediaState\n",
getName(),stringFromReturn(result));
} else {
changed = _mediaObject ? !currentState : currentState;
if (changed) {
result = mediaStateHasChanged(currentState ? kIOMediaStateOnline
: kIOMediaStateOffline);
}
}
return(result);
}
IOReturn
IOBlockStorageDriver::mediaStateHasChanged(IOMediaState state)
{
IOReturn result;
if (state == kIOMediaStateOnline) {
if (_mediaObject) {
return(kIOReturnBadArgument);
}
initMediaState();
if (validateNewMedia() == false) {
rejectMedia();
return(kIOReturnSuccess);
}
result = recordMediaParameters();
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::mediaStateHasChanged: err '%s' from recordMediaParameters\n",
getName(),stringFromReturn(result));
return(result);
}
result = acceptNewMedia();
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::mediaStateHasChanged; err '%s' from acceptNewMedia\n",
getName(),stringFromReturn(result));
}
return(result);
} else {
result = decommissionMedia(true);
if (result != kIOReturnSuccess && result != kIOReturnNoMedia) {
IOLog("%s[IOBlockStorageDriver]::mediaStateHasChanged; 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)
{
IOMedia *m = NULL;
IOReturn result;
lockForArbitration();
if (_mediaObject) {
if (forcible || !_openClients->containsObject(_mediaObject)) {
m = _mediaObject;
_mediaObject = 0;
result = kIOReturnSuccess;
} else {
result = kIOReturnBusy;
}
} else {
result = kIOReturnNoMedia;
}
unlockForArbitration();
if (m) {
IORegistryEntry * parent;
if ( (parent = m->getParentEntry(gIODTPlane)) ) {
m->detachFromParent(parent, gIODTPlane);
}
m->terminate();
m->release();
}
return(result);
}
IOReturn
IOBlockStorageDriver::ejectMedia(void)
{
IOReturn result;
if (_ejectable)
{
result = decommissionMedia(false);
}
else
{
lockForArbitration();
if (_mediaObject) {
if (!_openClients->containsObject(_mediaObject)) {
result = kIOReturnSuccess;
} else {
result = kIOReturnBusy;
}
} else {
result = kIOReturnNoMedia;
}
unlockForArbitration();
}
if (result == kIOReturnSuccess) {
if (!_writeProtected) {
(void)getProvider()->doSynchronizeCache();
}
(void)getProvider()->doEjectMedia();
}
return(result);
}
void
IOBlockStorageDriver::executeRequest(UInt64 byteStart,
IOMemoryDescriptor * buffer,
#ifdef __LP64__
IOStorageAttributes * attributes,
IOStorageCompletion * completion,
#else
IOStorageCompletion completion,
#endif
IOBlockStorageDriver::Context * context)
{
UInt64 block;
UInt64 nblks;
IOReturn result;
if (!_mediaObject) {
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;
#ifdef __LP64__
result = getProvider()->doAsyncReadWrite(buffer,block,nblks,attributes,completion);
#else
result = getProvider()->doAsyncReadWrite(buffer,block,nblks,&context->request.attributes,&completion);
#endif
if (result != kIOReturnSuccess) {
if (result != kIOReturnNotPermitted) {
IOLog("%s[IOBlockStorageDriver]; executeRequest: request failed to start!\n",getName());
}
complete(completion,result);
return;
}
}
IOReturn
IOBlockStorageDriver::formatMedia(UInt64 byteCapacity)
{
IOReturn result;
result = decommissionMedia(false);
if (result == kIOReturnSuccess) {
result = getProvider()->doFormatMedia(byteCapacity);
if (result == kIOReturnSuccess) {
result = mediaStateHasChanged(kIOMediaStateOnline);
} else {
(void)mediaStateHasChanged(kIOMediaStateOnline);
}
}
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 (_mediaObject) {
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->open(driver)) {
if (driver->_mediaObject) {
if (!driver->_writeProtected) {
driver->getProvider()->doSynchronizeCache();
}
if (!driver->_removable && (messageType == kIOMessageSystemWillPowerOff)) {
driver->getProvider()->doEjectMedia();
}
}
driver->close(driver);
}
result = kIOReturnSuccess;
break;
default:
result = kIOReturnUnsupported;
break;
}
return(result);
}
bool
IOBlockStorageDriver::handleStart(IOService * provider)
{
OSObject *object;
OSNumber *number;
IOReturn result;
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()->reportEjectability(&_ejectable);
if (result != kIOReturnSuccess) {
IOLog("%s[IOBlockStorageDriver]::handleStart; err '%s' from reportEjectability\n",
getName(),stringFromReturn(result));
return(false);
}
} else {
_ejectable = false;
}
object = copyProperty(kIOMaximumBlockCountReadKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_maxReadBlockTransfer = number->unsigned64BitValue();
}
object->release();
}
#ifndef __LP64__
if (object == 0) {
UInt64 maxReadTransfer;
result = getProvider()->reportMaxReadTransfer(512, &maxReadTransfer);
if (result == kIOReturnSuccess) {
_maxReadBlockTransfer = maxReadTransfer / 512;
}
getProvider()->setProperty(kIOMaximumBlockCountReadKey, _maxReadBlockTransfer, 64);
}
#endif
object = copyProperty(kIOMaximumBlockCountWriteKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_maxWriteBlockTransfer = number->unsigned64BitValue();
}
object->release();
}
#ifndef __LP64__
if (object == 0) {
UInt64 maxWriteTransfer;
result = getProvider()->reportMaxWriteTransfer(512, &maxWriteTransfer);
if (result == kIOReturnSuccess) {
_maxWriteBlockTransfer = maxWriteTransfer / 512;
}
getProvider()->setProperty(kIOMaximumBlockCountWriteKey, _maxWriteBlockTransfer, 64);
}
#endif
#ifndef __LP64__
_maxReadSegmentTransfer = _maxReadByteTransfer / page_size;
_maxReadByteTransfer = 0;
#endif
object = copyProperty(kIOMaximumByteCountReadKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_maxReadByteTransfer = number->unsigned64BitValue();
}
object->release();
}
#ifndef __LP64__
_maxWriteSegmentTransfer = _maxWriteByteTransfer / page_size;
_maxWriteByteTransfer = 0;
#endif
object = copyProperty(kIOMaximumByteCountWriteKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_maxWriteByteTransfer = number->unsigned64BitValue();
}
object->release();
}
object = copyProperty(kIOMaximumSegmentCountReadKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_maxReadSegmentTransfer = number->unsigned64BitValue();
}
object->release();
}
#ifndef __LP64__
if (object == 0) {
getProvider()->setProperty(kIOMaximumSegmentCountReadKey, _maxReadSegmentTransfer, 64);
}
#endif
object = copyProperty(kIOMaximumSegmentCountWriteKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_maxWriteSegmentTransfer = number->unsigned64BitValue();
}
object->release();
}
#ifndef __LP64__
if (object == 0) {
getProvider()->setProperty(kIOMaximumSegmentCountWriteKey, _maxWriteSegmentTransfer, 64);
}
#endif
object = copyProperty(kIOMaximumSegmentByteCountReadKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_maxReadSegmentByteTransfer = number->unsigned64BitValue();
}
object->release();
}
object = copyProperty(kIOMaximumSegmentByteCountWriteKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_maxWriteSegmentByteTransfer = number->unsigned64BitValue();
}
object->release();
}
object = copyProperty(kIOMinimumSegmentAlignmentByteCountKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_minSegmentAlignmentByteTransfer = number->unsigned64BitValue();
}
object->release();
} else {
getProvider()->setProperty(kIOMinimumSegmentAlignmentByteCountKey, _minSegmentAlignmentByteTransfer, 64);
}
object = copyProperty(kIOMaximumSegmentAddressableBitCountKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
if (number->unsigned64BitValue()) {
if (number->unsigned64BitValue() < 64) {
_maxSegmentWidthByteTransfer = 1ULL << number->unsigned64BitValue();
}
}
}
object->release();
}
object = copyProperty(kIOCommandPoolSizeKey, gIOServicePlane);
if (object) {
number = OSDynamicCast(OSNumber, object);
if (number) {
_contextsMaxCount = number->unsigned32BitValue();
}
object->release();
}
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)
{
return false;
}
void
IOBlockStorageDriver::initMediaState(void)
{
_mediaType = 0;
}
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) {
const 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) {
if (string->isEqualTo(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();
}
}
if (mediaName) {
m->setName(mediaName);
}
return(m);
} else {
m->release();
return(NULL);
}
}
bool
IOBlockStorageDriver::isMediaEjectable(void) const
{
return(_ejectable);
}
#ifdef __LP64__
bool
IOBlockStorageDriver::isMediaRemovable(void) const
{
return(_removable);
}
#endif
bool
IOBlockStorageDriver::isMediaPollExpensive(void) const
{
return(false);
}
bool
IOBlockStorageDriver::isMediaPollRequired(void) const
{
return(false);
}
bool
IOBlockStorageDriver::isMediaWritable(void) const
{
return(!_writeProtected);
}
IOReturn
IOBlockStorageDriver::lockMedia(bool locked)
{
return(kIOReturnUnsupported);
}
IOReturn
IOBlockStorageDriver::pollMedia(void)
{
return(kIOReturnUnsupported);
}
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:
return(result);
}
void
IOBlockStorageDriver::rejectMedia(void)
{
(void)getProvider()->doEjectMedia();
}
void
IOBlockStorageDriver::stop(IOService * provider)
{
if (_powerEventNotifier) {
_powerEventNotifier->remove();
_powerEventNotifier = NULL;
release();
}
super::stop(provider);
}
IOReturn
IOBlockStorageDriver::synchronizeCache(IOService *client)
{
return(getProvider()->doSynchronizeCache());
}
IOReturn
IOBlockStorageDriver::unmap(IOService * client,
IOStorageExtent * extents,
UInt32 extentsCount,
UInt32 options)
{
UInt32 extentsIndex;
IOBlockStorageDeviceExtent * extentsOut;
UInt32 extentsOutCount;
assert( sizeof( IOStorageExtent ) == sizeof( IOBlockStorageDeviceExtent ) );
extentsOut = ( IOBlockStorageDeviceExtent * ) extents;
extentsOutCount = 0;
for ( extentsIndex = 0; extentsIndex < extentsCount; extentsIndex++ )
{
UInt64 blockStart;
UInt64 blockCount;
blockStart = ( extents[ extentsIndex ].byteStart + _mediaBlockSize - 1 ) / _mediaBlockSize;
blockCount = ( extents[ extentsIndex ].byteStart + extents[ extentsIndex ].byteCount ) / _mediaBlockSize;
if ( blockCount > blockStart )
{
blockCount = blockCount - blockStart;
extentsOut[ extentsOutCount ].blockStart = blockStart;
extentsOut[ extentsOutCount ].blockCount = blockCount;
extentsOutCount++;
}
}
if ( extentsOutCount )
{
return getProvider( )->doUnmap( extentsOut, extentsOutCount, options );
}
else
{
return kIOReturnSuccess;
}
}
bool IOBlockStorageDriver::lockPhysicalExtents(IOService * client)
{
return(true);
}
IOStorage * IOBlockStorageDriver::copyPhysicalExtent(IOService * client,
UInt64 * byteStart,
UInt64 * byteCount)
{
IOMedia *m;
lockForArbitration();
m = _mediaObject;
if (m) {
m->retain();
}
unlockForArbitration();
return(m);
}
void IOBlockStorageDriver::unlockPhysicalExtents(IOService * client)
{
return;
}
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;
IOStorageAttributes _requestAttributes;
IOStorageCompletion _requestCompletion;
void * _requestContext;
UInt64 _requestCount;
bool _requestIsOneBlock;
UInt64 _requestStart;
UInt64 _byteStart;
thread_call_t _threadCallback;
enum
{
kStageInit,
kStagePrepareExcessStart,
kStagePrepareExcessFinal,
kStageLast,
kStageDone
} _stage;
virtual void free();
public:
static IODeblocker * withBlockSize(
UInt64 blockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageAttributes * withRequestAttributes,
IOStorageCompletion * withRequestCompletion,
void * withRequestContext );
virtual bool initWithBlockSize(
UInt64 blockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageAttributes * withRequestAttributes,
IOStorageCompletion * withRequestCompletion,
void * withRequestContext );
virtual addr64_t getPhysicalSegment( IOByteCount offset,
IOByteCount * length,
IOOptionBits options = 0 );
virtual IOReturn prepare(IODirection forDirection = kIODirectionNone);
virtual IOReturn complete(IODirection forDirection = kIODirectionNone);
virtual bool getNextStage();
virtual IOStorageAttributes * getRequestAttributes();
virtual IOStorageCompletion * getRequestCompletion(UInt64 * actualByteCount);
virtual IOMemoryDescriptor * getRequestBuffer();
virtual void * getRequestContext();
virtual UInt64 getByteStart();
virtual thread_call_t getThreadCallback();
virtual bool setThreadCallback(thread_call_func_t callback);
};
#undef super
#define super IOMemoryDescriptor
OSDefineMetaClassAndStructors(IODeblocker, IOMemoryDescriptor)
IODeblocker * IODeblocker::withBlockSize(
UInt64 blockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageAttributes * withRequestAttributes,
IOStorageCompletion * withRequestCompletion,
void * withRequestContext )
{
IODeblocker * me = new IODeblocker;
if ( me && me->initWithBlockSize(
blockSize,
withRequestStart,
withRequestBuffer,
withRequestAttributes,
withRequestCompletion,
withRequestContext ) == false )
{
me->release();
me = 0;
}
return me;
}
bool IODeblocker::initWithBlockSize(
UInt64 blockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageAttributes * withRequestAttributes,
IOStorageCompletion * withRequestCompletion,
void * withRequestContext )
{
UInt32 excessBufferSize = 0;
if ( super::init() == false ) return false;
_blockSize = blockSize;
_chunksCount = 0;
_flags = kIODirectionNone;
_length = 0;
_requestBuffer = withRequestBuffer;
_requestBuffer->retain();
if (withRequestAttributes) _requestAttributes = *withRequestAttributes;
if (withRequestCompletion) _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();
if ( _threadCallback ) thread_call_free(_threadCallback);
super::free();
}
IOReturn IODeblocker::prepare(IODirection forDirection)
{
unsigned index;
IOReturn status = kIOReturnInternalError;
IOReturn statusUndo;
if ( forDirection == kIODirectionNone )
{
forDirection = (IODirection) (_flags & kIOMemoryDirectionMask);
}
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 = (IODirection) (_flags & kIOMemoryDirectionMask);
}
for ( unsigned index = 0; index < _chunksCount; index++ )
{
status = _chunks[index].buffer->complete(forDirection);
if ( status != kIOReturnSuccess ) statusFinal = status;
assert(status == kIOReturnSuccess);
}
return statusFinal;
}
addr64_t IODeblocker::getPhysicalSegment( IOByteCount offset,
IOByteCount * length,
IOOptionBits options )
{
assert(offset <= _length);
for ( unsigned index = 0; index < _chunksCount; index++ )
{
if ( offset < _chunks[index].length )
{
addr64_t address;
address = _chunks[index].buffer->getPhysicalSegment(
offset + _chunks[index].offset,
length,
options );
if ( length ) *length = min(*length, _chunks[index].length);
return address;
}
offset -= _chunks[index].length;
}
if ( length ) *length = 0;
return 0;
}
bool IODeblocker::getNextStage()
{
_chunksCount = 0;
_flags = (_flags & ~kIOMemoryDirectionMask) | kIODirectionNone;
_length = 0;
switch ( _requestBuffer->getDirection() )
{
case kIODirectionIn: {
switch ( _stage )
{
case kStageInit:
{
_stage = kStageLast;
_excessBuffer->setDirection(kIODirectionIn);
_flags = (_flags & ~kIOMemoryDirectionMask) | 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);
_flags = (_flags & ~kIOMemoryDirectionMask) | 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);
_flags = (_flags & ~kIOMemoryDirectionMask) | 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);
_flags = (_flags & ~kIOMemoryDirectionMask) | 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;
}
IOStorageAttributes * IODeblocker::getRequestAttributes()
{
return &_requestAttributes;
}
IOStorageCompletion * IODeblocker::getRequestCompletion(UInt64 * actualByteCount)
{
switch ( _stage )
{
case kStageInit: {
*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;
}
return &_requestCompletion;
}
IOMemoryDescriptor * IODeblocker::getRequestBuffer()
{
return _requestBuffer;
}
void * IODeblocker::getRequestContext()
{
return _requestContext;
}
UInt64 IODeblocker::getByteStart()
{
return _byteStart;
}
thread_call_t IODeblocker::getThreadCallback()
{
return _threadCallback;
}
bool IODeblocker::setThreadCallback(thread_call_func_t callback)
{
_threadCallback = thread_call_allocate(callback, this);
return _threadCallback ? true : false;
}
void IOBlockStorageDriver::deblockRequest(
UInt64 byteStart,
IOMemoryDescriptor * buffer,
#ifdef __LP64__
IOStorageAttributes * attributes,
IOStorageCompletion * completion,
#else
IOStorageCompletion completion,
#endif
IOBlockStorageDriver::Context * context )
{
IODeblocker * deblocker;
if ( (byteStart % context->block.size) == 0 &&
(buffer->getLength() % context->block.size) == 0 )
{
#ifdef __LP64__
breakUpRequest(byteStart, buffer, attributes, completion, context);
#else
breakUpRequest(byteStart, buffer, completion, context);
#endif
return;
}
deblocker = IODeblocker::withBlockSize(
context->block.size,
byteStart,
buffer,
#ifdef __LP64__
attributes,
completion,
#else
NULL,
&completion,
#endif
context );
if ( deblocker == 0 )
{
complete(completion, kIOReturnNoMemory);
return;
}
if ( buffer->getDirection() == kIODirectionOut )
{
IOLockLock(_deblockRequestWriteLock);
_deblockRequestWriteLockCount++;
if ( _deblockRequestWriteLockCount > 1 )
{
while ( IOLockSleep( _deblockRequestWriteLock,
_deblockRequestWriteLock,
THREAD_UNINT ) );
}
IOLockUnlock(_deblockRequestWriteLock);
}
deblockRequestCompletion(this, deblocker, kIOReturnSuccess, 0);
}
void IOBlockStorageDriver::deblockRequestCompletion( void * target,
void * parameter,
IOReturn status,
UInt64 actualByteCount )
{
thread_call_t callback;
IODeblocker * deblocker = (IODeblocker *) parameter;
IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target;
callback = deblocker->getThreadCallback();
if ( callback == 0 )
{
if ( deblocker->setThreadCallback(deblockRequestExecute) == false )
{
status = kIOReturnNoMemory;
}
}
if ( actualByteCount < deblocker->getLength() ||
status != kIOReturnSuccess ||
deblocker->getNextStage() == false )
{
IOStorageCompletion * completion;
if ( deblocker->getRequestBuffer()->getDirection() == kIODirectionOut )
{
IOLockLock(driver->_deblockRequestWriteLock);
driver->_deblockRequestWriteLockCount--;
if ( driver->_deblockRequestWriteLockCount > 0 )
{
IOLockWakeup( driver->_deblockRequestWriteLock,
driver->_deblockRequestWriteLock,
true );
}
IOLockUnlock(driver->_deblockRequestWriteLock);
}
completion = deblocker->getRequestCompletion(&actualByteCount);
IOStorage::complete(completion, status, actualByteCount);
deblocker->release();
}
else
{
if ( callback )
{
thread_call_enter1(callback, driver);
}
else
{
deblockRequestExecute(deblocker, driver);
}
}
}
void IOBlockStorageDriver::deblockRequestExecute(void * parameter, void * target)
{
IOStorageAttributes * attributes;
UInt64 byteStart;
IOStorageCompletion completion;
Context * context;
IODeblocker * deblocker = (IODeblocker *) parameter;
IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target;
attributes = deblocker->getRequestAttributes();
byteStart = deblocker->getByteStart();
completion.target = driver;
completion.action = deblockRequestCompletion;
completion.parameter = deblocker;
context = (Context *) deblocker->getRequestContext();
#ifdef __LP64__
driver->breakUpRequest(byteStart, deblocker, attributes, &completion, context);
#else
driver->breakUpRequest(byteStart, deblocker, completion, context);
#endif
}
class IOBreaker : public IOSubMemoryDescriptor
{
OSDeclareDefaultStructors(IOBreaker);
protected:
UInt64 _breakSize;
UInt64 _maximumBlockCount;
UInt64 _maximumByteCount;
UInt64 _maximumSegmentCount;
UInt64 _maximumSegmentByteCount;
UInt64 _minimumSegmentAlignmentByteCount;
UInt64 _maximumSegmentWidthByteCount;
UInt64 _requestBlockSize;
IOMemoryDescriptor * _requestBuffer;
IOStorageAttributes _requestAttributes;
IOStorageCompletion _requestCompletion;
void * _requestContext;
UInt64 _requestCount;
UInt64 _requestStart;
UInt64 _byteStart;
thread_call_t _threadCallback;
virtual void free();
public:
static UInt64 getBreakSize(
UInt64 withMaximumBlockCount,
UInt64 withMaximumByteCount,
UInt64 withMaximumSegmentCount,
UInt64 withMaximumSegmentByteCount,
UInt64 withMinimumSegmentAlignmentByteCount,
UInt64 withMaximumSegmentWidthByteCount,
UInt64 withRequestBlockSize,
IOMemoryDescriptor * withRequestBuffer,
UInt64 withRequestBufferOffset );
static IOBreaker * withBreakSize(
UInt64 breakSize,
UInt64 withMaximumBlockCount,
UInt64 withMaximumByteCount,
UInt64 withMaximumSegmentCount,
UInt64 withMaximumSegmentByteCount,
UInt64 withMinimumSegmentAlignmentByteCount,
UInt64 withMaximumSegmentWidthByteCount,
UInt64 withRequestBlockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageAttributes * withRequestAttributes,
IOStorageCompletion * withRequestCompletion,
void * withRequestContext );
virtual bool initWithBreakSize(
UInt64 breakSize,
UInt64 withMaximumBlockCount,
UInt64 withMaximumByteCount,
UInt64 withMaximumSegmentCount,
UInt64 withMaximumSegmentByteCount,
UInt64 withMinimumSegmentAlignmentByteCount,
UInt64 withMaximumSegmentWidthByteCount,
UInt64 withRequestBlockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageAttributes * withRequestAttributes,
IOStorageCompletion * withRequestCompletion,
void * withRequestContext );
virtual bool getNextStage();
virtual IOStorageAttributes * getRequestAttributes();
virtual IOStorageCompletion * getRequestCompletion(UInt64 * actualByteCount);
virtual IOMemoryDescriptor * getRequestBuffer();
virtual void * getRequestContext();
virtual UInt64 getByteStart();
virtual thread_call_t getThreadCallback();
virtual bool setThreadCallback(thread_call_func_t callback);
};
#undef super
#define super IOSubMemoryDescriptor
OSDefineMetaClassAndStructors(IOBreaker, IOSubMemoryDescriptor)
UInt64 IOBreaker::getBreakSize(
UInt64 withMaximumBlockCount,
UInt64 withMaximumByteCount,
UInt64 withMaximumSegmentCount,
UInt64 withMaximumSegmentByteCount,
UInt64 withMinimumSegmentAlignmentByteCount,
UInt64 withMaximumSegmentWidthByteCount,
UInt64 withRequestBlockSize,
IOMemoryDescriptor * withRequestBuffer,
UInt64 withRequestBufferOffset )
{
UInt64 breakSize = 0;
IOMemoryDescriptor * buffer = withRequestBuffer;
IOByteCount bufferLength = withRequestBuffer->getLength();
IOByteCount bufferOffset = withRequestBufferOffset;
addr64_t chunk = 0;
UInt32 chunkSize = 0;
addr64_t segment = 0;
UInt32 segmentCount = 0;
IOByteCount segmentSize = 0;
if ( withMinimumSegmentAlignmentByteCount )
{
withMinimumSegmentAlignmentByteCount--;
}
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, 0);
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 )
{
segmentSize = chunkSize - withMaximumSegmentByteCount;
}
}
if ( withMinimumSegmentAlignmentByteCount )
{
if ( ( chunk & withMinimumSegmentAlignmentByteCount ) || ( chunkSize & withMinimumSegmentAlignmentByteCount ) )
{
if ( chunkSize > PAGE_SIZE )
{
segmentSize = max(chunkSize - PAGE_SIZE, segmentSize);
}
}
}
if ( withMaximumSegmentWidthByteCount )
{
if ( chunk >= withMaximumSegmentWidthByteCount )
{
if ( chunkSize > PAGE_SIZE )
{
segmentSize = max(chunkSize - PAGE_SIZE, segmentSize);
}
}
else if ( chunk + chunkSize > withMaximumSegmentWidthByteCount )
{
segmentSize = max(chunk + chunkSize - withMaximumSegmentWidthByteCount, segmentSize);
}
}
if ( segmentSize )
{
segment = chunk + chunkSize - segmentSize;
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 withMinimumSegmentAlignmentByteCount,
UInt64 withMaximumSegmentWidthByteCount,
UInt64 withRequestBlockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageAttributes * withRequestAttributes,
IOStorageCompletion * withRequestCompletion,
void * withRequestContext )
{
IOBreaker * me = new IOBreaker;
if ( me && me->initWithBreakSize(
breakSize,
withMaximumBlockCount,
withMaximumByteCount,
withMaximumSegmentCount,
withMaximumSegmentByteCount,
withMinimumSegmentAlignmentByteCount,
withMaximumSegmentWidthByteCount,
withRequestBlockSize,
withRequestStart,
withRequestBuffer,
withRequestAttributes,
withRequestCompletion,
withRequestContext ) == false )
{
me->release();
me = 0;
}
return me;
}
bool IOBreaker::initWithBreakSize(
UInt64 breakSize,
UInt64 withMaximumBlockCount,
UInt64 withMaximumByteCount,
UInt64 withMaximumSegmentCount,
UInt64 withMaximumSegmentByteCount,
UInt64 withMinimumSegmentAlignmentByteCount,
UInt64 withMaximumSegmentWidthByteCount,
UInt64 withRequestBlockSize,
UInt64 withRequestStart,
IOMemoryDescriptor * withRequestBuffer,
IOStorageAttributes * withRequestAttributes,
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;
_minimumSegmentAlignmentByteCount = withMinimumSegmentAlignmentByteCount;
_maximumSegmentWidthByteCount = withMaximumSegmentWidthByteCount;
_requestBlockSize = withRequestBlockSize;
_requestBuffer = withRequestBuffer;
_requestBuffer->retain();
if (withRequestAttributes) _requestAttributes = *withRequestAttributes;
if (withRequestCompletion) _requestCompletion = *withRequestCompletion;
_requestContext = withRequestContext;
_requestCount = withRequestBuffer->getLength();
_requestStart = withRequestStart;
return true;
}
void IOBreaker::free()
{
if ( _requestBuffer ) _requestBuffer->release();
if ( _threadCallback ) thread_call_free(_threadCallback);
super::free();
}
bool IOBreaker::getNextStage()
{
if ( _start + _length < _requestCount )
{
_start += _length;
_length = min(_breakSize, _requestCount - _start);
_breakSize = getBreakSize(
_maximumBlockCount,
_maximumByteCount,
_maximumSegmentCount,
_maximumSegmentByteCount,
_minimumSegmentAlignmentByteCount,
_maximumSegmentWidthByteCount,
_requestBlockSize,
_requestBuffer,
_start + _length );
}
else
{
return false;
}
_byteStart = _requestStart + _start;
return true;
}
IOStorageAttributes * IOBreaker::getRequestAttributes()
{
return &_requestAttributes;
}
IOStorageCompletion * IOBreaker::getRequestCompletion(UInt64 * actualByteCount)
{
*actualByteCount += _start;
return &_requestCompletion;
}
IOMemoryDescriptor * IOBreaker::getRequestBuffer()
{
return _requestBuffer;
}
void * IOBreaker::getRequestContext()
{
return _requestContext;
}
UInt64 IOBreaker::getByteStart()
{
return _byteStart;
}
thread_call_t IOBreaker::getThreadCallback()
{
return _threadCallback;
}
bool IOBreaker::setThreadCallback(thread_call_func_t callback)
{
_threadCallback = thread_call_allocate(callback, this);
return _threadCallback ? true : false;
}
void IOBlockStorageDriver::breakUpRequest(
UInt64 byteStart,
IOMemoryDescriptor * buffer,
#ifdef __LP64__
IOStorageAttributes * attributes,
IOStorageCompletion * completion,
#else
IOStorageCompletion completion,
#endif
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,
_minSegmentAlignmentByteTransfer,
_maxSegmentWidthByteTransfer,
context->block.size,
buffer,
0 );
}
else
{
breakSize = IOBreaker::getBreakSize(
_maxWriteBlockTransfer,
_maxWriteByteTransfer,
_maxWriteSegmentTransfer,
_maxWriteSegmentByteTransfer,
_minSegmentAlignmentByteTransfer,
_maxSegmentWidthByteTransfer,
context->block.size,
buffer,
0 );
}
if ( buffer->getLength() <= breakSize )
{
#ifdef __LP64__
executeRequest(byteStart, buffer, attributes, completion, context);
#else
executeRequest(byteStart, buffer, completion, context);
#endif
return;
}
breakSize = IOTrunc(breakSize, context->block.size);
if ( buffer->getDirection() == kIODirectionIn )
{
breaker = IOBreaker::withBreakSize(
breakSize,
_maxReadBlockTransfer,
_maxReadByteTransfer,
_maxReadSegmentTransfer,
_maxReadSegmentByteTransfer,
_minSegmentAlignmentByteTransfer,
_maxSegmentWidthByteTransfer,
context->block.size,
byteStart,
buffer,
#ifdef __LP64__
attributes,
completion,
#else
NULL,
&completion,
#endif
context );
}
else
{
breaker = IOBreaker::withBreakSize(
breakSize,
_maxWriteBlockTransfer,
_maxWriteByteTransfer,
_maxWriteSegmentTransfer,
_maxWriteSegmentByteTransfer,
_minSegmentAlignmentByteTransfer,
_maxSegmentWidthByteTransfer,
context->block.size,
byteStart,
buffer,
#ifdef __LP64__
attributes,
completion,
#else
NULL,
&completion,
#endif
context );
}
if ( breaker == 0 )
{
complete(completion, kIOReturnNoMemory);
return;
}
breakUpRequestCompletion(this, breaker, kIOReturnSuccess, 0);
}
void IOBlockStorageDriver::breakUpRequestCompletion( void * target,
void * parameter,
IOReturn status,
UInt64 actualByteCount )
{
IOBreaker * breaker = (IOBreaker *) parameter;
thread_call_t callback;
IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target;
callback = breaker->getThreadCallback();
if ( callback == 0 )
{
if ( breaker->setThreadCallback(breakUpRequestExecute) == false )
{
status = kIOReturnNoMemory;
}
}
if ( actualByteCount < breaker->getLength() ||
status != kIOReturnSuccess ||
breaker->getNextStage() == false )
{
IOStorageCompletion * completion;
completion = breaker->getRequestCompletion(&actualByteCount);
IOStorage::complete(completion, status, actualByteCount);
breaker->release();
}
else
{
if ( callback )
{
thread_call_enter1(callback, driver);
}
else
{
breakUpRequestExecute(breaker, driver);
}
}
}
void IOBlockStorageDriver::breakUpRequestExecute(void * parameter, void * target)
{
IOStorageAttributes * attributes;
IOBreaker * breaker = (IOBreaker *) parameter;
UInt64 byteStart;
IOStorageCompletion completion;
Context * context;
IOBlockStorageDriver * driver = (IOBlockStorageDriver *) target;
attributes = breaker->getRequestAttributes();
byteStart = breaker->getByteStart();
completion.target = driver;
completion.action = breakUpRequestCompletion;
completion.parameter = breaker;
context = (Context *) breaker->getRequestContext();
#ifdef __LP64__
driver->executeRequest(byteStart, breaker, attributes, &completion, context);
#else
driver->executeRequest(byteStart, breaker, completion, context);
#endif
}
void IOBlockStorageDriver::prepareRequest(UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageAttributes * attributes,
IOStorageCompletion * completion)
{
IOStorageCompletion completionOut;
Context * context;
if (attributes)
{
if ((attributes->options & kIOStorageOptionReserved))
{
complete(completion, kIOReturnBadArgument);
return;
}
#ifdef __LP64__
if (attributes->reserved0032 || attributes->reserved0064 || attributes->reserved0128)
{
complete(completion, kIOReturnBadArgument);
return;
}
#else
if (attributes->reserved0064)
{
complete(completion, kIOReturnBadArgument);
return;
}
#endif
}
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();
if (attributes) context->request.attributes = *attributes;
if (completion) context->original.completion = *completion;
clock_get_uptime(&context->timeStart);
completionOut.target = this;
completionOut.action = prepareRequestCompletion;
completionOut.parameter = context;
#ifdef __LP64__
deblockRequest(byteStart, buffer, attributes, &completionOut, context);
#else
deblockRequest(byteStart, buffer, completionOut, context);
#endif
}
IOReturn
IOBlockStorageDriver::requestIdle(void)
{
return(getProvider()->requestIdle());
}
#ifdef __LP64__
OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 0);
OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 1);
OSMetaClassDefineReservedUnused(IOBlockStorageDriver, 2);
#else
OSMetaClassDefineReservedUsed(IOBlockStorageDriver, 0);
OSMetaClassDefineReservedUsed(IOBlockStorageDriver, 1);
OSMetaClassDefineReservedUsed(IOBlockStorageDriver, 2);
#endif
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);
#ifndef __LP64__
extern "C" void _ZN20IOBlockStorageDriver4readEP9IOServiceyP18IOMemoryDescriptor19IOStorageCompletion( IOBlockStorageDriver * driver, IOService * client, UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion )
{
driver->read( client, byteStart, buffer, NULL, &completion );
}
extern "C" void _ZN20IOBlockStorageDriver5writeEP9IOServiceyP18IOMemoryDescriptor19IOStorageCompletion( IOBlockStorageDriver * driver, IOService * client, UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion )
{
driver->write( client, byteStart, buffer, NULL, &completion );
}
#endif