• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2018 Google, Inc.
3  *
4  * SPDX-License-Identifier: GPL-2.0-or-later
5  *
6  * A task executes as small then as big. Upmigration latency and task placement
7  * are verified.
8  */
9 
10 #define _GNU_SOURCE
11 #include <errno.h>
12 #include <pthread.h>
13 #include <sched.h>
14 #include <sys/types.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_switch"
25 
26 static int task_tid;
27 
28 #define MAX_UPMIGRATE_LATENCY_US 100000
29 #define MAX_INCORRECT_CLUSTER_PCT 10
30 #define BURN_SEC 3
task_fn(void * arg LTP_ATTRIBUTE_UNUSED)31 static void *task_fn(void *arg LTP_ATTRIBUTE_UNUSED)
32 {
33 	task_tid = gettid();
34 
35 	printf("Small task executing for %ds...\n", BURN_SEC);
36 	burn(BURN_SEC * USEC_PER_SEC, 1);
37 
38 	printf("Changing to big task...\n");
39 	SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "CPU HOG");
40 	burn(BURN_SEC * USEC_PER_SEC, 0);
41 
42 	return NULL;
43 }
44 
parse_results(void)45 static int parse_results(void)
46 {
47 	int i, pct, rv = 0;
48 	unsigned long long exec_start_us = 0;
49 	unsigned long long too_big_cpu_us = 0;
50 	unsigned long long too_small_cpu_us = 0;
51 	unsigned long long small_task_us = 0;
52 	unsigned long long big_task_us = 0;
53 	unsigned long long cpuhog_ts_usec = 0;
54 	unsigned long long upmigrate_ts_usec = 0;
55 	unsigned long long upmigrate_latency_usec = 0;
56 	cpu_set_t cpuset;
57 
58 	if (find_cpus_with_capacity(0, &cpuset)) {
59 		printf("Failed to find the CPUs in the little cluster.\n");
60 		return -1;
61 	}
62 
63 	for (i = 0; i < num_trace_records; i++) {
64 		unsigned long long segment_us;
65 		struct trace_sched_switch *t = trace[i].event_data;
66 
67 		if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE &&
68 			   !strcmp(trace[i].event_data, "CPU HOG")) {
69 			cpuhog_ts_usec = TS_TO_USEC(trace[i].ts);
70 			continue;
71 		}
72 
73 		if (trace[i].event_type != TRACE_RECORD_SCHED_SWITCH)
74 			continue;
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 (cpuhog_ts_usec && !upmigrate_ts_usec &&
83 			    !CPU_ISSET(trace[i].cpu, &cpuset))
84 				upmigrate_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 (cpuhog_ts_usec) {
96 				/*
97 				 * Upmigration is accounted separately, so only
98 				 * record mis-scheduled time here if it happened
99 				 * after upmigration.
100 				 */
101 				if (upmigrate_ts_usec)
102 					too_small_cpu_us += segment_us;
103 			}
104 		} else {
105 			/* Task is running on big CPUs. */
106 			if (!cpuhog_ts_usec)
107 				too_big_cpu_us += segment_us;
108 		}
109 		if (cpuhog_ts_usec)
110 			big_task_us += segment_us;
111 		else
112 			small_task_us += segment_us;
113 	}
114 
115 	pct = (too_big_cpu_us * 100) / small_task_us;
116 	rv |= (pct > MAX_INCORRECT_CLUSTER_PCT);
117 	printf("Time incorrectly scheduled on big when task was small: "
118 	       "%lld usec (%d%% of small task CPU time)\n", too_big_cpu_us,
119 	       pct);
120 	pct = (too_small_cpu_us * 100) / big_task_us;
121 	rv |= (pct > MAX_INCORRECT_CLUSTER_PCT);
122 	printf("Time incorrectly scheduled on small when task was big, "
123 	       "after upmigration: "
124 	       "%lld usec (%d%% of big task CPU time)\n", too_small_cpu_us,
125 	       pct);
126 
127 	if (upmigrate_ts_usec) {
128 		upmigrate_latency_usec = upmigrate_ts_usec - cpuhog_ts_usec;
129 		printf("Upmigration latency: %lld usec\n",
130 		       upmigrate_latency_usec);
131 	} else {
132 		printf("Task never upmigrated!\n");
133 		upmigrate_latency_usec = UINT_MAX;
134 	}
135 
136 	return (rv || upmigrate_latency_usec > MAX_UPMIGRATE_LATENCY_US);
137 }
138 
run(void)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 upmigration latency: %d usec",
146 		MAX_UPMIGRATE_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 and migration latency goals "
164 			"were not met.\n");
165 	else
166 		tst_res(TPASS, "Task placement and migration latency goals "
167 			"were met.\n");
168 }
169 
170 static struct tst_test test = {
171 	.test_all = run,
172 	.cleanup = trace_cleanup,
173 };
174