| /* |
| * Copyright (c) 2018 Google, Inc. |
| * |
| * SPDX-License-Identifier: GPL-2.0-or-later |
| * |
| * Two big and three small tasks execute. Task placement is verified. |
| */ |
| |
| #define _GNU_SOURCE |
| #include <errno.h> |
| #include <pthread.h> |
| #include <sched.h> |
| #include <time.h> |
| |
| #include "tst_test.h" |
| #include "tst_safe_file_ops.h" |
| #include "tst_safe_pthread.h" |
| |
| #include "trace_parse.h" |
| #include "util.h" |
| |
| #define TRACE_EVENTS "sched_switch" |
| |
| static int task_tids[5]; |
| |
| #define MAX_INCORRECT_CLUSTER_PCT 10 |
| #define BURN_SEC 3 |
| static void *task_fn(void *arg) |
| { |
| int id = (int *)arg - task_tids; |
| |
| task_tids[id] = gettid(); |
| |
| /* Tasks 0-2 are small, 3-4 are big. */ |
| burn(BURN_SEC * USEC_PER_SEC, id < 3 ? 1 : 0); |
| |
| return NULL; |
| } |
| |
| static int parse_results(void) |
| { |
| int i, j, pct, rv = 0; |
| unsigned long long exec_start_us[5] = { 0, 0, 0, 0, 0 }; |
| unsigned long long incorrect_us[5] = { 0, 0, 0, 0, 0 }; |
| unsigned long long total_us[5] = { 0, 0, 0, 0, 0 }; |
| cpu_set_t cpuset; |
| |
| if (find_cpus_with_capacity(0, &cpuset)) { |
| printf("Failed to find the CPUs in the little cluster.\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < num_trace_records; i++) { |
| struct trace_sched_switch *t = trace[i].event_data; |
| unsigned long long segment_us; |
| |
| if (trace[i].event_type != TRACE_RECORD_SCHED_SWITCH) |
| continue; |
| |
| /* Is this the start of an execution segment? */ |
| for (j = 0; j < 5; j++) { |
| if (t->next_pid != task_tids[j]) |
| continue; |
| /* Start of execution segment for task j */ |
| if (exec_start_us[j]) { |
| printf("Trace parse fail: double exec start\n"); |
| return -1; |
| } |
| exec_start_us[j] = TS_TO_USEC(trace[i].ts); |
| } |
| |
| /* Is this the end of an execution segment? */ |
| for (j = 0; j < 5; j++) { |
| if (t->prev_pid != task_tids[j]) |
| continue; |
| /* End of execution segment for task j */ |
| segment_us = TS_TO_USEC(trace[i].ts); |
| segment_us -= exec_start_us[j]; |
| exec_start_us[j] = 0; |
| if (CPU_ISSET(trace[i].cpu, &cpuset) && j > 2) |
| incorrect_us[j] += segment_us; |
| if (!CPU_ISSET(trace[i].cpu, &cpuset) && j < 3) |
| incorrect_us[j] += segment_us; |
| total_us[j] += segment_us; |
| |
| } |
| } |
| |
| for (i = 0; i < 3; i++) { |
| pct = (incorrect_us[i] * 100) / total_us[i]; |
| rv |= (pct > MAX_INCORRECT_CLUSTER_PCT); |
| printf("Total time little task scheduled: %lld Time scheduled " |
| "on big CPU: %lld (%d%%)\n", total_us[i], |
| incorrect_us[i], pct); |
| } |
| for (i = 3; i < 5; i++) { |
| pct = (incorrect_us[i] * 100) / total_us[i]; |
| rv |= (pct > MAX_INCORRECT_CLUSTER_PCT); |
| printf("Total time big task scheduled: %lld Time scheduled on " |
| "little CPU: %lld (%d%%)\n", total_us[i], |
| incorrect_us[i], pct); |
| } |
| |
| return rv; |
| } |
| |
| #define NUM_TASKS 5 |
| static void run(void) |
| { |
| int i; |
| pthread_t tasks[NUM_TASKS]; |
| |
| tst_res(TINFO, "Maximum incorrect cluster time percentage: %d%%", |
| MAX_INCORRECT_CLUSTER_PCT); |
| |
| printf("Tasks running for %d sec\n", BURN_SEC); |
| |
| /* configure and enable tracing */ |
| tracefs_write("tracing_on", "0"); |
| tracefs_write("buffer_size_kb", "16384"); |
| tracefs_write("set_event", TRACE_EVENTS); |
| tracefs_write("trace", "\n"); |
| tracefs_write("tracing_on", "1"); |
| |
| for (i = 0; i < NUM_TASKS; i++) |
| SAFE_PTHREAD_CREATE(&tasks[i], NULL, task_fn, &task_tids[i]); |
| for (i = 0; i < NUM_TASKS; i++) |
| SAFE_PTHREAD_JOIN(tasks[i], NULL); |
| |
| /* disable tracing */ |
| tracefs_write("tracing_on", "0"); |
| LOAD_TRACE(); |
| |
| if (parse_results()) |
| tst_res(TFAIL, "Task placement goals were not met.\n"); |
| else |
| tst_res(TPASS, "Task placement goals were met.\n"); |
| } |
| |
| static struct tst_test test = { |
| .test_all = run, |
| .setup = trace_setup, |
| .cleanup = trace_cleanup, |
| }; |