1/* 2 * Copyright (c) 2001-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23 24#include "AppleRAID.h" 25 26 27enum { 28 kAppleRAIDHeaderV1_0_0 = 0x00010000, 29 kAppleRAIDMaxOFPath = 0x200, 30}; 31 32// RAID levels (version 1) 33enum { 34 kAppleRAIDStripe = 0x00000000, 35 kAppleRAIDMirror = 0x00000001, 36 kAppleRAIDConcat = 0x00000100 37}; 38 39struct AppleRAIDHeaderV1 { 40 char raidSignature[16]; // 0x0000 - kAppleRAIDSignature 41 UInt32 raidHeaderSize; // 0x0010 - Defaults to kAppleRAIDHeaderSize 42 UInt32 raidHeaderVersion; // 0x0014 - kAppleRAIDHeaderV1_0_0 43 UInt32 raidHeaderSequence; // 0x0018 - 0 member is bad, >0 member could be good 44 UInt32 raidLevel; // 0x001C - one of kAppleRAIDStripe, kAppleRAIDMirror or kAppleRAIDConcat 45 uuid_t raidUUID; // 0x0020 - 128 bit univeral unique identifier 46 char raidSetName[32]; // 0x0030 - Null Terminated 31 Character UTF8 String 47 UInt32 raidMemberCount; // 0x0050 - Number of members in set 48 UInt32 raidMemberIndex; // 0x0054 - 0 <= raidMemberIndex < raidMemberCount 49 UInt32 raidChunkSize; // 0x0058 - Usually 32 KB 50 UInt32 raidChunkCount; // 0x005C - Number of full chunks in set 51 UInt32 reserved1[104]; // 0x0060 - inited to zero, but preserved on update 52 char raidOFPaths[0]; // 0x0200 - Allow kAppleRAIDMaxOFPath for each member 53 // - Zero fill to size of header 54}; 55typedef struct AppleRAIDHeaderV1 AppleRAIDHeaderV1; 56 57 58#define super IOStorage 59OSDefineMetaClassAndStructors(AppleRAIDMember, IOStorage); 60 61 62bool AppleRAIDMember::init(OSDictionary * properties) 63{ 64 IOLog1("AppleRAIDMember::init(%p) isSet = %s\n", this, isRAIDSet() ? "yes":"no"); 65 66 if (super::init(properties) == false) return false; 67 68 // get the controller object 69 arController = gAppleRAIDGlobals.getController(); 70 if (!arController) return false; 71 72 arHeader = OSDictionary::withCapacity(32); 73 if (!arHeader) return false; 74 75 arTarget = 0; 76 arBaseOffset = 0xdeaddeaddeadbeefLL; 77 arHeaderOffset = 0xdeaddeaddeadbeefLL; 78 79 arIsWritable = false; 80 arIsEjectable = false; 81 arNativeBlockSize = 0; 82 arSyncronizeCacheThreadCall = 0; 83 84 arMemberIndex = 0xffffffff; 85 86#ifdef DEBUG 87 IOSleep(500); // let the system log catch up 88#endif 89 90 return true; 91} 92 93 94void AppleRAIDMember::free(void) 95{ 96 IOLog1("AppleRAIDMember::free(%p)\n", this); 97 98 if (arHeaderBuffer) arHeaderBuffer->release(); 99 if (arHeader) arHeader->release(); 100 101 if (arSyncronizeCacheThreadCall) { 102 thread_call_free(arSyncronizeCacheThreadCall); 103 arSyncronizeCacheThreadCall = 0; 104 } 105 106 gAppleRAIDGlobals.releaseController(); 107 108 super::free(); 109} 110 111 112//************************************************************************************************************* 113//*************************************************************************************************************** 114//*************************************************************************************************************** 115 116 117bool AppleRAIDMember::start(IOService * provider) 118{ 119 IOLog1("AppleRAIDMember::start(%p) isSet = %s\n", this, isRAIDSet() ? "yes":"no"); 120 121 if (!isRAIDSet()) { 122 123 assert(provider); 124 arTarget = (IOMedia *)provider; 125 126 if (super::start(provider) == false) return false; 127 128 arIsWritable = arTarget->isWritable(); 129 arIsEjectable = arTarget->isEjectable(); 130 arNativeBlockSize = arTarget->getPreferredBlockSize(); 131 132 if (!arSyncronizeCacheThreadCall) { 133 thread_call_func_t syncCacheMethod = OSMemberFunctionCast(thread_call_func_t, this, &AppleRAIDMember::synchronizeCacheCallout); 134 arSyncronizeCacheThreadCall = thread_call_allocate(syncCacheMethod, (thread_call_param_t)this); 135 if (arSyncronizeCacheThreadCall == 0) return false; 136 } 137 } 138 139 arIsRAIDMember = false; 140 arMemberState = kAppleRAIDMemberStateClosed; 141 setProperty(kAppleRAIDMemberStatusKey, kAppleRAIDStatusOnline); 142 143 // if no raid header just return 144 if (readRAIDHeader()) { 145 return false; 146 } 147 148#ifdef DEBUG 149 if (isRAIDSet()) IOLog1("AppleRAIDMember::start(%p) this set is part of stacked raid.\n", this); 150#endif 151 152 arIsRAIDMember = true; 153 154 return (arController->newMember(this) == kIOReturnSuccess); 155} 156 157 158bool AppleRAIDMember::requestTerminate(IOService *provider, IOOptionBits options) 159{ 160 IOLog1("AppleRAIDMember::requestTerminate(%p) isSet = %s\n", this, isRAIDSet() ? "yes":"no"); 161 162 // 163 // If the raid member is in use (opened) the stop method will not be called. 164 // luckily, the client still gets notified here and we can fix things. 165 // setting the state to closing will cause us to catch this on the next i/o 166 // 167 168 if (isRAIDSet()) { 169 return false; 170 } else { 171 if (arMemberState > kAppleRAIDMemberStateClosed) { 172 changeMemberState(kAppleRAIDMemberStateClosing); 173 arController->recoverMember(this); 174 } 175 } 176 177 return super::requestTerminate(provider, options); 178} 179 180 181void AppleRAIDMember::stop(IOService * provider) 182{ 183 IOLog1("AppleRAIDMember::stop(%p) isMember = %s isSet = %s\n", this, isRAIDMember() ? "yes":"no", isRAIDSet() ? "yes":"no"); 184 185 // 186 // when a member is pulled, it's parent set gets called here first. 187 // just ignore requests to stop sets, they are terminated in 188 // by the controller when all of the member/spares are gone. 189 // 190 191 if (!isRAIDSet()) { 192 arController->oldMember(this); 193 arIsRAIDMember = false; 194 super::stop(provider); // this is noop 195 } 196} 197 198 199//*************************************************************************************************************** 200//*************************************************************************************************************** 201//*************************************************************************************************************** 202 203 204bool AppleRAIDMember::handleOpen(IOService * /* client */, 205 IOOptionBits options, 206 void * argument) 207{ 208 bool isOpen = arTarget->open(this, options, (IOStorageAccess) (uintptr_t) argument); 209 if (isOpen) changeMemberState(kAppleRAIDMemberStateOpen); 210 return isOpen; 211} 212 213 214bool AppleRAIDMember::handleIsOpen(const IOService * /* client */) const 215{ 216 return arTarget->isOpen(this); 217} 218 219 220void AppleRAIDMember::handleClose(IOService * /* client */, 221 IOOptionBits options) 222{ 223 changeMemberState(kAppleRAIDMemberStateClosing); 224 arTarget->close(this, options); 225 changeMemberState(kAppleRAIDMemberStateClosed); 226} 227 228 229//*************************************************************************************************************** 230//*************************************************************************************************************** 231//*************************************************************************************************************** 232 233void AppleRAIDMember::read(IOService * client, 234 UInt64 byteStart, 235 IOMemoryDescriptor * buffer, 236 IOStorageAttributes * attributes, 237 IOStorageCompletion * completion) 238{ 239 IOLogRW("AppleRAIDMember::read, this %p start %llu size 0x%llx\n", this, byteStart, (UInt64)buffer->getLength()); 240 assert(handleIsOpen(NULL)); 241 arTarget->read(this, byteStart, buffer, attributes, completion); 242} 243 244 245void AppleRAIDMember::write(IOService * client, 246 UInt64 byteStart, 247 IOMemoryDescriptor * buffer, 248 IOStorageAttributes * attributes, 249 IOStorageCompletion * completion) 250{ 251 IOLogRW("AppleRAIDMember::write, this %p start %llu size 0x%llx\n", this, byteStart, (UInt64)buffer->getLength()); 252 assert(handleIsOpen(NULL)); 253 arTarget->write(this, byteStart, buffer, attributes, completion); 254} 255 256 257IOReturn AppleRAIDMember::synchronizeCache(IOService * client) 258{ 259 AppleRAIDSet * masterSet = OSDynamicCast(AppleRAIDSet, client); 260 assert(masterSet); 261 262 // we are running on the master's workloop 263 masterSet->synchronizeStarted(); 264 265 bool bumped = thread_call_enter1(arSyncronizeCacheThreadCall, (thread_call_param_t)masterSet); 266 267 // we bumped another sync request, decrement count for them 268 assert(!bumped); 269 if (bumped) masterSet->synchronizeCompleted(); 270 271 return 0; 272} 273 274IOReturn AppleRAIDMember::synchronizeCacheCallout(AppleRAIDSet *masterSet) 275{ 276 assert(masterSet); 277 278 IOReturn result = arTarget->synchronizeCache(this); 279 if (result) IOLog("AppleRAIDMember::synchronizeCacheCallout: failed with %x on %s\n", result, getUUIDString()); 280 281 masterSet->synchronizeCompleted(); 282 283 return result; 284} 285 286 287//*************************************************************************************************************** 288//*************************************************************************************************************** 289//*************************************************************************************************************** 290 291 292IOReturn AppleRAIDMember::readRAIDHeader(void) 293{ 294 UInt64 size = getSize(); 295 UInt64 headerSize = (UInt64)kAppleRAIDHeaderSize; 296 297 assert(size); 298 299 // look for version 2 header first 300 arHeaderOffset = ARHEADER_OFFSET(size); 301 302 // Allocate a buffer to read the AppleRAID Header. 303 if (arHeaderBuffer == 0) { 304 arHeaderBuffer = IOBufferMemoryDescriptor::withCapacity(headerSize, kIODirectionNone); 305 if (arHeaderBuffer == 0) return kIOReturnNoMemory; 306 } 307 308readheader: 309 310 IOLog2("AppleRAIDMember::readRAIDHeader(%p) size %llu hdr off %llu\n", this, size, arHeaderOffset); 311 312 // Open the member 313 if (!getTarget()->open(this, 0, kIOStorageAccessReader)) return kIOReturnIOError; 314 315 // Read the raid header 316 arHeaderBuffer->setDirection(kIODirectionIn); 317 IOReturn rc = getTarget()->read(this, arHeaderOffset, arHeaderBuffer); 318 319 // Close the member. 320 getTarget()->close(this, 0); 321 322 if (rc) return rc; 323 324 // Make sure the AppleRAID Header contains the correct signature. 325 AppleRAIDHeaderV2 * header = (AppleRAIDHeaderV2 *)arHeaderBuffer->getBytesNoCopy(); 326 if (strncmp(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature))) { 327 328 if (arHeaderOffset) { 329 arHeaderOffset = 0; // try for old v1 header at beginning of disk 330 goto readheader; 331 } 332 333 if (!isRAIDSet()) { 334 const OSString * diskname = getDiskName(); 335 if (diskname) IOLog("AppleRAIDMember::readRAIDHeader: failed, no header signature present on %s.\n", 336 diskname->getCStringNoCopy()); 337 } 338 339 return kIOReturnUnformattedMedia; 340 } 341 342 if (arHeaderOffset) { 343 arBaseOffset = 0; 344 rc = parseRAIDHeaderV2(); 345 } else { 346 arBaseOffset = kAppleRAIDHeaderSize; 347 rc = parseRAIDHeaderV1(); 348 } 349 setHeaderProperty(kAppleRAIDBaseOffsetKey, arBaseOffset, 64); 350 setHeaderProperty(kAppleRAIDNativeBlockSizeKey, arNativeBlockSize, 64); 351 352 setProperty(kAppleRAIDMemberUUIDKey, getHeaderProperty(kAppleRAIDMemberUUIDKey)); 353 setProperty(kAppleRAIDSetUUIDKey, getHeaderProperty(kAppleRAIDSetUUIDKey)); 354 355 OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDMemberIndexKey)); 356 arMemberIndex= number ? number->unsigned32BitValue() : 0xffffffff; 357 358 IOLog1(">>>>> %s %s %s <<<<<\n", getSetNameString(), getSetUUIDString(), getUUIDString()); 359 IOLog2("AppleRAIDMember::readRAIDHeader(%p): was %ssuccessful\n", this, rc ? "un" : ""); 360 return rc; 361} 362 363IOReturn AppleRAIDMember::writeRAIDHeader(void) 364{ 365 IOLog2("AppleRAIDMember::writeRAIDHeader(%p): entered.\n", this); 366 367 IOReturn rc = kIOReturnSuccess; 368 369 if ((arHeaderBuffer == 0) || (!handleIsOpen(0))) { 370 IOLog1("AppleRAIDMember::writeRAIDHeader(%p): aborting, rc = %x.\n", this, kIOReturnIOError); 371 return kIOReturnIOError; 372 } 373 374 if (arHeaderOffset) { 375 rc = buildOnDiskHeaderV2(); 376 } else { 377 rc = buildOnDiskHeaderV1(); 378 } 379 380 // write the raid header 381 if (!rc) { 382 arHeaderBuffer->setDirection(kIODirectionOut); 383 rc = getTarget()->write(this, arHeaderOffset, arHeaderBuffer); 384 } 385 386 // XXX if this fails we should change state or something? 387 388 IOLog1("AppleRAIDMember::writeRAIDHeader(%p): finished rc = %x.\n", this, rc); 389 return rc; 390} 391 392IOReturn AppleRAIDMember::updateRAIDHeader(OSDictionary * props) 393{ 394 // merge props into member's property list 395 OSCollectionIterator * iter = OSCollectionIterator::withCollection(props); 396 if (!iter) return kIOReturnNoMemory; 397 398 while (const OSString * key = OSDynamicCast(OSString, iter->getNextObject())) { 399 400 // if the key starts with "AppleRAID-" add it to the incore header 401 const char * match = "AppleRAID-"; 402 int matchSize = sizeof(match) - 1; 403 404 if (!strncmp(match, key->getCStringNoCopy(), matchSize)) { 405 setHeaderProperty(key, props->getObject(key)); 406 } 407 } 408 iter->release(); 409 410 return kIOReturnSuccess; 411} 412 413IOReturn AppleRAIDMember::zeroRAIDHeader(void) 414{ 415 IOLog1("AppleRAIDMember::zeroRAIDHeader(%p): entered.\n", this); 416 417 if (arHeaderBuffer == 0) { 418 IOLog1("AppleRAIDMember::zeroRAIDHeader(%p): no header buffer?\n", this); 419 return kIOReturnIOError; 420 } 421 422 // don't need to worry about read only/read-write state 423 // the member being removed from the set 424 bool alreadyOpen = handleIsOpen(0); 425 if ((!alreadyOpen) && (!getTarget()->open(this, 0, kIOStorageAccessReaderWriter))) { 426 IOLog("AppleRAIDMember::zeroRAIDHeader: failed trying to open member %s.\n", getUUIDString()); 427 return kIOReturnIOError; 428 } 429 430 char * header = (char *)arHeaderBuffer->getBytesNoCopy(); 431 bzero(header, arHeaderBuffer->getLength()); 432 433 // zero out the raid header 434 arHeaderBuffer->setDirection(kIODirectionOut); 435 IOReturn rc = getTarget()->write(this, arHeaderOffset, arHeaderBuffer); 436 437 if (!alreadyOpen) getTarget()->close(this, 0); 438 439 arIsRAIDMember = false; 440 441 IOLog1("AppleRAIDMember::zeroRAIDHeader(%p): exiting, rc = %x.\n", this, rc); 442 return rc; 443} 444 445 446//*************************************************************************************************************** 447//*************************************************************************************************************** 448//*************************************************************************************************************** 449 450 451#define ByteSwapHeaderV1(header) \ 452{ \ 453 (header)->raidHeaderSize = OSSwapBigToHostInt32((header)->raidHeaderSize); \ 454 (header)->raidHeaderVersion = OSSwapBigToHostInt32((header)->raidHeaderVersion); \ 455 (header)->raidHeaderSequence = OSSwapBigToHostInt32((header)->raidHeaderSequence); \ 456 (header)->raidLevel = OSSwapBigToHostInt32((header)->raidLevel); \ 457 (header)->raidMemberCount = OSSwapBigToHostInt32((header)->raidMemberCount); \ 458 (header)->raidMemberIndex = OSSwapBigToHostInt32((header)->raidMemberIndex); \ 459 (header)->raidChunkSize = OSSwapBigToHostInt32((header)->raidChunkSize); \ 460 (header)->raidChunkCount = OSSwapBigToHostInt32((header)->raidChunkCount); \ 461} 462 463IOReturn AppleRAIDMember::parseRAIDHeaderV1() 464{ 465 IOLog1("AppleRAIDMember::parseRAIDHeaderV1(%p): entered.\n", this); 466 467 if (isRAIDSet()) return kIOReturnUnsupported; 468 469 AppleRAIDHeaderV1 * header = (AppleRAIDHeaderV1 *)arHeaderBuffer->getBytesNoCopy(); 470 471 ByteSwapHeaderV1(header); 472 473 // check header size (not that it ever changes) 474 if (header->raidHeaderSize != kAppleRAIDHeaderSize) return kIOReturnUnformattedMedia; 475 476 // Make sure the header version is understood. 477 if (header->raidHeaderVersion != kAppleRAIDHeaderV1_0_0) return kIOReturnUnformattedMedia; 478 setHeaderProperty(kAppleRAIDHeaderVersionKey, header->raidHeaderVersion, 32); 479 480 // Make sure the header sequence is valid. 0 indicates a spare drive 481 if (header->raidHeaderSequence == 0) { 482 IOLog1("AppleRAIDMember::parseRAIDHeaderV1(%p): found a spare.\n", this); 483 setHeaderProperty(kAppleRAIDMemberTypeKey, kAppleRAIDSparesKey); 484 changeMemberState(kAppleRAIDMemberStateSpare); 485 } 486 487 setHeaderProperty(kAppleRAIDSequenceNumberKey, header->raidHeaderSequence, 32); 488 489 switch (header->raidLevel) { 490 case kAppleRAIDStripe: 491 { 492 setHeaderProperty(kAppleRAIDLevelNameKey, kAppleRAIDLevelNameStripe); 493 break; 494 } 495 case kAppleRAIDMirror: 496 { 497 setHeaderProperty(kAppleRAIDLevelNameKey, kAppleRAIDLevelNameMirror); 498 break; 499 } 500 default: 501 const OSString * diskname = getDiskName(); 502 if (diskname) IOLog("AppleRAIDMember::parseRAIDHeaderV1: unknown raid type on %s.\n", diskname->getCStringNoCopy()); 503 return kIOReturnUnformattedMedia; 504 } 505 506 char tmpString[kAppleRAIDMaxUUIDStringSize]; 507 uuid_unparse(header->raidUUID, tmpString); 508 setHeaderProperty(kAppleRAIDSetUUIDKey, (char *)tmpString); 509 510 setHeaderProperty(kAppleRAIDSetNameKey, (char *)header->raidSetName); 511 512 setHeaderProperty(kAppleRAIDMemberCountKey, header->raidMemberCount, 32); 513 setHeaderProperty(kAppleRAIDMemberIndexKey, header->raidMemberIndex, 32); 514 setHeaderProperty(kAppleRAIDChunkSizeKey, header->raidChunkSize, 64); 515 516 UInt64 chunkCount = header->raidChunkCount; 517 if (header->raidLevel == kAppleRAIDStripe) { 518 chunkCount /= header->raidMemberCount; 519 } 520 setHeaderProperty(kAppleRAIDChunkCountKey, chunkCount, 64); 521 522 if (header->raidLevel == kAppleRAIDMirror) { 523 setHeaderProperty(kAppleRAIDSetTimeoutKey, 30, 32); 524 setHeaderProperty(kAppleRAIDSetAutoRebuildKey, kOSBooleanFalse); 525 } 526 527 // fake up the member unique name for old style headers (but only once) 528 if (!getUUID()) { 529 uuid_t fakeUUID; 530 uuid_generate(fakeUUID); 531 uuid_unparse(fakeUUID, tmpString); 532 setHeaderProperty(kAppleRAIDMemberUUIDKey, (char *)tmpString); 533 } 534 535 return kIOReturnSuccess; 536} 537 538IOReturn AppleRAIDMember::buildOnDiskHeaderV1(void) 539{ 540 AppleRAIDHeaderV1 * header = (AppleRAIDHeaderV1 *)arHeaderBuffer->getBytesNoCopy(); 541 542 OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDSequenceNumberKey)); 543 if (number) header->raidHeaderSequence = number->unsigned32BitValue(); 544 545 ByteSwapHeaderV1(header); 546 547 return kIOReturnSuccess; 548} 549 550 551//*************************************************************************************************************** 552 553 554IOReturn AppleRAIDMember::parseRAIDHeaderV2() 555{ 556 IOLog1("AppleRAIDMember::parseRAIDHeaderV2(%p): entered.\n", this); 557 558 AppleRAIDHeaderV2 * headerBuffer = (AppleRAIDHeaderV2 *)arHeaderBuffer->getBytesNoCopy(); 559 560 OSString * errmsg = 0; 561 OSDictionary * props = OSDynamicCast(OSDictionary, OSUnserializeXML(headerBuffer->plist, &errmsg)); 562 if (!props) { 563 if (errmsg) { 564 IOLog("AppleRAIDMember::parseRAIDHeaderV2 - RAID header parsing failed with %s\n", errmsg->getCStringNoCopy()); 565 errmsg->release(); 566 } 567 return kIOReturnBadArgument; 568 } 569 570 // merge in member's property list 571 IOReturn rc = updateRAIDHeader(props); 572 props->release(); 573 if (rc) return rc; 574 575 // ok, we are done parsing the header 576 // set up a few extra things 577 578 OSArray * members = OSDynamicCast(OSArray, getHeaderProperty(kAppleRAIDMembersKey)); 579 if (members) setHeaderProperty(kAppleRAIDMemberCountKey, members->getCount(), 32); 580 581 OSString * tmpString = OSDynamicCast(OSString, getHeaderProperty(kAppleRAIDMemberTypeKey)); 582 if (tmpString->isEqualTo(kAppleRAIDSparesKey)) { 583 IOLog1("AppleRAIDMember::parseRAIDHeaderV2(%p): found a spare.\n", this); 584 changeMemberState(kAppleRAIDMemberStateSpare); 585 } 586 587 return kIOReturnSuccess; 588} 589 590IOReturn AppleRAIDMember::buildOnDiskHeaderV2(void) 591{ 592 IOLog2("AppleRAIDMember::buildOnDiskHeaderV2(%p) entered\n", this); 593 594 // make a copy of incore header and filter out the internal stuff 595 OSDictionary * copy = OSDictionary::withCapacity(arHeader->getCount()); 596 if (!copy) return kIOReturnNoMemory; 597 OSCollectionIterator * iter = OSCollectionIterator::withCollection(arHeader); 598 if (!iter) return kIOReturnNoMemory; 599 600 while (const OSString * key = OSDynamicCast(OSString, iter->getNextObject())) { 601 602 // if the key starts with "AppleRAID-" copy it 603 const char * match = "AppleRAID-"; 604 int matchSize = sizeof(match) - 1; 605 606 if (!strncmp(match, key->getCStringNoCopy(), matchSize)) { 607 copy->setObject(key, arHeader->getObject(key)); 608 } 609 } 610 iter->release(); 611 612 // serialize header to on-disk format 613 OSSerialize * s = OSSerialize::withCapacity(kAppleRAIDHeaderSize); 614 if (!s) return kIOReturnNoMemory; 615 616 s->clearText(); 617 if (!copy->serialize(s)) return kIOReturnInternalError; 618 copy->release(); 619 620 if (s->getLength() >= (kAppleRAIDHeaderSize - sizeof(AppleRAIDHeaderV2))) return kIOReturnNoResources; 621 622 AppleRAIDHeaderV2 * headerBuffer = (AppleRAIDHeaderV2 *)arHeaderBuffer->getBytesNoCopy(); 623 624 // set up header header 625 strlcpy(headerBuffer->raidSignature, kAppleRAIDSignature, sizeof(headerBuffer->raidSignature)); 626 strlcpy(headerBuffer->raidUUID, getSetUUIDString(), sizeof(headerBuffer->raidUUID)); 627 strlcpy(headerBuffer->memberUUID, getUUIDString(), sizeof(headerBuffer->memberUUID)); 628 629 // calculate the byte size for the raid data 630 headerBuffer->size = getUsableSize(); 631 if (headerBuffer->size == 0) return kIOReturnInternalError; 632 633 bcopy(s->text(), headerBuffer->plist, s->getLength()); 634 UInt32 bzSize = kAppleRAIDHeaderSize - (UInt32)((char *)headerBuffer->plist - (char *)headerBuffer) - s->getLength(); 635 bzero(headerBuffer->plist + s->getLength(), bzSize); 636 637 s->release(); 638 639 ByteSwapHeaderV2(headerBuffer); 640 641 IOLog2("AppleRAIDMember::buildOnDiskHeaderV2(%p) successful.\n", this); 642 return kIOReturnSuccess; 643} 644 645 646//*************************************************************************************************************** 647//*************************************************************************************************************** 648//*************************************************************************************************************** 649 650IOBufferMemoryDescriptor * 651AppleRAIDMember::readPrimaryMetaData() 652{ 653 IOLog1("AppleRAIDMember::readPrimaryMetaData(%p) entered.\n", this); 654 655 // get the reserved size of primary data area 656 OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDPrimaryMetaDataUsedKey)); 657 if (!number) return NULL; 658 UInt64 primarySize = number->unsigned64BitValue(); 659 if (primarySize == 0) return NULL; 660 661 primarySize = (((primarySize - 1) / 4096) + 1) * 4096; // round up for i/o 662 663 // calculate the start of primary data 664 UInt64 primaryOffset = getUsableSize(); 665 if (primaryOffset == 0) return NULL; 666 667 IOBufferMemoryDescriptor * primaryBuffer = 0; 668 669 // Allocate a buffer to read into 670 if (primaryBuffer == 0) { 671 primaryBuffer = IOBufferMemoryDescriptor::withCapacity(primarySize, kIODirectionNone); 672 if (primaryBuffer == 0) return NULL; 673 } 674 675 IOLog1("AppleRAIDMember::readPrimaryMetaData(%p) size %llu primary off %llu\n", this, primarySize, primaryOffset); 676 677 // Open the member 678 if (!getTarget()->open(this, 0, kIOStorageAccessReader)) return NULL; 679 680 // Read in the primary data from member 681 primaryBuffer->setDirection(kIODirectionIn); 682 IOReturn rc = getTarget()->read(this, primaryOffset, primaryBuffer); 683 684 // Close the member. 685 getTarget()->close(this, 0); 686 687 if (rc) { 688 primaryBuffer->release(); 689 return NULL; 690 } 691 692 // Make sure the AppleRAID Header contains the correct signature. 693 AppleRAIDPrimaryOnDisk * primary = (AppleRAIDPrimaryOnDisk *)primaryBuffer->getBytesNoCopy(); 694 if (strncmp(primary->priMagic, kAppleRAIDPrimaryMagic, sizeof(primary->priMagic))) { 695 primaryBuffer->release(); 696 return NULL; 697 } 698 699 IOLog1("AppleRAIDMember::readPrimaryMetaData(%p): was %ssuccessful\n", this, rc ? "un" : ""); 700 return primaryBuffer; 701} 702 703 704IOReturn AppleRAIDMember::writePrimaryMetaData(IOBufferMemoryDescriptor * primaryBuffer) 705{ 706 IOLog1("AppleRAIDMember::writePrimaryMetaData(%p) entered.\n", this); 707 708 // this code assumes that members are already opened for write 709 if ((primaryBuffer == 0) || (!handleIsOpen(0))) { 710 IOLog1("AppleRAIDMember::writePrimaryMetaData(%p): aborting, rc = %x.\n", this, kIOReturnInternalError); 711 return kIOReturnInternalError; 712 } 713 714 // get the reserved size of primary data area 715 OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDPrimaryMetaDataUsedKey)); 716 if (!number) return kIOReturnUnformattedMedia; 717 UInt64 primarySize = number->unsigned64BitValue(); 718 if (primarySize == 0) return kIOReturnUnformattedMedia; 719 720 primarySize = (((primarySize - 1) / 4096) + 1) * 4096; // round up for i/o 721 722 if (primaryBuffer->getLength() > primarySize) return kIOReturnInternalError; 723 724 // calculate the start of primary data 725 UInt64 primaryOffset = getUsableSize(); 726 if (primaryOffset == 0) return kIOReturnUnformattedMedia; 727 728 // write the primary meta data to disk 729 primaryBuffer->setDirection(kIODirectionOut); 730 IOReturn rc = getTarget()->write(this, primaryOffset, primaryBuffer); 731 732 // XXX if this fails we should change state or something? 733 734 IOLog1("AppleRAIDMember::writePrimaryMetaData(%p): finished rc = %x.\n", this, rc); 735 return rc; 736} 737 738 739//*************************************************************************************************************** 740//*************************************************************************************************************** 741//*************************************************************************************************************** 742 743 744OSDictionary * AppleRAIDMember::getHeader() 745{ 746 return arHeader; 747} 748 749OSObject * AppleRAIDMember::getHeaderProperty(const OSString * aKey) const 750{ 751 return arHeader->getObject(aKey); 752} 753 754OSObject * AppleRAIDMember::getHeaderProperty(const char * aKey) const 755{ 756 return arHeader->getObject(aKey); 757} 758 759bool AppleRAIDMember::setHeaderProperty(const OSString * aKey, OSObject * anObject) 760{ 761 return arHeader->setObject(aKey, anObject); 762} 763 764bool AppleRAIDMember::setHeaderProperty(const char * aKey, OSObject * anObject) 765{ 766 return arHeader->setObject(aKey, anObject); 767} 768 769bool AppleRAIDMember::setHeaderProperty(const char * aKey, const char * cString) 770{ 771 bool success = false; 772 773 OSString * aString = OSString::withCString(cString); 774 if (aString) { 775 success = arHeader->setObject(aKey, aString); 776 aString->release(); 777 } 778 779 return success; 780} 781 782bool AppleRAIDMember::setHeaderProperty(const char * key, unsigned long long value, unsigned int numberOfBits) 783{ 784 bool success = false; 785 786 OSNumber * number = OSNumber::withNumber(value, numberOfBits); 787 if (number) { 788 success = arHeader->setObject(key, number); 789 number->release(); 790 } 791 792 return success; 793} 794 795 796//*************************************************************************************************************** 797//*************************************************************************************************************** 798//*************************************************************************************************************** 799 800 801const OSString * AppleRAIDMember::getSetName(void) 802{ 803 return OSDynamicCast(OSString, getHeaderProperty(kAppleRAIDSetNameKey)); 804} 805 806const char * AppleRAIDMember::getSetNameString(void) 807{ 808 const OSString * name = getSetName(); 809 810 return name ? name->getCStringNoCopy() : "--internal error, set name not set--"; 811} 812 813const OSString * AppleRAIDMember::getUUID(void) 814{ 815 return OSDynamicCast(OSString, getHeaderProperty(kAppleRAIDMemberUUIDKey)); 816} 817 818const char * AppleRAIDMember::getUUIDString(void) 819{ 820 const OSString * uuid = getUUID(); 821 822 return uuid ? uuid->getCStringNoCopy() : "--internal error, uuid not set--"; 823} 824 825const OSString * AppleRAIDMember::getSetUUID(void) 826{ 827 return OSDynamicCast(OSString, getHeaderProperty(kAppleRAIDSetUUIDKey)); 828} 829 830const char * AppleRAIDMember::getSetUUIDString(void) 831{ 832 const OSString * uuid = getSetUUID(); 833 834 return uuid ? uuid->getCStringNoCopy() : "--internal error, set uuid not set--"; 835} 836 837const OSString * AppleRAIDMember::getDiskName(void) 838{ 839 return OSDynamicCast(OSString, arTarget->getProperty(kIOBSDNameKey)); 840} 841 842void AppleRAIDMember::setMemberIndex(UInt32 index) 843{ 844 arMemberIndex = index; 845 setHeaderProperty(kAppleRAIDMemberIndexKey, index, 32); 846} 847 848//*************************************************************************************************************** 849//*************************************************************************************************************** 850//*************************************************************************************************************** 851 852 853IOStorage * AppleRAIDMember::getTarget(void) const 854{ 855 return (IOStorage *)arTarget; 856} 857 858bool AppleRAIDMember::isRAIDSet(void) 859{ 860 return false; 861} 862 863bool AppleRAIDMember::isRAIDMember(void) 864{ 865 return arIsRAIDMember; 866} 867 868bool AppleRAIDMember::isSpare(void) 869{ 870 return getMemberState() == kAppleRAIDMemberStateSpare; 871} 872 873bool AppleRAIDMember::isBroken(void) 874{ 875 return getMemberState() == kAppleRAIDMemberStateBroken; 876} 877 878UInt64 AppleRAIDMember::getSize() const 879{ 880 return arTarget->getSize(); 881} 882 883UInt64 AppleRAIDMember::getUsableSize() const 884{ 885 OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDChunkCountKey)); 886 if (!number) return 0; 887 UInt64 usable = number->unsigned64BitValue(); 888 889 number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDChunkSizeKey)); 890 if (!number) return 0; 891 usable *= number->unsigned64BitValue(); 892 893 return usable; 894} 895 896UInt64 AppleRAIDMember::getPrimaryMaxSize() const 897{ 898 UInt64 primaryOffset = getUsableSize(); 899 if (primaryOffset == 0) return 0; 900 901 return arHeaderOffset - primaryOffset; 902} 903 904UInt64 AppleRAIDMember::getSecondarySize() const 905{ 906 OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDSecondaryMetaDataSizeKey)); 907 return number ? number->unsigned64BitValue() : 0; 908} 909 910//*************************************************************************************************************** 911 912// IOMedia clones 913 914bool AppleRAIDMember::isEjectable() const 915{ 916 return arIsEjectable; 917} 918 919bool AppleRAIDMember::isWritable() const 920{ 921 return arIsWritable; 922} 923 924UInt64 AppleRAIDMember::getBase() const 925{ 926 return arBaseOffset; 927} 928 929//*************************************************************************************************************** 930 931#include <IOKit/IODeviceTreeSupport.h> 932 933bool 934AppleRAIDMember::addBootDeviceInfo(OSArray * bootArray) 935{ 936 int length = kAppleRAIDMaxOFPath; 937 char ofPath[kAppleRAIDMaxOFPath]; 938 939 // get the path 940 if (!arTarget || !arTarget->getPath(ofPath, &length, gIODTPlane)) { 941 return false; 942 } 943 944 UInt64 memberSize = arTarget->getSize(); 945 946 IOLog2("AppleRAIDMember::addBootDeviceInfo %p, path = %s, size %llu\n", this, ofPath, memberSize); 947 948 OSString * string = OSString::withCString(ofPath); 949 OSNumber * number = OSNumber::withNumber(memberSize, 64); 950 951 if (bootArray && string && number) { 952 OSDictionary * dict = OSDictionary::withCapacity(2); 953 if (dict) { 954 dict->setObject(kIOBootDevicePathKey, string); 955 dict->setObject(kIOBootDeviceSizeKey, number); 956 bootArray->setObject(dict); 957 958 string->release(); 959 number->release(); 960 dict->release(); 961 962 return true; 963 } 964 } 965 966 return false; 967} 968 969//*************************************************************************************************************** 970 971OSDictionary * AppleRAIDMember::getMemberProperties(void) 972{ 973 OSDictionary * props = OSDictionary::withCapacity(16); 974 if (!props) return NULL; 975 976 props->setObject(kAppleRAIDHeaderVersionKey, getHeaderProperty(kAppleRAIDHeaderVersionKey)); 977 props->setObject(kAppleRAIDMemberUUIDKey, getHeaderProperty(kAppleRAIDMemberUUIDKey)); 978 props->setObject(kAppleRAIDSequenceNumberKey, getHeaderProperty(kAppleRAIDSequenceNumberKey)); 979 props->setObject(kAppleRAIDMemberIndexKey, getHeaderProperty(kAppleRAIDMemberIndexKey)); 980 props->setObject(kAppleRAIDChunkCountKey, getHeaderProperty(kAppleRAIDChunkCountKey)); 981 props->setObject(kAppleRAIDMemberTypeKey, getHeaderProperty(kAppleRAIDMemberTypeKey)); 982 983 props->setObject(kAppleRAIDSecondaryMetaDataSizeKey, getHeaderProperty(kAppleRAIDSecondaryMetaDataSizeKey)); 984 985 props->setObject(kAppleRAIDMemberStatusKey, getProperty(kAppleRAIDMemberStatusKey)); 986 props->setObject(kAppleRAIDRebuildStatus, getProperty(kAppleRAIDRebuildStatus)); 987 988 props->setObject(kIOBSDNameKey, getDiskName()); 989 990 if (isRAIDSet()) { 991 props->setObject(kAppleRAIDSetUUIDKey, getHeaderProperty(kAppleRAIDSetUUIDKey)); 992 } 993 994 if (arMemberState == kAppleRAIDMemberStateSpare) { 995 996 // broken members get spared, but we really want them to look broken here 997 // scan to see if this members UUID matches in the member list 998 const OSString * uuid = getUUID(); 999 OSArray * memberUUIDs = OSDynamicCast(OSArray, getHeaderProperty(kAppleRAIDMembersKey)); 1000 if (uuid && memberUUIDs) { 1001 UInt32 memberCount = memberUUIDs->getCount(); 1002 for (UInt32 i = 0; i < memberCount; i++) { 1003 if (uuid->isEqualTo(memberUUIDs->getObject(i))) { 1004 OSString * statusString = OSString::withCString(kAppleRAIDStatusFailed); 1005 if (statusString) props->setObject(kAppleRAIDMemberStatusKey, statusString); 1006 break; 1007 } 1008 } 1009 } 1010 1011 // after a rebuild fails and the drive is removed it comes back as a hot spare (technically correct) 1012 // DM doesn't like that and refuses future rebuilds on it. so just make it look broken. 1013 bool autoRebuild = OSDynamicCast(OSBoolean, getHeaderProperty(kAppleRAIDSetAutoRebuildKey)) == kOSBooleanTrue; 1014 if (!autoRebuild) { 1015 OSString * statusString = OSString::withCString(kAppleRAIDStatusFailed); 1016 if (statusString) props->setObject(kAppleRAIDMemberStatusKey, statusString); 1017 } 1018 } 1019 1020 return props; 1021} 1022 1023 1024//*************************************************************************************************************** 1025//*************************************************************************************************************** 1026//*************************************************************************************************************** 1027 1028 1029bool AppleRAIDMember::changeMemberState(UInt32 newState, bool force) 1030{ 1031 bool swapState = false; 1032 const char *newStatus = "bogus"; 1033 1034#ifdef DEBUG 1035 const char *oldStatus = "not set"; 1036 OSString *oldStatusString = OSDynamicCast(OSString, getProperty(kAppleRAIDMemberStatusKey)); 1037 if (oldStatusString) oldStatus = oldStatusString->getCStringNoCopy(); 1038#endif 1039 1040 if (arMemberState == newState) return true; 1041 1042 switch (newState) { 1043 1044 case kAppleRAIDMemberStateBroken : // 0 1045 swapState = true; 1046 newStatus = kAppleRAIDStatusFailed; 1047 break; 1048 1049 case kAppleRAIDMemberStateSpare : // 1 1050 swapState = arMemberState <= kAppleRAIDMemberStateClosed; 1051 newStatus = kAppleRAIDStatusSpare; 1052 break; 1053 1054 case kAppleRAIDMemberStateClosed : // 2 1055 swapState = arMemberState >= kAppleRAIDMemberStateClosing; 1056 newStatus = kAppleRAIDStatusOnline; 1057 break; 1058 1059 case kAppleRAIDMemberStateClosing : // 3 1060 swapState = arMemberState >= kAppleRAIDMemberStateClosing; 1061 newStatus = kAppleRAIDStatusOnline; 1062 break; 1063 1064 case kAppleRAIDMemberStateRebuilding : // 4 1065 swapState = arMemberState == kAppleRAIDMemberStateSpare; 1066 newStatus = kAppleRAIDStatusRebuilding; 1067 break; 1068 1069 case kAppleRAIDMemberStateOpen : // 5 1070 newStatus = kAppleRAIDStatusOnline; 1071 if (arMemberState == kAppleRAIDMemberStateRebuilding) break; // leave as is 1072 swapState = arMemberState >= kAppleRAIDMemberStateClosed; 1073 break; 1074 } 1075 1076 if (swapState) { 1077 1078 IOLog2("AppleRAIDMember::changeMemberState(%p) from %u (%s) to %u (%s) isSet = %s.\n", 1079 this, (uint32_t)arMemberState, oldStatus, (uint32_t)newState, newStatus, isRAIDSet() ? "yes":"no"); 1080 arMemberState = newState; 1081 1082 } else { 1083 1084#ifdef DEBUG 1085 if (arMemberState != newState) { 1086 IOLog1("AppleRAIDMember::changeMemberState(%p) %s from %u (%s) to %u (%s) isSet = %s.\n", 1087 this, force ? "FORCED" : "FAILED", (uint32_t)arMemberState, oldStatus, (uint32_t)newState, newStatus, isRAIDSet() ? "yes":"no"); 1088 } 1089#endif 1090 if (force) arMemberState = newState; 1091 } 1092 1093 if ((swapState || force) && isRAIDMember()) setProperty(kAppleRAIDMemberStatusKey, newStatus); 1094 1095 return swapState; 1096} 1097 1098 1099