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