]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOCommandQueue.cpp
e2cd65b4e601725a80a0a2248c820a78d9b1c673
[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 Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
30
31 HISTORY
32 1998-7-13 Godfrey van der Linden(gvdl)
33 Created.
34 ]*/
35
36 #if !defined(__LP64__)
37
38 #include <IOKit/IOCommandQueue.h>
39 #include <IOKit/IOWorkLoop.h>
40 #include <IOKit/IOTimeStamp.h>
41
42 #include <mach/sync_policy.h>
43
44
45 #define NUM_FIELDS_IN_COMMAND 4
46 typedef struct commandEntryTag {
47 void *f[NUM_FIELDS_IN_COMMAND];
48 } commandEntryT;
49
50 #define super IOEventSource
51
52 OSDefineMetaClassAndStructors(IOCommandQueue, IOEventSource)
53
54 /*[
55 Instance Methods
56
57 initWithNext:owner:action:size:
58 - initWithNext: (IOEventSource *) inNext
59 owner: (id) inOwner
60 action: (SEL) inAction
61 size: (int) inSize;
62
63 Primary initialiser for the IOCommandQueue class. Returns an
64 IOCommandQueue object that is initialised with the next object in
65 the chain and the owner and action. On return the signalWorkAvailableIMP
66 has been cached for this function.
67
68 If the object fails to initialise for some reason then [self free] will
69 be called and nil will be returned.
70
71 See also: initWithNext:owner:action:(IOEventSource)
72 ]*/
73 bool IOCommandQueue::init(OSObject *inOwner,
74 IOCommandQueueAction inAction,
75 int inSize)
76 {
77 if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
78 return false;
79
80 if (KERN_SUCCESS
81 != semaphore_create(kernel_task, &producerSema, SYNC_POLICY_FIFO, inSize))
82 return false;
83
84 size = inSize + 1; /* Allocate one more entry than needed */
85
86 queue = (void *)kalloc(size * sizeof(commandEntryT));
87 if (!queue)
88 return false;
89
90 producerLock = IOLockAlloc();
91 if (!producerLock)
92 return false;
93
94 producerIndex = consumerIndex = 0;
95
96 return true;
97 }
98
99 IOCommandQueue *
100 IOCommandQueue::commandQueue(OSObject *inOwner,
101 IOCommandQueueAction inAction,
102 int inSize)
103 {
104 IOCommandQueue *me = new IOCommandQueue;
105
106 if (me && !me->init(inOwner, inAction, inSize)) {
107 me->free();
108 return 0;
109 }
110
111 return me;
112 }
113
114 /*[
115 free
116 - free
117
118 Mandatory free of the object independent of the current retain count.
119 Returns nil.
120 ]*/
121 void IOCommandQueue::free()
122 {
123 if (queue)
124 kfree(queue, size * sizeof(commandEntryT));
125 if (producerSema)
126 semaphore_destroy(kernel_task, producerSema);
127 if (producerLock)
128 IOLockFree(producerLock);
129
130 super::free();
131 }
132
133 #if NUM_FIELDS_IN_COMMAND != 4
134 #error IOCommandQueue::checkForWork needs to be updated for new command size
135 #endif
136
137 bool IOCommandQueue::checkForWork()
138 {
139 void *field0, *field1, *field2, *field3;
140
141 if (!enabled || consumerIndex == producerIndex)
142 return false;
143
144 {
145 commandEntryT *q = (commandEntryT *) queue;
146 int localIndex = consumerIndex;
147
148 field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1];
149 field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3];
150 semaphore_signal(producerSema);
151 }
152
153 if (++consumerIndex >= size)
154 consumerIndex = 0;
155
156 IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
157 (uintptr_t) action, (uintptr_t) owner);
158
159 (*(IOCommandQueueAction) action)(owner, field0, field1, field2, field3);
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__) */