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