]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOCommandQueue.cpp
d528483b8e4f8e4de82cc4c199530bd4689aeb3a
[apple/xnu.git] / iokit / Kernel / IOCommandQueue.cpp
1 /*
2 * Copyright (c) 1998-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /*
31 Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
32
33 HISTORY
34 1998-7-13 Godfrey van der Linden(gvdl)
35 Created.
36 ]*/
37 #include <IOKit/IOCommandQueue.h>
38 #include <IOKit/IOWorkLoop.h>
39 #include <IOKit/IOTimeStamp.h>
40
41 #include <mach/sync_policy.h>
42
43 #define NUM_FIELDS_IN_COMMAND 4
44 typedef struct commandEntryTag {
45 void *f[NUM_FIELDS_IN_COMMAND];
46 } commandEntryT;
47
48 #define super IOEventSource
49
50 OSDefineMetaClassAndStructors(IOCommandQueue, IOEventSource)
51
52 /*[
53 Instance Methods
54
55 initWithNext:owner:action:size:
56 - initWithNext: (IOEventSource *) inNext
57 owner: (id) inOwner
58 action: (SEL) inAction
59 size: (int) inSize;
60
61 Primary initialiser for the IOCommandQueue class. Returns an
62 IOCommandQueue object that is initialised with the next object in
63 the chain and the owner and action. On return the signalWorkAvailableIMP
64 has been cached for this function.
65
66 If the object fails to initialise for some reason then [self free] will
67 be called and nil will be returned.
68
69 See also: initWithNext:owner:action:(IOEventSource)
70 ]*/
71 bool IOCommandQueue::init(OSObject *inOwner,
72 IOCommandQueueAction inAction,
73 int inSize)
74 {
75 if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
76 return false;
77
78 if (KERN_SUCCESS
79 != semaphore_create(kernel_task, &producerSema, SYNC_POLICY_FIFO, inSize))
80 return false;
81
82 size = inSize + 1; /* Allocate one more entry than needed */
83
84 queue = (void *)kalloc(size * sizeof(commandEntryT));
85 if (!queue)
86 return false;
87
88 producerLock = IOLockAlloc();
89 if (!producerLock)
90 return false;
91
92 producerIndex = consumerIndex = 0;
93
94 return true;
95 }
96
97 IOCommandQueue *
98 IOCommandQueue::commandQueue(OSObject *inOwner,
99 IOCommandQueueAction inAction,
100 int inSize)
101 {
102 IOCommandQueue *me = new IOCommandQueue;
103
104 if (me && !me->init(inOwner, inAction, inSize)) {
105 me->free();
106 return 0;
107 }
108
109 return me;
110 }
111
112 /*[
113 free
114 - free
115
116 Mandatory free of the object independent of the current retain count.
117 Returns nil.
118 ]*/
119 void IOCommandQueue::free()
120 {
121 if (queue)
122 kfree(queue, size * sizeof(commandEntryT));
123 if (producerSema)
124 semaphore_destroy(kernel_task, producerSema);
125 if (producerLock)
126 IOLockFree(producerLock);
127
128 super::free();
129 }
130
131 #if NUM_FIELDS_IN_COMMAND != 4
132 #error IOCommandQueue::checkForWork needs to be updated for new command size
133 #endif
134
135 bool IOCommandQueue::checkForWork()
136 {
137 void *field0, *field1, *field2, *field3;
138
139 if (!enabled || consumerIndex == producerIndex)
140 return false;
141
142 {
143 commandEntryT *q = (commandEntryT *) queue;
144 int localIndex = consumerIndex;
145
146 field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1];
147 field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3];
148 semaphore_signal(producerSema);
149 }
150
151 if (++consumerIndex >= size)
152 consumerIndex = 0;
153
154 IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
155 (unsigned int) action, (unsigned int) owner);
156
157 (*(IOCommandQueueAction) action)(owner, field0, field1, field2, field3);
158
159 return (consumerIndex != producerIndex);
160 }
161
162 /*[
163 enqueueSleep:command:
164 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
165 field0: (void *) field0 field1: (void *) field1
166 field2: (void *) field2 field3: (void *) field3;
167
168 Key method that enqueues the four input fields onto the command queue
169 and calls signalWorkAvailable to indicate that work is available to the
170 consumer. This routine is safe against multiple threaded producers.
171
172 A family of convenience functions have been provided to assist with the
173 enqueueing of an method selector and an integer tag. This relies on the
174 IODevice rawCommandOccurred... command to forward on the requests.
175
176 See also: signalWorkAvailable, checkForWork
177 ]*/
178 #if NUM_FIELDS_IN_COMMAND != 4
179 #error IOCommandQueue::enqueueCommand needs to be updated
180 #endif
181
182 kern_return_t
183 IOCommandQueue::enqueueCommand(bool gotoSleep,
184 void *field0, void *field1,
185 void *field2, void *field3)
186 {
187 kern_return_t rtn = KERN_SUCCESS;
188 int retry;
189
190 /* Make sure there is room in the queue before doing anything else */
191
192 if (gotoSleep) {
193 retry = 0;
194 do
195 rtn = semaphore_wait(producerSema);
196 while( (KERN_SUCCESS != rtn)
197 && (KERN_OPERATION_TIMED_OUT != rtn)
198 && (KERN_SEMAPHORE_DESTROYED != rtn)
199 && (KERN_TERMINATED != rtn)
200 && ((retry++) < 4));
201 } else
202 rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO);
203
204 if (KERN_SUCCESS != rtn)
205 return rtn;
206
207 /* Block other producers */
208 IOTakeLock(producerLock);
209
210 /*
211 * Make sure that we update the current producer entry before we
212 * increment the producer pointer. This avoids a nasty race as the
213 * as the test for work is producerIndex != consumerIndex and a signal.
214 */
215 {
216 commandEntryT *q = (commandEntryT *) queue;
217 int localIndex = producerIndex;
218
219 q[localIndex].f[0] = field0; q[localIndex].f[1] = field1;
220 q[localIndex].f[2] = field2; q[localIndex].f[3] = field3;
221 }
222 if (++producerIndex >= size)
223 producerIndex = 0;
224
225 /* Clear to allow other producers to go now */
226 IOUnlock(producerLock);
227
228 /*
229 * Right we have created some new work, we had better make sure that
230 * we notify the work loop that it has to test producerIndex.
231 */
232 signalWorkAvailable();
233 return rtn;
234 }
235
236 int IOCommandQueue::performAndFlush(OSObject *target,
237 IOCommandQueueAction inAction)
238 {
239 int numEntries;
240 kern_return_t rtn;
241
242 // Set the defaults if necessary
243 if (!target)
244 target = owner;
245 if (!inAction)
246 inAction = (IOCommandQueueAction) action;
247
248 // Lock out the producers first
249 do {
250 rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO);
251 } while (rtn == KERN_SUCCESS);
252
253 // now step over all remaining entries in the command queue
254 for (numEntries = 0; consumerIndex != producerIndex; ) {
255 void *field0, *field1, *field2, *field3;
256
257 {
258 commandEntryT *q = (commandEntryT *) queue;
259 int localIndex = consumerIndex;
260
261 field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1];
262 field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3];
263 }
264
265 if (++consumerIndex >= size)
266 consumerIndex = 0;
267
268 (*inAction)(target, field0, field1, field2, field3);
269 }
270
271 // finally refill the producer semaphore to size - 1
272 for (int i = 1; i < size; i++)
273 semaphore_signal(producerSema);
274
275 return numEntries;
276 }