]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOWorkLoop.cpp
xnu-3789.1.32.tar.gz
[apple/xnu.git] / iokit / Kernel / IOWorkLoop.cpp
CommitLineData
1c79356b 1/*
6d2010ae 2 * Copyright (c) 1998-2010 Apple Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b 27 */
b0d623f7
A
28
29#include <pexpert/pexpert.h>
1c79356b
A
30#include <IOKit/IOWorkLoop.h>
31#include <IOKit/IOEventSource.h>
32#include <IOKit/IOInterruptEventSource.h>
33#include <IOKit/IOCommandGate.h>
39037602 34#include <IOKit/IOCommandPool.h>
1c79356b 35#include <IOKit/IOTimeStamp.h>
060df5ea 36#include <IOKit/IOKitDebug.h>
2d21ac55 37#include <libkern/OSDebug.h>
4b17d6b6 38#include <kern/thread.h>
1c79356b
A
39
40#define super OSObject
41
42OSDefineMetaClassAndStructors(IOWorkLoop, OSObject);
43
44// Block of unused functions intended for future use
b0d623f7
A
45#if __LP64__
46OSMetaClassDefineReservedUnused(IOWorkLoop, 0);
47OSMetaClassDefineReservedUnused(IOWorkLoop, 1);
48OSMetaClassDefineReservedUnused(IOWorkLoop, 2);
49#else
0b4e3aa0 50OSMetaClassDefineReservedUsed(IOWorkLoop, 0);
0c530ab8 51OSMetaClassDefineReservedUsed(IOWorkLoop, 1);
b0d623f7
A
52OSMetaClassDefineReservedUsed(IOWorkLoop, 2);
53#endif
1c79356b
A
54OSMetaClassDefineReservedUnused(IOWorkLoop, 3);
55OSMetaClassDefineReservedUnused(IOWorkLoop, 4);
56OSMetaClassDefineReservedUnused(IOWorkLoop, 5);
57OSMetaClassDefineReservedUnused(IOWorkLoop, 6);
58OSMetaClassDefineReservedUnused(IOWorkLoop, 7);
59
60enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 };
2d21ac55
A
61static inline void SETP(void *addr, unsigned int flag)
62 { unsigned char *num = (unsigned char *) addr; *num |= flag; }
63static inline void CLRP(void *addr, unsigned int flag)
64 { unsigned char *num = (unsigned char *) addr; *num &= ~flag; }
65static inline bool ISSETP(void *addr, unsigned int flag)
66 { unsigned char *num = (unsigned char *) addr; return (*num & flag) != 0; }
1c79356b
A
67
68#define fFlags loopRestart
69
6d2010ae
A
70#define passiveEventChain reserved->passiveEventChain
71
72#if IOKITSTATS
73
74#define IOStatisticsRegisterCounter() \
75do { \
76 reserved->counter = IOStatistics::registerWorkLoop(this); \
77} while(0)
78
79#define IOStatisticsUnregisterCounter() \
80do { \
81 if (reserved) \
82 IOStatistics::unregisterWorkLoop(reserved->counter); \
83} while(0)
84
85#define IOStatisticsOpenGate() \
86do { \
87 IOStatistics::countWorkLoopOpenGate(reserved->counter); \
39037602 88 if (reserved->lockInterval) lockTime(); \
6d2010ae 89} while(0)
6d2010ae
A
90#define IOStatisticsCloseGate() \
91do { \
39037602
A
92 IOStatistics::countWorkLoopCloseGate(reserved->counter); \
93 if (reserved->lockInterval) reserved->lockTime = mach_absolute_time(); \
6d2010ae
A
94} while(0)
95
96#define IOStatisticsAttachEventSource() \
97do { \
98 IOStatistics::attachWorkLoopEventSource(reserved->counter, inEvent->reserved->counter); \
99} while(0)
100
101#define IOStatisticsDetachEventSource() \
102do { \
103 IOStatistics::detachWorkLoopEventSource(reserved->counter, inEvent->reserved->counter); \
104} while(0)
105
106#else
107
108#define IOStatisticsRegisterCounter()
109#define IOStatisticsUnregisterCounter()
110#define IOStatisticsOpenGate()
111#define IOStatisticsCloseGate()
112#define IOStatisticsAttachEventSource()
113#define IOStatisticsDetachEventSource()
114
115#endif /* IOKITSTATS */
b0d623f7 116
1c79356b
A
117bool IOWorkLoop::init()
118{
6d2010ae 119 // The super init and gateLock allocation MUST be done first.
1c79356b
A
120 if ( !super::init() )
121 return false;
2d21ac55 122
6d2010ae
A
123 // Allocate our ExpansionData if it hasn't been allocated already.
124 if ( !reserved )
125 {
126 reserved = IONew(ExpansionData,1);
127 if ( !reserved )
128 return false;
129
130 bzero(reserved,sizeof(ExpansionData));
131 }
132
133#if DEBUG
134 OSBacktrace ( reserved->allocationBacktrace, sizeof ( reserved->allocationBacktrace ) / sizeof ( reserved->allocationBacktrace[0] ) );
135#endif
136
2d21ac55
A
137 if ( gateLock == NULL ) {
138 if ( !( gateLock = IORecursiveLockAlloc()) )
139 return false;
140 }
141
142 if ( workToDoLock == NULL ) {
143 if ( !(workToDoLock = IOSimpleLockAlloc()) )
144 return false;
145 IOSimpleLockInit(workToDoLock);
146 workToDo = false;
147 }
1c79356b 148
6d2010ae
A
149 IOStatisticsRegisterCounter();
150
2d21ac55
A
151 if ( controlG == NULL ) {
152 controlG = IOCommandGate::commandGate(
153 this,
154 OSMemberFunctionCast(
155 IOCommandGate::Action,
156 this,
157 &IOWorkLoop::_maintRequest));
158
159 if ( !controlG )
160 return false;
161 // Point the controlGate at the workLoop. Usually addEventSource
162 // does this automatically. The problem is in this case addEventSource
163 // uses the control gate and it has to be bootstrapped.
164 controlG->setWorkLoop(this);
165 if (addEventSource(controlG) != kIOReturnSuccess)
166 return false;
167 }
1c79356b 168
2d21ac55 169 if ( workThread == NULL ) {
b0d623f7
A
170 thread_continue_t cptr = OSMemberFunctionCast(
171 thread_continue_t,
2d21ac55
A
172 this,
173 &IOWorkLoop::threadMain);
b0d623f7 174 if (KERN_SUCCESS != kernel_thread_start(cptr, this, &workThread))
2d21ac55
A
175 return false;
176 }
1c79356b 177
4b17d6b6 178 (void) thread_set_tag(workThread, THREAD_TAG_IOWORKLOOP);
1c79356b
A
179 return true;
180}
181
182IOWorkLoop *
183IOWorkLoop::workLoop()
2d21ac55
A
184{
185 return IOWorkLoop::workLoopWithOptions(0);
186}
187
188IOWorkLoop *
189IOWorkLoop::workLoopWithOptions(IOOptionBits options)
1c79356b 190{
6d2010ae
A
191 IOWorkLoop *me = new IOWorkLoop;
192
193 if (me && options) {
194 me->reserved = IONew(ExpansionData,1);
195 if (!me->reserved) {
196 me->release();
197 return 0;
198 }
199 bzero(me->reserved,sizeof(ExpansionData));
200 me->reserved->options = options;
2d21ac55 201 }
6d2010ae
A
202
203 if (me && !me->init()) {
204 me->release();
205 return 0;
206 }
207
208 return me;
1c79356b
A
209}
210
211// Free is called twice:
212// First when the atomic retainCount transitions from 1 -> 0
213// Secondly when the work loop itself is commiting hari kari
214// Hence the each leg of the free must be single threaded.
215void IOWorkLoop::free()
216{
217 if (workThread) {
218 IOInterruptState is;
219
220 // If we are here then we must be trying to shut down this work loop
2d21ac55 221 // in this case disable all of the event source, mark the loop
1c79356b
A
222 // as terminating and wakeup the work thread itself and return
223 // Note: we hold the gate across the entire operation mainly for the
224 // benefit of our event sources so we can disable them cleanly.
225 closeGate();
226
227 disableAllEventSources();
228
229 is = IOSimpleLockLockDisableInterrupt(workToDoLock);
230 SETP(&fFlags, kLoopTerminate);
231 thread_wakeup_one((void *) &workToDo);
232 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
233
234 openGate();
235 }
236 else /* !workThread */ {
237 IOEventSource *event, *next;
238
239 for (event = eventChain; event; event = next) {
240 next = event->getNext();
241 event->setWorkLoop(0);
242 event->setNext(0);
243 event->release();
244 }
245 eventChain = 0;
246
6d2010ae
A
247 for (event = passiveEventChain; event; event = next) {
248 next = event->getNext();
249 event->setWorkLoop(0);
250 event->setNext(0);
251 event->release();
252 }
253 passiveEventChain = 0;
254
2d21ac55
A
255 // Either we have a partial initialization to clean up
256 // or the workThread itself is performing hari-kari.
257 // Either way clean up all of our resources and return.
1c79356b
A
258
259 if (controlG) {
3e170ce0 260 controlG->workLoop = 0;
1c79356b
A
261 controlG->release();
262 controlG = 0;
263 }
264
265 if (workToDoLock) {
266 IOSimpleLockFree(workToDoLock);
267 workToDoLock = 0;
268 }
269
270 if (gateLock) {
271 IORecursiveLockFree(gateLock);
272 gateLock = 0;
273 }
6d2010ae
A
274
275 IOStatisticsUnregisterCounter();
276
2d21ac55
A
277 if (reserved) {
278 IODelete(reserved, ExpansionData, 1);
279 reserved = 0;
280 }
1c79356b
A
281
282 super::free();
283 }
284}
285
286IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
287{
39037602
A
288 if ((workThread)
289 && !thread_has_thread_name(workThread)
290 && (newEvent->owner)
291 && !OSDynamicCast(IOCommandPool, newEvent->owner)) {
292 thread_set_thread_name(workThread, newEvent->owner->getMetaClass()->getClassName());
293 }
294
1c79356b
A
295 return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
296}
297
298IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
299{
300 return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
301}
302
303void IOWorkLoop::enableAllEventSources() const
304{
305 IOEventSource *event;
306
307 for (event = eventChain; event; event = event->getNext())
308 event->enable();
6d2010ae
A
309
310 for (event = passiveEventChain; event; event = event->getNext())
311 event->enable();
1c79356b
A
312}
313
314void IOWorkLoop::disableAllEventSources() const
315{
316 IOEventSource *event;
317
318 for (event = eventChain; event; event = event->getNext())
6d2010ae
A
319 event->disable();
320
321 /* NOTE: controlG is in passiveEventChain since it's an IOCommandGate */
322 for (event = passiveEventChain; event; event = event->getNext())
1c79356b
A
323 if (event != controlG) // Don't disable the control gate
324 event->disable();
325}
326
327void IOWorkLoop::enableAllInterrupts() const
328{
329 IOEventSource *event;
6d2010ae 330
1c79356b
A
331 for (event = eventChain; event; event = event->getNext())
332 if (OSDynamicCast(IOInterruptEventSource, event))
333 event->enable();
334}
335
336void IOWorkLoop::disableAllInterrupts() const
337{
338 IOEventSource *event;
6d2010ae 339
1c79356b
A
340 for (event = eventChain; event; event = event->getNext())
341 if (OSDynamicCast(IOInterruptEventSource, event))
342 event->disable();
343}
344
1c79356b 345
0c530ab8 346/* virtual */ bool IOWorkLoop::runEventSources()
1c79356b 347{
0c530ab8 348 bool res = false;
060df5ea
A
349 bool traceWL = (gIOKitTrace & kIOTraceWorkLoops) ? true : false;
350 bool traceES = (gIOKitTrace & kIOTraceEventSources) ? true : false;
351
0c530ab8
A
352 closeGate();
353 if (ISSETP(&fFlags, kLoopTerminate))
6d2010ae
A
354 goto abort;
355
060df5ea
A
356 if (traceWL)
357 IOTimeStampStartConstant(IODBG_WORKLOOP(IOWL_WORK), (uintptr_t) this);
358
0c530ab8
A
359 bool more;
360 do {
6d2010ae
A
361 CLRP(&fFlags, kLoopRestart);
362 more = false;
363 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
364 workToDo = false;
365 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
366 /* NOTE: only loop over event sources in eventChain. Bypass "passive" event sources for performance */
367 for (IOEventSource *evnt = eventChain; evnt; evnt = evnt->getNext()) {
060df5ea 368
6d2010ae
A
369 if (traceES)
370 IOTimeStampStartConstant(IODBG_WORKLOOP(IOWL_CLIENT), (uintptr_t) this, (uintptr_t) evnt);
060df5ea 371
6d2010ae
A
372 more |= evnt->checkForWork();
373
374 if (traceES)
375 IOTimeStampEndConstant(IODBG_WORKLOOP(IOWL_CLIENT), (uintptr_t) this, (uintptr_t) evnt);
376
377 if (ISSETP(&fFlags, kLoopTerminate))
378 goto abort;
379 else if (fFlags & kLoopRestart) {
380 more = true;
381 break;
382 }
383 }
0c530ab8 384 } while (more);
6d2010ae 385
0c530ab8 386 res = true;
060df5ea
A
387
388 if (traceWL)
389 IOTimeStampEndConstant(IODBG_WORKLOOP(IOWL_WORK), (uintptr_t) this);
6d2010ae 390
0c530ab8
A
391abort:
392 openGate();
393 return res;
394}
395
396/* virtual */ void IOWorkLoop::threadMain()
397{
2d21ac55 398restartThread:
0c530ab8
A
399 do {
400 if ( !runEventSources() )
401 goto exitThread;
6601e61a 402
0c530ab8 403 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
1c79356b
A
404 if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
405 assert_wait((void *) &workToDo, false);
406 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
2d21ac55
A
407 thread_continue_t cptr = NULL;
408 if (!reserved || !(kPreciousStack & reserved->options))
409 cptr = OSMemberFunctionCast(
410 thread_continue_t, this, &IOWorkLoop::threadMain);
0c530ab8 411 thread_block_parameter(cptr, this);
2d21ac55 412 goto restartThread;
1c79356b
A
413 /* NOTREACHED */
414 }
415
416 // At this point we either have work to do or we need
417 // to commit suicide. But no matter
418 // Clear the simple lock and retore the interrupt state
419 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
0c530ab8 420 } while(workToDo);
1c79356b
A
421
422exitThread:
b0d623f7 423 thread_t thread = workThread;
1c79356b
A
424 workThread = 0; // Say we don't have a loop and free ourselves
425 free();
b0d623f7
A
426
427 thread_deallocate(thread);
428 (void) thread_terminate(thread);
1c79356b
A
429}
430
431IOThread IOWorkLoop::getThread() const
432{
433 return workThread;
434}
435
436bool IOWorkLoop::onThread() const
437{
438 return (IOThreadSelf() == workThread);
439}
440
441bool IOWorkLoop::inGate() const
442{
443 return IORecursiveLockHaveLock(gateLock);
444}
445
446// Internal APIs used by event sources to control the thread
447void IOWorkLoop::signalWorkAvailable()
448{
449 if (workToDoLock) {
450 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
451 workToDo = true;
452 thread_wakeup_one((void *) &workToDo);
453 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
454 }
455}
456
457void IOWorkLoop::openGate()
458{
6d2010ae 459 IOStatisticsOpenGate();
1c79356b
A
460 IORecursiveLockUnlock(gateLock);
461}
462
463void IOWorkLoop::closeGate()
464{
465 IORecursiveLockLock(gateLock);
6d2010ae 466 IOStatisticsCloseGate();
1c79356b
A
467}
468
469bool IOWorkLoop::tryCloseGate()
470{
6d2010ae
A
471 bool res = (IORecursiveLockTryLock(gateLock) != 0);
472 if (res) {
473 IOStatisticsCloseGate();
474 }
475 return res;
1c79356b
A
476}
477
478int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
479{
6d2010ae
A
480 int res;
481 IOStatisticsOpenGate();
482 res = IORecursiveLockSleep(gateLock, event, interuptibleType);
483 IOStatisticsCloseGate();
484 return res;
1c79356b
A
485}
486
b0d623f7
A
487int IOWorkLoop::sleepGate(void *event, AbsoluteTime deadline, UInt32 interuptibleType)
488{
6d2010ae
A
489 int res;
490 IOStatisticsOpenGate();
491 res = IORecursiveLockSleepDeadline(gateLock, event, deadline, interuptibleType);
492 IOStatisticsCloseGate();
493 return res;
b0d623f7
A
494}
495
1c79356b
A
496void IOWorkLoop::wakeupGate(void *event, bool oneThread)
497{
498 IORecursiveLockWakeup(gateLock, event, oneThread);
499}
500
0b4e3aa0 501IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
55e303ae
A
502 void *arg0, void *arg1,
503 void *arg2, void *arg3)
0b4e3aa0
A
504{
505 IOReturn res;
506
507 // closeGate is recursive so don't worry if we already hold the lock.
508 closeGate();
509 res = (*inAction)(target, arg0, arg1, arg2, arg3);
510 openGate();
511
512 return res;
513}
514
1c79356b
A
515IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
516{
b0d623f7 517 maintCommandEnum command = (maintCommandEnum) (uintptr_t) inC;
1c79356b
A
518 IOEventSource *inEvent = (IOEventSource *) inD;
519 IOReturn res = kIOReturnSuccess;
520
521 switch (command)
522 {
523 case mAddEvent:
9bccf70c
A
524 if (!inEvent->getWorkLoop()) {
525 SETP(&fFlags, kLoopRestart);
526
527 inEvent->retain();
528 inEvent->setWorkLoop(this);
529 inEvent->setNext(0);
6d2010ae
A
530
531 /* Check if this is a passive or active event source being added */
532 if (eventSourcePerformsWork(inEvent)) {
533
534 if (!eventChain)
535 eventChain = inEvent;
536 else {
537 IOEventSource *event, *next;
9bccf70c 538
6d2010ae
A
539 for (event = eventChain; (next = event->getNext()); event = next)
540 ;
541 event->setNext(inEvent);
542
543 }
544
545 }
9bccf70c 546 else {
6d2010ae
A
547
548 if (!passiveEventChain)
549 passiveEventChain = inEvent;
550 else {
551 IOEventSource *event, *next;
9bccf70c 552
6d2010ae
A
553 for (event = passiveEventChain; (next = event->getNext()); event = next)
554 ;
555 event->setNext(inEvent);
556
557 }
558
9bccf70c 559 }
6d2010ae 560 IOStatisticsAttachEventSource();
1c79356b
A
561 }
562 break;
563
564 case mRemoveEvent:
9bccf70c 565 if (inEvent->getWorkLoop()) {
316670eb
A
566 IOStatisticsDetachEventSource();
567
6d2010ae
A
568 if (eventSourcePerformsWork(inEvent)) {
569 if (eventChain == inEvent)
570 eventChain = inEvent->getNext();
571 else {
3e170ce0 572 IOEventSource *event, *next = 0;
6d2010ae
A
573
574 event = eventChain;
3e170ce0 575 if (event) while ((next = event->getNext()) && (next != inEvent))
6d2010ae
A
576 event = next;
577
578 if (!next) {
579 res = kIOReturnBadArgument;
580 break;
581 }
582 event->setNext(inEvent->getNext());
583 }
584 }
585 else {
586 if (passiveEventChain == inEvent)
587 passiveEventChain = inEvent->getNext();
588 else {
3e170ce0 589 IOEventSource *event, *next = 0;
6d2010ae
A
590
591 event = passiveEventChain;
3e170ce0 592 if (event) while ((next = event->getNext()) && (next != inEvent))
6d2010ae
A
593 event = next;
594
595 if (!next) {
596 res = kIOReturnBadArgument;
597 break;
598 }
599 event->setNext(inEvent->getNext());
600 }
601 }
602
9bccf70c
A
603 inEvent->setWorkLoop(0);
604 inEvent->setNext(0);
605 inEvent->release();
606 SETP(&fFlags, kLoopRestart);
1c79356b 607 }
1c79356b
A
608 break;
609
610 default:
611 return kIOReturnUnsupported;
612 }
613
614 return res;
615}
6d2010ae
A
616
617bool
618IOWorkLoop::eventSourcePerformsWork(IOEventSource *inEventSource)
619{
620 bool result = true;
621
622 /*
623 * The idea here is to see if the subclass of IOEventSource has overridden checkForWork().
624 * The assumption is that if you override checkForWork(), you need to be
625 * active and not passive.
626 *
627 * We picked a known quantity controlG that does not override
628 * IOEventSource::checkForWork(), namely the IOCommandGate associated with
629 * the workloop to which this event source is getting attached.
630 *
631 * We do a pointer comparison on the offset in the vtable for inNewEvent against
632 * the offset in the vtable for inReferenceEvent. This works because
633 * IOCommandGate's slot for checkForWork() has the address of
634 * IOEventSource::checkForWork() in it.
635 *
636 * Think of OSMemberFunctionCast yielding the value at the vtable offset for
637 * checkForWork() here. We're just testing to see if it's the same or not.
638 *
639 */
640 if (controlG) {
641 void * ptr1;
642 void * ptr2;
643
644 ptr1 = OSMemberFunctionCast(void*, inEventSource, &IOEventSource::checkForWork);
645 ptr2 = OSMemberFunctionCast(void*, controlG, &IOEventSource::checkForWork);
646
647 if (ptr1 == ptr2)
648 result = false;
649 }
650
651 return result;
652}
39037602
A
653
654void
655IOWorkLoop::lockTime(void)
656{
657 uint64_t time;
658 time = mach_absolute_time() - reserved->lockTime;
659 if (time > reserved->lockInterval)
660 {
661 absolutetime_to_nanoseconds(time, &time);
662 if (kTimeLockPanics & reserved->options) panic("IOWorkLoop %p lock time %qd us", this, time / 1000ULL);
663 else OSReportWithBacktrace("IOWorkLoop %p lock time %qd us", this, time / 1000ULL);
664 }
665}
666
667void
668IOWorkLoop::setMaximumLockTime(uint64_t interval, uint32_t options)
669{
670 IORecursiveLockLock(gateLock);
671 reserved->lockInterval = interval;
672 reserved->options = (reserved->options & ~kTimeLockPanics) | (options & kTimeLockPanics);
673 IORecursiveLockUnlock(gateLock);
674}