xref: /xnu-11215/iokit/Kernel/IOCommandQueue.cpp (revision a5e72196)
1c1dac77fSApple OSS Distributions /*
2855239e5SApple OSS Distributions  * Copyright (c) 1998-2010 Apple 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  */
283ca3bd55SApple OSS Distributions 
293ca3bd55SApple OSS Distributions #if !defined(__LP64__)
303ca3bd55SApple OSS Distributions 
31c1dac77fSApple OSS Distributions #include <IOKit/IOCommandQueue.h>
32c1dac77fSApple OSS Distributions #include <IOKit/IOWorkLoop.h>
33c1dac77fSApple OSS Distributions #include <IOKit/IOTimeStamp.h>
34855239e5SApple OSS Distributions #include <IOKit/IOKitDebug.h>
35c1dac77fSApple OSS Distributions 
36c1dac77fSApple OSS Distributions #include <mach/sync_policy.h>
37c1dac77fSApple OSS Distributions 
38855239e5SApple OSS Distributions #if IOKITSTATS
39855239e5SApple OSS Distributions 
40855239e5SApple OSS Distributions #define IOStatisticsInitializeCounter() \
41855239e5SApple OSS Distributions 	IOStatistics::setCounterType(reserved->counter, kIOStatisticsCommandQueueCounter)
42855239e5SApple OSS Distributions 
43855239e5SApple OSS Distributions #define IOStatisticsActionCall() \
44855239e5SApple OSS Distributions 	IOStatistics::countCommandQueueActionCall(reserved->counter)
45855239e5SApple OSS Distributions 
46855239e5SApple OSS Distributions #else
47855239e5SApple OSS Distributions 
48855239e5SApple OSS Distributions #define IOStatisticsInitializeCounter()
49855239e5SApple OSS Distributions #define IOStatisticsActionCall()
50855239e5SApple OSS Distributions 
51855239e5SApple OSS Distributions #endif /* IOKITSTATS */
523ca3bd55SApple OSS Distributions 
53c1dac77fSApple OSS Distributions #define NUM_FIELDS_IN_COMMAND   4
54c1dac77fSApple OSS Distributions typedef struct commandEntryTag {
55c1dac77fSApple OSS Distributions 	void *f[NUM_FIELDS_IN_COMMAND];
56c1dac77fSApple OSS Distributions } commandEntryT;
57c1dac77fSApple OSS Distributions 
58c1dac77fSApple OSS Distributions #define super IOEventSource
59c1dac77fSApple OSS Distributions 
60c1dac77fSApple OSS Distributions OSDefineMetaClassAndStructors(IOCommandQueue, IOEventSource)
61c1dac77fSApple OSS Distributions 
62c1dac77fSApple OSS Distributions /*[
63*a5e72196SApple OSS Distributions  *  Instance Methods
64*a5e72196SApple OSS Distributions  *
65*a5e72196SApple OSS Distributions  *  initWithNext:owner:action:size:
66*a5e72196SApple OSS Distributions  *   - initWithNext: (IOEventSource *) inNext
67*a5e72196SApple OSS Distributions  *           owner: (id) inOwner
68*a5e72196SApple OSS Distributions  *           action: (SEL) inAction
69*a5e72196SApple OSS Distributions  *             size: (int) inSize;
70*a5e72196SApple OSS Distributions  *
71*a5e72196SApple OSS Distributions  *  Primary initialiser for the IOCommandQueue class.  Returns an
72*a5e72196SApple OSS Distributions  *  IOCommandQueue object that is initialised with the next object in
73*a5e72196SApple OSS Distributions  *  the chain and the owner and action. On return the signalWorkAvailableIMP
74*a5e72196SApple OSS Distributions  *  has been cached for this function.
75*a5e72196SApple OSS Distributions  *
76*a5e72196SApple OSS Distributions  *  If the object fails to initialise for some reason then [self free] will
77*a5e72196SApple OSS Distributions  *  be called and nil will be returned.
78*a5e72196SApple OSS Distributions  *
79*a5e72196SApple OSS Distributions  *  See also: initWithNext:owner:action:(IOEventSource)
80*a5e72196SApple OSS Distributions  *  ]*/
81*a5e72196SApple OSS Distributions bool
82*a5e72196SApple OSS Distributions IOCommandQueue::init(OSObject *inOwner,
83c1dac77fSApple OSS Distributions     IOCommandQueueAction inAction,
84c1dac77fSApple OSS Distributions     int inSize)
85c1dac77fSApple OSS Distributions {
86*a5e72196SApple OSS Distributions 	if (!super::init(inOwner, (IOEventSourceAction) inAction)) {
87c1dac77fSApple OSS Distributions 		return false;
88*a5e72196SApple OSS Distributions 	}
89c1dac77fSApple OSS Distributions 
90c1dac77fSApple OSS Distributions 	if (KERN_SUCCESS
91*a5e72196SApple OSS Distributions 	    != semaphore_create(kernel_task, &producerSema, SYNC_POLICY_FIFO, inSize)) {
92c1dac77fSApple OSS Distributions 		return false;
93*a5e72196SApple OSS Distributions 	}
94c1dac77fSApple OSS Distributions 
95c1dac77fSApple OSS Distributions 	size = inSize + 1; /* Allocate one more entry than needed */
96c1dac77fSApple OSS Distributions 
97c1dac77fSApple OSS Distributions 	queue = (void *)kalloc(size * sizeof(commandEntryT));
98*a5e72196SApple OSS Distributions 	if (!queue) {
99c1dac77fSApple OSS Distributions 		return false;
100*a5e72196SApple OSS Distributions 	}
101c1dac77fSApple OSS Distributions 
102c1dac77fSApple OSS Distributions 	producerLock = IOLockAlloc();
103*a5e72196SApple OSS Distributions 	if (!producerLock) {
104c1dac77fSApple OSS Distributions 		return false;
105*a5e72196SApple OSS Distributions 	}
106c1dac77fSApple OSS Distributions 
107c1dac77fSApple OSS Distributions 	producerIndex = consumerIndex = 0;
108c1dac77fSApple OSS Distributions 
109855239e5SApple OSS Distributions 	IOStatisticsInitializeCounter();
110855239e5SApple OSS Distributions 
111c1dac77fSApple OSS Distributions 	return true;
112c1dac77fSApple OSS Distributions }
113c1dac77fSApple OSS Distributions 
114c1dac77fSApple OSS Distributions IOCommandQueue *
115c1dac77fSApple OSS Distributions IOCommandQueue::commandQueue(OSObject *inOwner,
116c1dac77fSApple OSS Distributions     IOCommandQueueAction inAction,
117c1dac77fSApple OSS Distributions     int inSize)
118c1dac77fSApple OSS Distributions {
119c1dac77fSApple OSS Distributions 	IOCommandQueue *me = new IOCommandQueue;
120c1dac77fSApple OSS Distributions 
121c1dac77fSApple OSS Distributions 	if (me && !me->init(inOwner, inAction, inSize)) {
122c1dac77fSApple OSS Distributions 		me->free();
123*a5e72196SApple OSS Distributions 		return NULL;
124c1dac77fSApple OSS Distributions 	}
125c1dac77fSApple OSS Distributions 
126c1dac77fSApple OSS Distributions 	return me;
127c1dac77fSApple OSS Distributions }
128c1dac77fSApple OSS Distributions 
129c1dac77fSApple OSS Distributions /*[
130*a5e72196SApple OSS Distributions  *  free
131*a5e72196SApple OSS Distributions  *   - free
132*a5e72196SApple OSS Distributions  *
133*a5e72196SApple OSS Distributions  *  Mandatory free of the object independent of the current retain count.
134*a5e72196SApple OSS Distributions  *  Returns nil.
135*a5e72196SApple OSS Distributions  *  ]*/
136*a5e72196SApple OSS Distributions void
137*a5e72196SApple OSS Distributions IOCommandQueue::free()
138c1dac77fSApple OSS Distributions {
139*a5e72196SApple OSS Distributions 	if (queue) {
14014e3d835SApple OSS Distributions 		kfree(queue, size * sizeof(commandEntryT));
141*a5e72196SApple OSS Distributions 	}
142*a5e72196SApple OSS Distributions 	if (producerSema) {
143c1dac77fSApple OSS Distributions 		semaphore_destroy(kernel_task, producerSema);
144*a5e72196SApple OSS Distributions 	}
145*a5e72196SApple OSS Distributions 	if (producerLock) {
146c1dac77fSApple OSS Distributions 		IOLockFree(producerLock);
147*a5e72196SApple OSS Distributions 	}
148c1dac77fSApple OSS Distributions 
149c1dac77fSApple OSS Distributions 	super::free();
150c1dac77fSApple OSS Distributions }
151c1dac77fSApple OSS Distributions 
152c1dac77fSApple OSS Distributions #if NUM_FIELDS_IN_COMMAND != 4
153c1dac77fSApple OSS Distributions #error IOCommandQueue::checkForWork needs to be updated for new command size
154c1dac77fSApple OSS Distributions #endif
155c1dac77fSApple OSS Distributions 
156*a5e72196SApple OSS Distributions bool
157*a5e72196SApple OSS Distributions IOCommandQueue::checkForWork()
158c1dac77fSApple OSS Distributions {
159c1dac77fSApple OSS Distributions 	void        *field0, *field1, *field2, *field3;
160855239e5SApple OSS Distributions 	bool    trace = (gIOKitTrace & kIOTraceCommandGates) ? true : false;
161c1dac77fSApple OSS Distributions 
162*a5e72196SApple OSS Distributions 	if (!enabled || consumerIndex == producerIndex) {
163c1dac77fSApple OSS Distributions 		return false;
164*a5e72196SApple OSS Distributions 	}
165c1dac77fSApple OSS Distributions 
166c1dac77fSApple OSS Distributions 	{
167c1dac77fSApple OSS Distributions 		commandEntryT *q = (commandEntryT *) queue;
168c1dac77fSApple OSS Distributions 		int localIndex = consumerIndex;
169c1dac77fSApple OSS Distributions 
170c1dac77fSApple OSS Distributions 		field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1];
171c1dac77fSApple OSS Distributions 		field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3];
172c1dac77fSApple OSS Distributions 		semaphore_signal(producerSema);
173c1dac77fSApple OSS Distributions 	}
174c1dac77fSApple OSS Distributions 
175*a5e72196SApple OSS Distributions 	if (++consumerIndex >= size) {
176c1dac77fSApple OSS Distributions 		consumerIndex = 0;
177*a5e72196SApple OSS Distributions 	}
178c1dac77fSApple OSS Distributions 
179*a5e72196SApple OSS Distributions 	if (trace) {
180855239e5SApple OSS Distributions 		IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
18176e12aa3SApple OSS Distributions 		    VM_KERNEL_ADDRHIDE(action), VM_KERNEL_ADDRHIDE(owner));
182*a5e72196SApple OSS Distributions 	}
183c1dac77fSApple OSS Distributions 
184855239e5SApple OSS Distributions 	IOStatisticsActionCall();
185c1dac77fSApple OSS Distributions 	(*(IOCommandQueueAction) action)(owner, field0, field1, field2, field3);
186c1dac77fSApple OSS Distributions 
187*a5e72196SApple OSS Distributions 	if (trace) {
188855239e5SApple OSS Distributions 		IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
18976e12aa3SApple OSS Distributions 		    VM_KERNEL_ADDRHIDE(action), VM_KERNEL_ADDRHIDE(owner));
190*a5e72196SApple OSS Distributions 	}
191855239e5SApple OSS Distributions 
192*a5e72196SApple OSS Distributions 	return consumerIndex != producerIndex;
193c1dac77fSApple OSS Distributions }
194c1dac77fSApple OSS Distributions 
195c1dac77fSApple OSS Distributions /*[
196*a5e72196SApple OSS Distributions  *  enqueueSleep:command:
197*a5e72196SApple OSS Distributions  *   - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
198*a5e72196SApple OSS Distributions  *                              field0: (void *) field0 field1: (void *) field1
199*a5e72196SApple OSS Distributions  *                              field2: (void *) field2 field3: (void *) field3;
200*a5e72196SApple OSS Distributions  *
201*a5e72196SApple OSS Distributions  *  Key method that enqueues the four input fields onto the command queue
202*a5e72196SApple OSS Distributions  *  and calls signalWorkAvailable to indicate that work is available to the
203*a5e72196SApple OSS Distributions  *  consumer.  This routine is safe against multiple threaded producers.
204*a5e72196SApple OSS Distributions  *
205*a5e72196SApple OSS Distributions  *  A family of convenience functions have been provided to assist with the
206*a5e72196SApple OSS Distributions  *  enqueueing of an method selector and an integer tag.  This relies on the
207*a5e72196SApple OSS Distributions  *  IODevice rawCommandOccurred... command to forward on the requests.
208*a5e72196SApple OSS Distributions  *
209*a5e72196SApple OSS Distributions  *  See also: signalWorkAvailable, checkForWork
210*a5e72196SApple OSS Distributions  *  ]*/
211c1dac77fSApple OSS Distributions #if NUM_FIELDS_IN_COMMAND != 4
212c1dac77fSApple OSS Distributions #error IOCommandQueue::enqueueCommand needs to be updated
213c1dac77fSApple OSS Distributions #endif
214c1dac77fSApple OSS Distributions 
215c1dac77fSApple OSS Distributions kern_return_t
216c1dac77fSApple OSS Distributions IOCommandQueue::enqueueCommand(bool gotoSleep,
217c1dac77fSApple OSS Distributions     void *field0, void *field1,
218c1dac77fSApple OSS Distributions     void *field2, void *field3)
219c1dac77fSApple OSS Distributions {
220c1dac77fSApple OSS Distributions 	kern_return_t rtn = KERN_SUCCESS;
221c1dac77fSApple OSS Distributions 	int retry;
222c1dac77fSApple OSS Distributions 
223c1dac77fSApple OSS Distributions 	/* Make sure there is room in the queue before doing anything else */
224c1dac77fSApple OSS Distributions 
225c1dac77fSApple OSS Distributions 	if (gotoSleep) {
226c1dac77fSApple OSS Distributions 		retry = 0;
227*a5e72196SApple OSS Distributions 		do{
228c1dac77fSApple OSS Distributions 			rtn = semaphore_wait(producerSema);
229*a5e72196SApple OSS Distributions 		} while ((KERN_SUCCESS != rtn)
230c1dac77fSApple OSS Distributions 		    && (KERN_OPERATION_TIMED_OUT != rtn)
231c1dac77fSApple OSS Distributions 		    && (KERN_SEMAPHORE_DESTROYED != rtn)
232c1dac77fSApple OSS Distributions 		    && (KERN_TERMINATED != rtn)
233c1dac77fSApple OSS Distributions 		    && ((retry++) < 4));
234*a5e72196SApple OSS Distributions 	} else {
235c1dac77fSApple OSS Distributions 		rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO);
236*a5e72196SApple OSS Distributions 	}
237c1dac77fSApple OSS Distributions 
238*a5e72196SApple OSS Distributions 	if (KERN_SUCCESS != rtn) {
239c1dac77fSApple OSS Distributions 		return rtn;
240*a5e72196SApple OSS Distributions 	}
241c1dac77fSApple OSS Distributions 
242c1dac77fSApple OSS Distributions 	/* Block other producers */
243c1dac77fSApple OSS Distributions 	IOTakeLock(producerLock);
244c1dac77fSApple OSS Distributions 
245c1dac77fSApple OSS Distributions 	/*
246c1dac77fSApple OSS Distributions 	 * Make sure that we update the current producer entry before we
247c1dac77fSApple OSS Distributions 	 * increment the producer pointer.  This avoids a nasty race as the
248cc9a6355SApple OSS Distributions 	 * test for work is producerIndex != consumerIndex and a signal.
249c1dac77fSApple OSS Distributions 	 */
250c1dac77fSApple OSS Distributions 	{
251c1dac77fSApple OSS Distributions 		commandEntryT *q = (commandEntryT *) queue;
252c1dac77fSApple OSS Distributions 		int localIndex = producerIndex;
253c1dac77fSApple OSS Distributions 
254c1dac77fSApple OSS Distributions 		q[localIndex].f[0] = field0; q[localIndex].f[1] = field1;
255c1dac77fSApple OSS Distributions 		q[localIndex].f[2] = field2; q[localIndex].f[3] = field3;
256c1dac77fSApple OSS Distributions 	}
257*a5e72196SApple OSS Distributions 	if (++producerIndex >= size) {
258c1dac77fSApple OSS Distributions 		producerIndex = 0;
259*a5e72196SApple OSS Distributions 	}
260c1dac77fSApple OSS Distributions 
261c1dac77fSApple OSS Distributions 	/* Clear to allow other producers to go now */
262c1dac77fSApple OSS Distributions 	IOUnlock(producerLock);
263c1dac77fSApple OSS Distributions 
264c1dac77fSApple OSS Distributions 	/*
265c1dac77fSApple OSS Distributions 	 * Right we have created some new work, we had better make sure that
266c1dac77fSApple OSS Distributions 	 * we notify the work loop that it has to test producerIndex.
267c1dac77fSApple OSS Distributions 	 */
268c1dac77fSApple OSS Distributions 	signalWorkAvailable();
269c1dac77fSApple OSS Distributions 	return rtn;
270c1dac77fSApple OSS Distributions }
271c1dac77fSApple OSS Distributions 
272*a5e72196SApple OSS Distributions int
273*a5e72196SApple OSS Distributions IOCommandQueue::performAndFlush(OSObject *target,
274c1dac77fSApple OSS Distributions     IOCommandQueueAction inAction)
275c1dac77fSApple OSS Distributions {
276c1dac77fSApple OSS Distributions 	int numEntries;
277c1dac77fSApple OSS Distributions 	kern_return_t rtn;
278c1dac77fSApple OSS Distributions 
279c1dac77fSApple OSS Distributions 	// Set the defaults if necessary
280*a5e72196SApple OSS Distributions 	if (!target) {
281c1dac77fSApple OSS Distributions 		target = owner;
282*a5e72196SApple OSS Distributions 	}
283*a5e72196SApple OSS Distributions 	if (!inAction) {
284c1dac77fSApple OSS Distributions 		inAction = (IOCommandQueueAction) action;
285*a5e72196SApple OSS Distributions 	}
286c1dac77fSApple OSS Distributions 
287c1dac77fSApple OSS Distributions 	// Lock out the producers first
288c1dac77fSApple OSS Distributions 	do {
289c1dac77fSApple OSS Distributions 		rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO);
290c1dac77fSApple OSS Distributions 	} while (rtn == KERN_SUCCESS);
291c1dac77fSApple OSS Distributions 
292c1dac77fSApple OSS Distributions 	// now step over all remaining entries in the command queue
293c1dac77fSApple OSS Distributions 	for (numEntries = 0; consumerIndex != producerIndex;) {
294c1dac77fSApple OSS Distributions 		void *field0, *field1, *field2, *field3;
295c1dac77fSApple OSS Distributions 
296c1dac77fSApple OSS Distributions 		{
297c1dac77fSApple OSS Distributions 			commandEntryT *q = (commandEntryT *) queue;
298c1dac77fSApple OSS Distributions 			int localIndex = consumerIndex;
299c1dac77fSApple OSS Distributions 
300c1dac77fSApple OSS Distributions 			field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1];
301c1dac77fSApple OSS Distributions 			field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3];
302c1dac77fSApple OSS Distributions 		}
303c1dac77fSApple OSS Distributions 
304*a5e72196SApple OSS Distributions 		if (++consumerIndex >= size) {
305c1dac77fSApple OSS Distributions 			consumerIndex = 0;
306*a5e72196SApple OSS Distributions 		}
307c1dac77fSApple OSS Distributions 
308c1dac77fSApple OSS Distributions 		(*inAction)(target, field0, field1, field2, field3);
309c1dac77fSApple OSS Distributions 	}
310c1dac77fSApple OSS Distributions 
311c1dac77fSApple OSS Distributions 	// finally refill the producer semaphore to size - 1
312*a5e72196SApple OSS Distributions 	for (int i = 1; i < size; i++) {
313c1dac77fSApple OSS Distributions 		semaphore_signal(producerSema);
314*a5e72196SApple OSS Distributions 	}
315c1dac77fSApple OSS Distributions 
316c1dac77fSApple OSS Distributions 	return numEntries;
317c1dac77fSApple OSS Distributions }
3183ca3bd55SApple OSS Distributions 
3193ca3bd55SApple OSS Distributions #endif /* !defined(__LP64__) */
320