1 /* $NetBSD: stats.c,v 1.2 2021/12/18 23:45:08 riastradh Exp $ */
2
3 /*
4 * Copyright 2016 Advanced Micro Devices, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: AMD
25 *
26 */
27
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: stats.c,v 1.2 2021/12/18 23:45:08 riastradh Exp $");
30
31 #include "mod_stats.h"
32 #include "dm_services.h"
33 #include "dc.h"
34 #include "core_types.h"
35
36 #define DAL_STATS_ENABLE_REGKEY "DalStatsEnable"
37 #define DAL_STATS_ENABLE_REGKEY_DEFAULT 0x00000000
38 #define DAL_STATS_ENABLE_REGKEY_ENABLED 0x00000001
39
40 #define DAL_STATS_ENTRIES_REGKEY "DalStatsEntries"
41 #define DAL_STATS_ENTRIES_REGKEY_DEFAULT 0x00350000
42 #define DAL_STATS_ENTRIES_REGKEY_MAX 0x01000000
43
44 #define DAL_STATS_EVENT_ENTRIES_DEFAULT 0x00000100
45
46 #define MOD_STATS_NUM_VSYNCS 5
47 #define MOD_STATS_EVENT_STRING_MAX 512
48
49 struct stats_time_cache {
50 unsigned int entry_id;
51
52 unsigned long flip_timestamp_in_ns;
53 unsigned long vupdate_timestamp_in_ns;
54
55 unsigned int render_time_in_us;
56 unsigned int avg_render_time_in_us_last_ten;
57 unsigned int v_sync_time_in_us[MOD_STATS_NUM_VSYNCS];
58 unsigned int num_vsync_between_flips;
59
60 unsigned int flip_to_vsync_time_in_us;
61 unsigned int vsync_to_flip_time_in_us;
62
63 unsigned int min_window;
64 unsigned int max_window;
65 unsigned int v_total_min;
66 unsigned int v_total_max;
67 unsigned int event_triggers;
68
69 unsigned int lfc_mid_point_in_us;
70 unsigned int num_frames_inserted;
71 unsigned int inserted_duration_in_us;
72
73 unsigned int flags;
74 };
75
76 struct stats_event_cache {
77 unsigned int entry_id;
78 char event_string[MOD_STATS_EVENT_STRING_MAX];
79 };
80
81 struct core_stats {
82 struct mod_stats public;
83 struct dc *dc;
84
85 bool enabled;
86 unsigned int entries;
87 unsigned int event_entries;
88 unsigned int entry_id;
89
90 struct stats_time_cache *time;
91 unsigned int index;
92
93 struct stats_event_cache *events;
94 unsigned int event_index;
95
96 };
97
98 #define MOD_STATS_TO_CORE(mod_stats)\
99 container_of(mod_stats, struct core_stats, public)
100
mod_stats_init(struct mod_stats * mod_stats)101 bool mod_stats_init(struct mod_stats *mod_stats)
102 {
103 bool result = false;
104 struct core_stats *core_stats = NULL;
105 struct dc *dc = NULL;
106
107 if (mod_stats == NULL)
108 return false;
109
110 core_stats = MOD_STATS_TO_CORE(mod_stats);
111 dc = core_stats->dc;
112
113 return result;
114 }
115
mod_stats_create(struct dc * dc)116 struct mod_stats *mod_stats_create(struct dc *dc)
117 {
118 struct core_stats *core_stats = NULL;
119 struct persistent_data_flag flag;
120 unsigned int reg_data;
121 int i = 0;
122
123 if (dc == NULL)
124 goto fail_construct;
125
126 core_stats = kzalloc(sizeof(struct core_stats), GFP_KERNEL);
127
128 if (core_stats == NULL)
129 goto fail_construct;
130
131 core_stats->dc = dc;
132
133 core_stats->enabled = DAL_STATS_ENABLE_REGKEY_DEFAULT;
134 if (dm_read_persistent_data(dc->ctx, NULL, NULL,
135 DAL_STATS_ENABLE_REGKEY,
136 ®_data, sizeof(unsigned int), &flag))
137 core_stats->enabled = reg_data;
138
139 if (core_stats->enabled) {
140 core_stats->entries = DAL_STATS_ENTRIES_REGKEY_DEFAULT;
141 if (dm_read_persistent_data(dc->ctx, NULL, NULL,
142 DAL_STATS_ENTRIES_REGKEY,
143 ®_data, sizeof(unsigned int), &flag)) {
144 if (reg_data > DAL_STATS_ENTRIES_REGKEY_MAX)
145 core_stats->entries = DAL_STATS_ENTRIES_REGKEY_MAX;
146 else
147 core_stats->entries = reg_data;
148 }
149 core_stats->time = kcalloc(core_stats->entries,
150 sizeof(struct stats_time_cache),
151 GFP_KERNEL);
152
153 if (core_stats->time == NULL)
154 goto fail_construct_time;
155
156 core_stats->event_entries = DAL_STATS_EVENT_ENTRIES_DEFAULT;
157 core_stats->events = kcalloc(core_stats->event_entries,
158 sizeof(struct stats_event_cache),
159 GFP_KERNEL);
160
161 if (core_stats->events == NULL)
162 goto fail_construct_events;
163
164 } else {
165 core_stats->entries = 0;
166 }
167
168 /* Purposely leave index 0 unused so we don't need special logic to
169 * handle calculation cases that depend on previous flip data.
170 */
171 core_stats->index = 1;
172 core_stats->event_index = 0;
173
174 // Keeps track of ordering within the different stats structures
175 core_stats->entry_id = 0;
176
177 return &core_stats->public;
178
179 fail_construct_events:
180 kfree(core_stats->time);
181
182 fail_construct_time:
183 kfree(core_stats);
184
185 fail_construct:
186 return NULL;
187 }
188
mod_stats_destroy(struct mod_stats * mod_stats)189 void mod_stats_destroy(struct mod_stats *mod_stats)
190 {
191 if (mod_stats != NULL) {
192 struct core_stats *core_stats = MOD_STATS_TO_CORE(mod_stats);
193
194 kfree(core_stats->time);
195 kfree(core_stats->events);
196 kfree(core_stats);
197 }
198 }
199
mod_stats_dump(struct mod_stats * mod_stats)200 void mod_stats_dump(struct mod_stats *mod_stats)
201 {
202 struct dc *dc = NULL;
203 struct dal_logger *logger = NULL;
204 struct core_stats *core_stats = NULL;
205 struct stats_time_cache *time = NULL;
206 struct stats_event_cache *events = NULL;
207 unsigned int time_index = 1;
208 unsigned int event_index = 0;
209 unsigned int index = 0;
210 struct log_entry log_entry;
211
212 if (mod_stats == NULL)
213 return;
214
215 core_stats = MOD_STATS_TO_CORE(mod_stats);
216 dc = core_stats->dc;
217 logger = dc->ctx->logger;
218 time = core_stats->time;
219 events = core_stats->events;
220
221 DISPLAY_STATS_BEGIN(log_entry);
222
223 DISPLAY_STATS("==Display Caps==\n");
224
225 DISPLAY_STATS("==Display Stats==\n");
226
227 DISPLAY_STATS("%10s %10s %10s %10s %10s"
228 " %11s %11s %17s %10s %14s"
229 " %10s %10s %10s %10s %10s"
230 " %10s %10s %10s %10s\n",
231 "render", "avgRender",
232 "minWindow", "midPoint", "maxWindow",
233 "vsyncToFlip", "flipToVsync", "vsyncsBetweenFlip",
234 "numFrame", "insertDuration",
235 "vTotalMin", "vTotalMax", "eventTrigs",
236 "vSyncTime1", "vSyncTime2", "vSyncTime3",
237 "vSyncTime4", "vSyncTime5", "flags");
238
239 for (int i = 0; i < core_stats->entry_id; i++) {
240 if (event_index < core_stats->event_index &&
241 i == events[event_index].entry_id) {
242 DISPLAY_STATS("==Event==%s\n", events[event_index].event_string);
243 event_index++;
244 } else if (time_index < core_stats->index &&
245 i == time[time_index].entry_id) {
246 DISPLAY_STATS("%10u %10u %10u %10u %10u"
247 " %11u %11u %17u %10u %14u"
248 " %10u %10u %10u %10u %10u"
249 " %10u %10u %10u %10u\n",
250 time[time_index].render_time_in_us,
251 time[time_index].avg_render_time_in_us_last_ten,
252 time[time_index].min_window,
253 time[time_index].lfc_mid_point_in_us,
254 time[time_index].max_window,
255 time[time_index].vsync_to_flip_time_in_us,
256 time[time_index].flip_to_vsync_time_in_us,
257 time[time_index].num_vsync_between_flips,
258 time[time_index].num_frames_inserted,
259 time[time_index].inserted_duration_in_us,
260 time[time_index].v_total_min,
261 time[time_index].v_total_max,
262 time[time_index].event_triggers,
263 time[time_index].v_sync_time_in_us[0],
264 time[time_index].v_sync_time_in_us[1],
265 time[time_index].v_sync_time_in_us[2],
266 time[time_index].v_sync_time_in_us[3],
267 time[time_index].v_sync_time_in_us[4],
268 time[time_index].flags);
269
270 time_index++;
271 }
272 }
273
274 DISPLAY_STATS_END(log_entry);
275 }
276
mod_stats_reset_data(struct mod_stats * mod_stats)277 void mod_stats_reset_data(struct mod_stats *mod_stats)
278 {
279 struct core_stats *core_stats = NULL;
280 struct stats_time_cache *time = NULL;
281 unsigned int index = 0;
282
283 if (mod_stats == NULL)
284 return;
285
286 core_stats = MOD_STATS_TO_CORE(mod_stats);
287
288 memset(core_stats->time, 0,
289 sizeof(struct stats_time_cache) * core_stats->entries);
290
291 memset(core_stats->events, 0,
292 sizeof(struct stats_event_cache) * core_stats->event_entries);
293
294 core_stats->index = 1;
295 core_stats->event_index = 0;
296
297 // Keeps track of ordering within the different stats structures
298 core_stats->entry_id = 0;
299 }
300
mod_stats_update_event(struct mod_stats * mod_stats,char * event_string,unsigned int length)301 void mod_stats_update_event(struct mod_stats *mod_stats,
302 char *event_string,
303 unsigned int length)
304 {
305 struct core_stats *core_stats = NULL;
306 struct stats_event_cache *events = NULL;
307 unsigned int index = 0;
308 unsigned int copy_length = 0;
309
310 if (mod_stats == NULL)
311 return;
312
313 core_stats = MOD_STATS_TO_CORE(mod_stats);
314
315 if (core_stats->event_index >= core_stats->event_entries)
316 return;
317
318 events = core_stats->events;
319 index = core_stats->event_index;
320
321 copy_length = length;
322 if (length > MOD_STATS_EVENT_STRING_MAX)
323 copy_length = MOD_STATS_EVENT_STRING_MAX;
324
325 memcpy(&events[index].event_string, event_string, copy_length);
326 events[index].event_string[copy_length - 1] = '\0';
327
328 events[index].entry_id = core_stats->entry_id;
329 core_stats->event_index++;
330 core_stats->entry_id++;
331 }
332
mod_stats_update_flip(struct mod_stats * mod_stats,unsigned long timestamp_in_ns)333 void mod_stats_update_flip(struct mod_stats *mod_stats,
334 unsigned long timestamp_in_ns)
335 {
336 struct core_stats *core_stats = NULL;
337 struct stats_time_cache *time = NULL;
338 unsigned int index = 0;
339
340 if (mod_stats == NULL)
341 return;
342
343 core_stats = MOD_STATS_TO_CORE(mod_stats);
344
345 if (core_stats->index >= core_stats->entries)
346 return;
347
348 time = core_stats->time;
349 index = core_stats->index;
350
351 time[index].flip_timestamp_in_ns = timestamp_in_ns;
352 time[index].render_time_in_us =
353 (timestamp_in_ns - time[index - 1].flip_timestamp_in_ns) / 1000;
354
355 if (index >= 10) {
356 for (unsigned int i = 0; i < 10; i++)
357 time[index].avg_render_time_in_us_last_ten +=
358 time[index - i].render_time_in_us;
359 time[index].avg_render_time_in_us_last_ten /= 10;
360 }
361
362 if (time[index].num_vsync_between_flips > 0)
363 time[index].vsync_to_flip_time_in_us =
364 (timestamp_in_ns -
365 time[index].vupdate_timestamp_in_ns) / 1000;
366 else
367 time[index].vsync_to_flip_time_in_us =
368 (timestamp_in_ns -
369 time[index - 1].vupdate_timestamp_in_ns) / 1000;
370
371 time[index].entry_id = core_stats->entry_id;
372 core_stats->index++;
373 core_stats->entry_id++;
374 }
375
mod_stats_update_vupdate(struct mod_stats * mod_stats,unsigned long timestamp_in_ns)376 void mod_stats_update_vupdate(struct mod_stats *mod_stats,
377 unsigned long timestamp_in_ns)
378 {
379 struct core_stats *core_stats = NULL;
380 struct stats_time_cache *time = NULL;
381 unsigned int index = 0;
382 unsigned int num_vsyncs = 0;
383 unsigned int prev_vsync_in_ns = 0;
384
385 if (mod_stats == NULL)
386 return;
387
388 core_stats = MOD_STATS_TO_CORE(mod_stats);
389
390 if (core_stats->index >= core_stats->entries)
391 return;
392
393 time = core_stats->time;
394 index = core_stats->index;
395 num_vsyncs = time[index].num_vsync_between_flips;
396
397 if (num_vsyncs < MOD_STATS_NUM_VSYNCS) {
398 if (num_vsyncs == 0) {
399 prev_vsync_in_ns =
400 time[index - 1].vupdate_timestamp_in_ns;
401
402 time[index].flip_to_vsync_time_in_us =
403 (timestamp_in_ns -
404 time[index - 1].flip_timestamp_in_ns) /
405 1000;
406 } else {
407 prev_vsync_in_ns =
408 time[index].vupdate_timestamp_in_ns;
409 }
410
411 time[index].v_sync_time_in_us[num_vsyncs] =
412 (timestamp_in_ns - prev_vsync_in_ns) / 1000;
413 }
414
415 time[index].vupdate_timestamp_in_ns = timestamp_in_ns;
416 time[index].num_vsync_between_flips++;
417 }
418
mod_stats_update_freesync(struct mod_stats * mod_stats,unsigned int v_total_min,unsigned int v_total_max,unsigned int event_triggers,unsigned int window_min,unsigned int window_max,unsigned int lfc_mid_point_in_us,unsigned int inserted_frames,unsigned int inserted_duration_in_us)419 void mod_stats_update_freesync(struct mod_stats *mod_stats,
420 unsigned int v_total_min,
421 unsigned int v_total_max,
422 unsigned int event_triggers,
423 unsigned int window_min,
424 unsigned int window_max,
425 unsigned int lfc_mid_point_in_us,
426 unsigned int inserted_frames,
427 unsigned int inserted_duration_in_us)
428 {
429 struct core_stats *core_stats = NULL;
430 struct stats_time_cache *time = NULL;
431 unsigned int index = 0;
432
433 if (mod_stats == NULL)
434 return;
435
436 core_stats = MOD_STATS_TO_CORE(mod_stats);
437
438 if (core_stats->index >= core_stats->entries)
439 return;
440
441 time = core_stats->time;
442 index = core_stats->index;
443
444 time[index].v_total_min = v_total_min;
445 time[index].v_total_max = v_total_max;
446 time[index].event_triggers = event_triggers;
447 time[index].min_window = window_min;
448 time[index].max_window = window_max;
449 time[index].lfc_mid_point_in_us = lfc_mid_point_in_us;
450 time[index].num_frames_inserted = inserted_frames;
451 time[index].inserted_duration_in_us = inserted_duration_in_us;
452 }
453
454