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 | |
15 | bool IOPerfControlClient::init(IOService *driver, uint64_t maxWorkCapacity) |
16 | { |
17 | if (!super::init()) |
18 | return false; |
19 | |
20 | interface = PerfControllerInterface{ |
21 | .version = 0, |
22 | .registerDevice = |
23 | [](IOService *device) { |
24 | return kIOReturnSuccess; |
25 | }, |
26 | .unregisterDevice = |
27 | [](IOService *device) { |
28 | return kIOReturnSuccess; |
29 | }, |
30 | .workCanSubmit = |
31 | [](IOService *device, PerfControllerInterface::WorkState *state, WorkSubmitArgs *args) { |
32 | return false; |
33 | }, |
34 | .workSubmit = |
35 | [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkSubmitArgs *args) { |
36 | }, |
37 | .workBegin = |
38 | [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkBeginArgs *args) { |
39 | }, |
40 | .workEnd = |
41 | [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkEndArgs *args, bool done) { |
42 | }, |
43 | }; |
44 | |
45 | interfaceLock = IOLockAlloc(); |
46 | if (!interfaceLock) |
47 | goto error; |
48 | |
49 | deviceRegistrationList = OSSet::withCapacity(4); |
50 | if (!deviceRegistrationList) |
51 | goto error; |
52 | |
53 | bzero(workTable, sizeof(workTable)); |
54 | memset(&workTable[kIOPerfControlClientWorkUntracked], ~0, sizeof(WorkTableEntry)); |
55 | workTableNextIndex = kIOPerfControlClientWorkUntracked + 1; |
56 | |
57 | workTableLock = IOSimpleLockAlloc(); |
58 | if (!workTableLock) |
59 | goto error; |
60 | |
61 | // TODO: check sum(maxWorkCapacities) < table size |
62 | |
63 | return true; |
64 | |
65 | error: |
66 | if (interfaceLock) |
67 | IOLockFree(interfaceLock); |
68 | if (deviceRegistrationList) |
69 | deviceRegistrationList->release(); |
70 | if (workTableLock) |
71 | IOSimpleLockFree(workTableLock); |
72 | return false; |
73 | } |
74 | |
75 | IOPerfControlClient *_Atomic gSharedClient = nullptr; |
76 | |
77 | IOPerfControlClient *IOPerfControlClient::copyClient(IOService *driver, uint64_t maxWorkCapacity) |
78 | { |
79 | IOPerfControlClient *client = atomic_load_explicit(&gSharedClient, memory_order_acquire); |
80 | if (client == nullptr) { |
81 | IOPerfControlClient *expected = client; |
82 | client = new IOPerfControlClient; |
83 | if (!client || !client->init(driver, maxWorkCapacity)) |
84 | panic("could not create IOPerfControlClient" ); |
85 | if (!atomic_compare_exchange_strong_explicit(&gSharedClient, &expected, client, memory_order_acq_rel, |
86 | memory_order_acquire)) { |
87 | client->release(); |
88 | client = expected; |
89 | } |
90 | } |
91 | // TODO: add maxWorkCapacity to existing client |
92 | client->retain(); |
93 | return client; |
94 | } |
95 | |
96 | uint64_t IOPerfControlClient::allocateToken(thread_group *thread_group) |
97 | { |
98 | uint64_t token = kIOPerfControlClientWorkUntracked; |
99 | |
100 | |
101 | return token; |
102 | } |
103 | |
104 | void IOPerfControlClient::deallocateToken(uint64_t token) |
105 | { |
106 | } |
107 | |
108 | bool IOPerfControlClient::getEntryForToken(uint64_t token, IOPerfControlClient::WorkTableEntry &entry) |
109 | { |
110 | if (token == kIOPerfControlClientWorkUntracked) |
111 | return false; |
112 | |
113 | if (token >= kWorkTableNumEntries) |
114 | panic("Invalid work token (%llu): index out of bounds." , token); |
115 | |
116 | entry = workTable[token]; |
117 | auto *thread_group = entry.thread_group; |
118 | assertf(thread_group, "Invalid work token: %llu" , token); |
119 | return thread_group != nullptr; |
120 | } |
121 | |
122 | void IOPerfControlClient::markEntryStarted(uint64_t token, bool started) |
123 | { |
124 | if (token == kIOPerfControlClientWorkUntracked) |
125 | return; |
126 | |
127 | if (token >= kWorkTableNumEntries) |
128 | panic("Invalid work token (%llu): index out of bounds." , token); |
129 | |
130 | workTable[token].started = started; |
131 | } |
132 | |
133 | IOReturn IOPerfControlClient::registerDevice(__unused IOService *driver, IOService *device) |
134 | { |
135 | IOReturn ret = kIOReturnSuccess; |
136 | |
137 | IOLockLock(interfaceLock); |
138 | |
139 | if (interface.version > 0) |
140 | ret = interface.registerDevice(device); |
141 | else |
142 | deviceRegistrationList->setObject(device); |
143 | |
144 | IOLockUnlock(interfaceLock); |
145 | |
146 | return ret; |
147 | } |
148 | |
149 | void IOPerfControlClient::unregisterDevice(__unused IOService *driver, IOService *device) |
150 | { |
151 | IOLockLock(interfaceLock); |
152 | |
153 | if (interface.version > 0) |
154 | interface.unregisterDevice(device); |
155 | else |
156 | deviceRegistrationList->removeObject(device); |
157 | |
158 | IOLockUnlock(interfaceLock); |
159 | } |
160 | |
161 | uint64_t IOPerfControlClient::workSubmit(IOService *device, WorkSubmitArgs *args) |
162 | { |
163 | return kIOPerfControlClientWorkUntracked; |
164 | } |
165 | |
166 | uint64_t IOPerfControlClient::workSubmitAndBegin(IOService *device, WorkSubmitArgs *submitArgs, WorkBeginArgs *beginArgs) |
167 | { |
168 | return kIOPerfControlClientWorkUntracked; |
169 | } |
170 | |
171 | void IOPerfControlClient::workBegin(IOService *device, uint64_t token, WorkBeginArgs *args) |
172 | { |
173 | } |
174 | |
175 | void IOPerfControlClient::workEnd(IOService *device, uint64_t token, WorkEndArgs *args, bool done) |
176 | { |
177 | } |
178 | |
179 | IOReturn IOPerfControlClient::registerPerformanceController(PerfControllerInterface pci) |
180 | { |
181 | IOReturn result = kIOReturnError; |
182 | |
183 | IOLockLock(interfaceLock); |
184 | |
185 | if (interface.version == 0 && pci.version > 0) { |
186 | assert(pci.registerDevice && pci.unregisterDevice && pci.workCanSubmit && pci.workSubmit && pci.workBegin && pci.workEnd); |
187 | result = kIOReturnSuccess; |
188 | |
189 | OSObject *obj; |
190 | while ((obj = deviceRegistrationList->getAnyObject())) { |
191 | IOService *device = OSDynamicCast(IOService, obj); |
192 | if (device) |
193 | pci.registerDevice(device); |
194 | deviceRegistrationList->removeObject(obj); |
195 | } |
196 | |
197 | interface = pci; |
198 | } |
199 | |
200 | IOLockUnlock(interfaceLock); |
201 | |
202 | return result; |
203 | } |
204 | |