/** * Copyright 2019 Sims Hill Osborne and Joshua Bakita * * This header provides facilities by which to separably run and time TACLeBench **/ #include #include #include #include #include #include #include #include // 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 1 #define MC2 1 #define MMDC_PROF 1 #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) { \ printf("Usage: %s \n", argv[0]);\ exit(1);\ }\ char *thisProgram=argv[1];\ int maxJobs=atoi(argv[2]);\ char *thisCore=argv[3];\ char *runID=argv[4];\ int output=atoi(argv[5]);\ pid_t killMe;\ struct timespec start, end;\ int jobsComplete;\ int jobs_complete = -1;\ float progTime[maxJobs*output];\ memset(progTime, 0, sizeof(float)*maxJobs*output);\ char fileName[50];\ char *bigArray;\ int wasteCount;\ float mmdc_read[maxJobs];\ float mmdc_write[maxJobs];\ memset(mmdc_read, 0, sizeof(float)*maxJobs);\ memset(mmdc_write, 0, sizeof(float)*maxJobs);\ 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>-1) {\ 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>-1) {\ 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("Error opening file. \n");\ exit(1);\ }\ for(int i = 0; i <= jobs_complete; i++){\ fprintf(fp, "%s none %s 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 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=-1; jobsComplete