#include "AppleRAID.h"
#define super IOService
OSDefineMetaClassAndStructors(AppleRAID, IOService);
bool AppleRAID::init()
{
if (super::init() == false) return false;
raidSets = OSDictionary::withCapacity(0x10);
raidMembers = OSDictionary::withCapacity(0x10);
return (raidSets && raidMembers);
}
void AppleRAID::free()
{
if (raidSets) {
raidSets->release();
raidSets = 0;
}
if (raidMembers) {
raidMembers->release();
raidMembers = 0;
}
super::free();
}
void AppleRAID::addSet(AppleRAIDSet *set)
{
const OSString * uuid = set->getUUID();
if (uuid) {
raidSets->setObject(uuid, set);
}
}
void AppleRAID::removeSet(AppleRAIDSet * set)
{
const OSString * uuid = set->getUUID();
if (uuid) {
raidSets->removeObject(uuid);
}
}
AppleRAIDSet *AppleRAID::findSet(const OSString *uuid)
{
return OSDynamicCast(AppleRAIDSet, raidSets->getObject(uuid));
}
AppleRAIDSet *AppleRAID::findSet(AppleRAIDMember * member)
{
const OSString * setUUID = member->getSetUUID();
if (setUUID == 0) return 0;
return OSDynamicCast(AppleRAIDSet, raidSets->getObject(setUUID));
}
void AppleRAID::addMember(AppleRAIDMember *member)
{
const OSString * uuid = member->getUUID();
if (uuid) {
raidMembers->setObject(uuid, member);
}
}
void AppleRAID::removeMember(AppleRAIDMember * member)
{
const OSString * uuid = member->getUUID();
if (uuid) {
raidMembers->removeObject(uuid);
}
}
AppleRAIDMember *AppleRAID::findMember(const OSString *uuid)
{
return OSDynamicCast(AppleRAIDMember, raidMembers->getObject(uuid));
}
IOReturn AppleRAID::newMember(IORegistryEntry * child)
{
AppleRAIDMember * member = OSDynamicCast(AppleRAIDMember, child);
gAppleRAIDGlobals.lock();
IOLog1("AppleRAID::newMember(%p) entered.\n", child);
while (1) {
if (member == 0) break;
const OSString * memberUUID = member->getUUID();
if (memberUUID == 0) break;
if (findMember(memberUUID)) {
IOLog("AppleRAID::newMember detected duplicate member %s in set \"%s\" (%s).\n",
member->getUUIDString(), member->getSetNameString(), member->getSetUUIDString());
break;
}
addMember(member);
const OSString * setUUID = member->getSetUUID();
if (setUUID == 0) {
IOLog("AppleRAID::newMember member %s in set \"%s\" has a corrupted header (no set UUID).\n",
member->getUUIDString(), member->getSetNameString());
break;
}
AppleRAIDSet * set = findSet(setUUID);
bool firstTime = set == 0;
if (!set) {
OSString * raidLevel = OSDynamicCast(OSString, member->getHeaderProperty(kAppleRAIDLevelNameKey));
if (!raidLevel) {
IOLog("AppleRAID::newMember member %s in set \"%s\" (%s) has a corrupted header (no RAID level).\n",
member->getUUIDString(), member->getSetNameString(), member->getSetUUIDString());
break;
}
IOLog1("AppleRAID::newMember(%p) new raid set, level = %s.\n", child, raidLevel->getCStringNoCopy());
if (raidLevel->isEqualTo(kAppleRAIDLevelNameConcat)) {
set = AppleRAIDConcatSet::createRAIDSet(member);
} else
if (raidLevel->isEqualTo(kAppleRAIDLevelNameMirror)) {
set = AppleRAIDMirrorSet::createRAIDSet(member);
} else
if (raidLevel->isEqualTo(kAppleRAIDLevelNameStripe)) {
set = AppleRAIDStripeSet::createRAIDSet(member);
}
if (set) {
IOLog1("AppleRAID::newMember(%p) raid set \"%s\" (%s) successfully created.\n",
child, set->getSetNameString(), set->getUUIDString());
addSet(set);
set->release();
} else {
IOLog("AppleRAID::newMember unknown raid level %s.\n", raidLevel->getCStringNoCopy());
}
}
if (!set) break;
if (set->isPaused() && !set->isSetComplete()) {
if (set->upgradeMember(member)) {
restartSet(set, true);
IOLog1("AppleRAID::newMember(%p) was successful (live add).\n", child);
gAppleRAIDGlobals.unlock();
return kIOReturnSuccess;
}
break;
}
if (!set->addMember(member)) {
if (!set->addSpare(member)) {
IOLog("AppleRAID::newMember was unable to add member %s to set \"%s\" (%s).\n",
member->getUUIDString(), set->getSetNameString(), set->getUUIDString());
break;
}
}
startSet(set);
if (firstTime) set->registerService();
IOLog1("AppleRAID::newMember(%p) was successful.\n", child);
gAppleRAIDGlobals.unlock();
return kIOReturnSuccess;
}
IOLog1("AppleRAID::newMember(%p) failed.\n", child);
if (member) removeMember(member);
gAppleRAIDGlobals.unlock();
return kIOReturnUnformattedMedia;
}
IOReturn AppleRAID::oldMember(IORegistryEntry * child)
{
IOLog1("AppleRAID::oldMember(%p) entered.\n", child);
gAppleRAIDGlobals.lock();
while (1) {
AppleRAIDMember * member = OSDynamicCast(AppleRAIDMember, child);
if (member == 0) break;
AppleRAIDSet * set = findSet(member);
if (!set) break;
removeMember(member);
set->removeMember(member, 0);
if ((set->getActiveCount() == 0) && (set->getSpareCount() == 0)) {
if (set->isRAIDMember()) {
oldMember(set);
}
IOLog1("AppleRAID::oldMember(%p) terminating parent raid set %p.\n", child, set);
removeSet(set);
} else {
set = NULL;
}
IOLog1("AppleRAID::oldMember(%p) was successful.\n", child);
gAppleRAIDGlobals.unlock();
if (set) {
IOLog("AppleRAID: terminating set \"%s\" (%s).\n", set->getSetNameString(), set->getUUIDString());
set->terminate();
}
return kIOReturnSuccess;
}
IOLog1("AppleRAID::oldMember(%p) failed.\n", child);
gAppleRAIDGlobals.unlock();
return kIOReturnError;
}
void AppleRAID::recoverMember(IORegistryEntry * child)
{
IOLog1("AppleRAID::recoverMember(%p) entered.\n", child);
AppleRAIDMember * member = OSDynamicCast(AppleRAIDMember, child);
if (member) {
AppleRAIDSet * set = findSet(member);
if (set) set->arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, set, &AppleRAIDSet::recoverStart));
}
}
void AppleRAID::startSet(AppleRAIDSet * set)
{
IOLog1("AppleRAID::startSet(%p) entered.\n", set);
assert(gAppleRAIDGlobals.islocked());
assert(!set->isPaused());
if (set->startSet()) {
IOLog1("AppleRAID::startSet: the set %p is started.\n", set);
if (!set->isRAIDMember()) set->start(NULL);
}
(void)set->publishSet();
return;
}
void AppleRAID::degradeSet(AppleRAIDSet *set)
{
gAppleRAIDGlobals.lock();
const OSString * setUUID = set->getUUID();
if ((set == findSet(setUUID)) && (set->getSetState() == kAppleRAIDSetStateInitializing)) {
assert(!set->isPaused());
IOLog("AppleRAID::degradeSet - starting the set \"%s\" (%s).\n",
set->getSetNameString(), set->getUUIDString());
if (set->startSet()) {
IOLog1("AppleRAID::degradeSet: the set %p is started.\n", set);
if (!set->isRAIDMember()) set->start(NULL);
}
if (set->getSetState() == kAppleRAIDSetStateDegraded) {
set->bumpSequenceNumber();
set->writeRAIDHeader();
}
(void)set->publishSet();
}
gAppleRAIDGlobals.unlock();
}
void AppleRAID::restartSet(AppleRAIDSet *set, bool bump)
{
IOLog1("AppleRAID::restartSet(%p) entered.\n", set);
gAppleRAIDGlobals.lock();
IOLog("AppleRAID::restartSet - restarting set \"%s\" (%s).\n", set->getSetNameString(), set->getUUIDString());
(void)set->startSet();
if (bump) {
set->bumpSequenceNumber();
(void)set->writeRAIDHeader();
}
(void)set->publishSet();
gAppleRAIDGlobals.unlock();
}
IOReturn AppleRAID::updateSet(char * setInfoBuffer, char * retBuffer, IOByteCount setInfoBufferSize, IOByteCount * retBufferSize)
{
IOReturn rc = kIOReturnSuccess;
IOLog1("AppleRAID::updateSet() entered\n");
if (!isOpen()) return kIOReturnNotOpen;
if (!setInfoBuffer || !setInfoBufferSize) return kIOReturnBadArgument;
gAppleRAIDGlobals.lock();
while (1) {
OSString * errmsg = 0;
OSDictionary * updateInfo = OSDynamicCast(OSDictionary, OSUnserializeXML(setInfoBuffer, &errmsg));
if (!updateInfo) {
if (errmsg) IOLog("AppleRAID::updateSet - header parsing failed with %s\n", errmsg->getCStringNoCopy());
errmsg->release();
rc = kIOReturnBadArgument;
break;
}
const OSString * setUUIDString = OSDynamicCast(OSString, updateInfo->getObject(kAppleRAIDSetUUIDKey));
AppleRAIDSet * set = findSet(setUUIDString);
if (!set) { rc = kIOReturnBadArgument; break; };
updateInfo->removeObject(kAppleRAIDSetUUIDKey);
OSNumber * number = OSDynamicCast(OSNumber, updateInfo->getObject(kAppleRAIDSequenceNumberKey));
if (!number) { rc = kIOReturnBadArgument; break; };
UInt32 seqNum = number->unsigned32BitValue();
if (seqNum && seqNum != set->getSequenceNumber()) { rc = kIOReturnBadMessageID; break; };
updateInfo->removeObject(kAppleRAIDSequenceNumberKey);
number = OSDynamicCast(OSNumber, updateInfo->getObject("_update command_"));
if (number) {
UInt32 subcommand = number->unsigned32BitValue();
IOLog1("AppleRAID::updateSet() executing subcommand %d\n", (int)subcommand);
switch (subcommand) {
case kAppleRAIDUpdateResetSet:
startSet(set);
break;
case kAppleRAIDUpdateDestroySet:
if (set->unpublishSet()) {
if (!set->destroySet()) {
rc = kIOReturnError;
}
}
break;
default:
IOLog("AppleRAID::updateSet() unknown subcommand %d\n", (int)subcommand);
}
}
updateInfo->removeObject("_update command_");
if (updateInfo->getCount()) {
AppleRAIDSet * parentSet = 0;
if (set->isRAIDMember()) {
parentSet = findSet((AppleRAIDMember *)set);
IOLog1("AppleRAID::updateSet() pausing parent set %p.\n", parentSet);
IOCommandGate::Action pauseSetMethod = OSMemberFunctionCast(IOCommandGate::Action, parentSet, &AppleRAIDSet::pauseSet);
if (parentSet) parentSet->arSetCommandGate->runAction(pauseSetMethod, (void *)false);
}
IOLog1("AppleRAID::updateSet() pausing set %p.\n", set);
IOCommandGate::Action pauseSetMethod = OSMemberFunctionCast(IOCommandGate::Action, set, &AppleRAIDSet::pauseSet);
set->arSetCommandGate->runAction(pauseSetMethod, (void *)false);
if (!set->reconfigureSet(updateInfo)) rc = kIOReturnError;
restartSet(set, true);
IOLog1("AppleRAID::updateSet() unpausing set.\n");
set->arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, set, &AppleRAIDSet::unpauseSet));
if (parentSet) parentSet->arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, parentSet, &AppleRAIDSet::unpauseSet));
}
*(UInt32 *)retBuffer = set->getSequenceNumber();
*retBufferSize = sizeof(UInt32);
updateInfo->release();
IOLog1("AppleRAID::updateSet() was %ssuccessful.\n", rc ? "un" : "");
break;
}
gAppleRAIDGlobals.unlock();
return rc;
}
IOReturn AppleRAID::getListOfSets(UInt32 inFlags, char * outList, IOByteCount * outListSize)
{
OSCollectionIterator * iter = 0;
OSArray * keys = 0;
OSSerialize * s = 0;
IOReturn rc = kIOReturnError;
IOLog1("AppleRAID::getListOfSets() entered\n");
if (!isOpen()) return kIOReturnNotOpen;
if (!inFlags || !outList || !outListSize) return kIOReturnBadArgument;
outList[0] = 0;
gAppleRAIDGlobals.lock();
unsigned int keyCount = raidSets->getCount();
while (keyCount) {
keys = OSArray::withCapacity(keyCount);
if (!keys) break;
iter = OSCollectionIterator::withCollection(raidSets);
if (!iter) break;
while (const OSString * setName = OSDynamicCast(OSString, iter->getNextObject())) {
AppleRAIDSet * set = findSet(setName);
if (!set) continue;
bool addToList = false;
if (inFlags && kAppleRAIDState) {
UInt32 state = set->getSetState();
addToList = (((state < kAppleRAIDSetStateOnline) && (inFlags & kAppleRAIDOfflineSets)) ||
((state == kAppleRAIDSetStateOnline) && (inFlags & kAppleRAIDOnlineSets)) ||
((state == kAppleRAIDSetStateDegraded) && (inFlags & kAppleRAIDDegradedSets)));
}
if (inFlags && kAppleRAIDVisibility) {
bool visible = !set->isRAIDMember();
addToList = visible ? (inFlags & kAppleRAIDVisibleSets) : (inFlags & kAppleRAIDInternalSets);
}
if (addToList) (void)keys->setObject(setName);
}
s = OSSerialize::withCapacity(keys->getCount() * 128);
if (!s) break;
s->clearText();
if (!keys->serialize(s)) break;
if (*outListSize < s->getLength()) {
IOLog("AppleRAID::getListOfSets() return buffer too small, need %d bytes, received %d.\n",
(int)s->getLength(), (int)*outListSize);
rc = kIOReturnNoSpace;
break;
}
*outListSize = s->getLength();
bcopy(s->text(), outList, *outListSize);
rc = kIOReturnSuccess;
break;
}
if (iter) iter->release();
if (keys) keys->release();
if (s) s->release();
if (keyCount == 0) rc = kIOReturnNoDevice;
if (rc) *outListSize = 0;
gAppleRAIDGlobals.unlock();
return rc;
}
IOReturn AppleRAID::getSetProperties(char * setString, char * outProp, IOByteCount setStringSize, IOByteCount * outPropSize)
{
IOReturn rc = kIOReturnError;
const OSString * setName = 0;
AppleRAIDSet * set = 0;
OSDictionary * props = 0;
OSSerialize * s = 0;
IOLog1("AppleRAID::getSetProperties() entered\n");
if (!isOpen()) return kIOReturnNotOpen;
if (!setString || !outProp || !outPropSize) return kIOReturnBadArgument;
outProp[0] = 0;
gAppleRAIDGlobals.lock();
while (1) {
setName = OSString::withCString(setString);
if (!setName) break;
set = findSet(setName);
if (!set) break;
IOLog1("setName = %s (%p)\n", setString, set);
props = set->getSetProperties();
if (!props) break;
s = OSSerialize::withCapacity(512);
if (!s) break;
s->clearText();
if (!props->serialize(s)) break;
if (*outPropSize < s->getLength()) {
IOLog("AppleRAID::getSetProperties() return buffer too small, need %d bytes, received %d.\n",
(int)s->getLength(), (int)*outPropSize);
rc = kIOReturnNoSpace;
break;
}
*outPropSize = s->getLength();
bcopy(s->text(), outProp, *outPropSize);
rc = kIOReturnSuccess;
break;
}
if (setName) setName->release();
if (props) props->release();
if (s) s->release();
if (rc) *outPropSize = 0;
gAppleRAIDGlobals.unlock();
return rc;
}
IOReturn AppleRAID::getMemberProperties(char * memberString, char * outProp, IOByteCount memberStringSize, IOByteCount * outPropSize)
{
IOReturn rc = kIOReturnError;
const OSString * memberName = 0;
AppleRAIDMember * member = 0;
OSDictionary * props = 0;
OSSerialize * s = 0;
IOLog1("AppleRAID::getMemberProperties() entered\n");
if (!isOpen()) return kIOReturnNotOpen;
if (!memberString || !outProp || !outPropSize) return kIOReturnBadArgument;
outProp[0] = 0;
gAppleRAIDGlobals.lock();
while (1) {
memberName = OSString::withCString(memberString);
if (!memberName) break;
member = findMember(memberName);
if (!member) break;
IOLog1("memberName = %s (%p)\n", memberString, member);
props = member->getMemberProperties();
if (!props) break;
s = OSSerialize::withCapacity(512);
if (!s) break;
s->clearText();
if (!props->serialize(s)) break;
if (*outPropSize < s->getLength()) {
IOLog("AppleRAID::getMemberProperties() return buffer too small, need %d bytes, received %d.\n",
(int)s->getLength(), (int)*outPropSize);
rc = kIOReturnNoSpace;
break;
}
*outPropSize = s->getLength();
bcopy(s->text(), outProp, *outPropSize);
rc = kIOReturnSuccess;
break;
}
if (memberName) memberName->release();
if (props) props->release();
if (s) s->release();
if (rc) *outPropSize = 0;
gAppleRAIDGlobals.unlock();
return rc;
}