2 * Copyright (c) 2017 Apple Inc. All rights reserved.
10 #include <IOKit/IOService.h>
11 #include <stdatomic.h>
12 #include <kern/bits.h>
13 #include <libkern/c++/OSPtr.h>
18 kIOPerfControlClientWorkUntracked
= 0,
22 * @class IOPerfControlClient : public OSObject
23 * @abstract Class which implements an interface allowing device drivers to participate in performance control.
26 class IOPerfControlClient final
: public OSObject
28 OSDeclareDefaultStructors(IOPerfControlClient
);
31 virtual bool init(IOService
*driver
, uint64_t maxWorkCapacity
);
35 * @function copyClient
36 * @abstract Return a retained reference to a client object, to be released by the driver. It may be
37 * shared with other drivers in the system.
38 * @param driver The device driver that will be using this interface.
39 * @param maxWorkCapacity The maximum number of concurrent work items supported by the device driver.
40 * @returns An instance of IOPerfControlClient.
42 static IOPerfControlClient
*copyClient(IOService
*driver
, uint64_t maxWorkCapacity
);
45 * @function registerDevice
46 * @abstract Inform the system that work will be dispatched to a device in the future.
47 * @discussion The system will do some one-time setup work associated with the device, and may block the
48 * current thread during the setup. Devices should not be passed to work workSubmit, workSubmitAndBegin,
49 * workBegin, or workEnd until they have been successfully registered. The unregistration process happens
50 * automatically when the device object is deallocated.
51 * @param device The device object. Some platforms require device to be a specific subclass of IOService.
52 * @returns kIOReturnSuccess or an IOReturn error code
54 virtual IOReturn
registerDevice(IOService
*driver
, IOService
*device
);
57 * @function unregisterDevice
58 * @abstract Inform the system that work will be no longer be dispatched to a device in the future.
59 * @discussion This call is optional as the unregistration process happens automatically when the device
60 * object is deallocated. This call may block the current thread and/or acquire locks. It should not be
61 * called until after all submitted work has been ended using workEnd.
62 * @param device The device object. Some platforms require device to be a specific subclass of IOService.
64 virtual void unregisterDevice(IOService
*driver
, IOService
*device
);
67 * @struct WorkSubmitArgs
68 * @discussion Drivers may submit additional device-specific arguments related to the submission of a work item
69 * by passing a struct with WorkSubmitArgs as its first member. Note: Drivers are responsible for publishing
70 * a header file describing these arguments.
72 struct WorkSubmitArgs
{
81 * @function workSubmit
82 * @abstract Tell the performance controller that work was submitted.
83 * @param device The device that will execute the work. Some platforms require device to be a
84 * specific subclass of IOService.
85 * @param args Optional device-specific arguments related to the submission of this work item.
86 * @returns A token representing this work item, which must be passed to workEnd when the work is finished
87 * unless the token equals kIOPerfControlClientWorkUntracked. Failure to do this will result in memory leaks
88 * and a degradation of system performance.
90 virtual uint64_t workSubmit(IOService
*device
, WorkSubmitArgs
*args
= nullptr);
93 * @struct WorkBeginArgs
94 * @discussion Drivers may submit additional device-specific arguments related to the start of a work item
95 * by passing a struct with WorkBeginArgs as its first member. Note: Drivers are responsible for publishing
96 * a header file describing these arguments.
98 struct WorkBeginArgs
{
102 uint64_t reserved
[4];
107 * @function workSubmitAndBegin
108 * @abstract Tell the performance controller that work was submitted and immediately began executing.
109 * @param device The device that is executing the work. Some platforms require device to be a
110 * specific subclass of IOService.
111 * @param submitArgs Optional device-specific arguments related to the submission of this work item.
112 * @param beginArgs Optional device-specific arguments related to the start of this work item.
113 * @returns A token representing this work item, which must be passed to workEnd when the work is finished
114 * unless the token equals kIOPerfControlClientWorkUntracked. Failure to do this will result in memory leaks
115 * and a degradation of system performance.
117 virtual uint64_t workSubmitAndBegin(IOService
*device
, WorkSubmitArgs
*submitArgs
= nullptr,
118 WorkBeginArgs
*beginArgs
= nullptr);
121 * @function workBegin
122 * @abstract Tell the performance controller that previously submitted work began executing.
123 * @param device The device that is executing the work. Some platforms require device to be a
124 * specific subclass of IOService.
125 * @param args Optional device-specific arguments related to the start of this work item.
127 virtual void workBegin(IOService
*device
, uint64_t token
, WorkBeginArgs
*args
= nullptr);
130 * @struct WorkEndArgs
131 * @discussion Drivers may submit additional device-specific arguments related to the end of a work item
132 * by passing a struct with WorkEndArgs as its first member. Note: Drivers are responsible for publishing
133 * a header file describing these arguments.
139 uint64_t reserved
[4];
145 * @abstract Tell the performance controller that previously started work finished executing.
146 * @param device The device that executed the work. Some platforms require device to be a
147 * specific subclass of IOService.
148 * @param args Optional device-specific arguments related to the end of this work item.
149 * @param done Optional Set to false if the work has not yet completed. Drivers are then responsible for
150 * calling workBegin when the work resumes and workEnd with done set to True when it has completed. A workEnd() call
151 * without a corresponding workBegin() call is a way to cancel a work item and return token to IOPerfControl.
153 virtual void workEnd(IOService
*device
, uint64_t token
, WorkEndArgs
*args
= nullptr, bool done
= true);
156 * @function copyWorkContext
157 * @abstract Return a retained reference to an opaque OSObject, to be released by the driver. This object can
158 * be used by IOPerfControl to track a work item. This may perform dynamic memory allocation.
159 * @returns A pointer to an OSObject
161 OSPtr
<OSObject
> copyWorkContext();
164 * @function workSubmitAndBeginWithContext
165 * @abstract Tell the performance controller that work was submitted and immediately began executing
166 * @param device The device that is executing the work. Some platforms require device to be a
167 * specific subclass of IOService.
168 * @param context An OSObject returned by copyWorkContext(). The context object will be used by IOPerfControl to track
170 * @param submitArgs Optional device-specific arguments related to the submission of this work item.
171 * @param beginArgs Optional device-specific arguments related to the start of this work item.
172 * @returns true if IOPerfControl is tracking this work item, else false.
173 * @note The workEndWithContext() call is optional if the corresponding workSubmitWithContext() call returned false.
175 bool workSubmitAndBeginWithContext(IOService
*device
, OSObject
*context
, WorkSubmitArgs
*submitArgs
= nullptr,
176 WorkBeginArgs
*beginArgs
= nullptr);
179 * @function workSubmitWithContext
180 * @abstract Tell the performance controller that work was submitted.
181 * @param device The device that will execute the work. Some platforms require device to be a
182 * specific subclass of IOService.
183 * @param context An OSObject returned by copyWorkContext(). The context object will be used by IOPerfControl to track
185 * @param args Optional device-specific arguments related to the submission of this work item.
186 * @returns true if IOPerfControl is tracking this work item, else false.
188 bool workSubmitWithContext(IOService
*device
, OSObject
*context
, WorkSubmitArgs
*args
= nullptr);
191 * @function workBeginWithContext
192 * @abstract Tell the performance controller that previously submitted work began executing.
193 * @param device The device that is executing the work. Some platforms require device to be a
194 * specific subclass of IOService.
195 * @param context An OSObject returned by copyWorkContext() and provided to the previous call to workSubmitWithContext().
196 * @param args Optional device-specific arguments related to the start of this work item.
197 * @note The workBeginWithContext() and workEndWithContext() calls are optional if the corresponding workSubmitWithContext() call returned false.
199 void workBeginWithContext(IOService
*device
, OSObject
*context
, WorkBeginArgs
*args
= nullptr);
202 * @function workEndWithContext
203 * @abstract Tell the performance controller that previously started work finished executing.
204 * @param device The device that executed the work. Some platforms require device to be a
205 * specific subclass of IOService.
206 * @param context An OSObject returned by copyWorkContext() and provided to the previous call to workSubmitWithContext().
207 * @param args Optional device-specific arguments related to the end of this work item.
208 * @param done Optional Set to false if the work has not yet completed. Drivers are then responsible for
209 * calling workBegin when the work resumes and workEnd with done set to True when it has completed.
210 * @note The workEndWithContext() call is optional if the corresponding workSubmitWithContext() call returned false. A workEndWithContext()
211 * call without a corresponding workBeginWithContext() call is a way to cancel a work item.
213 void workEndWithContext(IOService
*device
, OSObject
*context
, WorkEndArgs
*args
= nullptr, bool done
= true);
216 * @struct PerfControllerInterface
217 * @discussion Function pointers necessary to register a performance controller. Not for general driver use.
219 struct PerfControllerInterface
{
221 uint64_t thread_group_id
;
222 void *thread_group_data
;
224 uint32_t work_data_size
;
225 uint32_t started
: 1;
226 uint32_t reserved
: 31;
229 using RegisterDeviceFunction
= IOReturn (*)(IOService
*);
230 using WorkCanSubmitFunction
= bool (*)(IOService
*, WorkState
*, WorkSubmitArgs
*);
231 using WorkSubmitFunction
= void (*)(IOService
*, uint64_t, WorkState
*, WorkSubmitArgs
*);
232 using WorkBeginFunction
= void (*)(IOService
*, uint64_t, WorkState
*, WorkBeginArgs
*);
233 using WorkEndFunction
= void (*)(IOService
*, uint64_t, WorkState
*, WorkEndArgs
*, bool);
236 RegisterDeviceFunction registerDevice
;
237 RegisterDeviceFunction unregisterDevice
;
238 WorkCanSubmitFunction workCanSubmit
;
239 WorkSubmitFunction workSubmit
;
240 WorkBeginFunction workBegin
;
241 WorkEndFunction workEnd
;
244 struct IOPerfControlClientShared
{
245 atomic_uint_fast8_t maxDriverIndex
;
246 PerfControllerInterface interface
;
247 IOLock
*interfaceLock
;
248 OSSet
*deviceRegistrationList
;
252 * @function registerPerformanceController
253 * @abstract Register a performance controller to receive callbacks. Not for general driver use.
254 * @param interface Struct containing callback functions implemented by the performance controller.
255 * @returns kIOReturnSuccess or kIOReturnError if the interface was already registered.
257 virtual IOReturn
registerPerformanceController(PerfControllerInterface interface
);
260 struct WorkTableEntry
{
261 struct thread_group
*thread_group
;
263 uint8_t perfcontrol_data
[32];
266 static constexpr size_t kMaxWorkTableNumEntries
= 1024;
267 static constexpr size_t kWorkTableIndexBits
= 24;
268 static constexpr size_t kWorkTableMaxSize
= (1 << kWorkTableIndexBits
) - 1; // - 1 since
269 // kIOPerfControlClientWorkUntracked takes number 0
270 static constexpr size_t kWorkTableIndexMask
= (const size_t)mask(kWorkTableIndexBits
);
272 uint64_t allocateToken(thread_group
*thread_group
);
273 void deallocateToken(uint64_t token
);
274 WorkTableEntry
*getEntryForToken(uint64_t token
);
275 void markEntryStarted(uint64_t token
, bool started
);
276 inline uint64_t tokenToGlobalUniqueToken(uint64_t token
);
279 IOPerfControlClientShared
*shared
;
280 WorkTableEntry
*workTable
;
281 size_t workTableLength
;
282 size_t workTableNextIndex
;
283 IOSimpleLock
*workTableLock
;
286 #endif /* __cplusplus */
287 #endif /* KERNEL_PRIVATE */