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