]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOPerfControl.cpp
xnu-4903.231.4.tar.gz
[apple/xnu.git] / iokit / Kernel / IOPerfControl.cpp
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 }