2 * Copyright (c) 1998-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #if !defined(__LP64__)
31 #include <IOKit/IOCommandQueue.h>
32 #include <IOKit/IOWorkLoop.h>
33 #include <IOKit/IOTimeStamp.h>
34 #include <IOKit/IOKitDebug.h>
36 #include <mach/sync_policy.h>
40 #define IOStatisticsInitializeCounter() \
41 IOStatistics::setCounterType(reserved->counter, kIOStatisticsCommandQueueCounter)
43 #define IOStatisticsActionCall() \
44 IOStatistics::countCommandQueueActionCall(reserved->counter)
48 #define IOStatisticsInitializeCounter()
49 #define IOStatisticsActionCall()
51 #endif /* IOKITSTATS */
53 #define NUM_FIELDS_IN_COMMAND 4
54 typedef struct commandEntryTag
{
55 void *f
[NUM_FIELDS_IN_COMMAND
];
58 #define super IOEventSource
60 OSDefineMetaClassAndStructors(IOCommandQueue
, IOEventSource
)
65 initWithNext:owner:action:size:
66 - initWithNext: (IOEventSource *) inNext
68 action: (SEL) inAction
71 Primary initialiser for the IOCommandQueue class. Returns an
72 IOCommandQueue object that is initialised with the next object in
73 the chain and the owner and action. On return the signalWorkAvailableIMP
74 has been cached for this function.
76 If the object fails to initialise for some reason then [self free] will
77 be called and nil will be returned.
79 See also: initWithNext:owner:action:(IOEventSource)
81 bool IOCommandQueue::init(OSObject
*inOwner
,
82 IOCommandQueueAction inAction
,
85 if ( !super::init(inOwner
, (IOEventSourceAction
) inAction
) )
89 != semaphore_create(kernel_task
, &producerSema
, SYNC_POLICY_FIFO
, inSize
))
92 size
= inSize
+ 1; /* Allocate one more entry than needed */
94 queue
= (void *)kalloc(size
* sizeof(commandEntryT
));
98 producerLock
= IOLockAlloc();
102 producerIndex
= consumerIndex
= 0;
104 IOStatisticsInitializeCounter();
110 IOCommandQueue::commandQueue(OSObject
*inOwner
,
111 IOCommandQueueAction inAction
,
114 IOCommandQueue
*me
= new IOCommandQueue
;
116 if (me
&& !me
->init(inOwner
, inAction
, inSize
)) {
128 Mandatory free of the object independent of the current retain count.
131 void IOCommandQueue::free()
134 kfree(queue
, size
* sizeof(commandEntryT
));
136 semaphore_destroy(kernel_task
, producerSema
);
138 IOLockFree(producerLock
);
143 #if NUM_FIELDS_IN_COMMAND != 4
144 #error IOCommandQueue::checkForWork needs to be updated for new command size
147 bool IOCommandQueue::checkForWork()
149 void *field0
, *field1
, *field2
, *field3
;
150 bool trace
= ( gIOKitTrace
& kIOTraceCommandGates
) ? true : false;
152 if (!enabled
|| consumerIndex
== producerIndex
)
156 commandEntryT
*q
= (commandEntryT
*) queue
;
157 int localIndex
= consumerIndex
;
159 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
160 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
161 semaphore_signal(producerSema
);
164 if (++consumerIndex
>= size
)
168 IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION
),
169 (uintptr_t) action
, (uintptr_t) owner
);
171 IOStatisticsActionCall();
172 (*(IOCommandQueueAction
) action
)(owner
, field0
, field1
, field2
, field3
);
175 IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION
),
176 (uintptr_t) action
, (uintptr_t) owner
);
178 return (consumerIndex
!= producerIndex
);
182 enqueueSleep:command:
183 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
184 field0: (void *) field0 field1: (void *) field1
185 field2: (void *) field2 field3: (void *) field3;
187 Key method that enqueues the four input fields onto the command queue
188 and calls signalWorkAvailable to indicate that work is available to the
189 consumer. This routine is safe against multiple threaded producers.
191 A family of convenience functions have been provided to assist with the
192 enqueueing of an method selector and an integer tag. This relies on the
193 IODevice rawCommandOccurred... command to forward on the requests.
195 See also: signalWorkAvailable, checkForWork
197 #if NUM_FIELDS_IN_COMMAND != 4
198 #error IOCommandQueue::enqueueCommand needs to be updated
202 IOCommandQueue::enqueueCommand(bool gotoSleep
,
203 void *field0
, void *field1
,
204 void *field2
, void *field3
)
206 kern_return_t rtn
= KERN_SUCCESS
;
209 /* Make sure there is room in the queue before doing anything else */
214 rtn
= semaphore_wait(producerSema
);
215 while( (KERN_SUCCESS
!= rtn
)
216 && (KERN_OPERATION_TIMED_OUT
!= rtn
)
217 && (KERN_SEMAPHORE_DESTROYED
!= rtn
)
218 && (KERN_TERMINATED
!= rtn
)
221 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
223 if (KERN_SUCCESS
!= rtn
)
226 /* Block other producers */
227 IOTakeLock(producerLock
);
230 * Make sure that we update the current producer entry before we
231 * increment the producer pointer. This avoids a nasty race as the
232 * as the test for work is producerIndex != consumerIndex and a signal.
235 commandEntryT
*q
= (commandEntryT
*) queue
;
236 int localIndex
= producerIndex
;
238 q
[localIndex
].f
[0] = field0
; q
[localIndex
].f
[1] = field1
;
239 q
[localIndex
].f
[2] = field2
; q
[localIndex
].f
[3] = field3
;
241 if (++producerIndex
>= size
)
244 /* Clear to allow other producers to go now */
245 IOUnlock(producerLock
);
248 * Right we have created some new work, we had better make sure that
249 * we notify the work loop that it has to test producerIndex.
251 signalWorkAvailable();
255 int IOCommandQueue::performAndFlush(OSObject
*target
,
256 IOCommandQueueAction inAction
)
261 // Set the defaults if necessary
265 inAction
= (IOCommandQueueAction
) action
;
267 // Lock out the producers first
269 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
270 } while (rtn
== KERN_SUCCESS
);
272 // now step over all remaining entries in the command queue
273 for (numEntries
= 0; consumerIndex
!= producerIndex
; ) {
274 void *field0
, *field1
, *field2
, *field3
;
277 commandEntryT
*q
= (commandEntryT
*) queue
;
278 int localIndex
= consumerIndex
;
280 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
281 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
284 if (++consumerIndex
>= size
)
287 (*inAction
)(target
, field0
, field1
, field2
, field3
);
290 // finally refill the producer semaphore to size - 1
291 for (int i
= 1; i
< size
; i
++)
292 semaphore_signal(producerSema
);
297 #endif /* !defined(__LP64__) */