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