Home | History | Annotate | Download | only in eas
      1 /*
      2  * Copyright (c) 2018 Google, Inc.
      3  *
      4  * SPDX-License-Identifier: GPL-2.0-or-later
      5  *
      6  * A CFS task is affined to a particular CPU. The task runs as a CPU hog for a
      7  * while then as a very small task for a while. The latency for the CPU
      8  * frequency of the CPU to reach max and then min is verified.
      9  */
     10 
     11 #define _GNU_SOURCE
     12 #include <errno.h>
     13 #include <pthread.h>
     14 #include <sched.h>
     15 #include <time.h>
     16 
     17 #include "tst_test.h"
     18 #include "tst_safe_file_ops.h"
     19 #include "tst_safe_pthread.h"
     20 
     21 #include "trace_parse.h"
     22 #include "util.h"
     23 
     24 #define TRACE_EVENTS "sched_process_exit sched_process_fork cpu_frequency"
     25 
     26 #define MAX_FREQ_INCREASE_LATENCY_US 70000
     27 #define MAX_FREQ_DECREASE_LATENCY_US 70000
     28 
     29 static int test_cpu;
     30 
     31 #define BURN_MSEC 500
     32 static void *burn_fn(void *arg LTP_ATTRIBUTE_UNUSED)
     33 {
     34 	int i = 0;
     35 	unsigned int scaling_min_freq, scaling_cur_freq;
     36 	char scaling_freq_file[60];
     37 
     38 	affine(test_cpu);
     39 
     40 	/*
     41 	 * wait a bit to allow any hacks to boost frequency on migration
     42 	 * to take effect
     43 	 */
     44 	usleep(200);
     45 
     46 	sprintf(scaling_freq_file,
     47 		"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq",
     48 		test_cpu);
     49 	SAFE_FILE_SCANF(scaling_freq_file, "%d", &scaling_min_freq);
     50 
     51 	sprintf(scaling_freq_file,
     52 		"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq",
     53 		test_cpu);
     54 
     55 	/* wait for test_cpu to reach scaling_min_freq */
     56 	while(i++ < 10) {
     57 		usleep(100 * 1000);
     58 		SAFE_FILE_SCANF(scaling_freq_file, "%d",
     59 				&scaling_cur_freq);
     60 		if (scaling_cur_freq == scaling_min_freq)
     61 			break;
     62 	}
     63 	if (i >= 10) {
     64 		printf("Unable to reach scaling_min_freq before test!\n");
     65 		return NULL;
     66 	}
     67 
     68 	SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "affined");
     69 	burn(BURN_MSEC * 1000, 0);
     70 	SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "small task");
     71 	burn(BURN_MSEC * 1000, 1);
     72 
     73 	return NULL;
     74 }
     75 
     76 static int parse_results(void)
     77 {
     78 	int i;
     79 
     80 	int start_idx;
     81 	int sleep_idx;
     82 	unsigned int max_freq_seen = 0;
     83 	int max_freq_seen_idx;
     84 	unsigned int min_freq_seen = UINT_MAX;
     85 	int min_freq_seen_idx;
     86 
     87 	char scaling_freq_file[60];
     88 	unsigned int scaling_max_freq;
     89 	unsigned int scaling_min_freq;
     90 
     91 	unsigned int increase_latency_usec;
     92 	unsigned int decrease_latency_usec;
     93 
     94 	/* find starting timestamp of test */
     95 	for (i = 0; i < num_trace_records; i++)
     96 		if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE &&
     97 		    !strcmp(trace[i].event_data, "affined"))
     98 			break;
     99 	if (i == num_trace_records) {
    100 		printf("Did not find start of burn thread in trace!\n");
    101 		return -1;
    102 	}
    103 	start_idx = i;
    104 
    105 	/* find timestamp when burn thread sleeps */
    106 	for (; i < num_trace_records; i++)
    107 		if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE &&
    108 		    !strcmp(trace[i].event_data, "small task"))
    109 				break;
    110 	if (i == num_trace_records) {
    111 		printf("Did not find switch to small task of burn thread in "
    112 		       "trace!\n");
    113 		return -1;
    114 	}
    115 	sleep_idx = i;
    116 
    117 	/* find highest CPU frequency bewteen start and sleep timestamp */
    118 	for (i = start_idx; i < sleep_idx; i++)
    119 		if (trace[i].event_type == TRACE_RECORD_CPU_FREQUENCY) {
    120 			struct trace_cpu_frequency *t = trace[i].event_data;
    121 			if (t->cpu == test_cpu && t->state > max_freq_seen) {
    122 				max_freq_seen = t->state;
    123 				max_freq_seen_idx = i;
    124 			}
    125 		}
    126 	if (max_freq_seen == 0) {
    127 		printf("No freq events between start and sleep!\n");
    128 		return -1;
    129 	}
    130 
    131 	/* find lowest CPU frequency between sleep timestamp and end */
    132 	for (; i < num_trace_records; i++)
    133 		if (trace[i].event_type == TRACE_RECORD_CPU_FREQUENCY) {
    134 			struct trace_cpu_frequency *t = trace[i].event_data;
    135 			if (t->cpu == test_cpu && t->state < min_freq_seen) {
    136 				min_freq_seen = t->state;
    137 				min_freq_seen_idx = i;
    138 			}
    139 		}
    140 	if (min_freq_seen == UINT_MAX) {
    141 		printf("No freq events between sleep and end!\n");
    142 		return -1;
    143 	}
    144 
    145 	/* is highest CPU freq equal or greater than highest reported in
    146 	 * scaling_max_freq? */
    147 	sprintf(scaling_freq_file,
    148 		"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq",
    149 		test_cpu);
    150 	SAFE_FILE_SCANF(scaling_freq_file, "%d", &scaling_max_freq);
    151 	if (max_freq_seen < scaling_max_freq) {
    152 		printf("CPU%d did not reach scaling_max_freq!\n",
    153 		       test_cpu);
    154 		return -1;
    155 	} else {
    156 		printf("CPU%d reached %d MHz during test "
    157 		       "(scaling_max_freq %d MHz).\n", test_cpu,
    158 		       max_freq_seen / 1000, scaling_max_freq / 1000);
    159 	}
    160 
    161 	/* is lowest CPU freq equal or less than scaling_min_freq? */
    162 	sprintf(scaling_freq_file,
    163 		"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq",
    164 		test_cpu);
    165 	SAFE_FILE_SCANF(scaling_freq_file, "%d", &scaling_min_freq);
    166 	if (min_freq_seen > scaling_min_freq) {
    167 		printf("CPU%d did not reach scaling_min_freq!\n",
    168 		       test_cpu);
    169 		return -1;
    170 	} else {
    171 		printf("CPU%d reached %d MHz after test "
    172 		       "(scaling_min_freq %d Mhz).\n",
    173 		       test_cpu, min_freq_seen / 1000,
    174 		       scaling_min_freq / 1000);
    175 	}
    176 
    177 	/* calculate and check latencies */
    178 	increase_latency_usec = trace[max_freq_seen_idx].ts.sec * USEC_PER_SEC +
    179 		trace[max_freq_seen_idx].ts.usec;
    180 	increase_latency_usec -= trace[start_idx].ts.sec * USEC_PER_SEC +
    181 		trace[start_idx].ts.usec;
    182 
    183 	decrease_latency_usec = trace[min_freq_seen_idx].ts.sec * USEC_PER_SEC +
    184 		trace[min_freq_seen_idx].ts.usec;
    185 	decrease_latency_usec -= trace[sleep_idx].ts.sec * USEC_PER_SEC +
    186 		trace[sleep_idx].ts.usec;
    187 
    188 	printf("Increase latency: %d usec\n", increase_latency_usec);
    189 	printf("Decrease latency: %d usec\n", decrease_latency_usec);
    190 
    191 	return (increase_latency_usec > MAX_FREQ_INCREASE_LATENCY_US ||
    192 		decrease_latency_usec > MAX_FREQ_DECREASE_LATENCY_US);
    193 }
    194 
    195 static void run(void)
    196 {
    197 	pthread_t burn_thread;
    198 
    199 	tst_res(TINFO, "Max acceptable latency to fmax: %d usec\n",
    200 		MAX_FREQ_INCREASE_LATENCY_US);
    201 	tst_res(TINFO, "Max acceptable latency to fmin: %d usec\n",
    202 		MAX_FREQ_DECREASE_LATENCY_US);
    203 
    204 	test_cpu = tst_ncpus() - 1;
    205 	printf("CPU hog will be bound to CPU %d.\n", test_cpu);
    206 
    207 	/* configure and enable tracing */
    208 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
    209 	SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
    210 	SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
    211 	SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
    212 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
    213 
    214 	SAFE_PTHREAD_CREATE(&burn_thread, NULL, burn_fn, NULL);
    215 	SAFE_PTHREAD_JOIN(burn_thread, NULL);
    216 
    217 	/* disable tracing */
    218 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
    219 	LOAD_TRACE();
    220 
    221 	if (parse_results())
    222 		tst_res(TFAIL, "Governor did not meet latency targets.\n");
    223 	else
    224 		tst_res(TPASS, "Governor met latency targets.\n");
    225 }
    226 
    227 static struct tst_test test = {
    228 	.test_all = run,
    229 	.cleanup = trace_cleanup,
    230 };
    231