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