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