From b6ef12e31351d23a439597162f8d0fa7031b771d Mon Sep 17 00:00:00 2001 From: Namhoon Kim Date: Thu, 13 Oct 2016 20:27:40 +0000 Subject: RTAS 2017 submission --- .gitignore | 4 + Makefile | 12 +- bin/mc2mem.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++++ bin/mc2spin.c | 250 ++++++++++++++++++++++++++++++++++++++++ include/cache_common.h | 140 +++++++++++++++++++++++ include/litmus.h | 20 ++++ src/syscalls.c | 25 ++++ 7 files changed, 753 insertions(+), 2 deletions(-) create mode 100644 bin/mc2mem.c create mode 100644 bin/mc2spin.c create mode 100644 include/cache_common.h diff --git a/.gitignore b/.gitignore index a52500b..f42c50a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,10 @@ showst rtspin cycles measure_syscall +mc2spin +mc2mem_stc +mc2mem_ssh +mc2mem # build system files .config diff --git a/Makefile b/Makefile index 4742fd2..ee2a9b2 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ LITMUS_KERNEL ?= ../litmus-rt # Internal configuration. # compiler flags -flags-debug = -O2 -Wall -Werror -g -Wdeclaration-after-statement +flags-debug = -O2 -Wall -g -Wdeclaration-after-statement flags-api = -D_XOPEN_SOURCE=600 -D_GNU_SOURCE # architecture-specific flags @@ -73,7 +73,7 @@ AR := ${CROSS_COMPILE}${AR} all = lib ${rt-apps} rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \ - base_mt_task uncache runtests + base_mt_task uncache runtests mc2mem_stc mc2mem_ssh mc2spin .PHONY: all lib clean dump-config TAGS tags cscope help doc @@ -234,6 +234,14 @@ obj-release_ts = release_ts.o obj-measure_syscall = null_call.o lib-measure_syscall = -lm +obj-mc2mem_stc = mc2mem.o common.o +lib-mc2mem_stc = -lrt -static + +obj-mc2mem_ssh = mc2mem.o common.o +lib-mc2mem_ssh = -lrt + +obj-mc2spin = mc2spin.o common.o +lib-mc2spin = -lrt # ############################################################################## # Build everything that depends on liblitmus. diff --git a/bin/mc2mem.c b/bin/mc2mem.c new file mode 100644 index 0000000..2fba878 --- /dev/null +++ b/bin/mc2mem.c @@ -0,0 +1,304 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "litmus.h" +#include "common.h" +#include "cache_common.h" + +#define PAGE_SIZE (4096) +#define CACHELINE_SIZE 32 +#define INTS_IN_CACHELINE (CACHELINE_SIZE/sizeof(int)) +#define CACHELINES_IN_1KB (1024 / sizeof(cacheline_t)) +#define INTS_IN_1KB (1024 / sizeof(int)) +#define INTS_IN_CACHELINE (CACHELINE_SIZE/sizeof(int)) + +static int loops = 100; +static cacheline_t* arena = NULL; +static int verbose = 0; + +#define UNCACHE_DEV "/dev/litmus/uncache" + +/* Random walk around the arena in cacheline-sized chunks. + Cacheline-sized chucks ensures the same utilization of each + hit line as sequential read. (Otherwise, our utilization + would only be 1/INTS_IN_CACHELINE.) */ +static int random_walk(cacheline_t *mem, int wss, int write_cycle) +{ + /* a random cycle among the cache lines was set up by init_arena(). */ + int sum, i, next; + + int numlines = wss * CACHELINES_IN_1KB; + + sum = 0; + + /* contents of arena is structured s.t. offsets are all + w.r.t. to start of arena, so compute the initial offset */ + next = mem - arena; + + if (write_cycle == 0) { + for (i = 0; i < numlines; i++) { + /* every element in the cacheline has the same value */ + next = arena[next].line[0]; + sum += next; + } + } + + else { + int w, which_line; + for (i = 0, w = 0; i < numlines; i++) { + which_line = next; + next = arena[next].line[0]; + if((w % write_cycle) != (write_cycle - 1)) { + sum += next; + } + else { + ((volatile cacheline_t*)arena)[which_line].line[0] = next; + } + } + } + return sum; +} + +static cacheline_t* random_start(int wss) +{ + return arena + randrange(0, ((wss * 1024)/sizeof(cacheline_t))); +} + +static volatile int dont_optimize_me = 0; + +static void usage(char *error) { + fprintf(stderr, "Error: %s\n", error); + fprintf(stderr, + "Usage:\n" + " rt_spin [COMMON-OPTS] WCET PERIOD DURATION\n" + " rt_spin [COMMON-OPTS] -f FILE [-o COLUMN] WCET PERIOD\n" + " rt_spin -l\n" + "\n" + "COMMON-OPTS = [-w] [-s SCALE]\n" + " [-p PARTITION/CLUSTER [-z CLUSTER SIZE]] [-m CRITICALITY LEVEL]\n" + " [-k WSS] [-l LOOPS] [-b BUDGET]\n" + "\n" + "WCET and PERIOD are milliseconds, DURATION is seconds.\n"); + exit(EXIT_FAILURE); +} + +static int loop_once(int wss) +{ + cacheline_t *mem; + int temp; + + mem = random_start(wss); + temp = random_walk(mem, wss, 0); + + dont_optimize_me = temp; + + return dont_optimize_me; +} + +static int job(int wss, double exec_time, double program_end) +{ + if (wctime() > program_end) + return 0; + else { + register unsigned int iter = 0; + register cycles_t t; + t = get_cycles(); + while(iter++ < loops) { + loop_once(wss); + } + t = get_cycles() - t; + if (verbose) + printf("%ld cycles\n", t); + sleep_next_period(); + return 1; + } +} + +#define OPTSTR "p:wl:m:i:b:k:v" +int main(int argc, char** argv) +{ + int ret; + lt_t wcet, period, budget; + double wcet_ms, period_ms, budget_ms; + unsigned int priority = LITMUS_NO_PRIORITY; + int migrate = 0; + int cluster = 0; + int opt; + int wait = 0; + double duration = 0, start = 0; + struct rt_task param; + struct mc2_task mc2_param; + struct reservation_config config; + int res_type = PERIODIC_POLLING; + size_t arena_sz; + int wss = 0; + + /* default for reservation */ + config.id = 0; + config.priority = LITMUS_NO_PRIORITY; /* use EDF by default */ + config.cpu = -1; + + mc2_param.crit = CRIT_LEVEL_C; + + budget_ms = 1000; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'w': + wait = 1; + break; + case 'p': + cluster = atoi(optarg); + migrate = 1; + config.cpu = cluster; + break; + case 'l': + loops = atoi(optarg); + break; + case 'k': + wss = atoi(optarg); + break; + case 'm': + mc2_param.crit = atoi(optarg); + if ((mc2_param.crit >= CRIT_LEVEL_A) && (mc2_param.crit <= CRIT_LEVEL_C)) { + res_type = PERIODIC_POLLING; + } + else + usage("Invalid criticality level."); + break; + case 'b': + budget_ms = atof(optarg); + break; + case 'i': + config.priority = atoi(optarg); + break; + case 'v': + verbose = 1; + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + srand(getpid()); + + /* + * We need three parameters + */ + if (argc - optind < 3) + usage("Arguments missing."); + + wcet_ms = atof(argv[optind + 0]); + period_ms = atof(argv[optind + 1]); + + wcet = ms2ns(wcet_ms); + period = ms2ns(period_ms); + budget = ms2ns(budget_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 (wss == 0) { + usage("You need to specify a WSS (-k option)."); + } + + duration = atof(argv[optind + 2]); + + if (migrate) { + ret = be_migrate_to_domain(cluster); + if (ret < 0) + bail_out("could not migrate to target partition or cluster."); + } + + /* reservation config */ + config.id = gettid(); + config.polling_params.budget = budget; + config.polling_params.period = period; + config.polling_params.offset = 0; + config.polling_params.relative_deadline = 0; + + if (config.polling_params.budget > config.polling_params.period) { + usage("The budget must not exceed the period."); + } + + /* create a reservation */ + ret = reservation_create(res_type, &config); + if (ret < 0) { + bail_out("failed to create reservation."); + } + + init_rt_task_param(¶m); + param.exec_cost = wcet; + param.period = period; + param.priority = priority; + param.cls = RT_CLASS_HARD; + param.release_policy = TASK_PERIODIC; + param.budget_policy = NO_ENFORCEMENT; + if (migrate) { + param.cpu = gettid(); + } + ret = set_rt_task_param(gettid(), ¶m); + if (ret < 0) + bail_out("could not setup rt task params"); + + mc2_param.res_id = gettid(); + ret = set_mc2_task_param(gettid(), &mc2_param); + if (ret < 0) + bail_out("could not setup mc2 task params"); + + arena_sz = wss*1024; + arena = alloc_arena(arena_sz, 0, 0); + init_arena(arena, arena_sz); + + lock_memory(); + + if (mc2_param.crit == CRIT_LEVEL_C) + set_page_color(-1); + else + set_page_color(config.cpu); + + ret = init_litmus(); + if (ret != 0) + bail_out("init_litmus() failed\n"); + + start = wctime(); + 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 (job(wss, wcet_ms * 0.001, start + duration)) {}; + + ret = task_mode(BACKGROUND_TASK); + if (ret != 0) + bail_out("could not become regular task (huh?)"); + + reservation_destroy(gettid(), config.cpu); + dealloc_arena(arena, arena_sz); + return 0; +} diff --git a/bin/mc2spin.c b/bin/mc2spin.c new file mode 100644 index 0000000..e41c62c --- /dev/null +++ b/bin/mc2spin.c @@ -0,0 +1,250 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "litmus.h" +#include "common.h" + +#define PAGE_SIZE (4096) + +static int verbose = 0; + +#define UNCACHE_DEV "/dev/litmus/uncache" + +static void usage(char *error) { + fprintf(stderr, "Error: %s\n", error); + fprintf(stderr, + "Usage:\n" + " rt_spin [COMMON-OPTS] WCET PERIOD DURATION\n" + " rt_spin [COMMON-OPTS] -f FILE [-o COLUMN] WCET PERIOD\n" + " rt_spin -l\n" + "\n" + "COMMON-OPTS = [-w] [-s SCALE]\n" + " [-p PARTITION/CLUSTER [-z CLUSTER SIZE]] [-m CRITICALITY LEVEL]\n" + " [-k WSS] [-l LOOPS] [-b BUDGET]\n" + "\n" + "WCET and PERIOD are milliseconds, DURATION is seconds.\n"); + exit(EXIT_FAILURE); +} + +#define NUMS 4096 +static int num[NUMS]; +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 emergency_exit) +{ + double last_loop = 0, loop_start; + int tmp = 0; + + double start = cputime(); + double now = cputime(); + + while (now + last_loop < start + exec_time) { + loop_start = now; + tmp += loop_once(); + now = cputime(); + last_loop = now - loop_start; + if (emergency_exit && wctime() > emergency_exit) { + /* Oops --- this should only be possible if the execution time tracking + * is broken in the LITMUS^RT kernel. */ + fprintf(stderr, "!!! rtspin/%d emergency exit!\n", getpid()); + fprintf(stderr, "Something is seriously wrong! Do not ignore this.\n"); + break; + } + } + + return tmp; +} + +static int job(double exec_time, double program_end) +{ + if (wctime() > program_end) + return 0; + else { + loop_for(exec_time, program_end + 1); + sleep_next_period(); + return 1; + } +} + +#define OPTSTR "p:wm:i:v" +int main(int argc, char** argv) +{ + int ret; + lt_t wcet, period, budget; + double wcet_ms, period_ms; + unsigned int priority = LITMUS_NO_PRIORITY; + int migrate = 0; + int cluster = 0; + int opt; + int wait = 0; + double duration = 0, start = 0; + struct rt_task param; + struct mc2_task mc2_param; + struct reservation_config config; + int res_type = PERIODIC_POLLING; + + /* default for reservation */ + config.id = 0; + config.priority = LITMUS_NO_PRIORITY; /* use EDF by default */ + config.cpu = -1; + + mc2_param.crit = CRIT_LEVEL_C; + + progname = argv[0]; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'w': + wait = 1; + break; + case 'p': + cluster = atoi(optarg); + migrate = 1; + config.cpu = cluster; + break; + case 'm': + mc2_param.crit = atoi(optarg); + if ((mc2_param.crit >= CRIT_LEVEL_A) && (mc2_param.crit <= CRIT_LEVEL_C)) { + res_type = PERIODIC_POLLING; + } + else + usage("Invalid criticality level."); + break; + case 'i': + config.priority = atoi(optarg); + break; + case 'v': + verbose = 1; + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + srand(getpid()); + + /* + * We need three parameters + */ + if (argc - optind < 3) + usage("Arguments missing."); + + wcet_ms = atof(argv[optind + 0]); + period_ms = atof(argv[optind + 1]); + + wcet = ms2ns(wcet_ms); + period = ms2ns(period_ms); + budget = ms2ns(wcet_ms+1); + 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."); + } + + duration = atof(argv[optind + 2]); + + if (mc2_param.crit == CRIT_LEVEL_C) { + migrate = 0; + config.cpu = -1; + } + + if (migrate) { + ret = be_migrate_to_domain(cluster); + if (ret < 0) + bail_out("could not migrate to target partition or cluster."); + } + + /* reservation config */ + config.id = gettid(); + config.polling_params.budget = budget; + config.polling_params.period = period; + config.polling_params.offset = 0; + config.polling_params.relative_deadline = 0; + + if (config.polling_params.budget > config.polling_params.period) { + usage("The budget must not exceed the period."); + } + + /* create a reservation */ + ret = reservation_create(res_type, &config); + if (ret < 0) { + bail_out("failed to create reservation."); + } + + init_rt_task_param(¶m); + param.exec_cost = wcet; + param.period = period; + param.priority = priority; + param.cls = RT_CLASS_HARD; + param.release_policy = TASK_PERIODIC; + param.budget_policy = NO_ENFORCEMENT; + if (migrate) { + param.cpu = gettid(); + } + ret = set_rt_task_param(gettid(), ¶m); + if (ret < 0) + bail_out("could not setup rt task params"); + + mc2_param.res_id = gettid(); + ret = set_mc2_task_param(gettid(), &mc2_param); + if (ret < 0) + bail_out("could not setup mc2 task params"); + + ret = init_litmus(); + if (ret != 0) + bail_out("init_litmus() failed\n"); + + start = wctime(); + + 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(); + } + + do { + if (verbose) { + printf("rtspin:%u @ %.4fms\n", gettid(), + (wctime() - start) * 1000); + } + } while (job(wcet_ms * 0.001, start + duration)); + + ret = task_mode(BACKGROUND_TASK); + if (ret != 0) + bail_out("could not become regular task (huh?)"); + + reservation_destroy(gettid(), config.cpu); + return 0; +} + diff --git a/include/cache_common.h b/include/cache_common.h new file mode 100644 index 0000000..8239233 --- /dev/null +++ b/include/cache_common.h @@ -0,0 +1,140 @@ +#ifndef __CACHE_COMMON_H__ +#define __CACHE_COMMON_H__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "litmus.h" +#include "asm/cycles.h" + +#if defined(__i386__) || defined(__x86_64__) +#include "asm/irq.h" +#endif + + +#define UNCACHE_DEV "/dev/litmus/uncache" + +static void die(char *error) +{ + fprintf(stderr, "Error: %s (errno: %m)\n", + error); + exit(1); +} + +static int lock_memory(void) +{ + return mlockall(MCL_CURRENT | MCL_FUTURE); +} + +/* define CACHELINE_SIZE if not provided by compiler args */ +#ifndef CACHELINE_SIZE +#if defined(__i386__) || defined(__x86_64__) +/* recent intel cpus */ +#define CACHELINE_SIZE 64 +#elif defined(__arm__) +/* at least with Cortex-A9 cpus ("8 words") */ +#define CACHELINE_SIZE 32 +#else +#error "Could not determine cacheline size!" +#endif +#endif + +#define INTS_IN_CACHELINE (CACHELINE_SIZE/sizeof(int)) +typedef struct cacheline +{ + int line[INTS_IN_CACHELINE]; +} __attribute__((aligned(CACHELINE_SIZE))) cacheline_t; + +#define CACHELINES_IN_1KB (1024 / sizeof(cacheline_t)) + + +static cacheline_t* alloc_arena(size_t size, int use_huge_pages, int use_uncache_pages) +{ + int flags = MAP_PRIVATE | MAP_POPULATE; + cacheline_t* arena = NULL; + int fd; + + if(use_huge_pages) + flags |= MAP_HUGETLB; + + if(use_uncache_pages) { + fd = open(UNCACHE_DEV, O_RDWR); + if (fd == -1) + die("Failed to open uncache device. Are you running the LITMUS^RT kernel?"); + } + else { + fd = -1; + flags |= MAP_ANONYMOUS; + } + + arena = mmap(0, size, PROT_READ | PROT_WRITE, flags, fd, 0); + + if(use_uncache_pages) + close(fd); + + assert(arena); + + return arena; +} + +static void dealloc_arena(cacheline_t* arena, size_t size) +{ + int ret = munmap((void*)arena, size); + if(ret != 0) + die("munmap() error"); +} + +static int randrange(int min, int max) +{ + /* generate a random number on the range [min, max) w/o skew */ + int limit = max - min; + int devisor = RAND_MAX/limit; + int retval; + + do { + retval = rand() / devisor; + } while(retval == limit); + retval += min; + + return retval; +} + +static void init_arena(cacheline_t* arena, size_t size) +{ + int i; + size_t num_arena_elem = size / sizeof(cacheline_t); + + /* Generate a cycle among the cache lines using Sattolo's algorithm. + Every int in the cache line points to the same cache line. + Note: Sequential walk doesn't care about these values. */ + for (i = 0; i < num_arena_elem; i++) { + int j; + for(j = 0; j < INTS_IN_CACHELINE; ++j) + arena[i].line[j] = i; + } + while(1 < i--) { + int j = randrange(0, i); + cacheline_t temp = arena[j]; + arena[j] = arena[i]; + arena[i] = temp; + } +} + +#endif diff --git a/include/litmus.h b/include/litmus.h index b9dbdb5..4b97b87 100644 --- a/include/litmus.h +++ b/include/litmus.h @@ -40,6 +40,8 @@ extern "C" { #include "migration.h" +#include "litmus/mc2_common.h" + /** * @private * The numeric ID of the LITMUS^RT scheduling class. @@ -146,6 +148,12 @@ int sporadic_clustered(lt_t e_ns, lt_t p_ns, int cluster); /** Convert microseconds to nanoseconds * @param us Time units in microseconds */ #define us2ns(us) ((us)*1000LL) +#define ns2s(ns) ((ns)/1000000000LL) +#define ns2ms(ns) ((ns)/1000000LL) +#define ns2us(ns) ((ns)/1000LL) +#define us2ms(us) ((us)/1000LL) +#define us2s(us) ((us)/1000000LL) +#define ms2s(ms) ((ms)/1000LL) /** * Locking protocols for allocated shared objects @@ -416,6 +424,18 @@ int null_call(cycles_t *timestamp); */ struct control_page* get_ctrl_page(void); +int reservation_create(int rtype, void *config); + +int reservation_destroy(unsigned int reservation_id, int cpu); + +int set_mc2_task_param(pid_t pid, struct mc2_task* param); + +int set_page_color(int cpu); + +int test_call(unsigned int param); + +typedef struct cacheline cacheline_t; + #ifdef __cplusplus } #endif diff --git a/src/syscalls.c b/src/syscalls.c index c68f15b..ab064ad 100644 --- a/src/syscalls.c +++ b/src/syscalls.c @@ -86,3 +86,28 @@ int null_call(cycles_t *timestamp) { return syscall(__NR_null_call, timestamp); } + +int reservation_create(int rtype, void *config) +{ + return syscall(__NR_reservation_create, rtype, config); +} + +int reservation_destroy(unsigned int reservation_id, int cpu) +{ + return syscall(__NR_reservation_destroy, reservation_id, cpu); +} + +int set_mc2_task_param(pid_t pid, struct mc2_task *param) +{ + return syscall(__NR_set_mc2_task_param, pid, param); +} + +int set_page_color(int cpu) +{ + return syscall(__NR_set_page_color, cpu); +} + +int test_call(unsigned int param) +{ + return syscall(__NR_test_call, param); +} -- cgit v1.2.2