2 * Copyright (c) 2017 Apple Inc. All rights reserved.
5 #include <IOKit/perfcontrol/IOPerfControl.h>
9 #include <kern/thread_group.h>
12 #define super OSObject
13 OSDefineMetaClassAndStructors(IOPerfControlClient
, OSObject
);
15 static IOPerfControlClient::IOPerfControlClientShared
*_Atomic gIOPerfControlClientShared
;
18 IOPerfControlClient::init(IOService
*driver
, uint64_t maxWorkCapacity
)
20 // TODO: Remove this limit and implement dynamic table growth if workloads are found that exceed this
21 if (maxWorkCapacity
> kMaxWorkTableNumEntries
) {
22 maxWorkCapacity
= kMaxWorkTableNumEntries
;
29 shared
= atomic_load_explicit(&gIOPerfControlClientShared
, memory_order_acquire
);
30 if (shared
== nullptr) {
31 IOPerfControlClient::IOPerfControlClientShared
*expected
= shared
;
32 shared
= reinterpret_cast<IOPerfControlClient::IOPerfControlClientShared
*>(kalloc(sizeof(IOPerfControlClientShared
)));
37 atomic_init(&shared
->maxDriverIndex
, 0);
39 shared
->interface
= PerfControllerInterface
{
42 [](IOService
*device
) {
43 return kIOReturnSuccess
;
46 [](IOService
*device
) {
47 return kIOReturnSuccess
;
50 [](IOService
*device
, PerfControllerInterface::WorkState
*state
, WorkSubmitArgs
*args
) {
54 [](IOService
*device
, uint64_t token
, PerfControllerInterface::WorkState
*state
, WorkSubmitArgs
*args
) {
57 [](IOService
*device
, uint64_t token
, PerfControllerInterface::WorkState
*state
, WorkBeginArgs
*args
) {
60 [](IOService
*device
, uint64_t token
, PerfControllerInterface::WorkState
*state
, WorkEndArgs
*args
, bool done
) {
64 shared
->interfaceLock
= IOLockAlloc();
65 if (!shared
->interfaceLock
) {
66 goto shared_init_error
;
69 shared
->deviceRegistrationList
= OSSet::withCapacity(4);
70 if (!shared
->deviceRegistrationList
) {
71 goto shared_init_error
;
74 if (!atomic_compare_exchange_strong_explicit(&gIOPerfControlClientShared
, &expected
, shared
, memory_order_acq_rel
,
75 memory_order_acquire
)) {
76 IOLockFree(shared
->interfaceLock
);
77 shared
->deviceRegistrationList
->release();
78 kfree(shared
, sizeof(*shared
));
83 driverIndex
= atomic_fetch_add_explicit(&shared
->maxDriverIndex
, 1, memory_order_relaxed
) + 1;
84 assertf(driverIndex
!= 0, "Overflow in driverIndex. Too many IOPerfControlClients created.\n");
86 // + 1 since index 0 is unused for kIOPerfControlClientWorkUntracked
87 workTableLength
= maxWorkCapacity
+ 1;
88 assertf(workTableLength
<= kWorkTableMaxSize
, "%zu exceeds max allowed capacity of %zu", workTableLength
, kWorkTableMaxSize
);
89 if (maxWorkCapacity
> 0) {
90 workTable
= reinterpret_cast<WorkTableEntry
*>(kalloc(workTableLength
* sizeof(WorkTableEntry
)));
94 bzero(workTable
, workTableLength
* sizeof(WorkTableEntry
));
95 workTableNextIndex
= 1;
97 workTableLock
= IOSimpleLockAlloc();
107 kfree(workTable
, maxWorkCapacity
* sizeof(WorkTableEntry
));
110 IOSimpleLockFree(workTableLock
);
115 if (shared
->interfaceLock
) {
116 IOLockFree(shared
->interfaceLock
);
118 if (shared
->deviceRegistrationList
) {
119 shared
->deviceRegistrationList
->release();
121 kfree(shared
, sizeof(*shared
));
127 IOPerfControlClient
*
128 IOPerfControlClient::copyClient(IOService
*driver
, uint64_t maxWorkCapacity
)
130 IOPerfControlClient
*client
= new IOPerfControlClient
;
131 if (!client
|| !client
->init(driver
, maxWorkCapacity
)) {
132 panic("could not create IOPerfControlClient");
137 /* Convert the per driver token into a globally unique token for the performance
138 * controller's consumption. This is achieved by setting the driver's unique
139 * index onto the high order bits. The performance controller is shared between
140 * all drivers and must track all instances separately, while each driver has
141 * its own token table, so this step is needed to avoid token collisions between
145 IOPerfControlClient::tokenToGlobalUniqueToken(uint64_t token
)
147 return token
| (static_cast<uint64_t>(driverIndex
) << kWorkTableIndexBits
);
150 /* With this implementation, tokens returned to the driver differ from tokens
151 * passed to the performance controller. This implementation has the nice
152 * property that tokens returns to the driver will aways be between 1 and
153 * the value of maxWorkCapacity passed by the driver to copyClient. The tokens
154 * the performance controller sees will match on the lower order bits and have
155 * the driver index set on the high order bits.
158 IOPerfControlClient::allocateToken(thread_group
*thread_group
)
160 uint64_t token
= kIOPerfControlClientWorkUntracked
;
167 IOPerfControlClient::deallocateToken(uint64_t token
)
172 IOPerfControlClient::getEntryForToken(uint64_t token
, IOPerfControlClient::WorkTableEntry
&entry
)
174 if (token
== kIOPerfControlClientWorkUntracked
) {
178 if (token
>= workTableLength
) {
179 panic("Invalid work token (%llu): index out of bounds.", token
);
182 entry
= workTable
[token
];
183 auto *thread_group
= entry
.thread_group
;
184 assertf(thread_group
, "Invalid work token: %llu", token
);
185 return thread_group
!= nullptr;
189 IOPerfControlClient::markEntryStarted(uint64_t token
, bool started
)
191 if (token
== kIOPerfControlClientWorkUntracked
) {
195 if (token
>= workTableLength
) {
196 panic("Invalid work token (%llu): index out of bounds.", token
);
199 workTable
[token
].started
= started
;
203 IOPerfControlClient::registerDevice(__unused IOService
*driver
, IOService
*device
)
205 IOReturn ret
= kIOReturnSuccess
;
207 IOLockLock(shared
->interfaceLock
);
209 if (shared
->interface
.version
> 0) {
210 ret
= shared
->interface
.registerDevice(device
);
212 shared
->deviceRegistrationList
->setObject(device
);
215 IOLockUnlock(shared
->interfaceLock
);
221 IOPerfControlClient::unregisterDevice(__unused IOService
*driver
, IOService
*device
)
223 IOLockLock(shared
->interfaceLock
);
225 if (shared
->interface
.version
> 0) {
226 shared
->interface
.unregisterDevice(device
);
228 shared
->deviceRegistrationList
->removeObject(device
);
231 IOLockUnlock(shared
->interfaceLock
);
235 IOPerfControlClient::workSubmit(IOService
*device
, WorkSubmitArgs
*args
)
237 return kIOPerfControlClientWorkUntracked
;
241 IOPerfControlClient::workSubmitAndBegin(IOService
*device
, WorkSubmitArgs
*submitArgs
, WorkBeginArgs
*beginArgs
)
243 return kIOPerfControlClientWorkUntracked
;
247 IOPerfControlClient::workBegin(IOService
*device
, uint64_t token
, WorkBeginArgs
*args
)
252 IOPerfControlClient::workEnd(IOService
*device
, uint64_t token
, WorkEndArgs
*args
, bool done
)
257 IOPerfControlClient::registerPerformanceController(PerfControllerInterface pci
)
259 IOReturn result
= kIOReturnError
;
261 IOLockLock(shared
->interfaceLock
);
263 if (shared
->interface
.version
== 0 && pci
.version
> 0) {
264 assert(pci
.registerDevice
&& pci
.unregisterDevice
&& pci
.workCanSubmit
&& pci
.workSubmit
&& pci
.workBegin
&& pci
.workEnd
);
265 result
= kIOReturnSuccess
;
268 while ((obj
= shared
->deviceRegistrationList
->getAnyObject())) {
269 IOService
*device
= OSDynamicCast(IOService
, obj
);
271 pci
.registerDevice(device
);
273 shared
->deviceRegistrationList
->removeObject(obj
);
276 shared
->interface
= pci
;
279 IOLockUnlock(shared
->interfaceLock
);