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