]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOCommandQueue.cpp
xnu-2422.1.72.tar.gz
[apple/xnu.git] / iokit / Kernel / IOCommandQueue.cpp
1 /*
2 * Copyright (c) 1998-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 #if !defined(__LP64__)
30
31 #include <IOKit/IOCommandQueue.h>
32 #include <IOKit/IOWorkLoop.h>
33 #include <IOKit/IOTimeStamp.h>
34 #include <IOKit/IOKitDebug.h>
35
36 #include <mach/sync_policy.h>
37
38 #if IOKITSTATS
39
40 #define IOStatisticsInitializeCounter() \
41 IOStatistics::setCounterType(reserved->counter, kIOStatisticsCommandQueueCounter)
42
43 #define IOStatisticsActionCall() \
44 IOStatistics::countCommandQueueActionCall(reserved->counter)
45
46 #else
47
48 #define IOStatisticsInitializeCounter()
49 #define IOStatisticsActionCall()
50
51 #endif /* IOKITSTATS */
52
53 #define NUM_FIELDS_IN_COMMAND 4
54 typedef struct commandEntryTag {
55 void *f[NUM_FIELDS_IN_COMMAND];
56 } commandEntryT;
57
58 #define super IOEventSource
59
60 OSDefineMetaClassAndStructors(IOCommandQueue, IOEventSource)
61
62 /*[
63 Instance Methods
64
65 initWithNext:owner:action:size:
66 - initWithNext: (IOEventSource *) inNext
67 owner: (id) inOwner
68 action: (SEL) inAction
69 size: (int) inSize;
70
71 Primary initialiser for the IOCommandQueue class. Returns an
72 IOCommandQueue object that is initialised with the next object in
73 the chain and the owner and action. On return the signalWorkAvailableIMP
74 has been cached for this function.
75
76 If the object fails to initialise for some reason then [self free] will
77 be called and nil will be returned.
78
79 See also: initWithNext:owner:action:(IOEventSource)
80 ]*/
81 bool IOCommandQueue::init(OSObject *inOwner,
82 IOCommandQueueAction inAction,
83 int inSize)
84 {
85 if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
86 return false;
87
88 if (KERN_SUCCESS
89 != semaphore_create(kernel_task, &producerSema, SYNC_POLICY_FIFO, inSize))
90 return false;
91
92 size = inSize + 1; /* Allocate one more entry than needed */
93
94 queue = (void *)kalloc(size * sizeof(commandEntryT));
95 if (!queue)
96 return false;
97
98 producerLock = IOLockAlloc();
99 if (!producerLock)
100 return false;
101
102 producerIndex = consumerIndex = 0;
103
104 IOStatisticsInitializeCounter();
105
106 return true;
107 }
108
109 IOCommandQueue *
110 IOCommandQueue::commandQueue(OSObject *inOwner,
111 IOCommandQueueAction inAction,
112 int inSize)
113 {
114 IOCommandQueue *me = new IOCommandQueue;
115
116 if (me && !me->init(inOwner, inAction, inSize)) {
117 me->free();
118 return 0;
119 }
120
121 return me;
122 }
123
124 /*[
125 free
126 - free
127
128 Mandatory free of the object independent of the current retain count.
129 Returns nil.
130 ]*/
131 void IOCommandQueue::free()
132 {
133 if (queue)
134 kfree(queue, size * sizeof(commandEntryT));
135 if (producerSema)
136 semaphore_destroy(kernel_task, producerSema);
137 if (producerLock)
138 IOLockFree(producerLock);
139
140 super::free();
141 }
142
143 #if NUM_FIELDS_IN_COMMAND != 4
144 #error IOCommandQueue::checkForWork needs to be updated for new command size
145 #endif
146
147 bool IOCommandQueue::checkForWork()
148 {
149 void *field0, *field1, *field2, *field3;
150 bool trace = ( gIOKitTrace & kIOTraceCommandGates ) ? true : false;
151
152 if (!enabled || consumerIndex == producerIndex)
153 return false;
154
155 {
156 commandEntryT *q = (commandEntryT *) queue;
157 int localIndex = consumerIndex;
158
159 field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1];
160 field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3];
161 semaphore_signal(producerSema);
162 }
163
164 if (++consumerIndex >= size)
165 consumerIndex = 0;
166
167 if (trace)
168 IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
169 (uintptr_t) action, (uintptr_t) owner);
170
171 IOStatisticsActionCall();
172 (*(IOCommandQueueAction) action)(owner, field0, field1, field2, field3);
173
174 if (trace)
175 IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
176 (uintptr_t) action, (uintptr_t) owner);
177
178 return (consumerIndex != producerIndex);
179 }
180
181 /*[
182 enqueueSleep:command:
183 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
184 field0: (void *) field0 field1: (void *) field1
185 field2: (void *) field2 field3: (void *) field3;
186
187 Key method that enqueues the four input fields onto the command queue
188 and calls signalWorkAvailable to indicate that work is available to the
189 consumer. This routine is safe against multiple threaded producers.
190
191 A family of convenience functions have been provided to assist with the
192 enqueueing of an method selector and an integer tag. This relies on the
193 IODevice rawCommandOccurred... command to forward on the requests.
194
195 See also: signalWorkAvailable, checkForWork
196 ]*/
197 #if NUM_FIELDS_IN_COMMAND != 4
198 #error IOCommandQueue::enqueueCommand needs to be updated
199 #endif
200
201 kern_return_t
202 IOCommandQueue::enqueueCommand(bool gotoSleep,
203 void *field0, void *field1,
204 void *field2, void *field3)
205 {
206 kern_return_t rtn = KERN_SUCCESS;
207 int retry;
208
209 /* Make sure there is room in the queue before doing anything else */
210
211 if (gotoSleep) {
212 retry = 0;
213 do
214 rtn = semaphore_wait(producerSema);
215 while( (KERN_SUCCESS != rtn)
216 && (KERN_OPERATION_TIMED_OUT != rtn)
217 && (KERN_SEMAPHORE_DESTROYED != rtn)
218 && (KERN_TERMINATED != rtn)
219 && ((retry++) < 4));
220 } else
221 rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO);
222
223 if (KERN_SUCCESS != rtn)
224 return rtn;
225
226 /* Block other producers */
227 IOTakeLock(producerLock);
228
229 /*
230 * Make sure that we update the current producer entry before we
231 * increment the producer pointer. This avoids a nasty race as the
232 * as the test for work is producerIndex != consumerIndex and a signal.
233 */
234 {
235 commandEntryT *q = (commandEntryT *) queue;
236 int localIndex = producerIndex;
237
238 q[localIndex].f[0] = field0; q[localIndex].f[1] = field1;
239 q[localIndex].f[2] = field2; q[localIndex].f[3] = field3;
240 }
241 if (++producerIndex >= size)
242 producerIndex = 0;
243
244 /* Clear to allow other producers to go now */
245 IOUnlock(producerLock);
246
247 /*
248 * Right we have created some new work, we had better make sure that
249 * we notify the work loop that it has to test producerIndex.
250 */
251 signalWorkAvailable();
252 return rtn;
253 }
254
255 int IOCommandQueue::performAndFlush(OSObject *target,
256 IOCommandQueueAction inAction)
257 {
258 int numEntries;
259 kern_return_t rtn;
260
261 // Set the defaults if necessary
262 if (!target)
263 target = owner;
264 if (!inAction)
265 inAction = (IOCommandQueueAction) action;
266
267 // Lock out the producers first
268 do {
269 rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO);
270 } while (rtn == KERN_SUCCESS);
271
272 // now step over all remaining entries in the command queue
273 for (numEntries = 0; consumerIndex != producerIndex; ) {
274 void *field0, *field1, *field2, *field3;
275
276 {
277 commandEntryT *q = (commandEntryT *) queue;
278 int localIndex = consumerIndex;
279
280 field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1];
281 field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3];
282 }
283
284 if (++consumerIndex >= size)
285 consumerIndex = 0;
286
287 (*inAction)(target, field0, field1, field2, field3);
288 }
289
290 // finally refill the producer semaphore to size - 1
291 for (int i = 1; i < size; i++)
292 semaphore_signal(producerSema);
293
294 return numEntries;
295 }
296
297 #endif /* !defined(__LP64__) */