xref: /xnu-11215/iokit/Kernel/IOPerfControl.cpp (revision 8d741a5d)
1cc9a6355SApple OSS Distributions /*
2cc9a6355SApple OSS Distributions  * Copyright (c) 2017 Apple Inc. All rights reserved.
3cc9a6355SApple OSS Distributions  */
4cc9a6355SApple OSS Distributions 
5cc9a6355SApple OSS Distributions #include <IOKit/perfcontrol/IOPerfControl.h>
6cc9a6355SApple OSS Distributions 
7cc9a6355SApple OSS Distributions #include <stdatomic.h>
8cc9a6355SApple OSS Distributions 
9cc9a6355SApple OSS Distributions #include <kern/thread_group.h>
10*8d741a5dSApple OSS Distributions #include <kern/task.h>
11*8d741a5dSApple OSS Distributions #include <sys/coalition.h>
12*8d741a5dSApple OSS Distributions #include <kern/coalition.h>
13cc9a6355SApple OSS Distributions 
14cc9a6355SApple OSS Distributions #undef super
15cc9a6355SApple OSS Distributions #define super OSObject
16cc9a6355SApple OSS Distributions OSDefineMetaClassAndStructors(IOPerfControlClient, OSObject);
17cc9a6355SApple OSS Distributions 
18a5e72196SApple OSS Distributions static IOPerfControlClient::IOPerfControlClientShared *_Atomic gIOPerfControlClientShared;
19cc9a6355SApple OSS Distributions 
20a5e72196SApple OSS Distributions bool
init(IOService * driver,uint64_t maxWorkCapacity)21a5e72196SApple OSS Distributions IOPerfControlClient::init(IOService *driver, uint64_t maxWorkCapacity)
22a5e72196SApple OSS Distributions {
23a5e72196SApple OSS Distributions 	// TODO: Remove this limit and implement dynamic table growth if workloads are found that exceed this
24a5e72196SApple OSS Distributions 	if (maxWorkCapacity > kMaxWorkTableNumEntries) {
25a5e72196SApple OSS Distributions 		maxWorkCapacity = kMaxWorkTableNumEntries;
26a5e72196SApple OSS Distributions 	}
27a5e72196SApple OSS Distributions 
28a5e72196SApple OSS Distributions 	if (!super::init()) {
29a5e72196SApple OSS Distributions 		return false;
30a5e72196SApple OSS Distributions 	}
31a5e72196SApple OSS Distributions 
32a5e72196SApple OSS Distributions 	shared = atomic_load_explicit(&gIOPerfControlClientShared, memory_order_acquire);
33a5e72196SApple OSS Distributions 	if (shared == nullptr) {
34a5e72196SApple OSS Distributions 		IOPerfControlClient::IOPerfControlClientShared *expected = shared;
35e6231be0SApple OSS Distributions 		shared = kalloc_type(IOPerfControlClientShared, Z_WAITOK);
36a5e72196SApple OSS Distributions 		if (!shared) {
37a5e72196SApple OSS Distributions 			return false;
38a5e72196SApple OSS Distributions 		}
39a5e72196SApple OSS Distributions 
40a5e72196SApple OSS Distributions 		atomic_init(&shared->maxDriverIndex, 0);
41a5e72196SApple OSS Distributions 
42a5e72196SApple OSS Distributions 		shared->interface = PerfControllerInterface{
43e6231be0SApple OSS Distributions 			.version = PERFCONTROL_INTERFACE_VERSION_NONE,
44cc9a6355SApple OSS Distributions 			.registerDevice =
45cc9a6355SApple OSS Distributions 		    [](IOService *device) {
46cc9a6355SApple OSS Distributions 			    return kIOReturnSuccess;
47cc9a6355SApple OSS Distributions 		    },
48cc9a6355SApple OSS Distributions 			.unregisterDevice =
49cc9a6355SApple OSS Distributions 			    [](IOService *device) {
50cc9a6355SApple OSS Distributions 				    return kIOReturnSuccess;
51cc9a6355SApple OSS Distributions 			    },
52cc9a6355SApple OSS Distributions 			.workCanSubmit =
53cc9a6355SApple OSS Distributions 			    [](IOService *device, PerfControllerInterface::WorkState *state, WorkSubmitArgs *args) {
54cc9a6355SApple OSS Distributions 				    return false;
55cc9a6355SApple OSS Distributions 			    },
56cc9a6355SApple OSS Distributions 			.workSubmit =
57cc9a6355SApple OSS Distributions 			    [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkSubmitArgs *args) {
58cc9a6355SApple OSS Distributions 			    },
59cc9a6355SApple OSS Distributions 			.workBegin =
60cc9a6355SApple OSS Distributions 			    [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkBeginArgs *args) {
61cc9a6355SApple OSS Distributions 			    },
62cc9a6355SApple OSS Distributions 			.workEnd =
63cc9a6355SApple OSS Distributions 			    [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkEndArgs *args, bool done) {
64cc9a6355SApple OSS Distributions 			    },
65e6231be0SApple OSS Distributions 			.workUpdate =
66e6231be0SApple OSS Distributions 			    [](IOService *device, uint64_t token, PerfControllerInterface::WorkState *state, WorkUpdateArgs *args) {
67e6231be0SApple OSS Distributions 			    },
68cc9a6355SApple OSS Distributions 		};
69cc9a6355SApple OSS Distributions 
70a5e72196SApple OSS Distributions 		shared->interfaceLock = IOLockAlloc();
71a5e72196SApple OSS Distributions 		if (!shared->interfaceLock) {
72a5e72196SApple OSS Distributions 			goto shared_init_error;
73a5e72196SApple OSS Distributions 		}
74cc9a6355SApple OSS Distributions 
75a5e72196SApple OSS Distributions 		shared->deviceRegistrationList = OSSet::withCapacity(4);
76a5e72196SApple OSS Distributions 		if (!shared->deviceRegistrationList) {
77a5e72196SApple OSS Distributions 			goto shared_init_error;
78a5e72196SApple OSS Distributions 		}
79cc9a6355SApple OSS Distributions 
80a5e72196SApple OSS Distributions 		if (!atomic_compare_exchange_strong_explicit(&gIOPerfControlClientShared, &expected, shared, memory_order_acq_rel,
81a5e72196SApple OSS Distributions 		    memory_order_acquire)) {
82a5e72196SApple OSS Distributions 			IOLockFree(shared->interfaceLock);
83a5e72196SApple OSS Distributions 			shared->deviceRegistrationList->release();
84e6231be0SApple OSS Distributions 			kfree_type(IOPerfControlClientShared, shared);
85a5e72196SApple OSS Distributions 			shared = expected;
86a5e72196SApple OSS Distributions 		}
87a5e72196SApple OSS Distributions 	}
88e6231be0SApple OSS Distributions 	workTable = NULL;
89e6231be0SApple OSS Distributions 	workTableLock = NULL;
90a5e72196SApple OSS Distributions 
91bb611c8fSApple OSS Distributions 	// Note: driverIndex is not guaranteed to be unique if maxDriverIndex wraps around. It is intended for debugging only.
92a5e72196SApple OSS Distributions 	driverIndex = atomic_fetch_add_explicit(&shared->maxDriverIndex, 1, memory_order_relaxed) + 1;
93a5e72196SApple OSS Distributions 
94a5e72196SApple OSS Distributions 	// + 1 since index 0 is unused for kIOPerfControlClientWorkUntracked
95a5e72196SApple OSS Distributions 	workTableLength = maxWorkCapacity + 1;
96a5e72196SApple OSS Distributions 	assertf(workTableLength <= kWorkTableMaxSize, "%zu exceeds max allowed capacity of %zu", workTableLength, kWorkTableMaxSize);
97a5e72196SApple OSS Distributions 	if (maxWorkCapacity > 0) {
98e6231be0SApple OSS Distributions 		workTable = kalloc_type(WorkTableEntry, workTableLength, Z_WAITOK_ZERO);
99a5e72196SApple OSS Distributions 		if (!workTable) {
100a5e72196SApple OSS Distributions 			goto error;
101a5e72196SApple OSS Distributions 		}
102a5e72196SApple OSS Distributions 		workTableNextIndex = 1;
103cc9a6355SApple OSS Distributions 
104cc9a6355SApple OSS Distributions 		workTableLock = IOSimpleLockAlloc();
105a5e72196SApple OSS Distributions 		if (!workTableLock) {
106cc9a6355SApple OSS Distributions 			goto error;
107a5e72196SApple OSS Distributions 		}
108a5e72196SApple OSS Distributions 	}
109cc9a6355SApple OSS Distributions 
110e6231be0SApple OSS Distributions 	bzero(&clientData, sizeof(clientData));
111e6231be0SApple OSS Distributions 
112cc9a6355SApple OSS Distributions 	return true;
113cc9a6355SApple OSS Distributions 
114cc9a6355SApple OSS Distributions error:
115a5e72196SApple OSS Distributions 	if (workTable) {
116e6231be0SApple OSS Distributions 		kfree_type(WorkTableEntry, workTableLength, workTable);
117e6231be0SApple OSS Distributions 		workTable = NULL;
118a5e72196SApple OSS Distributions 	}
119a5e72196SApple OSS Distributions 	if (workTableLock) {
120cc9a6355SApple OSS Distributions 		IOSimpleLockFree(workTableLock);
121e6231be0SApple OSS Distributions 		workTableLock = NULL;
122a5e72196SApple OSS Distributions 	}
123a5e72196SApple OSS Distributions 	return false;
124a5e72196SApple OSS Distributions shared_init_error:
125a5e72196SApple OSS Distributions 	if (shared) {
126a5e72196SApple OSS Distributions 		if (shared->interfaceLock) {
127a5e72196SApple OSS Distributions 			IOLockFree(shared->interfaceLock);
128a5e72196SApple OSS Distributions 		}
129a5e72196SApple OSS Distributions 		if (shared->deviceRegistrationList) {
130a5e72196SApple OSS Distributions 			shared->deviceRegistrationList->release();
131a5e72196SApple OSS Distributions 		}
132e6231be0SApple OSS Distributions 		kfree_type(IOPerfControlClientShared, shared);
133a5e72196SApple OSS Distributions 		shared = nullptr;
134a5e72196SApple OSS Distributions 	}
135cc9a6355SApple OSS Distributions 	return false;
136cc9a6355SApple OSS Distributions }
137cc9a6355SApple OSS Distributions 
138e6231be0SApple OSS Distributions void
free()139e6231be0SApple OSS Distributions IOPerfControlClient::free()
140e6231be0SApple OSS Distributions {
141e6231be0SApple OSS Distributions 	if (workTable) {
142e6231be0SApple OSS Distributions 		kfree_type(WorkTableEntry, workTableLength, workTable);
143e6231be0SApple OSS Distributions 	}
144e6231be0SApple OSS Distributions 	if (workTableLock) {
145e6231be0SApple OSS Distributions 		IOSimpleLockFree(workTableLock);
146e6231be0SApple OSS Distributions 	}
147e6231be0SApple OSS Distributions 	super::free();
148e6231be0SApple OSS Distributions }
149e6231be0SApple OSS Distributions 
150a5e72196SApple OSS Distributions IOPerfControlClient *
copyClient(IOService * driver,uint64_t maxWorkCapacity)151a5e72196SApple OSS Distributions IOPerfControlClient::copyClient(IOService *driver, uint64_t maxWorkCapacity)
152cc9a6355SApple OSS Distributions {
153a5e72196SApple OSS Distributions 	IOPerfControlClient *client = new IOPerfControlClient;
154a5e72196SApple OSS Distributions 	if (!client || !client->init(driver, maxWorkCapacity)) {
155cc9a6355SApple OSS Distributions 		panic("could not create IOPerfControlClient");
156cc9a6355SApple OSS Distributions 	}
157cc9a6355SApple OSS Distributions 	return client;
158cc9a6355SApple OSS Distributions }
159cc9a6355SApple OSS Distributions 
160a5e72196SApple OSS Distributions /* Convert the per driver token into a globally unique token for the performance
161a5e72196SApple OSS Distributions  * controller's consumption. This is achieved by setting the driver's unique
162a5e72196SApple OSS Distributions  * index onto the high order bits. The performance controller is shared between
163a5e72196SApple OSS Distributions  * all drivers and must track all instances separately, while each driver has
164a5e72196SApple OSS Distributions  * its own token table, so this step is needed to avoid token collisions between
165a5e72196SApple OSS Distributions  * drivers.
166a5e72196SApple OSS Distributions  */
167a5e72196SApple OSS Distributions inline uint64_t
tokenToGlobalUniqueToken(uint64_t token)168a5e72196SApple OSS Distributions IOPerfControlClient::tokenToGlobalUniqueToken(uint64_t token)
169a5e72196SApple OSS Distributions {
170a5e72196SApple OSS Distributions 	return token | (static_cast<uint64_t>(driverIndex) << kWorkTableIndexBits);
171a5e72196SApple OSS Distributions }
172a5e72196SApple OSS Distributions 
173*8d741a5dSApple OSS Distributions /* Accounting resources used in a work item to the containing coalition.
174*8d741a5dSApple OSS Distributions  * Contigent upon the PerfController signaling that it wants resource accounting
175*8d741a5dSApple OSS Distributions  * in the registerDevice()/registerDriverDevice calls. More device types can
176*8d741a5dSApple OSS Distributions  * be added here in the future.
177*8d741a5dSApple OSS Distributions  */
178*8d741a5dSApple OSS Distributions void
accountResources(coalition_t coal,PerfControllerInterface::PerfDeviceID device_type,PerfControllerInterface::ResourceAccounting * resources)179*8d741a5dSApple OSS Distributions IOPerfControlClient::accountResources(coalition_t coal, PerfControllerInterface::PerfDeviceID device_type, PerfControllerInterface::ResourceAccounting *resources)
180*8d741a5dSApple OSS Distributions {
181*8d741a5dSApple OSS Distributions 	switch (device_type) {
182*8d741a5dSApple OSS Distributions 	case PerfControllerInterface::PerfDeviceID::kANE:
183*8d741a5dSApple OSS Distributions 		if (coal != nullptr) {
184*8d741a5dSApple OSS Distributions 			coalition_update_ane_stats(coal, resources->mach_time_delta, resources->energy_nj_delta);
185*8d741a5dSApple OSS Distributions 		}
186*8d741a5dSApple OSS Distributions 		break;
187*8d741a5dSApple OSS Distributions 
188*8d741a5dSApple OSS Distributions 	default:
189*8d741a5dSApple OSS Distributions 		assertf(false, "Unexpected device type for IOPerfControlClient::accountResources: %llu", static_cast<uint64_t>(device_type));
190*8d741a5dSApple OSS Distributions 	}
191*8d741a5dSApple OSS Distributions }
192*8d741a5dSApple OSS Distributions 
193a5e72196SApple OSS Distributions /* With this implementation, tokens returned to the driver differ from tokens
194a5e72196SApple OSS Distributions  * passed to the performance controller. This implementation has the nice
195a5e72196SApple OSS Distributions  * property that tokens returns to the driver will aways be between 1 and
196a5e72196SApple OSS Distributions  * the value of maxWorkCapacity passed by the driver to copyClient. The tokens
197a5e72196SApple OSS Distributions  * the performance controller sees will match on the lower order bits and have
198a5e72196SApple OSS Distributions  * the driver index set on the high order bits.
199a5e72196SApple OSS Distributions  */
200a5e72196SApple OSS Distributions uint64_t
allocateToken(thread_group * thread_group)201a5e72196SApple OSS Distributions IOPerfControlClient::allocateToken(thread_group *thread_group)
202cc9a6355SApple OSS Distributions {
203cc9a6355SApple OSS Distributions 	uint64_t token = kIOPerfControlClientWorkUntracked;
204cc9a6355SApple OSS Distributions 
205bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
206bb611c8fSApple OSS Distributions 	auto s = IOSimpleLockLockDisableInterrupt(workTableLock);
207bb611c8fSApple OSS Distributions 
208bb611c8fSApple OSS Distributions 	uint64_t num_tries = 0;
209bb611c8fSApple OSS Distributions 	size_t index = workTableNextIndex;
210bb611c8fSApple OSS Distributions 	// - 1 since entry 0 is for kIOPerfControlClientWorkUntracked
211bb611c8fSApple OSS Distributions 	while (num_tries < workTableLength - 1) {
212bb611c8fSApple OSS Distributions 		if (workTable[index].thread_group == nullptr) {
213bb611c8fSApple OSS Distributions 			thread_group_retain(thread_group);
214bb611c8fSApple OSS Distributions 			workTable[index].thread_group = thread_group;
215*8d741a5dSApple OSS Distributions 			if (clientData.driverState.resource_accounting) {
216*8d741a5dSApple OSS Distributions 				auto *coalition = task_get_coalition(current_task(), COALITION_TYPE_RESOURCE);
217*8d741a5dSApple OSS Distributions 				assert(coalition != nullptr);
218*8d741a5dSApple OSS Distributions 				coalition_retain(coalition);
219*8d741a5dSApple OSS Distributions 				workTable[index].coal = coalition;
220*8d741a5dSApple OSS Distributions 			}
221bb611c8fSApple OSS Distributions 			token = index;
222bb611c8fSApple OSS Distributions 			// next integer between 1 and workTableLength - 1
223bb611c8fSApple OSS Distributions 			workTableNextIndex = (index % (workTableLength - 1)) + 1;
224bb611c8fSApple OSS Distributions 			break;
225bb611c8fSApple OSS Distributions 		}
226bb611c8fSApple OSS Distributions 		// next integer between 1 and workTableLength - 1
227bb611c8fSApple OSS Distributions 		index = (index % (workTableLength - 1)) + 1;
228bb611c8fSApple OSS Distributions 		num_tries += 1;
229bb611c8fSApple OSS Distributions 	}
230bb611c8fSApple OSS Distributions #if (DEVELOPMENT || DEBUG)
231bb611c8fSApple OSS Distributions 	if (token == kIOPerfControlClientWorkUntracked) {
232bb611c8fSApple OSS Distributions 		/* When investigating a panic here, first check that the driver is not leaking tokens.
233bb611c8fSApple OSS Distributions 		 * If the driver is not leaking tokens and maximum is less than kMaxWorkTableNumEntries,
234bb611c8fSApple OSS Distributions 		 * the driver should be modified to pass a larger value to copyClient.
235bb611c8fSApple OSS Distributions 		 * If the driver is not leaking tokens and maximum is equal to kMaxWorkTableNumEntries,
236bb611c8fSApple OSS Distributions 		 * this code will have to be modified to support dynamic table growth to support larger
237bb611c8fSApple OSS Distributions 		 * numbers of tokens.
238bb611c8fSApple OSS Distributions 		 */
239e6231be0SApple OSS Distributions 		panic("Tokens allocated for this device exceeded maximum of %zu.",
240bb611c8fSApple OSS Distributions 		    workTableLength - 1); // - 1 since entry 0 is for kIOPerfControlClientWorkUntracked
241bb611c8fSApple OSS Distributions 	}
242bb611c8fSApple OSS Distributions #endif
243bb611c8fSApple OSS Distributions 
244bb611c8fSApple OSS Distributions 	IOSimpleLockUnlockEnableInterrupt(workTableLock, s);
245bb611c8fSApple OSS Distributions #endif
246cc9a6355SApple OSS Distributions 
247cc9a6355SApple OSS Distributions 	return token;
248cc9a6355SApple OSS Distributions }
249cc9a6355SApple OSS Distributions 
250a5e72196SApple OSS Distributions void
deallocateToken(uint64_t token)251a5e72196SApple OSS Distributions IOPerfControlClient::deallocateToken(uint64_t token)
252cc9a6355SApple OSS Distributions {
253bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
254bb611c8fSApple OSS Distributions 	assertf(token != kIOPerfControlClientWorkUntracked, "Attempt to deallocate token kIOPerfControlClientWorkUntracked\n");
255bb611c8fSApple OSS Distributions 	assertf(token <= workTableLength, "Attempt to deallocate token %llu which is greater than the table size of %zu\n", token, workTableLength);
256bb611c8fSApple OSS Distributions 	auto s = IOSimpleLockLockDisableInterrupt(workTableLock);
257bb611c8fSApple OSS Distributions 
258bb611c8fSApple OSS Distributions 	auto &entry = workTable[token];
259bb611c8fSApple OSS Distributions 	auto *thread_group = entry.thread_group;
260*8d741a5dSApple OSS Distributions 	auto *coal = entry.coal;
261bb611c8fSApple OSS Distributions 	bzero(&entry, sizeof(entry));
262bb611c8fSApple OSS Distributions 	workTableNextIndex = token;
263bb611c8fSApple OSS Distributions 
264bb611c8fSApple OSS Distributions 	IOSimpleLockUnlockEnableInterrupt(workTableLock, s);
265bb611c8fSApple OSS Distributions 
266bb611c8fSApple OSS Distributions 	// This can call into the performance controller if the last reference is dropped here. Are we sure
267bb611c8fSApple OSS Distributions 	// the driver isn't holding any locks? If not, we may want to async this to another context.
268bb611c8fSApple OSS Distributions 	thread_group_release(thread_group);
269*8d741a5dSApple OSS Distributions 	if (coal != nullptr) {
270*8d741a5dSApple OSS Distributions 		coalition_release(coal);
271*8d741a5dSApple OSS Distributions 	}
272bb611c8fSApple OSS Distributions #endif
273cc9a6355SApple OSS Distributions }
274cc9a6355SApple OSS Distributions 
275bb611c8fSApple OSS Distributions IOPerfControlClient::WorkTableEntry *
getEntryForToken(uint64_t token)276bb611c8fSApple OSS Distributions IOPerfControlClient::getEntryForToken(uint64_t token)
277cc9a6355SApple OSS Distributions {
278a5e72196SApple OSS Distributions 	if (token == kIOPerfControlClientWorkUntracked) {
279bb611c8fSApple OSS Distributions 		return nullptr;
280a5e72196SApple OSS Distributions 	}
281cc9a6355SApple OSS Distributions 
282a5e72196SApple OSS Distributions 	if (token >= workTableLength) {
283cc9a6355SApple OSS Distributions 		panic("Invalid work token (%llu): index out of bounds.", token);
284a5e72196SApple OSS Distributions 	}
285cc9a6355SApple OSS Distributions 
286bb611c8fSApple OSS Distributions 	WorkTableEntry *entry = &workTable[token];
287bb611c8fSApple OSS Distributions 	assertf(entry->thread_group, "Invalid work token: %llu", token);
288bb611c8fSApple OSS Distributions 	return entry;
289cc9a6355SApple OSS Distributions }
290cc9a6355SApple OSS Distributions 
291a5e72196SApple OSS Distributions void
markEntryStarted(uint64_t token,bool started)292a5e72196SApple OSS Distributions IOPerfControlClient::markEntryStarted(uint64_t token, bool started)
293cc9a6355SApple OSS Distributions {
294a5e72196SApple OSS Distributions 	if (token == kIOPerfControlClientWorkUntracked) {
295cc9a6355SApple OSS Distributions 		return;
296a5e72196SApple OSS Distributions 	}
297cc9a6355SApple OSS Distributions 
298a5e72196SApple OSS Distributions 	if (token >= workTableLength) {
299cc9a6355SApple OSS Distributions 		panic("Invalid work token (%llu): index out of bounds.", token);
300a5e72196SApple OSS Distributions 	}
301cc9a6355SApple OSS Distributions 
302cc9a6355SApple OSS Distributions 	workTable[token].started = started;
303cc9a6355SApple OSS Distributions }
304cc9a6355SApple OSS Distributions 
305e6231be0SApple OSS Distributions #if CONFIG_THREAD_GROUPS
306e6231be0SApple OSS Distributions 
307e6231be0SApple OSS Distributions static struct thread_group *
threadGroupForDextService(IOService * device)308e6231be0SApple OSS Distributions threadGroupForDextService(IOService *device)
309e6231be0SApple OSS Distributions {
310e6231be0SApple OSS Distributions 	assert(device);
311e6231be0SApple OSS Distributions 
312e6231be0SApple OSS Distributions 	if (!device->hasUserServer()) {
313e6231be0SApple OSS Distributions 		return NULL;
314e6231be0SApple OSS Distributions 	}
315e6231be0SApple OSS Distributions 
316e6231be0SApple OSS Distributions 	// Devices associated with a dext driver, must be called from dext
317e6231be0SApple OSS Distributions 	// context to ensure that thread_group reference is valid.
318e6231be0SApple OSS Distributions 	thread_t thread = current_thread();
319e6231be0SApple OSS Distributions 	assert(get_threadtask(thread) != kernel_task);
320e6231be0SApple OSS Distributions 	struct thread_group * thread_group = thread_group_get(thread);
321e6231be0SApple OSS Distributions 	assert(thread_group != nullptr);
322e6231be0SApple OSS Distributions 	return thread_group;
323e6231be0SApple OSS Distributions }
324e6231be0SApple OSS Distributions 
325e6231be0SApple OSS Distributions #endif /* CONFIG_THREAD_GROUPS */
326e6231be0SApple OSS Distributions 
327a5e72196SApple OSS Distributions IOReturn
registerDevice(IOService * driver,IOService * device)328e6231be0SApple OSS Distributions IOPerfControlClient::registerDevice(IOService *driver, IOService *device)
329cc9a6355SApple OSS Distributions {
330cc9a6355SApple OSS Distributions 	IOReturn ret = kIOReturnSuccess;
331e6231be0SApple OSS Distributions #if CONFIG_THREAD_GROUPS
332a5e72196SApple OSS Distributions 	IOLockLock(shared->interfaceLock);
333cc9a6355SApple OSS Distributions 
334e6231be0SApple OSS Distributions 	clientData.device = device;
335e6231be0SApple OSS Distributions 
336e6231be0SApple OSS Distributions 	if (device) {
337e6231be0SApple OSS Distributions 		struct thread_group *dext_thread_group = threadGroupForDextService(device);
338e6231be0SApple OSS Distributions 		if (dext_thread_group) {
339e6231be0SApple OSS Distributions 			if (clientData.driverState.has_target_thread_group) {
340e6231be0SApple OSS Distributions 				panic("driverState has already been initialized");
341e6231be0SApple OSS Distributions 			}
342e6231be0SApple OSS Distributions 			clientData.driverState.has_target_thread_group = true;
343e6231be0SApple OSS Distributions 			clientData.driverState.target_thread_group_id = thread_group_get_id(dext_thread_group);
344e6231be0SApple OSS Distributions 			clientData.driverState.target_thread_group_data = thread_group_get_machine_data(dext_thread_group);
345e6231be0SApple OSS Distributions 
346e6231be0SApple OSS Distributions 			clientData.target_thread_group = dext_thread_group;
347e6231be0SApple OSS Distributions 			thread_group_retain(dext_thread_group);
348e6231be0SApple OSS Distributions 		}
349e6231be0SApple OSS Distributions 	}
350e6231be0SApple OSS Distributions 
351e6231be0SApple OSS Distributions 	if (shared->interface.version >= PERFCONTROL_INTERFACE_VERSION_3) {
352e6231be0SApple OSS Distributions 		ret = shared->interface.registerDriverDevice(driver, device, &clientData.driverState);
353e6231be0SApple OSS Distributions 	} else if (shared->interface.version >= PERFCONTROL_INTERFACE_VERSION_1) {
354a5e72196SApple OSS Distributions 		ret = shared->interface.registerDevice(device);
355a5e72196SApple OSS Distributions 	} else {
356e6231be0SApple OSS Distributions 		shared->deviceRegistrationList->setObject(this);
357a5e72196SApple OSS Distributions 	}
358cc9a6355SApple OSS Distributions 
359a5e72196SApple OSS Distributions 	IOLockUnlock(shared->interfaceLock);
360e6231be0SApple OSS Distributions #endif
361cc9a6355SApple OSS Distributions 	return ret;
362cc9a6355SApple OSS Distributions }
363cc9a6355SApple OSS Distributions 
364a5e72196SApple OSS Distributions void
unregisterDevice(IOService * driver,IOService * device)365e6231be0SApple OSS Distributions IOPerfControlClient::unregisterDevice(IOService *driver, IOService *device)
366cc9a6355SApple OSS Distributions {
367e6231be0SApple OSS Distributions #if CONFIG_THREAD_GROUPS
368a5e72196SApple OSS Distributions 	IOLockLock(shared->interfaceLock);
369cc9a6355SApple OSS Distributions 
370e6231be0SApple OSS Distributions 	if (shared->interface.version >= PERFCONTROL_INTERFACE_VERSION_3) {
371e6231be0SApple OSS Distributions 		shared->interface.unregisterDriverDevice(driver, device, &clientData.driverState);
372e6231be0SApple OSS Distributions 	} else if (shared->interface.version >= PERFCONTROL_INTERFACE_VERSION_1) {
373a5e72196SApple OSS Distributions 		shared->interface.unregisterDevice(device);
374a5e72196SApple OSS Distributions 	} else {
375e6231be0SApple OSS Distributions 		shared->deviceRegistrationList->removeObject(this);
376cc9a6355SApple OSS Distributions 	}
377cc9a6355SApple OSS Distributions 
378e6231be0SApple OSS Distributions 	if (clientData.driverState.has_target_thread_group) {
379e6231be0SApple OSS Distributions 		thread_group_release(clientData.target_thread_group);
380e6231be0SApple OSS Distributions 		clientData.target_thread_group = nullptr;
381e6231be0SApple OSS Distributions 
382e6231be0SApple OSS Distributions 		clientData.driverState.has_target_thread_group = false;
383e6231be0SApple OSS Distributions 		clientData.driverState.target_thread_group_id = ~0ull;
384e6231be0SApple OSS Distributions 		clientData.driverState.target_thread_group_data = nullptr;
385e6231be0SApple OSS Distributions 	}
386e6231be0SApple OSS Distributions 
387e6231be0SApple OSS Distributions 	clientData.device = nullptr;
388e6231be0SApple OSS Distributions 
389a5e72196SApple OSS Distributions 	IOLockUnlock(shared->interfaceLock);
390e6231be0SApple OSS Distributions #endif
391a5e72196SApple OSS Distributions }
392a5e72196SApple OSS Distributions 
393a5e72196SApple OSS Distributions uint64_t
workSubmit(IOService * device,WorkSubmitArgs * args)394a5e72196SApple OSS Distributions IOPerfControlClient::workSubmit(IOService *device, WorkSubmitArgs *args)
395cc9a6355SApple OSS Distributions {
396bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
397bb611c8fSApple OSS Distributions 	auto *thread_group = thread_group_get(current_thread());
398bb611c8fSApple OSS Distributions 	if (!thread_group) {
399cc9a6355SApple OSS Distributions 		return kIOPerfControlClientWorkUntracked;
400cc9a6355SApple OSS Distributions 	}
401cc9a6355SApple OSS Distributions 
402bb611c8fSApple OSS Distributions 	PerfControllerInterface::WorkState state{
403bb611c8fSApple OSS Distributions 		.thread_group_id = thread_group_get_id(thread_group),
404bb611c8fSApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(thread_group),
405bb611c8fSApple OSS Distributions 		.work_data = nullptr,
406bb611c8fSApple OSS Distributions 		.work_data_size = 0,
407bb611c8fSApple OSS Distributions 		.started = false,
408e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
409bb611c8fSApple OSS Distributions 	};
410bb611c8fSApple OSS Distributions 	if (!shared->interface.workCanSubmit(device, &state, args)) {
411bb611c8fSApple OSS Distributions 		return kIOPerfControlClientWorkUntracked;
412bb611c8fSApple OSS Distributions 	}
413bb611c8fSApple OSS Distributions 
414bb611c8fSApple OSS Distributions 	uint64_t token = allocateToken(thread_group);
415bb611c8fSApple OSS Distributions 	if (token != kIOPerfControlClientWorkUntracked) {
416bb611c8fSApple OSS Distributions 		state.work_data = &workTable[token].perfcontrol_data;
417bb611c8fSApple OSS Distributions 		state.work_data_size = sizeof(workTable[token].perfcontrol_data);
418bb611c8fSApple OSS Distributions 		shared->interface.workSubmit(device, tokenToGlobalUniqueToken(token), &state, args);
419bb611c8fSApple OSS Distributions 	}
420bb611c8fSApple OSS Distributions 	return token;
421bb611c8fSApple OSS Distributions #else
422bb611c8fSApple OSS Distributions 	return kIOPerfControlClientWorkUntracked;
423bb611c8fSApple OSS Distributions #endif
424bb611c8fSApple OSS Distributions }
425bb611c8fSApple OSS Distributions 
426a5e72196SApple OSS Distributions uint64_t
workSubmitAndBegin(IOService * device,WorkSubmitArgs * submitArgs,WorkBeginArgs * beginArgs)427a5e72196SApple OSS Distributions IOPerfControlClient::workSubmitAndBegin(IOService *device, WorkSubmitArgs *submitArgs, WorkBeginArgs *beginArgs)
428cc9a6355SApple OSS Distributions {
429bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
430bb611c8fSApple OSS Distributions 	auto *thread_group = thread_group_get(current_thread());
431bb611c8fSApple OSS Distributions 	if (!thread_group) {
432cc9a6355SApple OSS Distributions 		return kIOPerfControlClientWorkUntracked;
433cc9a6355SApple OSS Distributions 	}
434cc9a6355SApple OSS Distributions 
435bb611c8fSApple OSS Distributions 	PerfControllerInterface::WorkState state{
436bb611c8fSApple OSS Distributions 		.thread_group_id = thread_group_get_id(thread_group),
437bb611c8fSApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(thread_group),
438bb611c8fSApple OSS Distributions 		.work_data = nullptr,
439bb611c8fSApple OSS Distributions 		.work_data_size = 0,
440bb611c8fSApple OSS Distributions 		.started = false,
441e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
442bb611c8fSApple OSS Distributions 	};
443bb611c8fSApple OSS Distributions 	if (!shared->interface.workCanSubmit(device, &state, submitArgs)) {
444bb611c8fSApple OSS Distributions 		return kIOPerfControlClientWorkUntracked;
445bb611c8fSApple OSS Distributions 	}
446bb611c8fSApple OSS Distributions 
447bb611c8fSApple OSS Distributions 	uint64_t token = allocateToken(thread_group);
448bb611c8fSApple OSS Distributions 	if (token != kIOPerfControlClientWorkUntracked) {
449bb611c8fSApple OSS Distributions 		auto &entry = workTable[token];
450bb611c8fSApple OSS Distributions 		state.work_data = &entry.perfcontrol_data;
451bb611c8fSApple OSS Distributions 		state.work_data_size = sizeof(workTable[token].perfcontrol_data);
452bb611c8fSApple OSS Distributions 		shared->interface.workSubmit(device, tokenToGlobalUniqueToken(token), &state, submitArgs);
453bb611c8fSApple OSS Distributions 		state.started = true;
454bb611c8fSApple OSS Distributions 		shared->interface.workBegin(device, tokenToGlobalUniqueToken(token), &state, beginArgs);
455bb611c8fSApple OSS Distributions 		markEntryStarted(token, true);
456bb611c8fSApple OSS Distributions 	}
457bb611c8fSApple OSS Distributions 	return token;
458bb611c8fSApple OSS Distributions #else
459bb611c8fSApple OSS Distributions 	return kIOPerfControlClientWorkUntracked;
460bb611c8fSApple OSS Distributions #endif
461bb611c8fSApple OSS Distributions }
462bb611c8fSApple OSS Distributions 
463a5e72196SApple OSS Distributions void
workBegin(IOService * device,uint64_t token,WorkBeginArgs * args)464a5e72196SApple OSS Distributions IOPerfControlClient::workBegin(IOService *device, uint64_t token, WorkBeginArgs *args)
465cc9a6355SApple OSS Distributions {
466bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
467bb611c8fSApple OSS Distributions 	WorkTableEntry *entry = getEntryForToken(token);
468bb611c8fSApple OSS Distributions 	if (entry == nullptr) {
469bb611c8fSApple OSS Distributions 		return;
470bb611c8fSApple OSS Distributions 	}
471bb611c8fSApple OSS Distributions 
472bb611c8fSApple OSS Distributions 	assertf(!entry->started, "Work for token %llu was already started", token);
473bb611c8fSApple OSS Distributions 
474bb611c8fSApple OSS Distributions 	PerfControllerInterface::WorkState state{
475bb611c8fSApple OSS Distributions 		.thread_group_id = thread_group_get_id(entry->thread_group),
476bb611c8fSApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(entry->thread_group),
477bb611c8fSApple OSS Distributions 		.work_data = &entry->perfcontrol_data,
478bb611c8fSApple OSS Distributions 		.work_data_size = sizeof(entry->perfcontrol_data),
479bb611c8fSApple OSS Distributions 		.started = true,
480e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
481bb611c8fSApple OSS Distributions 	};
482bb611c8fSApple OSS Distributions 	shared->interface.workBegin(device, tokenToGlobalUniqueToken(token), &state, args);
483bb611c8fSApple OSS Distributions 	markEntryStarted(token, true);
484bb611c8fSApple OSS Distributions #endif
485cc9a6355SApple OSS Distributions }
486cc9a6355SApple OSS Distributions 
487a5e72196SApple OSS Distributions void
workEnd(IOService * device,uint64_t token,WorkEndArgs * args,bool done)488a5e72196SApple OSS Distributions IOPerfControlClient::workEnd(IOService *device, uint64_t token, WorkEndArgs *args, bool done)
489cc9a6355SApple OSS Distributions {
490bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
491bb611c8fSApple OSS Distributions 	WorkTableEntry *entry = getEntryForToken(token);
492bb611c8fSApple OSS Distributions 	if (entry == nullptr) {
493bb611c8fSApple OSS Distributions 		return;
494bb611c8fSApple OSS Distributions 	}
495bb611c8fSApple OSS Distributions 
496bb611c8fSApple OSS Distributions 	PerfControllerInterface::WorkState state{
497bb611c8fSApple OSS Distributions 		.thread_group_id = thread_group_get_id(entry->thread_group),
498bb611c8fSApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(entry->thread_group),
499bb611c8fSApple OSS Distributions 		.work_data = &entry->perfcontrol_data,
500bb611c8fSApple OSS Distributions 		.work_data_size = sizeof(entry->perfcontrol_data),
501bb611c8fSApple OSS Distributions 		.started = entry->started,
502e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
503bb611c8fSApple OSS Distributions 	};
504*8d741a5dSApple OSS Distributions 
505*8d741a5dSApple OSS Distributions 	if (shared->interface.version >= PERFCONTROL_INTERFACE_VERSION_4) {
506*8d741a5dSApple OSS Distributions 		PerfControllerInterface::ResourceAccounting resources;
507*8d741a5dSApple OSS Distributions 		shared->interface.workEndWithResources(device, tokenToGlobalUniqueToken(token), &state, args, &resources, done);
508*8d741a5dSApple OSS Distributions 		if (clientData.driverState.resource_accounting) {
509*8d741a5dSApple OSS Distributions 			accountResources(workTable[token].coal, clientData.driverState.device_type, &resources);
510*8d741a5dSApple OSS Distributions 		}
511*8d741a5dSApple OSS Distributions 	} else {
512bb611c8fSApple OSS Distributions 		shared->interface.workEnd(device, tokenToGlobalUniqueToken(token), &state, args, done);
513*8d741a5dSApple OSS Distributions 	}
514bb611c8fSApple OSS Distributions 
515bb611c8fSApple OSS Distributions 	if (done) {
516bb611c8fSApple OSS Distributions 		deallocateToken(token);
517bb611c8fSApple OSS Distributions 	} else {
518bb611c8fSApple OSS Distributions 		markEntryStarted(token, false);
519bb611c8fSApple OSS Distributions 	}
520bb611c8fSApple OSS Distributions #endif
521bb611c8fSApple OSS Distributions }
522bb611c8fSApple OSS Distributions 
523bb611c8fSApple OSS Distributions static _Atomic uint64_t unique_work_context_id = 1ull;
524bb611c8fSApple OSS Distributions 
525bb611c8fSApple OSS Distributions class IOPerfControlWorkContext : public OSObject
526bb611c8fSApple OSS Distributions {
527bb611c8fSApple OSS Distributions 	OSDeclareDefaultStructors(IOPerfControlWorkContext);
528bb611c8fSApple OSS Distributions 
529bb611c8fSApple OSS Distributions public:
530bb611c8fSApple OSS Distributions 	uint64_t id;
531bb611c8fSApple OSS Distributions 	struct thread_group *thread_group;
532*8d741a5dSApple OSS Distributions 	coalition_t coal;
533bb611c8fSApple OSS Distributions 	bool started;
534bb611c8fSApple OSS Distributions 	uint8_t perfcontrol_data[32];
535bb611c8fSApple OSS Distributions 
536bb611c8fSApple OSS Distributions 	bool init() override;
537bb611c8fSApple OSS Distributions 	void reset();
538bb611c8fSApple OSS Distributions 	void free() override;
539bb611c8fSApple OSS Distributions };
540bb611c8fSApple OSS Distributions 
541bb611c8fSApple OSS Distributions OSDefineMetaClassAndStructors(IOPerfControlWorkContext, OSObject);
542bb611c8fSApple OSS Distributions 
543bb611c8fSApple OSS Distributions bool
init()544bb611c8fSApple OSS Distributions IOPerfControlWorkContext::init()
545bb611c8fSApple OSS Distributions {
546bb611c8fSApple OSS Distributions 	if (!super::init()) {
547bb611c8fSApple OSS Distributions 		return false;
548bb611c8fSApple OSS Distributions 	}
549bb611c8fSApple OSS Distributions 	id = atomic_fetch_add_explicit(&unique_work_context_id, 1, memory_order_relaxed) + 1;
550bb611c8fSApple OSS Distributions 	reset();
551bb611c8fSApple OSS Distributions 	return true;
552bb611c8fSApple OSS Distributions }
553bb611c8fSApple OSS Distributions 
554bb611c8fSApple OSS Distributions void
reset()555bb611c8fSApple OSS Distributions IOPerfControlWorkContext::reset()
556bb611c8fSApple OSS Distributions {
557bb611c8fSApple OSS Distributions 	thread_group = nullptr;
558*8d741a5dSApple OSS Distributions 	coal = nullptr;
559bb611c8fSApple OSS Distributions 	started = false;
560bb611c8fSApple OSS Distributions 	bzero(perfcontrol_data, sizeof(perfcontrol_data));
561bb611c8fSApple OSS Distributions }
562bb611c8fSApple OSS Distributions 
563bb611c8fSApple OSS Distributions void
free()564bb611c8fSApple OSS Distributions IOPerfControlWorkContext::free()
565bb611c8fSApple OSS Distributions {
566bb611c8fSApple OSS Distributions 	assertf(thread_group == nullptr, "IOPerfControlWorkContext ID %llu being released without calling workEnd!\n", id);
567*8d741a5dSApple OSS Distributions 	assertf(coal == nullptr, "IOPerfControlWorkContext ID %llu being released without calling workEnd!\n", id);
568bb611c8fSApple OSS Distributions 	super::free();
569bb611c8fSApple OSS Distributions }
570bb611c8fSApple OSS Distributions 
571bb611c8fSApple OSS Distributions OSObject *
copyWorkContext()572bb611c8fSApple OSS Distributions IOPerfControlClient::copyWorkContext()
573bb611c8fSApple OSS Distributions {
574bb611c8fSApple OSS Distributions 	IOPerfControlWorkContext *context = new IOPerfControlWorkContext;
575bb611c8fSApple OSS Distributions 
576bb611c8fSApple OSS Distributions 	if (context == nullptr) {
577bb611c8fSApple OSS Distributions 		return nullptr;
578bb611c8fSApple OSS Distributions 	}
579bb611c8fSApple OSS Distributions 
580bb611c8fSApple OSS Distributions 	if (!context->init()) {
581bb611c8fSApple OSS Distributions 		context->free();
582bb611c8fSApple OSS Distributions 		return nullptr;
583bb611c8fSApple OSS Distributions 	}
584bb611c8fSApple OSS Distributions 
585e6231be0SApple OSS Distributions 	return context;
586bb611c8fSApple OSS Distributions }
587bb611c8fSApple OSS Distributions 
588bb611c8fSApple OSS Distributions bool
workSubmitAndBeginWithContext(IOService * device,OSObject * context,WorkSubmitArgs * submitArgs,WorkBeginArgs * beginArgs)589bb611c8fSApple OSS Distributions IOPerfControlClient::workSubmitAndBeginWithContext(IOService *device, OSObject *context, WorkSubmitArgs *submitArgs, WorkBeginArgs *beginArgs)
590bb611c8fSApple OSS Distributions {
591bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
592bb611c8fSApple OSS Distributions 
593bb611c8fSApple OSS Distributions 	if (workSubmitWithContext(device, context, submitArgs) == false) {
594bb611c8fSApple OSS Distributions 		return false;
595bb611c8fSApple OSS Distributions 	}
596bb611c8fSApple OSS Distributions 
597bb611c8fSApple OSS Distributions 	IOPerfControlWorkContext *work_context = OSDynamicCast(IOPerfControlWorkContext, context);
598bb611c8fSApple OSS Distributions 
599bb611c8fSApple OSS Distributions 	PerfControllerInterface::WorkState state{
600bb611c8fSApple OSS Distributions 		.thread_group_id = thread_group_get_id(work_context->thread_group),
601bb611c8fSApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(work_context->thread_group),
602bb611c8fSApple OSS Distributions 		.work_data = &work_context->perfcontrol_data,
603bb611c8fSApple OSS Distributions 		.work_data_size = sizeof(work_context->perfcontrol_data),
604bb611c8fSApple OSS Distributions 		.started = true,
605e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
606bb611c8fSApple OSS Distributions 	};
607bb611c8fSApple OSS Distributions 
608bb611c8fSApple OSS Distributions 	shared->interface.workBegin(device, work_context->id, &state, beginArgs);
609bb611c8fSApple OSS Distributions 
610bb611c8fSApple OSS Distributions 	work_context->started = true;
611bb611c8fSApple OSS Distributions 
612bb611c8fSApple OSS Distributions 	return true;
613bb611c8fSApple OSS Distributions #else
614bb611c8fSApple OSS Distributions 	return false;
615bb611c8fSApple OSS Distributions #endif
616bb611c8fSApple OSS Distributions }
617bb611c8fSApple OSS Distributions 
618bb611c8fSApple OSS Distributions bool
workSubmitWithContext(IOService * device,OSObject * context,WorkSubmitArgs * args)619bb611c8fSApple OSS Distributions IOPerfControlClient::workSubmitWithContext(IOService *device, OSObject *context, WorkSubmitArgs *args)
620bb611c8fSApple OSS Distributions {
621bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
622bb611c8fSApple OSS Distributions 	IOPerfControlWorkContext *work_context = OSDynamicCast(IOPerfControlWorkContext, context);
623bb611c8fSApple OSS Distributions 
624bb611c8fSApple OSS Distributions 	if (work_context == nullptr) {
625bb611c8fSApple OSS Distributions 		return false;
626bb611c8fSApple OSS Distributions 	}
627bb611c8fSApple OSS Distributions 
628bb611c8fSApple OSS Distributions 	auto *thread_group = thread_group_get(current_thread());
629bb611c8fSApple OSS Distributions 	assert(thread_group != nullptr);
630bb611c8fSApple OSS Distributions 
631bb611c8fSApple OSS Distributions 	assertf(!work_context->started, "IOPerfControlWorkContext ID %llu was already started", work_context->id);
632bb611c8fSApple OSS Distributions 	assertf(work_context->thread_group == nullptr, "IOPerfControlWorkContext ID %llu has already taken a refcount on TG 0x%p \n", work_context->id, (void *)(work_context->thread_group));
633bb611c8fSApple OSS Distributions 
634bb611c8fSApple OSS Distributions 	PerfControllerInterface::WorkState state{
635bb611c8fSApple OSS Distributions 		.thread_group_id = thread_group_get_id(thread_group),
636bb611c8fSApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(thread_group),
637bb611c8fSApple OSS Distributions 		.work_data = nullptr,
638bb611c8fSApple OSS Distributions 		.work_data_size = 0,
639bb611c8fSApple OSS Distributions 		.started = false,
640e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
641bb611c8fSApple OSS Distributions 	};
642bb611c8fSApple OSS Distributions 	if (!shared->interface.workCanSubmit(device, &state, args)) {
643bb611c8fSApple OSS Distributions 		return false;
644bb611c8fSApple OSS Distributions 	}
645bb611c8fSApple OSS Distributions 
646bb611c8fSApple OSS Distributions 	work_context->thread_group = thread_group_retain(thread_group);
647*8d741a5dSApple OSS Distributions 	if (clientData.driverState.resource_accounting) {
648*8d741a5dSApple OSS Distributions 		auto *coalition = task_get_coalition(current_task(), COALITION_TYPE_RESOURCE);
649*8d741a5dSApple OSS Distributions 		assert(coalition != nullptr);
650*8d741a5dSApple OSS Distributions 		work_context->coal = coalition;
651*8d741a5dSApple OSS Distributions 		coalition_retain(coalition);
652*8d741a5dSApple OSS Distributions 	}
653bb611c8fSApple OSS Distributions 
654bb611c8fSApple OSS Distributions 	state.work_data = &work_context->perfcontrol_data;
655bb611c8fSApple OSS Distributions 	state.work_data_size = sizeof(work_context->perfcontrol_data);
656bb611c8fSApple OSS Distributions 
657bb611c8fSApple OSS Distributions 	shared->interface.workSubmit(device, work_context->id, &state, args);
658bb611c8fSApple OSS Distributions 
659bb611c8fSApple OSS Distributions 	return true;
660bb611c8fSApple OSS Distributions #else
661bb611c8fSApple OSS Distributions 	return false;
662bb611c8fSApple OSS Distributions #endif
663bb611c8fSApple OSS Distributions }
664bb611c8fSApple OSS Distributions 
665bb611c8fSApple OSS Distributions void
workUpdateWithContext(IOService * device,OSObject * context,WorkUpdateArgs * args)666e6231be0SApple OSS Distributions IOPerfControlClient::workUpdateWithContext(IOService *device, OSObject *context, WorkUpdateArgs *args)
667e6231be0SApple OSS Distributions {
668e6231be0SApple OSS Distributions #if CONFIG_THREAD_GROUPS
669e6231be0SApple OSS Distributions 	IOPerfControlWorkContext *work_context = OSDynamicCast(IOPerfControlWorkContext, context);
670e6231be0SApple OSS Distributions 
671e6231be0SApple OSS Distributions 	if (work_context == nullptr) {
672e6231be0SApple OSS Distributions 		return;
673e6231be0SApple OSS Distributions 	}
674e6231be0SApple OSS Distributions 
675e6231be0SApple OSS Distributions 	if (work_context->thread_group == nullptr) {
676e6231be0SApple OSS Distributions 		// This Work Context has not taken a refcount on a TG
677e6231be0SApple OSS Distributions 		return;
678e6231be0SApple OSS Distributions 	}
679e6231be0SApple OSS Distributions 
680e6231be0SApple OSS Distributions 	PerfControllerInterface::WorkState state{
681e6231be0SApple OSS Distributions 		.thread_group_id = thread_group_get_id(work_context->thread_group),
682e6231be0SApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(work_context->thread_group),
683e6231be0SApple OSS Distributions 		.work_data = &work_context->perfcontrol_data,
684e6231be0SApple OSS Distributions 		.work_data_size = sizeof(work_context->perfcontrol_data),
685e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
686e6231be0SApple OSS Distributions 	};
687e6231be0SApple OSS Distributions 	shared->interface.workUpdate(device, work_context->id, &state, args);
688e6231be0SApple OSS Distributions #endif
689e6231be0SApple OSS Distributions }
690e6231be0SApple OSS Distributions 
691e6231be0SApple OSS Distributions void
workBeginWithContext(IOService * device,OSObject * context,WorkBeginArgs * args)692bb611c8fSApple OSS Distributions IOPerfControlClient::workBeginWithContext(IOService *device, OSObject *context, WorkBeginArgs *args)
693bb611c8fSApple OSS Distributions {
694bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
695bb611c8fSApple OSS Distributions 	IOPerfControlWorkContext *work_context = OSDynamicCast(IOPerfControlWorkContext, context);
696bb611c8fSApple OSS Distributions 
697bb611c8fSApple OSS Distributions 	if (work_context == nullptr) {
698bb611c8fSApple OSS Distributions 		return;
699bb611c8fSApple OSS Distributions 	}
700bb611c8fSApple OSS Distributions 
701bb611c8fSApple OSS Distributions 	if (work_context->thread_group == nullptr) {
702bb611c8fSApple OSS Distributions 		// This Work Context has not taken a refcount on a TG
703bb611c8fSApple OSS Distributions 		return;
704bb611c8fSApple OSS Distributions 	}
705bb611c8fSApple OSS Distributions 
706bb611c8fSApple OSS Distributions 	assertf(!work_context->started, "IOPerfControlWorkContext %llu was already started", work_context->id);
707bb611c8fSApple OSS Distributions 
708bb611c8fSApple OSS Distributions 	PerfControllerInterface::WorkState state{
709bb611c8fSApple OSS Distributions 		.thread_group_id = thread_group_get_id(work_context->thread_group),
710bb611c8fSApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(work_context->thread_group),
711bb611c8fSApple OSS Distributions 		.work_data = &work_context->perfcontrol_data,
712bb611c8fSApple OSS Distributions 		.work_data_size = sizeof(work_context->perfcontrol_data),
713bb611c8fSApple OSS Distributions 		.started = true,
714e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
715bb611c8fSApple OSS Distributions 	};
716bb611c8fSApple OSS Distributions 	shared->interface.workBegin(device, work_context->id, &state, args);
717bb611c8fSApple OSS Distributions 
718bb611c8fSApple OSS Distributions 	work_context->started = true;
719bb611c8fSApple OSS Distributions #endif
720bb611c8fSApple OSS Distributions }
721bb611c8fSApple OSS Distributions 
722bb611c8fSApple OSS Distributions void
workEndWithContext(IOService * device,OSObject * context,WorkEndArgs * args,bool done)723bb611c8fSApple OSS Distributions IOPerfControlClient::workEndWithContext(IOService *device, OSObject *context, WorkEndArgs *args, bool done)
724bb611c8fSApple OSS Distributions {
725bb611c8fSApple OSS Distributions #if CONFIG_THREAD_GROUPS
726bb611c8fSApple OSS Distributions 	IOPerfControlWorkContext *work_context = OSDynamicCast(IOPerfControlWorkContext, context);
727bb611c8fSApple OSS Distributions 
728bb611c8fSApple OSS Distributions 	if (work_context == nullptr) {
729bb611c8fSApple OSS Distributions 		return;
730bb611c8fSApple OSS Distributions 	}
731bb611c8fSApple OSS Distributions 
732bb611c8fSApple OSS Distributions 	if (work_context->thread_group == nullptr) {
733bb611c8fSApple OSS Distributions 		return;
734bb611c8fSApple OSS Distributions 	}
735bb611c8fSApple OSS Distributions 
736bb611c8fSApple OSS Distributions 	PerfControllerInterface::WorkState state{
737bb611c8fSApple OSS Distributions 		.thread_group_id = thread_group_get_id(work_context->thread_group),
738bb611c8fSApple OSS Distributions 		.thread_group_data = thread_group_get_machine_data(work_context->thread_group),
739bb611c8fSApple OSS Distributions 		.work_data = &work_context->perfcontrol_data,
740bb611c8fSApple OSS Distributions 		.work_data_size = sizeof(work_context->perfcontrol_data),
741bb611c8fSApple OSS Distributions 		.started = work_context->started,
742e6231be0SApple OSS Distributions 		.driver_state = &clientData.driverState
743bb611c8fSApple OSS Distributions 	};
744bb611c8fSApple OSS Distributions 
745*8d741a5dSApple OSS Distributions 	if (shared->interface.version >= PERFCONTROL_INTERFACE_VERSION_4) {
746*8d741a5dSApple OSS Distributions 		PerfControllerInterface::ResourceAccounting resources;
747*8d741a5dSApple OSS Distributions 		shared->interface.workEndWithResources(device, work_context->id, &state, args, &resources, done);
748*8d741a5dSApple OSS Distributions 		if (clientData.driverState.resource_accounting) {
749*8d741a5dSApple OSS Distributions 			accountResources(work_context->coal, clientData.driverState.device_type, &resources);
750*8d741a5dSApple OSS Distributions 		}
751*8d741a5dSApple OSS Distributions 	} else {
752bb611c8fSApple OSS Distributions 		shared->interface.workEnd(device, work_context->id, &state, args, done);
753*8d741a5dSApple OSS Distributions 	}
754bb611c8fSApple OSS Distributions 
755bb611c8fSApple OSS Distributions 	if (done) {
756bb611c8fSApple OSS Distributions 		thread_group_release(work_context->thread_group);
757*8d741a5dSApple OSS Distributions 		if (work_context->coal != nullptr) {
758*8d741a5dSApple OSS Distributions 			coalition_release(work_context->coal);
759*8d741a5dSApple OSS Distributions 		}
760bb611c8fSApple OSS Distributions 		work_context->reset();
761bb611c8fSApple OSS Distributions 	} else {
762bb611c8fSApple OSS Distributions 		work_context->started = false;
763bb611c8fSApple OSS Distributions 	}
764bb611c8fSApple OSS Distributions 
765bb611c8fSApple OSS Distributions 	return;
766bb611c8fSApple OSS Distributions #else
767bb611c8fSApple OSS Distributions 	return;
768bb611c8fSApple OSS Distributions #endif
769cc9a6355SApple OSS Distributions }
770cc9a6355SApple OSS Distributions 
771a5e72196SApple OSS Distributions IOReturn
registerPerformanceController(PerfControllerInterface * pci)772e6231be0SApple OSS Distributions IOPerfControlClient::registerPerformanceController(PerfControllerInterface *pci)
773cc9a6355SApple OSS Distributions {
774cc9a6355SApple OSS Distributions 	IOReturn result = kIOReturnError;
775cc9a6355SApple OSS Distributions 
776a5e72196SApple OSS Distributions 	IOLockLock(shared->interfaceLock);
777cc9a6355SApple OSS Distributions 
778e6231be0SApple OSS Distributions 	if (shared->interface.version == PERFCONTROL_INTERFACE_VERSION_NONE) {
779e6231be0SApple OSS Distributions 		shared->interface.version = pci->version;
780e6231be0SApple OSS Distributions 
781e6231be0SApple OSS Distributions 		if (pci->version >= PERFCONTROL_INTERFACE_VERSION_1) {
782e6231be0SApple OSS Distributions 			assert(pci->registerDevice && pci->unregisterDevice && pci->workCanSubmit && pci->workSubmit && pci->workBegin && pci->workEnd);
783e6231be0SApple OSS Distributions 			shared->interface.registerDevice = pci->registerDevice;
784e6231be0SApple OSS Distributions 			shared->interface.unregisterDevice = pci->unregisterDevice;
785e6231be0SApple OSS Distributions 			shared->interface.workCanSubmit = pci->workCanSubmit;
786e6231be0SApple OSS Distributions 			shared->interface.workSubmit = pci->workSubmit;
787e6231be0SApple OSS Distributions 			shared->interface.workBegin = pci->workBegin;
788e6231be0SApple OSS Distributions 			shared->interface.workEnd = pci->workEnd;
789e6231be0SApple OSS Distributions 		}
790e6231be0SApple OSS Distributions 
791e6231be0SApple OSS Distributions 		if (pci->version >= PERFCONTROL_INTERFACE_VERSION_2) {
792e6231be0SApple OSS Distributions 			if (pci->workUpdate != nullptr) {
793e6231be0SApple OSS Distributions 				shared->interface.workUpdate = pci->workUpdate;
794e6231be0SApple OSS Distributions 			}
795e6231be0SApple OSS Distributions 		}
796e6231be0SApple OSS Distributions 
797e6231be0SApple OSS Distributions 		if (pci->version >= PERFCONTROL_INTERFACE_VERSION_3) {
798e6231be0SApple OSS Distributions 			assert(pci->registerDriverDevice && pci->unregisterDriverDevice);
799e6231be0SApple OSS Distributions 			shared->interface.registerDriverDevice = pci->registerDriverDevice;
800e6231be0SApple OSS Distributions 			shared->interface.unregisterDriverDevice = pci->unregisterDriverDevice;
801e6231be0SApple OSS Distributions 		}
802e6231be0SApple OSS Distributions 
803*8d741a5dSApple OSS Distributions 		if (pci->version >= PERFCONTROL_INTERFACE_VERSION_4) {
804*8d741a5dSApple OSS Distributions 			assert(pci->workEndWithResources);
805*8d741a5dSApple OSS Distributions 			shared->interface.workEndWithResources = pci->workEndWithResources;
806*8d741a5dSApple OSS Distributions 		}
807*8d741a5dSApple OSS Distributions 
808cc9a6355SApple OSS Distributions 		result = kIOReturnSuccess;
809cc9a6355SApple OSS Distributions 
810cc9a6355SApple OSS Distributions 		OSObject *obj;
811a5e72196SApple OSS Distributions 		while ((obj = shared->deviceRegistrationList->getAnyObject())) {
812e6231be0SApple OSS Distributions 			IOPerfControlClient *client = OSDynamicCast(IOPerfControlClient, obj);
813e6231be0SApple OSS Distributions 			IOPerfControlClientData *clientData = client->getClientData();
814e6231be0SApple OSS Distributions 			if (clientData && clientData->device) {
815e6231be0SApple OSS Distributions 				if (pci->version >= PERFCONTROL_INTERFACE_VERSION_3) {
816e6231be0SApple OSS Distributions 					pci->registerDriverDevice(clientData->device->getProvider(), clientData->device, &(clientData->driverState));
817e6231be0SApple OSS Distributions 				} else if (pci->version >= PERFCONTROL_INTERFACE_VERSION_1) {
818e6231be0SApple OSS Distributions 					pci->registerDevice(clientData->device);
819e6231be0SApple OSS Distributions 				}
820a5e72196SApple OSS Distributions 			}
821a5e72196SApple OSS Distributions 			shared->deviceRegistrationList->removeObject(obj);
822cc9a6355SApple OSS Distributions 		}
823cc9a6355SApple OSS Distributions 	}
824cc9a6355SApple OSS Distributions 
825a5e72196SApple OSS Distributions 	IOLockUnlock(shared->interfaceLock);
826cc9a6355SApple OSS Distributions 
827cc9a6355SApple OSS Distributions 	return result;
828cc9a6355SApple OSS Distributions }
829