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