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  * This test attempts to verify that the schedutil governor does not take into
      7  * account stale utilization from an idle CPU when calculating the frequency for
      8  * a shared policy.
      9  *
     10  * This test is not yet complete and may never be. The CPU in question may
     11  * receive spurious updates which push the stale deadline out, causing the test
     12  * to fail.
     13  */
     14 
     15 #define _GNU_SOURCE
     16 #include <errno.h>
     17 #include <pthread.h>
     18 #include <sched.h>
     19 #include <time.h>
     20 #include <semaphore.h>
     21 #include <stdlib.h>
     22 
     23 #include "tst_test.h"
     24 #include "tst_safe_file_ops.h"
     25 #include "tst_safe_pthread.h"
     26 
     27 #include "trace_parse.h"
     28 #include "util.h"
     29 
     30 #define TRACE_EVENTS "sugov_next_freq sugov_util_update"
     31 
     32 #define MAX_TEST_CPUS 32
     33 static int policy_cpus[MAX_TEST_CPUS];
     34 static int policy_num_cpus = 0;
     35 
     36 static int test_cpu;
     37 static sem_t sem;
     38 
     39 /* sugov currently waits 1.125 * TICK_NSEC, which with HZ=300, is
     40  * ~3.75ms for PELT
     41  * On WALT, 1.125 * sched_ravg_window (20ms) is 22.5ms */
     42 #define MAX_STALE_USEC 22500
     43 /* The event task may not wake up right away due to timer slack. */
     44 #define SLACK_USEC 10000
     45 
     46 static void *event_fn(void *arg LTP_ATTRIBUTE_UNUSED)
     47 {
     48 	/*
     49 	 * FIXME: Proper logic to identify a multi-CPU policy and select two
     50 	 * CPUS from it is required here.
     51 	 */
     52 	affine(test_cpu - 1);
     53 
     54 	sem_wait(&sem);
     55 
     56 	SAFE_FILE_PRINTF(TRACING_DIR "trace_marker",
     57 			 "event task sleep");
     58 	usleep(MAX_STALE_USEC);
     59 	SAFE_FILE_PRINTF(TRACING_DIR "trace_marker",
     60 			 "event task wake");
     61 	/*
     62 	 * Waking up should be sufficient to get the cpufreq policy to
     63 	 * re-evaluate.
     64 	 */
     65 	return NULL;
     66 }
     67 
     68 #define BURN_MSEC 500
     69 static void *burn_fn(void *arg LTP_ATTRIBUTE_UNUSED)
     70 {
     71 	affine(test_cpu);
     72 
     73 	/*
     74 	 * wait a bit to allow any hacks to boost frequency on migration
     75 	 * to take effect
     76 	 */
     77 	usleep(200);
     78 
     79 	/* Busy loop for BURN_MSEC to get the task demand to maximum. */
     80 	burn(BURN_MSEC * 1000, 0);
     81 
     82 	/*
     83 	 * Sleep. The next sugov update after TICK_NSEC should not include
     84 	 * this task's contribution.
     85 	 */
     86 	SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "sleeping");
     87 
     88 	/*
     89 	 * Wake up task on another CPU in the same policy which will sleep
     90 	 * for stale_ns, then wake up briefly to trigger a recalculation of the
     91 	 * cpufreq policy.
     92 	 */
     93 	sem_post(&sem);
     94 	sleep(2);
     95 
     96 	return NULL;
     97 }
     98 
     99 static int cpu_in_policy(int cpu)
    100 {
    101 	int i;
    102 	for (i = 0; i < policy_num_cpus; i++)
    103 		if (cpu == policy_cpus[i])
    104 			return 1;
    105 	return 0;
    106 }
    107 
    108 static int parse_results(void)
    109 {
    110 	int i, sleep_idx;
    111 	int max_util_seen = 0;
    112 	unsigned int stale_usec;
    113 
    114 	/* Verify that utilization reached 1024 before sleep. */
    115 	for (i = 0; i < num_trace_records; i++) {
    116 		if (trace[i].event_type == TRACE_RECORD_SUGOV_UTIL_UPDATE) {
    117 			struct trace_sugov_util_update *t =
    118 				trace[i].event_data;
    119 			if (t->cpu == test_cpu && t->util > max_util_seen)
    120 				max_util_seen = t->util;
    121 		}
    122 		if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE &&
    123 		    !strcmp(trace[i].event_data, "sleeping"))
    124 			break;
    125 	}
    126 	printf("Max util seen from CPU hog: %d\n", max_util_seen);
    127 	if (max_util_seen < 1000) {
    128 		printf("Trace parse error, utilization of CPU hog did "
    129 		       "not reach 1000.\n");
    130 		return -1;
    131 	}
    132 	sleep_idx = i;
    133 //	print_trace_record(&trace[i]);
    134 	for (; i < num_trace_records; i++)
    135 		if (trace[i].event_type == TRACE_RECORD_SUGOV_NEXT_FREQ) {
    136 			struct trace_sugov_next_freq *t =
    137 				trace[i].event_data;
    138 			/* We should only see some minor utilization. */
    139 			if (cpu_in_policy(t->cpu) && t->util < 200)
    140 				break;
    141 		}
    142 	if (i == num_trace_records) {
    143 		printf("Trace parse error, util never went stale!\n");
    144 		return -1;
    145 	}
    146 //	print_trace_record(&trace[i]);
    147 	stale_usec = TS_TO_USEC(trace[i].ts) - TS_TO_USEC(trace[sleep_idx].ts);
    148 
    149 	printf("Stale vote shown to be cleared in %d usec.\n", stale_usec);
    150 	return (stale_usec > (MAX_STALE_USEC + SLACK_USEC));
    151 }
    152 
    153 #define POLICY_CPUS_BUFSIZE 1024
    154 static void get_policy_cpus(void)
    155 {
    156 	int i=0, len, policy_cpus_fd;
    157 	char policy_cpus_fname[128];;
    158 	char *buf;
    159 
    160 	sprintf(policy_cpus_fname,
    161 		"/sys/devices/system/cpu/cpu%d/cpufreq/related_cpus",
    162 		test_cpu);
    163 	buf = SAFE_MALLOC(POLICY_CPUS_BUFSIZE);
    164 
    165 	policy_cpus_fd = open(policy_cpus_fname, O_RDONLY);
    166 	if (policy_cpus_fd < 0) {
    167 		printf("Failed to open policy cpus (errno %d)\n",
    168 		       errno);
    169 		return;
    170 	}
    171 
    172 	len = read(policy_cpus_fd, buf, POLICY_CPUS_BUFSIZE -1);
    173 	/* At least one digit is expected. */
    174 	if (len < 2) {
    175 		printf("Read of policy cpus returned %d (errno %d)\n",
    176 		       len, errno);
    177 		return;
    178 	}
    179 	close(policy_cpus_fd);
    180 	/* buf now has a list of CPUs, parse it */
    181 	while(buf[i] >= '0' && buf[i] <= '9') {
    182 		int j = i;
    183 		while (buf[j] >= '0' && buf[j] <= '9')
    184 			j++;
    185 		buf[j] = 0;
    186 		policy_cpus[policy_num_cpus++] = atoi(&buf[i]);
    187 		i = j + 1;
    188 	}
    189 	printf("Testing on CPU %d, all CPUs in that policy:\n",
    190 	       test_cpu);
    191 	for (int i = 0; i < policy_num_cpus; i++)
    192 		printf(" %d", policy_cpus[i]);
    193 	printf("\n");
    194 	free(buf);
    195 }
    196 
    197 static void run(void)
    198 {
    199 	pthread_t burn_thread, event_thread;
    200 
    201 	test_cpu = tst_ncpus() - 1;
    202 	printf("CPU hog will be bound to CPU %d.\n", test_cpu);
    203 	get_policy_cpus();
    204 
    205 	sem_init(&sem, 0, 0);
    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_CREATE(&event_thread, NULL, event_fn, NULL);
    216 
    217 	SAFE_PTHREAD_JOIN(burn_thread, NULL);
    218 	SAFE_PTHREAD_JOIN(event_thread, NULL);
    219 
    220 	/* disable tracing */
    221 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
    222 	LOAD_TRACE();
    223 
    224 	if (parse_results())
    225 		tst_res(TFAIL, "Stale utilization not cleared within expected "
    226 			"time (%d usec).\n", MAX_STALE_USEC + SLACK_USEC);
    227 	else
    228 		tst_res(TPASS, "Stale utilization cleared within expected "
    229 			"time.\n");
    230 }
    231 
    232 static struct tst_test test = {
    233 	.test_all = run,
    234 	.cleanup = trace_cleanup,
    235 };
    236