From: Olav Haugan ohaugan@codeaurora.org
Allow setting of cpusets at all levels of the spec.
Signed-off-by: Olav Haugan ohaugan@codeaurora.org Tested-by: Dietmar Eggemann dietmar.eggemann@arm.com Reviewed-by: Dietmar Eggemann dietmar.eggemann@arm.com Tested-by: Viresh Kumar viresh.kumar@linaro.org Acked-by: Juri Lelli juri.lelli@arm.com Signed-off-by: Stephen Boyd stephen.boyd@linaro.org ---
v4: -Rebased -Renamed example7.json to example8.json -Addressed comment formatting -Added check for # of cpus specified with bailout -Minor changes to example json
v3: -Rebased -Removed unused cpuset and locks variable -Removed include unistd.h
v2 changes: -Added documentation and example json -Clarified comments -Added check for invalid CPU -Reduced logging level for affinity setting message -Code fixes based on feedback
doc/examples/tutorial/example8.json | 40 ++++++++++++ doc/tutorial.txt | 37 +++++++++++ src/rt-app.c | 126 ++++++++++++++++++++++++++++++++---- src/rt-app_parse_config.c | 64 ++++++++++++------ src/rt-app_types.h | 12 +++- 5 files changed, 243 insertions(+), 36 deletions(-) create mode 100644 doc/examples/tutorial/example8.json
diff --git a/doc/examples/tutorial/example8.json b/doc/examples/tutorial/example8.json new file mode 100644 index 000000000000..9ffc594b4f47 --- /dev/null +++ b/doc/examples/tutorial/example8.json @@ -0,0 +1,40 @@ +{ + /* + * Simple use case which creates a thread with 3 phases each running for + * 1.5ms. In phase 1 the thread will be affined to CPU 0, in phase 2 it + * will be affined to CPU 1 and in the third phase it will be affined to + * CPUs 2 (inherit from "cpus" at task level). + */ + "tasks" : { + "thread0" : { + "cpus" : [2], + "phases" : { + "phase1" : { + "cpus" : [0], + "loop" : 1, + "run" : 1500 + }, + "phase2" : { + "cpus" : [1], + "loop" : 1, + "run" : 1500 + }, + "phase3" : { + "loop" : 1, + "run" : 1500 + } + } + } + }, + "global" : { + "duration" : 2, + "calibration" : "CPU0", + "default_policy" : "SCHED_OTHER", + "pi_enabled" : false, + "lock_pages" : false, + "logdir" : "./", + "log_basename" : "rt-app1", + "ftrace" : true, + "gnuplot" : true + } +} diff --git a/doc/tutorial.txt b/doc/tutorial.txt index 3af2c57b2844..36d79ffe0d15 100644 --- a/doc/tutorial.txt +++ b/doc/tutorial.txt @@ -232,6 +232,43 @@ below are equals: } }
+"cpus" can be specified at task level or phase level. As an example, below +creates two threads. One thread will run with affinity of CPU 2 and 3. The +second task will run first phase with affinity to CPU 0 and 1, second phase with +affinity to CPU 2, and the third phase with affinity to CPU 4, 5, and 6 (it will +inherit the affinity from the task level): + +"task" : { + "thread1" : { + "cpus" : [2,3], + "phases" : { + "phase1" : { + "run" : 10, + "sleep" : 10 + } + } + } + "thread2" : { + "cpus" : [4,5,6], + "phases" : { + "phase1" : { + "cpus" : [0,1], + "run" : 10, + "sleep" : 10 + } + "phase2" : { + "cpus" : [2], + "run" : 10, + "sleep" : 10 + } + "phase3" : { + "run" : 10, + "sleep" : 10 + } + } + } +} + * loop: Integer. Define the number of times the parent object must be run. The parent object can be a phase or a thread. For phase object, the loop defines the number of time the phase will be executed before starting the next diff --git a/src/rt-app.c b/src/rt-app.c index ec9710cfcc3f..981ed6f244c8 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -474,6 +474,116 @@ shutdown(int sig) exit(EXIT_SUCCESS); }
+static void create_cpuset_str(cpuset_data_t *cpu_data) +{ + unsigned int cpu_count = CPU_COUNT_S(cpu_data->cpusetsize, + cpu_data->cpuset); + unsigned int i; + unsigned int idx = 0; + + /* + * Assume we can go up to 9999 cpus. Each cpu would take up to 4 + 2 + * bytes (4 for the number and 2 for the comma and space). 2 bytes + * for beginning bracket + space and 2 bytes for end bracket and space + * and finally null-terminator. + */ + unsigned int size_needed = cpu_count * 6 + 2 + 2 + 1; + + if (cpu_count > 9999) { + log_error("Too many cpus specified. Up to 9999 cpus supported"); + exit(EXIT_FAILURE); + } + + cpu_data->cpuset_str = malloc(size_needed); + strcpy(cpu_data->cpuset_str, "[ "); + idx += 2; + + for (i = 0; i < 10000 && cpu_count; ++i) { + unsigned int n; + + if (CPU_ISSET(i, cpu_data->cpuset)) { + --cpu_count; + if (size_needed <= (idx + 1)) { + log_error("Not enough memory for array"); + exit(EXIT_FAILURE); + } + n = snprintf(&cpu_data->cpuset_str[idx], + size_needed - idx - 1, "%d", i); + if (n > 0) { + idx += n; + } else { + log_error("Error creating array"); + exit(EXIT_FAILURE); + } + if (size_needed <= (idx + 1)) { + log_error("Not enough memory for array"); + exit(EXIT_FAILURE); + } + if (cpu_count) { + strncat(cpu_data->cpuset_str, ", ", + size_needed - idx - 1); + idx += 2; + } + } + } + strncat(cpu_data->cpuset_str, " ]", size_needed - idx - 1); +} + +static void set_thread_affinity(thread_data_t *data, cpuset_data_t *cpu_data) +{ + int ret; + cpuset_data_t *actual_cpu_data = &data->cpu_data; + + if (data->def_cpu_data.cpuset == NULL) { + /* Get default affinity */ + cpu_set_t cpuset; + unsigned int cpu_count; + unsigned int cpu = 0; + + ret = pthread_getaffinity_np(pthread_self(), + sizeof(cpu_set_t), &cpuset); + if (ret != 0) { + errno = ret; + perror("pthread_get_affinity"); + exit(EXIT_FAILURE); + } + cpu_count = CPU_COUNT(&cpuset); + data->def_cpu_data.cpusetsize = CPU_ALLOC_SIZE(cpu_count); + data->def_cpu_data.cpuset = CPU_ALLOC(cpu_count); + memcpy(data->def_cpu_data.cpuset, &cpuset, + data->def_cpu_data.cpusetsize); + create_cpuset_str(&data->def_cpu_data); + data->curr_cpu_data = &data->def_cpu_data; + } + + /* + * Order of preference: + * 1. Phase cpuset + * 2. Task level cpuset + * 3. Default cpuset + */ + if (cpu_data->cpuset != NULL) + actual_cpu_data = cpu_data; + + if (actual_cpu_data->cpuset == NULL) + actual_cpu_data = &data->def_cpu_data; + + if (!CPU_EQUAL(actual_cpu_data->cpuset, data->curr_cpu_data->cpuset)) + { + log_debug("[%d] setting cpu affinity to CPU(s) %s", data->ind, + actual_cpu_data->cpuset_str); + ret = pthread_setaffinity_np(pthread_self(), + actual_cpu_data->cpusetsize, + actual_cpu_data->cpuset); + if (ret != 0) { + errno = ret; + perror("pthread_setaffinity_np"); + exit(EXIT_FAILURE); + } + data->curr_cpu_data = actual_cpu_data; + } +} + void *thread_body(void *arg) { thread_data_t *data = (thread_data_t*) arg; @@ -501,20 +611,6 @@ void *thread_body(void *arg) /* Get the 1st phase's data */ pdata = &data->phases[0];
- /* Set thread affinity */ - if (data->cpuset != NULL) - { - log_notice("[%d] setting cpu affinity to CPU(s) %s", data->ind, - data->cpuset_str); - ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), - data->cpuset); - if (ret < 0) { - errno = ret; - perror("pthread_setaffinity_np"); - exit(EXIT_FAILURE); - } - } - /* Set scheduling policy and print pretty info on stdout */ log_notice("[%d] Using %s policy with priority %d", data->ind, data->sched_policy_descr, data->sched_prio); switch (data->sched_policy) @@ -662,6 +758,8 @@ void *thread_body(void *arg) while (continue_running && (i != data->loop)) { struct timespec t_diff, t_rel_start;
+ set_thread_affinity(data, &pdata->cpu_data); + if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] begins loop %d phase %d step %d", data->ind, i, j, loop); log_debug("[%d] begins loop %d phase %d step %d", data->ind, i, j, loop);; diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index bdb11a6a2460..f0706ae6a13f 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -648,6 +648,41 @@ obj_is_event(char *name) return 0; }
+static void parse_cpuset_data(struct json_object *obj, cpuset_data_t *data) +{ + struct json_object *cpuset_obj, *cpu; + unsigned int max_cpu = sysconf(_SC_NPROCESSORS_CONF) - 1; + + /* cpuset */ + cpuset_obj = get_in_object(obj, "cpus", TRUE); + if (cpuset_obj) { + unsigned int i; + unsigned int cpu_idx; + + assure_type_is(cpuset_obj, obj, "cpus", json_type_array); + data->cpuset_str = strdup(json_object_to_json_string(cpuset_obj)); + data->cpusetsize = sizeof(cpu_set_t); + data->cpuset = malloc(data->cpusetsize); + CPU_ZERO(data->cpuset); + for (i = 0; i < json_object_array_length(cpuset_obj); i++) { + cpu = json_object_array_get_idx(cpuset_obj, i); + cpu_idx = json_object_get_int(cpu); + if (cpu_idx > max_cpu) { + log_critical(PIN2 "Invalid cpu %d in cpuset %s", cpu_idx, data->cpuset_str); + free(data->cpuset); + free(data->cpuset_str); + exit(EXIT_INV_CONFIG); + } + CPU_SET(cpu_idx, data->cpuset); + } + } else { + data->cpuset_str = strdup("-"); + data->cpuset = NULL; + data->cpusetsize = 0; + } + log_info(PIN "key: cpus %s", data->cpuset_str); +} + static void parse_thread_phase_data(struct json_object *obj, phase_data_t *data, rtapp_options_t *opts, long tag) @@ -681,6 +716,7 @@ parse_thread_phase_data(struct json_object *obj, i++; } } + parse_cpuset_data(obj, &data->cpu_data); }
static void @@ -689,8 +725,8 @@ parse_thread_data(char *name, struct json_object *obj, int index, { char *policy; char def_policy[RTAPP_POLICY_DESCR_LENGTH]; - struct json_object *cpuset_obj, *phases_obj, *cpu, *resources, *locks; - int i, cpu_idx, prior_def; + struct json_object *phases_obj, *resources; + int prior_def;
log_info(PFX "Parsing thread %s [%d]", name, index);
@@ -699,8 +735,11 @@ parse_thread_data(char *name, struct json_object *obj, int index, data->ind = index; data->name = strdup(name); data->lock_pages = opts->lock_pages; - data->cpuset = NULL; - data->cpuset_str = NULL; + data->cpu_data.cpuset = NULL; + data->cpu_data.cpuset_str = NULL; + data->curr_cpu_data = NULL; + data->def_cpu_data.cpuset = NULL; + data->def_cpu_data.cpuset_str = NULL;
/* policy */ policy_to_string(opts->policy, def_policy); @@ -734,22 +773,7 @@ parse_thread_data(char *name, struct json_object *obj, int index, data->deadline = get_int_value_from(obj, "deadline", TRUE, data->period);
/* cpuset */ - cpuset_obj = get_in_object(obj, "cpus", TRUE); - if (cpuset_obj) { - assure_type_is(cpuset_obj, obj, "cpus", json_type_array); - data->cpuset_str = strdup(json_object_to_json_string(cpuset_obj)); - data->cpuset = malloc(sizeof(cpu_set_t)); - CPU_ZERO(data->cpuset); - for (i = 0; i < json_object_array_length(cpuset_obj); i++) { - cpu = json_object_array_get_idx(cpuset_obj, i); - cpu_idx = json_object_get_int(cpu); - CPU_SET(cpu_idx, data->cpuset); - } - } else { - data->cpuset_str = strdup("-"); - data->cpuset = NULL; - } - log_info(PIN "key: cpus %s", data->cpuset_str); + parse_cpuset_data(obj, &data->cpu_data);
/* initial delay */ data->delay = get_int_value_from(obj, "delay", TRUE, 0); diff --git a/src/rt-app_types.h b/src/rt-app_types.h index 7f3ba58cfce9..06271c2d26c8 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -141,10 +141,17 @@ typedef struct _event_data_t { int count; } event_data_t;
+typedef struct _cpuset_data_t { + cpu_set_t *cpuset; + char *cpuset_str; + size_t cpusetsize; +} cpuset_data_t; + typedef struct _phase_data_t { int loop; event_data_t *events; int nbevents; + cpuset_data_t cpu_data; } phase_data_t;
typedef struct _thread_data_t { @@ -153,8 +160,9 @@ typedef struct _thread_data_t { int lock_pages; int duration; rtapp_resource_t **resources; - cpu_set_t *cpuset; - char *cpuset_str; + cpuset_data_t cpu_data; /* cpu set information */ + cpuset_data_t *curr_cpu_data; /* Current cpu set being used */ + cpuset_data_t def_cpu_data; /* Default cpu set for task */
unsigned long runtime, deadline, period;
Hi,
On 16/03/17 14:42, Stephen Boyd wrote:
From: Olav Haugan ohaugan@codeaurora.org
Allow setting of cpusets at all levels of the spec.
Signed-off-by: Olav Haugan ohaugan@codeaurora.org Tested-by: Dietmar Eggemann dietmar.eggemann@arm.com Reviewed-by: Dietmar Eggemann dietmar.eggemann@arm.com Tested-by: Viresh Kumar viresh.kumar@linaro.org Acked-by: Juri Lelli juri.lelli@arm.com Signed-off-by: Stephen Boyd stephen.boyd@linaro.org
Thanks for addressing my comments.
Vincent, are you OK with me pushing this to master?
Thanks,
- Juri
v4: -Rebased -Renamed example7.json to example8.json -Addressed comment formatting -Added check for # of cpus specified with bailout -Minor changes to example json
v3: -Rebased -Removed unused cpuset and locks variable -Removed include unistd.h
v2 changes: -Added documentation and example json -Clarified comments -Added check for invalid CPU -Reduced logging level for affinity setting message -Code fixes based on feedback
doc/examples/tutorial/example8.json | 40 ++++++++++++ doc/tutorial.txt | 37 +++++++++++ src/rt-app.c | 126 ++++++++++++++++++++++++++++++++---- src/rt-app_parse_config.c | 64 ++++++++++++------ src/rt-app_types.h | 12 +++- 5 files changed, 243 insertions(+), 36 deletions(-) create mode 100644 doc/examples/tutorial/example8.json
diff --git a/doc/examples/tutorial/example8.json b/doc/examples/tutorial/example8.json new file mode 100644 index 000000000000..9ffc594b4f47 --- /dev/null +++ b/doc/examples/tutorial/example8.json @@ -0,0 +1,40 @@ +{
- /*
* Simple use case which creates a thread with 3 phases each running for
* 1.5ms. In phase 1 the thread will be affined to CPU 0, in phase 2 it
* will be affined to CPU 1 and in the third phase it will be affined to
* CPUs 2 (inherit from "cpus" at task level).
*/
- "tasks" : {
"thread0" : {
"cpus" : [2],
"phases" : {
"phase1" : {
"cpus" : [0],
"loop" : 1,
"run" : 1500
},
"phase2" : {
"cpus" : [1],
"loop" : 1,
"run" : 1500
},
"phase3" : {
"loop" : 1,
"run" : 1500
}
}
}
- },
- "global" : {
"duration" : 2,
"calibration" : "CPU0",
"default_policy" : "SCHED_OTHER",
"pi_enabled" : false,
"lock_pages" : false,
"logdir" : "./",
"log_basename" : "rt-app1",
"ftrace" : true,
"gnuplot" : true
- }
+} diff --git a/doc/tutorial.txt b/doc/tutorial.txt index 3af2c57b2844..36d79ffe0d15 100644 --- a/doc/tutorial.txt +++ b/doc/tutorial.txt @@ -232,6 +232,43 @@ below are equals: } } +"cpus" can be specified at task level or phase level. As an example, below +creates two threads. One thread will run with affinity of CPU 2 and 3. The +second task will run first phase with affinity to CPU 0 and 1, second phase with +affinity to CPU 2, and the third phase with affinity to CPU 4, 5, and 6 (it will +inherit the affinity from the task level):
+"task" : {
- "thread1" : {
"cpus" : [2,3],
"phases" : {
"phase1" : {
"run" : 10,
"sleep" : 10
}
}
- }
- "thread2" : {
"cpus" : [4,5,6],
"phases" : {
"phase1" : {
"cpus" : [0,1],
"run" : 10,
"sleep" : 10
}
"phase2" : {
"cpus" : [2],
"run" : 10,
"sleep" : 10
}
"phase3" : {
"run" : 10,
"sleep" : 10
}
}
- }
+}
- loop: Integer. Define the number of times the parent object must be run.
The parent object can be a phase or a thread. For phase object, the loop defines the number of time the phase will be executed before starting the next diff --git a/src/rt-app.c b/src/rt-app.c index ec9710cfcc3f..981ed6f244c8 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -474,6 +474,116 @@ shutdown(int sig) exit(EXIT_SUCCESS); } +static void create_cpuset_str(cpuset_data_t *cpu_data) +{
- unsigned int cpu_count = CPU_COUNT_S(cpu_data->cpusetsize,
cpu_data->cpuset);
- unsigned int i;
- unsigned int idx = 0;
- /*
* Assume we can go up to 9999 cpus. Each cpu would take up to 4 + 2
* bytes (4 for the number and 2 for the comma and space). 2 bytes
* for beginning bracket + space and 2 bytes for end bracket and space
* and finally null-terminator.
*/
- unsigned int size_needed = cpu_count * 6 + 2 + 2 + 1;
- if (cpu_count > 9999) {
log_error("Too many cpus specified. Up to 9999 cpus supported");
exit(EXIT_FAILURE);
- }
- cpu_data->cpuset_str = malloc(size_needed);
- strcpy(cpu_data->cpuset_str, "[ ");
- idx += 2;
- for (i = 0; i < 10000 && cpu_count; ++i) {
unsigned int n;
if (CPU_ISSET(i, cpu_data->cpuset)) {
--cpu_count;
if (size_needed <= (idx + 1)) {
log_error("Not enough memory for array");
exit(EXIT_FAILURE);
}
n = snprintf(&cpu_data->cpuset_str[idx],
size_needed - idx - 1, "%d", i);
if (n > 0) {
idx += n;
} else {
log_error("Error creating array");
exit(EXIT_FAILURE);
}
if (size_needed <= (idx + 1)) {
log_error("Not enough memory for array");
exit(EXIT_FAILURE);
}
if (cpu_count) {
strncat(cpu_data->cpuset_str, ", ",
size_needed - idx - 1);
idx += 2;
}
}
- }
- strncat(cpu_data->cpuset_str, " ]", size_needed - idx - 1);
+}
+static void set_thread_affinity(thread_data_t *data, cpuset_data_t *cpu_data) +{
- int ret;
- cpuset_data_t *actual_cpu_data = &data->cpu_data;
- if (data->def_cpu_data.cpuset == NULL) {
/* Get default affinity */
cpu_set_t cpuset;
unsigned int cpu_count;
unsigned int cpu = 0;
ret = pthread_getaffinity_np(pthread_self(),
sizeof(cpu_set_t), &cpuset);
if (ret != 0) {
errno = ret;
perror("pthread_get_affinity");
exit(EXIT_FAILURE);
}
cpu_count = CPU_COUNT(&cpuset);
data->def_cpu_data.cpusetsize = CPU_ALLOC_SIZE(cpu_count);
data->def_cpu_data.cpuset = CPU_ALLOC(cpu_count);
memcpy(data->def_cpu_data.cpuset, &cpuset,
data->def_cpu_data.cpusetsize);
create_cpuset_str(&data->def_cpu_data);
data->curr_cpu_data = &data->def_cpu_data;
- }
- /*
* Order of preference:
* 1. Phase cpuset
* 2. Task level cpuset
* 3. Default cpuset
*/
- if (cpu_data->cpuset != NULL)
actual_cpu_data = cpu_data;
- if (actual_cpu_data->cpuset == NULL)
actual_cpu_data = &data->def_cpu_data;
- if (!CPU_EQUAL(actual_cpu_data->cpuset, data->curr_cpu_data->cpuset))
- {
log_debug("[%d] setting cpu affinity to CPU(s) %s", data->ind,
actual_cpu_data->cpuset_str);
ret = pthread_setaffinity_np(pthread_self(),
actual_cpu_data->cpusetsize,
actual_cpu_data->cpuset);
if (ret != 0) {
errno = ret;
perror("pthread_setaffinity_np");
exit(EXIT_FAILURE);
}
data->curr_cpu_data = actual_cpu_data;
- }
+}
void *thread_body(void *arg) { thread_data_t *data = (thread_data_t*) arg; @@ -501,20 +611,6 @@ void *thread_body(void *arg) /* Get the 1st phase's data */ pdata = &data->phases[0];
- /* Set thread affinity */
- if (data->cpuset != NULL)
- {
log_notice("[%d] setting cpu affinity to CPU(s) %s", data->ind,
data->cpuset_str);
ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
data->cpuset);
if (ret < 0) {
errno = ret;
perror("pthread_setaffinity_np");
exit(EXIT_FAILURE);
}
- }
- /* Set scheduling policy and print pretty info on stdout */ log_notice("[%d] Using %s policy with priority %d", data->ind, data->sched_policy_descr, data->sched_prio); switch (data->sched_policy)
@@ -662,6 +758,8 @@ void *thread_body(void *arg) while (continue_running && (i != data->loop)) { struct timespec t_diff, t_rel_start;
set_thread_affinity(data, &pdata->cpu_data);
- if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] begins loop %d phase %d step %d", data->ind, i, j, loop); log_debug("[%d] begins loop %d phase %d step %d", data->ind, i, j, loop);;
diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index bdb11a6a2460..f0706ae6a13f 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -648,6 +648,41 @@ obj_is_event(char *name) return 0; } +static void parse_cpuset_data(struct json_object *obj, cpuset_data_t *data) +{
- struct json_object *cpuset_obj, *cpu;
- unsigned int max_cpu = sysconf(_SC_NPROCESSORS_CONF) - 1;
- /* cpuset */
- cpuset_obj = get_in_object(obj, "cpus", TRUE);
- if (cpuset_obj) {
unsigned int i;
unsigned int cpu_idx;
assure_type_is(cpuset_obj, obj, "cpus", json_type_array);
data->cpuset_str = strdup(json_object_to_json_string(cpuset_obj));
data->cpusetsize = sizeof(cpu_set_t);
data->cpuset = malloc(data->cpusetsize);
CPU_ZERO(data->cpuset);
for (i = 0; i < json_object_array_length(cpuset_obj); i++) {
cpu = json_object_array_get_idx(cpuset_obj, i);
cpu_idx = json_object_get_int(cpu);
if (cpu_idx > max_cpu) {
log_critical(PIN2 "Invalid cpu %d in cpuset %s", cpu_idx, data->cpuset_str);
free(data->cpuset);
free(data->cpuset_str);
exit(EXIT_INV_CONFIG);
}
CPU_SET(cpu_idx, data->cpuset);
}
- } else {
data->cpuset_str = strdup("-");
data->cpuset = NULL;
data->cpusetsize = 0;
- }
- log_info(PIN "key: cpus %s", data->cpuset_str);
+}
static void parse_thread_phase_data(struct json_object *obj, phase_data_t *data, rtapp_options_t *opts, long tag) @@ -681,6 +716,7 @@ parse_thread_phase_data(struct json_object *obj, i++; } }
- parse_cpuset_data(obj, &data->cpu_data);
} static void @@ -689,8 +725,8 @@ parse_thread_data(char *name, struct json_object *obj, int index, { char *policy; char def_policy[RTAPP_POLICY_DESCR_LENGTH];
- struct json_object *cpuset_obj, *phases_obj, *cpu, *resources, *locks;
- int i, cpu_idx, prior_def;
- struct json_object *phases_obj, *resources;
- int prior_def;
log_info(PFX "Parsing thread %s [%d]", name, index); @@ -699,8 +735,11 @@ parse_thread_data(char *name, struct json_object *obj, int index, data->ind = index; data->name = strdup(name); data->lock_pages = opts->lock_pages;
- data->cpuset = NULL;
- data->cpuset_str = NULL;
- data->cpu_data.cpuset = NULL;
- data->cpu_data.cpuset_str = NULL;
- data->curr_cpu_data = NULL;
- data->def_cpu_data.cpuset = NULL;
- data->def_cpu_data.cpuset_str = NULL;
/* policy */ policy_to_string(opts->policy, def_policy); @@ -734,22 +773,7 @@ parse_thread_data(char *name, struct json_object *obj, int index, data->deadline = get_int_value_from(obj, "deadline", TRUE, data->period); /* cpuset */
- cpuset_obj = get_in_object(obj, "cpus", TRUE);
- if (cpuset_obj) {
assure_type_is(cpuset_obj, obj, "cpus", json_type_array);
data->cpuset_str = strdup(json_object_to_json_string(cpuset_obj));
data->cpuset = malloc(sizeof(cpu_set_t));
CPU_ZERO(data->cpuset);
for (i = 0; i < json_object_array_length(cpuset_obj); i++) {
cpu = json_object_array_get_idx(cpuset_obj, i);
cpu_idx = json_object_get_int(cpu);
CPU_SET(cpu_idx, data->cpuset);
}
- } else {
data->cpuset_str = strdup("-");
data->cpuset = NULL;
- }
- log_info(PIN "key: cpus %s", data->cpuset_str);
- parse_cpuset_data(obj, &data->cpu_data);
/* initial delay */ data->delay = get_int_value_from(obj, "delay", TRUE, 0); diff --git a/src/rt-app_types.h b/src/rt-app_types.h index 7f3ba58cfce9..06271c2d26c8 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -141,10 +141,17 @@ typedef struct _event_data_t { int count; } event_data_t; +typedef struct _cpuset_data_t {
- cpu_set_t *cpuset;
- char *cpuset_str;
- size_t cpusetsize;
+} cpuset_data_t;
typedef struct _phase_data_t { int loop; event_data_t *events; int nbevents;
- cpuset_data_t cpu_data;
} phase_data_t; typedef struct _thread_data_t { @@ -153,8 +160,9 @@ typedef struct _thread_data_t { int lock_pages; int duration; rtapp_resource_t **resources;
- cpu_set_t *cpuset;
- char *cpuset_str;
- cpuset_data_t cpu_data; /* cpu set information */
- cpuset_data_t *curr_cpu_data; /* Current cpu set being used */
- cpuset_data_t def_cpu_data; /* Default cpu set for task */
unsigned long runtime, deadline, period; -- 2.10.0.297.gf6727b0
Hi,
On 17 March 2017 at 13:07, Juri Lelli juri.lelli@arm.com wrote:
Hi,
On 16/03/17 14:42, Stephen Boyd wrote:
From: Olav Haugan ohaugan@codeaurora.org
Allow setting of cpusets at all levels of the spec.
Signed-off-by: Olav Haugan ohaugan@codeaurora.org Tested-by: Dietmar Eggemann dietmar.eggemann@arm.com Reviewed-by: Dietmar Eggemann dietmar.eggemann@arm.com Tested-by: Viresh Kumar viresh.kumar@linaro.org Acked-by: Juri Lelli juri.lelli@arm.com Signed-off-by: Stephen Boyd stephen.boyd@linaro.org
Thanks for addressing my comments.
Vincent, are you OK with me pushing this to master?
Yes.
Thanks Vincent
Thanks,
- Juri
v4: -Rebased -Renamed example7.json to example8.json -Addressed comment formatting -Added check for # of cpus specified with bailout -Minor changes to example json
v3: -Rebased -Removed unused cpuset and locks variable -Removed include unistd.h
v2 changes: -Added documentation and example json -Clarified comments -Added check for invalid CPU -Reduced logging level for affinity setting message -Code fixes based on feedback
doc/examples/tutorial/example8.json | 40 ++++++++++++ doc/tutorial.txt | 37 +++++++++++ src/rt-app.c | 126 ++++++++++++++++++++++++++++++
++----
src/rt-app_parse_config.c | 64 ++++++++++++------ src/rt-app_types.h | 12 +++- 5 files changed, 243 insertions(+), 36 deletions(-) create mode 100644 doc/examples/tutorial/example8.json
diff --git a/doc/examples/tutorial/example8.json
b/doc/examples/tutorial/example8.json
new file mode 100644 index 000000000000..9ffc594b4f47 --- /dev/null +++ b/doc/examples/tutorial/example8.json @@ -0,0 +1,40 @@ +{
/*
* Simple use case which creates a thread with 3 phases each
running for
* 1.5ms. In phase 1 the thread will be affined to CPU 0, in phase
2 it
* will be affined to CPU 1 and in the third phase it will be
affined to
* CPUs 2 (inherit from "cpus" at task level).
*/
"tasks" : {
"thread0" : {
"cpus" : [2],
"phases" : {
"phase1" : {
"cpus" : [0],
"loop" : 1,
"run" : 1500
},
"phase2" : {
"cpus" : [1],
"loop" : 1,
"run" : 1500
},
"phase3" : {
"loop" : 1,
"run" : 1500
}
}
}
},
"global" : {
"duration" : 2,
"calibration" : "CPU0",
"default_policy" : "SCHED_OTHER",
"pi_enabled" : false,
"lock_pages" : false,
"logdir" : "./",
"log_basename" : "rt-app1",
"ftrace" : true,
"gnuplot" : true
}
+} diff --git a/doc/tutorial.txt b/doc/tutorial.txt index 3af2c57b2844..36d79ffe0d15 100644 --- a/doc/tutorial.txt +++ b/doc/tutorial.txt @@ -232,6 +232,43 @@ below are equals: } }
+"cpus" can be specified at task level or phase level. As an example,
below
+creates two threads. One thread will run with affinity of CPU 2 and 3.
The
+second task will run first phase with affinity to CPU 0 and 1, second
phase with
+affinity to CPU 2, and the third phase with affinity to CPU 4, 5, and 6
(it will
+inherit the affinity from the task level):
+"task" : {
"thread1" : {
"cpus" : [2,3],
"phases" : {
"phase1" : {
"run" : 10,
"sleep" : 10
}
}
}
"thread2" : {
"cpus" : [4,5,6],
"phases" : {
"phase1" : {
"cpus" : [0,1],
"run" : 10,
"sleep" : 10
}
"phase2" : {
"cpus" : [2],
"run" : 10,
"sleep" : 10
}
"phase3" : {
"run" : 10,
"sleep" : 10
}
}
}
+}
- loop: Integer. Define the number of times the parent object must be
run.
The parent object can be a phase or a thread. For phase object, the loop defines the number of time the phase will be executed before starting
the next
diff --git a/src/rt-app.c b/src/rt-app.c index ec9710cfcc3f..981ed6f244c8 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -474,6 +474,116 @@ shutdown(int sig) exit(EXIT_SUCCESS); }
+static void create_cpuset_str(cpuset_data_t *cpu_data) +{
unsigned int cpu_count = CPU_COUNT_S(cpu_data->cpusetsize,
cpu_data->cpuset);
unsigned int i;
unsigned int idx = 0;
/*
* Assume we can go up to 9999 cpus. Each cpu would take up to 4 +
2
* bytes (4 for the number and 2 for the comma and space). 2 bytes
* for beginning bracket + space and 2 bytes for end bracket and
space
* and finally null-terminator.
*/
unsigned int size_needed = cpu_count * 6 + 2 + 2 + 1;
if (cpu_count > 9999) {
log_error("Too many cpus specified. Up to 9999 cpus
supported");
exit(EXIT_FAILURE);
}
cpu_data->cpuset_str = malloc(size_needed);
strcpy(cpu_data->cpuset_str, "[ ");
idx += 2;
for (i = 0; i < 10000 && cpu_count; ++i) {
unsigned int n;
if (CPU_ISSET(i, cpu_data->cpuset)) {
--cpu_count;
if (size_needed <= (idx + 1)) {
log_error("Not enough memory for array");
exit(EXIT_FAILURE);
}
n = snprintf(&cpu_data->cpuset_str[idx],
size_needed - idx - 1,
"%d", i);
if (n > 0) {
idx += n;
} else {
log_error("Error creating array");
exit(EXIT_FAILURE);
}
if (size_needed <= (idx + 1)) {
log_error("Not enough memory for array");
exit(EXIT_FAILURE);
}
if (cpu_count) {
strncat(cpu_data->cpuset_str, ", ",
size_needed - idx
- 1);
idx += 2;
}
}
}
strncat(cpu_data->cpuset_str, " ]", size_needed - idx - 1);
+}
+static void set_thread_affinity(thread_data_t *data, cpuset_data_t
*cpu_data)
+{
int ret;
cpuset_data_t *actual_cpu_data = &data->cpu_data;
if (data->def_cpu_data.cpuset == NULL) {
/* Get default affinity */
cpu_set_t cpuset;
unsigned int cpu_count;
unsigned int cpu = 0;
ret = pthread_getaffinity_np(pthread_self(),
sizeof(cpu_set_t),
&cpuset);
if (ret != 0) {
errno = ret;
perror("pthread_get_affinity");
exit(EXIT_FAILURE);
}
cpu_count = CPU_COUNT(&cpuset);
data->def_cpu_data.cpusetsize = CPU_ALLOC_SIZE(cpu_count);
data->def_cpu_data.cpuset = CPU_ALLOC(cpu_count);
memcpy(data->def_cpu_data.cpuset, &cpuset,
data->def_cpu_data.cpusetsize);
create_cpuset_str(&data->def_cpu_data);
data->curr_cpu_data = &data->def_cpu_data;
}
/*
* Order of preference:
* 1. Phase cpuset
* 2. Task level cpuset
* 3. Default cpuset
*/
if (cpu_data->cpuset != NULL)
actual_cpu_data = cpu_data;
if (actual_cpu_data->cpuset == NULL)
actual_cpu_data = &data->def_cpu_data;
if (!CPU_EQUAL(actual_cpu_data->cpuset,
data->curr_cpu_data->cpuset))
{
log_debug("[%d] setting cpu affinity to CPU(s) %s",
data->ind,
actual_cpu_data->cpuset_str);
ret = pthread_setaffinity_np(pthread_self(),
actual_cpu_data->cpusetsize,
actual_cpu_data->cpuset);
if (ret != 0) {
errno = ret;
perror("pthread_setaffinity_np");
exit(EXIT_FAILURE);
}
data->curr_cpu_data = actual_cpu_data;
}
+}
void *thread_body(void *arg) { thread_data_t *data = (thread_data_t*) arg; @@ -501,20 +611,6 @@ void *thread_body(void *arg) /* Get the 1st phase's data */ pdata = &data->phases[0];
/* Set thread affinity */
if (data->cpuset != NULL)
{
log_notice("[%d] setting cpu affinity to CPU(s) %s",
data->ind,
data->cpuset_str);
ret = pthread_setaffinity_np(pthread_self(),
sizeof(cpu_set_t),
data->cpuset);
if (ret < 0) {
errno = ret;
perror("pthread_setaffinity_np");
exit(EXIT_FAILURE);
}
}
/* Set scheduling policy and print pretty info on stdout */ log_notice("[%d] Using %s policy with priority %d", data->ind,
data->sched_policy_descr, data->sched_prio);
switch (data->sched_policy)
@@ -662,6 +758,8 @@ void *thread_body(void *arg) while (continue_running && (i != data->loop)) { struct timespec t_diff, t_rel_start;
set_thread_affinity(data, &pdata->cpu_data);
if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] begins loop %d
phase %d step %d", data->ind, i, j, loop);
log_debug("[%d] begins loop %d phase %d step %d",
data->ind, i, j, loop);;
diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index bdb11a6a2460..f0706ae6a13f 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -648,6 +648,41 @@ obj_is_event(char *name) return 0; }
+static void parse_cpuset_data(struct json_object *obj, cpuset_data_t
*data)
+{
struct json_object *cpuset_obj, *cpu;
unsigned int max_cpu = sysconf(_SC_NPROCESSORS_CONF) - 1;
/* cpuset */
cpuset_obj = get_in_object(obj, "cpus", TRUE);
if (cpuset_obj) {
unsigned int i;
unsigned int cpu_idx;
assure_type_is(cpuset_obj, obj, "cpus", json_type_array);
data->cpuset_str = strdup(json_object_to_json_
string(cpuset_obj));
data->cpusetsize = sizeof(cpu_set_t);
data->cpuset = malloc(data->cpusetsize);
CPU_ZERO(data->cpuset);
for (i = 0; i < json_object_array_length(cpuset_obj);
i++) {
cpu = json_object_array_get_idx(cpuset_obj, i);
cpu_idx = json_object_get_int(cpu);
if (cpu_idx > max_cpu) {
log_critical(PIN2 "Invalid cpu %d in
cpuset %s", cpu_idx, data->cpuset_str);
free(data->cpuset);
free(data->cpuset_str);
exit(EXIT_INV_CONFIG);
}
CPU_SET(cpu_idx, data->cpuset);
}
} else {
data->cpuset_str = strdup("-");
data->cpuset = NULL;
data->cpusetsize = 0;
}
log_info(PIN "key: cpus %s", data->cpuset_str);
+}
static void parse_thread_phase_data(struct json_object *obj, phase_data_t *data, rtapp_options_t *opts, long tag) @@ -681,6 +716,7 @@ parse_thread_phase_data(struct json_object *obj, i++; } }
parse_cpuset_data(obj, &data->cpu_data);
}
static void @@ -689,8 +725,8 @@ parse_thread_data(char *name, struct json_object
*obj, int index,
{ char *policy; char def_policy[RTAPP_POLICY_DESCR_LENGTH];
struct json_object *cpuset_obj, *phases_obj, *cpu, *resources,
*locks;
int i, cpu_idx, prior_def;
struct json_object *phases_obj, *resources;
int prior_def; log_info(PFX "Parsing thread %s [%d]", name, index);
@@ -699,8 +735,11 @@ parse_thread_data(char *name, struct json_object
*obj, int index,
data->ind = index; data->name = strdup(name); data->lock_pages = opts->lock_pages;
data->cpuset = NULL;
data->cpuset_str = NULL;
data->cpu_data.cpuset = NULL;
data->cpu_data.cpuset_str = NULL;
data->curr_cpu_data = NULL;
data->def_cpu_data.cpuset = NULL;
data->def_cpu_data.cpuset_str = NULL; /* policy */ policy_to_string(opts->policy, def_policy);
@@ -734,22 +773,7 @@ parse_thread_data(char *name, struct json_object
*obj, int index,
data->deadline = get_int_value_from(obj, "deadline", TRUE,
data->period);
/* cpuset */
cpuset_obj = get_in_object(obj, "cpus", TRUE);
if (cpuset_obj) {
assure_type_is(cpuset_obj, obj, "cpus", json_type_array);
data->cpuset_str = strdup(json_object_to_json_
string(cpuset_obj));
data->cpuset = malloc(sizeof(cpu_set_t));
CPU_ZERO(data->cpuset);
for (i = 0; i < json_object_array_length(cpuset_obj);
i++) {
cpu = json_object_array_get_idx(cpuset_obj, i);
cpu_idx = json_object_get_int(cpu);
CPU_SET(cpu_idx, data->cpuset);
}
} else {
data->cpuset_str = strdup("-");
data->cpuset = NULL;
}
log_info(PIN "key: cpus %s", data->cpuset_str);
parse_cpuset_data(obj, &data->cpu_data); /* initial delay */ data->delay = get_int_value_from(obj, "delay", TRUE, 0);
diff --git a/src/rt-app_types.h b/src/rt-app_types.h index 7f3ba58cfce9..06271c2d26c8 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -141,10 +141,17 @@ typedef struct _event_data_t { int count; } event_data_t;
+typedef struct _cpuset_data_t {
cpu_set_t *cpuset;
char *cpuset_str;
size_t cpusetsize;
+} cpuset_data_t;
typedef struct _phase_data_t { int loop; event_data_t *events; int nbevents;
cpuset_data_t cpu_data;
} phase_data_t;
typedef struct _thread_data_t { @@ -153,8 +160,9 @@ typedef struct _thread_data_t { int lock_pages; int duration; rtapp_resource_t **resources;
cpu_set_t *cpuset;
char *cpuset_str;
cpuset_data_t cpu_data; /* cpu set information */
cpuset_data_t *curr_cpu_data; /* Current cpu set being used */
cpuset_data_t def_cpu_data; /* Default cpu set for task */ unsigned long runtime, deadline, period;
-- 2.10.0.297.gf6727b0
On 17/03/17 14:26, Vincent Guittot wrote:
Hi,
On 17 March 2017 at 13:07, Juri Lelli juri.lelli@arm.com wrote:
Hi,
On 16/03/17 14:42, Stephen Boyd wrote:
From: Olav Haugan ohaugan@codeaurora.org
Allow setting of cpusets at all levels of the spec.
Signed-off-by: Olav Haugan ohaugan@codeaurora.org Tested-by: Dietmar Eggemann dietmar.eggemann@arm.com Reviewed-by: Dietmar Eggemann dietmar.eggemann@arm.com Tested-by: Viresh Kumar viresh.kumar@linaro.org Acked-by: Juri Lelli juri.lelli@arm.com Signed-off-by: Stephen Boyd stephen.boyd@linaro.org
Thanks for addressing my comments.
Vincent, are you OK with me pushing this to master?
Yes.
Thanks!
Patch merged upstream.
Best,
- Juri