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> |
11 | |
12 | struct thread_group; |
13 | |
14 | enum |
15 | { |
16 | kIOPerfControlClientWorkUntracked = 0, |
17 | }; |
18 | |
19 | /*! |
20 | * @class IOPerfControlClient : public OSObject |
21 | * @abstract Class which implements an interface allowing device drivers to participate in performance control. |
22 | * @discussion TODO |
23 | */ |
24 | class IOPerfControlClient final : public OSObject |
25 | { |
26 | OSDeclareDefaultStructors(IOPerfControlClient); |
27 | |
28 | protected: |
29 | virtual bool init(IOService *driver, uint64_t maxWorkCapacity); |
30 | |
31 | public: |
32 | /*! |
33 | * @function copyClient |
34 | * @abstract Return a retained reference to a client object, to be released by the driver. It may be |
35 | * shared with other drivers in the system. |
36 | * @param driver The device driver that will be using this interface. |
37 | * @param maxWorkCapacity The maximum number of concurrent work items supported by the device driver. |
38 | * @returns An instance of IOPerfControlClient. |
39 | */ |
40 | static IOPerfControlClient *copyClient(IOService *driver, uint64_t maxWorkCapacity); |
41 | |
42 | /*! |
43 | * @function registerDevice |
44 | * @abstract Inform the system that work will be dispatched to a device in the future. |
45 | * @discussion The system will do some one-time setup work associated with the device, and may block the |
46 | * current thread during the setup. Devices should not be passed to work workSubmit, workSubmitAndBegin, |
47 | * workBegin, or workEnd until they have been successfully registered. The unregistration process happens |
48 | * automatically when the device object is deallocated. |
49 | * @param device The device object. Some platforms require device to be a specific subclass of IOService. |
50 | * @returns kIOReturnSuccess or an IOReturn error code |
51 | */ |
52 | virtual IOReturn registerDevice(IOService *driver, IOService *device); |
53 | |
54 | /*! |
55 | * @function unregisterDevice |
56 | * @abstract Inform the system that work will be no longer be dispatched to a device in the future. |
57 | * @discussion This call is optional as the unregistration process happens automatically when the device |
58 | * object is deallocated. This call may block the current thread and/or acquire locks. It should not be |
59 | * called until after all submitted work has been ended using workEnd. |
60 | * @param device The device object. Some platforms require device to be a specific subclass of IOService. |
61 | */ |
62 | virtual void unregisterDevice(IOService *driver, IOService *device); |
63 | |
64 | /*! |
65 | * @struct WorkSubmitArgs |
66 | * @discussion Drivers may submit additional device-specific arguments related to the submission of a work item |
67 | * by passing a struct with WorkSubmitArgs as its first member. Note: Drivers are responsible for publishing |
68 | * a header file describing these arguments. |
69 | */ |
70 | struct WorkSubmitArgs |
71 | { |
72 | uint32_t version; |
73 | uint32_t size; |
74 | uint64_t submit_time; |
75 | uint64_t reserved[4]; |
76 | void *driver_data; |
77 | }; |
78 | |
79 | /*! |
80 | * @function workSubmit |
81 | * @abstract Tell the performance controller that work was submitted. |
82 | * @param device The device that will execute the work. Some platforms require device to be a |
83 | * specific subclass of IOService. |
84 | * @param args Optional device-specific arguments related to the submission of this work item. |
85 | * @returns A token representing this work item, which must be passed to workEnd when the work is finished |
86 | * unless the token equals kIOPerfControlClientWorkUntracked. Failure to do this will result in memory leaks |
87 | * and a degradation of system performance. |
88 | */ |
89 | virtual uint64_t workSubmit(IOService *device, WorkSubmitArgs *args = nullptr); |
90 | |
91 | /*! |
92 | * @struct WorkBeginArgs |
93 | * @discussion Drivers may submit additional device-specific arguments related to the start of a work item |
94 | * by passing a struct with WorkBeginArgs as its first member. Note: Drivers are responsible for publishing |
95 | * a header file describing these arguments. |
96 | */ |
97 | struct WorkBeginArgs |
98 | { |
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 | { |
137 | uint32_t version; |
138 | uint32_t size; |
139 | uint64_t end_time; |
140 | uint64_t reserved[4]; |
141 | void *driver_data; |
142 | }; |
143 | |
144 | /*! |
145 | * @function workEnd |
146 | * @abstract Tell the performance controller that previously started work finished executing. |
147 | * @param device The device that executed the work. Some platforms require device to be a |
148 | * specific subclass of IOService. |
149 | * @param args Optional device-specific arguments related to the end of this work item. |
150 | * @param done Optional Set to false if the work has not yet completed. Drivers are then responsible for |
151 | * calling workBegin when the work resumes and workEnd with done set to True when it has completed. |
152 | */ |
153 | virtual void workEnd(IOService *device, uint64_t token, WorkEndArgs *args = nullptr, bool done = true); |
154 | |
155 | /*! |
156 | * @struct PerfControllerInterface |
157 | * @discussion Function pointers necessary to register a performance controller. Not for general driver use. |
158 | */ |
159 | struct PerfControllerInterface |
160 | { |
161 | struct WorkState { |
162 | uint64_t thread_group_id; |
163 | void *thread_group_data; |
164 | void *work_data; |
165 | uint32_t work_data_size; |
166 | }; |
167 | |
168 | using RegisterDeviceFunction = IOReturn (*)(IOService *); |
169 | using WorkCanSubmitFunction = bool (*)(IOService *, WorkState *, WorkSubmitArgs *); |
170 | using WorkSubmitFunction = void (*)(IOService *, uint64_t, WorkState *, WorkSubmitArgs *); |
171 | using WorkBeginFunction = void (*)(IOService *, uint64_t, WorkState *, WorkBeginArgs *); |
172 | using WorkEndFunction = void (*)(IOService *, uint64_t, WorkState *, WorkEndArgs *, bool); |
173 | |
174 | uint64_t version; |
175 | RegisterDeviceFunction registerDevice; |
176 | RegisterDeviceFunction unregisterDevice; |
177 | WorkCanSubmitFunction workCanSubmit; |
178 | WorkSubmitFunction workSubmit; |
179 | WorkBeginFunction workBegin; |
180 | WorkEndFunction workEnd; |
181 | }; |
182 | |
183 | /*! |
184 | * @function registerPerformanceController |
185 | * @abstract Register a performance controller to receive callbacks. Not for general driver use. |
186 | * @param interface Struct containing callback functions implemented by the performance controller. |
187 | * @returns kIOReturnSuccess or kIOReturnError if the interface was already registered. |
188 | */ |
189 | virtual IOReturn registerPerformanceController(PerfControllerInterface interface); |
190 | |
191 | private: |
192 | struct WorkTableEntry |
193 | { |
194 | struct thread_group *thread_group; |
195 | bool started; |
196 | uint8_t perfcontrol_data[32]; |
197 | }; |
198 | |
199 | // TODO: size of table should match sum(maxWorkCapacity) of all users |
200 | static constexpr size_t kWorkTableNumEntries = 1024; |
201 | |
202 | uint64_t allocateToken(thread_group *thread_group); |
203 | void deallocateToken(uint64_t token); |
204 | bool getEntryForToken(uint64_t token, WorkTableEntry &entry); |
205 | void markEntryStarted(uint64_t token, bool started); |
206 | |
207 | PerfControllerInterface interface; |
208 | IOLock *interfaceLock; |
209 | OSSet *deviceRegistrationList; |
210 | |
211 | // TODO: replace with ltable or pool of objects |
212 | WorkTableEntry workTable[kWorkTableNumEntries]; |
213 | size_t workTableNextIndex; |
214 | IOSimpleLock *workTableLock; |
215 | }; |
216 | |
217 | #endif /* __cplusplus */ |
218 | #endif /* KERNEL_PRIVATE */ |
219 | |