]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOCommandQueue.cpp
xnu-792.17.14.tar.gz
[apple/xnu.git] / iokit / Kernel / IOCommandQueue.cpp
CommitLineData
1c79356b 1/*
91447636 2 * Copyright (c) 1998-2004 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
8f6c56a5 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
8f6c56a5
A
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
8ad349bb 24 * limitations under the License.
8f6c56a5
A
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
30
31HISTORY
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
42typedef struct commandEntryTag {
43 void *f[NUM_FIELDS_IN_COMMAND];
44} commandEntryT;
45
46#define super IOEventSource
47
48OSDefineMetaClassAndStructors(IOCommandQueue, IOEventSource)
49
50/*[
51Instance Methods
52
53initWithNext:owner:action:size:
54 - initWithNext: (IOEventSource *) inNext
55 owner: (id) inOwner
56 action: (SEL) inAction
57 size: (int) inSize;
58
59Primary initialiser for the IOCommandQueue class. Returns an
60IOCommandQueue object that is initialised with the next object in
61the chain and the owner and action. On return the signalWorkAvailableIMP
62has been cached for this function.
63
64If the object fails to initialise for some reason then [self free] will
65be called and nil will be returned.
66
67See also: initWithNext:owner:action:(IOEventSource)
68]*/
69bool 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
95IOCommandQueue *
96IOCommandQueue::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/*[
111free
112 - free
113
114Mandatory free of the object independent of the current retain count.
115Returns nil.
116]*/
117void IOCommandQueue::free()
118{
119 if (queue)
91447636 120 kfree(queue, size * sizeof(commandEntryT));
1c79356b
A
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
133bool 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/*[
161enqueueSleep:command:
162 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
163 field0: (void *) field0 field1: (void *) field1
164 field2: (void *) field2 field3: (void *) field3;
165
166Key method that enqueues the four input fields onto the command queue
167and calls signalWorkAvailable to indicate that work is available to the
168consumer. This routine is safe against multiple threaded producers.
169
170A family of convenience functions have been provided to assist with the
171enqueueing of an method selector and an integer tag. This relies on the
172IODevice rawCommandOccurred... command to forward on the requests.
173
174See also: signalWorkAvailable, checkForWork
175]*/
176#if NUM_FIELDS_IN_COMMAND != 4
177#error IOCommandQueue::enqueueCommand needs to be updated
178#endif
179
180kern_return_t
181IOCommandQueue::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
234int 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}