diff --git a/SConstruct b/SConstruct index c41e41e..9935396 100644 --- a/SConstruct +++ b/SConstruct @@ -208,6 +208,13 @@ rt.Program('base_task', 'bin/base_task.c') mtrt.Program('base_mt_task', 'bin/base_mt_task.c') rt.Program('rt_launch', ['bin/rt_launch.c', 'bin/common.c']) rt.Program('rtspin', ['bin/rtspin.c', 'bin/common.c']) +rt.Program('rtspin_edffm', ['bin/rtspin_edffm.c', 'bin/common.c']) +rt.Program('rt_launch_edffm', ['bin/rt_launch_edffm.c', 'bin/common.c']) +rt.Program('rtspin_npsf', ['bin/rtspin_npsf.c', 'bin/common.c']) +rt.Program('npsf_add_server', ['bin/npsf_add_server.c', 'bin/common.c']) +rt.Program('rt_launch_npsf', ['bin/rt_launch_npsf.c', 'bin/common.c']) +rt.Program('rtspin_edfwm', ['bin/rtspin_edfwm.c', 'bin/common.c']) +rt.Program('rt_launch_edfwm', ['bin/rt_launch_edfwm.c', 'bin/common.c']) rt.Program('release_ts', 'bin/release_ts.c') rtm.Program('measure_syscall', 'bin/null_call.c') diff --git a/bin/common.c b/bin/common.c index 452b882..167344a 100644 --- a/bin/common.c +++ b/bin/common.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "common.h" @@ -9,3 +10,120 @@ void bail_out(const char* msg) perror(msg); exit(-1 * errno); } + +/* EDF-WM helper functions to parse a custom text file format to "easily" + * launch tests with rtspin and rt_launch: + * + * Format for task: + * + * . + * + * If the task is split on multiple slices, slices_number is non 0 + * and we scan a list of slice parameters up to slices_number: + * + * Format for slices: + * + * . + * + * The offset is the start time for the slice relative to the job release. + * + * Example: + * 14 2.26245771754 10 0 5 2 + * 14 5 5.000000 1.497306 0.000000 + * 14 7 10.000000 0.765152 5.000000 + */ + +#define fms_to_ns(x) (lt_t)(((x) * __NS_PER_MS)) +/* + * . + */ +int parse_edfwm_slice(FILE *ts, int slices_no, int task_id, + struct rt_task *rt) +{ + int i, tid; + unsigned int cpu; + double deadline, budget, offset; + + lt_t total_budget = 0; + + struct edf_wm_params* wm = (struct edf_wm_params*) &rt->semi_part; + + for (i = 0; i < slices_no; i++) { + + if (fscanf(ts, "%d %u %lf %lf %lf\n", &tid, &cpu, + &deadline, &budget, &offset) != EOF) { + + if (task_id != tid) { + fprintf(stderr, "task_id %d != tid %d\n", + task_id, tid); + return -1; + } + + wm->slices[i].deadline = fms_to_ns(deadline); + wm->slices[i].budget = fms_to_ns(budget); + wm->slices[i].offset = fms_to_ns(offset); + wm->slices[i].cpu = cpu; + + printf("slice(tid, cpu, d, e, ph) = (%d, %u, %llu, %llu, %llu)\n", + tid, cpu, wm->slices[i].deadline, + wm->slices[i].budget, wm->slices[i].offset); + + total_budget += wm->slices[i].budget; + if (wm->slices[i].budget < MIN_EDF_WM_SLICE_SIZE) { + + fprintf(stderr, "Slice %llu is too small\n", + wm->slices[i].budget); + return -1; + } + } + + if (ferror(ts)) { + fprintf(stderr, "Cannot read file\n"); + return -1; + } + } + wm->count = slices_no; + rt->exec_cost = total_budget; + printf("--- total %u slices ---\n", wm->count); + return 0; +} + +/* + * . + */ +int parse_edfwm_ts_file(FILE *ts, struct rt_task *rt) +{ + int task_id, ret = 1; + unsigned int cpu, sliceno; + double fwcet, fperiod, fphase; + + ret = fscanf(ts, "%d %lf %lf %lf %d %d\n", + &task_id, &fwcet, &fperiod, &fphase, &cpu, &sliceno); + + if (ferror(ts)) + goto err; + + rt->exec_cost = fms_to_ns(fwcet); + rt->period = fms_to_ns(fperiod); + rt->phase = fms_to_ns(fphase); + rt->cpu = cpu; + rt->cls = RT_CLASS_HARD; + rt->budget_policy = PRECISE_ENFORCEMENT; + + printf("(tid, wcet, period, ph, cpu, slices) = " + "(%d, %llu, %llu, %llu, %u, %u)\n", + task_id, rt->exec_cost, rt->period, rt->phase, cpu, sliceno); + if (sliceno > 0) { + memset(&rt->semi_part, 0, sizeof(struct edf_wm_params)); + ret = parse_edfwm_slice(ts, sliceno, task_id, rt); + if (ret < 0) + goto err; + } + + return 0; + +err: + fprintf(stderr, "Error parsing file\n"); + return -1; +} + diff --git a/bin/npsf_add_server.c b/bin/npsf_add_server.c new file mode 100644 index 0000000..9f3f92c --- /dev/null +++ b/bin/npsf_add_server.c @@ -0,0 +1,108 @@ +/* wrapper for sys_add_server + * + * Input: a file with on each line: + * npsf_id cpu budget(us) + */ +#include +#include +#include + +#include "litmus.h" +#include "common.h" + +void usage(char *error) { + fprintf(stderr, "Error: %s\n", error); + fprintf(stderr, + "Usage: npsf_add_server SERVERS-FILE MAX-SPLITS-PER-NPSFID\n"); + exit(1); +} + +int main(int argc, char** argv) +{ + int ret; + FILE *file; + int i,j; + int npsf_id, curr_id = -1; + int cpu, max_splits_server; + int budget_us; + struct npsf_budgets *budgets; + + if (argc < 3) + usage("Arguments missing."); + + max_splits_server = atoi(argv[2]); + + if ((file = fopen(argv[1], "r")) == NULL) { + fprintf(stderr, "Cannot open %s\n", argv[1]); + return -1; + } + + /* format: npsf_id cpu budget-us */ + i = 0; + while (fscanf(file, "%d %d %d\n", &npsf_id, &cpu, &budget_us) != EOF) { + + printf("Read: %d %d %d\n", npsf_id, cpu, budget_us); + + if (curr_id == -1) { + curr_id = npsf_id; + budgets = malloc(max_splits_server * + sizeof(struct npsf_budgets)); + for(j = 0; j < max_splits_server; j++) { + budgets[j].cpu = -1; + budgets[j].budget = 0; + } + } + + if (npsf_id == curr_id) { + /* same notional processor, different cpu and budget */ + budgets[i].cpu = cpu; + budgets[i].budget = (lt_t) (budget_us * 1000); + i++; + } else { + /* different notional processor */ + /* add server */ + printf("Adding npsf_id = %d\n", curr_id); + ret = add_server(&curr_id, budgets, 0); + + if (ret < 0) { + fclose(file); + free(budgets); + printf("Cannot add Notional Processor %d\n", + curr_id); + return ret; + } + + /* reinit new */ + i = 0; + budgets = malloc(max_splits_server * + sizeof(struct npsf_budgets)); + for(j = 0; j < max_splits_server; j++) { + budgets[j].cpu = -1; + budgets[j].budget = 0; + } + curr_id = npsf_id; + budgets[i].cpu = cpu; + budgets[i].budget = (lt_t) (budget_us * 1000); + i++; + } + } + + if (ferror(file)) { + fprintf(stderr, "Error while reading\n"); + fclose(file); + return -1; + } + + /* save the last entry */ + ret = add_server(&curr_id, budgets, 1); + printf("Adding npsf_id = %d\n", curr_id); + if (ret < 0) { + fclose(file); + free(budgets); + bail_out("Cannot add Notional Processor: "); + } + + fclose(file); + + return 0; +} diff --git a/bin/rt_launch_edffm.c b/bin/rt_launch_edffm.c new file mode 100644 index 0000000..ddde7dd --- /dev/null +++ b/bin/rt_launch_edffm.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include + +#include "litmus.h" +#include "common.h" + +typedef struct { + int wait; + char * exec_path; + char ** argv; +} startup_info_t; + + +int launch(void *task_info_p) { + startup_info_t *info = (startup_info_t*) task_info_p; + int ret; + if (info->wait) { + ret = wait_for_ts_release(); + if (ret != 0) + perror("wait_for_ts_release()"); + } + ret = execvp(info->exec_path, info->argv); + perror("execv failed"); + return ret; +} + +void usage(char *error) { + fprintf(stderr, "%s\nUsage: rt_launch [-w][-v][-p cpu][-c hrt | srt | be] wcet period" + " fracnum1 fracden1 cpu1 fracnum2 fracden2 cpu2 program [arg1 arg2 ...]\n" + "\t-w\tSynchronous release\n" + "\t-v\tVerbose\n" + "\t-p\tcpu (or initial cpu)\n" + "\t-c\tClass\n" + "\twcet, period in ms\n" + "\tprogram to be launched\n", + error); + exit(1); +} + + +#define OPTSTR "p:c:vw" + +int main(int argc, char** argv) +{ + int ret; + lt_t wcet; + lt_t period; + /* [num,den] */ + lt_t frac1[2], frac2[2]; + int cpu1, cpu2; + int migrate = 0; + int cpu = 0; + int opt; + int verbose = 0; + int wait = 0; + startup_info_t info; + task_class_t class = RT_CLASS_HARD; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'w': + wait = 1; + break; + case 'v': + verbose = 1; + break; + case 'p': + cpu = atoi(optarg); + migrate = 1; + break; + case 'c': + class = str2class(optarg); + if (class == -1) + usage("Unknown task class."); + break; + + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + signal(SIGUSR1, SIG_IGN); + + if (argc - optind < 8) + usage("Arguments missing."); + wcet = ms2lt(atoi(argv[optind + 0])); + period = ms2lt(atoi(argv[optind + 1])); + /* frac num, den = 0 means fixed task */ + frac1[0] = atoi(argv[optind + 2]); + frac1[1] = atoi(argv[optind + 3]); + cpu1 = atoi(argv[optind + 4]); + frac2[0] = atoi(argv[optind + 5]); + frac2[1] = atoi(argv[optind + 6]); + cpu2 = atoi(argv[optind + 7]); + if (wcet <= 0) + usage("The worst-case execution time must be a " + "positive number."); + if (period <= 0) + usage("The period must be a positive number."); + if (wcet > period) { + usage("The worst-case execution time must not " + "exceed the period."); + } + info.exec_path = argv[optind + 8]; + info.argv = argv + optind + 8; + info.wait = wait; + if (migrate) { + ret = be_migrate_to(cpu); + if (ret < 0) + bail_out("could not migrate to target partition"); + } + /* create in src/task.c a new wrapper for the __launch_rt_task + * which takes the fraction and the cpus */ + ret = __create_rt_task_edffm(launch, &info, cpu, wcet, period, frac1, + frac2, cpu1, cpu2, class); + + + if (ret < 0) + bail_out("could not create rt child process"); + else if (verbose) + printf("%d\n", ret); + + return 0; +} diff --git a/bin/rt_launch_edfwm.c b/bin/rt_launch_edfwm.c new file mode 100644 index 0000000..9e8a322 --- /dev/null +++ b/bin/rt_launch_edfwm.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include + +#include "litmus.h" +#include "common.h" + +typedef struct { + int wait; + char * exec_path; + char ** argv; +} startup_info_t; + + +int launch(void *task_info_p) { + startup_info_t *info = (startup_info_t*) task_info_p; + int ret; + if (info->wait) { + ret = wait_for_ts_release(); + if (ret != 0) + perror("wait_for_ts_release()"); + } + ret = execvp(info->exec_path, info->argv); + perror("execv failed"); + return ret; +} + +void usage(char *error) { + fprintf(stderr, "%s\nUsage: rt_launch [-w] task_parameters_file " + "program [arg1 arg2 ...]\n" + "\t-w\tSynchronous release\n" + "\tprogram to be launched\n", + error); + exit(1); +} + +#define OPTSTR "w" + +int main(int argc, char** argv) +{ + int ret; + int wait = 0; + int opt; + startup_info_t info; + + struct rt_task rt; + FILE *file; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'w': + wait = 1; + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + signal(SIGUSR1, SIG_IGN); + + if (argc - optind < 2) + usage("Arguments missing."); + + if ((file = fopen(argv[optind + 0], "r")) == NULL) { + fprintf(stderr, "Cannot open %s\n", argv[1]); + return -1; + } + + memset(&rt, 0, sizeof(struct rt_task)); + + if (parse_edfwm_ts_file(file, &rt) < 0) + bail_out("Could not parse file\n"); + + if (sporadic_task_ns_semi(&rt) < 0) + bail_out("could not setup rt task params"); + + fclose(file); + + info.exec_path = argv[optind + 1]; + info.argv = argv + optind + 1; + info.wait = wait; + + ret = create_rt_task_semi(launch, &info, &rt); + + if (ret < 0) + bail_out("could not create rt child process"); + + return 0; +} + diff --git a/bin/rt_launch_npsf.c b/bin/rt_launch_npsf.c new file mode 100644 index 0000000..97ad361 --- /dev/null +++ b/bin/rt_launch_npsf.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include + +#include "litmus.h" +#include "common.h" + +typedef struct { + int wait; + char * exec_path; + char ** argv; +} startup_info_t; + + +int launch(void *task_info_p) { + startup_info_t *info = (startup_info_t*) task_info_p; + int ret; + if (info->wait) { + ret = wait_for_ts_release(); + if (ret != 0) + perror("wait_for_ts_release()"); + } + ret = execvp(info->exec_path, info->argv); + perror("execv failed"); + return ret; +} + +void usage(char *error) { + fprintf(stderr, "%s\nUsage: rt_launch [-w][-v] wcet period cpu npsf-id program [arg1 arg2 ...]\n" + "\t-w\tSynchronous release\n" + "\t-v\tVerbose\n" + "\twcet, period in ms\n" + "\tprogram to be launched\n", + error); + exit(1); +} + + +#define OPTSTR "vw" + +int main(int argc, char** argv) +{ + int ret; + lt_t wcet; + lt_t period; + int migrate = 0; + int cpu = 0; + int npsf_id; + int opt; + int verbose = 0; + int wait = 0; + startup_info_t info; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'w': + wait = 1; + break; + case 'v': + verbose = 1; + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + signal(SIGUSR1, SIG_IGN); + + if (argc - optind < 5) + usage("Arguments missing."); + wcet = ms2lt(atoi(argv[optind + 0])); + period = ms2lt(atoi(argv[optind + 1])); + cpu = atoi(argv[optind + 2]); + migrate = 1; + npsf_id = atoi(argv[optind + 3]); + if (wcet <= 0) + usage("The worst-case execution time must be a " + "positive number."); + if (period <= 0) + usage("The period must be a positive number."); + if (wcet > period) { + usage("The worst-case execution time must not " + "exceed the period."); + } + info.exec_path = argv[optind + 4]; + info.argv = argv + optind + 4; + info.wait = wait; + if (migrate) { + ret = be_migrate_to(cpu); + if (ret < 0) + bail_out("could not migrate to target partition"); + } + ret = __create_rt_task_npsf(launch, &info, cpu, wcet, period, npsf_id, RT_CLASS_HARD); + + + if (ret < 0) + bail_out("could not create rt child process"); + else if (verbose) + printf("%d\n", ret); + + return 0; +} diff --git a/bin/rtspin_edffm.c b/bin/rtspin_edffm.c new file mode 100644 index 0000000..5db79b8 --- /dev/null +++ b/bin/rtspin_edffm.c @@ -0,0 +1,263 @@ +#include + +#include +#include +#include +#include + + +#include "litmus.h" +#include "common.h" + + +static double cputime() +{ + struct timespec ts; + int err; + err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + if (err != 0) + perror("clock_gettime"); + return (ts.tv_sec + 1E-9 * ts.tv_nsec); +} + +static double wctime() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec + 1E-6 * tv.tv_usec); +} + +void usage(char *error) { + fprintf(stderr, "Error: %s\n", error); + fprintf(stderr, + "Usage: rt_spin [-w] [-p PARTITION] [-c CLASS] WCET PERIOD DURATION fracnum1 fracden1 cpu1 fracnum2 fracden2 cpu2\n" + " rt_spin -l\n"); + exit(1); +} + +#define NUMS 4096 +static int num[NUMS]; +static double loop_length = 1.0; +static char* progname; + +static int loop_once(void) +{ + int i, j = 0; + for (i = 0; i < NUMS; i++) + j += num[i]++; + return j; +} + +static int loop_for(double exec_time) +{ + double t = 0; + int tmp = 0; +/* while (t + loop_length < exec_time) { + tmp += loop_once(); + t += loop_length; + } +*/ + double start = cputime(); + double now = cputime(); + while (now + loop_length < start + exec_time) { + tmp += loop_once(); + t += loop_length; + now = cputime(); + } + + return tmp; +} + +static void fine_tune(double interval) +{ + double start, end, delta; + + start = wctime(); + loop_for(interval); + end = wctime(); + delta = (end - start - interval) / interval; + if (delta != 0) + loop_length = loop_length / (1 - delta); +} + +static void configure_loop(void) +{ + double start; + + /* prime cache */ + loop_once(); + loop_once(); + loop_once(); + + /* measure */ + start = wctime(); + loop_once(); /* hope we didn't get preempted */ + loop_length = wctime(); + loop_length -= start; + + /* fine tune */ + fine_tune(0.1); + fine_tune(0.1); + fine_tune(0.1); +} + +static void show_loop_length(void) +{ + printf("%s/%d: loop_length=%f (%ldus)\n", + progname, getpid(), loop_length, + (long) (loop_length * 1000000)); +} + +static void debug_delay_loop(void) +{ + double start, end, delay; + show_loop_length(); + while (1) { + for (delay = 0.5; delay > 0.01; delay -= 0.01) { + start = wctime(); + loop_for(delay); + end = wctime(); + printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n", + delay, + end - start, + end - start - delay, + 100 * (end - start - delay) / delay); + } + } +} + +static int job(double exec_time) +{ + loop_for(exec_time); + sleep_next_period(); + return 0; +} + +#define OPTSTR "p:c:wld:v" + +int main(int argc, char** argv) +{ + int ret; + lt_t wcet; + lt_t period; + /* [num,den] */ + lt_t frac1[2], frac2[2]; + int cpu1, cpu2; + double wcet_ms, period_ms; + int migrate = 0; + int cpu = 0; + int opt; + int wait = 0; + int test_loop = 0; + int skip_config = 0; + int verbose = 0; + double duration, start; + task_class_t class = RT_CLASS_HARD; + + progname = argv[0]; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'w': + wait = 1; + break; + case 'p': + cpu = atoi(optarg); + migrate = 1; + break; + case 'c': + class = str2class(optarg); + if (class == -1) + usage("Unknown task class."); + break; + case 'l': + test_loop = 1; + break; + case 'd': + /* manually configure delay per loop iteration + * unit: microseconds */ + loop_length = atof(optarg) / 1000000; + skip_config = 1; + break; + case 'v': + verbose = 1; + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + + if (!skip_config) + configure_loop(); + + if (test_loop) { + debug_delay_loop(); + return 0; + } + + if (argc - optind < 9) + usage("Arguments missing."); + wcet_ms = atof(argv[optind + 0]); + period_ms = atof(argv[optind + 1]); + duration = atof(argv[optind + 2]); + /* frac num, den = 0 means fixed task */ + frac1[0] = atoi(argv[optind + 3]); + frac1[1] = atoi(argv[optind + 4]); + cpu1 = atoi(argv[optind + 5]); + frac2[0] = atoi(argv[optind + 6]); + frac2[1] = atoi(argv[optind + 7]); + cpu2 = atoi(argv[optind + 8]); + wcet = wcet_ms * __NS_PER_MS; + period = period_ms * __NS_PER_MS; + if (wcet <= 0) + usage("The worst-case execution time must be a " + "positive number."); + if (period <= 0) + usage("The period must be a positive number."); + if (wcet > period) { + usage("The worst-case execution time must not " + "exceed the period."); + } + + if (migrate) { + ret = be_migrate_to(cpu); + if (ret < 0) + bail_out("could not migrate to target partition"); + } + + ret = sporadic_task_ns_edffm(wcet, period, 0, cpu, + frac1, frac2, cpu1, cpu2, + class, NO_ENFORCEMENT, migrate); + + if (ret < 0) + bail_out("could not setup rt task params"); + + if (verbose) + show_loop_length(); + + init_litmus(); + + ret = task_mode(LITMUS_RT_TASK); + if (ret != 0) + bail_out("could not become RT task"); + + if (wait) { + ret = wait_for_ts_release(); + if (ret != 0) + bail_out("wait_for_ts_release()"); + } + + start = wctime(); + + while (start + duration > wctime()) { + job(wcet_ms * 0.0009); /* 90% wcet, in seconds */ + } + + return 0; +} diff --git a/bin/rtspin_edfwm.c b/bin/rtspin_edfwm.c new file mode 100644 index 0000000..21a5f3b --- /dev/null +++ b/bin/rtspin_edfwm.c @@ -0,0 +1,233 @@ +#include + +#include +#include +#include +#include +#include + +#include "litmus.h" +#include "common.h" + + +static double cputime() +{ + struct timespec ts; + int err; + err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + if (err != 0) + perror("clock_gettime"); + return (ts.tv_sec + 1E-9 * ts.tv_nsec); +} + +static double wctime() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec + 1E-6 * tv.tv_usec); +} + +void usage(char *error) { + fprintf(stderr, "Error: %s\n", error); + fprintf(stderr, + "Usage: rt_spin [-w] task_parameters_file duration\n" + " rt_spin -l\n"); + exit(1); +} + +#define NUMS 4096 +static int num[NUMS]; +static double loop_length = 1.0; +static char* progname; + +static int loop_once(void) +{ + int i, j = 0; + for (i = 0; i < NUMS; i++) + j += num[i]++; + return j; +} + +static int loop_for(double exec_time) +{ + double t = 0; + int tmp = 0; +/* while (t + loop_length < exec_time) { + tmp += loop_once(); + t += loop_length; + } +*/ + double start = cputime(); + double now = cputime(); + while (now + loop_length < start + exec_time) { + tmp += loop_once(); + t += loop_length; + now = cputime(); + } + + return tmp; +} + +static void fine_tune(double interval) +{ + double start, end, delta; + + start = wctime(); + loop_for(interval); + end = wctime(); + delta = (end - start - interval) / interval; + if (delta != 0) + loop_length = loop_length / (1 - delta); +} + +static void configure_loop(void) +{ + double start; + + /* prime cache */ + loop_once(); + loop_once(); + loop_once(); + + /* measure */ + start = wctime(); + loop_once(); /* hope we didn't get preempted */ + loop_length = wctime(); + loop_length -= start; + + /* fine tune */ + fine_tune(0.1); + fine_tune(0.1); + fine_tune(0.1); +} + +static void show_loop_length(void) +{ + printf("%s/%d: loop_length=%f (%ldus)\n", + progname, getpid(), loop_length, + (long) (loop_length * 1000000)); +} + +static void debug_delay_loop(void) +{ + double start, end, delay; + show_loop_length(); + while (1) { + for (delay = 0.5; delay > 0.01; delay -= 0.01) { + start = wctime(); + loop_for(delay); + end = wctime(); + printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n", + delay, + end - start, + end - start - delay, + 100 * (end - start - delay) / delay); + } + } +} + +static int job(double exec_time) +{ + loop_for(exec_time); + sleep_next_period(); + return 0; +} + +#define OPTSTR "wld:v" + +int main(int argc, char** argv) +{ + int ret; + + int opt; + int wait = 0; + int test_loop = 0; + int skip_config = 0; + int verbose = 0; + double wcet_ms; + double duration, start; + + struct rt_task rt; + FILE *file; + + progname = argv[0]; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'w': + wait = 1; + break; + case 'l': + test_loop = 1; + break; + case 'd': + /* manually configure delay per loop iteration + * unit: microseconds */ + loop_length = atof(optarg) / 1000000; + skip_config = 1; + break; + case 'v': + verbose = 1; + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + + if (!skip_config) + configure_loop(); + + if (test_loop) { + debug_delay_loop(); + return 0; + } + + if (argc - optind < 2) + usage("Arguments missing."); + + if ((file = fopen(argv[optind + 0], "r")) == NULL) { + fprintf(stderr, "Cannot open %s\n", argv[1]); + return -1; + } + duration = atof(argv[optind + 1]); + + memset(&rt, 0, sizeof(struct rt_task)); + + if (parse_edfwm_ts_file(file, &rt) < 0) + bail_out("Could not parse file\n"); + + if (sporadic_task_ns_semi(&rt) < 0) + bail_out("could not setup rt task params"); + + fclose(file); + + if (verbose) + show_loop_length(); + + init_litmus(); + + ret = task_mode(LITMUS_RT_TASK); + if (ret != 0) + bail_out("could not become RT task"); + + if (wait) { + ret = wait_for_ts_release(); + if (ret != 0) + bail_out("wait_for_ts_release()"); + } + + wcet_ms = ((double) rt.exec_cost ) / __NS_PER_MS; + start = wctime(); + + while (start + duration > wctime()) { + job(wcet_ms * 0.0009); /* 90% wcet, in seconds */ + } + + return 0; +} diff --git a/bin/rtspin_npsf.c b/bin/rtspin_npsf.c new file mode 100644 index 0000000..d5dff3d --- /dev/null +++ b/bin/rtspin_npsf.c @@ -0,0 +1,252 @@ +#include + +#include +#include +#include +#include + + +#include "litmus.h" +#include "common.h" + + +static double cputime() +{ + struct timespec ts; + int err; + err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + if (err != 0) + perror("clock_gettime"); + return (ts.tv_sec + 1E-9 * ts.tv_nsec); +} + +static double wctime() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec + 1E-6 * tv.tv_usec); +} + +void usage(char *error) { + fprintf(stderr, "Error: %s\n", error); + fprintf(stderr, + "Usage: rt_spin [-w] WCET PERIOD DURATION CPU NPSF-ID\n" + " rt_spin -l\n"); + exit(1); +} + +#define NUMS 4096 +static int num[NUMS]; +static double loop_length = 1.0; +static char* progname; + +static int loop_once(void) +{ + int i, j = 0; + for (i = 0; i < NUMS; i++) + j += num[i]++; + return j; +} + +static int loop_for(double exec_time) +{ + double t = 0; + int tmp = 0; +/* while (t + loop_length < exec_time) { + tmp += loop_once(); + t += loop_length; + } +*/ + double start = cputime(); + double now = cputime(); + while (now + loop_length < start + exec_time) { + tmp += loop_once(); + t += loop_length; + now = cputime(); + } + + return tmp; +} + +static void fine_tune(double interval) +{ + double start, end, delta; + + start = wctime(); + loop_for(interval); + end = wctime(); + delta = (end - start - interval) / interval; + if (delta != 0) + loop_length = loop_length / (1 - delta); +} + +static void configure_loop(void) +{ + double start; + + /* prime cache */ + loop_once(); + loop_once(); + loop_once(); + + /* measure */ + start = wctime(); + loop_once(); /* hope we didn't get preempted */ + loop_length = wctime(); + loop_length -= start; + + /* fine tune */ + fine_tune(0.1); + fine_tune(0.1); + fine_tune(0.1); +} + +static void show_loop_length(void) +{ + printf("%s/%d: loop_length=%f (%ldus)\n", + progname, getpid(), loop_length, + (long) (loop_length * 1000000)); +} + +static void debug_delay_loop(void) +{ + double start, end, delay; + show_loop_length(); + while (1) { + for (delay = 0.5; delay > 0.01; delay -= 0.01) { + start = wctime(); + loop_for(delay); + end = wctime(); + printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n", + delay, + end - start, + end - start - delay, + 100 * (end - start - delay) / delay); + } + } +} + +static int job(double exec_time) +{ + loop_for(exec_time); + sleep_next_period(); + return 0; +} + +#define OPTSTR "c:wld:v" + +int main(int argc, char** argv) +{ + int ret; + lt_t wcet; + lt_t period; + double wcet_ms, period_ms; + int migrate = 0; + int cpu = 0; + int npsf_id = 0; + int opt; + int wait = 0; + int test_loop = 0; + int skip_config = 0; + int verbose = 0; + double duration, start; + task_class_t class = RT_CLASS_HARD; + + progname = argv[0]; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'w': + wait = 1; + break; + case 'c': + class = str2class(optarg); + if (class == -1) + usage("Unknown task class."); + break; + case 'l': + test_loop = 1; + break; + case 'd': + /* manually configure delay per loop iteration + * unit: microseconds */ + loop_length = atof(optarg) / 1000000; + skip_config = 1; + break; + case 'v': + verbose = 1; + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + + if (!skip_config) + configure_loop(); + + if (test_loop) { + debug_delay_loop(); + return 0; + } + + if (argc - optind < 5) + usage("Arguments missing."); + wcet_ms = atof(argv[optind + 0]); + period_ms = atof(argv[optind + 1]); + duration = atof(argv[optind + 2]); + cpu = atoi(argv[optind + 3]); + migrate = 1; + npsf_id = atoi(argv[optind + 4]); + wcet = wcet_ms * __NS_PER_MS; + period = period_ms * __NS_PER_MS; + if (wcet <= 0) + usage("The worst-case execution time must be a " + "positive number."); + if (period <= 0) + usage("The period must be a positive number."); + if (wcet > period) { + usage("The worst-case execution time must not " + "exceed the period."); + } + + if (migrate) { + ret = be_migrate_to(cpu); + if (ret < 0) + bail_out("could not migrate to target partition"); + } + + ret = sporadic_task_ns_npsf(wcet, period, 0, cpu, class, npsf_id, + NO_ENFORCEMENT, migrate); + + if (ret < 0) + bail_out("could not setup rt task params"); + + if (verbose) + show_loop_length(); + + init_litmus(); + + ret = task_mode(LITMUS_RT_TASK); + if (ret != 0) + bail_out("could not become RT task"); + + if (wait) { + ret = wait_for_ts_release(); + if (ret != 0) + bail_out("wait_for_ts_release()"); + } + + start = wctime(); + + while (start + duration > wctime()) { + job(wcet_ms * 0.0009); /* 90% wcet, in seconds */ + } + + return 0; +} diff --git a/include/common.h b/include/common.h index d1234ba..dbcfd34 100644 --- a/include/common.h +++ b/include/common.h @@ -1,7 +1,11 @@ #ifndef COMMON_H #define COMMON_H +#include "litmus.h" void bail_out(const char* msg); +/* EDF-WM helper functions to parse task parameters from file */ +int parse_edfwm_ts_file(FILE *ts, struct rt_task *rt); + #endif diff --git a/include/litmus.h b/include/litmus.h index b798c92..9179ca6 100644 --- a/include/litmus.h +++ b/include/litmus.h @@ -9,6 +9,7 @@ extern "C" { * This is required for the rt_param * and control_page structures. */ +#include #include #include @@ -40,6 +41,19 @@ int sporadic_task_ns( int cpu, task_class_t cls, budget_policy_t budget_policy, int set_cpu_set); +int sporadic_task_ns_edffm(lt_t e, lt_t p, lt_t phase, int cpu, + lt_t *frac1, lt_t *frac2, int cpu1, int cpu2, + task_class_t cls, budget_policy_t budget_policy, + int set_cpu_set); + +int sporadic_task_ns_npsf( + lt_t e, lt_t p, lt_t phase, + int cpu, task_class_t cls, int npsf_id, + budget_policy_t budget_policy, int set_cpu_set); + +/* times are in ns, specific helper for semi-partitioned algos */ +int sporadic_task_ns_semi(struct rt_task *rt); + /* budget enforcement off by default in these macros */ #define sporadic_global(e, p) \ sporadic_task(e, p, 0, 0, RT_CLASS_SOFT, NO_ENFORCEMENT, 0) @@ -85,6 +99,17 @@ typedef int (*rt_fn_t)(void*); int create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period); int __create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period, task_class_t cls); +int __create_rt_task_edffm(rt_fn_t rt_prog, void *arg, int cpu, int wcet, + int period, lt_t *frac1, lt_t *frac2, + int cpu1, int cpu2, task_class_t class); +int __create_rt_task_npsf(rt_fn_t rt_prog, void *arg, int cpu, int wcet, + int period, int npsf_id, task_class_t class); + +/* wrapper to mask __launch_rt_task() for semi-partitioned algorithms + * (it can be extended to cover all algorithms that directly submit + * an rt_task structure instead of a set of values). + */ +int create_rt_task_semi(rt_fn_t rt_prog, void *arg, struct rt_task *params); /* per-task modes */ enum rt_task_mode_t { @@ -134,6 +159,21 @@ int null_call(cycles_t *timestamp); */ struct control_page* get_ctrl_page(void); +/* NPS-F syscall to add a notional processor (a server) to a cpu. + * A notional processor may span across multiple cpu. + * + * @npsf_id: a "unique" identifier for the notional processor. + * @budgets: array of (cpu, budget-ns) that describes this np. + * on possibly more than one cpu. + * @last: marks the end of servers initialization and trigger + * the switching of servers in the plugin. + * Should be set to 1 only once at the end of the sequence + * of add_server() calls + * + * Currently implemented on x86_64 only. + */ +int add_server(int *npsf_id, struct npsf_budgets *budgets, int last); + #ifdef __cplusplus } #endif diff --git a/src/litmus.c b/src/litmus.c index d3cc6bb..c0dae95 100644 --- a/src/litmus.c +++ b/src/litmus.c @@ -29,8 +29,6 @@ task_class_t str2class(const char* str) return -1; } -#define NS_PER_MS 1000000 - /* only for best-effort execution: migrate to target_cpu */ int be_migrate_to(int target_cpu) { @@ -45,7 +43,7 @@ int sporadic_task(lt_t e, lt_t p, lt_t phase, int cpu, task_class_t cls, budget_policy_t budget_policy, int set_cpu_set) { - return sporadic_task_ns(e * NS_PER_MS, p * NS_PER_MS, phase * NS_PER_MS, + return sporadic_task_ns(e * __NS_PER_MS, p * __NS_PER_MS, phase * __NS_PER_MS, cpu, cls, budget_policy, set_cpu_set); } @@ -75,6 +73,72 @@ int sporadic_task_ns(lt_t e, lt_t p, lt_t phase, return set_rt_task_param(gettid(), ¶m); } +int sporadic_task_ns_edffm(lt_t e, lt_t p, lt_t phase, int cpu, + lt_t *frac1, lt_t *frac2, int cpu1, int cpu2, + task_class_t cls, budget_policy_t budget_policy, + int set_cpu_set) +{ + struct rt_task param; + struct edffm_params fm; + int ret; + param.exec_cost = e; + param.period = p; + param.cpu = cpu; + /* check on denominators */ + if (frac1[1] != 0 && frac2[1] != 0) { + /* edf-fm migrat task */ + fm.nr_cpus = 1; + fm.cpus[0] = cpu1; + fm.cpus[1] = cpu2; + fm.fraction[0][0] = frac1[0]; + fm.fraction[1][0] = frac1[1]; + fm.fraction[0][1] = frac2[0]; + fm.fraction[1][1] = frac2[1]; + } + param.semi_part.fm = fm; + param.cls = cls; + param.phase = phase; + param.budget_policy = budget_policy; + + if (set_cpu_set) { + ret = be_migrate_to(cpu); + check("migrate to cpu"); + } + return set_rt_task_param(gettid(), ¶m); +} + +int sporadic_task_ns_npsf(lt_t e, lt_t p, lt_t phase, + int cpu, task_class_t cls, int npsf_id, + budget_policy_t budget_policy, int set_cpu_set) +{ + struct rt_task param; + int ret; + param.exec_cost = e; + param.period = p; + param.cpu = cpu; + param.cls = cls; + param.phase = phase; + param.budget_policy = budget_policy; + param.semi_part.npsf_id = (int) npsf_id; + + if (set_cpu_set) { + ret = be_migrate_to(cpu); + check("migrate to cpu"); + } + return set_rt_task_param(gettid(), ¶m); +} + +/* Sporadic task helper function for Semi-Partitioned algorithms. */ +int sporadic_task_ns_semi(struct rt_task *param) +{ + int ret; + + ret = be_migrate_to(param->cpu); + check("migrate to cpu"); + + return set_rt_task_param(gettid(), param); +} + int init_kernel_iface(void); int init_litmus(void) diff --git a/src/syscalls.c b/src/syscalls.c index 77a6277..7ac488a 100644 --- a/src/syscalls.c +++ b/src/syscalls.c @@ -95,3 +95,8 @@ int null_call(cycles_t *timestamp) { return syscall(__NR_null_call, timestamp); } + +int add_server(int *npsf_id, struct npsf_budgets *budgets, int last) +{ + return syscall(__NR_add_server, npsf_id, budgets, last); +} diff --git a/src/task.c b/src/task.c index 4d237bd..daf95ca 100644 --- a/src/task.c +++ b/src/task.c @@ -40,6 +40,54 @@ int __launch_rt_task(rt_fn_t rt_prog, void *rt_arg, rt_setup_fn_t setup, return rt_task; } +int __create_rt_task_edffm(rt_fn_t rt_prog, void *arg, int cpu, int wcet, + int period, lt_t *frac1, lt_t *frac2, + int cpu1, int cpu2, task_class_t class) +{ + struct rt_task params; + struct edffm_params fm; + params.cpu = cpu; + params.period = period; + params.exec_cost = wcet; + params.cls = class; + params.phase = 0; + /* enforce budget for tasks that might not use sleep_next_period() */ + params.budget_policy = QUANTUM_ENFORCEMENT; + + /* edf-fm check on denominators for migratory tasks */ + if (frac1[1] != 0 && frac2[1] != 0) { + /* edf-fm migrat task */ + fm.nr_cpus = 1; + fm.cpus[0] = cpu1; + fm.cpus[1] = cpu2; + fm.fraction[0][0] = frac1[0]; + fm.fraction[1][0] = frac1[1]; + fm.fraction[0][1] = frac2[0]; + fm.fraction[1][1] = frac2[1]; + } + params.semi_part.fm = fm; + + return __launch_rt_task(rt_prog, arg, + (rt_setup_fn_t) set_rt_task_param, ¶ms); +} + +int __create_rt_task_npsf(rt_fn_t rt_prog, void *arg, int cpu, int wcet, + int period, int npsf_id, task_class_t class) +{ + struct rt_task params; + params.cpu = cpu; + params.period = period; + params.exec_cost = wcet; + params.cls = class; + params.phase = 0; + /* enforce budget for tasks that might not use sleep_next_period() */ + params.budget_policy = QUANTUM_ENFORCEMENT; + params.semi_part.npsf_id = (int) npsf_id; + + return __launch_rt_task(rt_prog, arg, + (rt_setup_fn_t) set_rt_task_param, ¶ms); +} + int __create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period, task_class_t class) { @@ -60,6 +108,11 @@ int create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period) { return __create_rt_task(rt_prog, arg, cpu, wcet, period, RT_CLASS_HARD); } +int create_rt_task_semi(rt_fn_t rt_prog, void *arg, struct rt_task *params) +{ + return __launch_rt_task(rt_prog, arg, + (rt_setup_fn_t) set_rt_task_param, params); +} #define SCHED_NORMAL 0 #define SCHED_LITMUS 6