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