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  * Task starts out as a CPU hog and then becomes small.
      7  * Task placement and downmigration latency are verified.
      8  */
      9 
     10 #define _GNU_SOURCE
     11 #include <errno.h>
     12 #include <pthread.h>
     13 #include <sched.h>
     14 #include <time.h>
     15 
     16 #include "tst_test.h"
     17 #include "tst_safe_file_ops.h"
     18 #include "tst_safe_pthread.h"
     19 
     20 #include "trace_parse.h"
     21 #include "util.h"
     22 
     23 #define TRACE_EVENTS "sched_switch"
     24 
     25 static int task_tid;
     26 
     27 #define MAX_DOWNMIGRATE_LATENCY_US 100000
     28 #define MAX_INCORRECT_CLUSTER_PCT 10
     29 #define BURN_SEC 3
     30 static void *task_fn(void *arg LTP_ATTRIBUTE_UNUSED)
     31 {
     32 	task_tid = gettid();
     33 
     34 	printf("Big task executing for %ds...\n", BURN_SEC);
     35 	burn(BURN_SEC * USEC_PER_SEC, 0);
     36 
     37 	printf("Changing to small task...\n");
     38 	SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "SMALL TASK");
     39 	burn(BURN_SEC * USEC_PER_SEC, 1);
     40 
     41 	return NULL;
     42 }
     43 
     44 static int parse_results(void)
     45 {
     46 	int i, pct, rv = 0;
     47 	unsigned long long exec_start_us = 0;
     48 	unsigned long long too_big_cpu_us = 0;
     49 	unsigned long long too_small_cpu_us = 0;
     50 	unsigned long long big_task_us = 0;
     51 	unsigned long long small_task_us = 0;
     52 	unsigned long long smalltask_ts_usec = 0;
     53 	unsigned long long downmigrate_ts_usec = 0;
     54 	unsigned long long downmigrate_latency_usec = 0;
     55 	cpu_set_t cpuset;
     56 
     57 	if (find_cpus_with_capacity(0, &cpuset)) {
     58 		printf("Failed to find the CPUs in the little cluster.\n");
     59 		return -1;
     60 	}
     61 
     62 	for (i = 0; i < num_trace_records; i++) {
     63 		unsigned long long segment_us;
     64 		struct trace_sched_switch *t = trace[i].event_data;
     65 
     66 		if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE &&
     67 			   !strcmp(trace[i].event_data, "SMALL TASK")) {
     68 			smalltask_ts_usec = TS_TO_USEC(trace[i].ts);
     69 			continue;
     70 		}
     71 
     72 		if (trace[i].event_type != TRACE_RECORD_SCHED_SWITCH)
     73 			continue;
     74 
     75 		if (t->next_pid == task_tid) {
     76 			/* Start of task execution segment. */
     77 			if (exec_start_us) {
     78 				printf("Trace parse fail: double exec start\n");
     79 				return -1;
     80 			}
     81 			exec_start_us = TS_TO_USEC(trace[i].ts);
     82 			if (smalltask_ts_usec && !downmigrate_ts_usec &&
     83 			    CPU_ISSET(trace[i].cpu, &cpuset))
     84 				downmigrate_ts_usec = exec_start_us;
     85 			continue;
     86 		}
     87 		if (t->prev_pid != task_tid)
     88 			continue;
     89 		/* End of task execution segment. */
     90 		segment_us = TS_TO_USEC(trace[i].ts);
     91 		segment_us -= exec_start_us;
     92 		exec_start_us = 0;
     93 		if (CPU_ISSET(trace[i].cpu, &cpuset)) {
     94 			/* Task is running on little CPUs. */
     95 			if (!smalltask_ts_usec)
     96 				too_small_cpu_us += segment_us;
     97 		} else {
     98 			/* Task is running on big CPUs. */
     99 			if (smalltask_ts_usec) {
    100 				/*
    101 				 * Downmigration is accounted separately, so
    102 				 * only record mis-scheduled time here if it
    103 				 * happened after downmigration.
    104 				 */
    105 				if (downmigrate_ts_usec)
    106 					too_big_cpu_us += segment_us;
    107 			}
    108 		}
    109 		if (smalltask_ts_usec)
    110 			small_task_us += segment_us;
    111 		else
    112 			big_task_us += segment_us;
    113 	}
    114 
    115 	pct = (too_small_cpu_us * 100) / big_task_us;
    116 	rv |= (pct > MAX_INCORRECT_CLUSTER_PCT);
    117 	printf("Time incorrectly scheduled on small when task was big: "
    118 	       "%lld usec (%d%% of big task CPU time)\n", too_small_cpu_us,
    119 	       pct);
    120 	pct = (too_big_cpu_us * 100) / small_task_us;
    121 	rv |= (pct > MAX_INCORRECT_CLUSTER_PCT);
    122 	printf("Time incorrectly scheduled on big when task was small, after "
    123 	       "downmigration: %lld usec (%d%% of small task CPU time)\n",
    124 	       too_big_cpu_us, pct);
    125 
    126 	if (downmigrate_ts_usec) {
    127 		downmigrate_latency_usec = downmigrate_ts_usec -
    128 			smalltask_ts_usec;
    129 		printf("Downmigration latency: %lld usec\n",
    130 		       downmigrate_latency_usec);
    131 	} else {
    132 		printf("Task never downmigrated!\n");
    133 		downmigrate_latency_usec = UINT_MAX;
    134 	}
    135 
    136 	return (rv || downmigrate_latency_usec > MAX_DOWNMIGRATE_LATENCY_US);
    137 }
    138 
    139 static void run(void)
    140 {
    141 	pthread_t task_thread;
    142 
    143 	tst_res(TINFO, "Maximum incorrect cluster time percentage: %d%%",
    144 		MAX_INCORRECT_CLUSTER_PCT);
    145 	tst_res(TINFO, "Maximum downmigration latency: %d usec",
    146 		MAX_DOWNMIGRATE_LATENCY_US);
    147 
    148 	/* configure and enable tracing */
    149 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
    150 	SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
    151 	SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
    152 	SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
    153 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
    154 
    155 	SAFE_PTHREAD_CREATE(&task_thread, NULL, task_fn, NULL);
    156 	SAFE_PTHREAD_JOIN(task_thread, NULL);
    157 
    158 	/* disable tracing */
    159 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
    160 	LOAD_TRACE();
    161 
    162 	if (parse_results())
    163 		tst_res(TFAIL, "Task placement/migration latency goals "
    164 			"not met.\n");
    165 	else
    166 		tst_res(TPASS, "Task placement/migration latency goals "
    167 			"met.\n");
    168 }
    169 
    170 static struct tst_test test = {
    171 	.test_all = run,
    172 	.cleanup = trace_cleanup,
    173 };
    174