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