|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1998-2000 Apple Computer, 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: #include <dev/disk.h> // (DKIOCGETBLOCKSIZE, ...) ! 24: #include <mach/vm_types.h> // (mach/vm_region.h, ...) ! 25: #include <mach/vm_region.h> // (VM_REGION_BASIC_INFO, ...) ! 26: #include <miscfs/devfs/devfs.h> // (devfs_make_node, ...) ! 27: #include <sys/buf.h> // (struct buf, ...) ! 28: #include <sys/conf.h> // (bdevsw_add, ...) ! 29: #include <sys/fcntl.h> // (FWRITE, ...) ! 30: #include <sys/ioccom.h> // (IOCGROUP, ...) ! 31: #include <sys/stat.h> // (S_ISBLK, ...) ! 32: #include <sys/uio.h> // (struct uio, ...) ! 33: #include <IOKit/assert.h> ! 34: #include <IOKit/IOBSD.h> ! 35: #include <IOKit/IODeviceTreeSupport.h> ! 36: #include <IOKit/IOLib.h> ! 37: #include <IOKit/IOMemoryDescriptor.h> ! 38: #include <IOKit/IOMessage.h> ! 39: #include <IOKit/storage/IODrive.h> ! 40: #include <IOKit/storage/IOMedia.h> ! 41: #include <IOKit/storage/IOMediaBSDClient.h> ! 42: ! 43: #define super IOService ! 44: OSDefineMetaClassAndStructors(IOMediaBSDClient, IOService) ! 45: ! 46: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 47: ! 48: static IOMediaBSDClient * gIOMediaBSDClient = 0; ! 49: ! 50: const signed kMajor = 14; // (bsd interface [b|c]devsw major) ! 51: const unsigned kMinorsGrowCount = 16; // (entries to add on table growth) ! 52: const unsigned kMinorsMaxCount = 1 << 24; // (maximum entries; 24-bit minor) ! 53: const unsigned kAnchorsGrowCount = 2; // (entries to add on table growth) ! 54: const unsigned kAnchorsMaxCount = kMinorsMaxCount; // (maximum entries) ! 55: ! 56: #define kMsgBadWhole "%s: Peer whole media \"%s\" is not allowed.", getName() ! 57: #define kMsgNoWhole "%s: No whole media found for media \"%s\".\n", getName() ! 58: #define kMsgNoLocation "%s: No location is found for media \"%s\".\n", getName() ! 59: ! 60: #define IOMEDIABSDCLIENT_IOSTAT_SUPPORT // (enable iostat support for bsd) ! 61: ! 62: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 63: ! 64: extern "C" ! 65: { ! 66: int dkclose(dev_t dev, int flags, int devtype, struct proc *); ! 67: int dkioctl(dev_t dev, u_long cmd, caddr_t data, int, struct proc *); ! 68: int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int, struct proc *); ! 69: int dkopen(dev_t dev, int flags, int devtype, struct proc *); ! 70: int dkread(dev_t dev, struct uio * uio, int flags); ! 71: int dksize(dev_t dev); ! 72: void dkstrategy(struct buf * bp); ! 73: int dkwrite(dev_t dev, struct uio * uio, int flags); ! 74: } // extern "C" ! 75: ! 76: static struct bdevsw bdevswFunctions = ! 77: { ! 78: /* d_open */ dkopen, ! 79: /* d_close */ dkclose, ! 80: /* d_strategy */ dkstrategy, ! 81: /* d_ioctl */ dkioctl_bdev, ! 82: /* d_dump */ eno_dump, ! 83: /* d_psize */ dksize, ! 84: /* d_type */ D_DISK ! 85: }; ! 86: ! 87: struct cdevsw cdevswFunctions = ! 88: { ! 89: /* d_open */ dkopen, ! 90: /* d_close */ dkclose, ! 91: /* d_read */ dkread, ! 92: /* d_write */ dkwrite, ! 93: /* d_ioctl */ dkioctl, ! 94: /* d_stop */ eno_stop, ! 95: /* d_reset */ eno_reset, ! 96: /* d_ttys */ 0, ! 97: /* d_select */ eno_select, ! 98: /* d_mmap */ eno_mmap, ! 99: /* d_strategy */ eno_strat, ! 100: /* d_getc */ eno_getc, ! 101: /* d_putc */ eno_putc, ! 102: /* d_type */ D_TAPE ! 103: }; ! 104: ! 105: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 106: ! 107: struct dio { dev_t dev; struct uio * uio; }; ! 108: ! 109: typedef void * dkr_t; /* dkreadwrite request */ ! 110: typedef enum { DKRTYPE_BUF, DKRTYPE_DIO } dkrtype_t; ! 111: ! 112: int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype); ! 113: void dkreadwritecompletion(void *, void *, IOReturn, UInt64); ! 114: ! 115: #define get_kernel_task() kernel_task ! 116: #define get_user_task() current_task() ! 117: ! 118: #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT ! 119: #include <sys/dkstat.h> ! 120: IODrive * dk_drive[DK_NDRIVE]; ! 121: #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT ! 122: ! 123: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 124: ! 125: const UInt32 kInvalidAnchorID = (UInt32) (-1); ! 126: ! 127: struct AnchorSlot ! 128: { ! 129: UInt32 isAssigned:1, // (anchor slot is occupied) ! 130: isObsolete:1; // (anchor slot is to be removed once refs gone) ! 131: ! 132: IOService * anchor; // (anchor object) ! 133: IONotifier * notifier; // (anchor termination notification, post-stop) ! 134: }; ! 135: ! 136: class AnchorTable ! 137: { ! 138: protected: ! 139: AnchorSlot * _table; ! 140: UInt32 _tableCount; ! 141: UInt32 _tableGrowCount; ! 142: UInt32 _tableMaxCount; ! 143: ! 144: static IOReturn anchorChange(void *, void *, UInt32, ! 145: IOService *, void *, vm_size_t); ! 146: ! 147: public: ! 148: AnchorTable(UInt32 growCount, UInt32 maxCount); ! 149: ~AnchorTable(); ! 150: ! 151: UInt32 insert(IOService * anchor); ! 152: UInt32 locate(IOService * anchor); ! 153: void obsolete(UInt32 anchorID); ! 154: void remove(UInt32 anchorID); ! 155: ! 156: bool isObsolete(UInt32 anchorID); ! 157: }; ! 158: ! 159: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 160: ! 161: const UInt32 kInvalidMinorID = (UInt32) (-1); ! 162: ! 163: struct MinorSlot ! 164: { ! 165: UInt32 isAssigned:1, // (minor slot is occupied) ! 166: isEjecting:1, // (minor slot is in eject flux, needs close) ! 167: isObsolete:1; // (minor slot is in eject flux, needs removal) ! 168: ! 169: UInt32 anchorID; // (minor's associated anchor ID) ! 170: IOMedia * media; // (minor's media object) ! 171: char * name; // (minor's name, private allocation space) ! 172: ! 173: UInt64 bdevBlockSize; // (block device's preferred block size) ! 174: void * bdevNode; // (block device's devfs node) ! 175: UInt32 bdevOpen:1, // (block device's open flag) ! 176: bdevWriter:1; // (block device's open writer flag) ! 177: ! 178: void * cdevNode; // (character device's devfs node) ! 179: UInt32 cdevOpen:1, // (character device's open flag) ! 180: cdevWriter:1; // (character device's open writer flag) ! 181: }; ! 182: ! 183: class MinorTable ! 184: { ! 185: protected: ! 186: MinorSlot * _table; ! 187: UInt32 _tableCount; ! 188: UInt32 _tableGrowCount; ! 189: UInt32 _tableMaxCount; ! 190: ! 191: public: ! 192: MinorTable(UInt32 growCount, UInt32 maxCount); ! 193: ~MinorTable(); ! 194: ! 195: UInt32 insert(IOMedia * media, UInt32 anchorID, char * slicePath); ! 196: UInt32 locate(IOMedia * media); ! 197: void obsolete(UInt32 minorID); ! 198: void remove(UInt32 minorID); ! 199: ! 200: bool isObsolete(UInt32 minorID); ! 201: ! 202: MinorSlot * getMinor(UInt32 minorID); ! 203: ! 204: UInt32 getOpenCountForAnchorID(UInt32 anchorID); ! 205: IOMedia * getWholeMediaAtAnchorID(UInt32 anchorID); ! 206: bool hasReferencesToAnchorID(UInt32 anchorID); ! 207: }; ! 208: ! 209: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 210: ! 211: bool IOMediaBSDClient::init(OSDictionary * properties = 0) ! 212: { ! 213: // ! 214: // Initialize this object's minimal state. ! 215: // ! 216: ! 217: if ( super::init(properties) == false ) return false; ! 218: ! 219: _anchors = new AnchorTable(kAnchorsGrowCount, kAnchorsMaxCount); ! 220: _bdevswInstalled = false; ! 221: _cdevswInstalled = false; ! 222: _minors = new MinorTable(kMinorsGrowCount, kMinorsMaxCount); ! 223: _notifier = 0; ! 224: ! 225: if ( _anchors == 0 || _minors == 0 ) return false; ! 226: ! 227: return true; ! 228: } ! 229: ! 230: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 231: ! 232: void IOMediaBSDClient::free() ! 233: { ! 234: // ! 235: // Free all of this object's outstanding resources. ! 236: // ! 237: ! 238: if ( _notifier ) _notifier->remove(); ! 239: if ( _cdevswInstalled ) cdevsw_remove(kMajor, &cdevswFunctions); ! 240: if ( _bdevswInstalled ) bdevsw_remove(kMajor, &bdevswFunctions); ! 241: if ( _minors ) delete _minors; ! 242: if ( _anchors ) delete _anchors; ! 243: ! 244: super::free(); ! 245: } ! 246: ! 247: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 248: ! 249: bool IOMediaBSDClient::start(IOService * provider) ! 250: { ! 251: // ! 252: // This method is called once we have been attached to the provider object. ! 253: // ! 254: ! 255: assert(gIOMediaBSDClient == 0); ! 256: ! 257: // Ask our superclass' opinion. ! 258: ! 259: if ( super::start(provider) == false ) return false; ! 260: ! 261: // Establish a global reference to this instance. ! 262: ! 263: gIOMediaBSDClient = this; ! 264: ! 265: // Install bdevsw and cdevsw functions. ! 266: ! 267: _bdevswInstalled = (bdevsw_add(kMajor, &bdevswFunctions) == kMajor); ! 268: _cdevswInstalled = (cdevsw_add(kMajor, &cdevswFunctions) == kMajor); ! 269: ! 270: if ( _bdevswInstalled == false && _cdevswInstalled == false ) return false; ! 271: ! 272: // Create a notification handler for media arrival. We ask for a priority ! 273: // of ten to ensure that we are notified ahead of other interested clients ! 274: // (with a default priority of zero), so that we can place the BSD-related ! 275: // properties on the media object that they might need in time. ! 276: ! 277: _notifier = addNotification( /* type */ gIOFirstPublishNotification, ! 278: /* description */ serviceMatching("IOMedia"), ! 279: /* action */ mediaHasArrived, ! 280: /* target */ this, ! 281: /* parameter */ 0, ! 282: /* priority */ 10 ); ! 283: ! 284: if ( _notifier == 0 ) return false; ! 285: ! 286: // Register this object so it can be found via notification requests. It is ! 287: // not being registered to have I/O Kit attempt to have drivers match on it, ! 288: // which is the reason most other services are registered -- that's not the ! 289: // intention of this registerService call. ! 290: ! 291: registerService(); ! 292: ! 293: return true; ! 294: } ! 295: ! 296: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 297: ! 298: void IOMediaBSDClient::stop(IOService * provider) ! 299: { ! 300: // ! 301: // This method is called before we are detached from the provider object. ! 302: // ! 303: ! 304: IOMedia * media = (IOMedia *) provider; ! 305: UInt32 minorID = 0; ! 306: ! 307: // Disable access to tables, matching, opens, closes, and terminations. ! 308: ! 309: gIOMediaBSDClient->lockForArbitration(); ! 310: ! 311: // Find the minor assigned to this media. ! 312: ! 313: minorID = _minors->locate(media); ! 314: assert(minorID != kInvalidMinorID); ! 315: ! 316: // State our assumptions. ! 317: ! 318: assert(media->isOpen() == false); ! 319: ! 320: // Remove the minor from the minor table, unless it's still in flux (which ! 321: // means an open on the bdevsw/cdevsw switch is still outstanding: the one ! 322: // that sent the eject ioctl), in which case we mark the minor as obsolete ! 323: // for later removal. ! 324: ! 325: if ( _minors->getMinor(minorID)->isEjecting ) // (is minor in flux?) ! 326: { ! 327: assert(_minors->isObsolete(minorID) == false); ! 328: ! 329: _minors->obsolete(minorID); ! 330: } ! 331: else ! 332: { ! 333: assert(_minors->getMinor(minorID)->bdevOpen == false); ! 334: assert(_minors->getMinor(minorID)->cdevOpen == false); ! 335: ! 336: _minors->remove(minorID); ! 337: } ! 338: ! 339: // Enable access to tables, matching, opens, closes, and terminations. ! 340: ! 341: gIOMediaBSDClient->unlockForArbitration(); ! 342: ! 343: // Call upon the superclass to finish its work. ! 344: ! 345: super::stop(media); ! 346: } ! 347: ! 348: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 349: ! 350: bool IOMediaBSDClient::mediaHasArrived( void * /* target */, ! 351: void * /* parameter */, ! 352: IOService * service ) ! 353: { ! 354: // ! 355: // Notification handler for media arrivals. ! 356: // ! 357: ! 358: IOMedia * media = OSDynamicCast(IOMedia, service); ! 359: bool success = false; ! 360: ! 361: assert(gIOMediaBSDClient); ! 362: ! 363: // Attach the media-bsd-client object as a client of the new media object. ! 364: ! 365: if ( media && gIOMediaBSDClient->attach(media) ) ! 366: { ! 367: // Disable access to tables, matching, opens, closes, and terminations. ! 368: ! 369: gIOMediaBSDClient->lockForArbitration(); ! 370: ! 371: // Create bdevsw and cdevsw nodes for the new media object. ! 372: ! 373: success = gIOMediaBSDClient->createNodes(media); ! 374: ! 375: // Enable access to tables, matching, opens, closes, and terminations. ! 376: ! 377: gIOMediaBSDClient->unlockForArbitration(); ! 378: ! 379: // Detach the media-bsd-client object from the media object on error. ! 380: ! 381: if (success == false) gIOMediaBSDClient->detach(media); ! 382: } ! 383: ! 384: return true; // (meaningless return value) ! 385: } ! 386: ! 387: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 388: ! 389: IOMedia * IOMediaBSDClient::getWholeMedia( IOMedia * media, ! 390: UInt32 * slicePathSize = 0, ! 391: char * slicePath = 0 ) ! 392: { ! 393: // ! 394: // Find the whole media that roots this media tree. A null return value ! 395: // indicates no whole media was found or a malformed tree was detected. ! 396: // ! 397: // If slicePathSize is non-zero, the size required to fit the slice path ! 398: // (including the zero terminator) is passed back as a result. ! 399: // ! 400: // If slicePathSize and slicePath are both non-zero, the slice path will ! 401: // be written into the slicePath buffer. The value slicePathSize points ! 402: // to must be the size of the slicePath buffer, which is used for sanity ! 403: // checking in this method. ! 404: // ! 405: ! 406: UInt32 depth = 1; ! 407: UInt32 position = sizeof('\0'); ! 408: IOService * service = 0; ! 409: ! 410: assert(slicePath == 0 || slicePathSize != 0); ! 411: ! 412: // Search the registry for the parent whole media for this media. ! 413: ! 414: for ( service = media; service; service = service->getProvider() ) ! 415: { ! 416: if ( OSDynamicCast(IOMedia, service) ) // (is it a media?) ! 417: { ! 418: if ( ((IOMedia *)service)->isWhole() ) // (is it a whole media?) ! 419: { ! 420: if ( slicePath ) // (are we building the slice path?) ! 421: { ! 422: slicePath[*slicePathSize - 1] = 0; // (zero terminate path) ! 423: ! 424: if ( position < *slicePathSize ) // (need to move path?) ! 425: { ! 426: memmove( slicePath, // (move path to start of buffer) ! 427: slicePath + (*slicePathSize - position), ! 428: position ); ! 429: } ! 430: } ! 431: else if ( slicePathSize ) // (report size req'd for slice path?) ! 432: { ! 433: *slicePathSize = position; ! 434: } ! 435: ! 436: return (IOMedia *)service; // (return the whole media) ! 437: } ! 438: ! 439: // Determine whether this non-whole media has a location value. It ! 440: // must, by definition of a non-whole media, but if it does not, we ! 441: // should return an error condition. ! 442: ! 443: const char * location = service->getLocation(); ! 444: ! 445: if ( location == 0 ) // (no location on non-whole media?) ! 446: { ! 447: if ( service == media ) IOLog(kMsgNoLocation, media->getName()); ! 448: return 0; ! 449: } ! 450: ! 451: // Otherwise, it's a valid non-whole media: we compute the required ! 452: // size for the slice path or build the slice path, if so requested. ! 453: // Note that the slice path is built backwards from the ends of the ! 454: // supplied buffer to the beginning of the buffer. ! 455: ! 456: position += sizeof('s') + strlen(location); ! 457: ! 458: if ( slicePath ) // (build the slice path?) ! 459: { ! 460: char * path = slicePath + *slicePathSize - position; ! 461: ! 462: if ( position > *slicePathSize ) { assert(0); return 0; } ! 463: ! 464: *path = 's'; ! 465: strncpy(path + sizeof('s'), location, strlen(location)); ! 466: } ! 467: ! 468: depth += 1; ! 469: } ! 470: } ! 471: ! 472: // If we've fallen through, then the whole media was never found. ! 473: ! 474: if ( depth == 1 ) IOLog(kMsgNoWhole, media->getName()); ! 475: return 0; ! 476: } ! 477: ! 478: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 479: ! 480: bool IOMediaBSDClient::createNodes(IOMedia * media) ! 481: { ! 482: // ! 483: // Create bdevsw and cdevsw nodes for the given media object. ! 484: // ! 485: // This method assumes that the arbitration lock is held. ! 486: // ! 487: ! 488: IOService * anchor; ! 489: UInt32 anchorID; ! 490: bool anchorNew = false; ! 491: UInt32 minorID; ! 492: char * slicePath = 0; ! 493: UInt32 slicePathSize; ! 494: IOMedia * whole; ! 495: ! 496: // ! 497: // Find the anchor that roots this media tree. The anchor is defined as the ! 498: // parent of the whole media that roots this media tree. It is an important ! 499: // object to us because this object stays in place when media is ejected, so ! 500: // we can continue to maintain the "unit number" of the "drive" such that if ! 501: // media is re-inserted, it will show up under the same "unit number". You ! 502: // can think of the typical anchor as being the drive, if it helps, although ! 503: // it could be one of many other kinds of objects (eg. a RAID scheme). ! 504: // ! 505: ! 506: whole = getWholeMedia(media, &slicePathSize); ! 507: if ( whole == 0 ) return false; ! 508: ! 509: anchor = whole->getProvider(); ! 510: if ( anchor == 0 ) return false; ! 511: ! 512: // ! 513: // Determine whether the anchor already exists in the anchor table (obsolete ! 514: // occurences are skipped in the search, as appropriate, since those anchor ! 515: // IDs are to be removed soon). If the anchor does not exist, insert it into ! 516: // anchor table. ! 517: // ! 518: ! 519: anchorID = _anchors->locate(anchor); ! 520: ! 521: if ( anchorID != kInvalidAnchorID ) ! 522: { ! 523: // ! 524: // The anchor does exist in the table, however we've got more to check. ! 525: // ! 526: // We need to ensure that the whole media associated with this anchor is ! 527: // the same as ours. If it is, all is well. If it isn't, then there is ! 528: // still a chance all is well. It is possible to have an old media tree ! 529: // still associated with the anchor: the tree would be inactive, but not ! 530: // yet terminated (this can happen on forced termination of a media tree ! 531: // with oustanding opens, since the close must come before the terminate ! 532: // can proceed; it can happen even in normal eject conditions should the ! 533: // media be immediately reinserted when the termination on the old tree, ! 534: // which is asynchronous, is still chugging along on another thread). In ! 535: // case the tree is inactive, we mark the anchorID as obsolete and use a ! 536: // new anchorID. In the case the tree is not inactive, then we've got a ! 537: // problem and we must bail out. ! 538: // ! 539: // A few additional notes: ! 540: // ! 541: // o if the whole media is indeed the same as the one in our tables, we ! 542: // need not check that it is active, because by virtue of the fact we ! 543: // got a new media notification on the same tree, we know for sure it ! 544: // cannot be in the inactive state. ! 545: // ! 546: // o if the whole media is not in our tables, it is quite possible that ! 547: // some child non-whole media from the old media tree is still around ! 548: // as terminations work from the bottom (whole media) up (to leaves), ! 549: // and the asynchronous termination thread is still not done chugging ! 550: // through the medias on the old tree. We use a new anchorID in this ! 551: // case. ! 552: // ! 553: ! 554: IOMedia * wholeInTable = _minors->getWholeMediaAtAnchorID(anchorID); ! 555: ! 556: if ( wholeInTable == 0 ) // (is an existing whole media in our tables?) ! 557: { ! 558: if ( _minors->hasReferencesToAnchorID(anchorID) ) // (any medias?) ! 559: { ! 560: _anchors->obsolete(anchorID); // (obsolete old anchor ID) ! 561: anchorID = kInvalidAnchorID; // ( request new anchor ID) ! 562: } // (else, all is well) ! 563: } ! 564: else if ( whole != wholeInTable ) // (old whole media not same as new?) ! 565: { ! 566: if ( wholeInTable->isInactive() ) // (is it inactive/terminating?) ! 567: { ! 568: _anchors->obsolete(anchorID); // (obsolete old anchor ID) ! 569: anchorID = kInvalidAnchorID; // ( request new anchor ID) ! 570: } ! 571: else // (peer active whole medias detected, log error) ! 572: { ! 573: if ( whole == media ) IOLog(kMsgBadWhole, whole->getName()); ! 574: return false; ! 575: } ! 576: } // (else, all is well) ! 577: } ! 578: ! 579: if ( anchorID == kInvalidAnchorID ) ! 580: { ! 581: anchorID = _anchors->insert(anchor); // (get new anchor ID) ! 582: if ( anchorID == kInvalidAnchorID ) return false; ! 583: anchorNew = true; ! 584: } ! 585: ! 586: // ! 587: // Allocate space for and build the slice path for the device node names. ! 588: // ! 589: ! 590: slicePath = (char *) IOMalloc(slicePathSize); ! 591: if ( slicePath == 0 ) goto createNodesErr; ! 592: ! 593: whole = getWholeMedia(media, &slicePathSize, slicePath); ! 594: assert(whole); ! 595: ! 596: // ! 597: // Insert the new media into our minor table (we're almost done :-). ! 598: // ! 599: ! 600: minorID = _minors->insert(media, anchorID, slicePath); ! 601: if ( minorID == kInvalidMinorID ) goto createNodesErr; ! 602: ! 603: // ! 604: // Create the required properties on the media. ! 605: // ! 606: ! 607: media->setProperty(kIOBSDName, _minors->getMinor(minorID)->name); ! 608: media->setProperty(kIOBSDUnit, anchorID, 32); // ("BSD Unit" ) ! 609: media->setProperty(kIOBSDMajor, kMajor, 32); // ("BSD Major") ! 610: media->setProperty(kIOBSDMinor, minorID, 32); // ("BSD Minor") ! 611: ! 612: // ! 613: // Clean up outstanding resources. ! 614: // ! 615: ! 616: IOFree(slicePath, slicePathSize); ! 617: ! 618: return true; // (success) ! 619: ! 620: createNodesErr: ! 621: ! 622: if (anchorNew) _anchors->remove(anchorID); ! 623: if (slicePath) IOFree(slicePath, slicePathSize); ! 624: ! 625: return false; // (failure) ! 626: } ! 627: ! 628: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 629: ! 630: AnchorTable * IOMediaBSDClient::getAnchors() ! 631: { ! 632: // ! 633: // Obtain the table of anchors. ! 634: // ! 635: ! 636: return _anchors; ! 637: } ! 638: ! 639: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 640: ! 641: MinorTable * IOMediaBSDClient::getMinors() ! 642: { ! 643: // ! 644: // Obtain the table of anchors. ! 645: // ! 646: ! 647: return _minors; ! 648: } ! 649: ! 650: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 651: ! 652: MinorSlot * IOMediaBSDClient::getMinor(UInt32 minorID) ! 653: { ! 654: // ! 655: // Obtain information for the specified minor ID. ! 656: // ! 657: ! 658: return _minors->getMinor(minorID); ! 659: } ! 660: ! 661: // ============================================================================= ! 662: // BSD Functions ! 663: ! 664: int dkopen(dev_t dev, int flags, int devtype, struct proc *) ! 665: { ! 666: // ! 667: // dkopen opens the device (called on each open). ! 668: // ! 669: ! 670: int error; ! 671: IOStorageAccess level; ! 672: MinorSlot * minor; ! 673: ! 674: assert(gIOMediaBSDClient); ! 675: assert(S_ISBLK(devtype) || S_ISCHR(devtype)); ! 676: ! 677: gIOMediaBSDClient->lockForArbitration(); // (disable access) ! 678: ! 679: assert(gIOMediaBSDClient->getMinors()); ! 680: ! 681: error = 0; ! 682: level = (flags & FWRITE) ? kAccessReaderWriter : kAccessReader; ! 683: minor = gIOMediaBSDClient->getMinor(minor(dev)); ! 684: ! 685: // ! 686: // Process the open. ! 687: // ! 688: ! 689: if ( minor == 0 ) // (is minor valid?) ! 690: { ! 691: error = ENXIO; ! 692: } ! 693: else if ( minor->isEjecting ) // (is minor in flux?) ! 694: { ! 695: error = EBUSY; ! 696: } ! 697: else if ( (flags & FWRITE) ) // (is client a writer?) ! 698: { ! 699: if ( minor->bdevWriter || minor->cdevWriter ) level = kAccessNone; ! 700: } ! 701: else // (is client a reader?) ! 702: { ! 703: if ( minor->bdevOpen || minor->cdevOpen ) level = kAccessNone; ! 704: } ! 705: ! 706: if ( error == 0 && level != kAccessNone ) // (issue the open (or upgrade)?) ! 707: { ! 708: if ( minor->media->open(gIOMediaBSDClient, 0, level) == false ) // (go) ! 709: { ! 710: error = EBUSY; ! 711: } ! 712: } ! 713: ! 714: if ( error == 0 ) // (update state) ! 715: { ! 716: if ( S_ISBLK(devtype) ) ! 717: { ! 718: minor->bdevOpen = true; ! 719: if ( (flags & FWRITE) ) minor->bdevWriter = true; ! 720: } ! 721: else ! 722: { ! 723: minor->cdevOpen = true; ! 724: if ( (flags & FWRITE) ) minor->cdevWriter = true; ! 725: } ! 726: } ! 727: ! 728: gIOMediaBSDClient->unlockForArbitration(); // (enable access) ! 729: ! 730: return error; ! 731: } ! 732: ! 733: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 734: ! 735: int dkclose(dev_t dev, int /* flags */, int devtype, struct proc *) ! 736: { ! 737: // ! 738: // dkclose closes the device (called on last close). ! 739: // ! 740: ! 741: MinorSlot * minor; ! 742: bool wasWriter; ! 743: ! 744: assert(S_ISBLK(devtype) || S_ISCHR(devtype)); ! 745: ! 746: gIOMediaBSDClient->lockForArbitration(); // (disable access) ! 747: ! 748: minor = gIOMediaBSDClient->getMinor(minor(dev)); ! 749: wasWriter = (minor->bdevWriter || minor->cdevWriter); ! 750: ! 751: if ( S_ISBLK(devtype) ) // (update state) ! 752: { ! 753: minor->bdevBlockSize = minor->media->getPreferredBlockSize(); ! 754: minor->bdevOpen = false; ! 755: minor->bdevWriter = false; ! 756: } ! 757: else ! 758: { ! 759: minor->cdevOpen = false; ! 760: minor->cdevWriter = false; ! 761: } ! 762: ! 763: if ( minor->isEjecting ) // (is minor in flux?) ! 764: { ! 765: // ! 766: // We've determined that the specified minor is in ejection flux. This ! 767: // means we are in a state where the media object has been closed, only ! 768: // the device node is still open. This happens to the minor subsequent ! 769: // to a DKIOCEJECT ioctl -- this close resets the flux state to normal. ! 770: // ! 771: ! 772: minor->isEjecting = false; ! 773: ! 774: // If this minor is marked as obsolete, then we've already received the ! 775: // media's termination notification (stop method), but the minor is yet ! 776: // to be removed from the table -- remove it now. ! 777: ! 778: assert(minor->bdevOpen == false); ! 779: assert(minor->cdevOpen == false); ! 780: ! 781: if ( minor->isObsolete ) ! 782: gIOMediaBSDClient->getMinors()->remove(minor(dev)); ! 783: } ! 784: else if ( !minor->bdevOpen && !minor->cdevOpen ) ! 785: { ! 786: // ! 787: // We communicate the close down to the media object once all opens are ! 788: // gone, on both the block and character device nodes. ! 789: // ! 790: ! 791: minor->media->close(gIOMediaBSDClient); // (go) ! 792: } ! 793: else if ( !minor->bdevWriter && !minor->cdevWriter && wasWriter ) ! 794: { ! 795: // ! 796: // We communicate a downgrade down to the media object once all writers ! 797: // are gone and while readers still exist. ! 798: // ! 799: ! 800: bool success; ! 801: success = minor->media->open(gIOMediaBSDClient, 0, kAccessReader); ! 802: assert(success); // (should never fail, unless avoided deadlock) ! 803: } ! 804: ! 805: gIOMediaBSDClient->unlockForArbitration(); // (enable access) ! 806: ! 807: return 0; ! 808: } ! 809: ! 810: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 811: ! 812: int dkread(dev_t dev, struct uio * uio, int /* flags */) ! 813: { ! 814: // ! 815: // dkread reads data from a device. ! 816: // ! 817: ! 818: struct dio dio = { dev, uio }; ! 819: ! 820: return dkreadwrite(&dio, DKRTYPE_DIO); ! 821: } ! 822: ! 823: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 824: ! 825: int dkwrite(dev_t dev, struct uio * uio, int /* flags */) ! 826: { ! 827: // ! 828: // dkwrite writes data to a device. ! 829: // ! 830: ! 831: struct dio dio = { dev, uio }; ! 832: ! 833: return dkreadwrite(&dio, DKRTYPE_DIO); ! 834: } ! 835: ! 836: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 837: ! 838: void dkstrategy(struct buf * bp) ! 839: { ! 840: // ! 841: // dkstrategy starts an asynchronous read or write operation. It returns ! 842: // to the caller as soon as the operation is queued, and completes it via ! 843: // the biodone function. ! 844: // ! 845: ! 846: dkreadwrite(bp, DKRTYPE_BUF); ! 847: } ! 848: ! 849: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 850: ! 851: int dkioctl(dev_t dev, u_long cmd, caddr_t data, int, struct proc *) ! 852: { ! 853: // ! 854: // dkioctl performs operations other than a read or write. ! 855: // ! 856: ! 857: int error = 0; ! 858: MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev)); ! 859: ! 860: if ( minor->isEjecting ) return EBADF; // (is minor in flux?) ! 861: ! 862: // ! 863: // Process the ioctl. ! 864: // ! 865: ! 866: switch ( cmd ) ! 867: { ! 868: case DKIOCGETBLOCKSIZE: // getBlockSize(int * out); ! 869: { ! 870: // ! 871: // This ioctl returns the preferred block size of the media object. ! 872: // ! 873: ! 874: *(int *)data = (int) minor->media->getPreferredBlockSize(); ! 875: ! 876: } break; ! 877: ! 878: case DKIOCGETBLOCKCOUNT: // getBlockCount(int * out); ! 879: { ! 880: // ! 881: // This ioctl returns the size of the media object in blocks. The ! 882: // implied block size is returned by DKIOCGETBLOCKSIZE. ! 883: // ! 884: ! 885: if ( minor->media->getPreferredBlockSize() ) ! 886: *(int *)data = (int) ( minor->media->getSize() / ! 887: minor->media->getPreferredBlockSize() ); ! 888: else ! 889: *(int *)data = 0; ! 890: ! 891: } break; ! 892: ! 893: case DKIOCISFORMATTED: // isFormatted(int * out); ! 894: { ! 895: // ! 896: // This ioctl returns truth if the media object is formatted. ! 897: // ! 898: ! 899: *(int *)data = (int) minor->media->isFormatted(); ! 900: ! 901: } break; ! 902: ! 903: case DKIOCGETLOCATION: // getLocation(char[128] out); ! 904: { ! 905: // ! 906: // This ioctl returns the open firmware path for this media object. ! 907: // ! 908: ! 909: int l = sizeof(((struct drive_location *)data)->location); ! 910: char * p = ((struct drive_location *)data)->location; ! 911: ! 912: if ( minor->media->getPath(p, &l, gIODTPlane) && strchr(p, ':') ) ! 913: strcpy(p, strchr(p, ':') + 1); // (strip the plane name) ! 914: else ! 915: error = EINVAL; ! 916: ! 917: } break; ! 918: ! 919: case DKIOCEJECT: // eject(void); ! 920: { ! 921: // ! 922: // This ioctl asks that the media object be ejected from the drive. ! 923: // ! 924: ! 925: IODrive * drive; ! 926: MinorTable * minors; ! 927: ! 928: drive = OSDynamicCast(IODrive, minor->media->getProvider()); ! 929: minors = gIOMediaBSDClient->getMinors(); ! 930: ! 931: // Determine whether this media has a drive object as its parent. ! 932: ! 933: if ( drive == 0 ) { error = ENOTTY; break; } ! 934: ! 935: // Disable access to tables, matching, opens, closes, terminations. ! 936: ! 937: gIOMediaBSDClient->lockForArbitration(); ! 938: ! 939: // Determine whether there are other opens on the device nodes that ! 940: // are associated with this anchor -- the one valid open is the one ! 941: // that issued this eject. ! 942: ! 943: if ( minors->getOpenCountForAnchorID(minor->anchorID) > 1 ) ! 944: { ! 945: error = EBUSY; ! 946: ! 947: // Enable access to tables, matching, opens, closes, and so on. ! 948: ! 949: gIOMediaBSDClient->unlockForArbitration(); ! 950: } ! 951: else ! 952: { ! 953: // Mark this minor as being in ejection flux (which means are in ! 954: // a state where the media object has been closed but the device ! 955: // node is still open; we must reject all future accesses to the ! 956: // device node until it is closed; note that we do this both on ! 957: // success and failure of the ejection call). ! 958: ! 959: minor->isEjecting = true; ! 960: ! 961: // Enable access to tables, matching, opens, closes, and so on. ! 962: ! 963: gIOMediaBSDClient->unlockForArbitration(); ! 964: ! 965: // Close the media object, since IODrive semantics require that ! 966: // no opens exist when the ejection request in made. ! 967: ! 968: minor->media->close(gIOMediaBSDClient); ! 969: ! 970: // Eject the media. ! 971: ! 972: error = gIOMediaBSDClient->errnoFromReturn(drive->ejectMedia()); ! 973: } ! 974: ! 975: } break; ! 976: ! 977: default: ! 978: { ! 979: // ! 980: // A foreign ioctl was received. Log an error to the console. ! 981: // ! 982: ! 983: IOLog( "%s: ioctl(%s\'%c\',%d,%d) is unsupported.\n", ! 984: minor->name, ! 985: ((cmd & IOC_INOUT) == IOC_INOUT) ? ("_IOWR,") : ! 986: ( ((cmd & IOC_OUT) == IOC_OUT) ? ("_IOR,") : ! 987: ( ((cmd & IOC_IN) == IOC_IN) ? ("_IOW,") : ! 988: ( ((cmd & IOC_VOID) == IOC_VOID) ? ("_IO,") : "" ) ) ), ! 989: (char) IOCGROUP(cmd), ! 990: (int) (cmd & 0xff), ! 991: (int) IOCPARM_LEN(cmd) ); ! 992: ! 993: error = ENOTTY; ! 994: ! 995: } break; ! 996: } ! 997: ! 998: return error; // (return error status) ! 999: } ! 1000: ! 1001: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1002: ! 1003: int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int f, struct proc * proc) ! 1004: { ! 1005: // ! 1006: // dkioctl_bdev performs operations other than a read or write, specific to ! 1007: // the block device. ! 1008: // ! 1009: ! 1010: int error = 0; ! 1011: MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev)); ! 1012: ! 1013: if ( minor->isEjecting ) return EBADF; // (is minor in flux?) ! 1014: ! 1015: // ! 1016: // Process the ioctl. ! 1017: // ! 1018: ! 1019: switch ( cmd ) ! 1020: { ! 1021: case DKIOCGETBLOCKSIZE: // getBlockSize(int * out); ! 1022: { ! 1023: // ! 1024: // This ioctl returns the preferred (or overrided) block size of the ! 1025: // media object. ! 1026: // ! 1027: ! 1028: *(int *)data = (int) minor->bdevBlockSize; ! 1029: ! 1030: } break; ! 1031: ! 1032: case DKIOCSETBLOCKSIZE: // setBlockSize(int * in); ! 1033: { ! 1034: // ! 1035: // This ioctl overrides the block size for the media object, for the ! 1036: // duration of all block device opens at this minor. ! 1037: // ! 1038: ! 1039: if ( *(int *)data > 0 ) ! 1040: minor->bdevBlockSize = (UInt64) (*(int *)data); ! 1041: else ! 1042: error = EINVAL; ! 1043: ! 1044: } break; ! 1045: ! 1046: case DKIOCGETBLOCKCOUNT: // getBlockCount(int * out); ! 1047: { ! 1048: // ! 1049: // This ioctl returns the size of the media object in blocks. The ! 1050: // implied block size is returned by DKIOCGETBLOCKSIZE. ! 1051: // ! 1052: ! 1053: if ( minor->bdevBlockSize ) ! 1054: *(int *)data = (int) ( minor->media->getSize() / ! 1055: minor->bdevBlockSize ); ! 1056: else ! 1057: *(int *)data = 0; ! 1058: ! 1059: } break; ! 1060: ! 1061: default: ! 1062: { ! 1063: // ! 1064: // Call the common ioctl handler for all other ioctls. ! 1065: // ! 1066: ! 1067: error = dkioctl(dev, cmd, data, f, proc); ! 1068: ! 1069: } break; ! 1070: } ! 1071: ! 1072: return error; // (return error status) ! 1073: } ! 1074: ! 1075: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1076: ! 1077: int dksize(dev_t dev) ! 1078: { ! 1079: // ! 1080: // dksize returns the block size of the media. ! 1081: // ! 1082: // This is a departure from BSD 4.4's definition of this function, that is, ! 1083: // it will not return the size of the disk partition, as would be expected ! 1084: // in a BSD 4.4 implementation. ! 1085: // ! 1086: ! 1087: MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev)); ! 1088: ! 1089: if ( minor->isEjecting ) return 0; // (is minor in flux?) ! 1090: ! 1091: return (int) minor->bdevBlockSize; // (return block size) ! 1092: } ! 1093: ! 1094: // ============================================================================= ! 1095: // Support For BSD Functions ! 1096: ! 1097: inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype) ! 1098: { ! 1099: return (dkrtype == DKRTYPE_BUF) ! 1100: ? ((struct buf *)dkr)->b_dev ! 1101: : ((struct dio *)dkr)->dev; ! 1102: } ! 1103: ! 1104: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1105: ! 1106: inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype) ! 1107: { ! 1108: return (dkrtype == DKRTYPE_BUF) ! 1109: ? ((struct buf *)dkr)->b_bcount ! 1110: : ((struct dio *)dkr)->uio->uio_resid; ! 1111: } ! 1112: ! 1113: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1114: ! 1115: inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype) ! 1116: { ! 1117: if (dkrtype == DKRTYPE_BUF) ! 1118: { ! 1119: struct buf * bp = (struct buf *)dkr; ! 1120: MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(bp->b_dev)); ! 1121: ! 1122: return bp->b_blkno * minor->bdevBlockSize; ! 1123: } ! 1124: return ((struct dio *)dkr)->uio->uio_offset; ! 1125: } ! 1126: ! 1127: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1128: ! 1129: inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype) ! 1130: { ! 1131: return (dkrtype == DKRTYPE_BUF) ! 1132: ? ((((struct buf *)dkr)->b_flags & B_READ) == B_READ) ! 1133: : ((((struct dio *)dkr)->uio->uio_rw) == UIO_READ); ! 1134: } ! 1135: ! 1136: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1137: ! 1138: inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype) ! 1139: { ! 1140: return (dkrtype == DKRTYPE_BUF) ! 1141: ? true ! 1142: : false; ! 1143: } ! 1144: ! 1145: ! 1146: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1147: ! 1148: inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype) ! 1149: { ! 1150: return (dkrtype == DKRTYPE_BUF) ! 1151: ? false ! 1152: : true; ! 1153: } ! 1154: ! 1155: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1156: ! 1157: inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount) ! 1158: { ! 1159: if (dkrtype == DKRTYPE_BUF) ! 1160: ((struct buf *)dkr)->b_resid = ((struct buf *)dkr)->b_bcount - bcount; ! 1161: else ! 1162: ((struct dio *)dkr)->uio->uio_resid -= bcount; ! 1163: } ! 1164: ! 1165: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1166: ! 1167: inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status) ! 1168: { ! 1169: if (dkrtype == DKRTYPE_BUF) ! 1170: { ! 1171: struct buf * bp = (struct buf *)dkr; ! 1172: ! 1173: bp->b_error = gIOMediaBSDClient->errnoFromReturn(status); // (error?) ! 1174: bp->b_flags |= (status != kIOReturnSuccess) ? B_ERROR : 0; // (error?) ! 1175: biodone(bp); // (complete request) ! 1176: } ! 1177: } ! 1178: ! 1179: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1180: ! 1181: inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype) ! 1182: { ! 1183: if (dkrtype == DKRTYPE_BUF) ! 1184: { ! 1185: struct buf * bp = (struct buf *)dkr; ! 1186: ! 1187: return IOMemoryDescriptor::withAddress( // (single-range) ! 1188: (vm_address_t) bp->b_data, ! 1189: (vm_size_t) bp->b_bcount, ! 1190: (bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut, ! 1191: (bp->b_flags & B_PHYS) ? get_user_task() : get_kernel_task() ); ! 1192: } ! 1193: else ! 1194: { ! 1195: struct uio * uio = ((struct dio *)dkr)->uio; ! 1196: ! 1197: assert( sizeof(IOVirtualRange ) == sizeof(iovec ) ); ! 1198: assert( sizeof(IOVirtualRange::address) == sizeof(iovec::iov_base) ); ! 1199: assert( sizeof(IOVirtualRange::length ) == sizeof(iovec::iov_len ) ); ! 1200: ! 1201: return IOMemoryDescriptor::withRanges( // (multiple-range) ! 1202: (IOVirtualRange *) uio->uio_iov, ! 1203: (UInt32) uio->uio_iovcnt, ! 1204: (uio->uio_rw == UIO_READ ) ? kIODirectionIn : kIODirectionOut, ! 1205: (uio->uio_segflg != UIO_SYSSPACE) ? get_user_task() : get_kernel_task(), ! 1206: true ); ! 1207: } ! 1208: } ! 1209: ! 1210: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1211: ! 1212: int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype) ! 1213: { ! 1214: // ! 1215: // dkreadwrite performs a read or write operation. ! 1216: // ! 1217: ! 1218: IOMemoryDescriptor * buffer; ! 1219: register UInt64 byteCount; ! 1220: register UInt64 byteStart; ! 1221: UInt64 mediaSize; ! 1222: MinorSlot * minor; ! 1223: IOReturn status; ! 1224: ! 1225: minor = gIOMediaBSDClient->getMinor(minor(DKR_GET_DEV(dkr, dkrtype))); ! 1226: ! 1227: if ( minor->isEjecting ) // (is minor in flux?) ! 1228: { ! 1229: status = kIOReturnNoMedia; ! 1230: goto dkreadwriteErr; ! 1231: } ! 1232: ! 1233: if ( minor->media->isFormatted() == false ) // (is media unformatted?) ! 1234: { ! 1235: status = kIOReturnUnformattedMedia; ! 1236: goto dkreadwriteErr; ! 1237: } ! 1238: ! 1239: byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype); // (get byte count) ! 1240: byteStart = DKR_GET_BYTE_START(dkr, dkrtype); // (get byte start) ! 1241: mediaSize = minor->media->getSize(); // (get media size) ! 1242: ! 1243: // ! 1244: // We must reject non-block-aligned requests to conform to BSD semantics. ! 1245: // ! 1246: ! 1247: if ( DKR_IS_RAW(dkr, dkrtype) ) ! 1248: { ! 1249: UInt64 mediaBlockSize = minor->media->getPreferredBlockSize(); ! 1250: ! 1251: if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) ) ! 1252: { ! 1253: status = kIOReturnNotAligned; ! 1254: goto dkreadwriteErr; ! 1255: } ! 1256: } ! 1257: ! 1258: // ! 1259: // We must take in account that short reads and writes are not considered ! 1260: // errors under BSD semantics. We're to transfer as many bytes as can be ! 1261: // read or written from the medium and return no error. Also, reads that ! 1262: // start at (or perhaps past) the end-of-media are not considered errors, ! 1263: // even though no data is transferred, while writes at (or past) the end- ! 1264: // of-media do indeed return errors. This differs from IOMedia semantics ! 1265: // which is to fail the entire request without transferring a single byte ! 1266: // should it include for something past the end-of-media. We must adapt ! 1267: // the IOMedia semantics to look like BSD semantics here. ! 1268: // ! 1269: ! 1270: if ( byteStart >= mediaSize ) // (is start at or past the end-of-media?) ! 1271: { ! 1272: status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError; ! 1273: goto dkreadwriteErr; ! 1274: } ! 1275: ! 1276: // ! 1277: // Build a descriptor which describes the buffer involved in the transfer. ! 1278: // ! 1279: ! 1280: buffer = DKR_GET_BUFFER(dkr, dkrtype); ! 1281: ! 1282: if ( buffer == 0 ) // (no buffer?) ! 1283: { ! 1284: status = kIOReturnNoMemory; ! 1285: goto dkreadwriteErr; ! 1286: } ! 1287: ! 1288: // ! 1289: // Clip the transfer buffer should this be a short read or write request. ! 1290: // ! 1291: ! 1292: if ( byteCount > mediaSize - byteStart ) // (clip at end-of-media) ! 1293: { ! 1294: IOMemoryDescriptor * originalBuffer = buffer; ! 1295: ! 1296: buffer = IOMemoryDescriptor::withSubRange( ! 1297: /* descriptor */ originalBuffer, ! 1298: /* withOffset */ 0, ! 1299: /* withLength */ mediaSize - byteStart, ! 1300: /* withDirection */ originalBuffer->getDirection() ); ! 1301: ! 1302: originalBuffer->release(); // (either retained above or about to fail) ! 1303: ! 1304: if ( buffer == 0 ) // (no buffer?) ! 1305: { ! 1306: status = kIOReturnNoMemory; ! 1307: goto dkreadwriteErr; ! 1308: } ! 1309: } ! 1310: ! 1311: // ! 1312: // Execute the transfer. ! 1313: // ! 1314: ! 1315: if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) ) // (an asynchronous request?) ! 1316: { ! 1317: IOStorageCompletion completion; ! 1318: ! 1319: completion.target = dkr; ! 1320: completion.action = dkreadwritecompletion; ! 1321: completion.parameter = (void *) dkrtype; ! 1322: ! 1323: if ( DKR_IS_READ(dkr, dkrtype) ) // (a read?) ! 1324: { ! 1325: minor->media->read( /* client */ gIOMediaBSDClient, ! 1326: /* byteStart */ byteStart, ! 1327: /* buffer */ buffer, ! 1328: /* completion */ completion ); // (go) ! 1329: } ! 1330: else // (a write?) ! 1331: { ! 1332: minor->media->write( /* client */ gIOMediaBSDClient, ! 1333: /* byteStart */ byteStart, ! 1334: /* buffer */ buffer, ! 1335: /* completion */ completion ); // (go) ! 1336: } ! 1337: status = kIOReturnSuccess; ! 1338: } ! 1339: else // (is this a synchronous request?) ! 1340: { ! 1341: if ( DKR_IS_READ(dkr, dkrtype) ) // (a read?) ! 1342: { ! 1343: status = minor->media->read( ! 1344: /* client */ gIOMediaBSDClient, ! 1345: /* byteStart */ byteStart, ! 1346: /* buffer */ buffer, ! 1347: /* actualByteCount */ &byteCount ); // (go) ! 1348: } ! 1349: else // (a write?) ! 1350: { ! 1351: status = minor->media->write( ! 1352: /* client */ gIOMediaBSDClient, ! 1353: /* byteStart */ byteStart, ! 1354: /* buffer */ buffer, ! 1355: /* actualByteCount */ &byteCount ); // (go) ! 1356: } ! 1357: ! 1358: dkreadwritecompletion(dkr, (void *)dkrtype, status, byteCount); ! 1359: } ! 1360: ! 1361: // ! 1362: // We release our retain on the buffer now, even though in the asynchronous ! 1363: // case, the object needs to exist for the duration of the transfer. While ! 1364: // this might appear to be a mistake, it is not. The layers below us will ! 1365: // have retained the buffer themselves. On the last release, the buffer is ! 1366: // complete()'d appropriately. ! 1367: // ! 1368: ! 1369: buffer->release(); // (release our retain on the buffer) ! 1370: ! 1371: return gIOMediaBSDClient->errnoFromReturn(status); // (return error status) ! 1372: ! 1373: dkreadwriteErr: ! 1374: ! 1375: dkreadwritecompletion(dkr, (void *)dkrtype, status, 0); ! 1376: ! 1377: return gIOMediaBSDClient->errnoFromReturn(status); // (return error status) ! 1378: } ! 1379: ! 1380: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1381: ! 1382: void dkreadwritecompletion( void * target, ! 1383: void * parameter, ! 1384: IOReturn status, ! 1385: UInt64 actualByteCount ) ! 1386: { ! 1387: // ! 1388: // dkreadwritecompletion cleans up after a read or write operation. ! 1389: // ! 1390: ! 1391: dkr_t dkr = (dkr_t) target; ! 1392: dkrtype_t dkrtype = (dkrtype_t) (int) parameter; ! 1393: dev_t dev = DKR_GET_DEV(dkr, dkrtype); ! 1394: ! 1395: #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT ! 1396: UInt32 anchorID = gIOMediaBSDClient->getMinor(minor(dev))->anchorID; ! 1397: ! 1398: if ( anchorID < DK_NDRIVE ) ! 1399: { ! 1400: IODrive * drive = dk_drive[anchorID]; ! 1401: ! 1402: if ( drive ) ! 1403: { ! 1404: dk_xfer[anchorID] = (long) ! 1405: ( drive->getStatistic(IODrive::kStatisticsReads ) + ! 1406: drive->getStatistic(IODrive::kStatisticsWrites) ); ! 1407: dk_wds[anchorID] = (long) ( 8 * ! 1408: ( drive->getStatistic(IODrive::kStatisticsBytesRead ) + ! 1409: drive->getStatistic(IODrive::kStatisticsBytesWritten) ) ); ! 1410: } ! 1411: } ! 1412: #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT ! 1413: ! 1414: if ( status != kIOReturnSuccess ) // (log errors to the console) ! 1415: { ! 1416: IOLog( "%s: %s.\n", ! 1417: gIOMediaBSDClient->getMinor(minor(dev))->name, ! 1418: gIOMediaBSDClient->stringFromReturn(status) ); ! 1419: } ! 1420: ! 1421: DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount); // (set byte count) ! 1422: DKR_RUN_COMPLETION(dkr, dkrtype, status); // (run completion) ! 1423: } ! 1424: ! 1425: // ============================================================================= ! 1426: // AnchorTable Class ! 1427: ! 1428: AnchorTable::AnchorTable(UInt32 growCount, UInt32 maxCount) ! 1429: { ! 1430: // ! 1431: // Initialize this object's minimal state. ! 1432: // ! 1433: ! 1434: _table = 0; ! 1435: _tableCount = 0; ! 1436: _tableGrowCount = growCount; ! 1437: _tableMaxCount = maxCount; ! 1438: } ! 1439: ! 1440: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1441: ! 1442: AnchorTable::~AnchorTable() ! 1443: { ! 1444: // ! 1445: // Free all of this object's outstanding resources. ! 1446: // ! 1447: ! 1448: for ( UInt32 anchorID = 0; anchorID < _tableCount; anchorID++ ) ! 1449: if ( _table[anchorID].isAssigned ) remove(anchorID); ! 1450: ! 1451: if ( _table ) IODelete(_table, AnchorSlot, _tableCount); ! 1452: } ! 1453: ! 1454: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1455: ! 1456: UInt32 AnchorTable::insert(IOService * anchor) ! 1457: { ! 1458: // ! 1459: // This method inserts the specified anchor into an unassigned slot in the ! 1460: // anchor table and returns its ID (or kInvalidAnchorID on a failure). ! 1461: // ! 1462: // Note that the anchor is transparently removed from the table should the ! 1463: // anchor terminate (or it is at least marked obsolete, should references ! 1464: // to the anchor still exist in the minor table). ! 1465: // ! 1466: ! 1467: UInt32 anchorID; ! 1468: IONotifier * notifier; ! 1469: ! 1470: // Search for an unassigned slot in the anchor table. ! 1471: ! 1472: for ( anchorID = 0; anchorID < _tableCount; anchorID++ ) ! 1473: if ( _table[anchorID].isAssigned == false ) break; ! 1474: ! 1475: // Was an unassigned slot found? If not, grow the table. ! 1476: ! 1477: if ( anchorID == _tableCount ) ! 1478: { ! 1479: AnchorSlot * newTable; ! 1480: UInt32 newTableCount; ! 1481: ! 1482: // We must expand the anchor table since no more slots are available. ! 1483: ! 1484: if ( _tableCount >= _tableMaxCount ) return kInvalidAnchorID; ! 1485: ! 1486: newTableCount = min(_tableGrowCount + _tableCount, _tableMaxCount); ! 1487: newTable = IONew(AnchorSlot, newTableCount); ! 1488: ! 1489: if ( newTable == 0 ) return kInvalidAnchorID; ! 1490: ! 1491: bzero(newTable, newTableCount * sizeof(AnchorSlot)); ! 1492: ! 1493: // Copy over the old table's entries, then free the old table. ! 1494: ! 1495: if ( _table ) ! 1496: { ! 1497: bcopy(_table, newTable, _tableCount * sizeof(AnchorSlot)); ! 1498: IODelete(_table, AnchorSlot, _tableCount); ! 1499: } ! 1500: ! 1501: // Obtain the next unassigned index (simple since we know the size of ! 1502: // the old table), then update our instance variables to reflect the ! 1503: // new tables. ! 1504: ! 1505: anchorID = _tableCount; ! 1506: _table = newTable; ! 1507: _tableCount = newTableCount; ! 1508: } ! 1509: ! 1510: // Create a notification handler for the anchor's termination (post-stop); ! 1511: // the handler will remove the anchor transparently from the table if the ! 1512: // anchor terminates (or at least marks it obsolete, if references to the ! 1513: // anchor still exist in the minor table). ! 1514: ! 1515: notifier = anchor->registerInterest( ! 1516: /* type */ gIOGeneralInterest, ! 1517: /* action */ anchorChange, ! 1518: /* target */ (void *) this, ! 1519: /* parameter */ 0 ); ! 1520: ! 1521: if ( notifier == 0 ) return kInvalidAnchorID; ! 1522: ! 1523: // Zero the new slot, fill it in, and retain the anchor object. ! 1524: ! 1525: bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot) ! 1526: ! 1527: _table[anchorID].isAssigned = true; // (fill in slot) ! 1528: _table[anchorID].isObsolete = false; ! 1529: _table[anchorID].anchor = anchor; ! 1530: _table[anchorID].notifier = notifier; ! 1531: ! 1532: _table[anchorID].anchor->retain(); // (retain anchor) ! 1533: ! 1534: #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT ! 1535: if ( anchorID < DK_NDRIVE ) ! 1536: { ! 1537: dk_drive[anchorID] = OSDynamicCast(IODrive, anchor); ! 1538: if ( anchorID + 1 > (UInt32) dk_ndrive ) dk_ndrive = anchorID + 1; ! 1539: } ! 1540: #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT ! 1541: ! 1542: return anchorID; ! 1543: } ! 1544: ! 1545: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1546: ! 1547: void AnchorTable::remove(UInt32 anchorID) ! 1548: { ! 1549: // ! 1550: // This method removes the specified anchor from the anchor table. ! 1551: // ! 1552: ! 1553: assert(anchorID < _tableCount); ! 1554: assert(_table[anchorID].isAssigned); ! 1555: ! 1556: // Release the resources retained in the anchor slot and zero it. ! 1557: ! 1558: _table[anchorID].notifier->remove(); ! 1559: _table[anchorID].anchor->release(); // (release anchor) ! 1560: ! 1561: bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot) ! 1562: ! 1563: #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT ! 1564: if ( anchorID < DK_NDRIVE ) ! 1565: { ! 1566: dk_drive[anchorID] = 0; ! 1567: for (dk_ndrive = DK_NDRIVE; dk_ndrive; dk_ndrive--) ! 1568: { ! 1569: if ( dk_drive[dk_ndrive - 1] ) break; ! 1570: } ! 1571: } ! 1572: #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT ! 1573: } ! 1574: ! 1575: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1576: ! 1577: void AnchorTable::obsolete(UInt32 anchorID) ! 1578: { ! 1579: // ! 1580: // This method obsoletes the specified anchor, that is, the slot is marked ! 1581: // as obsolete and will be removed later via the minor table remove method ! 1582: // once it detects references to the anchor ID drop to 0. Once obsoleted, ! 1583: // the anchor can be considered to be removed, since it will not appear in ! 1584: // locate searches, even though behind the scenes it still occupies a slot. ! 1585: // ! 1586: ! 1587: assert(anchorID < _tableCount); ! 1588: assert(_table[anchorID].isAssigned); ! 1589: ! 1590: // Mark the anchor as obsolete so that it can be removed from the table as ! 1591: // soon as all its references go away (minor table's responsibility). ! 1592: ! 1593: _table[anchorID].isObsolete = true; ! 1594: } ! 1595: ! 1596: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1597: ! 1598: UInt32 AnchorTable::locate(IOService * anchor) ! 1599: { ! 1600: // ! 1601: // This method searches for the specified anchor in the anchor table and ! 1602: // returns its ID (or kInvalidAnchorID on a failure). It ignores slots ! 1603: // marked as obsolete. ! 1604: // ! 1605: ! 1606: for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++) ! 1607: { ! 1608: if ( _table[anchorID].isAssigned != false && ! 1609: _table[anchorID].isObsolete == false && ! 1610: _table[anchorID].anchor == anchor ) return anchorID; ! 1611: } ! 1612: ! 1613: return kInvalidAnchorID; ! 1614: } ! 1615: ! 1616: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1617: ! 1618: bool AnchorTable::isObsolete(UInt32 anchorID) ! 1619: { ! 1620: // ! 1621: // Determine whether the specified anchor ID is marked as obsolete. ! 1622: // ! 1623: ! 1624: assert(anchorID < _tableCount); ! 1625: assert(_table[anchorID].isAssigned); ! 1626: ! 1627: return _table[anchorID].isObsolete ? true : false; ! 1628: } ! 1629: ! 1630: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1631: ! 1632: IOReturn AnchorTable::anchorChange( void * /* target */, ! 1633: void * /* parameter */, ! 1634: UInt32 messageType, ! 1635: IOService * anchor, ! 1636: void * /* messageArgument */, ! 1637: vm_size_t /* argSize */ ) ! 1638: { ! 1639: if ( messageType != kIOMessageServiceIsTerminated ) ! 1640: return kIOReturnSuccess; ! 1641: ! 1642: // ! 1643: // Notification handler for anchor termination (post-stop). ! 1644: // ! 1645: ! 1646: UInt32 anchorID; ! 1647: ! 1648: assert(gIOMediaBSDClient); ! 1649: ! 1650: // Disable access to tables, matching, opens, closes, and terminations. ! 1651: ! 1652: gIOMediaBSDClient->lockForArbitration(); ! 1653: ! 1654: // Determine whether this anchor is in the anchor table (obsolete occurences ! 1655: // are skipped in the search, as appropriate, since those anchor IDs will be ! 1656: // removed as it is). ! 1657: ! 1658: anchorID = gIOMediaBSDClient->getAnchors()->locate(anchor); ! 1659: ! 1660: if ( anchorID != kInvalidAnchorID ) ! 1661: { ! 1662: // Determine whether this anchor is still has references in the minor ! 1663: // table. If it does, we mark the the anchor as obsolete so that it ! 1664: // will be removed later, once references to it go to zero (which is ! 1665: // handled by MinorTable::remove). ! 1666: ! 1667: if ( gIOMediaBSDClient->getMinors()->hasReferencesToAnchorID(anchorID) ) ! 1668: gIOMediaBSDClient->getAnchors()->obsolete(anchorID); ! 1669: else ! 1670: gIOMediaBSDClient->getAnchors()->remove(anchorID); ! 1671: } ! 1672: ! 1673: // Enable access to tables, matching, opens, closes, and terminations. ! 1674: ! 1675: gIOMediaBSDClient->unlockForArbitration(); ! 1676: ! 1677: return kIOReturnSuccess; ! 1678: } ! 1679: ! 1680: // ============================================================================= ! 1681: // MinorTable Class ! 1682: ! 1683: MinorTable::MinorTable(UInt32 growCount, UInt32 maxCount) ! 1684: { ! 1685: // ! 1686: // Initialize this object's minimal state. ! 1687: // ! 1688: ! 1689: _table = 0; ! 1690: _tableCount = 0; ! 1691: _tableGrowCount = growCount; ! 1692: _tableMaxCount = maxCount; ! 1693: } ! 1694: ! 1695: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1696: ! 1697: MinorTable::~MinorTable() ! 1698: { ! 1699: // ! 1700: // Free all of this object's outstanding resources. ! 1701: // ! 1702: ! 1703: for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ ) ! 1704: if ( _table[minorID].isAssigned ) remove(minorID); ! 1705: ! 1706: if ( _table ) IODelete(_table, MinorSlot, _tableCount); ! 1707: } ! 1708: ! 1709: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1710: ! 1711: UInt32 MinorTable::insert(IOMedia * media, UInt32 anchorID, char * slicePath) ! 1712: { ! 1713: // ! 1714: // This method inserts the specified media/anchorID pair into an unassigned ! 1715: // slot in the minor table and returns its ID (or kInvalidMinorID on error). ! 1716: // ! 1717: // Note that the bdev and cdev nodes are published as a result of this call, ! 1718: // with the name "[r]disk<anchorID><slicePath>". For instance, "disk2s3s1" ! 1719: // for an anchorID of 2 and slicePath of "s3s1". ! 1720: // ! 1721: ! 1722: void * bdevNode; ! 1723: void * cdevNode; ! 1724: UInt32 minorID; ! 1725: char * minorName; ! 1726: UInt32 minorNameSize; ! 1727: ! 1728: // Search for an unassigned slot in the minor table. ! 1729: ! 1730: for ( minorID = 0; minorID < _tableCount; minorID++ ) ! 1731: if ( _table[minorID].isAssigned == false ) break; ! 1732: ! 1733: // Was an unassigned slot found? If not, grow the table. ! 1734: ! 1735: if ( minorID == _tableCount ) ! 1736: { ! 1737: MinorSlot * newTable; ! 1738: UInt32 newTableCount; ! 1739: ! 1740: // We must expand the minor table since no more slots are available. ! 1741: ! 1742: if ( _tableCount >= _tableMaxCount) return kInvalidMinorID; ! 1743: ! 1744: newTableCount = min(_tableGrowCount + _tableCount, _tableMaxCount); ! 1745: newTable = IONew(MinorSlot, newTableCount); ! 1746: ! 1747: if ( newTable == 0 ) return kInvalidMinorID; ! 1748: ! 1749: bzero(newTable, newTableCount * sizeof(MinorSlot)); ! 1750: ! 1751: // Copy over the old table's entries, then free the old table. ! 1752: ! 1753: if ( _table ) ! 1754: { ! 1755: bcopy(_table, newTable, _tableCount * sizeof(MinorSlot)); ! 1756: IODelete(_table, MinorSlot, _tableCount); ! 1757: } ! 1758: ! 1759: // Obtain the next unassigned index (simple since we know the size of ! 1760: // the old table), then update our instance variables to reflect the ! 1761: // new tables. ! 1762: ! 1763: minorID = _tableCount; ! 1764: _table = newTable; ! 1765: _tableCount = newTableCount; ! 1766: } ! 1767: ! 1768: // Create a buffer large enough to hold the full name of the minor. ! 1769: ! 1770: minorNameSize = strlen("disk#"); ! 1771: for (unsigned temp = anchorID; temp >= 10; temp /= 10) minorNameSize++; ! 1772: minorNameSize += strlen(slicePath); ! 1773: minorNameSize += 1; ! 1774: minorName = IONew(char, minorNameSize); ! 1775: ! 1776: // Create a block and character device node in BSD for this media. ! 1777: ! 1778: bdevNode = devfs_make_node( /* dev */ makedev(kMajor, minorID), ! 1779: /* type */ DEVFS_BLOCK, ! 1780: /* owner */ UID_ROOT, ! 1781: /* group */ GID_OPERATOR, ! 1782: /* permission */ media->isWritable()?0640:0440, ! 1783: /* name (fmt) */ "disk%d%s", ! 1784: /* name (arg) */ anchorID, ! 1785: /* name (arg) */ slicePath ); ! 1786: ! 1787: cdevNode = devfs_make_node( /* dev */ makedev(kMajor, minorID), ! 1788: /* type */ DEVFS_CHAR, ! 1789: /* owner */ UID_ROOT, ! 1790: /* group */ GID_OPERATOR, ! 1791: /* permission */ media->isWritable()?0640:0440, ! 1792: /* name (fmt) */ "rdisk%d%s", ! 1793: /* name (arg) */ anchorID, ! 1794: /* name (arg) */ slicePath ); ! 1795: ! 1796: if ( minorName == 0 || bdevNode == 0 || cdevNode == 0 ) ! 1797: { ! 1798: if ( cdevNode ) devfs_remove(cdevNode); ! 1799: if ( bdevNode ) devfs_remove(bdevNode); ! 1800: if ( minorName ) IODelete(minorName, char, minorNameSize); ! 1801: ! 1802: return kInvalidMinorID; ! 1803: } ! 1804: ! 1805: // Construct a name for the node. ! 1806: ! 1807: sprintf(minorName, "disk%ld%s", anchorID, slicePath); ! 1808: assert(strlen(minorName) + 1 == minorNameSize); ! 1809: ! 1810: // Zero the new slot, fill it in, and retain the media object. ! 1811: ! 1812: bzero(&_table[minorID], sizeof(MinorSlot)); // (zero slot) ! 1813: ! 1814: _table[minorID].isAssigned = true; // (fill in slot) ! 1815: _table[minorID].isEjecting = false; ! 1816: _table[minorID].isObsolete = false; ! 1817: _table[minorID].anchorID = anchorID; ! 1818: _table[minorID].media = media; ! 1819: _table[minorID].name = minorName; ! 1820: _table[minorID].bdevBlockSize = media->getPreferredBlockSize(); ! 1821: _table[minorID].bdevNode = bdevNode; ! 1822: _table[minorID].bdevOpen = false; ! 1823: _table[minorID].cdevNode = cdevNode; ! 1824: _table[minorID].cdevOpen = false; ! 1825: ! 1826: _table[minorID].media->retain(); // (retain media) ! 1827: ! 1828: return minorID; ! 1829: } ! 1830: ! 1831: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1832: ! 1833: void MinorTable::remove(UInt32 minorID) ! 1834: { ! 1835: // ! 1836: // This method removes the specified minor from the minor table. ! 1837: // ! 1838: ! 1839: UInt32 anchorID; ! 1840: ! 1841: assert(minorID < _tableCount); ! 1842: assert(_table[minorID].isAssigned); ! 1843: ! 1844: assert(_table[minorID].isEjecting == false); ! 1845: assert(_table[minorID].bdevOpen == false); ! 1846: assert(_table[minorID].cdevOpen == false); ! 1847: ! 1848: anchorID = _table[minorID].anchorID; ! 1849: ! 1850: // Release the resources retained in the minor slot and zero it. ! 1851: ! 1852: devfs_remove(_table[minorID].cdevNode); ! 1853: devfs_remove(_table[minorID].bdevNode); ! 1854: IODelete(_table[minorID].name, char, strlen(_table[minorID].name) + 1); ! 1855: _table[minorID].media->release(); // (release media) ! 1856: ! 1857: bzero(&_table[minorID], sizeof(MinorSlot)); // (zero slot) ! 1858: ! 1859: // Determine whether the associated anchor ID is marked as obsolete. If it ! 1860: // is and there are no other references to the anchor ID in the minor table, ! 1861: // we remove the anchor ID from the anchor table. ! 1862: ! 1863: assert(gIOMediaBSDClient); ! 1864: ! 1865: if ( gIOMediaBSDClient->getAnchors()->isObsolete(anchorID) ) ! 1866: { ! 1867: if ( hasReferencesToAnchorID(anchorID) == false ) ! 1868: gIOMediaBSDClient->getAnchors()->remove(anchorID); ! 1869: } ! 1870: } ! 1871: ! 1872: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1873: ! 1874: UInt32 MinorTable::locate(IOMedia * media) ! 1875: { ! 1876: // ! 1877: // This method searches for the specified media in the minor table and ! 1878: // returns its ID (or kInvalidMinorID on an error). It ignores slots ! 1879: // marked as obsolete. ! 1880: // ! 1881: ! 1882: for (UInt32 minorID = 0; minorID < _tableCount; minorID++) ! 1883: { ! 1884: if ( _table[minorID].isAssigned != false && ! 1885: _table[minorID].isObsolete == false && ! 1886: _table[minorID].media == media ) return minorID; ! 1887: } ! 1888: ! 1889: return kInvalidMinorID; ! 1890: } ! 1891: ! 1892: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1893: ! 1894: UInt32 MinorTable::getOpenCountForAnchorID(UInt32 anchorID) ! 1895: { ! 1896: // ! 1897: // This method obtains a count of opens on the minors associated with the ! 1898: // specified anchor ID. A block device open is counted separately from a ! 1899: // character device open. ! 1900: // ! 1901: ! 1902: UInt32 opens = 0; ! 1903: ! 1904: for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ ) ! 1905: { ! 1906: if ( _table[minorID].isAssigned != false && ! 1907: _table[minorID].anchorID == anchorID ) ! 1908: { ! 1909: opens += (_table[minorID].bdevOpen) ? 1 : 0; ! 1910: opens += (_table[minorID].cdevOpen) ? 1 : 0; ! 1911: } ! 1912: } ! 1913: ! 1914: return opens; ! 1915: } ! 1916: ! 1917: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1918: ! 1919: IOMedia * MinorTable::getWholeMediaAtAnchorID(UInt32 anchorID) ! 1920: { ! 1921: // ! 1922: // This method obtains the whole media associated with the specified anchor ! 1923: // ID. ! 1924: // ! 1925: ! 1926: for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ ) ! 1927: { ! 1928: if ( _table[minorID].isAssigned != false && ! 1929: _table[minorID].anchorID == anchorID && ! 1930: _table[minorID].media->isWhole() ) return _table[minorID].media; ! 1931: } ! 1932: ! 1933: return 0; ! 1934: } ! 1935: ! 1936: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1937: ! 1938: bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID) ! 1939: { ! 1940: // ! 1941: // This method determines whether there are assigned minors in the minor ! 1942: // table that refer to the specified anchor ID. ! 1943: // ! 1944: ! 1945: for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ ) ! 1946: { ! 1947: if ( _table[minorID].isAssigned != false && ! 1948: _table[minorID].anchorID == anchorID ) return true; ! 1949: } ! 1950: ! 1951: return false; ! 1952: } ! 1953: ! 1954: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1955: ! 1956: MinorSlot * MinorTable::getMinor(UInt32 minorID) ! 1957: { ! 1958: // ! 1959: // Obtain the structure describing the specified minor. ! 1960: // ! 1961: ! 1962: if ( minorID < _tableCount && _table[minorID].isAssigned ) ! 1963: return &_table[minorID]; ! 1964: else ! 1965: return 0; ! 1966: } ! 1967: ! 1968: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1969: ! 1970: void MinorTable::obsolete(UInt32 minorID) ! 1971: { ! 1972: // ! 1973: // This method obsoletes the specified minor, that is, the slot is marked ! 1974: // as obsolete and will be removed later via the dkclose function once it ! 1975: // detects the last close arrive. Once obsoleted, the minor can be cons- ! 1976: // idered to be removed, since it will not appear in locate searches. ! 1977: // ! 1978: ! 1979: assert(minorID < _tableCount); ! 1980: assert(_table[minorID].isAssigned); ! 1981: ! 1982: // Mark the minor as obsolete so that it can be removed from the table as ! 1983: // soon as the last close arrives (dkclose function's responsibility). ! 1984: ! 1985: _table[minorID].isObsolete = true; ! 1986: } ! 1987: ! 1988: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 1989: ! 1990: bool MinorTable::isObsolete(UInt32 minorID) ! 1991: { ! 1992: // ! 1993: // Determine whether the specified minor ID is marked as obsolete. ! 1994: // ! 1995: ! 1996: assert(minorID < _tableCount); ! 1997: assert(_table[minorID].isAssigned); ! 1998: ! 1999: return _table[minorID].isObsolete ? true : false; ! 2000: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.