/* based_mt_task.c -- A basic multi-threaded real-time task skeleton. * * This (by itself useless) task demos how to setup a multi-threaded LITMUS^RT * real-time task. Familiarity with the single threaded example (base_task.c) * is assumed. * * Currently, liblitmus still lacks automated support for real-time * tasks, but internaly it is thread-safe, and thus can be used together * with pthreads. */ #include #include #include #include #include #include #include #include /* Include gettid() */ #include /* Include threading support. */ #include /* Include the LITMUS^RT API.*/ #include "litmus.h" #include "color_shm.h" #include "asm/cycles.h" #define PERIOD 100 #define RELATIVE_DEADLINE 100 #define EXEC_COST 10 /* Let's create 10 threads in the example, * for a total utilization of 1. */ #define NUM_THREADS 2 /* The information passed to each thread. Could be anything. */ struct thread_context { int id; int cpu; int job_no; char* shm1; char* shm2; char* shm3; }; /* The real-time thread program. Doesn't have to be the same for * all threads. Here, we only have one that will invoke job(). */ void* rt_thread(void *tcontext); /* Declare the periodically invoked job. * Returns 1 -> task should exit. * 0 -> task should continue. */ int job(struct thread_context *tcx); /* Catch errors. */ #define CALL( exp ) do { \ int ret; \ ret = exp; \ if (ret != 0) \ fprintf(stderr, "%s failed: %m\n", #exp);\ else \ fprintf(stderr, "%s ok.\n", #exp); \ } while (0) /* Basic setup is the same as in the single-threaded example. However, * we do some thread initiliazation first before invoking the job. */ int main(int argc, char** argv) { int i; struct thread_context ctx[NUM_THREADS]; pthread_t task[NUM_THREADS]; char *shm1; char *shm2; char *shm3; struct color_ioctl_cmd shm_info; struct color_ioctl_offset shm_offset; /* The task is in background mode upon startup. */ /***** * 1) Command line paramter parsing would be done here. */ /***** * 2) Work environment (e.g., global data structures, file data, etc.) would * be setup here. */ shm_info.color = 0x00000001; shm_info.bank = 0x00000020; shm_offset.offset = 0; shm_offset.lock = 1; shm1 = (char*)color_mmap(1024, shm_info, shm_offset); if (!shm1) { printf("color mmap failed.\n"); exit(-1); } else { printf("Mapped vaddr = %p\n", shm1); } shm_info.color = 0x00000003; shm_offset.offset = 1024; shm2 = (char*)color_mmap(4096, shm_info, shm_offset); if (!shm2) { printf("color mmap failed.\n"); exit(-1); } else { printf("Mapped vaddr = %p\n", shm2); } mlockall(MCL_CURRENT | MCL_FUTURE); /***** * 3) Initialize LITMUS^RT. * Task parameters will be specified per thread. */ init_litmus(); /***** * 4) Launch threads. */ for (i = 0; i < NUM_THREADS; i++) { ctx[i].id = i; ctx[i].cpu = 0; ctx[i].job_no = 0; ctx[i].shm1 = shm1; ctx[i].shm3 = shm2; ctx[i].shm2 = shm3; pthread_create(task + i, NULL, rt_thread, (void *) (ctx + i)); } /***** * 5) Wait for RT threads to terminate. */ for (i = 0; i < NUM_THREADS; i++) pthread_join(task[i], NULL); /***** * 6) Clean up, maybe print results and stats, and exit. */ return 0; } /* A real-time thread is very similar to the main function of a single-threaded * real-time app. Notice, that init_rt_thread() is called to initialized per-thread * data structures of the LITMUS^RT user space libary. */ void* rt_thread(void *tcontext) { int do_exit; struct thread_context *ctx = (struct thread_context *) tcontext; struct rt_task param; int ret; struct mc2_task mc2_param; struct reservation_config res_config; /* Set up task parameters */ init_rt_task_param(¶m); param.exec_cost = ms2ns(EXEC_COST); param.period = ms2ns(PERIOD); param.relative_deadline = ms2ns(RELATIVE_DEADLINE*(ctx->id+1)); /* What to do in the case of budget overruns? */ param.budget_policy = NO_ENFORCEMENT; /* The task class parameter is ignored by most plugins. */ param.cls = RT_CLASS_SOFT; /* The priority parameter is only used by fixed-priority plugins. */ param.priority = LITMUS_LOWEST_PRIORITY; /* Make presence visible. */ printf("RT Thread %d active.\n", ctx->id); /* reservation config */ res_config.id = gettid(); res_config.polling_params.budget = ms2ns(EXEC_COST+1); res_config.polling_params.period = param.period; res_config.polling_params.offset = 0; res_config.polling_params.relative_deadline = 0; res_config.priority = LITMUS_MAX_PRIORITY; res_config.cpu = ctx->cpu; mc2_param.crit = CRIT_LEVEL_A; mc2_param.res_id = gettid(); /***** * 1) Initialize real-time settings. */ CALL( init_rt_thread() ); ret = reservation_create(PERIODIC_POLLING, &res_config); if (ret < 0) { printf("reservation failed.\n"); return NULL; } /* To specify a partition, do * * param.cpu = CPU; * be_migrate_to(CPU); * * where CPU ranges from 0 to "Number of CPUs" - 1 before calling * set_rt_task_param(). */ param.cpu = ctx->cpu; ret = be_migrate_to_cpu(ctx->cpu); if (ret < 0) { printf("RT Thread %d fails to migrate to CPU%d\n", ctx->id, ctx->cpu); return NULL; } CALL( set_rt_task_param(gettid(), ¶m) ); CALL( set_mc2_task_param(gettid(), &mc2_param) ); /***** * 2) Transition to real-time mode. */ CALL( task_mode(LITMUS_RT_TASK) ); /* The task is now executing as a real-time task if the call didn't fail. */ /***** * 3) Invoke real-time jobs. */ do { /* Wait until the next job is released. */ sleep_next_period(); /* Invoke job. */ do_exit = job(ctx); } while (!do_exit); /***** * 4) Transition to background mode. */ CALL( task_mode(BACKGROUND_TASK) ); reservation_destroy(gettid(), res_config.cpu); return NULL; } int job(struct thread_context *tcx) { int i; char* buf1 = tcx->shm1; char* buf2 = tcx->shm2; char* buf3 = tcx->shm3; char tmp = 0; /* Do real-time calculation. */ printf("Task %d Job %d executinig\n", tcx->id, tcx->job_no); if (tcx->id == 0) { printf("READ\n"); for (i=0; i<600; i++) { char t = buf1[i]; printf("%x ", t); } printf("\n"); ; } //test_call(0); tcx->job_no++; if (tcx->job_no == 10) return 1; /* Don't exit. */ return 0; }