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