2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
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/IOBlockStorageDriver.h>
40 #include <IOKit/storage/IOMedia.h>
41 #include <IOKit/storage/IOMediaBSDClient.h>
43 #define super IOService
44 OSDefineMetaClassAndStructors(IOMediaBSDClient
, IOService
)
46 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
48 static IOMediaBSDClient
* gIOMediaBSDClient
= 0;
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)
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()
60 #define IOMEDIABSDCLIENT_IOSTAT_SUPPORT // (enable iostat support for bsd)
62 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
);
76 static struct bdevsw bdevswFunctions
=
79 /* d_close */ dkclose
,
80 /* d_strategy */ dkstrategy
,
81 /* d_ioctl */ dkioctl_bdev
,
82 /* d_dump */ eno_dump
,
87 struct cdevsw cdevswFunctions
=
90 /* d_close */ dkclose
,
92 /* d_write */ dkwrite
,
93 /* d_ioctl */ dkioctl
,
94 /* d_stop */ eno_stop
,
95 /* d_reset */ eno_reset
,
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
,
105 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
107 struct dio
{ dev_t dev
; struct uio
* uio
; };
109 typedef void * dkr_t
; /* dkreadwrite request */
110 typedef enum { DKRTYPE_BUF
, DKRTYPE_DIO
} dkrtype_t
;
112 int dkreadwrite(dkr_t dkr
, dkrtype_t dkrtype
);
113 void dkreadwritecompletion(void *, void *, IOReturn
, UInt64
);
115 #define get_kernel_task() kernel_task
116 #define get_user_task() current_task()
118 #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
119 #include <sys/dkstat.h>
120 IOBlockStorageDriver
* dk_drive
[DK_NDRIVE
];
121 #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
123 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
125 const UInt32 kInvalidAnchorID
= (UInt32
) (-1);
129 UInt32 isAssigned
:1, // (anchor slot is occupied)
130 isObsolete
:1; // (anchor slot is to be removed once refs gone)
132 IOService
* anchor
; // (anchor object)
133 IONotifier
* notifier
; // (anchor termination notification, post-stop)
141 UInt32 _tableGrowCount
;
142 UInt32 _tableMaxCount
;
144 static IOReturn
anchorWasNotified( void * target
,
147 IOService
* provider
,
148 void * messageArgument
,
149 vm_size_t messageArgumentSize
);
152 AnchorTable(UInt32 growCount
, UInt32 maxCount
);
155 UInt32
insert(IOService
* anchor
);
156 UInt32
locate(IOService
* anchor
);
157 void obsolete(UInt32 anchorID
);
158 void remove(UInt32 anchorID
);
160 bool isObsolete(UInt32 anchorID
);
163 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
165 const UInt32 kInvalidMinorID
= (UInt32
) (-1);
169 UInt32 isAssigned
:1, // (minor slot is occupied)
170 isEjecting
:1, // (minor slot is in eject flux, needs close)
171 isObsolete
:1; // (minor slot is in eject flux, needs removal)
173 UInt32 anchorID
; // (minor's associated anchor ID)
174 IOMedia
* media
; // (minor's media object)
175 char * name
; // (minor's name, private allocation space)
177 UInt64 bdevBlockSize
; // (block device's preferred block size)
178 void * bdevNode
; // (block device's devfs node)
179 UInt32 bdevOpen
:1, // (block device's open flag)
180 bdevWriter
:1; // (block device's open writer flag)
182 void * cdevNode
; // (character device's devfs node)
183 UInt32 cdevOpen
:1, // (character device's open flag)
184 cdevWriter
:1; // (character device's open writer flag)
192 UInt32 _tableGrowCount
;
193 UInt32 _tableMaxCount
;
196 MinorTable(UInt32 growCount
, UInt32 maxCount
);
199 UInt32
insert(IOMedia
* media
, UInt32 anchorID
, char * slicePath
);
200 UInt32
locate(IOMedia
* media
);
201 void obsolete(UInt32 minorID
);
202 void remove(UInt32 minorID
);
204 bool isObsolete(UInt32 minorID
);
206 MinorSlot
* getMinor(UInt32 minorID
);
208 UInt32
getOpenCountForAnchorID(UInt32 anchorID
);
209 IOMedia
* getWholeMediaAtAnchorID(UInt32 anchorID
);
210 bool hasReferencesToAnchorID(UInt32 anchorID
);
213 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
215 bool IOMediaBSDClient::init(OSDictionary
* properties
= 0)
218 // Initialize this object's minimal state.
221 if ( super::init(properties
) == false ) return false;
223 _anchors
= new AnchorTable(kAnchorsGrowCount
, kAnchorsMaxCount
);
224 _bdevswInstalled
= false;
225 _cdevswInstalled
= false;
226 _minors
= new MinorTable(kMinorsGrowCount
, kMinorsMaxCount
);
229 if ( _anchors
== 0 || _minors
== 0 ) return false;
234 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236 void IOMediaBSDClient::free()
239 // Free all of this object's outstanding resources.
242 if ( _notifier
) _notifier
->remove();
243 if ( _cdevswInstalled
) cdevsw_remove(kMajor
, &cdevswFunctions
);
244 if ( _bdevswInstalled
) bdevsw_remove(kMajor
, &bdevswFunctions
);
245 if ( _minors
) delete _minors
;
246 if ( _anchors
) delete _anchors
;
251 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
253 bool IOMediaBSDClient::start(IOService
* provider
)
256 // This method is called once we have been attached to the provider object.
259 assert(gIOMediaBSDClient
== 0);
261 // Ask our superclass' opinion.
263 if ( super::start(provider
) == false ) return false;
265 // Establish a global reference to this instance.
267 gIOMediaBSDClient
= this;
269 // Install bdevsw and cdevsw functions.
271 _bdevswInstalled
= (bdevsw_add(kMajor
, &bdevswFunctions
) == kMajor
);
272 _cdevswInstalled
= (cdevsw_add(kMajor
, &cdevswFunctions
) == kMajor
);
274 if ( _bdevswInstalled
== false && _cdevswInstalled
== false ) return false;
276 // Create a notification handler for media arrival. We ask for a priority
277 // of ten to ensure that we are notified ahead of other interested clients
278 // (with a default priority of zero), so that we can place the BSD-related
279 // properties on the media object that they might need in time.
281 _notifier
= addNotification( /* type */ gIOFirstPublishNotification
,
282 /* description */ serviceMatching("IOMedia"),
283 /* action */ mediaHasArrived
,
288 if ( _notifier
== 0 ) return false;
290 // Register this object so it can be found via notification requests. It is
291 // not being registered to have I/O Kit attempt to have drivers match on it,
292 // which is the reason most other services are registered -- that's not the
293 // intention of this registerService call.
300 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
302 void IOMediaBSDClient::stop(IOService
* provider
)
305 // This method is called before we are detached from the provider object.
308 IOMedia
* media
= (IOMedia
*) provider
;
311 // Disable access to tables, matching, opens, closes, and terminations.
313 gIOMediaBSDClient
->lockForArbitration();
315 // Find the minor assigned to this media.
317 minorID
= _minors
->locate(media
);
318 assert(minorID
!= kInvalidMinorID
);
320 // State our assumptions.
322 assert(media
->isOpen() == false);
324 // Remove the minor from the minor table, unless it's still in flux (which
325 // means an open on the bdevsw/cdevsw switch is still outstanding: the one
326 // that sent the eject ioctl), in which case we mark the minor as obsolete
327 // for later removal.
329 if ( _minors
->getMinor(minorID
)->isEjecting
) // (is minor in flux?)
331 assert(_minors
->isObsolete(minorID
) == false);
333 _minors
->obsolete(minorID
);
337 assert(_minors
->getMinor(minorID
)->bdevOpen
== false);
338 assert(_minors
->getMinor(minorID
)->cdevOpen
== false);
340 _minors
->remove(minorID
);
343 // Enable access to tables, matching, opens, closes, and terminations.
345 gIOMediaBSDClient
->unlockForArbitration();
347 // Call upon the superclass to finish its work.
352 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
354 bool IOMediaBSDClient::mediaHasArrived( void * /* target */,
355 void * /* parameter */,
356 IOService
* service
)
359 // Notification handler for media arrivals.
362 IOMedia
* media
= OSDynamicCast(IOMedia
, service
);
363 bool success
= false;
365 assert(gIOMediaBSDClient
);
367 // Attach the media-bsd-client object as a client of the new media object.
369 if ( media
&& gIOMediaBSDClient
->attach(media
) )
371 // Disable access to tables, matching, opens, closes, and terminations.
373 gIOMediaBSDClient
->lockForArbitration();
375 // Create bdevsw and cdevsw nodes for the new media object.
377 success
= gIOMediaBSDClient
->createNodes(media
);
379 // Enable access to tables, matching, opens, closes, and terminations.
381 gIOMediaBSDClient
->unlockForArbitration();
383 // Detach the media-bsd-client object from the media object on error.
385 if (success
== false) gIOMediaBSDClient
->detach(media
);
388 return true; // (meaningless return value)
391 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
393 IOMedia
* IOMediaBSDClient::getWholeMedia( IOMedia
* media
,
394 UInt32
* slicePathSize
= 0,
395 char * slicePath
= 0 )
398 // Find the whole media that roots this media tree. A null return value
399 // indicates no whole media was found or a malformed tree was detected.
401 // If slicePathSize is non-zero, the size required to fit the slice path
402 // (including the zero terminator) is passed back as a result.
404 // If slicePathSize and slicePath are both non-zero, the slice path will
405 // be written into the slicePath buffer. The value slicePathSize points
406 // to must be the size of the slicePath buffer, which is used for sanity
407 // checking in this method.
411 UInt32 position
= sizeof('\0');
412 IOService
* service
= 0;
414 assert(slicePath
== 0 || slicePathSize
!= 0);
416 // Search the registry for the parent whole media for this media.
418 for ( service
= media
; service
; service
= service
->getProvider() )
420 if ( OSDynamicCast(IOMedia
, service
) ) // (is it a media?)
422 if ( ((IOMedia
*)service
)->isWhole() ) // (is it a whole media?)
424 if ( slicePath
) // (are we building the slice path?)
426 slicePath
[*slicePathSize
- 1] = 0; // (zero terminate path)
428 if ( position
< *slicePathSize
) // (need to move path?)
430 memmove( slicePath
, // (move path to start of buffer)
431 slicePath
+ (*slicePathSize
- position
),
435 else if ( slicePathSize
) // (report size req'd for slice path?)
437 *slicePathSize
= position
;
440 return (IOMedia
*)service
; // (return the whole media)
443 // Determine whether this non-whole media has a location value. It
444 // must, by definition of a non-whole media, but if it does not, we
445 // should return an error condition.
447 const char * location
= service
->getLocation();
449 if ( location
== 0 ) // (no location on non-whole media?)
451 if ( service
== media
) IOLog(kMsgNoLocation
, media
->getName());
455 // Otherwise, it's a valid non-whole media: we compute the required
456 // size for the slice path or build the slice path, if so requested.
457 // Note that the slice path is built backwards from the ends of the
458 // supplied buffer to the beginning of the buffer.
460 position
+= sizeof('s') + strlen(location
);
462 if ( slicePath
) // (build the slice path?)
464 char * path
= slicePath
+ *slicePathSize
- position
;
466 if ( position
> *slicePathSize
) { assert(0); return 0; }
469 strncpy(path
+ sizeof('s'), location
, strlen(location
));
476 // If we've fallen through, then the whole media was never found.
478 if ( depth
== 1 ) IOLog(kMsgNoWhole
, media
->getName());
482 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
484 bool IOMediaBSDClient::createNodes(IOMedia
* media
)
487 // Create bdevsw and cdevsw nodes for the given media object.
489 // This method assumes that the arbitration lock is held.
494 bool anchorNew
= false;
496 char * slicePath
= 0;
497 UInt32 slicePathSize
;
501 // Find the anchor that roots this media tree. The anchor is defined as the
502 // parent of the whole media that roots this media tree. It is an important
503 // object to us because this object stays in place when media is ejected, so
504 // we can continue to maintain the "unit number" of the "drive" such that if
505 // media is re-inserted, it will show up under the same "unit number". You
506 // can think of the typical anchor as being the drive, if it helps, although
507 // it could be one of many other kinds of drivers (eg. a RAID scheme).
510 whole
= getWholeMedia(media
, &slicePathSize
);
511 if ( whole
== 0 ) return false;
513 anchor
= whole
->getProvider();
514 if ( anchor
== 0 ) return false;
517 // Determine whether the anchor already exists in the anchor table (obsolete
518 // occurences are skipped in the search, as appropriate, since those anchor
519 // IDs are to be removed soon). If the anchor does not exist, insert it into
523 anchorID
= _anchors
->locate(anchor
);
525 if ( anchorID
!= kInvalidAnchorID
)
528 // The anchor does exist in the table, however we've got more to check.
530 // We need to ensure that the whole media associated with this anchor is
531 // the same as ours. If it is, all is well. If it isn't, then there is
532 // still a chance all is well. It is possible to have an old media tree
533 // still associated with the anchor: the tree would be inactive, but not
534 // yet terminated (this can happen on forced termination of a media tree
535 // with oustanding opens, since the close must come before the terminate
536 // can proceed; it can happen even in normal eject conditions should the
537 // media be immediately reinserted when the termination on the old tree,
538 // which is asynchronous, is still chugging along on another thread). In
539 // case the tree is inactive, we mark the anchorID as obsolete and use a
540 // new anchorID. In the case the tree is not inactive, then we've got a
541 // problem and we must bail out.
543 // A few additional notes:
545 // o if the whole media is indeed the same as the one in our tables, we
546 // need not check that it is active, because by virtue of the fact we
547 // got a new media notification on the same tree, we know for sure it
548 // cannot be in the inactive state.
550 // o if the whole media is not in our tables, it is quite possible that
551 // some child non-whole media from the old media tree is still around
552 // as terminations work from the bottom (whole media) up (to leaves),
553 // and the asynchronous termination thread is still not done chugging
554 // through the medias on the old tree. We use a new anchorID in this
558 IOMedia
* wholeInTable
= _minors
->getWholeMediaAtAnchorID(anchorID
);
560 if ( wholeInTable
== 0 ) // (is an existing whole media in our tables?)
562 if ( _minors
->hasReferencesToAnchorID(anchorID
) ) // (any medias?)
564 _anchors
->obsolete(anchorID
); // (obsolete old anchor ID)
565 anchorID
= kInvalidAnchorID
; // ( request new anchor ID)
566 } // (else, all is well)
568 else if ( whole
!= wholeInTable
) // (old whole media not same as new?)
570 if ( wholeInTable
->isInactive() ) // (is it inactive/terminating?)
572 _anchors
->obsolete(anchorID
); // (obsolete old anchor ID)
573 anchorID
= kInvalidAnchorID
; // ( request new anchor ID)
575 else // (peer active whole medias detected, log error)
577 if ( whole
== media
) IOLog(kMsgBadWhole
, whole
->getName());
580 } // (else, all is well)
583 if ( anchorID
== kInvalidAnchorID
)
585 anchorID
= _anchors
->insert(anchor
); // (get new anchor ID)
586 if ( anchorID
== kInvalidAnchorID
) return false;
591 // Allocate space for and build the slice path for the device node names.
594 slicePath
= (char *) IOMalloc(slicePathSize
);
595 if ( slicePath
== 0 ) goto createNodesErr
;
597 whole
= getWholeMedia(media
, &slicePathSize
, slicePath
);
601 // Insert the new media into our minor table (we're almost done :-).
604 minorID
= _minors
->insert(media
, anchorID
, slicePath
);
605 if ( minorID
== kInvalidMinorID
) goto createNodesErr
;
608 // Create the required properties on the media.
611 media
->setProperty(kIOBSDNameKey
, _minors
->getMinor(minorID
)->name
);
612 media
->setProperty(kIOBSDUnitKey
, anchorID
, 32); // ("BSD Unit" )
613 media
->setProperty(kIOBSDMajorKey
, kMajor
, 32); // ("BSD Major")
614 media
->setProperty(kIOBSDMinorKey
, minorID
, 32); // ("BSD Minor")
617 // Clean up outstanding resources.
620 IOFree(slicePath
, slicePathSize
);
622 return true; // (success)
626 if (anchorNew
) _anchors
->remove(anchorID
);
627 if (slicePath
) IOFree(slicePath
, slicePathSize
);
629 return false; // (failure)
632 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
634 AnchorTable
* IOMediaBSDClient::getAnchors()
637 // Obtain the table of anchors.
643 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
645 MinorTable
* IOMediaBSDClient::getMinors()
648 // Obtain the table of anchors.
654 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
656 MinorSlot
* IOMediaBSDClient::getMinor(UInt32 minorID
)
659 // Obtain information for the specified minor ID.
662 return _minors
->getMinor(minorID
);
665 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
667 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 0);
669 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
671 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 1);
673 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
675 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 2);
677 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
679 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 3);
681 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
683 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 4);
685 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
687 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 5);
689 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
691 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 6);
693 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
695 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 7);
697 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
699 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 8);
701 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
703 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 9);
705 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
707 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 10);
709 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
711 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 11);
713 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
715 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 12);
717 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
719 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 13);
721 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
723 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 14);
725 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
727 OSMetaClassDefineReservedUnused(IOMediaBSDClient
, 15);
729 // =============================================================================
732 int dkopen(dev_t dev
, int flags
, int devtype
, struct proc
*)
735 // dkopen opens the device (called on each open).
739 IOStorageAccess level
;
742 assert(gIOMediaBSDClient
);
743 assert(S_ISBLK(devtype
) || S_ISCHR(devtype
));
745 gIOMediaBSDClient
->lockForArbitration(); // (disable access)
747 assert(gIOMediaBSDClient
->getMinors());
750 level
= (flags
& FWRITE
) ? kIOStorageAccessReaderWriter
751 : kIOStorageAccessReader
;
752 minor
= gIOMediaBSDClient
->getMinor(minor(dev
));
758 if ( minor
== 0 ) // (is minor valid?)
762 else if ( minor
->isEjecting
) // (is minor in flux?)
766 else if ( (flags
& FWRITE
) ) // (is client a writer?)
768 if ( minor
->bdevWriter
|| minor
->cdevWriter
)
769 level
= kIOStorageAccessNone
;
771 else // (is client a reader?)
773 if ( minor
->bdevOpen
|| minor
->cdevOpen
)
774 level
= kIOStorageAccessNone
;
777 if ( error
== 0 && level
!= kIOStorageAccessNone
) // (issue open/upgrade?)
779 if ( minor
->media
->open(gIOMediaBSDClient
, 0, level
) == false ) // (go)
785 if ( error
== 0 ) // (update state)
787 if ( S_ISBLK(devtype
) )
789 minor
->bdevOpen
= true;
790 if ( (flags
& FWRITE
) ) minor
->bdevWriter
= true;
794 minor
->cdevOpen
= true;
795 if ( (flags
& FWRITE
) ) minor
->cdevWriter
= true;
799 gIOMediaBSDClient
->unlockForArbitration(); // (enable access)
804 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
806 int dkclose(dev_t dev
, int /* flags */, int devtype
, struct proc
*)
809 // dkclose closes the device (called on last close).
815 assert(S_ISBLK(devtype
) || S_ISCHR(devtype
));
817 gIOMediaBSDClient
->lockForArbitration(); // (disable access)
819 minor
= gIOMediaBSDClient
->getMinor(minor(dev
));
820 wasWriter
= (minor
->bdevWriter
|| minor
->cdevWriter
);
822 if ( S_ISBLK(devtype
) ) // (update state)
824 minor
->bdevBlockSize
= minor
->media
->getPreferredBlockSize();
825 minor
->bdevOpen
= false;
826 minor
->bdevWriter
= false;
830 minor
->cdevOpen
= false;
831 minor
->cdevWriter
= false;
834 if ( minor
->isEjecting
) // (is minor in flux?)
837 // We've determined that the specified minor is in ejection flux. This
838 // means we are in a state where the media object has been closed, only
839 // the device node is still open. This happens to the minor subsequent
840 // to a DKIOCEJECT ioctl -- this close resets the flux state to normal.
843 minor
->isEjecting
= false;
845 // If this minor is marked as obsolete, then we've already received the
846 // media's termination notification (stop method), but the minor is yet
847 // to be removed from the table -- remove it now.
849 assert(minor
->bdevOpen
== false);
850 assert(minor
->cdevOpen
== false);
852 if ( minor
->isObsolete
)
853 gIOMediaBSDClient
->getMinors()->remove(minor(dev
));
855 else if ( !minor
->bdevOpen
&& !minor
->cdevOpen
)
858 // We communicate the close down to the media object once all opens are
859 // gone, on both the block and character device nodes.
862 minor
->media
->close(gIOMediaBSDClient
); // (go)
864 else if ( !minor
->bdevWriter
&& !minor
->cdevWriter
&& wasWriter
)
867 // We communicate a downgrade down to the media object once all writers
868 // are gone and while readers still exist.
872 s
= minor
->media
->open(gIOMediaBSDClient
, 0, kIOStorageAccessReader
);
873 assert(s
); // (should never fail, unless deadlock avoided)
876 gIOMediaBSDClient
->unlockForArbitration(); // (enable access)
881 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
883 int dkread(dev_t dev
, struct uio
* uio
, int /* flags */)
886 // dkread reads data from a device.
889 struct dio dio
= { dev
, uio
};
891 return dkreadwrite(&dio
, DKRTYPE_DIO
);
894 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
896 int dkwrite(dev_t dev
, struct uio
* uio
, int /* flags */)
899 // dkwrite writes data to a device.
902 struct dio dio
= { dev
, uio
};
904 return dkreadwrite(&dio
, DKRTYPE_DIO
);
907 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
909 void dkstrategy(struct buf
* bp
)
912 // dkstrategy starts an asynchronous read or write operation. It returns
913 // to the caller as soon as the operation is queued, and completes it via
914 // the biodone function.
917 dkreadwrite(bp
, DKRTYPE_BUF
);
920 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
922 int dkioctl(dev_t dev
, u_long cmd
, caddr_t data
, int, struct proc
*)
925 // dkioctl performs operations other than a read or write.
929 MinorSlot
* minor
= gIOMediaBSDClient
->getMinor(minor(dev
));
931 if ( minor
->isEjecting
) return EBADF
; // (is minor in flux?)
934 // Process the ioctl.
939 case DKIOCGETBLOCKSIZE
: // getBlockSize(int * out);
942 // This ioctl returns the preferred block size of the media object.
945 *(int *)data
= (int) minor
->media
->getPreferredBlockSize();
949 case DKIOCGETBLOCKCOUNT
: // getBlockCount(int * out);
952 // This ioctl returns the size of the media object in blocks. The
953 // implied block size is returned by DKIOCGETBLOCKSIZE.
956 if ( minor
->media
->getPreferredBlockSize() )
957 *(int *)data
= (int) ( minor
->media
->getSize() /
958 minor
->media
->getPreferredBlockSize() );
964 case DKIOCISFORMATTED
: // isFormatted(int * out);
967 // This ioctl returns truth if the media object is formatted.
970 *(int *)data
= (int) minor
->media
->isFormatted();
974 case DKIOCISWRITABLE
: // isWritable(int * out);
977 // This ioctl returns truth if the media object is writable.
980 *(int *)data
= (int) minor
->media
->isWritable();
984 case DKIOCGETLOCATION
: // getLocation(char[128] out);
987 // This ioctl returns the open firmware path for this media object.
990 int l
= sizeof(((struct drive_location
*)data
)->location
);
991 char * p
= ((struct drive_location
*)data
)->location
;
993 if ( minor
->media
->getPath(p
, &l
, gIODTPlane
) && strchr(p
, ':') )
994 strcpy(p
, strchr(p
, ':') + 1); // (strip the plane name)
1000 case DKIOCEJECT
: // eject(void);
1003 // This ioctl asks that the media object be ejected from the device.
1006 IOBlockStorageDriver
* driver
;
1007 MinorTable
* minors
;
1009 driver
= OSDynamicCast( IOBlockStorageDriver
,
1010 minor
->media
->getProvider() );
1011 minors
= gIOMediaBSDClient
->getMinors();
1013 // Determine whether this media has an IOBlockStorageDriver parent.
1015 if ( driver
== 0 ) { error
= ENOTTY
; break; }
1017 // Disable access to tables, matching, opens, closes, terminations.
1019 gIOMediaBSDClient
->lockForArbitration();
1021 // Determine whether there are other opens on the device nodes that
1022 // are associated with this anchor -- the one valid open is the one
1023 // that issued this eject.
1025 if ( minors
->getOpenCountForAnchorID(minor
->anchorID
) > 1 )
1029 // Enable access to tables, matching, opens, closes, and so on.
1031 gIOMediaBSDClient
->unlockForArbitration();
1037 // Mark this minor as being in ejection flux (which means are in
1038 // a state where the media object has been closed but the device
1039 // node is still open; we must reject all future accesses to the
1040 // device node until it is closed; note that we do this both on
1041 // success and failure of the ejection call).
1043 minor
->isEjecting
= true;
1045 // Enable access to tables, matching, opens, closes, and so on.
1047 gIOMediaBSDClient
->unlockForArbitration();
1049 // Close the media object before the ejection request is made.
1051 minor
->media
->close(gIOMediaBSDClient
);
1053 // Open the block storage driver to make the ejection request.
1055 if (driver
->open(gIOMediaBSDClient
, 0, kIOStorageAccessReader
))
1057 // Eject the media from the drive.
1059 status
= driver
->ejectMedia();
1061 // Close the block storage driver.
1063 driver
->close(gIOMediaBSDClient
);
1067 status
= kIOReturnBusy
;
1070 error
= gIOMediaBSDClient
->errnoFromReturn(status
);
1078 // A foreign ioctl was received. Log an error to the console.
1081 IOLog( "%s: ioctl(%s\'%c\',%d,%d) is unsupported.\n",
1083 ((cmd
& IOC_INOUT
) == IOC_INOUT
) ? ("_IOWR,") :
1084 ( ((cmd
& IOC_OUT
) == IOC_OUT
) ? ("_IOR,") :
1085 ( ((cmd
& IOC_IN
) == IOC_IN
) ? ("_IOW,") :
1086 ( ((cmd
& IOC_VOID
) == IOC_VOID
) ? ("_IO,") : "" ) ) ),
1087 (char) IOCGROUP(cmd
),
1089 (int) IOCPARM_LEN(cmd
) );
1096 return error
; // (return error status)
1099 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1101 int dkioctl_bdev(dev_t dev
, u_long cmd
, caddr_t data
, int f
, struct proc
* proc
)
1104 // dkioctl_bdev performs operations other than a read or write, specific to
1105 // the block device.
1109 MinorSlot
* minor
= gIOMediaBSDClient
->getMinor(minor(dev
));
1111 if ( minor
->isEjecting
) return EBADF
; // (is minor in flux?)
1114 // Process the ioctl.
1119 case DKIOCGETBLOCKSIZE
: // getBlockSize(int * out);
1122 // This ioctl returns the preferred (or overrided) block size of the
1126 *(int *)data
= (int) minor
->bdevBlockSize
;
1130 case DKIOCSETBLOCKSIZE
: // setBlockSize(int * in);
1133 // This ioctl overrides the block size for the media object, for the
1134 // duration of all block device opens at this minor.
1137 if ( *(int *)data
> 0 )
1138 minor
->bdevBlockSize
= (UInt64
) (*(int *)data
);
1144 case DKIOCGETBLOCKCOUNT
: // getBlockCount(int * out);
1147 // This ioctl returns the size of the media object in blocks. The
1148 // implied block size is returned by DKIOCGETBLOCKSIZE.
1151 if ( minor
->bdevBlockSize
)
1152 *(int *)data
= (int) ( minor
->media
->getSize() /
1153 minor
->bdevBlockSize
);
1162 // Call the common ioctl handler for all other ioctls.
1165 error
= dkioctl(dev
, cmd
, data
, f
, proc
);
1170 return error
; // (return error status)
1173 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1175 int dksize(dev_t dev
)
1178 // dksize returns the block size of the media.
1180 // This is a departure from BSD 4.4's definition of this function, that is,
1181 // it will not return the size of the disk partition, as would be expected
1182 // in a BSD 4.4 implementation.
1185 MinorSlot
* minor
= gIOMediaBSDClient
->getMinor(minor(dev
));
1187 if ( minor
->isEjecting
) return 0; // (is minor in flux?)
1189 return (int) minor
->bdevBlockSize
; // (return block size)
1192 // =============================================================================
1193 // Support For BSD Functions
1195 inline dev_t
DKR_GET_DEV(dkr_t dkr
, dkrtype_t dkrtype
)
1197 return (dkrtype
== DKRTYPE_BUF
)
1198 ? ((struct buf
*)dkr
)->b_dev
1199 : ((struct dio
*)dkr
)->dev
;
1202 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1204 inline UInt64
DKR_GET_BYTE_COUNT(dkr_t dkr
, dkrtype_t dkrtype
)
1206 return (dkrtype
== DKRTYPE_BUF
)
1207 ? ((struct buf
*)dkr
)->b_bcount
1208 : ((struct dio
*)dkr
)->uio
->uio_resid
;
1211 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1213 inline UInt64
DKR_GET_BYTE_START(dkr_t dkr
, dkrtype_t dkrtype
)
1215 if (dkrtype
== DKRTYPE_BUF
)
1217 struct buf
* bp
= (struct buf
*)dkr
;
1218 MinorSlot
* minor
= gIOMediaBSDClient
->getMinor(minor(bp
->b_dev
));
1220 return bp
->b_blkno
* minor
->bdevBlockSize
;
1222 return ((struct dio
*)dkr
)->uio
->uio_offset
;
1225 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1227 inline bool DKR_IS_READ(dkr_t dkr
, dkrtype_t dkrtype
)
1229 return (dkrtype
== DKRTYPE_BUF
)
1230 ? ((((struct buf
*)dkr
)->b_flags
& B_READ
) == B_READ
)
1231 : ((((struct dio
*)dkr
)->uio
->uio_rw
) == UIO_READ
);
1234 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1236 inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr
, dkrtype_t dkrtype
)
1238 return (dkrtype
== DKRTYPE_BUF
)
1244 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1246 inline bool DKR_IS_RAW(dkr_t dkr
, dkrtype_t dkrtype
)
1248 return (dkrtype
== DKRTYPE_BUF
)
1253 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1255 inline void DKR_SET_BYTE_COUNT(dkr_t dkr
, dkrtype_t dkrtype
, UInt64 bcount
)
1257 if (dkrtype
== DKRTYPE_BUF
)
1258 ((struct buf
*)dkr
)->b_resid
= ((struct buf
*)dkr
)->b_bcount
- bcount
;
1260 ((struct dio
*)dkr
)->uio
->uio_resid
-= bcount
;
1263 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1265 inline void DKR_RUN_COMPLETION(dkr_t dkr
, dkrtype_t dkrtype
, IOReturn status
)
1267 if (dkrtype
== DKRTYPE_BUF
)
1269 struct buf
* bp
= (struct buf
*)dkr
;
1271 bp
->b_error
= gIOMediaBSDClient
->errnoFromReturn(status
); // (error?)
1272 bp
->b_flags
|= (status
!= kIOReturnSuccess
) ? B_ERROR
: 0; // (error?)
1273 biodone(bp
); // (complete request)
1277 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1279 inline IOMemoryDescriptor
* DKR_GET_BUFFER(dkr_t dkr
, dkrtype_t dkrtype
)
1281 if (dkrtype
== DKRTYPE_BUF
)
1283 struct buf
* bp
= (struct buf
*)dkr
;
1285 if ( (bp
->b_flags
& B_VECTORLIST
) )
1287 assert(sizeof(IOPhysicalRange
) == sizeof(iovec
));
1288 assert(sizeof(IOPhysicalRange::address
) == sizeof(iovec::iov_base
));
1289 assert(sizeof(IOPhysicalRange::length
) == sizeof(iovec::iov_len
));
1291 return IOMemoryDescriptor::withPhysicalRanges( // (multiple-range)
1292 (IOPhysicalRange
*) bp
->b_vectorlist
,
1293 (UInt32
) bp
->b_vectorcount
,
1294 (bp
->b_flags
& B_READ
) ? kIODirectionIn
: kIODirectionOut
,
1298 return IOMemoryDescriptor::withAddress( // (single-range)
1299 (vm_address_t
) bp
->b_data
,
1300 (vm_size_t
) bp
->b_bcount
,
1301 (bp
->b_flags
& B_READ
) ? kIODirectionIn
: kIODirectionOut
,
1302 (bp
->b_flags
& B_PHYS
) ? get_user_task() : get_kernel_task() );
1306 struct uio
* uio
= ((struct dio
*)dkr
)->uio
;
1308 assert(sizeof(IOVirtualRange
) == sizeof(iovec
));
1309 assert(sizeof(IOVirtualRange::address
) == sizeof(iovec::iov_base
));
1310 assert(sizeof(IOVirtualRange::length
) == sizeof(iovec::iov_len
));
1312 return IOMemoryDescriptor::withRanges( // (multiple-range)
1313 (IOVirtualRange
*) uio
->uio_iov
,
1314 (UInt32
) uio
->uio_iovcnt
,
1315 (uio
->uio_rw
== UIO_READ
) ? kIODirectionIn
: kIODirectionOut
,
1316 (uio
->uio_segflg
!= UIO_SYSSPACE
) ? get_user_task() : get_kernel_task(),
1321 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1323 int dkreadwrite(dkr_t dkr
, dkrtype_t dkrtype
)
1326 // dkreadwrite performs a read or write operation.
1329 IOMemoryDescriptor
* buffer
;
1330 register UInt64 byteCount
;
1331 register UInt64 byteStart
;
1336 minor
= gIOMediaBSDClient
->getMinor(minor(DKR_GET_DEV(dkr
, dkrtype
)));
1338 if ( minor
->isEjecting
) // (is minor in flux?)
1340 status
= kIOReturnNoMedia
;
1341 goto dkreadwriteErr
;
1344 if ( minor
->media
->isFormatted() == false ) // (is media unformatted?)
1346 status
= kIOReturnUnformattedMedia
;
1347 goto dkreadwriteErr
;
1350 byteCount
= DKR_GET_BYTE_COUNT(dkr
, dkrtype
); // (get byte count)
1351 byteStart
= DKR_GET_BYTE_START(dkr
, dkrtype
); // (get byte start)
1352 mediaSize
= minor
->media
->getSize(); // (get media size)
1355 // Reads that start at (or perhaps past) the end-of-media are not considered
1356 // errors, even though no data is transferred, while writes at (or past) the
1357 // end-of-media do indeed return errors under BSD semantics.
1360 if ( byteStart
>= mediaSize
) // (is start at or past the end-of-media?)
1362 status
= DKR_IS_READ(dkr
,dkrtype
) ? kIOReturnSuccess
: kIOReturnIOError
;
1363 goto dkreadwriteErr
;
1367 // Reads and writes, via the character device, that do not start or end on a
1368 // media block boundary are considered errors under BSD semantics.
1371 if ( DKR_IS_RAW(dkr
, dkrtype
) )
1373 UInt64 mediaBlockSize
= minor
->media
->getPreferredBlockSize();
1375 if ( (byteStart
% mediaBlockSize
) || (byteCount
% mediaBlockSize
) )
1377 status
= kIOReturnNotAligned
;
1378 goto dkreadwriteErr
;
1383 // Build a descriptor which describes the buffer involved in the transfer.
1386 buffer
= DKR_GET_BUFFER(dkr
, dkrtype
);
1388 if ( buffer
== 0 ) // (no buffer?)
1390 status
= kIOReturnNoMemory
;
1391 goto dkreadwriteErr
;
1395 // Reads and writes that extend beyond the end-of-media are not considered
1396 // errors under BSD semantics. We are to transfer as many bytes as can be
1397 // read or written from the medium and return no error. This differs from
1398 // IOMedia semantics which is to fail the entire request without copying a
1399 // single byte should it include something past the end-of-media. We must
1400 // adapt the IOMedia semantics to look like BSD semantics here.
1402 // Clip the transfer buffer should this be a short read or write request.
1405 if ( byteCount
> mediaSize
- byteStart
) // (clip at end-of-media)
1407 IOMemoryDescriptor
* originalBuffer
= buffer
;
1409 buffer
= IOMemoryDescriptor::withSubRange(
1410 /* descriptor */ originalBuffer
,
1412 /* withLength */ mediaSize
- byteStart
,
1413 /* withDirection */ originalBuffer
->getDirection() );
1415 originalBuffer
->release(); // (either retained above or about to fail)
1417 if ( buffer
== 0 ) // (no buffer?)
1419 status
= kIOReturnNoMemory
;
1420 goto dkreadwriteErr
;
1425 // Execute the transfer.
1428 if ( DKR_IS_ASYNCHRONOUS(dkr
, dkrtype
) ) // (an asynchronous request?)
1430 IOStorageCompletion completion
;
1432 completion
.target
= dkr
;
1433 completion
.action
= dkreadwritecompletion
;
1434 completion
.parameter
= (void *) dkrtype
;
1436 if ( DKR_IS_READ(dkr
, dkrtype
) ) // (a read?)
1438 minor
->media
->read( /* client */ gIOMediaBSDClient
,
1439 /* byteStart */ byteStart
,
1440 /* buffer */ buffer
,
1441 /* completion */ completion
); // (go)
1445 minor
->media
->write( /* client */ gIOMediaBSDClient
,
1446 /* byteStart */ byteStart
,
1447 /* buffer */ buffer
,
1448 /* completion */ completion
); // (go)
1451 status
= kIOReturnSuccess
;
1453 else // (is this a synchronous request?)
1455 if ( DKR_IS_READ(dkr
, dkrtype
) ) // (a read?)
1457 ///m:2333367:workaround:commented:start
1458 // status = minor->media->read(
1459 ///m:2333367:workaround:commented:stop
1460 ///m:2333367:workaround:added:start
1461 status
= minor
->media
->IOStorage::read(
1462 ///m:2333367:workaround:added:stop
1463 /* client */ gIOMediaBSDClient
,
1464 /* byteStart */ byteStart
,
1465 /* buffer */ buffer
,
1466 /* actualByteCount */ &byteCount
); // (go)
1470 ///m:2333367:workaround:commented:start
1471 // status = minor->media->write(
1472 ///m:2333367:workaround:commented:stop
1473 ///m:2333367:workaround:added:start
1474 status
= minor
->media
->IOStorage::write(
1475 ///m:2333367:workaround:added:stop
1476 /* client */ gIOMediaBSDClient
,
1477 /* byteStart */ byteStart
,
1478 /* buffer */ buffer
,
1479 /* actualByteCount */ &byteCount
); // (go)
1482 dkreadwritecompletion(dkr
, (void *)dkrtype
, status
, byteCount
);
1486 // We release our retain on the buffer now, even though in the asynchronous
1487 // case, the object needs to exist for the duration of the transfer. While
1488 // this might appear to be a mistake, it is not. The layers below us will
1489 // have retained the buffer themselves.
1492 buffer
->release(); // (release our retain on the buffer)
1494 return gIOMediaBSDClient
->errnoFromReturn(status
); // (return error status)
1498 dkreadwritecompletion(dkr
, (void *)dkrtype
, status
, 0);
1500 return gIOMediaBSDClient
->errnoFromReturn(status
); // (return error status)
1503 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1505 void dkreadwritecompletion( void * target
,
1508 UInt64 actualByteCount
)
1511 // dkreadwritecompletion cleans up after a read or write operation.
1514 dkr_t dkr
= (dkr_t
) target
;
1515 dkrtype_t dkrtype
= (dkrtype_t
) (int) parameter
;
1516 dev_t dev
= DKR_GET_DEV(dkr
, dkrtype
);
1518 #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1519 UInt32 anchorID
= gIOMediaBSDClient
->getMinor(minor(dev
))->anchorID
;
1521 if ( anchorID
< DK_NDRIVE
)
1523 IOBlockStorageDriver
* d
= dk_drive
[anchorID
];
1527 dk_xfer
[anchorID
] = (long)
1528 ( d
->getStatistic(IOBlockStorageDriver::kStatisticsReads
) +
1529 d
->getStatistic(IOBlockStorageDriver::kStatisticsWrites
) );
1530 dk_wds
[anchorID
] = (long) (8 *
1531 ( d
->getStatistic(IOBlockStorageDriver::kStatisticsBytesRead
) +
1532 d
->getStatistic(IOBlockStorageDriver::kStatisticsBytesWritten
)) );
1535 #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1537 if ( status
!= kIOReturnSuccess
) // (log errors to the console)
1540 gIOMediaBSDClient
->getMinor(minor(dev
))->name
,
1541 gIOMediaBSDClient
->stringFromReturn(status
) );
1544 DKR_SET_BYTE_COUNT(dkr
, dkrtype
, actualByteCount
); // (set byte count)
1545 DKR_RUN_COMPLETION(dkr
, dkrtype
, status
); // (run completion)
1548 // =============================================================================
1549 // AnchorTable Class
1551 AnchorTable::AnchorTable(UInt32 growCount
, UInt32 maxCount
)
1554 // Initialize this object's minimal state.
1559 _tableGrowCount
= growCount
;
1560 _tableMaxCount
= maxCount
;
1563 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1565 AnchorTable::~AnchorTable()
1568 // Free all of this object's outstanding resources.
1571 for ( UInt32 anchorID
= 0; anchorID
< _tableCount
; anchorID
++ )
1572 if ( _table
[anchorID
].isAssigned
) remove(anchorID
);
1574 if ( _table
) IODelete(_table
, AnchorSlot
, _tableCount
);
1577 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1579 UInt32
AnchorTable::insert(IOService
* anchor
)
1582 // This method inserts the specified anchor into an unassigned slot in the
1583 // anchor table and returns its ID (or kInvalidAnchorID on a failure).
1585 // Note that the anchor is transparently removed from the table should the
1586 // anchor terminate (or it is at least marked obsolete, should references
1587 // to the anchor still exist in the minor table).
1591 IONotifier
* notifier
;
1593 // Search for an unassigned slot in the anchor table.
1595 for ( anchorID
= 0; anchorID
< _tableCount
; anchorID
++ )
1596 if ( _table
[anchorID
].isAssigned
== false ) break;
1598 // Was an unassigned slot found? If not, grow the table.
1600 if ( anchorID
== _tableCount
)
1602 AnchorSlot
* newTable
;
1603 UInt32 newTableCount
;
1605 // We must expand the anchor table since no more slots are available.
1607 if ( _tableCount
>= _tableMaxCount
) return kInvalidAnchorID
;
1609 newTableCount
= min(_tableGrowCount
+ _tableCount
, _tableMaxCount
);
1610 newTable
= IONew(AnchorSlot
, newTableCount
);
1612 if ( newTable
== 0 ) return kInvalidAnchorID
;
1614 bzero(newTable
, newTableCount
* sizeof(AnchorSlot
));
1616 // Copy over the old table's entries, then free the old table.
1620 bcopy(_table
, newTable
, _tableCount
* sizeof(AnchorSlot
));
1621 IODelete(_table
, AnchorSlot
, _tableCount
);
1624 // Obtain the next unassigned index (simple since we know the size of
1625 // the old table), then update our instance variables to reflect the
1628 anchorID
= _tableCount
;
1630 _tableCount
= newTableCount
;
1633 // Create a notification handler for the anchor's termination (post-stop);
1634 // the handler will remove the anchor transparently from the table if the
1635 // anchor terminates (or at least marks it obsolete, if references to the
1636 // anchor still exist in the minor table).
1638 notifier
= anchor
->registerInterest(
1639 /* type */ gIOGeneralInterest
,
1640 /* action */ anchorWasNotified
,
1642 /* parameter */ 0 );
1644 if ( notifier
== 0 ) return kInvalidAnchorID
;
1646 // Zero the new slot, fill it in, and retain the anchor object.
1648 bzero(&_table
[anchorID
], sizeof(AnchorSlot
)); // (zero slot)
1650 _table
[anchorID
].isAssigned
= true; // (fill in slot)
1651 _table
[anchorID
].isObsolete
= false;
1652 _table
[anchorID
].anchor
= anchor
;
1653 _table
[anchorID
].notifier
= notifier
;
1655 _table
[anchorID
].anchor
->retain(); // (retain anchor)
1657 #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1658 if ( anchorID
< DK_NDRIVE
)
1660 dk_drive
[anchorID
] = OSDynamicCast(IOBlockStorageDriver
, anchor
);
1661 if ( anchorID
+ 1 > (UInt32
) dk_ndrive
) dk_ndrive
= anchorID
+ 1;
1663 #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1668 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1670 void AnchorTable::remove(UInt32 anchorID
)
1673 // This method removes the specified anchor from the anchor table.
1676 assert(anchorID
< _tableCount
);
1677 assert(_table
[anchorID
].isAssigned
);
1679 // Release the resources retained in the anchor slot and zero it.
1681 _table
[anchorID
].notifier
->remove();
1682 _table
[anchorID
].anchor
->release(); // (release anchor)
1684 bzero(&_table
[anchorID
], sizeof(AnchorSlot
)); // (zero slot)
1686 #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1687 if ( anchorID
< DK_NDRIVE
)
1689 dk_drive
[anchorID
] = 0;
1690 for (dk_ndrive
= DK_NDRIVE
; dk_ndrive
; dk_ndrive
--)
1692 if ( dk_drive
[dk_ndrive
- 1] ) break;
1695 #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1698 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1700 void AnchorTable::obsolete(UInt32 anchorID
)
1703 // This method obsoletes the specified anchor, that is, the slot is marked
1704 // as obsolete and will be removed later via the minor table remove method
1705 // once it detects references to the anchor ID drop to 0. Once obsoleted,
1706 // the anchor can be considered to be removed, since it will not appear in
1707 // locate searches, even though behind the scenes it still occupies a slot.
1710 assert(anchorID
< _tableCount
);
1711 assert(_table
[anchorID
].isAssigned
);
1713 // Mark the anchor as obsolete so that it can be removed from the table as
1714 // soon as all its references go away (minor table's responsibility).
1716 _table
[anchorID
].isObsolete
= true;
1719 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1721 UInt32
AnchorTable::locate(IOService
* anchor
)
1724 // This method searches for the specified anchor in the anchor table and
1725 // returns its ID (or kInvalidAnchorID on a failure). It ignores slots
1726 // marked as obsolete.
1729 for (UInt32 anchorID
= 0; anchorID
< _tableCount
; anchorID
++)
1731 if ( _table
[anchorID
].isAssigned
!= false &&
1732 _table
[anchorID
].isObsolete
== false &&
1733 _table
[anchorID
].anchor
== anchor
) return anchorID
;
1736 return kInvalidAnchorID
;
1739 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1741 bool AnchorTable::isObsolete(UInt32 anchorID
)
1744 // Determine whether the specified anchor ID is marked as obsolete.
1747 assert(anchorID
< _tableCount
);
1748 assert(_table
[anchorID
].isAssigned
);
1750 return _table
[anchorID
].isObsolete
? true : false;
1753 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1755 IOReturn
AnchorTable::anchorWasNotified( void * /* target */,
1756 void * /* parameter */,
1759 void * /* messageArgument */,
1760 vm_size_t
/* messageArgumentSize */ )
1763 // Notification handler for anchors.
1768 assert(gIOMediaBSDClient
);
1770 // Determine whether this is a termination notification (post-stop).
1772 if ( messageType
!= kIOMessageServiceIsTerminated
)
1773 return kIOReturnSuccess
;
1775 // Disable access to tables, matching, opens, closes, and terminations.
1777 gIOMediaBSDClient
->lockForArbitration();
1779 // Determine whether this anchor is in the anchor table (obsolete occurences
1780 // are skipped in the search, as appropriate, since those anchor IDs will be
1781 // removed as it is).
1783 anchorID
= gIOMediaBSDClient
->getAnchors()->locate(anchor
);
1785 if ( anchorID
!= kInvalidAnchorID
)
1787 // Determine whether this anchor is still has references in the minor
1788 // table. If it does, we mark the the anchor as obsolete so that it
1789 // will be removed later, once references to it go to zero (which is
1790 // handled by MinorTable::remove).
1792 if ( gIOMediaBSDClient
->getMinors()->hasReferencesToAnchorID(anchorID
) )
1793 gIOMediaBSDClient
->getAnchors()->obsolete(anchorID
);
1795 gIOMediaBSDClient
->getAnchors()->remove(anchorID
);
1798 // Enable access to tables, matching, opens, closes, and terminations.
1800 gIOMediaBSDClient
->unlockForArbitration();
1802 return kIOReturnSuccess
;
1805 // =============================================================================
1808 MinorTable::MinorTable(UInt32 growCount
, UInt32 maxCount
)
1811 // Initialize this object's minimal state.
1816 _tableGrowCount
= growCount
;
1817 _tableMaxCount
= maxCount
;
1820 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1822 MinorTable::~MinorTable()
1825 // Free all of this object's outstanding resources.
1828 for ( UInt32 minorID
= 0; minorID
< _tableCount
; minorID
++ )
1829 if ( _table
[minorID
].isAssigned
) remove(minorID
);
1831 if ( _table
) IODelete(_table
, MinorSlot
, _tableCount
);
1834 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1836 UInt32
MinorTable::insert(IOMedia
* media
, UInt32 anchorID
, char * slicePath
)
1839 // This method inserts the specified media/anchorID pair into an unassigned
1840 // slot in the minor table and returns its ID (or kInvalidMinorID on error).
1842 // Note that the bdev and cdev nodes are published as a result of this call,
1843 // with the name "[r]disk<anchorID><slicePath>". For instance, "disk2s3s1"
1844 // for an anchorID of 2 and slicePath of "s3s1".
1851 UInt32 minorNameSize
;
1853 // Search for an unassigned slot in the minor table.
1855 for ( minorID
= 0; minorID
< _tableCount
; minorID
++ )
1856 if ( _table
[minorID
].isAssigned
== false ) break;
1858 // Was an unassigned slot found? If not, grow the table.
1860 if ( minorID
== _tableCount
)
1862 MinorSlot
* newTable
;
1863 UInt32 newTableCount
;
1865 // We must expand the minor table since no more slots are available.
1867 if ( _tableCount
>= _tableMaxCount
) return kInvalidMinorID
;
1869 newTableCount
= min(_tableGrowCount
+ _tableCount
, _tableMaxCount
);
1870 newTable
= IONew(MinorSlot
, newTableCount
);
1872 if ( newTable
== 0 ) return kInvalidMinorID
;
1874 bzero(newTable
, newTableCount
* sizeof(MinorSlot
));
1876 // Copy over the old table's entries, then free the old table.
1880 bcopy(_table
, newTable
, _tableCount
* sizeof(MinorSlot
));
1881 IODelete(_table
, MinorSlot
, _tableCount
);
1884 // Obtain the next unassigned index (simple since we know the size of
1885 // the old table), then update our instance variables to reflect the
1888 minorID
= _tableCount
;
1890 _tableCount
= newTableCount
;
1893 // Create a buffer large enough to hold the full name of the minor.
1895 minorNameSize
= strlen("disk#");
1896 for (unsigned temp
= anchorID
; temp
>= 10; temp
/= 10) minorNameSize
++;
1897 minorNameSize
+= strlen(slicePath
);
1899 minorName
= IONew(char, minorNameSize
);
1901 // Create a block and character device node in BSD for this media.
1903 bdevNode
= devfs_make_node( /* dev */ makedev(kMajor
, minorID
),
1904 /* type */ DEVFS_BLOCK
,
1905 /* owner */ UID_ROOT
,
1906 /* group */ GID_OPERATOR
,
1907 /* permission */ media
->isWritable()?0640:0440,
1908 /* name (fmt) */ "disk%d%s",
1909 /* name (arg) */ anchorID
,
1910 /* name (arg) */ slicePath
);
1912 cdevNode
= devfs_make_node( /* dev */ makedev(kMajor
, minorID
),
1913 /* type */ DEVFS_CHAR
,
1914 /* owner */ UID_ROOT
,
1915 /* group */ GID_OPERATOR
,
1916 /* permission */ media
->isWritable()?0640:0440,
1917 /* name (fmt) */ "rdisk%d%s",
1918 /* name (arg) */ anchorID
,
1919 /* name (arg) */ slicePath
);
1921 if ( minorName
== 0 || bdevNode
== 0 || cdevNode
== 0 )
1923 if ( cdevNode
) devfs_remove(cdevNode
);
1924 if ( bdevNode
) devfs_remove(bdevNode
);
1925 if ( minorName
) IODelete(minorName
, char, minorNameSize
);
1927 return kInvalidMinorID
;
1930 // Construct a name for the node.
1932 sprintf(minorName
, "disk%ld%s", anchorID
, slicePath
);
1933 assert(strlen(minorName
) + 1 == minorNameSize
);
1935 // Zero the new slot, fill it in, and retain the media object.
1937 bzero(&_table
[minorID
], sizeof(MinorSlot
)); // (zero slot)
1939 _table
[minorID
].isAssigned
= true; // (fill in slot)
1940 _table
[minorID
].isEjecting
= false;
1941 _table
[minorID
].isObsolete
= false;
1942 _table
[minorID
].anchorID
= anchorID
;
1943 _table
[minorID
].media
= media
;
1944 _table
[minorID
].name
= minorName
;
1945 _table
[minorID
].bdevBlockSize
= media
->getPreferredBlockSize();
1946 _table
[minorID
].bdevNode
= bdevNode
;
1947 _table
[minorID
].bdevOpen
= false;
1948 _table
[minorID
].cdevNode
= cdevNode
;
1949 _table
[minorID
].cdevOpen
= false;
1951 _table
[minorID
].media
->retain(); // (retain media)
1956 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1958 void MinorTable::remove(UInt32 minorID
)
1961 // This method removes the specified minor from the minor table.
1966 assert(minorID
< _tableCount
);
1967 assert(_table
[minorID
].isAssigned
);
1969 assert(_table
[minorID
].isEjecting
== false);
1970 assert(_table
[minorID
].bdevOpen
== false);
1971 assert(_table
[minorID
].cdevOpen
== false);
1973 anchorID
= _table
[minorID
].anchorID
;
1975 // Release the resources retained in the minor slot and zero it.
1977 devfs_remove(_table
[minorID
].cdevNode
);
1978 devfs_remove(_table
[minorID
].bdevNode
);
1979 IODelete(_table
[minorID
].name
, char, strlen(_table
[minorID
].name
) + 1);
1980 _table
[minorID
].media
->release(); // (release media)
1982 bzero(&_table
[minorID
], sizeof(MinorSlot
)); // (zero slot)
1984 // Determine whether the associated anchor ID is marked as obsolete. If it
1985 // is and there are no other references to the anchor ID in the minor table,
1986 // we remove the anchor ID from the anchor table.
1988 assert(gIOMediaBSDClient
);
1990 if ( gIOMediaBSDClient
->getAnchors()->isObsolete(anchorID
) )
1992 if ( hasReferencesToAnchorID(anchorID
) == false )
1993 gIOMediaBSDClient
->getAnchors()->remove(anchorID
);
1997 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1999 UInt32
MinorTable::locate(IOMedia
* media
)
2002 // This method searches for the specified media in the minor table and
2003 // returns its ID (or kInvalidMinorID on an error). It ignores slots
2004 // marked as obsolete.
2007 for (UInt32 minorID
= 0; minorID
< _tableCount
; minorID
++)
2009 if ( _table
[minorID
].isAssigned
!= false &&
2010 _table
[minorID
].isObsolete
== false &&
2011 _table
[minorID
].media
== media
) return minorID
;
2014 return kInvalidMinorID
;
2017 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2019 UInt32
MinorTable::getOpenCountForAnchorID(UInt32 anchorID
)
2022 // This method obtains a count of opens on the minors associated with the
2023 // specified anchor ID. A block device open is counted separately from a
2024 // character device open.
2029 for ( UInt32 minorID
= 0; minorID
< _tableCount
; minorID
++ )
2031 if ( _table
[minorID
].isAssigned
!= false &&
2032 _table
[minorID
].anchorID
== anchorID
)
2034 opens
+= (_table
[minorID
].bdevOpen
) ? 1 : 0;
2035 opens
+= (_table
[minorID
].cdevOpen
) ? 1 : 0;
2042 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2044 IOMedia
* MinorTable::getWholeMediaAtAnchorID(UInt32 anchorID
)
2047 // This method obtains the whole media associated with the specified anchor
2051 for ( UInt32 minorID
= 0; minorID
< _tableCount
; minorID
++ )
2053 if ( _table
[minorID
].isAssigned
!= false &&
2054 _table
[minorID
].anchorID
== anchorID
&&
2055 _table
[minorID
].media
->isWhole() ) return _table
[minorID
].media
;
2061 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2063 bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID
)
2066 // This method determines whether there are assigned minors in the minor
2067 // table that refer to the specified anchor ID.
2070 for ( UInt32 minorID
= 0; minorID
< _tableCount
; minorID
++ )
2072 if ( _table
[minorID
].isAssigned
!= false &&
2073 _table
[minorID
].anchorID
== anchorID
) return true;
2079 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2081 MinorSlot
* MinorTable::getMinor(UInt32 minorID
)
2084 // Obtain the structure describing the specified minor.
2087 if ( minorID
< _tableCount
&& _table
[minorID
].isAssigned
)
2088 return &_table
[minorID
];
2093 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2095 void MinorTable::obsolete(UInt32 minorID
)
2098 // This method obsoletes the specified minor, that is, the slot is marked
2099 // as obsolete and will be removed later via the dkclose function once it
2100 // detects the last close arrive. Once obsoleted, the minor can be cons-
2101 // idered to be removed, since it will not appear in locate searches.
2104 assert(minorID
< _tableCount
);
2105 assert(_table
[minorID
].isAssigned
);
2107 // Mark the minor as obsolete so that it can be removed from the table as
2108 // soon as the last close arrives (dkclose function's responsibility).
2110 _table
[minorID
].isObsolete
= true;
2113 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2115 bool MinorTable::isObsolete(UInt32 minorID
)
2118 // Determine whether the specified minor ID is marked as obsolete.
2121 assert(minorID
< _tableCount
);
2122 assert(_table
[minorID
].isAssigned
);
2124 return _table
[minorID
].isObsolete
? true : false;