/** * Copyright 2019 Sims Hill Osborne and Joshua Bakita * * This header provides facilities by which to separably run and time TACLeBench **/ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include // This is only visible if _GNU_SOURCE is defined, and that define does not // come along to places where this file is included. Address this by manually // forcing it into the global namespace. extern int sched_getcpu(); // These constants correspond to the imx6q-sabredb platform #define LINE_SIZE 32 #define L2_SIZE 16*2048*32 #if __arm__ #include #include #endif #define LITMUS 0 #define MC2 0 #define MMDC_PROF 0 #if LITMUS #include #endif #if MMDC_PROF #include "/media/speedy/litmus/tools/mmdc/mmdc.h" #endif #if LITMUS #define SET_UP LOAD_PARAMS SETUP_LITMUS #else #define SET_UP LOAD_PARAMS #endif #if MMDC_PROF #define LOAD_PARAMS LOAD_PARAMS_ITRL SETUP_MMDC #else #define LOAD_PARAMS LOAD_PARAMS_ITRL #endif #define LOAD_PARAMS_ITRL \ if (argc != 6) { \ fprintf(stderr, "Usage: %s \n", argv[0]);\ fprintf(stderr, " integer number of iterations. -1 for infitite.\n");\ fprintf(stderr, " 1 to save results, 0 to discard.\n");\ exit(1);\ }\ char *thisProgram=argv[1];\ int parsedMaxJobs=atoi(argv[2]);\ unsigned int thisCore=atoi(argv[3]);\ thisCore = sched_getcpu();\ char *runID=argv[4];\ int output=atoi(argv[5]);\ if (parsedMaxJobs < 0 && output != 0){\ fprintf(stderr, "Infinite loops only supported when output is disabled!\n");\ exit(1);\ }\ /* Cheat. -1 is larger than jobsComplete can ever reach. */\ unsigned int maxJobs = parsedMaxJobs;\ struct timespec _start, _end;\ int jobsComplete;\ int jobs_complete = -1;\ float *progTime = malloc(sizeof(float)*maxJobs*output);\ memset(progTime, 0, sizeof(float)*maxJobs*output);\ char fileName[64];\ float *mmdc_read = malloc(sizeof(float)*maxJobs*output);\ float *mmdc_write = malloc(sizeof(float)*maxJobs*output);\ memset(mmdc_read, 0, sizeof(float)*maxJobs*output);\ memset(mmdc_write, 0, sizeof(float)*maxJobs*output);\ if (strlen(runID) + 5 > sizeof(fileName)) {\ fprintf(stderr, "Run ID is too large! Keep it to less than 60 characters.\n");\ exit(1);\ }\ strcpy(fileName, runID);\ strcat(fileName, ".txt");\ mlockall(MCL_CURRENT || MCL_FUTURE); #define SETUP_MMDC \ MMDC_PROFILE_RES_t mmdc_res; \ memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\ int fd = open("/dev/mem", O_RDWR, 0);\ if (fd < 0) {\ perror("Unable to open /dev/mem");\ exit(1);\ }\ pMMDC_t mmdc = mmap(NULL, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MMDC_P0_IPS_BASE_ADDR);\ if (mmdc == MAP_FAILED) {\ perror("Unable to map MMDC address space");\ exit(1);\ }\ mmdc->madpcr1 = axi_arm1;\ msync(&(mmdc->madpcr1),4,MS_SYNC); #define SETUP_LITMUS \ unsigned int cpu = atoi(thisCore); \ unsigned int wait = 0; \ if (be_migrate_to_domain(cpu) < 0) { \ perror("Unable to migrate to specified CPU"); \ exit(1); \ } \ struct reservation_config res; \ res.id = gettid(); \ res.cpu = cpu; \ res.priority = LITMUS_HIGHEST_PRIORITY; \ /* we take over half the CPU time (these are ns) */ \ res.polling_params.budget = ms2ns(3000); \ res.polling_params.period = ms2ns(3000); \ res.polling_params.offset = 0; \ res.polling_params.relative_deadline = ms2ns(3000); \ /* Not 100% sure that we should use periodic polling */ \ if (reservation_create(PERIODIC_POLLING, &res) < 0) { \ perror("Unable to create reservation"); \ exit(1); \ } \ struct rt_task rt_param; \ init_rt_task_param(&rt_param); \ /* Supposedly the next two parameters are irrelevant when reservations are enabled, but I'm leaving them anyway... */ \ rt_param.exec_cost = ms2ns(999); \ rt_param.period = ms2ns(1000); \ rt_param.priority = LITMUS_HIGHEST_PRIORITY; \ rt_param.cls = RT_CLASS_HARD; \ rt_param.release_policy = TASK_PERIODIC; \ rt_param.budget_policy = NO_ENFORCEMENT; \ rt_param.cpu = cpu; \ if (set_rt_task_param(gettid(), &rt_param) < 0) { \ perror("Unable to set real-time parameters"); \ exit(1); \ } \ if (init_litmus() != 0) { \ perror("init_litmus failed"); \ exit(1); \ } \ MC2_SETUP \ if (task_mode(LITMUS_RT_TASK) != 0) { \ perror("Unable to become real-time task"); \ exit(1); \ } \ if (wait && wait_for_ts_release() != 0) { \ perror("Unable to wait for taskset release"); \ exit(1); \ } #if MC2 #define MC2_SETUP \ struct mc2_task mc2_param; \ mc2_param.res_id = gettid(); \ mc2_param.crit = CRIT_LEVEL_A; \ if (set_mc2_task_param(gettid(), &mc2_param) < 0) { \ perror("Unable to set MC^2 task params"); \ exit(1); \ } \ set_page_color(rt_param.cpu); #else #define MC2_SETUP #endif #define CLEANUP_LITMUS \ if (task_mode(BACKGROUND_TASK) != 0) { \ perror("Unable to become a real-time task"); \ exit(1); \ } \ reservation_destroy(gettid(), rt_param.cpu); #if MMDC_PROF #define SAVE_RESULTS \ if(jobs_complete >= maxJobs) {\ fprintf(stderr, "Max jobs setting too small! Trying to record job #%d when we only have space for %d jobs. Exiting...\n", jobs_complete, maxJobs);\ exit(1);\ }\ if(jobs_complete > -1 && output) {\ progTime[jobs_complete] = _end.tv_sec - _start.tv_sec;\ progTime[jobs_complete] *= 1000000000;\ progTime[jobs_complete] += _end.tv_nsec - _start.tv_nsec;\ mmdc_read[jobs_complete] = mmdc_res.read_bytes;\ mmdc_write[jobs_complete] = mmdc_res.write_bytes;\ } #else #define SAVE_RESULTS \ if(jobs_complete >= maxJobs) {\ fprintf(stderr, "Max jobs setting too small! Trying to record job #%d when we only have space for %d jobs. Exiting...\n", jobs_complete, maxJobs);\ exit(1);\ }\ if(jobs_complete > -1 && output) {\ progTime[jobs_complete] = _end.tv_sec - _start.tv_sec;\ progTime[jobs_complete] *= 1000000000;\ progTime[jobs_complete] += _end.tv_nsec - _start.tv_nsec;\ } #endif #if LITMUS #define WRITE_TO_FILE WRITE_TO_FILE_ITRNL CLEANUP_LITMUS #else #define WRITE_TO_FILE WRITE_TO_FILE_ITRNL #endif #define WRITE_TO_FILE_ITRNL if (output){\ munlockall();\ FILE *fp=fopen(fileName, "a");\ if (fp == NULL) {\ perror("Unable to open output file");\ exit(1);\ }\ for (int i = 0; i <= jobs_complete; i++){\ fprintf(fp, "%s none %u none %d %.f %s %d %.f %.f \n",\ thisProgram, thisCore, maxJobs,\ progTime[i], runID, i, mmdc_read[i], mmdc_write[i]);\ }\ fclose(fp);\ } #if __arm__ // On ARM, manually flush the cache #define FLUSH_CACHES \ volatile uint8_t buffer[L2_SIZE * 4]; \ for (uint32_t j = 0; j < 4; j++) \ for (uint32_t i = 0; i < L2_SIZE * 4; i += LINE_SIZE) \ buffer[i]++; #else // On x86 call the wbinvld instruction (it's in a kernel module due to it being ring-0) #define FLUSH_CACHES \ FILE *fp = fopen("/proc/wbinvd", "r");\ if (fp == NULL) {\ perror("Cache flush module interface cannot be opened");\ exit(1);\ }\ char dummy;\ if (fread(&dummy, 1, 1, fp) == 0) {\ perror("Unable to access cache flush module interface");\ exit(1);\ }\ fclose(fp); #endif #if MMDC_PROF #define START_TIMER \ /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */ \ start_mmdc_profiling(mmdc); \ /*nanosleep(&(struct timespec){0, ms2ns(999)}, NULL);*/ \ clock_gettime(CLOCK_MONOTONIC, &_start); #else #define START_TIMER clock_gettime(CLOCK_MONOTONIC, &_start); #endif #if MMDC_PROF #define STOP_TIMER \ clock_gettime(CLOCK_MONOTONIC, &_end); \ /* This freezes the profiling and makes results available */ \ pause_mmdc_profiling(mmdc); \ get_mmdc_profiling_results(mmdc, &mmdc_res); #else #define STOP_TIMER clock_gettime(CLOCK_MONOTONIC, &_end); #endif #define SLEEP nanosleep((const struct timespec[]){{0, 1000000}}, NULL); #if LITMUS #define START_LOOP \ if (sleep_next_period() != 0) { \ perror("Unable to sleep for next period"); \ } \ FLUSH_CACHES START_TIMER #else #define START_LOOP sched_yield(); FLUSH_CACHES START_TIMER #endif #define STOP_LOOP STOP_TIMER jobs_complete++; SAVE_RESULTS /* Intended structure main SET_UP notice that STOP LOOP negates the ++ if outout=0 for (jobsComplete=0; jobsComplete