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 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 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 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 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 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 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 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 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 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