]>
Commit | Line | Data |
---|---|---|
d9a64523 A |
1 | /* |
2 | * Copyright (c) 2017 Apple Inc. All rights reserved. | |
3 | */ | |
4 | ||
5 | #pragma once | |
6 | ||
7 | #ifdef KERNEL_PRIVATE | |
8 | #ifdef __cplusplus | |
9 | ||
10 | #include <IOKit/IOService.h> | |
cb323159 A |
11 | #include <stdatomic.h> |
12 | #include <kern/bits.h> | |
f427ee49 | 13 | #include <libkern/c++/OSPtr.h> |
d9a64523 A |
14 | |
15 | struct thread_group; | |
16 | ||
0a7de745 A |
17 | enum{ |
18 | kIOPerfControlClientWorkUntracked = 0, | |
d9a64523 A |
19 | }; |
20 | ||
21 | /*! | |
22 | * @class IOPerfControlClient : public OSObject | |
23 | * @abstract Class which implements an interface allowing device drivers to participate in performance control. | |
24 | * @discussion TODO | |
25 | */ | |
26 | class IOPerfControlClient final : public OSObject | |
27 | { | |
0a7de745 | 28 | OSDeclareDefaultStructors(IOPerfControlClient); |
d9a64523 A |
29 | |
30 | protected: | |
0a7de745 | 31 | virtual bool init(IOService *driver, uint64_t maxWorkCapacity); |
d9a64523 A |
32 | |
33 | public: | |
0a7de745 A |
34 | /*! |
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. | |
41 | */ | |
42 | static IOPerfControlClient *copyClient(IOService *driver, uint64_t maxWorkCapacity); | |
43 | ||
44 | /*! | |
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 | |
53 | */ | |
54 | virtual IOReturn registerDevice(IOService *driver, IOService *device); | |
55 | ||
56 | /*! | |
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. | |
63 | */ | |
64 | virtual void unregisterDevice(IOService *driver, IOService *device); | |
65 | ||
66 | /*! | |
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. | |
71 | */ | |
72 | struct WorkSubmitArgs { | |
73 | uint32_t version; | |
74 | uint32_t size; | |
75 | uint64_t submit_time; | |
76 | uint64_t reserved[4]; | |
77 | void *driver_data; | |
78 | }; | |
79 | ||
80 | /*! | |
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. | |
89 | */ | |
90 | virtual uint64_t workSubmit(IOService *device, WorkSubmitArgs *args = nullptr); | |
91 | ||
92 | /*! | |
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. | |
97 | */ | |
98 | struct WorkBeginArgs { | |
99 | uint32_t version; | |
100 | uint32_t size; | |
101 | uint64_t begin_time; | |
102 | uint64_t reserved[4]; | |
103 | void *driver_data; | |
104 | }; | |
105 | ||
106 | /*! | |
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. | |
116 | */ | |
117 | virtual uint64_t workSubmitAndBegin(IOService *device, WorkSubmitArgs *submitArgs = nullptr, | |
118 | WorkBeginArgs *beginArgs = nullptr); | |
119 | ||
120 | /*! | |
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. | |
126 | */ | |
127 | virtual void workBegin(IOService *device, uint64_t token, WorkBeginArgs *args = nullptr); | |
128 | ||
129 | /*! | |
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. | |
134 | */ | |
135 | struct WorkEndArgs { | |
136 | uint32_t version; | |
137 | uint32_t size; | |
138 | uint64_t end_time; | |
139 | uint64_t reserved[4]; | |
140 | void *driver_data; | |
141 | }; | |
142 | ||
143 | /*! | |
144 | * @function workEnd | |
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 | |
f427ee49 A |
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. | |
0a7de745 A |
152 | */ |
153 | virtual void workEnd(IOService *device, uint64_t token, WorkEndArgs *args = nullptr, bool done = true); | |
154 | ||
f427ee49 A |
155 | /*! |
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 | |
160 | */ | |
161 | OSPtr<OSObject> copyWorkContext(); | |
162 | ||
163 | /*! | |
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 | |
169 | * this work item. | |
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. | |
174 | */ | |
175 | bool workSubmitAndBeginWithContext(IOService *device, OSObject *context, WorkSubmitArgs *submitArgs = nullptr, | |
176 | WorkBeginArgs *beginArgs = nullptr); | |
177 | ||
178 | /*! | |
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 | |
184 | * this work item. | |
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. | |
187 | */ | |
188 | bool workSubmitWithContext(IOService *device, OSObject *context, WorkSubmitArgs *args = nullptr); | |
189 | ||
190 | /*! | |
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. | |
198 | */ | |
199 | void workBeginWithContext(IOService *device, OSObject *context, WorkBeginArgs *args = nullptr); | |
200 | ||
201 | /*! | |
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. | |
212 | */ | |
213 | void workEndWithContext(IOService *device, OSObject *context, WorkEndArgs *args = nullptr, bool done = true); | |
214 | ||
0a7de745 A |
215 | /*! |
216 | * @struct PerfControllerInterface | |
217 | * @discussion Function pointers necessary to register a performance controller. Not for general driver use. | |
218 | */ | |
219 | struct PerfControllerInterface { | |
220 | struct WorkState { | |
221 | uint64_t thread_group_id; | |
222 | void *thread_group_data; | |
223 | void *work_data; | |
224 | uint32_t work_data_size; | |
f427ee49 A |
225 | uint32_t started : 1; |
226 | uint32_t reserved : 31; | |
0a7de745 A |
227 | }; |
228 | ||
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); | |
234 | ||
235 | uint64_t version; | |
236 | RegisterDeviceFunction registerDevice; | |
237 | RegisterDeviceFunction unregisterDevice; | |
238 | WorkCanSubmitFunction workCanSubmit; | |
239 | WorkSubmitFunction workSubmit; | |
240 | WorkBeginFunction workBegin; | |
241 | WorkEndFunction workEnd; | |
242 | }; | |
243 | ||
cb323159 A |
244 | struct IOPerfControlClientShared { |
245 | atomic_uint_fast8_t maxDriverIndex; | |
246 | PerfControllerInterface interface; | |
247 | IOLock *interfaceLock; | |
248 | OSSet *deviceRegistrationList; | |
249 | }; | |
250 | ||
0a7de745 A |
251 | /*! |
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. | |
256 | */ | |
257 | virtual IOReturn registerPerformanceController(PerfControllerInterface interface); | |
d9a64523 A |
258 | |
259 | private: | |
0a7de745 A |
260 | struct WorkTableEntry { |
261 | struct thread_group *thread_group; | |
262 | bool started; | |
263 | uint8_t perfcontrol_data[32]; | |
264 | }; | |
265 | ||
cb323159 A |
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 | |
f427ee49 | 270 | static constexpr size_t kWorkTableIndexMask = (const size_t)mask(kWorkTableIndexBits); |
0a7de745 A |
271 | |
272 | uint64_t allocateToken(thread_group *thread_group); | |
273 | void deallocateToken(uint64_t token); | |
f427ee49 | 274 | WorkTableEntry *getEntryForToken(uint64_t token); |
0a7de745 | 275 | void markEntryStarted(uint64_t token, bool started); |
cb323159 | 276 | inline uint64_t tokenToGlobalUniqueToken(uint64_t token); |
0a7de745 | 277 | |
cb323159 A |
278 | uint8_t driverIndex; |
279 | IOPerfControlClientShared *shared; | |
280 | WorkTableEntry *workTable; | |
281 | size_t workTableLength; | |
0a7de745 A |
282 | size_t workTableNextIndex; |
283 | IOSimpleLock *workTableLock; | |
d9a64523 A |
284 | }; |
285 | ||
286 | #endif /* __cplusplus */ | |
287 | #endif /* KERNEL_PRIVATE */ |