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