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