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