]>
Commit | Line | Data |
---|---|---|
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 | |
13 | OSDefineMetaClassAndStructors(IOPerfControlClient, OSObject); | |
14 | ||
cb323159 A |
15 | static IOPerfControlClient::IOPerfControlClientShared *_Atomic gIOPerfControlClientShared; |
16 | ||
0a7de745 A |
17 | bool |
18 | IOPerfControlClient::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 | |
105 | error: | |
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 |
113 | shared_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 |
127 | IOPerfControlClient * |
128 | IOPerfControlClient::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 | */ | |
144 | inline uint64_t | |
145 | IOPerfControlClient::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 |
157 | uint64_t |
158 | IOPerfControlClient::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 |
166 | void |
167 | IOPerfControlClient::deallocateToken(uint64_t token) | |
d9a64523 A |
168 | { |
169 | } | |
170 | ||
0a7de745 A |
171 | bool |
172 | IOPerfControlClient::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 |
188 | void |
189 | IOPerfControlClient::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 |
202 | IOReturn |
203 | IOPerfControlClient::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 |
220 | void |
221 | IOPerfControlClient::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 |
234 | uint64_t |
235 | IOPerfControlClient::workSubmit(IOService *device, WorkSubmitArgs *args) | |
d9a64523 | 236 | { |
0a7de745 | 237 | return kIOPerfControlClientWorkUntracked; |
d9a64523 A |
238 | } |
239 | ||
0a7de745 A |
240 | uint64_t |
241 | IOPerfControlClient::workSubmitAndBegin(IOService *device, WorkSubmitArgs *submitArgs, WorkBeginArgs *beginArgs) | |
d9a64523 | 242 | { |
0a7de745 | 243 | return kIOPerfControlClientWorkUntracked; |
d9a64523 A |
244 | } |
245 | ||
0a7de745 A |
246 | void |
247 | IOPerfControlClient::workBegin(IOService *device, uint64_t token, WorkBeginArgs *args) | |
d9a64523 A |
248 | { |
249 | } | |
250 | ||
0a7de745 A |
251 | void |
252 | IOPerfControlClient::workEnd(IOService *device, uint64_t token, WorkEndArgs *args, bool done) | |
d9a64523 A |
253 | { |
254 | } | |
255 | ||
0a7de745 A |
256 | IOReturn |
257 | IOPerfControlClient::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 | } |