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