1 /* 2 * Copyright (c) 2018 Google, Inc. 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 * 6 * A DL task runs. Its execution pattern is checked to see that the constraints 7 * it has been given (runtime, period, deadline) are satisfied. 8 */ 9 10 #define _GNU_SOURCE 11 #include <errno.h> 12 #include <pthread.h> 13 #include <sched.h> 14 #include <semaphore.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 "lapi/syscalls.h" 22 #include "lapi/sched.h" 23 #include "trace_parse.h" 24 #include "util.h" 25 26 #define TRACE_EVENTS "sched_switch" 27 28 static int dl_task_tid; 29 30 /* 31 * It is not possible to restrict CPU affinity on SCHED_DEADLINE tasks, so we do 32 * not force the CFS and DL tasks to be on the same CPU in this test. CPUsets 33 * can be used to do this, perhaps implement that later. 34 */ 35 36 static void *dl_fn(void *arg LTP_ATTRIBUTE_UNUSED) 37 { 38 struct sched_attr attr; 39 struct timespec ts; 40 uint64_t now_usec, end_usec; 41 42 attr.size = sizeof(attr); 43 attr.sched_flags = 0; 44 attr.sched_nice = 0; 45 attr.sched_priority = 0; 46 47 attr.sched_policy = SCHED_DEADLINE; 48 attr.sched_runtime = 5000000; 49 attr.sched_period = 20000000; 50 attr.sched_deadline = 10000000; 51 52 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "DL START"); 53 ERROR_CHECK(sched_setattr(0, &attr, 0)); 54 55 dl_task_tid = gettid(); 56 57 if (clock_gettime(CLOCK_MONOTONIC, &ts)) { 58 printf("clock_gettime() reported an error\n"); 59 return NULL; 60 } 61 now_usec = (ts.tv_sec) * USEC_PER_SEC + (ts.tv_nsec / 1000); 62 end_usec = now_usec + 3 * USEC_PER_SEC; 63 while (now_usec < end_usec) { 64 /* Run for 5ms */ 65 burn(5000, 0); 66 67 /* Wait until next 20ms boundary. sched_yield() for DL tasks 68 * throttles the task until its next period. */ 69 sched_yield(); 70 if (clock_gettime(CLOCK_MONOTONIC, &ts)) { 71 printf("clock_gettime() reported an error\n"); 72 return NULL; 73 } 74 now_usec = (ts.tv_sec) * USEC_PER_SEC + (ts.tv_nsec / 1000); 75 } 76 77 return NULL; 78 } 79 80 static int parse_results(void) 81 { 82 int i; 83 unsigned long long next_period_ts_us = 0; 84 unsigned long long next_deadline_ts_us = 0; 85 unsigned long long start_ts_us = 0; 86 unsigned long long period_exec_time_us = 0; 87 int periods_parsed = 0; 88 89 for (i = 0; i < num_trace_records; i++) { 90 if (trace[i].event_type == TRACE_RECORD_SCHED_SWITCH) { 91 struct trace_sched_switch *t = trace[i].event_data; 92 if (t->prev_pid == dl_task_tid) { 93 unsigned long long end_ts_us; 94 if (!start_ts_us) { 95 printf("Trace parse error, " 96 "start_ts_us = 0\n"); 97 return -1; 98 } 99 if (TS_TO_USEC(trace[i].ts) > 100 next_period_ts_us) { 101 printf("Task ran past end of " 102 "period!\n"); 103 return -1; 104 } 105 end_ts_us = TS_TO_USEC(trace[i].ts); 106 if (end_ts_us > next_deadline_ts_us) 107 end_ts_us = next_deadline_ts_us; 108 if (start_ts_us > next_deadline_ts_us) 109 start_ts_us = next_deadline_ts_us; 110 period_exec_time_us += 111 (end_ts_us - start_ts_us); 112 start_ts_us = 0; 113 } 114 } 115 if (next_period_ts_us && 116 TS_TO_USEC(trace[i].ts) > next_period_ts_us) { 117 if (start_ts_us) { 118 printf("Task was running across period boundary!\n"); 119 return -1; 120 } 121 if (period_exec_time_us < 5000) { 122 printf("Missed deadline at %llu!\n", 123 next_deadline_ts_us); 124 return -1; 125 } 126 periods_parsed++; 127 next_deadline_ts_us += 20000; 128 next_period_ts_us += 20000; 129 } 130 if (trace[i].event_type == TRACE_RECORD_SCHED_SWITCH) { 131 struct trace_sched_switch *t = trace[i].event_data; 132 if (t->next_pid == dl_task_tid) { 133 134 if (start_ts_us) { 135 printf("Trace parse error, " 136 "start_ts_us != 0\n"); 137 return -1; 138 } 139 start_ts_us = TS_TO_USEC(trace[i].ts); 140 if (!next_period_ts_us) { 141 /* 142 * Initialize period and deadline the 143 * first time the DL task runs. 144 */ 145 next_period_ts_us = start_ts_us + 20000; 146 next_deadline_ts_us = start_ts_us + 147 10000; 148 } 149 } 150 } 151 } 152 if (periods_parsed >= 149) 153 printf("%d periods parsed successfully.\n", periods_parsed); 154 else 155 printf("Only %d periods parsed successfully.\n", 156 periods_parsed); 157 return 0; 158 } 159 160 static void run(void) 161 { 162 pthread_t dl_thread; 163 pthread_attr_t dl_thread_attrs; 164 struct sched_param dl_thread_sched_params; 165 166 ERROR_CHECK(pthread_attr_init(&dl_thread_attrs)); 167 ERROR_CHECK(pthread_attr_setinheritsched(&dl_thread_attrs, 168 PTHREAD_EXPLICIT_SCHED)); 169 ERROR_CHECK(pthread_attr_setschedpolicy(&dl_thread_attrs, 170 SCHED_FIFO)); 171 dl_thread_sched_params.sched_priority = 80; 172 ERROR_CHECK(pthread_attr_setschedparam(&dl_thread_attrs, 173 &dl_thread_sched_params)); 174 175 /* configure and enable tracing */ 176 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 177 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384"); 178 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS); 179 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n"); 180 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1"); 181 182 SAFE_PTHREAD_CREATE(&dl_thread, NULL, dl_fn, NULL); 183 SAFE_PTHREAD_JOIN(dl_thread, NULL); 184 185 /* disable tracing */ 186 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 187 LOAD_TRACE(); 188 189 if (parse_results()) 190 tst_res(TFAIL, "DL task did not execute as expected.\n"); 191 else 192 tst_res(TPASS, "DL task ran as expected.\n"); 193 } 194 195 static struct tst_test test = { 196 .test_all = run, 197 .cleanup = trace_cleanup, 198 }; 199