Home | History | Annotate | Line # | Download | only in stats
      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 			&reg_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 				&reg_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