diff --git a/Makefile b/Makefile index db94ffb..a80f7de 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ AR := ${CROSS_COMPILE}${AR} # Targets all = lib ${rt-apps} -rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \ +rt-apps = cycles base_task rt_launch rtspin rtspin_sem release_ts measure_syscall \ base_mt_task runtests .PHONY: all lib clean dump-config TAGS tags cscope help @@ -211,6 +211,9 @@ obj-rt_launch = rt_launch.o common.o obj-rtspin = rtspin.o common.o lib-rtspin = -lrt +obj-rtspin_sem = rtspin_sem.o common.o +lib-rtspin_sem = -lrt + obj-release_ts = release_ts.o obj-measure_syscall = null_call.o diff --git a/bin/rtspin.c b/bin/rtspin.c index ae76941..86af3cc 100644 --- a/bin/rtspin.c +++ b/bin/rtspin.c @@ -161,7 +161,7 @@ static int job(double exec_time, double program_end) } } -#define OPTSTR "p:c:wlveo:f:s:" +#define OPTSTR "p:c:wlvedo:f:s:" int main(int argc, char** argv) { @@ -177,6 +177,7 @@ int main(int argc, char** argv) int column = 1; const char *file = NULL; int want_enforcement = 0; + int want_complete = 1; double duration = 0, start; double *exec_times = NULL; double scale = 1.0; @@ -202,6 +203,9 @@ int main(int argc, char** argv) case 'e': want_enforcement = 1; break; + case 'd': + want_complete = 0; + break; case 'l': test_loop = 1; break; @@ -277,6 +281,8 @@ int main(int argc, char** argv) ret = sporadic_task_ns(wcet, period, 0, cpu, class, want_enforcement ? PRECISE_ENFORCEMENT : NO_ENFORCEMENT, + want_complete ? COMPLETE_JOB + : POSTPONE_DEADLINE, migrate); if (ret < 0) bail_out("could not setup rt task params"); diff --git a/bin/rtspin_sem.c b/bin/rtspin_sem.c new file mode 100644 index 0000000..462ef64 --- /dev/null +++ b/bin/rtspin_sem.c @@ -0,0 +1,379 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include "litmus.h" +#include "common.h" + +typedef struct _exec_segment_t { + double duration; + int res_id, lock_od; + + struct _exec_segment_t *next_nested; + struct _exec_segment_t *next; +} exec_segment_t; + +static exec_segment_t *eseg_list = NULL; + +#define LOCKING_NONE 0 +#define LOCKING_FMLP 1 +#define LOCKING_BWI 2 + +static int locks_fd; +int locking = LOCKING_NONE; + +static double emergency_exit = 0.0; + +static void usage(char *error) { + fprintf(stderr, "Error: %s\n", error); + exit(EXIT_FAILURE); +} + +/* + * returns the character that made processing stop, newline or EOF + */ +static int skip_to_next_line(FILE *fstream) +{ + int ch; + for (ch = fgetc(fstream); ch != EOF && ch != '\n'; ch = fgetc(fstream)); + return ch; +} + +static void skip_comments(FILE *fstream) +{ + int ch; + for (ch = fgetc(fstream); ch == '#'; ch = fgetc(fstream)) + skip_to_next_line(fstream); + ungetc(ch, fstream); +} + +static void get_exec(const char *file, const int my_id, + double *wcet_ms, double *period_ms, int locking) +{ + FILE *fstream; + int i, j, task_id; + + char line[4096], task_string[4096]; + char exec_string[2048], budget_string[256], + period_string[256], *exec_pos, *p, *pp; + exec_segment_t *curr_eseg, *curr_nested_eseg; + + fstream = fopen(file, "r"); + if (!fstream) + bail_out("could not open execution time file"); + + skip_comments(fstream); + while (fgets(line, 4096, fstream)) { + sscanf(line, "task%d, %[^\n]", &task_id, task_string); + + if (task_id == my_id) + break; + + }; + + if (task_id != my_id) + bail_out("unable to find the correct task in the file"); + + printf("\ntask: %s\n\n", task_string); + + if (!sscanf(task_string, "%[^','], %[^','], %[^',']", + exec_string, budget_string, period_string)) + bail_out("unable to find execution segments and period"); + + printf("exec: %s\n", exec_string); + printf("budget: %s\n", budget_string); + printf("period: %s\n\n", period_string); + + *wcet_ms = atof(budget_string); + *period_ms = atof(period_string); + + exec_pos = strtok(exec_string, "|"); + while (exec_pos != NULL) { + printf(" exec_segment: %s\n", exec_pos); + + if (!strstr(exec_pos, "R")) { + eseg_list = malloc(sizeof(exec_segment_t)); + + sscanf(exec_pos, "%lf", &eseg_list->duration); + eseg_list->res_id = -1; + eseg_list->next_nested = eseg_list->next = NULL; + curr_eseg = eseg_list; + } else { + curr_eseg->next = malloc(sizeof(exec_segment_t)); + curr_eseg = curr_eseg->next; + + sscanf(exec_pos, "R%d-%lf", &curr_eseg->res_id, &curr_eseg->duration); + if (locking == LOCKING_FMLP) + curr_eseg->lock_od = open_fmlp_sem(locks_fd, curr_eseg->res_id); + else if (locking == LOCKING_BWI) + curr_eseg->lock_od = open_bwi_sem(locks_fd, curr_eseg->res_id); + curr_eseg->next_nested = curr_eseg->next = NULL; + + if ((p = strstr(exec_pos, "["))) { + curr_eseg->next_nested = malloc(sizeof(exec_segment_t)); + curr_nested_eseg = curr_eseg->next_nested; + curr_nested_eseg->next_nested = curr_nested_eseg->next = NULL; + + pp = p; + do { + if (pp != p) { + curr_nested_eseg->next = malloc(sizeof(exec_segment_t)); + curr_nested_eseg = curr_nested_eseg->next; + curr_nested_eseg->next_nested = NULL; + curr_nested_eseg->next = NULL; + } + + pp++; + sscanf(pp, "R%d-%lf", &curr_nested_eseg->res_id, + &curr_nested_eseg->duration); + if (locking == LOCKING_FMLP) + curr_nested_eseg->lock_od = open_fmlp_sem(locks_fd, + curr_nested_eseg->res_id); + else if (locking == LOCKING_BWI) + curr_nested_eseg->lock_od = open_bwi_sem(locks_fd, + curr_nested_eseg->res_id); + } while ((pp = strstr(pp, "!"))); + } + } + + exec_pos = strtok(NULL, "|"); + } + + printf("\n"); + + for (curr_eseg = eseg_list, i = 1; curr_eseg != NULL;curr_eseg = curr_eseg->next, i++) { + printf("eseg_%d: duration=%lf, res_id=%d\n", + i, curr_eseg->duration, curr_eseg->res_id); + for (curr_nested_eseg = curr_eseg->next_nested, j = 1;curr_nested_eseg != NULL; + curr_nested_eseg = curr_nested_eseg->next, j++) + printf(" nested_eseg_%d: duration=%lf, res_id=%d\n", + j, curr_nested_eseg->duration, curr_nested_eseg->res_id); + } +} + +#define NUMS 4096 +static int num[NUMS]; +static char* progname; + +double scale = 1.0; + +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 last_loop = 0, loop_start; + int tmp = 0; + + double start = cputime(); + double now = cputime(); + + //printf("loop_for(%lf)\n", exec_time); + //return 0; + + while (now + last_loop < start + (exec_time * 0.001 * scale) && + wctime() < emergency_exit) { + loop_start = now; + tmp += loop_once(); + now = cputime(); + last_loop = now - loop_start; + /*if (emergency_exit && wctime() > emergency_exit) { + fprintf(stderr, "rtspin/%d timed out while inside a job (%lf > %lf)\n", + getpid(), wctime(), emergency_exit); + return tmp; + }*/ + } + + return tmp; +} + +static void run_eseg(exec_segment_t *this_eseg) +{ + /* */ + if (!this_eseg) { + exit(EXIT_FAILURE); + } + + if (this_eseg->res_id < 0) + loop_for(this_eseg->duration / 2); + else { + if (locking != LOCKING_NONE) + litmus_lock(this_eseg->lock_od); + loop_for(this_eseg->duration); + if (this_eseg->next_nested) { + run_eseg(this_eseg->next_nested); + } + if (locking != LOCKING_NONE) + litmus_unlock(this_eseg->lock_od); + } + + if (this_eseg->next) + run_eseg(this_eseg->next); + + if (this_eseg->res_id < 0) + loop_for(this_eseg->duration / 2); + + return; +} + +int job() +{ + static int job_no = 1; + + //fprintf(stderr, "running job# %d\n", job_no); + + job_no++; + run_eseg(eseg_list); + + if (wctime() >= emergency_exit) + return 0; + + sleep_next_period(); + + return 1; +} + +#define OPTSTR "p:c:wvedo:f:i:s:D:lb" + +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 task_id = -1; + int opt; + int wait = 0; + const char *file = NULL; + int want_enforcement = 0; + int want_complete = 1; + double duration = 0, 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 'e': + want_enforcement = 1; + break; + case 'd': + want_complete = 0; + break; + case 'f': + file = optarg; + break; + case 'i': + task_id = atoi(optarg); + break; + case 's': + scale = atof(optarg); + break; + case 'l': + locking = LOCKING_FMLP; + break; + case 'b': + locking = LOCKING_BWI; + break; + case 'D': + duration = atof(optarg); + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + if (!file) + bail_out("file not specified"); + + get_exec(file, task_id, &wcet_ms, &period_ms, locking); + wcet = wcet_ms * scale * __NS_PER_MS; + period = period_ms * scale * __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 (!file && wcet > period) { + usage("The worst-case execution time must not " + "exceed the period."); + } + + locks_fd = open(".locks", O_RDONLY | O_CREAT); + + if (migrate) { + ret = be_migrate_to(cpu); + if (ret < 0) + bail_out("could not migrate to target partition"); + } + + ret = sporadic_task_ns(wcet, period, 0, cpu, class, + want_enforcement ? PRECISE_ENFORCEMENT + : NO_ENFORCEMENT, + want_complete ? COMPLETE_JOB + : POSTPONE_DEADLINE, + migrate); + if (ret < 0) + bail_out("could not setup rt task params"); + + 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(); + emergency_exit = start + duration; + + while (job()); + + ret = task_mode(BACKGROUND_TASK); + if (ret != 0) + bail_out("could not become regular task (huh?)"); + + close(locks_fd); + remove(".locks"); + + //if (file) + // free(exec_times); + + return 0; +} diff --git a/include/litmus.h b/include/litmus.h index 52435d8..c531291 100644 --- a/include/litmus.h +++ b/include/litmus.h @@ -32,24 +32,39 @@ int get_rt_task_param(pid_t pid, struct rt_task* param); int sporadic_task( lt_t e, lt_t p, lt_t phase, int partition, task_class_t cls, - budget_policy_t budget_policy, int set_cpu_set); + budget_policy_t budget_policy, + budget_action_t budget_action, int set_cpu_set); /* times are given in ns */ int sporadic_task_ns( lt_t e, lt_t p, lt_t phase, int cpu, task_class_t cls, - budget_policy_t budget_policy, int set_cpu_set); + budget_policy_t budget_policy, + budget_action_t budget_action, int set_cpu_set); /* 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) + sporadic_task(e, p, 0, 0, RT_CLASS_SOFT, NO_ENFORCEMENT, COMPLETE_JOB, 0) #define sporadic_partitioned(e, p, cpu) \ - sporadic_task(e, p, 0, cpu, RT_CLASS_SOFT, NO_ENFORCEMENT, 1) + sporadic_task(e, p, 0, cpu, RT_CLASS_SOFT, NO_ENFORCEMENT, COMPLETE_JOB, 1) + +/* budget enforcement enabled by default in these macros */ +#define sporadic_global_enforced(e, p) \ + sporadic_task(e, p, 0, 0, RT_CLASS_SOFT, PRECISE_ENFORCEMENT, COMPLETE_JOB, 0) +#define sporadic_partitioned_enforced(e, p, cpu) \ + sporadic_task(e, p, 0, cpu, RT_CLASS_SOFT_PRECISE_ENFORCEMENT, COMPLETE_JOB, 1); + +/* deadline postponement enabled by default in these macros */ +#define sporadic_global_postponed(e, p) \ + sporadic_task(e, p, 0, 0, RT_CLASS_SOFT, PRECISE_ENFORCEMENT, POSTPONE_DEADLINE, 0) +#define sporadic_partitioned_postponed(e, p) \ + sporadic_task(e, p, 0, cppu, RT_CLASS_SOFT, PRECISE_ENFORCEMENT, POSTPONE_DEADLINE, 0); /* file descriptor attached shared objects support */ typedef enum { FMLP_SEM = 0, SRP_SEM = 1, + BWI_SEM = 2, } obj_type_t; int od_openx(int fd, obj_type_t type, int obj_id, void* config); @@ -125,6 +140,11 @@ static inline int open_srp_sem(int fd, int name) return od_open(fd, SRP_SEM, name); } +static inline int open_bwi_sem(int fd, int name) +{ + return od_open(fd, BWI_SEM, name); +} + /* syscall overhead measuring */ int null_call(cycles_t *timestamp); diff --git a/src/litmus.c b/src/litmus.c index d3cc6bb..f7460e1 100644 --- a/src/litmus.c +++ b/src/litmus.c @@ -43,15 +43,17 @@ int be_migrate_to(int target_cpu) 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) + budget_policy_t budget_policy, + budget_action_t budget_action, int set_cpu_set) { return sporadic_task_ns(e * NS_PER_MS, p * NS_PER_MS, phase * NS_PER_MS, - cpu, cls, budget_policy, set_cpu_set); + cpu, cls, budget_policy, budget_action, set_cpu_set); } int sporadic_task_ns(lt_t e, lt_t p, lt_t phase, int cpu, task_class_t cls, - budget_policy_t budget_policy, int set_cpu_set) + budget_policy_t budget_policy, + budget_action_t budget_action, int set_cpu_set) { struct rt_task param; int ret; @@ -67,6 +69,7 @@ int sporadic_task_ns(lt_t e, lt_t p, lt_t phase, param.cls = cls; param.phase = phase; param.budget_policy = budget_policy; + param.budget_action = budget_action; if (set_cpu_set) { ret = be_migrate_to(cpu); diff --git a/src/task.c b/src/task.c index 4d237bd..c5139f8 100644 --- a/src/task.c +++ b/src/task.c @@ -51,6 +51,8 @@ int __create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period, params.phase = 0; /* enforce budget for tasks that might not use sleep_next_period() */ params.budget_policy = QUANTUM_ENFORCEMENT; + /* on budget overrun force a job completion */ + params.budget_action = COMPLETE_JOB; return __launch_rt_task(rt_prog, arg, (rt_setup_fn_t) set_rt_task_param, ¶ms); diff --git a/tests/core_api.c b/tests/core_api.c index 7487c1c..d9c7e13 100644 --- a/tests/core_api.c +++ b/tests/core_api.c @@ -22,6 +22,7 @@ TESTCASE(set_rt_task_param_invalid_params, ALL, params.period = 100; params.phase = 0; params.cls = RT_CLASS_HARD; + params.budget_policy = NO_ENFORCEMENT; /* over utilize */ params.exec_cost = 110; @@ -36,8 +37,17 @@ TESTCASE(set_rt_task_param_invalid_params, ALL, params.cpu = 0; SYSCALL_FAILS( EINVAL, set_rt_task_param(-1, ¶ms) ); + /* bad enforcement policy */ + params.budget_policy = -1; + SYSCALL_FAILS( EINVAL, set_rt_task_param(gettid(), ¶ms) ); + + /* bad enforcement action */ + params.budget_policy = PRECISE_ENFORCEMENT; + params.budget_action = -1; + SYSCALL_FAILS( EINVAL, set_rt_task_param(gettid(), ¶ms) ); /* now try correct params */ + params.budget_policy = NO_ENFORCEMENT; SYSCALL( set_rt_task_param(gettid(), ¶ms) ); } diff --git a/tests/locks.c b/tests/locks.c index 65b932a..851078b 100644 --- a/tests/locks.c +++ b/tests/locks.c @@ -46,6 +46,29 @@ TESTCASE(not_lock_srp_be, PSN_EDF, } +TESTCASE(not_lock_bwi_be, C_EDF, + "don't let best-effor tasks lock BWI semaphores") +{ + int fd, od; + + SYSCALL( fd = open(".bwi_locks", O_RDONLY | O_CREAT) ); + + SYSCALL( od = open_bwi_sem(fd, 0) ); + + /* BE tasks may not lock BWI semaphores */ + SYSCALL_FAILS(EPERM, litmus_lock(od) ); + + /* tasks may not unlock resources they don't own */ + SYSCALL_FAILS(EINVAL, litmus_unlock(od) ); + + SYSCALL( od_close(od) ); + + SYSCALL( close(fd) ); + + SYSCALL( remove(".bwi_locks") ); + +} + TESTCASE(lock_srp, PSN_EDF, "SRP acquisition and release") { @@ -108,3 +131,34 @@ TESTCASE(lock_fmlp, PSN_EDF | GSN_EDF, SYSCALL( remove(".fmlp_locks") ); } + +TESTCASE(lock_bwi, C_EDF, + "BWI acquisition and release") +{ + int fd, od; + + SYSCALL( fd = open(".bwi_locks", O_RDONLY | O_CREAT) ); + + SYSCALL( sporadic_partitioned(10, 100, 0) ); + SYSCALL( task_mode(LITMUS_RT_TASK) ); + + SYSCALL( od = open_bwi_sem(fd, 0) ); + + SYSCALL( litmus_lock(od) ); + SYSCALL( litmus_unlock(od) ); + + SYSCALL( litmus_lock(od) ); + SYSCALL( litmus_unlock(od) ); + + SYSCALL( litmus_lock(od) ); + SYSCALL( litmus_unlock(od) ); + + /* tasks may not unlock resources they don't own */ + SYSCALL_FAILS(EINVAL, litmus_unlock(od) ); + + SYSCALL( od_close(od) ); + + SYSCALL( close(fd) ); + + SYSCALL( remove(".bwi_locks") ); +}