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 |
79 | IOCommandGate::init(OSObject *inOwner, Action inAction) |
80 | { |
81 | bool res = super::init(owner: inOwner, action: (IOEventSource::Action) inAction); |
82 | if (res) { |
83 | IOStatisticsInitializeCounter(); |
84 | } |
85 | |
86 | return res; |
87 | } |
88 | |
89 | OSSharedPtr<IOCommandGate> |
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 |
102 | IOCommandGate::disable() |
103 | { |
104 | if (workLoop && !workLoop->inGate()) { |
105 | OSReportWithBacktrace(str: "IOCommandGate::disable() called when not gated" ); |
106 | } |
107 | |
108 | super::disable(); |
109 | } |
110 | |
111 | /* virtual */ void |
112 | IOCommandGate::enable() |
113 | { |
114 | if (workLoop) { |
115 | closeGate(); |
116 | super::enable(); |
117 | wakeupGate(event: &enabled, /* oneThread */ false); // Unblock sleeping threads |
118 | openGate(); |
119 | } |
120 | } |
121 | |
122 | /* virtual */ void |
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 |
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(event: 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 |
166 | IOCommandGate::runCommand(void *arg0, void *arg1, |
167 | void *arg2, void *arg3) |
168 | { |
169 | return runAction(action: (Action) action, arg0, arg1, arg2, arg3); |
170 | } |
171 | |
172 | IOReturn |
173 | IOCommandGate::attemptCommand(void *arg0, void *arg1, |
174 | void *arg2, void *arg3) |
175 | { |
176 | return attemptAction(action: (Action) action, arg0, arg1, arg2, arg3); |
177 | } |
178 | |
179 | #pragma clang diagnostic pop |
180 | |
181 | static IOReturn |
182 | IOCommandGateActionToBlock(OSObject *owner, |
183 | void *arg0, void *arg1, |
184 | void *arg2, void *arg3) |
185 | { |
186 | return ((IOEventSource::ActionBlock) arg0)(); |
187 | } |
188 | |
189 | IOReturn |
190 | IOCommandGate::runActionBlock(ActionBlock _action) |
191 | { |
192 | return runAction(action: &IOCommandGateActionToBlock, arg0: _action); |
193 | } |
194 | |
195 | IOReturn |
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(event: &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(event: sleepersP, oneThread: 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 |
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 |
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, type: interruptible); |
323 | } |
324 | |
325 | IOReturn |
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, type: interruptible); |
334 | } |
335 | |
336 | void |
337 | IOCommandGate::commandWakeup(void *event, bool oneThread) |
338 | { |
339 | wakeupGate(event, oneThread); |
340 | } |
341 | |