xref: /xnu-8020.101.4/iokit/Kernel/IOCommandGate.cpp (revision e7776783b89a353188416a9a346c6cdb4928faad)
1 /*
2  * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
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.
14  *
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
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #define IOKIT_ENABLE_SHARED_PTR
30 
31 #include <libkern/OSDebug.h>
32 #include <libkern/c++/OSSharedPtr.h>
33 
34 #include <IOKit/IOCommandGate.h>
35 #include <IOKit/IOWorkLoop.h>
36 #include <IOKit/IOReturn.h>
37 #include <IOKit/IOTimeStamp.h>
38 #include <IOKit/IOKitDebug.h>
39 
40 #define super IOEventSource
41 
42 OSDefineMetaClassAndStructorsWithZone(IOCommandGate, IOEventSource, ZC_NONE)
43 #if __LP64__
44 OSMetaClassDefineReservedUnused(IOCommandGate, 0);
45 #else
46 OSMetaClassDefineReservedUsedX86(IOCommandGate, 0);
47 #endif
48 OSMetaClassDefineReservedUnused(IOCommandGate, 1);
49 OSMetaClassDefineReservedUnused(IOCommandGate, 2);
50 OSMetaClassDefineReservedUnused(IOCommandGate, 3);
51 OSMetaClassDefineReservedUnused(IOCommandGate, 4);
52 OSMetaClassDefineReservedUnused(IOCommandGate, 5);
53 OSMetaClassDefineReservedUnused(IOCommandGate, 6);
54 OSMetaClassDefineReservedUnused(IOCommandGate, 7);
55 
56 #if IOKITSTATS
57 
58 #define IOStatisticsInitializeCounter() \
59 do { \
60 	IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsCommandGateCounter); \
61 } while (0)
62 
63 #define IOStatisticsActionCall() \
64 do { \
65 	IOStatistics::countCommandGateActionCall(IOEventSource::reserved->counter); \
66 } while (0)
67 
68 #else
69 
70 #define IOStatisticsInitializeCounter()
71 #define IOStatisticsActionCall()
72 
73 #endif /* IOKITSTATS */
74 
75 bool
init(OSObject * inOwner,Action inAction)76 IOCommandGate::init(OSObject *inOwner, Action inAction)
77 {
78 	bool res = super::init(inOwner, (IOEventSource::Action) inAction);
79 	if (res) {
80 		IOStatisticsInitializeCounter();
81 	}
82 
83 	return res;
84 }
85 
86 OSSharedPtr<IOCommandGate>
commandGate(OSObject * inOwner,Action inAction)87 IOCommandGate::commandGate(OSObject *inOwner, Action inAction)
88 {
89 	OSSharedPtr<IOCommandGate> me = OSMakeShared<IOCommandGate>();
90 
91 	if (me && !me->init(inOwner, inAction)) {
92 		return nullptr;
93 	}
94 
95 	return me;
96 }
97 
98 /* virtual */ void
disable()99 IOCommandGate::disable()
100 {
101 	if (workLoop && !workLoop->inGate()) {
102 		OSReportWithBacktrace("IOCommandGate::disable() called when not gated");
103 	}
104 
105 	super::disable();
106 }
107 
108 /* virtual */ void
enable()109 IOCommandGate::enable()
110 {
111 	if (workLoop) {
112 		closeGate();
113 		super::enable();
114 		wakeupGate(&enabled, /* oneThread */ false); // Unblock sleeping threads
115 		openGate();
116 	}
117 }
118 
119 /* virtual */ void
free()120 IOCommandGate::free()
121 {
122 	if (workLoop) {
123 		setWorkLoop(NULL);
124 	}
125 	super::free();
126 }
127 
128 enum{
129 	kSleepersRemoved     = 0x00000001,
130 	kSleepersWaitEnabled = 0x00000002,
131 	kSleepersActions     = 0x00000100,
132 	kSleepersActionsMask = 0xffffff00,
133 };
134 
135 /* virtual */ void
setWorkLoop(IOWorkLoop * inWorkLoop)136 IOCommandGate::setWorkLoop(IOWorkLoop *inWorkLoop)
137 {
138 	IOWorkLoop * wl;
139 	uintptr_t  * sleepersP = (uintptr_t *) &reserved;
140 	bool         defer;
141 
142 	if (!inWorkLoop && (wl = workLoop)) {           // tearing down
143 		wl->closeGate();
144 		*sleepersP |= kSleepersRemoved;
145 		while (*sleepersP & kSleepersWaitEnabled) {
146 			thread_wakeup_with_result(&enabled, THREAD_INTERRUPTED);
147 			sleepGate(sleepersP, THREAD_UNINT);
148 		}
149 		*sleepersP &= ~kSleepersWaitEnabled;
150 		defer = (0 != (kSleepersActionsMask & *sleepersP));
151 		if (!defer) {
152 			super::setWorkLoop(NULL);
153 			*sleepersP &= ~kSleepersRemoved;
154 		}
155 		wl->openGate();
156 		return;
157 	}
158 
159 	super::setWorkLoop(inWorkLoop);
160 }
161 
162 IOReturn
runCommand(void * arg0,void * arg1,void * arg2,void * arg3)163 IOCommandGate::runCommand(void *arg0, void *arg1,
164     void *arg2, void *arg3)
165 {
166 	return runAction((Action) action, arg0, arg1, arg2, arg3);
167 }
168 
169 IOReturn
attemptCommand(void * arg0,void * arg1,void * arg2,void * arg3)170 IOCommandGate::attemptCommand(void *arg0, void *arg1,
171     void *arg2, void *arg3)
172 {
173 	return attemptAction((Action) action, arg0, arg1, arg2, arg3);
174 }
175 
176 
177 static IOReturn
IOCommandGateActionToBlock(OSObject * owner,void * arg0,void * arg1,void * arg2,void * arg3)178 IOCommandGateActionToBlock(OSObject *owner,
179     void *arg0, void *arg1,
180     void *arg2, void *arg3)
181 {
182 	return ((IOEventSource::ActionBlock) arg0)();
183 }
184 
185 IOReturn
runActionBlock(ActionBlock _action)186 IOCommandGate::runActionBlock(ActionBlock _action)
187 {
188 	return runAction(&IOCommandGateActionToBlock, _action);
189 }
190 
191 IOReturn
runAction(Action inAction,void * arg0,void * arg1,void * arg2,void * arg3)192 IOCommandGate::runAction(Action inAction,
193     void *arg0, void *arg1,
194     void *arg2, void *arg3)
195 {
196 	IOWorkLoop * wl;
197 	uintptr_t  * sleepersP;
198 
199 	if (!inAction) {
200 		return kIOReturnBadArgument;
201 	}
202 	if (!(wl = workLoop)) {
203 		return kIOReturnNotReady;
204 	}
205 
206 	// closeGate is recursive needn't worry if we already hold the lock.
207 	wl->closeGate();
208 	sleepersP = (uintptr_t *) &reserved;
209 
210 	// If the command gate is disabled and we aren't on the workloop thread
211 	// itself then sleep until we get enabled.
212 	IOReturn res;
213 	if (!wl->onThread()) {
214 		while (!enabled) {
215 			IOReturn sleepResult = kIOReturnSuccess;
216 			if (workLoop) {
217 				*sleepersP |= kSleepersWaitEnabled;
218 				sleepResult = wl->sleepGate(&enabled, THREAD_INTERRUPTIBLE);
219 				*sleepersP &= ~kSleepersWaitEnabled;
220 			}
221 			bool wakeupTearDown = (!workLoop || (0 != (*sleepersP & kSleepersRemoved)));
222 			if ((kIOReturnSuccess != sleepResult) || wakeupTearDown) {
223 				wl->openGate();
224 
225 				if (wakeupTearDown) {
226 					wl->wakeupGate(sleepersP, false); // No further resources used
227 				}
228 				return kIOReturnAborted;
229 			}
230 		}
231 	}
232 
233 	bool trace = (gIOKitTrace & kIOTraceCommandGates) ? true : false;
234 
235 	if (trace) {
236 		IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
237 		    VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
238 	}
239 
240 	IOStatisticsActionCall();
241 
242 	// Must be gated and on the work loop or enabled
243 
244 	*sleepersP += kSleepersActions;
245 	res = (*inAction)(owner, arg0, arg1, arg2, arg3);
246 	*sleepersP -= kSleepersActions;
247 
248 	if (trace) {
249 		IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
250 		    VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
251 	}
252 
253 	if (kSleepersRemoved == ((kSleepersActionsMask | kSleepersRemoved) & *sleepersP)) {
254 		// no actions outstanding
255 		*sleepersP &= ~kSleepersRemoved;
256 		super::setWorkLoop(NULL);
257 	}
258 
259 	wl->openGate();
260 
261 	return res;
262 }
263 
264 IOReturn
attemptAction(Action inAction,void * arg0,void * arg1,void * arg2,void * arg3)265 IOCommandGate::attemptAction(Action inAction,
266     void *arg0, void *arg1,
267     void *arg2, void *arg3)
268 {
269 	IOReturn res;
270 	IOWorkLoop * wl;
271 
272 	if (!inAction) {
273 		return kIOReturnBadArgument;
274 	}
275 	if (!(wl = workLoop)) {
276 		return kIOReturnNotReady;
277 	}
278 
279 	// Try to close the gate if can't get return immediately.
280 	if (!wl->tryCloseGate()) {
281 		return kIOReturnCannotLock;
282 	}
283 
284 	// If the command gate is disabled then sleep until we get a wakeup
285 	if (!wl->onThread() && !enabled) {
286 		res = kIOReturnNotPermitted;
287 	} else {
288 		bool trace = (gIOKitTrace & kIOTraceCommandGates) ? true : false;
289 
290 		if (trace) {
291 			IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
292 			    VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
293 		}
294 
295 		IOStatisticsActionCall();
296 
297 		res = (*inAction)(owner, arg0, arg1, arg2, arg3);
298 
299 		if (trace) {
300 			IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
301 			    VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
302 		}
303 	}
304 
305 	wl->openGate();
306 
307 	return res;
308 }
309 
310 IOReturn
commandSleep(void * event,UInt32 interruptible)311 IOCommandGate::commandSleep(void *event, UInt32 interruptible)
312 {
313 	if (!workLoop->inGate()) {
314 		/* The equivalent of 'msleep' while not holding the mutex is invalid */
315 		panic("invalid commandSleep while not holding the gate");
316 	}
317 
318 	return sleepGate(event, interruptible);
319 }
320 
321 IOReturn
commandSleep(void * event,AbsoluteTime deadline,UInt32 interruptible)322 IOCommandGate::commandSleep(void *event, AbsoluteTime deadline, UInt32 interruptible)
323 {
324 	if (!workLoop->inGate()) {
325 		/* The equivalent of 'msleep' while not holding the mutex is invalid */
326 		panic("invalid commandSleep while not holding the gate");
327 	}
328 
329 	return sleepGate(event, deadline, interruptible);
330 }
331 
332 void
commandWakeup(void * event,bool oneThread)333 IOCommandGate::commandWakeup(void *event, bool oneThread)
334 {
335 	wakeupGate(event, oneThread);
336 }
337