1 /*
2 * Copyright (c) 2018 Google, Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 *
6 * Two big and three small tasks execute. Task placement is verified.
7 */
8
9 #define _GNU_SOURCE
10 #include <errno.h>
11 #include <pthread.h>
12 #include <sched.h>
13 #include <time.h>
14
15 #include "tst_test.h"
16 #include "tst_safe_file_ops.h"
17 #include "tst_safe_pthread.h"
18
19 #include "trace_parse.h"
20 #include "util.h"
21
22 #define TRACE_EVENTS "sched_switch"
23
24 static int task_tids[5];
25
26 #define MAX_INCORRECT_CLUSTER_PCT 10
27 #define BURN_SEC 3
task_fn(void * arg)28 static void *task_fn(void *arg)
29 {
30 int id = (int *)arg - task_tids;
31
32 task_tids[id] = gettid();
33
34 /* Tasks 0-2 are small, 3-4 are big. */
35 burn(BURN_SEC * USEC_PER_SEC, id < 3 ? 1 : 0);
36
37 return NULL;
38 }
39
parse_results(void)40 static int parse_results(void)
41 {
42 int i, j, pct, rv = 0;
43 unsigned long long exec_start_us[5] = { 0, 0, 0, 0, 0 };
44 unsigned long long incorrect_us[5] = { 0, 0, 0, 0, 0 };
45 unsigned long long total_us[5] = { 0, 0, 0, 0, 0 };
46 cpu_set_t cpuset;
47
48 if (find_cpus_with_capacity(0, &cpuset)) {
49 printf("Failed to find the CPUs in the little cluster.\n");
50 return -1;
51 }
52
53 for (i = 0; i < num_trace_records; i++) {
54 struct trace_sched_switch *t = trace[i].event_data;
55 unsigned long long segment_us;
56
57 if (trace[i].event_type != TRACE_RECORD_SCHED_SWITCH)
58 continue;
59
60 /* Is this the start of an execution segment? */
61 for (j = 0; j < 5; j++) {
62 if (t->next_pid != task_tids[j])
63 continue;
64 /* Start of execution segment for task j */
65 if (exec_start_us[j]) {
66 printf("Trace parse fail: double exec start\n");
67 return -1;
68 }
69 exec_start_us[j] = TS_TO_USEC(trace[i].ts);
70 }
71
72 /* Is this the end of an execution segment? */
73 for (j = 0; j < 5; j++) {
74 if (t->prev_pid != task_tids[j])
75 continue;
76 /* End of execution segment for task j */
77 segment_us = TS_TO_USEC(trace[i].ts);
78 segment_us -= exec_start_us[j];
79 exec_start_us[j] = 0;
80 if (CPU_ISSET(trace[i].cpu, &cpuset) && j > 2)
81 incorrect_us[j] += segment_us;
82 if (!CPU_ISSET(trace[i].cpu, &cpuset) && j < 3)
83 incorrect_us[j] += segment_us;
84 total_us[j] += segment_us;
85
86 }
87 }
88
89 for (i = 0; i < 3; i++) {
90 pct = (incorrect_us[i] * 100) / total_us[i];
91 rv |= (pct > MAX_INCORRECT_CLUSTER_PCT);
92 printf("Total time little task scheduled: %lld Time scheduled "
93 "on big CPU: %lld (%d%%)\n", total_us[i],
94 incorrect_us[i], pct);
95 }
96 for (i = 3; i < 5; i++) {
97 pct = (incorrect_us[i] * 100) / total_us[i];
98 rv |= (pct > MAX_INCORRECT_CLUSTER_PCT);
99 printf("Total time big task scheduled: %lld Time scheduled on "
100 "little CPU: %lld (%d%%)\n", total_us[i],
101 incorrect_us[i], pct);
102 }
103
104 return rv;
105 }
106
107 #define NUM_TASKS 5
run(void)108 static void run(void)
109 {
110 int i;
111 pthread_t tasks[NUM_TASKS];
112
113 tst_res(TINFO, "Maximum incorrect cluster time percentage: %d%%",
114 MAX_INCORRECT_CLUSTER_PCT);
115
116 printf("Tasks running for %d sec\n", BURN_SEC);
117
118 /* configure and enable tracing */
119 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
120 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
121 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
122 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
123 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
124
125 for (i = 0; i < NUM_TASKS; i++)
126 SAFE_PTHREAD_CREATE(&tasks[i], NULL, task_fn, &task_tids[i]);
127 for (i = 0; i < NUM_TASKS; i++)
128 SAFE_PTHREAD_JOIN(tasks[i], NULL);
129
130 /* disable tracing */
131 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
132 LOAD_TRACE();
133
134 if (parse_results())
135 tst_res(TFAIL, "Task placement goals were not met.\n");
136 else
137 tst_res(TPASS, "Task placement goals were met.\n");
138 }
139
140 static struct tst_test test = {
141 .test_all = run,
142 .cleanup = trace_cleanup,
143 };
144