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
15bool 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
65error:
66 if (interfaceLock)
67 IOLockFree(interfaceLock);
68 if (deviceRegistrationList)
69 deviceRegistrationList->release();
70 if (workTableLock)
71 IOSimpleLockFree(workTableLock);
72 return false;
73}
74
75IOPerfControlClient *_Atomic gSharedClient = nullptr;
76
77IOPerfControlClient *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
96uint64_t IOPerfControlClient::allocateToken(thread_group *thread_group)
97{
98 uint64_t token = kIOPerfControlClientWorkUntracked;
99
100
101 return token;
102}
103
104void IOPerfControlClient::deallocateToken(uint64_t token)
105{
106}
107
108bool 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
122void 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
133IOReturn 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
149void 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
161uint64_t IOPerfControlClient::workSubmit(IOService *device, WorkSubmitArgs *args)
162{
163 return kIOPerfControlClientWorkUntracked;
164}
165
166uint64_t IOPerfControlClient::workSubmitAndBegin(IOService *device, WorkSubmitArgs *submitArgs, WorkBeginArgs *beginArgs)
167{
168 return kIOPerfControlClientWorkUntracked;
169}
170
171void IOPerfControlClient::workBegin(IOService *device, uint64_t token, WorkBeginArgs *args)
172{
173}
174
175void IOPerfControlClient::workEnd(IOService *device, uint64_t token, WorkEndArgs *args, bool done)
176{
177}
178
179IOReturn 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