]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOPerfControl.cpp
xnu-6153.81.5.tar.gz
[apple/xnu.git] / iokit / Kernel / IOPerfControl.cpp
CommitLineData
d9a64523
A
1/*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 */
4
5#include <IOKit/perfcontrol/IOPerfControl.h>
6
7#include <stdatomic.h>
8
9#include <kern/thread_group.h>
10
11#undef super
12#define super OSObject
13OSDefineMetaClassAndStructors(IOPerfControlClient, OSObject);
14
cb323159
A
15static IOPerfControlClient::IOPerfControlClientShared *_Atomic gIOPerfControlClientShared;
16
0a7de745
A
17bool
18IOPerfControlClient::init(IOService *driver, uint64_t maxWorkCapacity)
d9a64523 19{
cb323159
A
20 // TODO: Remove this limit and implement dynamic table growth if workloads are found that exceed this
21 if (maxWorkCapacity > kMaxWorkTableNumEntries) {
22 maxWorkCapacity = kMaxWorkTableNumEntries;
23 }
24
0a7de745
A
25 if (!super::init()) {
26 return false;
27 }
28
cb323159
A
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)));
33 if (!shared) {
34 return false;
35 }
36
37 atomic_init(&shared->maxDriverIndex, 0);
38
39 shared->interface = PerfControllerInterface{
40 .version = 0,
41 .registerDevice =
0a7de745
A
42 [](IOService *device) {
43 return kIOReturnSuccess;
44 },
cb323159
A
45 .unregisterDevice =
46 [](IOService *device) {
47 return kIOReturnSuccess;
48 },
49 .workCanSubmit =
50 [](IOService *device, PerfControllerInterface::WorkState *state, WorkSubmitArgs *args) {
51 return false;
52 },
53 .workSubmit =
54 [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkSubmitArgs *args) {
55 },
56 .workBegin =
57 [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkBeginArgs *args) {
58 },
59 .workEnd =
60 [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkEndArgs *args, bool done) {
61 },
62 };
63
64 shared->interfaceLock = IOLockAlloc();
65 if (!shared->interfaceLock) {
66 goto shared_init_error;
67 }
0a7de745 68
cb323159
A
69 shared->deviceRegistrationList = OSSet::withCapacity(4);
70 if (!shared->deviceRegistrationList) {
71 goto shared_init_error;
72 }
0a7de745 73
cb323159
A
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));
79 shared = expected;
80 }
0a7de745
A
81 }
82
cb323159
A
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");
0a7de745 85
cb323159
A
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)));
91 if (!workTable) {
92 goto error;
93 }
94 bzero(workTable, workTableLength * sizeof(WorkTableEntry));
95 workTableNextIndex = 1;
0a7de745 96
cb323159
A
97 workTableLock = IOSimpleLockAlloc();
98 if (!workTableLock) {
99 goto error;
100 }
101 }
0a7de745
A
102
103 return true;
d9a64523
A
104
105error:
cb323159
A
106 if (workTable) {
107 kfree(workTable, maxWorkCapacity * sizeof(WorkTableEntry));
0a7de745
A
108 }
109 if (workTableLock) {
110 IOSimpleLockFree(workTableLock);
111 }
112 return false;
cb323159
A
113shared_init_error:
114 if (shared) {
115 if (shared->interfaceLock) {
116 IOLockFree(shared->interfaceLock);
117 }
118 if (shared->deviceRegistrationList) {
119 shared->deviceRegistrationList->release();
120 }
121 kfree(shared, sizeof(*shared));
122 shared = nullptr;
123 }
124 return false;
d9a64523
A
125}
126
0a7de745
A
127IOPerfControlClient *
128IOPerfControlClient::copyClient(IOService *driver, uint64_t maxWorkCapacity)
d9a64523 129{
cb323159
A
130 IOPerfControlClient *client = new IOPerfControlClient;
131 if (!client || !client->init(driver, maxWorkCapacity)) {
132 panic("could not create IOPerfControlClient");
0a7de745 133 }
0a7de745 134 return client;
d9a64523
A
135}
136
cb323159
A
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
142 * drivers.
143 */
144inline uint64_t
145IOPerfControlClient::tokenToGlobalUniqueToken(uint64_t token)
146{
147 return token | (static_cast<uint64_t>(driverIndex) << kWorkTableIndexBits);
148}
149
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.
156 */
0a7de745
A
157uint64_t
158IOPerfControlClient::allocateToken(thread_group *thread_group)
d9a64523 159{
0a7de745 160 uint64_t token = kIOPerfControlClientWorkUntracked;
d9a64523
A
161
162
0a7de745 163 return token;
d9a64523
A
164}
165
0a7de745
A
166void
167IOPerfControlClient::deallocateToken(uint64_t token)
d9a64523
A
168{
169}
170
0a7de745
A
171bool
172IOPerfControlClient::getEntryForToken(uint64_t token, IOPerfControlClient::WorkTableEntry &entry)
d9a64523 173{
0a7de745
A
174 if (token == kIOPerfControlClientWorkUntracked) {
175 return false;
176 }
177
cb323159 178 if (token >= workTableLength) {
0a7de745
A
179 panic("Invalid work token (%llu): index out of bounds.", token);
180 }
181
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;
d9a64523
A
186}
187
0a7de745
A
188void
189IOPerfControlClient::markEntryStarted(uint64_t token, bool started)
d9a64523 190{
0a7de745
A
191 if (token == kIOPerfControlClientWorkUntracked) {
192 return;
193 }
d9a64523 194
cb323159 195 if (token >= workTableLength) {
0a7de745
A
196 panic("Invalid work token (%llu): index out of bounds.", token);
197 }
d9a64523 198
0a7de745 199 workTable[token].started = started;
d9a64523
A
200}
201
0a7de745
A
202IOReturn
203IOPerfControlClient::registerDevice(__unused IOService *driver, IOService *device)
d9a64523 204{
0a7de745 205 IOReturn ret = kIOReturnSuccess;
d9a64523 206
cb323159 207 IOLockLock(shared->interfaceLock);
d9a64523 208
cb323159
A
209 if (shared->interface.version > 0) {
210 ret = shared->interface.registerDevice(device);
0a7de745 211 } else {
cb323159 212 shared->deviceRegistrationList->setObject(device);
0a7de745 213 }
d9a64523 214
cb323159 215 IOLockUnlock(shared->interfaceLock);
d9a64523 216
0a7de745 217 return ret;
d9a64523
A
218}
219
0a7de745
A
220void
221IOPerfControlClient::unregisterDevice(__unused IOService *driver, IOService *device)
d9a64523 222{
cb323159 223 IOLockLock(shared->interfaceLock);
d9a64523 224
cb323159
A
225 if (shared->interface.version > 0) {
226 shared->interface.unregisterDevice(device);
0a7de745 227 } else {
cb323159 228 shared->deviceRegistrationList->removeObject(device);
0a7de745 229 }
d9a64523 230
cb323159 231 IOLockUnlock(shared->interfaceLock);
d9a64523
A
232}
233
0a7de745
A
234uint64_t
235IOPerfControlClient::workSubmit(IOService *device, WorkSubmitArgs *args)
d9a64523 236{
0a7de745 237 return kIOPerfControlClientWorkUntracked;
d9a64523
A
238}
239
0a7de745
A
240uint64_t
241IOPerfControlClient::workSubmitAndBegin(IOService *device, WorkSubmitArgs *submitArgs, WorkBeginArgs *beginArgs)
d9a64523 242{
0a7de745 243 return kIOPerfControlClientWorkUntracked;
d9a64523
A
244}
245
0a7de745
A
246void
247IOPerfControlClient::workBegin(IOService *device, uint64_t token, WorkBeginArgs *args)
d9a64523
A
248{
249}
250
0a7de745
A
251void
252IOPerfControlClient::workEnd(IOService *device, uint64_t token, WorkEndArgs *args, bool done)
d9a64523
A
253{
254}
255
0a7de745
A
256IOReturn
257IOPerfControlClient::registerPerformanceController(PerfControllerInterface pci)
d9a64523 258{
0a7de745 259 IOReturn result = kIOReturnError;
d9a64523 260
cb323159 261 IOLockLock(shared->interfaceLock);
d9a64523 262
cb323159 263 if (shared->interface.version == 0 && pci.version > 0) {
0a7de745
A
264 assert(pci.registerDevice && pci.unregisterDevice && pci.workCanSubmit && pci.workSubmit && pci.workBegin && pci.workEnd);
265 result = kIOReturnSuccess;
d9a64523 266
0a7de745 267 OSObject *obj;
cb323159 268 while ((obj = shared->deviceRegistrationList->getAnyObject())) {
0a7de745
A
269 IOService *device = OSDynamicCast(IOService, obj);
270 if (device) {
271 pci.registerDevice(device);
272 }
cb323159 273 shared->deviceRegistrationList->removeObject(obj);
0a7de745 274 }
d9a64523 275
cb323159 276 shared->interface = pci;
0a7de745 277 }
d9a64523 278
cb323159 279 IOLockUnlock(shared->interfaceLock);
d9a64523 280
0a7de745 281 return result;
d9a64523 282}