Skip to content

Commit 482e47a

Browse files
author
Juri Lelli
authored
Merge pull request #22 from scheduler-tools/for-origin/broader-cpusets
rtapp: Add broader support for specifying cpusets
2 parents bd639b4 + 8b30bfb commit 482e47a

File tree

5 files changed

+243
-36
lines changed

5 files changed

+243
-36
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
/*
3+
* Simple use case which creates a thread with 3 phases each running for
4+
* 1.5ms. In phase 1 the thread will be affined to CPU 0, in phase 2 it
5+
* will be affined to CPU 1 and in the third phase it will be affined to
6+
* CPUs 2 (inherit from "cpus" at task level).
7+
*/
8+
"tasks" : {
9+
"thread0" : {
10+
"cpus" : [2],
11+
"phases" : {
12+
"phase1" : {
13+
"cpus" : [0],
14+
"loop" : 1,
15+
"run" : 1500
16+
},
17+
"phase2" : {
18+
"cpus" : [1],
19+
"loop" : 1,
20+
"run" : 1500
21+
},
22+
"phase3" : {
23+
"loop" : 1,
24+
"run" : 1500
25+
}
26+
}
27+
}
28+
},
29+
"global" : {
30+
"duration" : 2,
31+
"calibration" : "CPU0",
32+
"default_policy" : "SCHED_OTHER",
33+
"pi_enabled" : false,
34+
"lock_pages" : false,
35+
"logdir" : "./",
36+
"log_basename" : "rt-app1",
37+
"ftrace" : true,
38+
"gnuplot" : true
39+
}
40+
}

doc/tutorial.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,43 @@ below are equals:
232232
}
233233
}
234234

235+
"cpus" can be specified at task level or phase level. As an example, below
236+
creates two threads. One thread will run with affinity of CPU 2 and 3. The
237+
second task will run first phase with affinity to CPU 0 and 1, second phase with
238+
affinity to CPU 2, and the third phase with affinity to CPU 4, 5, and 6 (it will
239+
inherit the affinity from the task level):
240+
241+
"task" : {
242+
"thread1" : {
243+
"cpus" : [2,3],
244+
"phases" : {
245+
"phase1" : {
246+
"run" : 10,
247+
"sleep" : 10
248+
}
249+
}
250+
}
251+
"thread2" : {
252+
"cpus" : [4,5,6],
253+
"phases" : {
254+
"phase1" : {
255+
"cpus" : [0,1],
256+
"run" : 10,
257+
"sleep" : 10
258+
}
259+
"phase2" : {
260+
"cpus" : [2],
261+
"run" : 10,
262+
"sleep" : 10
263+
}
264+
"phase3" : {
265+
"run" : 10,
266+
"sleep" : 10
267+
}
268+
}
269+
}
270+
}
271+
235272
* loop: Integer. Define the number of times the parent object must be run.
236273
The parent object can be a phase or a thread. For phase object, the loop
237274
defines the number of time the phase will be executed before starting the next

src/rt-app.c

Lines changed: 112 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,116 @@ shutdown(int sig)
474474
exit(EXIT_SUCCESS);
475475
}
476476

477+
static void create_cpuset_str(cpuset_data_t *cpu_data)
478+
{
479+
unsigned int cpu_count = CPU_COUNT_S(cpu_data->cpusetsize,
480+
cpu_data->cpuset);
481+
unsigned int i;
482+
unsigned int idx = 0;
483+
484+
/*
485+
* Assume we can go up to 9999 cpus. Each cpu would take up to 4 + 2
486+
* bytes (4 for the number and 2 for the comma and space). 2 bytes
487+
* for beginning bracket + space and 2 bytes for end bracket and space
488+
* and finally null-terminator.
489+
*/
490+
unsigned int size_needed = cpu_count * 6 + 2 + 2 + 1;
491+
492+
if (cpu_count > 9999) {
493+
log_error("Too many cpus specified. Up to 9999 cpus supported");
494+
exit(EXIT_FAILURE);
495+
}
496+
497+
cpu_data->cpuset_str = malloc(size_needed);
498+
strcpy(cpu_data->cpuset_str, "[ ");
499+
idx += 2;
500+
501+
for (i = 0; i < 10000 && cpu_count; ++i) {
502+
unsigned int n;
503+
504+
if (CPU_ISSET(i, cpu_data->cpuset)) {
505+
--cpu_count;
506+
if (size_needed <= (idx + 1)) {
507+
log_error("Not enough memory for array");
508+
exit(EXIT_FAILURE);
509+
}
510+
n = snprintf(&cpu_data->cpuset_str[idx],
511+
size_needed - idx - 1, "%d", i);
512+
if (n > 0) {
513+
idx += n;
514+
} else {
515+
log_error("Error creating array");
516+
exit(EXIT_FAILURE);
517+
}
518+
if (size_needed <= (idx + 1)) {
519+
log_error("Not enough memory for array");
520+
exit(EXIT_FAILURE);
521+
}
522+
if (cpu_count) {
523+
strncat(cpu_data->cpuset_str, ", ",
524+
size_needed - idx - 1);
525+
idx += 2;
526+
}
527+
}
528+
}
529+
strncat(cpu_data->cpuset_str, " ]", size_needed - idx - 1);
530+
}
531+
532+
static void set_thread_affinity(thread_data_t *data, cpuset_data_t *cpu_data)
533+
{
534+
int ret;
535+
cpuset_data_t *actual_cpu_data = &data->cpu_data;
536+
537+
if (data->def_cpu_data.cpuset == NULL) {
538+
/* Get default affinity */
539+
cpu_set_t cpuset;
540+
unsigned int cpu_count;
541+
unsigned int cpu = 0;
542+
543+
ret = pthread_getaffinity_np(pthread_self(),
544+
sizeof(cpu_set_t), &cpuset);
545+
if (ret != 0) {
546+
errno = ret;
547+
perror("pthread_get_affinity");
548+
exit(EXIT_FAILURE);
549+
}
550+
cpu_count = CPU_COUNT(&cpuset);
551+
data->def_cpu_data.cpusetsize = CPU_ALLOC_SIZE(cpu_count);
552+
data->def_cpu_data.cpuset = CPU_ALLOC(cpu_count);
553+
memcpy(data->def_cpu_data.cpuset, &cpuset,
554+
data->def_cpu_data.cpusetsize);
555+
create_cpuset_str(&data->def_cpu_data);
556+
data->curr_cpu_data = &data->def_cpu_data;
557+
}
558+
559+
/*
560+
* Order of preference:
561+
* 1. Phase cpuset
562+
* 2. Task level cpuset
563+
* 3. Default cpuset
564+
*/
565+
if (cpu_data->cpuset != NULL)
566+
actual_cpu_data = cpu_data;
567+
568+
if (actual_cpu_data->cpuset == NULL)
569+
actual_cpu_data = &data->def_cpu_data;
570+
571+
if (!CPU_EQUAL(actual_cpu_data->cpuset, data->curr_cpu_data->cpuset))
572+
{
573+
log_debug("[%d] setting cpu affinity to CPU(s) %s", data->ind,
574+
actual_cpu_data->cpuset_str);
575+
ret = pthread_setaffinity_np(pthread_self(),
576+
actual_cpu_data->cpusetsize,
577+
actual_cpu_data->cpuset);
578+
if (ret != 0) {
579+
errno = ret;
580+
perror("pthread_setaffinity_np");
581+
exit(EXIT_FAILURE);
582+
}
583+
data->curr_cpu_data = actual_cpu_data;
584+
}
585+
}
586+
477587
void *thread_body(void *arg)
478588
{
479589
thread_data_t *data = (thread_data_t*) arg;
@@ -501,20 +611,6 @@ void *thread_body(void *arg)
501611
/* Get the 1st phase's data */
502612
pdata = &data->phases[0];
503613

504-
/* Set thread affinity */
505-
if (data->cpuset != NULL)
506-
{
507-
log_notice("[%d] setting cpu affinity to CPU(s) %s", data->ind,
508-
data->cpuset_str);
509-
ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
510-
data->cpuset);
511-
if (ret < 0) {
512-
errno = ret;
513-
perror("pthread_setaffinity_np");
514-
exit(EXIT_FAILURE);
515-
}
516-
}
517-
518614
/* Set scheduling policy and print pretty info on stdout */
519615
log_notice("[%d] Using %s policy with priority %d", data->ind, data->sched_policy_descr, data->sched_prio);
520616
switch (data->sched_policy)
@@ -662,6 +758,8 @@ void *thread_body(void *arg)
662758
while (continue_running && (i != data->loop)) {
663759
struct timespec t_diff, t_rel_start;
664760

761+
set_thread_affinity(data, &pdata->cpu_data);
762+
665763
if (opts.ftrace)
666764
log_ftrace(ft_data.marker_fd, "[%d] begins loop %d phase %d step %d", data->ind, i, j, loop);
667765
log_debug("[%d] begins loop %d phase %d step %d", data->ind, i, j, loop);;

src/rt-app_parse_config.c

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,41 @@ obj_is_event(char *name)
648648
return 0;
649649
}
650650

651+
static void parse_cpuset_data(struct json_object *obj, cpuset_data_t *data)
652+
{
653+
struct json_object *cpuset_obj, *cpu;
654+
unsigned int max_cpu = sysconf(_SC_NPROCESSORS_CONF) - 1;
655+
656+
/* cpuset */
657+
cpuset_obj = get_in_object(obj, "cpus", TRUE);
658+
if (cpuset_obj) {
659+
unsigned int i;
660+
unsigned int cpu_idx;
661+
662+
assure_type_is(cpuset_obj, obj, "cpus", json_type_array);
663+
data->cpuset_str = strdup(json_object_to_json_string(cpuset_obj));
664+
data->cpusetsize = sizeof(cpu_set_t);
665+
data->cpuset = malloc(data->cpusetsize);
666+
CPU_ZERO(data->cpuset);
667+
for (i = 0; i < json_object_array_length(cpuset_obj); i++) {
668+
cpu = json_object_array_get_idx(cpuset_obj, i);
669+
cpu_idx = json_object_get_int(cpu);
670+
if (cpu_idx > max_cpu) {
671+
log_critical(PIN2 "Invalid cpu %d in cpuset %s", cpu_idx, data->cpuset_str);
672+
free(data->cpuset);
673+
free(data->cpuset_str);
674+
exit(EXIT_INV_CONFIG);
675+
}
676+
CPU_SET(cpu_idx, data->cpuset);
677+
}
678+
} else {
679+
data->cpuset_str = strdup("-");
680+
data->cpuset = NULL;
681+
data->cpusetsize = 0;
682+
}
683+
log_info(PIN "key: cpus %s", data->cpuset_str);
684+
}
685+
651686
static void
652687
parse_thread_phase_data(struct json_object *obj,
653688
phase_data_t *data, rtapp_options_t *opts, long tag)
@@ -681,6 +716,7 @@ parse_thread_phase_data(struct json_object *obj,
681716
i++;
682717
}
683718
}
719+
parse_cpuset_data(obj, &data->cpu_data);
684720
}
685721

686722
static void
@@ -689,8 +725,8 @@ parse_thread_data(char *name, struct json_object *obj, int index,
689725
{
690726
char *policy;
691727
char def_policy[RTAPP_POLICY_DESCR_LENGTH];
692-
struct json_object *cpuset_obj, *phases_obj, *cpu, *resources, *locks;
693-
int i, cpu_idx, prior_def;
728+
struct json_object *phases_obj, *resources;
729+
int prior_def;
694730

695731
log_info(PFX "Parsing thread %s [%d]", name, index);
696732

@@ -699,8 +735,11 @@ parse_thread_data(char *name, struct json_object *obj, int index,
699735
data->ind = index;
700736
data->name = strdup(name);
701737
data->lock_pages = opts->lock_pages;
702-
data->cpuset = NULL;
703-
data->cpuset_str = NULL;
738+
data->cpu_data.cpuset = NULL;
739+
data->cpu_data.cpuset_str = NULL;
740+
data->curr_cpu_data = NULL;
741+
data->def_cpu_data.cpuset = NULL;
742+
data->def_cpu_data.cpuset_str = NULL;
704743

705744
/* policy */
706745
policy_to_string(opts->policy, def_policy);
@@ -734,22 +773,7 @@ parse_thread_data(char *name, struct json_object *obj, int index,
734773
data->deadline = get_int_value_from(obj, "deadline", TRUE, data->period);
735774

736775
/* cpuset */
737-
cpuset_obj = get_in_object(obj, "cpus", TRUE);
738-
if (cpuset_obj) {
739-
assure_type_is(cpuset_obj, obj, "cpus", json_type_array);
740-
data->cpuset_str = strdup(json_object_to_json_string(cpuset_obj));
741-
data->cpuset = malloc(sizeof(cpu_set_t));
742-
CPU_ZERO(data->cpuset);
743-
for (i = 0; i < json_object_array_length(cpuset_obj); i++) {
744-
cpu = json_object_array_get_idx(cpuset_obj, i);
745-
cpu_idx = json_object_get_int(cpu);
746-
CPU_SET(cpu_idx, data->cpuset);
747-
}
748-
} else {
749-
data->cpuset_str = strdup("-");
750-
data->cpuset = NULL;
751-
}
752-
log_info(PIN "key: cpus %s", data->cpuset_str);
776+
parse_cpuset_data(obj, &data->cpu_data);
753777

754778
/* initial delay */
755779
data->delay = get_int_value_from(obj, "delay", TRUE, 0);

src/rt-app_types.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,17 @@ typedef struct _event_data_t {
141141
int count;
142142
} event_data_t;
143143

144+
typedef struct _cpuset_data_t {
145+
cpu_set_t *cpuset;
146+
char *cpuset_str;
147+
size_t cpusetsize;
148+
} cpuset_data_t;
149+
144150
typedef struct _phase_data_t {
145151
int loop;
146152
event_data_t *events;
147153
int nbevents;
154+
cpuset_data_t cpu_data;
148155
} phase_data_t;
149156

150157
typedef struct _thread_data_t {
@@ -153,8 +160,9 @@ typedef struct _thread_data_t {
153160
int lock_pages;
154161
int duration;
155162
rtapp_resource_t **resources;
156-
cpu_set_t *cpuset;
157-
char *cpuset_str;
163+
cpuset_data_t cpu_data; /* cpu set information */
164+
cpuset_data_t *curr_cpu_data; /* Current cpu set being used */
165+
cpuset_data_t def_cpu_data; /* Default cpu set for task */
158166

159167
unsigned long runtime, deadline, period;
160168

0 commit comments

Comments
 (0)