1/* 2 * Copyright (c) 1998-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 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. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 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. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#if !defined(__LP64__) 30 31#include <IOKit/IOCommandQueue.h> 32#include <IOKit/IOWorkLoop.h> 33#include <IOKit/IOTimeStamp.h> 34#include <IOKit/IOKitDebug.h> 35 36#include <mach/sync_policy.h> 37 38#if IOKITSTATS 39 40#define IOStatisticsInitializeCounter() \ 41 IOStatistics::setCounterType(reserved->counter, kIOStatisticsCommandQueueCounter) 42 43#define IOStatisticsActionCall() \ 44 IOStatistics::countCommandQueueActionCall(reserved->counter) 45 46#else 47 48#define IOStatisticsInitializeCounter() 49#define IOStatisticsActionCall() 50 51#endif /* IOKITSTATS */ 52 53#define NUM_FIELDS_IN_COMMAND 4 54typedef struct commandEntryTag { 55 void *f[NUM_FIELDS_IN_COMMAND]; 56} commandEntryT; 57 58#define super IOEventSource 59 60OSDefineMetaClassAndStructors(IOCommandQueue, IOEventSource) 61 62/*[ 63Instance Methods 64 65initWithNext:owner:action:size: 66 - initWithNext: (IOEventSource *) inNext 67 owner: (id) inOwner 68 action: (SEL) inAction 69 size: (int) inSize; 70 71Primary initialiser for the IOCommandQueue class. Returns an 72IOCommandQueue object that is initialised with the next object in 73the chain and the owner and action. On return the signalWorkAvailableIMP 74has been cached for this function. 75 76If the object fails to initialise for some reason then [self free] will 77be called and nil will be returned. 78 79See also: initWithNext:owner:action:(IOEventSource) 80]*/ 81bool IOCommandQueue::init(OSObject *inOwner, 82 IOCommandQueueAction inAction, 83 int inSize) 84{ 85 if ( !super::init(inOwner, (IOEventSourceAction) inAction) ) 86 return false; 87 88 if (KERN_SUCCESS 89 != semaphore_create(kernel_task, &producerSema, SYNC_POLICY_FIFO, inSize)) 90 return false; 91 92 size = inSize + 1; /* Allocate one more entry than needed */ 93 94 queue = (void *)kalloc(size * sizeof(commandEntryT)); 95 if (!queue) 96 return false; 97 98 producerLock = IOLockAlloc(); 99 if (!producerLock) 100 return false; 101 102 producerIndex = consumerIndex = 0; 103 104 IOStatisticsInitializeCounter(); 105 106 return true; 107} 108 109IOCommandQueue * 110IOCommandQueue::commandQueue(OSObject *inOwner, 111 IOCommandQueueAction inAction, 112 int inSize) 113{ 114 IOCommandQueue *me = new IOCommandQueue; 115 116 if (me && !me->init(inOwner, inAction, inSize)) { 117 me->free(); 118 return 0; 119 } 120 121 return me; 122} 123 124/*[ 125free 126 - free 127 128Mandatory free of the object independent of the current retain count. 129Returns nil. 130]*/ 131void IOCommandQueue::free() 132{ 133 if (queue) 134 kfree(queue, size * sizeof(commandEntryT)); 135 if (producerSema) 136 semaphore_destroy(kernel_task, producerSema); 137 if (producerLock) 138 IOLockFree(producerLock); 139 140 super::free(); 141} 142 143#if NUM_FIELDS_IN_COMMAND != 4 144#error IOCommandQueue::checkForWork needs to be updated for new command size 145#endif 146 147bool IOCommandQueue::checkForWork() 148{ 149 void *field0, *field1, *field2, *field3; 150 bool trace = ( gIOKitTrace & kIOTraceCommandGates ) ? true : false; 151 152 if (!enabled || consumerIndex == producerIndex) 153 return false; 154 155 { 156 commandEntryT *q = (commandEntryT *) queue; 157 int localIndex = consumerIndex; 158 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); 162 } 163 164 if (++consumerIndex >= size) 165 consumerIndex = 0; 166 167 if (trace) 168 IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION), 169 (uintptr_t) action, (uintptr_t) owner); 170 171 IOStatisticsActionCall(); 172 (*(IOCommandQueueAction) action)(owner, field0, field1, field2, field3); 173 174 if (trace) 175 IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION), 176 (uintptr_t) action, (uintptr_t) owner); 177 178 return (consumerIndex != producerIndex); 179} 180 181/*[ 182enqueueSleep:command: 183 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep 184 field0: (void *) field0 field1: (void *) field1 185 field2: (void *) field2 field3: (void *) field3; 186 187Key method that enqueues the four input fields onto the command queue 188and calls signalWorkAvailable to indicate that work is available to the 189consumer. This routine is safe against multiple threaded producers. 190 191A family of convenience functions have been provided to assist with the 192enqueueing of an method selector and an integer tag. This relies on the 193IODevice rawCommandOccurred... command to forward on the requests. 194 195See also: signalWorkAvailable, checkForWork 196]*/ 197#if NUM_FIELDS_IN_COMMAND != 4 198#error IOCommandQueue::enqueueCommand needs to be updated 199#endif 200 201kern_return_t 202IOCommandQueue::enqueueCommand(bool gotoSleep, 203 void *field0, void *field1, 204 void *field2, void *field3) 205{ 206 kern_return_t rtn = KERN_SUCCESS; 207 int retry; 208 209 /* Make sure there is room in the queue before doing anything else */ 210 211 if (gotoSleep) { 212 retry = 0; 213 do 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) 219 && ((retry++) < 4)); 220 } else 221 rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO); 222 223 if (KERN_SUCCESS != rtn) 224 return rtn; 225 226 /* Block other producers */ 227 IOTakeLock(producerLock); 228 229 /* 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. 233 */ 234 { 235 commandEntryT *q = (commandEntryT *) queue; 236 int localIndex = producerIndex; 237 238 q[localIndex].f[0] = field0; q[localIndex].f[1] = field1; 239 q[localIndex].f[2] = field2; q[localIndex].f[3] = field3; 240 } 241 if (++producerIndex >= size) 242 producerIndex = 0; 243 244 /* Clear to allow other producers to go now */ 245 IOUnlock(producerLock); 246 247 /* 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. 250 */ 251 signalWorkAvailable(); 252 return rtn; 253} 254 255int IOCommandQueue::performAndFlush(OSObject *target, 256 IOCommandQueueAction inAction) 257{ 258 int numEntries; 259 kern_return_t rtn; 260 261 // Set the defaults if necessary 262 if (!target) 263 target = owner; 264 if (!inAction) 265 inAction = (IOCommandQueueAction) action; 266 267 // Lock out the producers first 268 do { 269 rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO); 270 } while (rtn == KERN_SUCCESS); 271 272 // now step over all remaining entries in the command queue 273 for (numEntries = 0; consumerIndex != producerIndex; ) { 274 void *field0, *field1, *field2, *field3; 275 276 { 277 commandEntryT *q = (commandEntryT *) queue; 278 int localIndex = consumerIndex; 279 280 field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1]; 281 field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3]; 282 } 283 284 if (++consumerIndex >= size) 285 consumerIndex = 0; 286 287 (*inAction)(target, field0, field1, field2, field3); 288 } 289 290 // finally refill the producer semaphore to size - 1 291 for (int i = 1; i < size; i++) 292 semaphore_signal(producerSema); 293 294 return numEntries; 295} 296 297#endif /* !defined(__LP64__) */ 298