/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Include gettid() */
#include <sys/types.h>
/* Include threading support. */
#include <pthread.h>
/* 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;
}