#include "AppleRAID.h"
#include <IOKit/IOPolledInterface.h>
#include "sys/param.h" // MAXBSIZE
#define super AppleRAIDMember
OSDefineMetaClassAndAbstractStructors(AppleRAIDSet, AppleRAIDMember);
void AppleRAIDSet::free(void)
{
if (arOpenReaders) arOpenReaders->release();
if (arMembers) IODelete(arMembers, AppleRAIDMember *, arLastAllocCount);
if (arSpareMembers) arSpareMembers->release();
if (arStorageRequestPool) {
while (1) {
AppleRAIDStorageRequest * storageRequest;
storageRequest = (AppleRAIDStorageRequest *)arStorageRequestPool->getCommand(false);
if (storageRequest == 0) break;
storageRequest->release();
}
arStorageRequestPool->release();
arStorageRequestPool = 0;
}
if (arSetCommandGate != 0) {
arSetWorkLoop->removeEventSource(arSetCommandGate);
arSetCommandGate->release();
arSetCommandGate = 0;
}
if (arSetEventSource != 0) {
arSetWorkLoop->removeEventSource(arSetEventSource);
arSetEventSource->release();
arSetEventSource = 0;
}
if (arSetWorkLoop != 0) arSetWorkLoop->release();
arSetWorkLoop = 0;
if (arRecoveryThreadCall) thread_call_free(arRecoveryThreadCall);
arRecoveryThreadCall = 0;
super::free();
}
bool AppleRAIDSet::init()
{
if (super::init() == false) return false;
arSetState = kAppleRAIDSetStateInitializing;
setProperty(kAppleRAIDStatusKey, kAppleRAIDStatusOffline);
setProperty(kIOPolledInterfaceSupportKey, kOSBooleanFalse);
arMemberCount = 0;
arLastAllocCount = 0;
arSequenceNumber = 0;
arMedia = NULL;
arOpenLevel = kIOStorageAccessNone;
arOpenReaders = OSSet::withCapacity(10);
arOpenReaderWriters = OSSet::withCapacity(10);
arSetCompleteTimeout = kARSetCompleteTimeoutNone;
arSetBlockCount = 0;
arSetMediaSize = 0;
arMaxReadRequestFactor = 0;
arPrimaryMetaDataUsed = 0;
arPrimaryMetaDataMax = 0;
arMembers = 0;
arSpareMembers = OSSet::withCapacity(10);
arSetIsSyncingCount = 0;
if (!arSpareMembers || !arOpenReaders) return false;
if (getWorkLoop() != 0) {
arSetCommandGate = IOCommandGate::commandGate(this);
if (arSetCommandGate != 0) {
getWorkLoop()->addEventSource(arSetCommandGate);
}
AppleRAIDEventSource::Action completeRequestMethod = OSMemberFunctionCast(AppleRAIDEventSource::Action, this, &AppleRAIDSet::completeRAIDRequest);
arSetEventSource = AppleRAIDEventSource::withAppleRAIDSet(this, completeRequestMethod);
if (arSetEventSource != 0) {
getWorkLoop()->addEventSource(arSetEventSource);
}
}
thread_call_func_t recoverMethod = OSMemberFunctionCast(thread_call_func_t, this, &AppleRAIDSet::recover);
arRecoveryThreadCall = thread_call_allocate(recoverMethod, (thread_call_param_t)this);
if (arRecoveryThreadCall == 0) return false;
arAllocateRequestMethod = (IOCommandGate::Action)0xdeadbeef;
return true;
}
bool AppleRAIDSet::initWithHeader(OSDictionary * header, bool firstTime)
{
if (!header) return false;
OSString * string;
string = OSDynamicCast(OSString, header->getObject(kAppleRAIDSetNameKey));
if (string) setProperty(kAppleRAIDSetNameKey, string);
string = OSDynamicCast(OSString, header->getObject(kAppleRAIDSetUUIDKey));
if (string) {
setProperty(kAppleRAIDMemberUUIDKey, string); setProperty(kAppleRAIDSetUUIDKey, string); } else {
if (firstTime) return false;
}
OSArray * members = OSDynamicCast(OSArray, header->getObject(kAppleRAIDMembersKey));
if (members) setProperty(kAppleRAIDMembersKey, members);
OSNumber * number;
number = OSDynamicCast(OSNumber, header->getObject(kAppleRAIDHeaderVersionKey));
if (number) {
arHeaderVersion = number->unsigned32BitValue();
} else {
if (firstTime) return false;
}
number = OSDynamicCast(OSNumber, header->getObject(kAppleRAIDSequenceNumberKey));
if (number) {
arSequenceNumber = number->unsigned32BitValue();
} else {
if (firstTime) return false;
}
number = OSDynamicCast(OSNumber, header->getObject(kAppleRAIDChunkSizeKey));
if (number) {
arSetBlockSize = number->unsigned64BitValue();
} else {
if (firstTime) return false;
}
number = OSDynamicCast(OSNumber, header->getObject(kAppleRAIDMemberCountKey));
if (number) {
arMemberCount = number->unsigned32BitValue();
} else {
if (firstTime) return false;
}
number = OSDynamicCast(OSNumber, header->getObject(kAppleRAIDBaseOffsetKey));
if (number) {
arBaseOffset = number->unsigned64BitValue();
} else {
if (firstTime) return false;
}
number = OSDynamicCast(OSNumber, header->getObject(kAppleRAIDNativeBlockSizeKey));
if (number) {
arNativeBlockSize = number->unsigned64BitValue();
} else {
if (firstTime) return false;
}
number = OSDynamicCast(OSNumber, header->getObject(kAppleRAIDPrimaryMetaDataUsedKey));
if (number) {
arPrimaryMetaDataUsed = number->unsigned64BitValue();
}
setProperty(kAppleRAIDSetContentHintKey, header->getObject(kAppleRAIDSetContentHintKey));
setProperty(kAppleRAIDCanAddMembersKey, header->getObject(kAppleRAIDCanAddMembersKey));
setProperty(kAppleRAIDCanAddSparesKey, header->getObject(kAppleRAIDCanAddSparesKey));
setProperty(kAppleRAIDRemovalAllowedKey, header->getObject(kAppleRAIDRemovalAllowedKey));
setProperty(kAppleRAIDSizesCanVaryKey, header->getObject(kAppleRAIDSizesCanVaryKey));
return true;
}
bool AppleRAIDSet::addSpare(AppleRAIDMember * member)
{
IOLog1("AppleRAIDSet::addSpare(%p) entered, spare count was %lu.\n", member, getSpareCount());
assert(gAppleRAIDGlobals.islocked());
if (!this->attach(member)) {
IOLog1("AppleRAIDSet::addSpare(%p) this->attach(%p) failed\n", this, member);
member->changeMemberState(kAppleRAIDMemberStateBroken);
return false;
}
arSpareMembers->setObject(member);
return true;
}
bool AppleRAIDSet::addMember(AppleRAIDMember * member)
{
IOLog1("AppleRAIDSet::addMember(%p) called\n", member);
assert(gAppleRAIDGlobals.islocked());
if (member->isBroken()) return false;
if (member->isSpare()) return false;
if (member->getMemberState() != kAppleRAIDMemberStateClosed) {
IOLog1("AppleRAIDSet::addMember(%p) member is not closed.\n", member);
member->changeMemberState(kAppleRAIDMemberStateBroken);
return false;
}
if (arActiveCount >= arMemberCount) {
IOLog("AppleRAIDSet::addMember() too many members, active = %lu, count = %lu, member = %s\n",
arActiveCount, arMemberCount, member->getUUIDString());
member->changeMemberState(kAppleRAIDMemberStateSpare);
return false;
}
if (getSetState() >= kAppleRAIDSetStateOnline && !arSetIsPaused) {
IOLog("AppleRAIDSet::addMember() set already started, ignoring new member %s\n", member->getUUIDString());
member->changeMemberState(kAppleRAIDMemberStateSpare);
return false;
}
OSNumber * number;
number = OSDynamicCast(OSNumber, member->getHeaderProperty(kAppleRAIDHeaderVersionKey));
if (!number) return false;
UInt32 memberHeaderVersion = number->unsigned32BitValue();
OSArray * memberUUIDs = OSDynamicCast(OSArray, getProperty(kAppleRAIDMembersKey));
if (memberUUIDs) {
const OSString * uuid = member->getUUID();
if (!uuid) return false;
bool foundit = false;
for (UInt32 i = 0; i < arMemberCount; i++) {
if (uuid->isEqualTo(memberUUIDs->getObject(i))) {
foundit = arMembers[i] == 0;
}
if (foundit) break;
}
if (!foundit) return false;
}
if (memberHeaderVersion != arHeaderVersion) {
IOLog("AppleRAIDSet::addMember() header version mismatch for member %s\n",
member->getUUIDString());
changeSetState(kAppleRAIDSetStateFailed);
member->changeMemberState(kAppleRAIDMemberStateBroken);
return false;
}
number = OSDynamicCast(OSNumber, member->getHeaderProperty(kAppleRAIDSequenceNumberKey));
if (!number) return false;
UInt32 memberSequenceNumber = number->unsigned32BitValue();
UInt32 memberIndex = member->getMemberIndex();
if (memberSequenceNumber < arSequenceNumber) {
IOLog("AppleRAIDSet::addMember() detected expired sequenceNumber (%lu) for member %s\n",
memberSequenceNumber, member->getUUIDString());
member->changeMemberState(kAppleRAIDMemberStateSpare);
return false;
}
if (memberSequenceNumber > arSequenceNumber) {
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] != 0) {
OSNumber * number = OSDynamicCast(OSNumber, arMembers[cnt]->getHeaderProperty(kAppleRAIDSequenceNumberKey));
if (number) {
IOLog("AppleRAIDSet::addMember() detected expired sequenceNumber (%u) for member %s\n",
number->unsigned32BitValue(), arMembers[cnt]->getUUIDString());
}
AppleRAIDMember * expiredMember = arMembers[cnt];
removeMember(arMembers[cnt], 0);
expiredMember->changeMemberState(kAppleRAIDMemberStateSpare);
addSpare(expiredMember);
}
}
if (arActiveCount == 0) {
arSetState = kAppleRAIDSetStateInitializing;
setProperty(kAppleRAIDStatusKey, kAppleRAIDStatusOffline);
initWithHeader(member->getHeader(), true);
}
arSequenceNumber = memberSequenceNumber;
arSetBlockCount = 0;
assert(arPrimaryMetaDataMax ? (arPrimaryMetaDataMax == member->getPrimaryMaxSize()) : 1);
arPrimaryMetaDataMax = member->getPrimaryMaxSize();
}
if (arMembers[memberIndex] != 0) {
IOLog("AppleRAIDSet::addMember() detected the same member index (%lu) twice?\n", memberIndex);
changeSetState(kAppleRAIDSetStateFailed);
member->changeMemberState(kAppleRAIDMemberStateBroken);
return false;
}
arMembers[memberIndex] = member;
if (!this->attach(member)) {
IOLog1("AppleRAIDSet::addMember(%p) this->attach(%p) failed\n", this, member);
member->changeMemberState(kAppleRAIDMemberStateBroken);
return false;
}
arActiveCount++;
IOLog1("AppleRAIDSet::addMember(%p) was successful.\n", member);
return true;
}
bool AppleRAIDSet::removeMember(AppleRAIDMember * member, IOOptionBits options)
{
IOLog1("AppleRAIDSet::removeMember(%p) called\n", member);
assert(gAppleRAIDGlobals.islocked());
bool shouldBeClosed = member->changeMemberState(kAppleRAIDMemberStateClosing);
if (shouldBeClosed) member->close(this, options);
member->changeMemberState(kAppleRAIDMemberStateClosed);
UInt32 memberIndex = member->getMemberIndex();
if (arMembers[memberIndex] == member) {
arMembers[memberIndex] = 0;
arActiveCount--;
}
arSpareMembers->removeObject(member);
this->detach(member);
return true;
}
bool AppleRAIDSet::upgradeMember(AppleRAIDMember *member)
{
IOLog1("AppleRAIDSet::upgradeMember(%p) entered.\n", this);
assert(arSetIsPaused);
OSArray * memberUUIDs = OSDynamicCast(OSArray, getProperty(kAppleRAIDMembersKey));
OSArray * spareUUIDs = OSDynamicCast(OSArray, getProperty(kAppleRAIDSparesKey));
for (UInt32 i = 0; i < arMemberCount; i++) {
if (arMembers[i]) {
arMembers[i]->setHeaderProperty(kAppleRAIDMembersKey, memberUUIDs);
arMembers[i]->setHeaderProperty(kAppleRAIDSparesKey, spareUUIDs);
}
}
member->setHeaderProperty(kAppleRAIDMemberTypeKey, kAppleRAIDMembersKey);
member->setHeaderProperty(kAppleRAIDSequenceNumberKey, arSequenceNumber, 32);
member->setHeaderProperty(kAppleRAIDMembersKey, memberUUIDs);
member->setHeaderProperty(kAppleRAIDSparesKey, spareUUIDs);
if (!addMember(member)) return false;
if (arOpenReaderWriters->getCount() || arOpenReaders->getCount()) {
IOStorageAccess level = arOpenReaderWriters->getCount() ? kIOStorageAccessReaderWriter : kIOStorageAccessReader;
IOLog1("AppleRAIDSet::upgradeMember(%p) opening for read%s.\n", this, arOpenReaderWriters->getCount() ? "/write" : " only");
if (!member->open(this, 0, level)) {
IOLog("AppleRAIDSet::upgradeMember(%p) open failed.\n", this);
return false;
}
}
return true;
}
bool AppleRAIDSet::resizeSet(UInt32 newMemberCount)
{
AppleRAIDMember **oldMembers = 0;
IOLog1("AppleRAIDSet::resizeSet(%p) entered. alloc = %d old = %d new = %d\n",
this, (int)arLastAllocCount, (int)arMemberCount, (int)newMemberCount);
UInt32 oldMemberCount = arMemberCount;
if (arLastAllocCount && (arLastAllocCount >= newMemberCount)) {
arMemberCount = newMemberCount;
for (UInt32 i = newMemberCount; i < arLastAllocCount; i++) {
arMembers[i] = NULL;
}
return true;
}
if (arLastAllocCount) {
oldMembers = arMembers;
}
arMembers = IONew(AppleRAIDMember *, newMemberCount);
if (!arMembers) return false;
bzero(arMembers, sizeof(AppleRAIDMember *) * newMemberCount);
if (arLastAllocCount) {
bcopy(oldMembers, arMembers, sizeof(AppleRAIDMember *) * oldMemberCount);
IODelete(oldMembers, AppleRAIDMember *, arLastAllocCount);
}
arLastAllocCount = newMemberCount;
arMemberCount = newMemberCount;
IOLog1("AppleRAIDSet::resizeSet(%p) successful\n", this);
return true;
}
UInt32 AppleRAIDSet::nextSetState(void)
{
if (isSetComplete()) {
return kAppleRAIDSetStateOnline;
}
if (arActiveCount == 0 && getSpareCount() == 0) {
IOLog1("AppleRAIDSet::nextSetState: %p is empty, setting state to terminating.\n", this);
return kAppleRAIDSetStateTerminating;
}
if (getSetState() != kAppleRAIDSetStateInitializing) {
IOLog1("AppleRAIDSet::nextSetState: set \"%s\" failed to come online.\n", getSetNameString());
}
return kAppleRAIDSetStateInitializing;
}
UInt64 AppleRAIDSet::getSmallestMaxByteCount(void)
{
UInt64 minimum = MAXBSIZE; UInt64 newMinimum;
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
AppleRAIDMember * target = arMembers[cnt];
if (target) {
newMinimum = 0;
OSNumber * number = OSDynamicCast(OSNumber, target->getProperty(kIOMaximumByteCountReadKey, gIOServicePlane));
if (number) {
newMinimum = number->unsigned64BitValue();
if (newMinimum) minimum = min(minimum, newMinimum);
}
if (!newMinimum) {
OSNumber * number = OSDynamicCast(OSNumber, target->getProperty(kIOMaximumBlockCountReadKey, gIOServicePlane));
if (number) {
newMinimum = number->unsigned64BitValue() * 512;
if (newMinimum) minimum = min(minimum, newMinimum);
}
}
}
}
return minimum;
}
void AppleRAIDSet::setSmallest64BitMemberPropertyFor(char * key, UInt32 multiplier)
{
UInt64 minimum = UINT64_MAX;
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
AppleRAIDMember * target = arMembers[cnt];
if (target) {
OSNumber * number = OSDynamicCast(OSNumber, target->getProperty(key, gIOServicePlane));
if (number) {
UInt64 newMinimum = number->unsigned64BitValue();
if (newMinimum) minimum = min(minimum, newMinimum);
}
}
}
if (minimum < UINT64_MAX) {
setProperty(key, minimum * multiplier, 64);
} else {
removeProperty(key);
}
}
void AppleRAIDSet::setLargest64BitMemberPropertyFor(char * key, UInt32 multiplier)
{
UInt64 maximum = 0;
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
AppleRAIDMember * target = arMembers[cnt];
if (target) {
OSNumber * number = OSDynamicCast(OSNumber, target->getProperty(key, gIOServicePlane));
if (number) {
UInt64 newMaximum = number->unsigned64BitValue();
if (newMaximum) maximum = max(maximum, newMaximum);
}
}
}
if (maximum > 0) {
setProperty(key, maximum * multiplier, 64);
} else {
removeProperty(key);
}
}
bool AppleRAIDSet::startSet(void)
{
IOLog1("AppleRAIDSet::startSet %p called with %lu of %lu members (%lu spares).\n",
this, arActiveCount, arMemberCount, getSpareCount());
if (getSetState() <= kAppleRAIDSetStateTerminating) {
IOLog1("AppleRAIDSet::startSet: the set \"%s\" is terminating or broken (%d).\n", getSetNameString(), (int)getSetState());
return false;
}
UInt32 nextState = nextSetState();
changeSetState(nextState);
if (nextState < kAppleRAIDSetStateOnline) {
IOLog1("AppleRAIDSet::startSet %p was unsuccessful.\n", this);
return false;
}
if (arStorageRequestPool && arSetIsPaused) {
assert(arStorageRequestsPending == 0);
while (1) {
AppleRAIDStorageRequest * storageRequest;
storageRequest = (AppleRAIDStorageRequest *)arStorageRequestPool->getCommand(false);
if (storageRequest == 0) break;
storageRequest->release();
}
arStorageRequestPool->release();
arStorageRequestPool = 0;
}
if (!arStorageRequestPool) {
arStorageRequestPool = IOCommandPool::withWorkLoop(getWorkLoop());
if (arStorageRequestPool == 0) return kIOReturnNoMemory;
for (UInt32 cnt = 0; cnt < kAppleRAIDStorageRequestCount; cnt++) {
AppleRAIDStorageRequest * storageRequest;
if (OSDynamicCast(AppleLVMGroup, this)) {
storageRequest = AppleLVMStorageRequest::withAppleRAIDSet(this);
} else {
storageRequest = AppleRAIDStorageRequest::withAppleRAIDSet(this);
}
if (storageRequest == 0) break;
arStorageRequestPool->returnCommand(storageRequest);
}
}
arIsWritable = true;
arIsEjectable = true;
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] && (arMembers[cnt]->getMemberState() >= kAppleRAIDMemberStateClosed)) {
if (!arMembers[cnt]->isEjectable()) arIsEjectable = false;
if (!arMembers[cnt]->isWritable()) arIsWritable = false;
}
}
setSmallest64BitMemberPropertyFor(kIOMaximumBlockCountReadKey, 1);
setSmallest64BitMemberPropertyFor(kIOMaximumBlockCountWriteKey, 1);
setSmallest64BitMemberPropertyFor(kIOMaximumByteCountReadKey, 1);
setSmallest64BitMemberPropertyFor(kIOMaximumByteCountWriteKey, 1);
setSmallest64BitMemberPropertyFor(kIOMaximumSegmentCountReadKey, 1);
setSmallest64BitMemberPropertyFor(kIOMaximumSegmentCountWriteKey, 1);
setSmallest64BitMemberPropertyFor(kIOMaximumSegmentByteCountReadKey, 1); setSmallest64BitMemberPropertyFor(kIOMaximumSegmentByteCountWriteKey, 1);
setLargest64BitMemberPropertyFor(kIOMinimumSegmentAlignmentByteCountKey, 1); setSmallest64BitMemberPropertyFor(kIOMaximumSegmentAddressableBitCountKey, 1);
IOLog1("AppleRAIDSet::startSet %p was successful.\n", this);
return true;
}
bool AppleRAIDSet::publishSet(void)
{
IOLog1("AppleRAIDSet::publishSet called %p\n", this);
if (arActiveCount == 0 && getSpareCount() == 0) {
IOLog1("AppleRAIDSet::publishSet: the set %p is empty, aborting.\n", this);
return false;
}
if (getSetState() < kAppleRAIDSetStateOnline || isRAIDMember()) {
IOLog1("AppleRAIDSet::publishSet: skipping offline or stacked raid set.\n");
unpublishSet();
return true;
}
const char * contentHint = 0;
OSString * theHint = OSDynamicCast(OSString, getProperty(kAppleRAIDSetContentHintKey));
if (theHint) {
if (theHint->isEqualTo(kAppleRAIDNoMediaExport)) {
IOLog1("AppleRAIDSet::publishSet: shortcircuiting publish for no media set.\n");
return true;
}
contentHint = theHint->getCStringNoCopy();
}
bool firstTime = false;
if (arMedia) {
arMedia->retain();
} else {
arMedia = new IOMedia;
firstTime = true;
}
if (arMedia) {
IOMediaAttributeMask attributes = arIsEjectable ? (kIOMediaAttributeEjectableMask | kIOMediaAttributeRemovableMask) : 0;
if (arMedia->init( 0,
arSetMediaSize,
arNativeBlockSize,
attributes,
true,
arIsWritable,
contentHint)) {
arMedia->setName(getSetNameString());
char location[12];
snprintf(location, sizeof(location), "%d", 0);
arMedia->setLocation(location);
OSArray * bootArray = OSArray::withCapacity(arMemberCount);
if (bootArray) {
(void)addBootDeviceInfo(bootArray);
arMedia->setProperty(kIOBootDeviceKey, bootArray);
bootArray->release();
}
arMedia->setProperty(kIOMediaUUIDKey, (OSObject *)getUUID());
arMedia->setProperty(kAppleRAIDIsRAIDKey, kOSBooleanTrue);
if (getSetState() < kAppleRAIDSetStateOnline || isRAIDMember()) {
arMedia->setProperty("autodiskmount", kOSBooleanFalse);
} else {
arMedia->removeProperty("autodiskmount");
}
if (firstTime) {
arMedia->attach(this);
arMedia->registerService();
} else {
arMedia->messageClients(kIOMessageServicePropertyChange);
}
}
} else {
IOLog("AppleRAIDSet::publishSet(void): failed for set \"%s\" (%s)\n", getSetNameString(), getUUIDString());
}
if (arMedia) arMedia->release();
IOLog1("AppleRAIDSet::publishSet: was %ssuccessful.\n", arMedia ? "" : "un");
return arMedia != NULL;
}
bool AppleRAIDSet::unpublishSet(void)
{
bool success = true;
IOLog1("AppleRAIDSet::unpublishSet(%p) entered, arMedia = %p\n", this, arMedia);
if (arMedia) {
success = arMedia->terminate(kIOServiceRequired | kIOServiceSynchronous);
arMedia = 0;
}
return success;
}
bool AppleRAIDSet::destroySet(void)
{
IOLog1("AppleRAIDSet::destroySet(%p) entered.\n", this);
assert(gAppleRAIDGlobals.islocked());
if (isRAIDMember()) {
IOLog("AppleRAIDSet::destroySet() failed, an attempt was made to destroy subordinate set\n");
return false;
}
for (UInt32 i = 0; i < arMemberCount; i++) {
if (arMembers[i]) {
(void)arMembers[i]->zeroRAIDHeader();
if (arMembers[i]->getMemberState() == kAppleRAIDMemberStateRebuilding) {
arMembers[i]->changeMemberState(kAppleRAIDMemberStateSpare, true);
while (arMembers[i]->getMemberState() == kAppleRAIDMemberStateSpare) {
IOSleep(50);
}
AppleRAIDMember * member = arMembers[i];
arMembers[i] = 0;
arSpareMembers->setObject(member);
}
}
}
OSCollectionIterator * iter = OSCollectionIterator::withCollection(arSpareMembers);
if (!iter) return false;
while (AppleRAIDMember * spare = (AppleRAIDMember *)iter->getNextObject()) {
(void)spare->zeroRAIDHeader();
}
iter->release();
changeSetState(kAppleRAIDSetStateTerminating);
for (UInt32 i = 0; i < arMemberCount; i++) {
if (arMembers[i]) {
if (arMembers[i]->isRAIDSet()) {
arController->oldMember(arMembers[i]);
} else {
arMembers[i]->stop(NULL);
}
}
}
OSSet * copy = OSSet::withSet(arSpareMembers, arSpareMembers->getCount());
if (!copy) return false;
while (AppleRAIDMember * spare = (AppleRAIDMember *)copy->getAnyObject()) {
copy->removeObject(spare);
if (spare->isRAIDSet()) {
arController->oldMember(spare);
} else {
spare->stop(NULL);
}
}
copy->release();
IOLog1("AppleRAIDSet::destroySet(%p) was successful.\n", this);
return true;
}
bool AppleRAIDSet::reconfigureSet(OSDictionary * updateInfo)
{
bool updateHeader = false;
UInt32 newMemberCount = arMemberCount;
IOLog1("AppleRAIDSet::reconfigureSet(%p) entered.\n", this);
OSString * deleted = OSString::withCString(kAppleRAIDDeletedUUID);
if (!deleted) return false;
OSArray * oldMemberList = OSDynamicCast(OSArray, getProperty(kAppleRAIDMembersKey));
OSArray * newMemberList = OSDynamicCast(OSArray, updateInfo->getObject(kAppleRAIDMembersKey));
if (oldMemberList && newMemberList) {
IOLog1("AppleRAIDSet::reconfigureSet(%p) updating member list.\n", this);
assert(arMemberCount == oldMemberList->getCount());
for (UInt32 i = 0; i < newMemberCount; i++) {
OSString * uuid = OSDynamicCast(OSString, newMemberList->getObject(i));
if (uuid && (uuid->isEqualTo(deleted))) {
if (arMembers[i]) {
arMembers[i]->zeroRAIDHeader();
if (arMembers[i]->getMemberState() == kAppleRAIDMemberStateRebuilding) {
arMembers[i]->changeMemberState(kAppleRAIDMemberStateSpare, true);
while (arMembers[i]->getMemberState() == kAppleRAIDMemberStateSpare) {
arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &AppleRAIDSet::unpauseSet));
IOSleep(50);
arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &AppleRAIDSet::pauseSet), (void *)false);
}
AppleRAIDMember * member = arMembers[i];
arMembers[i] = 0;
arSpareMembers->setObject(member);
if (member->isRAIDSet()) {
arController->oldMember(member);
} else {
member->stop(NULL);
}
} else {
if (arMembers[i]->isRAIDSet()) {
arController->oldMember(arMembers[i]);
} else {
arMembers[i]->stop(NULL);
}
}
} else {
OSString * olduuid = OSDynamicCast(OSString, oldMemberList->getObject(i));
OSCollectionIterator * iter = OSCollectionIterator::withCollection(arSpareMembers);
if (!iter) return false;
while (AppleRAIDMember * spare = (AppleRAIDMember *)iter->getNextObject()) {
if (spare->getUUID()->isEqualTo(olduuid)) {
spare->zeroRAIDHeader();
if (spare->isRAIDSet()) {
arController->oldMember(spare);
} else {
spare->stop(NULL);
}
break;
}
}
iter->release();
}
newMemberCount--;
newMemberList->removeObject(i);
for (UInt32 j = i; j < newMemberCount; j++) {
arMembers[j] = arMembers[j + 1];
if (arMembers[j]) arMembers[j]->setMemberIndex(j);
}
break; }
}
newMemberCount = newMemberList->getCount();
setProperty(kAppleRAIDMembersKey, newMemberList);
updateInfo->removeObject(kAppleRAIDMembersKey);
updateHeader = true;
}
OSArray * oldSpareList = OSDynamicCast(OSArray, getProperty(kAppleRAIDSparesKey));
OSArray * newSpareList = OSDynamicCast(OSArray, updateInfo->getObject(kAppleRAIDSparesKey));
if (oldSpareList && newSpareList) {
IOLog1("AppleRAIDSet::reconfigureSet(%p) updating spare list.\n", this);
UInt32 spareCount = newSpareList->getCount();
for (UInt32 i = 0; i < spareCount; i++) {
OSString * uuid = OSDynamicCast(OSString, newSpareList->getObject(i));
if (!uuid || !uuid->isEqualTo(deleted)) continue;
newSpareList->removeObject(i);
OSString * olduuid = OSDynamicCast(OSString, oldSpareList->getObject(i));
if (!olduuid) return false;
OSCollectionIterator * iter = OSCollectionIterator::withCollection(arSpareMembers);
if (!iter) return false;
while (AppleRAIDMember * spare = (AppleRAIDMember *)iter->getNextObject()) {
if (spare->getUUID()->isEqualTo(olduuid)) {
spare->zeroRAIDHeader();
if (spare->isRAIDSet()) {
arController->oldMember(spare);
} else {
spare->stop(NULL);
}
break;
}
}
iter->release();
break; }
setProperty(kAppleRAIDSparesKey, newSpareList);
updateInfo->removeObject(kAppleRAIDSparesKey);
updateHeader = true;
}
deleted->release();
if (updateInfo->getCount()) {
initWithHeader(updateInfo, false);
updateHeader = true;
}
if (newMemberCount != arMemberCount) {
resizeSet(newMemberCount);
OSNumber * number = OSNumber::withNumber(newMemberCount, 32);
if (number) {
updateInfo->setObject(kAppleRAIDMemberCountKey, number);
number->release();
}
}
if (updateHeader) {
changeSetState(kAppleRAIDSetStateInitializing);
OSArray * memberUUIDs = OSDynamicCast(OSArray, getProperty(kAppleRAIDMembersKey));
OSArray * spareUUIDs = OSDynamicCast(OSArray, getProperty(kAppleRAIDSparesKey));
for (UInt32 i = 0; i < arMemberCount; i++) {
if (arMembers[i]) {
arMembers[i]->updateRAIDHeader(updateInfo);
arMembers[i]->setHeaderProperty(kAppleRAIDMembersKey, memberUUIDs);
arMembers[i]->setHeaderProperty(kAppleRAIDSparesKey, spareUUIDs);
}
}
}
return true;
}
UInt32 AppleRAIDSet::getSequenceNumber()
{
return arSequenceNumber;
}
void AppleRAIDSet::bumpSequenceNumber(void)
{
UInt32 cnt;
assert(gAppleRAIDGlobals.islocked());
arSequenceNumber++;
IOLog1("AppleRAIDSet::bumpSequenceNumber(%p) bumping to %lu\n", this, arSequenceNumber);
for (cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt]) {
arMembers[cnt]->setHeaderProperty(kAppleRAIDSequenceNumberKey, arSequenceNumber, 32);
}
}
}
IOReturn AppleRAIDSet::writeRAIDHeader(void)
{
UInt32 cnt;
IOReturn rc = kIOReturnSuccess, rc2;
IOLog1("AppleRAIDSet::writeRAIDHeader(%p) entered.\n", this);
assert(gAppleRAIDGlobals.islocked());
if ((arActiveCount == 0) || getSetState() <= kAppleRAIDSetStateTerminating) {
IOLog1("AppleRAIDSet::writeRAIDHeader(%p) ignoring request, the set is empty or broken/terminating.\n", this);
return rc;
}
UInt32 formerSetState = getSetState();
bool openedForWrite = arOpenReaderWriters->getCount() != 0;
bool openedForRead = arOpenReaders->getCount() != 0;
if (!openedForWrite) {
IOLog1("AppleRAIDSet::writeRAIDHeader(%p): opening set for writing.\n", this);
if (!open(this, 0, kIOStorageAccessReaderWriter)) return kIOReturnIOError;
}
for (cnt = 0; cnt < arMemberCount; cnt++) {
if (!arMembers[cnt] || (arMembers[cnt]->getMemberState() < kAppleRAIDMemberStateOpen)) continue;
if ((rc2 = arMembers[cnt]->writeRAIDHeader()) != kIOReturnSuccess) {
IOLog("AppleRAIDSet::writeRAIDHeader() update failed on set \"%s\" (%s) member %s, rc = %x\n",
getSetNameString(), getUUIDString(), arMembers[cnt]->getUUIDString(), rc2);
rc = rc2;
}
}
if (!openedForWrite) {
if (!openedForRead) {
IOLog1("AppleRAIDSet::writeRAIDHeader(%p): closing set.\n", this);
close(this, 0);
} else {
IOLog1("AppleRAIDSet::writeRAIDHeader(%p): downgrading set to read only.\n", this);
if (!open(this, 0, kIOStorageAccessReader)) { IOLog1("AppleRAIDSet::writeRAIDHeader(%p): downgrade back to RO failed.\n", this);
changeSetState(kAppleRAIDSetStateFailed);
return kIOReturnError;
}
}
changeSetState(formerSetState);
}
IOLog1("AppleRAIDSet::writeRAIDHeader(%p) exiting with 0x%x.\n", this, rc);
return rc;
}
IOBufferMemoryDescriptor * AppleRAIDSet::readPrimaryMetaData(AppleRAIDMember * member)
{
if (!member) return NULL;
return member->readPrimaryMetaData();
}
IOReturn AppleRAIDSet::writePrimaryMetaData(IOBufferMemoryDescriptor * primaryBuffer)
{
UInt32 cnt;
IOReturn rc = kIOReturnSuccess, rc2;
IOLog1("AppleRAIDSet::writePrimaryMetaData(%p) entered.\n", this);
if ((arActiveCount == 0) || getSetState() <= kAppleRAIDSetStateTerminating) {
IOLog1("AppleRAIDSet::writePrimaryMetaData(%p) ignoring request, the set is empty or broken/terminating.\n", this);
return rc;
}
UInt32 formerSetState = getSetState();
bool openedForWrite = arOpenReaderWriters->getCount() != 0;
bool openedForRead = arOpenReaders->getCount() != 0;
if (!openedForWrite) {
IOLog1("AppleRAIDSet::writePrimaryMetaData(%p): opening set for writing.\n", this);
if (!open(this, 0, kIOStorageAccessReaderWriter)) return kIOReturnIOError;
}
for (cnt = 0; cnt < arMemberCount; cnt++) {
if (!arMembers[cnt] || (arMembers[cnt]->getMemberState() < kAppleRAIDMemberStateOpen)) continue;
if ((rc2 = arMembers[cnt]->writePrimaryMetaData(primaryBuffer)) != kIOReturnSuccess) {
IOLog("AppleRAIDSet::writePrimaryMetaData() update failed on set \"%s\" (%s) member %s, rc = %x\n",
getSetNameString(), getUUIDString(), arMembers[cnt]->getUUIDString(), rc2);
rc = rc2;
}
}
if (!openedForWrite) {
if (!openedForRead) {
IOLog1("AppleRAIDSet::writePrimaryMetaData(%p): closing set.\n", this);
close(this, 0);
} else {
IOLog1("AppleRAIDSet::writePrimaryMetaData(%p): downgrading set to read only.\n", this);
if (!open(this, 0, kIOStorageAccessReader)) { IOLog1("AppleRAIDSet::writePrimaryMetaData(%p): downgrade back to RO failed.\n", this);
changeSetState(kAppleRAIDSetStateFailed);
return kIOReturnError;
}
}
changeSetState(formerSetState);
}
IOLog1("AppleRAIDSet::writePrimaryMetaData(%p) exiting with 0x%x.\n", this, rc);
return rc;
}
bool AppleRAIDSet::readIntoBuffer(AppleRAIDMember * member, IOBufferMemoryDescriptor * buffer, UInt64 offset)
{
assert(buffer);
assert(member);
bool openedForRead = isOpen();
if (!openedForRead) {
if (!getTarget()->open(this, 0, kIOStorageAccessReader)) return false;
}
buffer->setDirection(kIODirectionIn);
IOReturn rc = member->getTarget()->read(this, offset, buffer);
if (!openedForRead) {
getTarget()->close(this, 0);
}
return rc == kIOReturnSuccess;
}
IOReturn AppleRAIDSet::writeFromBuffer(AppleRAIDMember * member, IOBufferMemoryDescriptor * buffer, UInt64 offset)
{
IOReturn rc = kIOReturnSuccess;
IOLog1("AppleRAIDSet::writeFromBuffer(%p) entered.\n", this);
if ((arActiveCount == 0) || getSetState() <= kAppleRAIDSetStateTerminating) {
IOLog1("AppleRAIDSet::writeFromBuffer(%p) ignoring request, the set is empty or broken/terminating.\n", this);
return rc;
}
UInt32 formerSetState = getSetState();
bool openedForWrite = arOpenReaderWriters->getCount() != 0;
bool openedForRead = arOpenReaders->getCount() != 0;
if (!openedForWrite) {
IOLog1("AppleRAIDSet::writeFromBuffer(%p): opening set for writing.\n", this);
if (!open(this, 0, kIOStorageAccessReaderWriter)) return kIOReturnIOError;
}
buffer->setDirection(kIODirectionOut);
rc = member->getTarget()->write(this, offset, buffer);
if (!openedForWrite) {
if (!openedForRead) {
IOLog1("AppleRAIDSet::writeFromBuffer(%p): closing set.\n", this);
close(this, 0);
} else {
IOLog1("AppleRAIDSet::writeFromBuffer(%p): downgrading set to read only.\n", this);
if (!open(this, 0, kIOStorageAccessReader)) { IOLog1("AppleRAIDSet::writeFromBuffer(%p): downgrade back to RO failed.\n", this);
changeSetState(kAppleRAIDSetStateFailed);
return kIOReturnError;
}
}
changeSetState(formerSetState);
}
IOLog1("AppleRAIDSet::writeFromBuffer(%p) exiting with 0x%x.\n", this, rc);
return rc;
}
const OSString * AppleRAIDSet::getSetName(void)
{
return OSDynamicCast(OSString, getProperty(kAppleRAIDSetNameKey));
}
const OSString * AppleRAIDSet::getUUID(void)
{
return OSDynamicCast(OSString, getProperty(kAppleRAIDMemberUUIDKey));
}
const OSString * AppleRAIDSet::getSetUUID(void)
{
return OSDynamicCast(OSString, getProperty(kAppleRAIDSetUUIDKey));
}
const OSString * AppleRAIDSet::getDiskName(void)
{
return arMedia ? OSDynamicCast(OSString, arMedia->getProperty(kIOBSDNameKey)) : NULL;
}
IOStorage * AppleRAIDSet::getTarget(void) const
{
return (IOStorage *)this;
}
bool AppleRAIDSet::isRAIDSet(void)
{
return true;
}
bool AppleRAIDSet::isSetComplete(void)
{
return arActiveCount == arMemberCount;
}
bool AppleRAIDSet::bumpOnError(void)
{
return false;
}
UInt64 AppleRAIDSet::getSize() const
{
return arSetMediaSize;
}
IOWorkLoop * AppleRAIDSet::getWorkLoop(void)
{
if (arSetWorkLoop == 0) {
arSetWorkLoop = IOWorkLoop::workLoop();
}
return arSetWorkLoop;
}
bool AppleRAIDSet::changeSetState(UInt32 newState)
{
bool swapState = false;
char *newStatus = "bogus";
#ifdef DEBUG
const char *oldStatus = "not set";
OSString *oldStatusString = OSDynamicCast(OSString, getProperty(kAppleRAIDStatusKey));
if (oldStatusString) oldStatus = oldStatusString->getCStringNoCopy();
#endif
if (arSetState == newState) return true;
switch (newState) {
case kAppleRAIDSetStateFailed: swapState = true;
newStatus = kAppleRAIDStatusFailed;
break;
case kAppleRAIDSetStateTerminating: swapState = arSetState > kAppleRAIDSetStateFailed;
newStatus = kAppleRAIDStatusOffline;
break;
case kAppleRAIDSetStateInitializing: swapState = arSetState > kAppleRAIDSetStateTerminating;
newStatus = kAppleRAIDStatusOffline;
break;
case kAppleRAIDSetStateOnline: swapState = arSetState >= kAppleRAIDSetStateInitializing;
newStatus = kAppleRAIDStatusOnline;
break;
case kAppleRAIDSetStateDegraded: swapState = arSetState >= kAppleRAIDSetStateInitializing;
newStatus = kAppleRAIDStatusDegraded;
break;
default:
IOLog("AppleRAIDSet::changeSetState() this \"%s\" (%s), bogus state %lu?\n",
getSetNameString(), getUUIDString(), newState);
}
if (swapState) {
IOLog1("AppleRAIDSet::changeSetState(%p) from %lu (%s) to %lu (%s).\n",
this, arSetState, oldStatus, newState, newStatus);
if (isRAIDMember()) {
if ((newState >= kAppleRAIDSetStateOnline) && (newState > arSetState)) {
if (getMemberState() >= kAppleRAIDMemberStateClosing) {
changeMemberState(kAppleRAIDMemberStateOpen);
} else {
changeMemberState(kAppleRAIDMemberStateClosed);
}
}
if ((newState < kAppleRAIDSetStateOnline) && (newState < arSetState)) {
changeMemberState(kAppleRAIDMemberStateClosing);
}
}
arSetState = newState;
setProperty(kAppleRAIDStatusKey, newStatus);
messageClients(kAppleRAIDMessageSetChanged);
} else {
IOLog1("AppleRAIDSet::changeSetState(%p) FAILED from %lu (%s) to %lu (%s).\n",
this, arSetState, oldStatus, newState, newStatus);
}
return swapState;
}
bool
AppleRAIDSet::addBootDeviceInfo(OSArray * bootArray)
{
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] != NULL) {
if ((arMembers[cnt]->getMemberState() >= kAppleRAIDMemberStateClosed) &&
(arMembers[cnt]->getMemberState() != kAppleRAIDMemberStateRebuilding)) {
if (!arMembers[cnt]->addBootDeviceInfo(bootArray)) {
bootArray->flushCollection();
return false;
}
}
}
}
return true;
}
OSDictionary * AppleRAIDSet::getSetProperties(void)
{
OSNumber * tmpNumber;
OSDictionary * props = OSDictionary::withCapacity(32);
if (!props) return NULL;
props->setObject(kAppleRAIDSetNameKey, getSetName());
props->setObject(kAppleRAIDSetUUIDKey, getUUID());
props->setObject(kAppleRAIDLevelNameKey, getProperty(kAppleRAIDLevelNameKey));
tmpNumber = OSNumber::withNumber(arHeaderVersion, 32);
if (tmpNumber) {
props->setObject(kAppleRAIDHeaderVersionKey, tmpNumber);
tmpNumber->release();
}
tmpNumber = OSNumber::withNumber(arSequenceNumber, 32);
if (tmpNumber) {
props->setObject(kAppleRAIDSequenceNumberKey, tmpNumber);
tmpNumber->release();
}
tmpNumber = OSNumber::withNumber(arSetBlockSize, 64);
if (tmpNumber) {
props->setObject(kAppleRAIDChunkSizeKey, tmpNumber);
tmpNumber->release();
}
tmpNumber = OSNumber::withNumber(arSetBlockCount, 64);
if (tmpNumber) {
props->setObject(kAppleRAIDChunkCountKey, tmpNumber);
tmpNumber->release();
}
if (arPrimaryMetaDataUsed) {
tmpNumber = OSNumber::withNumber(arPrimaryMetaDataUsed, 64);
if (tmpNumber){
props->setObject(kAppleRAIDPrimaryMetaDataUsedKey, tmpNumber);
tmpNumber->release();
}
}
props->setObject(kAppleRAIDSetContentHintKey, getProperty(kAppleRAIDSetContentHintKey));
props->setObject(kAppleRAIDCanAddMembersKey, getProperty(kAppleRAIDCanAddMembersKey));
props->setObject(kAppleRAIDCanAddSparesKey, getProperty(kAppleRAIDCanAddSparesKey));
props->setObject(kAppleRAIDRemovalAllowedKey, getProperty(kAppleRAIDRemovalAllowedKey));
props->setObject(kAppleRAIDSizesCanVaryKey, getProperty(kAppleRAIDSizesCanVaryKey));
props->setObject(kAppleRAIDStatusKey, getProperty(kAppleRAIDStatusKey));
props->setObject(kIOBSDNameKey, getDiskName());
OSArray * members = OSDynamicCast(OSArray, getProperty(kAppleRAIDMembersKey));
if (members) {
props->setObject(kAppleRAIDMembersKey, members);
} else {
members = OSArray::withCapacity(arMemberCount);
if (members) {
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] != 0) {
const OSString * uuid = arMembers[cnt]->getUUID();
if (uuid) members->setObject(uuid);
} else {
OSString * uuid = OSString::withCString(kAppleRAIDMissingUUID);
if (uuid) {
members->setObject(uuid);
uuid->release();
}
}
}
props->setObject(kAppleRAIDMembersKey, members);
members->release();
}
}
OSArray * spares = OSArray::withCapacity(arMemberCount);
OSCollectionIterator * iter = OSCollectionIterator::withCollection(arSpareMembers);
if (spares && iter) {
while (AppleRAIDMember * spare = (AppleRAIDMember *)iter->getNextObject()) {
const OSString * uuid = spare->getUUID();
assert(uuid);
if (!uuid) continue;
OSArray * members = (OSArray *)props->getObject(kAppleRAIDMembersKey);
UInt32 memberCount = members ? members->getCount() : 0;
bool foundIt = false;
for (UInt32 cnt2 = 0; cnt2 < memberCount; cnt2++) {
foundIt = members->getObject(cnt2)->isEqualTo(uuid);
if (foundIt) break;
}
if (foundIt) continue;
spares->setObject(uuid);
}
props->setObject(kAppleRAIDSparesKey, spares);
setProperty(kAppleRAIDSparesKey, spares); spares->release();
iter->release();
}
return props;
}
bool AppleRAIDSet::handleOpen(IOService * client,
IOOptionBits options,
void * argument)
{
IOStorageAccess access = (IOStorageAccess) argument;
IOStorageAccess level;
IOLogOC("AppleRAIDSet::handleOpen(%p) called, client %p, access %lu, state %lu, client is a set = %s, raid member = %s.\n",
this, client, access, arSetState, OSDynamicCast(AppleRAIDSet, client) ? "y" : "n", isRAIDMember() ? "y" : "n");
assert(client);
assert( access == kIOStorageAccessReader ||
access == kIOStorageAccessReaderWriter );
access &= kIOStorageAccessReaderWriter;
if (!OSDynamicCast(AppleRAIDSet, client) && getSetState() < kAppleRAIDSetStateOnline) {
IOLogOC("AppleRAIDSet::handleOpen(%p) open refused (set is not online (published)).\n", this);
return false;
}
if (!OSDynamicCast(AppleRAIDSet, client) && isRAIDMember()) {
IOLogOC("AppleRAIDSet::handleOpen(%p) open refused (set is stacked).\n", this);
return false;
}
assert(arSetState >= kAppleRAIDSetStateOnline);
unsigned writers = arOpenReaderWriters->getCount();
#ifdef XXX
if (writers >= arOpenReaderWriterMax)
{
IOLogOC("AppleRAIDSet::handleOpen(%p) client %p access %lu arOpenReaderWriter already set %p\n",
this, client, access, arOpenReaderWriter);
return false;
}
#endif
if (arOpenReaderWriters->containsObject(client)) writers--;
if (access == kIOStorageAccessReaderWriter) writers++;
level = (writers) ? kIOStorageAccessReaderWriter : kIOStorageAccessReader;
if (arOpenLevel != level)
{
bool success = false;
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] != 0) {
IOLogOC("AppleRAIDSet::handleOpen(%p) opening %p member=%lu access=%lu level=%lu\n",
this, arMembers[cnt], cnt, access, level);
if (arMembers[cnt]->getMemberState() == kAppleRAIDMemberStateRebuilding) continue;
success = arMembers[cnt]->open(this, options, level);
if (!success) {
IOLog("AppleRAIDSet::handleOpen(%p) client %p member %s failed to open for set \"%s\" (%s).\n",
this, client, arMembers[cnt]->getUUIDString(), getSetNameString(), getUUIDString());
IOLogOC("AppleRAIDSet::handleOpen() open failed on member %lu of %lu (active = %lu), state = %lu isOpen = %s",
cnt, arMemberCount, arActiveCount, arSetState, arMembers[cnt]->isOpen(NULL) ? "t" : "f");
for (UInt32 cnt2 = 0; cnt2 < cnt; cnt2++) {
if (arMembers[cnt2] != 0) {
if (arMembers[cnt]->getMemberState() == kAppleRAIDMemberStateRebuilding) continue;
arMembers[cnt2]->close(this, 0);
}
}
return false;
}
}
}
level = (level & kIOStorageAccessReaderWriter);
}
if (access == kIOStorageAccessReader)
{
arOpenReaders->setObject(client);
arOpenReaderWriters->removeObject(client); }
else {
arOpenReaderWriters->setObject(client);
arOpenReaders->removeObject(client); }
arOpenLevel = level;
changeMemberState(kAppleRAIDMemberStateOpen);
IOLogOC("AppleRAIDSet::handleOpen(%p) successful, client %p, access %lu, state %lu\n", this, client, access, arSetState);
return true;
}
bool AppleRAIDSet::handleIsOpen(const IOService * client) const
{
if (client == 0) return (arOpenLevel != kIOStorageAccessNone);
bool open = arOpenReaderWriters->containsObject(client) || arOpenReaders->containsObject(client);
IOLogOC("AppleRAIDSet::handleIsOpen(%p) client %p is %s\n", this, client, open ? "true" : "false");
return open;
}
void AppleRAIDSet::handleClose(IOService * client, IOOptionBits options)
{
IOLogOC("AppleRAIDSet::handleClose(%p) called, client %p current state %lu\n", this, client, arSetState);
assert(client);
if (arOpenReaderWriters->containsObject(client)) {
arOpenReaderWriters->removeObject(client);
}
else if (arOpenReaders->containsObject(client)) {
arOpenReaders->removeObject(client);
}
else {
assert(0);
return;
}
IOStorageAccess level;
if (arOpenReaderWriters->getCount()) level = kIOStorageAccessReaderWriter;
else if (arOpenReaders->getCount()) level = kIOStorageAccessReader;
else level = kIOStorageAccessNone;
if (level == kIOStorageAccessNone) {
changeMemberState(kAppleRAIDMemberStateClosing); }
if (arOpenLevel != level) {
assert(level != kIOStorageAccessReaderWriter);
if (level == kIOStorageAccessNone) {
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] != 0) {
if (arMembers[cnt]->getMemberState() == kAppleRAIDMemberStateRebuilding) continue;
arMembers[cnt]->close(this, options);
}
}
} else {
bool success;
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] != 0) {
if (arMembers[cnt]->getMemberState() == kAppleRAIDMemberStateRebuilding) continue;
success = arMembers[cnt]->open(this, 0, level);
assert(success); }
}
level = (level & kIOStorageAccessReaderWriter); }
arOpenLevel = level;
}
if (level == kIOStorageAccessNone) {
changeMemberState(kAppleRAIDMemberStateClosed); }
}
void AppleRAIDSet::read(IOService *client, UInt64 byteStart,
IOMemoryDescriptor *buffer, IOStorageCompletion completion)
{
AppleRAIDStorageRequest * storageRequest;
IOLogRW("AppleRAIDSet::read(%p, %llu, 0x%lx) this %p, state %lu\n", client, byteStart, buffer ? buffer->getLength() : 0, this, arSetState);
arSetCommandGate->runAction(arAllocateRequestMethod, &storageRequest);
if (storageRequest != 0) {
buffer->retain();
storageRequest->read(client, byteStart, buffer, completion);
} else {
IOLogRW("AppleRAIDSet::read(%p, 0x%llx) could not allocate a storage request\n", client, byteStart);
IOStorage::complete(completion, kIOReturnNoMedia, 0);
}
}
void AppleRAIDSet::write(IOService *client, UInt64 byteStart,
IOMemoryDescriptor *buffer, IOStorageCompletion completion)
{
AppleRAIDStorageRequest * storageRequest;
IOLogRW("AppleRAIDSet::write(%p, %llu, 0x%lx) this %p, state %lu\n", client, byteStart, buffer ? buffer->getLength() : 0, this, arSetState);
arSetCommandGate->runAction(arAllocateRequestMethod, &storageRequest);
if (storageRequest != 0) {
buffer->retain();
storageRequest->write(client, byteStart, buffer, completion);
} else {
IOLogRW("AppleRAIDSet::write(%p, 0x%llx) could not allocate a storage request\n", client, byteStart);
IOStorage::complete(completion, kIOReturnNoMedia, 0);
}
}
void AppleRAIDSet::activeReadMembers(AppleRAIDMember ** activeMembers, UInt64 byteStart, UInt32 byteCount)
{
for (UInt32 index = 0; index < arMemberCount; index++) {
AppleRAIDMember * member = arMembers[index];
if (member && member->getMemberState() >= kAppleRAIDMemberStateClosing) {
activeMembers[index] = arMembers[index];
} else {
activeMembers[index] = (AppleRAIDMember *)index;
}
}
}
void AppleRAIDSet::activeWriteMembers(AppleRAIDMember ** activeMembers, UInt64 byteStart, UInt32 byteCount)
{
for (UInt32 index = 0; index < arMemberCount; index++) {
AppleRAIDMember * member = arMembers[index];
if (member && member->getMemberState() >= kAppleRAIDMemberStateClosing) {
activeMembers[index] = arMembers[index];
} else {
activeMembers[index] = (AppleRAIDMember *)index;
}
}
}
IOReturn AppleRAIDSet::synchronizeCache(IOService *client)
{
if (OSDynamicCast(AppleRAIDSet, client)) return synchronizeCacheGated(client);
IOCommandGate::Action syncCacheMethod = OSMemberFunctionCast(IOCommandGate::Action, this, &AppleRAIDSet::synchronizeCacheGated);
return arSetCommandGate->runAction(syncCacheMethod, (void *)client);
}
IOReturn AppleRAIDSet::synchronizeCacheGated(IOService *client)
{
AppleRAIDSet * masterSet = OSDynamicCast(AppleRAIDSet, client);
if (masterSet == NULL) {
while (arSetIsSyncingCount > 0) {
IOLog1("AppleRAIDSet::requestSynchronizeCache(%p) stalled count=%ld \n", client, arSetIsSyncingCount);
arSetCommandGate->commandSleep(&arSetIsSyncingCount, THREAD_UNINT);
}
arSetIsSyncingCount++; }
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (!arMembers[cnt] || (arMembers[cnt]->getMemberState() < kAppleRAIDMemberStateRebuilding)) continue;
arMembers[cnt]->synchronizeCache(masterSet ? masterSet : this);
}
if (masterSet == NULL) {
while (arSetIsSyncingCount > 1) {
arSetCommandGate->commandSleep(&arSetIsSyncingCount, THREAD_UNINT);
}
arSetIsSyncingCount--;
assert(arSetIsSyncingCount == 0);
arSetCommandGate->commandWakeup(&arSetIsSyncingCount, false);
}
return 0;
}
void AppleRAIDSet::synchronizeStarted(void)
{
arSetIsSyncingCount++;
}
void AppleRAIDSet::synchronizeCompleted(void)
{
arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &AppleRAIDSet::synchronizeCompletedGated));
}
void AppleRAIDSet::synchronizeCompletedGated(void)
{
arSetIsSyncingCount--;
if (arSetIsSyncingCount <= 1) {
assert(arSetIsSyncingCount == 1);
arSetCommandGate->commandWakeup(&arSetIsSyncingCount, false);
}
}
bool AppleRAIDSet::pauseSet(bool whenIdle)
{
if (whenIdle) {
if (arStorageRequestsPending != 0) return false;
if (arSetWasBlockedByPause) {
arSetWasBlockedByPause = false;
return false;
}
}
while (arSetIsPaused) {
arSetWasBlockedByPause = true;
arSetCommandGate->commandSleep(&arSetIsPaused, THREAD_UNINT);
}
arSetIsPaused++;
while (arStorageRequestsPending != 0) {
arSetCommandGate->commandSleep(&arStorageRequestPool, THREAD_UNINT);
}
return true;
}
void AppleRAIDSet::unpauseSet()
{
assert(arSetIsPaused);
arSetIsPaused--;
if (arSetIsPaused == 0) {
arSetCommandGate->commandWakeup(&arSetIsPaused, false);
}
}
IOReturn AppleRAIDSet::allocateRAIDRequest(AppleRAIDStorageRequest **storageRequest)
{
while (1) {
if ((arActiveCount == 0) || getSetState() <= kAppleRAIDSetStateTerminating) {
*storageRequest = 0;
return kIOReturnNoMedia;
}
if (arSetIsPaused) {
arSetWasBlockedByPause = true;
arSetCommandGate->commandSleep(&arSetIsPaused, THREAD_UNINT);
continue;
}
*storageRequest = (AppleRAIDStorageRequest *)arStorageRequestPool->getCommand(false);
if (*storageRequest == 0) {
arSetCommandGate->commandSleep(&arStorageRequestPool, THREAD_UNINT);
continue;
}
break;
}
arStorageRequestsPending++;
return kIOReturnSuccess;
}
void AppleRAIDSet::returnRAIDRequest(AppleRAIDStorageRequest *storageRequest)
{
arStorageRequestsPending--;
arStorageRequestPool->returnCommand(storageRequest);
arSetCommandGate->commandWakeup(&arStorageRequestPool, false);
}
void AppleRAIDSet::completeRAIDRequest(AppleRAIDStorageRequest *storageRequest)
{
UInt32 cnt;
UInt64 byteCount;
IOReturn status;
bool isWrite;
IOStorageCompletion storageCompletion;
isWrite = (storageRequest->srMemoryDescriptorDirection == kIODirectionOut);
byteCount = 0;
status = kIOReturnSuccess;
for (cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] == 0) continue;
if (arMembers[cnt]->getMemberState() != kAppleRAIDMemberStateOpen) {
IOLogRW("AppleRAIDSet::completeRAIDRequest - [%lu] tbc 0x%llx, sbc 0x%llx bc 0x%llx, member %p, member state %lu\n",
cnt, storageRequest->srByteCount, storageRequest->srRequestByteCounts[cnt],
byteCount, arMembers[cnt], arMembers[cnt]->getMemberState());
if (arMembers[cnt]->getMemberState() == kAppleRAIDMemberStateClosing) {
status = kIOReturnOffline;
}
continue;
}
if (storageRequest->srRequestStatus[cnt] != kIOReturnSuccess) {
status = storageRequest->srRequestStatus[cnt];
IOLog("AppleRAID::completeRAIDRequest - error 0x%x detected for set \"%s\" (%s), member %s, set byte offset = %llu.\n",
status, getSetNameString(), getUUIDString(), arMembers[cnt]->getUUIDString(), storageRequest->srByteStart);
continue;
}
if (status == kIOReturnSuccess) {
byteCount += storageRequest->srRequestByteCounts[cnt];
}
IOLogRW("AppleRAIDSet::completeRAIDRequest - [%lu] tbc 0x%llx, sbc 0x%llx bc 0x%llx, member %p\n",
cnt, storageRequest->srByteCount, storageRequest->srRequestByteCounts[cnt],
byteCount, arMembers[cnt]);
}
if ((status == kIOReturnSuccess) && (byteCount != storageRequest->srByteCount)) {
IOLog("AppleRAID::completeRAIDRequest - underrun detected, expected = 0x%llx, actual = 0x%llx, set = \"%s\" (%s)\n",
storageRequest->srByteCount, byteCount, getSetNameString(), getUUIDString());
status = kIOReturnUnderrun;
byteCount = 0;
}
storageRequest->srMemoryDescriptor->release();
storageCompletion = storageRequest->srCompletion;
returnRAIDRequest(storageRequest);
IOStorage::complete(storageCompletion, status, byteCount);
if (status != kIOReturnSuccess) recoverStart();
}
void AppleRAIDSet::recoverStart()
{
IOLog1("AppleRAID::recoverStart entered\n");
arSetIsPaused++;
retain();
bool bumped = thread_call_enter(arRecoveryThreadCall);
if (bumped) {
arSetIsPaused--;
release();
}
}
void AppleRAIDSet::recoverWait()
{
assert(arSetIsPaused);
IOLog1("AppleRAID::recover %lu requests are pending.\n", arStorageRequestsPending);
while (arStorageRequestsPending != 0) {
arSetCommandGate->commandSleep(&arStorageRequestPool, THREAD_UNINT);
}
}
bool AppleRAIDSet::recover()
{
if (arController->findSet(getUUID()) != this) return false;
arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &AppleRAIDSet::recoverWait));
IOLog1("AppleRAID::recover wait for requests complete.\n");
IOSleep(100);
gAppleRAIDGlobals.lock();
assert(arSetIsPaused);
UInt32 oldActiveCount = arActiveCount;
OSSet * brokenMembers= OSSet::withCapacity(10);
for (UInt32 cnt = 0; cnt < arMemberCount; cnt++) {
if (arMembers[cnt] == 0) continue;
if (arMembers[cnt]->getMemberState() == kAppleRAIDMemberStateClosing) {
IOLog("AppleRAID::recover() member %s from set \"%s\" (%s) has been marked offline.\n",
arMembers[cnt]->getUUIDString(), getSetNameString(), getUUIDString());
AppleRAIDMember * brokenMember = arMembers[cnt];
arMembers[cnt] = 0;
arActiveCount--;
brokenMembers->setObject(brokenMember);
arSpareMembers->setObject(brokenMember);
brokenMember->changeMemberState(kAppleRAIDMemberStateBroken);
}
}
if (oldActiveCount != arActiveCount) {
arController->restartSet(this, bumpOnError());
while (brokenMembers->getCount()) {
AppleRAIDMember * brokenMember = (AppleRAIDMember *)brokenMembers->getAnyObject();
brokenMember->close(this, 0);
brokenMembers->removeObject(brokenMember);
}
}
brokenMembers->release();
bool stillAlive = arActiveCount > 0;
gAppleRAIDGlobals.unlock();
arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &AppleRAIDSet::unpauseSet));
release();
IOLog1("AppleRAID::recover finished\n");
return stillAlive;
}