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 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 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 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