]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOStorage/IOMediaBSDClient.cpp
13b3eb6f1a12e4ff1d90254630bc79efd6811fb2
[apple/xnu.git] / iokit / Families / IOStorage / IOMediaBSDClient.cpp
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/IOBlockStorageDriver.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 IOBlockStorageDriver * 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 anchorWasNotified( void * target,
145 void * parameter,
146 UInt32 messageType,
147 IOService * provider,
148 void * messageArgument,
149 vm_size_t messageArgumentSize );
150
151 public:
152 AnchorTable(UInt32 growCount, UInt32 maxCount);
153 ~AnchorTable();
154
155 UInt32 insert(IOService * anchor);
156 UInt32 locate(IOService * anchor);
157 void obsolete(UInt32 anchorID);
158 void remove(UInt32 anchorID);
159
160 bool isObsolete(UInt32 anchorID);
161 };
162
163 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
164
165 const UInt32 kInvalidMinorID = (UInt32) (-1);
166
167 struct MinorSlot
168 {
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)
172
173 UInt32 anchorID; // (minor's associated anchor ID)
174 IOMedia * media; // (minor's media object)
175 char * name; // (minor's name, private allocation space)
176
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)
181
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)
185 };
186
187 class MinorTable
188 {
189 protected:
190 MinorSlot * _table;
191 UInt32 _tableCount;
192 UInt32 _tableGrowCount;
193 UInt32 _tableMaxCount;
194
195 public:
196 MinorTable(UInt32 growCount, UInt32 maxCount);
197 ~MinorTable();
198
199 UInt32 insert(IOMedia * media, UInt32 anchorID, char * slicePath);
200 UInt32 locate(IOMedia * media);
201 void obsolete(UInt32 minorID);
202 void remove(UInt32 minorID);
203
204 bool isObsolete(UInt32 minorID);
205
206 MinorSlot * getMinor(UInt32 minorID);
207
208 UInt32 getOpenCountForAnchorID(UInt32 anchorID);
209 IOMedia * getWholeMediaAtAnchorID(UInt32 anchorID);
210 bool hasReferencesToAnchorID(UInt32 anchorID);
211 };
212
213 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
214
215 bool IOMediaBSDClient::init(OSDictionary * properties = 0)
216 {
217 //
218 // Initialize this object's minimal state.
219 //
220
221 if ( super::init(properties) == false ) return false;
222
223 _anchors = new AnchorTable(kAnchorsGrowCount, kAnchorsMaxCount);
224 _bdevswInstalled = false;
225 _cdevswInstalled = false;
226 _minors = new MinorTable(kMinorsGrowCount, kMinorsMaxCount);
227 _notifier = 0;
228
229 if ( _anchors == 0 || _minors == 0 ) return false;
230
231 return true;
232 }
233
234 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
235
236 void IOMediaBSDClient::free()
237 {
238 //
239 // Free all of this object's outstanding resources.
240 //
241
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;
247
248 super::free();
249 }
250
251 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
252
253 bool IOMediaBSDClient::start(IOService * provider)
254 {
255 //
256 // This method is called once we have been attached to the provider object.
257 //
258
259 assert(gIOMediaBSDClient == 0);
260
261 // Ask our superclass' opinion.
262
263 if ( super::start(provider) == false ) return false;
264
265 // Establish a global reference to this instance.
266
267 gIOMediaBSDClient = this;
268
269 // Install bdevsw and cdevsw functions.
270
271 _bdevswInstalled = (bdevsw_add(kMajor, &bdevswFunctions) == kMajor);
272 _cdevswInstalled = (cdevsw_add(kMajor, &cdevswFunctions) == kMajor);
273
274 if ( _bdevswInstalled == false && _cdevswInstalled == false ) return false;
275
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.
280
281 _notifier = addNotification( /* type */ gIOFirstPublishNotification,
282 /* description */ serviceMatching("IOMedia"),
283 /* action */ mediaHasArrived,
284 /* target */ this,
285 /* parameter */ 0,
286 /* priority */ 10 );
287
288 if ( _notifier == 0 ) return false;
289
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.
294
295 registerService();
296
297 return true;
298 }
299
300 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
301
302 void IOMediaBSDClient::stop(IOService * provider)
303 {
304 //
305 // This method is called before we are detached from the provider object.
306 //
307
308 IOMedia * media = (IOMedia *) provider;
309 UInt32 minorID = 0;
310
311 // Disable access to tables, matching, opens, closes, and terminations.
312
313 gIOMediaBSDClient->lockForArbitration();
314
315 // Find the minor assigned to this media.
316
317 minorID = _minors->locate(media);
318 assert(minorID != kInvalidMinorID);
319
320 // State our assumptions.
321
322 assert(media->isOpen() == false);
323
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.
328
329 if ( _minors->getMinor(minorID)->isEjecting ) // (is minor in flux?)
330 {
331 assert(_minors->isObsolete(minorID) == false);
332
333 _minors->obsolete(minorID);
334 }
335 else
336 {
337 assert(_minors->getMinor(minorID)->bdevOpen == false);
338 assert(_minors->getMinor(minorID)->cdevOpen == false);
339
340 _minors->remove(minorID);
341 }
342
343 // Enable access to tables, matching, opens, closes, and terminations.
344
345 gIOMediaBSDClient->unlockForArbitration();
346
347 // Call upon the superclass to finish its work.
348
349 super::stop(media);
350 }
351
352 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
353
354 bool IOMediaBSDClient::mediaHasArrived( void * /* target */,
355 void * /* parameter */,
356 IOService * service )
357 {
358 //
359 // Notification handler for media arrivals.
360 //
361
362 IOMedia * media = OSDynamicCast(IOMedia, service);
363 bool success = false;
364
365 assert(gIOMediaBSDClient);
366
367 // Attach the media-bsd-client object as a client of the new media object.
368
369 if ( media && gIOMediaBSDClient->attach(media) )
370 {
371 // Disable access to tables, matching, opens, closes, and terminations.
372
373 gIOMediaBSDClient->lockForArbitration();
374
375 // Create bdevsw and cdevsw nodes for the new media object.
376
377 success = gIOMediaBSDClient->createNodes(media);
378
379 // Enable access to tables, matching, opens, closes, and terminations.
380
381 gIOMediaBSDClient->unlockForArbitration();
382
383 // Detach the media-bsd-client object from the media object on error.
384
385 if (success == false) gIOMediaBSDClient->detach(media);
386 }
387
388 return true; // (meaningless return value)
389 }
390
391 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
392
393 IOMedia * IOMediaBSDClient::getWholeMedia( IOMedia * media,
394 UInt32 * slicePathSize = 0,
395 char * slicePath = 0 )
396 {
397 //
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.
400 //
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.
403 //
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.
408 //
409
410 UInt32 depth = 1;
411 UInt32 position = sizeof('\0');
412 IOService * service = 0;
413
414 assert(slicePath == 0 || slicePathSize != 0);
415
416 // Search the registry for the parent whole media for this media.
417
418 for ( service = media; service; service = service->getProvider() )
419 {
420 if ( OSDynamicCast(IOMedia, service) ) // (is it a media?)
421 {
422 if ( ((IOMedia *)service)->isWhole() ) // (is it a whole media?)
423 {
424 if ( slicePath ) // (are we building the slice path?)
425 {
426 slicePath[*slicePathSize - 1] = 0; // (zero terminate path)
427
428 if ( position < *slicePathSize ) // (need to move path?)
429 {
430 memmove( slicePath, // (move path to start of buffer)
431 slicePath + (*slicePathSize - position),
432 position );
433 }
434 }
435 else if ( slicePathSize ) // (report size req'd for slice path?)
436 {
437 *slicePathSize = position;
438 }
439
440 return (IOMedia *)service; // (return the whole media)
441 }
442
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.
446
447 const char * location = service->getLocation();
448
449 if ( location == 0 ) // (no location on non-whole media?)
450 {
451 if ( service == media ) IOLog(kMsgNoLocation, media->getName());
452 return 0;
453 }
454
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.
459
460 position += sizeof('s') + strlen(location);
461
462 if ( slicePath ) // (build the slice path?)
463 {
464 char * path = slicePath + *slicePathSize - position;
465
466 if ( position > *slicePathSize ) { assert(0); return 0; }
467
468 *path = 's';
469 strncpy(path + sizeof('s'), location, strlen(location));
470 }
471
472 depth += 1;
473 }
474 }
475
476 // If we've fallen through, then the whole media was never found.
477
478 if ( depth == 1 ) IOLog(kMsgNoWhole, media->getName());
479 return 0;
480 }
481
482 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
483
484 bool IOMediaBSDClient::createNodes(IOMedia * media)
485 {
486 //
487 // Create bdevsw and cdevsw nodes for the given media object.
488 //
489 // This method assumes that the arbitration lock is held.
490 //
491
492 IOService * anchor;
493 UInt32 anchorID;
494 bool anchorNew = false;
495 UInt32 minorID;
496 char * slicePath = 0;
497 UInt32 slicePathSize;
498 IOMedia * whole;
499
500 //
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).
508 //
509
510 whole = getWholeMedia(media, &slicePathSize);
511 if ( whole == 0 ) return false;
512
513 anchor = whole->getProvider();
514 if ( anchor == 0 ) return false;
515
516 //
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
520 // anchor table.
521 //
522
523 anchorID = _anchors->locate(anchor);
524
525 if ( anchorID != kInvalidAnchorID )
526 {
527 //
528 // The anchor does exist in the table, however we've got more to check.
529 //
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.
542 //
543 // A few additional notes:
544 //
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.
549 //
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
555 // case.
556 //
557
558 IOMedia * wholeInTable = _minors->getWholeMediaAtAnchorID(anchorID);
559
560 if ( wholeInTable == 0 ) // (is an existing whole media in our tables?)
561 {
562 if ( _minors->hasReferencesToAnchorID(anchorID) ) // (any medias?)
563 {
564 _anchors->obsolete(anchorID); // (obsolete old anchor ID)
565 anchorID = kInvalidAnchorID; // ( request new anchor ID)
566 } // (else, all is well)
567 }
568 else if ( whole != wholeInTable ) // (old whole media not same as new?)
569 {
570 if ( wholeInTable->isInactive() ) // (is it inactive/terminating?)
571 {
572 _anchors->obsolete(anchorID); // (obsolete old anchor ID)
573 anchorID = kInvalidAnchorID; // ( request new anchor ID)
574 }
575 else // (peer active whole medias detected, log error)
576 {
577 if ( whole == media ) IOLog(kMsgBadWhole, whole->getName());
578 return false;
579 }
580 } // (else, all is well)
581 }
582
583 if ( anchorID == kInvalidAnchorID )
584 {
585 anchorID = _anchors->insert(anchor); // (get new anchor ID)
586 if ( anchorID == kInvalidAnchorID ) return false;
587 anchorNew = true;
588 }
589
590 //
591 // Allocate space for and build the slice path for the device node names.
592 //
593
594 slicePath = (char *) IOMalloc(slicePathSize);
595 if ( slicePath == 0 ) goto createNodesErr;
596
597 whole = getWholeMedia(media, &slicePathSize, slicePath);
598 assert(whole);
599
600 //
601 // Insert the new media into our minor table (we're almost done :-).
602 //
603
604 minorID = _minors->insert(media, anchorID, slicePath);
605 if ( minorID == kInvalidMinorID ) goto createNodesErr;
606
607 //
608 // Create the required properties on the media.
609 //
610
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")
615
616 //
617 // Clean up outstanding resources.
618 //
619
620 IOFree(slicePath, slicePathSize);
621
622 return true; // (success)
623
624 createNodesErr:
625
626 if (anchorNew) _anchors->remove(anchorID);
627 if (slicePath) IOFree(slicePath, slicePathSize);
628
629 return false; // (failure)
630 }
631
632 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
633
634 AnchorTable * IOMediaBSDClient::getAnchors()
635 {
636 //
637 // Obtain the table of anchors.
638 //
639
640 return _anchors;
641 }
642
643 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
644
645 MinorTable * IOMediaBSDClient::getMinors()
646 {
647 //
648 // Obtain the table of anchors.
649 //
650
651 return _minors;
652 }
653
654 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
655
656 MinorSlot * IOMediaBSDClient::getMinor(UInt32 minorID)
657 {
658 //
659 // Obtain information for the specified minor ID.
660 //
661
662 return _minors->getMinor(minorID);
663 }
664
665 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
666
667 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 0);
668
669 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
670
671 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 1);
672
673 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
674
675 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 2);
676
677 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
678
679 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 3);
680
681 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
682
683 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 4);
684
685 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
686
687 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 5);
688
689 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
690
691 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 6);
692
693 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
694
695 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 7);
696
697 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
698
699 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 8);
700
701 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
702
703 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 9);
704
705 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
706
707 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 10);
708
709 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
710
711 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 11);
712
713 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
714
715 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 12);
716
717 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
718
719 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 13);
720
721 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
722
723 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 14);
724
725 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
726
727 OSMetaClassDefineReservedUnused(IOMediaBSDClient, 15);
728
729 // =============================================================================
730 // BSD Functions
731
732 int dkopen(dev_t dev, int flags, int devtype, struct proc *)
733 {
734 //
735 // dkopen opens the device (called on each open).
736 //
737
738 int error;
739 IOStorageAccess level;
740 MinorSlot * minor;
741
742 assert(gIOMediaBSDClient);
743 assert(S_ISBLK(devtype) || S_ISCHR(devtype));
744
745 gIOMediaBSDClient->lockForArbitration(); // (disable access)
746
747 assert(gIOMediaBSDClient->getMinors());
748
749 error = 0;
750 level = (flags & FWRITE) ? kIOStorageAccessReaderWriter
751 : kIOStorageAccessReader;
752 minor = gIOMediaBSDClient->getMinor(minor(dev));
753
754 //
755 // Process the open.
756 //
757
758 if ( minor == 0 ) // (is minor valid?)
759 {
760 error = ENXIO;
761 }
762 else if ( minor->isEjecting ) // (is minor in flux?)
763 {
764 error = EBUSY;
765 }
766 else if ( (flags & FWRITE) ) // (is client a writer?)
767 {
768 if ( minor->bdevWriter || minor->cdevWriter )
769 level = kIOStorageAccessNone;
770 }
771 else // (is client a reader?)
772 {
773 if ( minor->bdevOpen || minor->cdevOpen )
774 level = kIOStorageAccessNone;
775 }
776
777 if ( error == 0 && level != kIOStorageAccessNone ) // (issue open/upgrade?)
778 {
779 if ( minor->media->open(gIOMediaBSDClient, 0, level) == false ) // (go)
780 {
781 error = EBUSY;
782 }
783 }
784
785 if ( error == 0 ) // (update state)
786 {
787 if ( S_ISBLK(devtype) )
788 {
789 minor->bdevOpen = true;
790 if ( (flags & FWRITE) ) minor->bdevWriter = true;
791 }
792 else
793 {
794 minor->cdevOpen = true;
795 if ( (flags & FWRITE) ) minor->cdevWriter = true;
796 }
797 }
798
799 gIOMediaBSDClient->unlockForArbitration(); // (enable access)
800
801 return error;
802 }
803
804 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
805
806 int dkclose(dev_t dev, int /* flags */, int devtype, struct proc *)
807 {
808 //
809 // dkclose closes the device (called on last close).
810 //
811
812 MinorSlot * minor;
813 bool wasWriter;
814
815 assert(S_ISBLK(devtype) || S_ISCHR(devtype));
816
817 gIOMediaBSDClient->lockForArbitration(); // (disable access)
818
819 minor = gIOMediaBSDClient->getMinor(minor(dev));
820 wasWriter = (minor->bdevWriter || minor->cdevWriter);
821
822 if ( S_ISBLK(devtype) ) // (update state)
823 {
824 minor->bdevBlockSize = minor->media->getPreferredBlockSize();
825 minor->bdevOpen = false;
826 minor->bdevWriter = false;
827 }
828 else
829 {
830 minor->cdevOpen = false;
831 minor->cdevWriter = false;
832 }
833
834 if ( minor->isEjecting ) // (is minor in flux?)
835 {
836 //
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.
841 //
842
843 minor->isEjecting = false;
844
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.
848
849 assert(minor->bdevOpen == false);
850 assert(minor->cdevOpen == false);
851
852 if ( minor->isObsolete )
853 gIOMediaBSDClient->getMinors()->remove(minor(dev));
854 }
855 else if ( !minor->bdevOpen && !minor->cdevOpen )
856 {
857 //
858 // We communicate the close down to the media object once all opens are
859 // gone, on both the block and character device nodes.
860 //
861
862 minor->media->close(gIOMediaBSDClient); // (go)
863 }
864 else if ( !minor->bdevWriter && !minor->cdevWriter && wasWriter )
865 {
866 //
867 // We communicate a downgrade down to the media object once all writers
868 // are gone and while readers still exist.
869 //
870
871 bool s;
872 s = minor->media->open(gIOMediaBSDClient, 0, kIOStorageAccessReader);
873 assert(s); // (should never fail, unless deadlock avoided)
874 }
875
876 gIOMediaBSDClient->unlockForArbitration(); // (enable access)
877
878 return 0;
879 }
880
881 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
882
883 int dkread(dev_t dev, struct uio * uio, int /* flags */)
884 {
885 //
886 // dkread reads data from a device.
887 //
888
889 struct dio dio = { dev, uio };
890
891 return dkreadwrite(&dio, DKRTYPE_DIO);
892 }
893
894 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
895
896 int dkwrite(dev_t dev, struct uio * uio, int /* flags */)
897 {
898 //
899 // dkwrite writes data to a device.
900 //
901
902 struct dio dio = { dev, uio };
903
904 return dkreadwrite(&dio, DKRTYPE_DIO);
905 }
906
907 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
908
909 void dkstrategy(struct buf * bp)
910 {
911 //
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.
915 //
916
917 dkreadwrite(bp, DKRTYPE_BUF);
918 }
919
920 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
921
922 int dkioctl(dev_t dev, u_long cmd, caddr_t data, int, struct proc *)
923 {
924 //
925 // dkioctl performs operations other than a read or write.
926 //
927
928 int error = 0;
929 MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
930
931 if ( minor->isEjecting ) return EBADF; // (is minor in flux?)
932
933 //
934 // Process the ioctl.
935 //
936
937 switch ( cmd )
938 {
939 case DKIOCGETBLOCKSIZE: // getBlockSize(int * out);
940 {
941 //
942 // This ioctl returns the preferred block size of the media object.
943 //
944
945 *(int *)data = (int) minor->media->getPreferredBlockSize();
946
947 } break;
948
949 case DKIOCGETBLOCKCOUNT: // getBlockCount(int * out);
950 {
951 //
952 // This ioctl returns the size of the media object in blocks. The
953 // implied block size is returned by DKIOCGETBLOCKSIZE.
954 //
955
956 if ( minor->media->getPreferredBlockSize() )
957 *(int *)data = (int) ( minor->media->getSize() /
958 minor->media->getPreferredBlockSize() );
959 else
960 *(int *)data = 0;
961
962 } break;
963
964 case DKIOCISFORMATTED: // isFormatted(int * out);
965 {
966 //
967 // This ioctl returns truth if the media object is formatted.
968 //
969
970 *(int *)data = (int) minor->media->isFormatted();
971
972 } break;
973
974 case DKIOCISWRITABLE: // isWritable(int * out);
975 {
976 //
977 // This ioctl returns truth if the media object is writable.
978 //
979
980 *(int *)data = (int) minor->media->isWritable();
981
982 } break;
983
984 case DKIOCGETLOCATION: // getLocation(char[128] out);
985 {
986 //
987 // This ioctl returns the open firmware path for this media object.
988 //
989
990 int l = sizeof(((struct drive_location *)data)->location);
991 char * p = ((struct drive_location *)data)->location;
992
993 if ( minor->media->getPath(p, &l, gIODTPlane) && strchr(p, ':') )
994 strcpy(p, strchr(p, ':') + 1); // (strip the plane name)
995 else
996 error = EINVAL;
997
998 } break;
999
1000 case DKIOCEJECT: // eject(void);
1001 {
1002 //
1003 // This ioctl asks that the media object be ejected from the device.
1004 //
1005
1006 IOBlockStorageDriver * driver;
1007 MinorTable * minors;
1008
1009 driver = OSDynamicCast( IOBlockStorageDriver,
1010 minor->media->getProvider() );
1011 minors = gIOMediaBSDClient->getMinors();
1012
1013 // Determine whether this media has an IOBlockStorageDriver parent.
1014
1015 if ( driver == 0 ) { error = ENOTTY; break; }
1016
1017 // Disable access to tables, matching, opens, closes, terminations.
1018
1019 gIOMediaBSDClient->lockForArbitration();
1020
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.
1024
1025 if ( minors->getOpenCountForAnchorID(minor->anchorID) > 1 )
1026 {
1027 error = EBUSY;
1028
1029 // Enable access to tables, matching, opens, closes, and so on.
1030
1031 gIOMediaBSDClient->unlockForArbitration();
1032 }
1033 else
1034 {
1035 IOReturn status;
1036
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).
1042
1043 minor->isEjecting = true;
1044
1045 // Enable access to tables, matching, opens, closes, and so on.
1046
1047 gIOMediaBSDClient->unlockForArbitration();
1048
1049 // Close the media object before the ejection request is made.
1050
1051 minor->media->close(gIOMediaBSDClient);
1052
1053 // Open the block storage driver to make the ejection request.
1054
1055 if (driver->open(gIOMediaBSDClient, 0, kIOStorageAccessReader))
1056 {
1057 // Eject the media from the drive.
1058
1059 status = driver->ejectMedia();
1060
1061 // Close the block storage driver.
1062
1063 driver->close(gIOMediaBSDClient);
1064 }
1065 else
1066 {
1067 status = kIOReturnBusy;
1068 }
1069
1070 error = gIOMediaBSDClient->errnoFromReturn(status);
1071 }
1072
1073 } break;
1074
1075 default:
1076 {
1077 //
1078 // A foreign ioctl was received. Log an error to the console.
1079 //
1080
1081 IOLog( "%s: ioctl(%s\'%c\',%d,%d) is unsupported.\n",
1082 minor->name,
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),
1088 (int) (cmd & 0xff),
1089 (int) IOCPARM_LEN(cmd) );
1090
1091 error = ENOTTY;
1092
1093 } break;
1094 }
1095
1096 return error; // (return error status)
1097 }
1098
1099 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1100
1101 int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int f, struct proc * proc)
1102 {
1103 //
1104 // dkioctl_bdev performs operations other than a read or write, specific to
1105 // the block device.
1106 //
1107
1108 int error = 0;
1109 MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
1110
1111 if ( minor->isEjecting ) return EBADF; // (is minor in flux?)
1112
1113 //
1114 // Process the ioctl.
1115 //
1116
1117 switch ( cmd )
1118 {
1119 case DKIOCGETBLOCKSIZE: // getBlockSize(int * out);
1120 {
1121 //
1122 // This ioctl returns the preferred (or overrided) block size of the
1123 // media object.
1124 //
1125
1126 *(int *)data = (int) minor->bdevBlockSize;
1127
1128 } break;
1129
1130 case DKIOCSETBLOCKSIZE: // setBlockSize(int * in);
1131 {
1132 //
1133 // This ioctl overrides the block size for the media object, for the
1134 // duration of all block device opens at this minor.
1135 //
1136
1137 if ( *(int *)data > 0 )
1138 minor->bdevBlockSize = (UInt64) (*(int *)data);
1139 else
1140 error = EINVAL;
1141
1142 } break;
1143
1144 case DKIOCGETBLOCKCOUNT: // getBlockCount(int * out);
1145 {
1146 //
1147 // This ioctl returns the size of the media object in blocks. The
1148 // implied block size is returned by DKIOCGETBLOCKSIZE.
1149 //
1150
1151 if ( minor->bdevBlockSize )
1152 *(int *)data = (int) ( minor->media->getSize() /
1153 minor->bdevBlockSize );
1154 else
1155 *(int *)data = 0;
1156
1157 } break;
1158
1159 default:
1160 {
1161 //
1162 // Call the common ioctl handler for all other ioctls.
1163 //
1164
1165 error = dkioctl(dev, cmd, data, f, proc);
1166
1167 } break;
1168 }
1169
1170 return error; // (return error status)
1171 }
1172
1173 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1174
1175 int dksize(dev_t dev)
1176 {
1177 //
1178 // dksize returns the block size of the media.
1179 //
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.
1183 //
1184
1185 MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
1186
1187 if ( minor->isEjecting ) return 0; // (is minor in flux?)
1188
1189 return (int) minor->bdevBlockSize; // (return block size)
1190 }
1191
1192 // =============================================================================
1193 // Support For BSD Functions
1194
1195 inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
1196 {
1197 return (dkrtype == DKRTYPE_BUF)
1198 ? ((struct buf *)dkr)->b_dev
1199 : ((struct dio *)dkr)->dev;
1200 }
1201
1202 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1203
1204 inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
1205 {
1206 return (dkrtype == DKRTYPE_BUF)
1207 ? ((struct buf *)dkr)->b_bcount
1208 : ((struct dio *)dkr)->uio->uio_resid;
1209 }
1210
1211 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1212
1213 inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
1214 {
1215 if (dkrtype == DKRTYPE_BUF)
1216 {
1217 struct buf * bp = (struct buf *)dkr;
1218 MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(bp->b_dev));
1219
1220 return bp->b_blkno * minor->bdevBlockSize;
1221 }
1222 return ((struct dio *)dkr)->uio->uio_offset;
1223 }
1224
1225 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1226
1227 inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
1228 {
1229 return (dkrtype == DKRTYPE_BUF)
1230 ? ((((struct buf *)dkr)->b_flags & B_READ) == B_READ)
1231 : ((((struct dio *)dkr)->uio->uio_rw) == UIO_READ);
1232 }
1233
1234 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1235
1236 inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
1237 {
1238 return (dkrtype == DKRTYPE_BUF)
1239 ? true
1240 : false;
1241 }
1242
1243
1244 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1245
1246 inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
1247 {
1248 return (dkrtype == DKRTYPE_BUF)
1249 ? false
1250 : true;
1251 }
1252
1253 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1254
1255 inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount)
1256 {
1257 if (dkrtype == DKRTYPE_BUF)
1258 ((struct buf *)dkr)->b_resid = ((struct buf *)dkr)->b_bcount - bcount;
1259 else
1260 ((struct dio *)dkr)->uio->uio_resid -= bcount;
1261 }
1262
1263 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1264
1265 inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
1266 {
1267 if (dkrtype == DKRTYPE_BUF)
1268 {
1269 struct buf * bp = (struct buf *)dkr;
1270
1271 bp->b_error = gIOMediaBSDClient->errnoFromReturn(status); // (error?)
1272 bp->b_flags |= (status != kIOReturnSuccess) ? B_ERROR : 0; // (error?)
1273 biodone(bp); // (complete request)
1274 }
1275 }
1276
1277 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1278
1279 inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
1280 {
1281 if (dkrtype == DKRTYPE_BUF)
1282 {
1283 struct buf * bp = (struct buf *)dkr;
1284
1285 if ( (bp->b_flags & B_VECTORLIST) )
1286 {
1287 assert(sizeof(IOPhysicalRange ) == sizeof(iovec ));
1288 assert(sizeof(IOPhysicalRange::address) == sizeof(iovec::iov_base));
1289 assert(sizeof(IOPhysicalRange::length ) == sizeof(iovec::iov_len ));
1290
1291 return IOMemoryDescriptor::withPhysicalRanges( // (multiple-range)
1292 (IOPhysicalRange *) bp->b_vectorlist,
1293 (UInt32) bp->b_vectorcount,
1294 (bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
1295 true );
1296 }
1297
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() );
1303 }
1304 else
1305 {
1306 struct uio * uio = ((struct dio *)dkr)->uio;
1307
1308 assert(sizeof(IOVirtualRange ) == sizeof(iovec ));
1309 assert(sizeof(IOVirtualRange::address) == sizeof(iovec::iov_base));
1310 assert(sizeof(IOVirtualRange::length ) == sizeof(iovec::iov_len ));
1311
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(),
1317 true );
1318 }
1319 }
1320
1321 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1322
1323 int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype)
1324 {
1325 //
1326 // dkreadwrite performs a read or write operation.
1327 //
1328
1329 IOMemoryDescriptor * buffer;
1330 register UInt64 byteCount;
1331 register UInt64 byteStart;
1332 UInt64 mediaSize;
1333 MinorSlot * minor;
1334 IOReturn status;
1335
1336 minor = gIOMediaBSDClient->getMinor(minor(DKR_GET_DEV(dkr, dkrtype)));
1337
1338 if ( minor->isEjecting ) // (is minor in flux?)
1339 {
1340 status = kIOReturnNoMedia;
1341 goto dkreadwriteErr;
1342 }
1343
1344 if ( minor->media->isFormatted() == false ) // (is media unformatted?)
1345 {
1346 status = kIOReturnUnformattedMedia;
1347 goto dkreadwriteErr;
1348 }
1349
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)
1353
1354 //
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.
1358 //
1359
1360 if ( byteStart >= mediaSize ) // (is start at or past the end-of-media?)
1361 {
1362 status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError;
1363 goto dkreadwriteErr;
1364 }
1365
1366 //
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.
1369 //
1370
1371 if ( DKR_IS_RAW(dkr, dkrtype) )
1372 {
1373 UInt64 mediaBlockSize = minor->media->getPreferredBlockSize();
1374
1375 if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
1376 {
1377 status = kIOReturnNotAligned;
1378 goto dkreadwriteErr;
1379 }
1380 }
1381
1382 //
1383 // Build a descriptor which describes the buffer involved in the transfer.
1384 //
1385
1386 buffer = DKR_GET_BUFFER(dkr, dkrtype);
1387
1388 if ( buffer == 0 ) // (no buffer?)
1389 {
1390 status = kIOReturnNoMemory;
1391 goto dkreadwriteErr;
1392 }
1393
1394 //
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.
1401 //
1402 // Clip the transfer buffer should this be a short read or write request.
1403 //
1404
1405 if ( byteCount > mediaSize - byteStart ) // (clip at end-of-media)
1406 {
1407 IOMemoryDescriptor * originalBuffer = buffer;
1408
1409 buffer = IOMemoryDescriptor::withSubRange(
1410 /* descriptor */ originalBuffer,
1411 /* withOffset */ 0,
1412 /* withLength */ mediaSize - byteStart,
1413 /* withDirection */ originalBuffer->getDirection() );
1414
1415 originalBuffer->release(); // (either retained above or about to fail)
1416
1417 if ( buffer == 0 ) // (no buffer?)
1418 {
1419 status = kIOReturnNoMemory;
1420 goto dkreadwriteErr;
1421 }
1422 }
1423
1424 //
1425 // Execute the transfer.
1426 //
1427
1428 if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) ) // (an asynchronous request?)
1429 {
1430 IOStorageCompletion completion;
1431
1432 completion.target = dkr;
1433 completion.action = dkreadwritecompletion;
1434 completion.parameter = (void *) dkrtype;
1435
1436 if ( DKR_IS_READ(dkr, dkrtype) ) // (a read?)
1437 {
1438 minor->media->read( /* client */ gIOMediaBSDClient,
1439 /* byteStart */ byteStart,
1440 /* buffer */ buffer,
1441 /* completion */ completion ); // (go)
1442 }
1443 else // (a write?)
1444 {
1445 minor->media->write( /* client */ gIOMediaBSDClient,
1446 /* byteStart */ byteStart,
1447 /* buffer */ buffer,
1448 /* completion */ completion ); // (go)
1449 }
1450
1451 status = kIOReturnSuccess;
1452 }
1453 else // (is this a synchronous request?)
1454 {
1455 if ( DKR_IS_READ(dkr, dkrtype) ) // (a read?)
1456 {
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)
1467 }
1468 else // (a write?)
1469 {
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)
1480 }
1481
1482 dkreadwritecompletion(dkr, (void *)dkrtype, status, byteCount);
1483 }
1484
1485 //
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.
1490 //
1491
1492 buffer->release(); // (release our retain on the buffer)
1493
1494 return gIOMediaBSDClient->errnoFromReturn(status); // (return error status)
1495
1496 dkreadwriteErr:
1497
1498 dkreadwritecompletion(dkr, (void *)dkrtype, status, 0);
1499
1500 return gIOMediaBSDClient->errnoFromReturn(status); // (return error status)
1501 }
1502
1503 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1504
1505 void dkreadwritecompletion( void * target,
1506 void * parameter,
1507 IOReturn status,
1508 UInt64 actualByteCount )
1509 {
1510 //
1511 // dkreadwritecompletion cleans up after a read or write operation.
1512 //
1513
1514 dkr_t dkr = (dkr_t) target;
1515 dkrtype_t dkrtype = (dkrtype_t) (int) parameter;
1516 dev_t dev = DKR_GET_DEV(dkr, dkrtype);
1517
1518 #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1519 UInt32 anchorID = gIOMediaBSDClient->getMinor(minor(dev))->anchorID;
1520
1521 if ( anchorID < DK_NDRIVE )
1522 {
1523 IOBlockStorageDriver * d = dk_drive[anchorID];
1524
1525 if ( d )
1526 {
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)) );
1533 }
1534 }
1535 #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1536
1537 if ( status != kIOReturnSuccess ) // (log errors to the console)
1538 {
1539 IOLog( "%s: %s.\n",
1540 gIOMediaBSDClient->getMinor(minor(dev))->name,
1541 gIOMediaBSDClient->stringFromReturn(status) );
1542 }
1543
1544 DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount); // (set byte count)
1545 DKR_RUN_COMPLETION(dkr, dkrtype, status); // (run completion)
1546 }
1547
1548 // =============================================================================
1549 // AnchorTable Class
1550
1551 AnchorTable::AnchorTable(UInt32 growCount, UInt32 maxCount)
1552 {
1553 //
1554 // Initialize this object's minimal state.
1555 //
1556
1557 _table = 0;
1558 _tableCount = 0;
1559 _tableGrowCount = growCount;
1560 _tableMaxCount = maxCount;
1561 }
1562
1563 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1564
1565 AnchorTable::~AnchorTable()
1566 {
1567 //
1568 // Free all of this object's outstanding resources.
1569 //
1570
1571 for ( UInt32 anchorID = 0; anchorID < _tableCount; anchorID++ )
1572 if ( _table[anchorID].isAssigned ) remove(anchorID);
1573
1574 if ( _table ) IODelete(_table, AnchorSlot, _tableCount);
1575 }
1576
1577 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1578
1579 UInt32 AnchorTable::insert(IOService * anchor)
1580 {
1581 //
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).
1584 //
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).
1588 //
1589
1590 UInt32 anchorID;
1591 IONotifier * notifier;
1592
1593 // Search for an unassigned slot in the anchor table.
1594
1595 for ( anchorID = 0; anchorID < _tableCount; anchorID++ )
1596 if ( _table[anchorID].isAssigned == false ) break;
1597
1598 // Was an unassigned slot found? If not, grow the table.
1599
1600 if ( anchorID == _tableCount )
1601 {
1602 AnchorSlot * newTable;
1603 UInt32 newTableCount;
1604
1605 // We must expand the anchor table since no more slots are available.
1606
1607 if ( _tableCount >= _tableMaxCount ) return kInvalidAnchorID;
1608
1609 newTableCount = min(_tableGrowCount + _tableCount, _tableMaxCount);
1610 newTable = IONew(AnchorSlot, newTableCount);
1611
1612 if ( newTable == 0 ) return kInvalidAnchorID;
1613
1614 bzero(newTable, newTableCount * sizeof(AnchorSlot));
1615
1616 // Copy over the old table's entries, then free the old table.
1617
1618 if ( _table )
1619 {
1620 bcopy(_table, newTable, _tableCount * sizeof(AnchorSlot));
1621 IODelete(_table, AnchorSlot, _tableCount);
1622 }
1623
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
1626 // new tables.
1627
1628 anchorID = _tableCount;
1629 _table = newTable;
1630 _tableCount = newTableCount;
1631 }
1632
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).
1637
1638 notifier = anchor->registerInterest(
1639 /* type */ gIOGeneralInterest,
1640 /* action */ anchorWasNotified,
1641 /* target */ this,
1642 /* parameter */ 0 );
1643
1644 if ( notifier == 0 ) return kInvalidAnchorID;
1645
1646 // Zero the new slot, fill it in, and retain the anchor object.
1647
1648 bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot)
1649
1650 _table[anchorID].isAssigned = true; // (fill in slot)
1651 _table[anchorID].isObsolete = false;
1652 _table[anchorID].anchor = anchor;
1653 _table[anchorID].notifier = notifier;
1654
1655 _table[anchorID].anchor->retain(); // (retain anchor)
1656
1657 #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1658 if ( anchorID < DK_NDRIVE )
1659 {
1660 dk_drive[anchorID] = OSDynamicCast(IOBlockStorageDriver, anchor);
1661 if ( anchorID + 1 > (UInt32) dk_ndrive ) dk_ndrive = anchorID + 1;
1662 }
1663 #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1664
1665 return anchorID;
1666 }
1667
1668 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1669
1670 void AnchorTable::remove(UInt32 anchorID)
1671 {
1672 //
1673 // This method removes the specified anchor from the anchor table.
1674 //
1675
1676 assert(anchorID < _tableCount);
1677 assert(_table[anchorID].isAssigned);
1678
1679 // Release the resources retained in the anchor slot and zero it.
1680
1681 _table[anchorID].notifier->remove();
1682 _table[anchorID].anchor->release(); // (release anchor)
1683
1684 bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot)
1685
1686 #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1687 if ( anchorID < DK_NDRIVE )
1688 {
1689 dk_drive[anchorID] = 0;
1690 for (dk_ndrive = DK_NDRIVE; dk_ndrive; dk_ndrive--)
1691 {
1692 if ( dk_drive[dk_ndrive - 1] ) break;
1693 }
1694 }
1695 #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
1696 }
1697
1698 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1699
1700 void AnchorTable::obsolete(UInt32 anchorID)
1701 {
1702 //
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.
1708 //
1709
1710 assert(anchorID < _tableCount);
1711 assert(_table[anchorID].isAssigned);
1712
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).
1715
1716 _table[anchorID].isObsolete = true;
1717 }
1718
1719 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1720
1721 UInt32 AnchorTable::locate(IOService * anchor)
1722 {
1723 //
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.
1727 //
1728
1729 for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
1730 {
1731 if ( _table[anchorID].isAssigned != false &&
1732 _table[anchorID].isObsolete == false &&
1733 _table[anchorID].anchor == anchor ) return anchorID;
1734 }
1735
1736 return kInvalidAnchorID;
1737 }
1738
1739 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1740
1741 bool AnchorTable::isObsolete(UInt32 anchorID)
1742 {
1743 //
1744 // Determine whether the specified anchor ID is marked as obsolete.
1745 //
1746
1747 assert(anchorID < _tableCount);
1748 assert(_table[anchorID].isAssigned);
1749
1750 return _table[anchorID].isObsolete ? true : false;
1751 }
1752
1753 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1754
1755 IOReturn AnchorTable::anchorWasNotified( void * /* target */,
1756 void * /* parameter */,
1757 UInt32 messageType,
1758 IOService * anchor,
1759 void * /* messageArgument */,
1760 vm_size_t /* messageArgumentSize */ )
1761 {
1762 //
1763 // Notification handler for anchors.
1764 //
1765
1766 UInt32 anchorID;
1767
1768 assert(gIOMediaBSDClient);
1769
1770 // Determine whether this is a termination notification (post-stop).
1771
1772 if ( messageType != kIOMessageServiceIsTerminated )
1773 return kIOReturnSuccess;
1774
1775 // Disable access to tables, matching, opens, closes, and terminations.
1776
1777 gIOMediaBSDClient->lockForArbitration();
1778
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).
1782
1783 anchorID = gIOMediaBSDClient->getAnchors()->locate(anchor);
1784
1785 if ( anchorID != kInvalidAnchorID )
1786 {
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).
1791
1792 if ( gIOMediaBSDClient->getMinors()->hasReferencesToAnchorID(anchorID) )
1793 gIOMediaBSDClient->getAnchors()->obsolete(anchorID);
1794 else
1795 gIOMediaBSDClient->getAnchors()->remove(anchorID);
1796 }
1797
1798 // Enable access to tables, matching, opens, closes, and terminations.
1799
1800 gIOMediaBSDClient->unlockForArbitration();
1801
1802 return kIOReturnSuccess;
1803 }
1804
1805 // =============================================================================
1806 // MinorTable Class
1807
1808 MinorTable::MinorTable(UInt32 growCount, UInt32 maxCount)
1809 {
1810 //
1811 // Initialize this object's minimal state.
1812 //
1813
1814 _table = 0;
1815 _tableCount = 0;
1816 _tableGrowCount = growCount;
1817 _tableMaxCount = maxCount;
1818 }
1819
1820 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1821
1822 MinorTable::~MinorTable()
1823 {
1824 //
1825 // Free all of this object's outstanding resources.
1826 //
1827
1828 for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
1829 if ( _table[minorID].isAssigned ) remove(minorID);
1830
1831 if ( _table ) IODelete(_table, MinorSlot, _tableCount);
1832 }
1833
1834 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1835
1836 UInt32 MinorTable::insert(IOMedia * media, UInt32 anchorID, char * slicePath)
1837 {
1838 //
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).
1841 //
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".
1845 //
1846
1847 void * bdevNode;
1848 void * cdevNode;
1849 UInt32 minorID;
1850 char * minorName;
1851 UInt32 minorNameSize;
1852
1853 // Search for an unassigned slot in the minor table.
1854
1855 for ( minorID = 0; minorID < _tableCount; minorID++ )
1856 if ( _table[minorID].isAssigned == false ) break;
1857
1858 // Was an unassigned slot found? If not, grow the table.
1859
1860 if ( minorID == _tableCount )
1861 {
1862 MinorSlot * newTable;
1863 UInt32 newTableCount;
1864
1865 // We must expand the minor table since no more slots are available.
1866
1867 if ( _tableCount >= _tableMaxCount) return kInvalidMinorID;
1868
1869 newTableCount = min(_tableGrowCount + _tableCount, _tableMaxCount);
1870 newTable = IONew(MinorSlot, newTableCount);
1871
1872 if ( newTable == 0 ) return kInvalidMinorID;
1873
1874 bzero(newTable, newTableCount * sizeof(MinorSlot));
1875
1876 // Copy over the old table's entries, then free the old table.
1877
1878 if ( _table )
1879 {
1880 bcopy(_table, newTable, _tableCount * sizeof(MinorSlot));
1881 IODelete(_table, MinorSlot, _tableCount);
1882 }
1883
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
1886 // new tables.
1887
1888 minorID = _tableCount;
1889 _table = newTable;
1890 _tableCount = newTableCount;
1891 }
1892
1893 // Create a buffer large enough to hold the full name of the minor.
1894
1895 minorNameSize = strlen("disk#");
1896 for (unsigned temp = anchorID; temp >= 10; temp /= 10) minorNameSize++;
1897 minorNameSize += strlen(slicePath);
1898 minorNameSize += 1;
1899 minorName = IONew(char, minorNameSize);
1900
1901 // Create a block and character device node in BSD for this media.
1902
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 );
1911
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 );
1920
1921 if ( minorName == 0 || bdevNode == 0 || cdevNode == 0 )
1922 {
1923 if ( cdevNode ) devfs_remove(cdevNode);
1924 if ( bdevNode ) devfs_remove(bdevNode);
1925 if ( minorName ) IODelete(minorName, char, minorNameSize);
1926
1927 return kInvalidMinorID;
1928 }
1929
1930 // Construct a name for the node.
1931
1932 sprintf(minorName, "disk%ld%s", anchorID, slicePath);
1933 assert(strlen(minorName) + 1 == minorNameSize);
1934
1935 // Zero the new slot, fill it in, and retain the media object.
1936
1937 bzero(&_table[minorID], sizeof(MinorSlot)); // (zero slot)
1938
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;
1950
1951 _table[minorID].media->retain(); // (retain media)
1952
1953 return minorID;
1954 }
1955
1956 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1957
1958 void MinorTable::remove(UInt32 minorID)
1959 {
1960 //
1961 // This method removes the specified minor from the minor table.
1962 //
1963
1964 UInt32 anchorID;
1965
1966 assert(minorID < _tableCount);
1967 assert(_table[minorID].isAssigned);
1968
1969 assert(_table[minorID].isEjecting == false);
1970 assert(_table[minorID].bdevOpen == false);
1971 assert(_table[minorID].cdevOpen == false);
1972
1973 anchorID = _table[minorID].anchorID;
1974
1975 // Release the resources retained in the minor slot and zero it.
1976
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)
1981
1982 bzero(&_table[minorID], sizeof(MinorSlot)); // (zero slot)
1983
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.
1987
1988 assert(gIOMediaBSDClient);
1989
1990 if ( gIOMediaBSDClient->getAnchors()->isObsolete(anchorID) )
1991 {
1992 if ( hasReferencesToAnchorID(anchorID) == false )
1993 gIOMediaBSDClient->getAnchors()->remove(anchorID);
1994 }
1995 }
1996
1997 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1998
1999 UInt32 MinorTable::locate(IOMedia * media)
2000 {
2001 //
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.
2005 //
2006
2007 for (UInt32 minorID = 0; minorID < _tableCount; minorID++)
2008 {
2009 if ( _table[minorID].isAssigned != false &&
2010 _table[minorID].isObsolete == false &&
2011 _table[minorID].media == media ) return minorID;
2012 }
2013
2014 return kInvalidMinorID;
2015 }
2016
2017 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2018
2019 UInt32 MinorTable::getOpenCountForAnchorID(UInt32 anchorID)
2020 {
2021 //
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.
2025 //
2026
2027 UInt32 opens = 0;
2028
2029 for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
2030 {
2031 if ( _table[minorID].isAssigned != false &&
2032 _table[minorID].anchorID == anchorID )
2033 {
2034 opens += (_table[minorID].bdevOpen) ? 1 : 0;
2035 opens += (_table[minorID].cdevOpen) ? 1 : 0;
2036 }
2037 }
2038
2039 return opens;
2040 }
2041
2042 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2043
2044 IOMedia * MinorTable::getWholeMediaAtAnchorID(UInt32 anchorID)
2045 {
2046 //
2047 // This method obtains the whole media associated with the specified anchor
2048 // ID.
2049 //
2050
2051 for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
2052 {
2053 if ( _table[minorID].isAssigned != false &&
2054 _table[minorID].anchorID == anchorID &&
2055 _table[minorID].media->isWhole() ) return _table[minorID].media;
2056 }
2057
2058 return 0;
2059 }
2060
2061 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2062
2063 bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID)
2064 {
2065 //
2066 // This method determines whether there are assigned minors in the minor
2067 // table that refer to the specified anchor ID.
2068 //
2069
2070 for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
2071 {
2072 if ( _table[minorID].isAssigned != false &&
2073 _table[minorID].anchorID == anchorID ) return true;
2074 }
2075
2076 return false;
2077 }
2078
2079 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2080
2081 MinorSlot * MinorTable::getMinor(UInt32 minorID)
2082 {
2083 //
2084 // Obtain the structure describing the specified minor.
2085 //
2086
2087 if ( minorID < _tableCount && _table[minorID].isAssigned )
2088 return &_table[minorID];
2089 else
2090 return 0;
2091 }
2092
2093 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2094
2095 void MinorTable::obsolete(UInt32 minorID)
2096 {
2097 //
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.
2102 //
2103
2104 assert(minorID < _tableCount);
2105 assert(_table[minorID].isAssigned);
2106
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).
2109
2110 _table[minorID].isObsolete = true;
2111 }
2112
2113 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2114
2115 bool MinorTable::isObsolete(UInt32 minorID)
2116 {
2117 //
2118 // Determine whether the specified minor ID is marked as obsolete.
2119 //
2120
2121 assert(minorID < _tableCount);
2122 assert(_table[minorID].isAssigned);
2123
2124 return _table[minorID].isObsolete ? true : false;
2125 }