xref: /xnu-11215/iokit/Kernel/IOCommandGate.cpp (revision 5c2921b0)
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 #pragma clang diagnostic push
76 #pragma clang diagnostic ignored "-Wcast-function-type"
77 
78 bool
init(OSObject * inOwner,Action inAction)79 IOCommandGate::init(OSObject *inOwner, Action inAction)
80 {
81 	bool res = super::init(inOwner, (IOEventSource::Action) inAction);
82 	if (res) {
83 		IOStatisticsInitializeCounter();
84 	}
85 
86 	return res;
87 }
88 
89 OSSharedPtr<IOCommandGate>
commandGate(OSObject * inOwner,Action inAction)90 IOCommandGate::commandGate(OSObject *inOwner, Action inAction)
91 {
92 	OSSharedPtr<IOCommandGate> me = OSMakeShared<IOCommandGate>();
93 
94 	if (me && !me->init(inOwner, inAction)) {
95 		return nullptr;
96 	}
97 
98 	return me;
99 }
100 
101 /* virtual */ void
disable()102 IOCommandGate::disable()
103 {
104 	if (workLoop && !workLoop->inGate()) {
105 		OSReportWithBacktrace("IOCommandGate::disable() called when not gated");
106 	}
107 
108 	super::disable();
109 }
110 
111 /* virtual */ void
enable()112 IOCommandGate::enable()
113 {
114 	if (workLoop) {
115 		closeGate();
116 		super::enable();
117 		wakeupGate(&enabled, /* oneThread */ false); // Unblock sleeping threads
118 		openGate();
119 	}
120 }
121 
122 /* virtual */ void
free()123 IOCommandGate::free()
124 {
125 	if (workLoop) {
126 		setWorkLoop(NULL);
127 	}
128 	super::free();
129 }
130 
131 enum{
132 	kSleepersRemoved     = 0x00000001,
133 	kSleepersWaitEnabled = 0x00000002,
134 	kSleepersActions     = 0x00000100,
135 	kSleepersActionsMask = 0xffffff00,
136 };
137 
138 /* virtual */ void
setWorkLoop(IOWorkLoop * inWorkLoop)139 IOCommandGate::setWorkLoop(IOWorkLoop *inWorkLoop)
140 {
141 	IOWorkLoop * wl;
142 	uintptr_t  * sleepersP = (uintptr_t *) &reserved;
143 	bool         defer;
144 
145 	if (!inWorkLoop && (wl = workLoop)) {           // tearing down
146 		wl->closeGate();
147 		*sleepersP |= kSleepersRemoved;
148 		while (*sleepersP & kSleepersWaitEnabled) {
149 			thread_wakeup_with_result(&enabled, THREAD_INTERRUPTED);
150 			sleepGate(sleepersP, THREAD_UNINT);
151 		}
152 		*sleepersP &= ~kSleepersWaitEnabled;
153 		defer = (0 != (kSleepersActionsMask & *sleepersP));
154 		if (!defer) {
155 			super::setWorkLoop(NULL);
156 			*sleepersP &= ~kSleepersRemoved;
157 		}
158 		wl->openGate();
159 		return;
160 	}
161 
162 	super::setWorkLoop(inWorkLoop);
163 }
164 
165 IOReturn
runCommand(void * arg0,void * arg1,void * arg2,void * arg3)166 IOCommandGate::runCommand(void *arg0, void *arg1,
167     void *arg2, void *arg3)
168 {
169 	return runAction((Action) action, arg0, arg1, arg2, arg3);
170 }
171 
172 IOReturn
attemptCommand(void * arg0,void * arg1,void * arg2,void * arg3)173 IOCommandGate::attemptCommand(void *arg0, void *arg1,
174     void *arg2, void *arg3)
175 {
176 	return attemptAction((Action) action, arg0, arg1, arg2, arg3);
177 }
178 
179 #pragma clang diagnostic pop
180 
181 static IOReturn
IOCommandGateActionToBlock(OSObject * owner,void * arg0,void * arg1,void * arg2,void * arg3)182 IOCommandGateActionToBlock(OSObject *owner,
183     void *arg0, void *arg1,
184     void *arg2, void *arg3)
185 {
186 	return ((IOEventSource::ActionBlock) arg0)();
187 }
188 
189 IOReturn
runActionBlock(ActionBlock _action)190 IOCommandGate::runActionBlock(ActionBlock _action)
191 {
192 	return runAction(&IOCommandGateActionToBlock, _action);
193 }
194 
195 IOReturn
runAction(Action inAction,void * arg0,void * arg1,void * arg2,void * arg3)196 IOCommandGate::runAction(Action inAction,
197     void *arg0, void *arg1,
198     void *arg2, void *arg3)
199 {
200 	IOWorkLoop * wl;
201 	uintptr_t  * sleepersP;
202 
203 	if (!inAction) {
204 		return kIOReturnBadArgument;
205 	}
206 	if (!(wl = workLoop)) {
207 		return kIOReturnNotReady;
208 	}
209 
210 	// closeGate is recursive needn't worry if we already hold the lock.
211 	wl->closeGate();
212 	sleepersP = (uintptr_t *) &reserved;
213 
214 	// If the command gate is disabled and we aren't on the workloop thread
215 	// itself then sleep until we get enabled.
216 	IOReturn res;
217 	if (!wl->onThread()) {
218 		while (!enabled) {
219 			IOReturn sleepResult = kIOReturnSuccess;
220 			if (workLoop) {
221 				*sleepersP |= kSleepersWaitEnabled;
222 				sleepResult = wl->sleepGate(&enabled, THREAD_INTERRUPTIBLE);
223 				*sleepersP &= ~kSleepersWaitEnabled;
224 			}
225 			bool wakeupTearDown = (!workLoop || (0 != (*sleepersP & kSleepersRemoved)));
226 			if ((kIOReturnSuccess != sleepResult) || wakeupTearDown) {
227 				wl->openGate();
228 
229 				if (wakeupTearDown) {
230 					wl->wakeupGate(sleepersP, false); // No further resources used
231 				}
232 				return kIOReturnAborted;
233 			}
234 		}
235 	}
236 
237 	bool trace = (gIOKitTrace & kIOTraceCommandGates) ? true : false;
238 
239 	if (trace) {
240 		IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
241 		    VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
242 	}
243 
244 	IOStatisticsActionCall();
245 
246 	// Must be gated and on the work loop or enabled
247 
248 	*sleepersP += kSleepersActions;
249 	res = (*inAction)(owner, arg0, arg1, arg2, arg3);
250 	*sleepersP -= kSleepersActions;
251 
252 	if (trace) {
253 		IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
254 		    VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
255 	}
256 
257 	if (kSleepersRemoved == ((kSleepersActionsMask | kSleepersRemoved) & *sleepersP)) {
258 		// no actions outstanding
259 		*sleepersP &= ~kSleepersRemoved;
260 		super::setWorkLoop(NULL);
261 	}
262 
263 	wl->openGate();
264 
265 	return res;
266 }
267 
268 IOReturn
attemptAction(Action inAction,void * arg0,void * arg1,void * arg2,void * arg3)269 IOCommandGate::attemptAction(Action inAction,
270     void *arg0, void *arg1,
271     void *arg2, void *arg3)
272 {
273 	IOReturn res;
274 	IOWorkLoop * wl;
275 
276 	if (!inAction) {
277 		return kIOReturnBadArgument;
278 	}
279 	if (!(wl = workLoop)) {
280 		return kIOReturnNotReady;
281 	}
282 
283 	// Try to close the gate if can't get return immediately.
284 	if (!wl->tryCloseGate()) {
285 		return kIOReturnCannotLock;
286 	}
287 
288 	// If the command gate is disabled then sleep until we get a wakeup
289 	if (!wl->onThread() && !enabled) {
290 		res = kIOReturnNotPermitted;
291 	} else {
292 		bool trace = (gIOKitTrace & kIOTraceCommandGates) ? true : false;
293 
294 		if (trace) {
295 			IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
296 			    VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
297 		}
298 
299 		IOStatisticsActionCall();
300 
301 		res = (*inAction)(owner, arg0, arg1, arg2, arg3);
302 
303 		if (trace) {
304 			IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
305 			    VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
306 		}
307 	}
308 
309 	wl->openGate();
310 
311 	return res;
312 }
313 
314 IOReturn
commandSleep(void * event,UInt32 interruptible)315 IOCommandGate::commandSleep(void *event, UInt32 interruptible)
316 {
317 	if (!workLoop->inGate()) {
318 		/* The equivalent of 'msleep' while not holding the mutex is invalid */
319 		panic("invalid commandSleep while not holding the gate");
320 	}
321 
322 	return sleepGate(event, interruptible);
323 }
324 
325 IOReturn
commandSleep(void * event,AbsoluteTime deadline,UInt32 interruptible)326 IOCommandGate::commandSleep(void *event, AbsoluteTime deadline, UInt32 interruptible)
327 {
328 	if (!workLoop->inGate()) {
329 		/* The equivalent of 'msleep' while not holding the mutex is invalid */
330 		panic("invalid commandSleep while not holding the gate");
331 	}
332 
333 	return sleepGate(event, deadline, interruptible);
334 }
335 
336 void
commandWakeup(void * event,bool oneThread)337 IOCommandGate::commandWakeup(void *event, bool oneThread)
338 {
339 	wakeupGate(event, oneThread);
340 }
341