MINOR: ha-ring: Move some thread parsing functions to cfgparse-thread.c

Add cfgparse-thread.c new C file to avoid compiling such code for ha-inject.
Move thread.c parsing functions to this new file.
This commit is contained in:
Frederic Lecaille 2025-12-10 17:08:32 +01:00
parent 07b440516d
commit 2b7e9ffa34
3 changed files with 487 additions and 455 deletions

View File

@ -1006,6 +1006,7 @@ OBJS_COMMON += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o \
OBJS += $(OBJS_COMMON) src/acl.o src/cfgdiag.o src/cfgparse.o \
src/cfgparse-global.o src/cfgparse-listen.o src/cfgparse-tcp.o \
src/cfgparse-thread.o \
src/cfgparse-unix.o src/check.o src/dns.o src/dns_ring.o src/event_hdl.o \
src/extcheck.o src/filters.o src/flt_bwlim.o src/flt_http_comp.o \
src/flt_spoe.o src/flt_trace.o src/haproxy.o src/http_acl.o \

486
src/cfgparse-thread.c Normal file
View File

@ -0,0 +1,486 @@
#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#ifdef _POSIX_PRIORITY_SCHEDULING
#include <sched.h>
#endif
#ifdef USE_THREAD
# include <pthread.h>
#endif
#ifdef USE_CPU_AFFINITY
# include <sched.h>
# if defined(__FreeBSD__) || defined(__DragonFly__)
# include <pthread_np.h>
# endif
# ifdef __APPLE__
# include <mach/mach_types.h>
# include <mach/thread_act.h>
# include <mach/thread_policy.h>
# endif
# include <haproxy/cpuset.h>
# include <haproxy/cpu_topo.h>
#endif
#include <haproxy/cfgparse.h>
#include <haproxy/errors.h>
#include <haproxy/thread.h>
#include <haproxy/tools.h>
/* Parse a string representing a thread set in one of the following forms:
*
* - { "all" | "odd" | "even" | <abs_num> [ "-" <abs_num> ] }[,...]
* => these are (lists of) absolute thread numbers
*
* - <tgnum> "/" { "all" | "odd" | "even" | <rel_num> [ "-" <rel_num> ][,...]
* => these are (lists of) per-group relative thread numbers. All numbers
* must be lower than or equal to LONGBITS. When multiple list elements
* are provided, each of them must contain the thread group number.
*
* Minimum value for a thread or group number is always 1. Maximum value for an
* absolute thread number is MAX_THREADS, maximum value for a relative thread
* number is MAX_THREADS_PER_GROUP, an maximum value for a thread group is
* MAX_TGROUPS. "all", "even" and "odd" will be bound by MAX_THREADS and/or
* MAX_THREADS_PER_GROUP in any case. In ranges, a missing digit before "-"
* is implicitly 1, and a missing digit after "-" is implicitly the highest of
* its class. As such "-" is equivalent to "all", allowing to build strings
* such as "${MIN}-${MAX}" where both MIN and MAX are optional.
*
* It is not valid to mix absolute and relative numbers. As such:
* - all valid (all absolute threads)
* - 12-19,24-31 valid (abs threads 12 to 19 and 24 to 31)
* - 1/all valid (all 32 or 64 threads of group 1)
* - 1/1-4,1/8-10,2/1 valid
* - 1/1-4,8-10 invalid (mixes relatve "1/1-4" with absolute "8-10")
* - 1-4,8-10,2/1 invalid (mixes absolute "1-4,8-10" with relative "2/1")
* - 1/odd-4 invalid (mixes range with boundary)
*
* The target thread set is *completed* with supported threads, which means
* that it's the caller's responsibility for pre-initializing it. If the target
* thread set is NULL, it's not updated and the function only verifies that the
* input parses.
*
* On success, it returns 0. otherwise it returns non-zero with an error
* message in <err>.
*/
int parse_thread_set(const char *arg, struct thread_set *ts, char **err)
{
const char *set;
const char *sep;
int v, min, max, tg;
int is_rel;
/* search for the first delimiter (',', '-' or '/') to decide whether
* we're facing an absolute or relative form. The relative form always
* starts with a number followed by a slash.
*/
for (sep = arg; isdigit((uchar)*sep); sep++)
;
is_rel = (/*sep > arg &&*/ *sep == '/'); /* relative form */
/* from there we have to cut the thread spec around commas */
set = arg;
tg = 0;
while (*set) {
/* note: we can't use strtol() here because "-3" would parse as
* (-3) while we want to stop before the "-", so we find the
* separator ourselves and rely on atoi() whose value we may
* ignore depending where the separator is.
*/
for (sep = set; isdigit((uchar)*sep); sep++)
;
if (sep != set && *sep && *sep != '/' && *sep != '-' && *sep != ',') {
memprintf(err, "invalid character '%c' in thread set specification: '%s'.", *sep, set);
return -1;
}
v = (sep != set) ? atoi(set) : 0;
/* Now we know that the string is made of an optional series of digits
* optionally followed by one of the delimiters above, or that it
* starts with a different character.
*/
/* first, let's search for the thread group (digits before '/') */
if (tg || !is_rel) {
/* thread group already specified or not expected if absolute spec */
if (*sep == '/') {
if (tg)
memprintf(err, "redundant thread group specification '%s' for group %d", set, tg);
else
memprintf(err, "group-relative thread specification '%s' is not permitted after a absolute thread range.", set);
return -1;
}
} else {
/* this is a group-relative spec, first field is the group number */
if (sep == set && *sep == '/') {
memprintf(err, "thread group number expected before '%s'.", set);
return -1;
}
if (*sep != '/') {
memprintf(err, "absolute thread specification '%s' is not permitted after a group-relative thread range.", set);
return -1;
}
if (v < 1 || v > MAX_TGROUPS) {
memprintf(err, "invalid thread group number '%d', permitted range is 1..%d in '%s'.", v, MAX_TGROUPS, set);
return -1;
}
tg = v;
/* skip group number and go on with set,sep,v as if
* there was no group number.
*/
set = sep + 1;
continue;
}
/* Now 'set' starts at the min thread number, whose value is in v if any,
* and preset the max to it, unless the range is filled at once via "all"
* (stored as 1:0), "odd" (stored as) 1:-1, or "even" (stored as 1:-2).
* 'sep' points to the next non-digit which may be set itself e.g. for
* "all" etc or "-xx".
*/
if (!*set) {
/* empty set sets no restriction */
min = 1;
max = is_rel ? MAX_THREADS_PER_GROUP : MAX_THREADS;
}
else {
if (sep != set && *sep && *sep != '-' && *sep != ',') {
// Only delimiters are permitted around digits.
memprintf(err, "invalid character '%c' in thread set specification: '%s'.", *sep, set);
return -1;
}
/* for non-digits, find next delim */
for (; *sep && *sep != '-' && *sep != ','; sep++)
;
min = max = 1;
if (sep != set) {
/* non-empty first thread */
if (isteq(ist2(set, sep-set), ist("all")))
max = 0;
else if (isteq(ist2(set, sep-set), ist("odd")))
max = -1;
else if (isteq(ist2(set, sep-set), ist("even")))
max = -2;
else if (v)
min = max = v;
else
max = min = 0; // throw an error below
}
if (min < 1 || min > MAX_THREADS || (is_rel && min > MAX_THREADS_PER_GROUP)) {
memprintf(err, "invalid first thread number '%s', permitted range is 1..%d, or 'all', 'odd', 'even'.",
set, is_rel ? MAX_THREADS_PER_GROUP : MAX_THREADS);
return -1;
}
/* is this a range ? */
if (*sep == '-') {
if (min != max) {
memprintf(err, "extraneous range after 'all', 'odd' or 'even': '%s'.", set);
return -1;
}
/* this is a seemingly valid range, there may be another number */
for (set = ++sep; isdigit((uchar)*sep); sep++)
;
v = atoi(set);
if (sep == set) { // no digit: to the max
max = is_rel ? MAX_THREADS_PER_GROUP : MAX_THREADS;
if (*sep && *sep != ',')
max = 0; // throw an error below
} else
max = v;
if (max < 1 || max > MAX_THREADS || (is_rel && max > MAX_THREADS_PER_GROUP)) {
memprintf(err, "invalid last thread number '%s', permitted range is 1..%d.",
set, is_rel ? MAX_THREADS_PER_GROUP : MAX_THREADS);
return -1;
}
}
/* here sep points to the first non-digit after the thread spec,
* must be a valid delimiter.
*/
if (*sep && *sep != ',') {
memprintf(err, "invalid character '%c' after thread set specification: '%s'.", *sep, set);
return -1;
}
}
/* store values */
if (ts) {
if (is_rel) {
/* group-relative thread numbers */
ts->grps |= 1UL << (tg - 1);
if (max >= min) {
for (v = min; v <= max; v++)
ts->rel[tg - 1] |= 1UL << (v - 1);
} else {
memset(&ts->rel[tg - 1],
(max == 0) ? 0xff /* all */ : (max == -1) ? 0x55 /* odd */: 0xaa /* even */,
sizeof(ts->rel[tg - 1]));
}
} else {
/* absolute thread numbers */
if (max >= min) {
for (v = min; v <= max; v++)
ts->abs[(v - 1) / LONGBITS] |= 1UL << ((v - 1) % LONGBITS);
} else {
memset(&ts->abs,
(max == 0) ? 0xff /* all */ : (max == -1) ? 0x55 /* odd */: 0xaa /* even */,
sizeof(ts->abs));
}
}
}
set = *sep ? sep + 1 : sep;
tg = 0;
}
return 0;
}
/* Parse the "nbthread" global directive, which takes an integer argument that
* contains the desired number of threads.
*/
static int cfg_parse_nbthread(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
long nbthread;
char *errptr;
if (too_many_args(1, args, err, NULL))
return -1;
if (non_global_section_parsed == 1) {
memprintf(err, "'%s' not allowed if a non-global section was previously defined. This parameter must be declared in the first global section", args[0]);
return -1;
}
nbthread = strtol(args[1], &errptr, 10);
if (!*args[1] || *errptr) {
memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
return -1;
}
#ifndef USE_THREAD
if (nbthread != 1) {
memprintf(err, "'%s' specified with a value other than 1 while HAProxy is not compiled with threads support. Please check build options for USE_THREAD", args[0]);
return -1;
}
#else
if (nbthread < 1 || nbthread > MAX_THREADS) {
memprintf(err, "'%s' value must be between 1 and %d (was %ld)", args[0], MAX_THREADS, nbthread);
return -1;
}
#endif
HA_DIAG_WARNING_COND(global.nbthread,
"parsing [%s:%d] : '%s' is already defined and will be overridden.\n",
file, line, args[0]);
global.nbthread = nbthread;
return 0;
}
/* Parse the "thread-hard-limit" global directive, which takes an integer
* argument that contains the desired maximum number of threads that will
* not be crossed.
*/
static int cfg_parse_thread_hard_limit(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
long nbthread;
char *errptr;
if (too_many_args(1, args, err, NULL))
return -1;
nbthread = strtol(args[1], &errptr, 10);
if (!*args[1] || *errptr) {
memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
return -1;
}
if (nbthread < 1 || nbthread > MAX_THREADS) {
memprintf(err, "'%s' value must be at least 1 (was %ld)", args[0], nbthread);
return -1;
}
global.thread_limit = nbthread;
return 0;
}
/* Parse the "thread-group" global directive, which takes an integer argument
* that designates a thread group, and a list of threads to put into that group.
*/
static int cfg_parse_thread_group(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
char *errptr;
long tnum, tend, tgroup;
int arg, tot;
if (non_global_section_parsed == 1) {
memprintf(err, "'%s' not allowed if a non-global section was previously defined. This parameter must be declared in the first global section", args[0]);
return -1;
}
tgroup = strtol(args[1], &errptr, 10);
if (!*args[1] || *errptr) {
memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
return -1;
}
if (tgroup < 1 || tgroup > MAX_TGROUPS) {
memprintf(err, "'%s' thread-group number must be between 1 and %d (was %ld)", args[0], MAX_TGROUPS, tgroup);
return -1;
}
/* look for a preliminary definition of any thread pointing to this
* group, and remove them.
*/
if (ha_tgroup_info[tgroup-1].count) {
ha_warning("parsing [%s:%d] : '%s %ld' was already defined and will be overridden.\n",
file, line, args[0], tgroup);
for (tnum = ha_tgroup_info[tgroup-1].base;
tnum < ha_tgroup_info[tgroup-1].base + ha_tgroup_info[tgroup-1].count;
tnum++) {
if (ha_thread_info[tnum-1].tg == &ha_tgroup_info[tgroup-1]) {
ha_thread_info[tnum-1].tg = NULL;
ha_thread_info[tnum-1].tgid = 0;
ha_thread_info[tnum-1].tg_ctx = NULL;
}
}
ha_tgroup_info[tgroup-1].count = ha_tgroup_info[tgroup-1].base = 0;
}
tot = 0;
for (arg = 2; args[arg] && *args[arg]; arg++) {
tend = tnum = strtol(args[arg], &errptr, 10);
if (*errptr == '-')
tend = strtol(errptr + 1, &errptr, 10);
if (*errptr || tnum < 1 || tend < 1 || tnum > MAX_THREADS || tend > MAX_THREADS) {
memprintf(err, "'%s %ld' passed an unparsable or invalid thread number '%s' (valid range is 1 to %d)", args[0], tgroup, args[arg], MAX_THREADS);
return -1;
}
for(; tnum <= tend; tnum++) {
if (ha_thread_info[tnum-1].tg == &ha_tgroup_info[tgroup-1]) {
ha_warning("parsing [%s:%d] : '%s %ld': thread %ld assigned more than once on the same line.\n",
file, line, args[0], tgroup, tnum);
} else if (ha_thread_info[tnum-1].tg) {
ha_warning("parsing [%s:%d] : '%s %ld': thread %ld was previously assigned to thread group %ld and will be overridden.\n",
file, line, args[0], tgroup, tnum,
(long)(ha_thread_info[tnum-1].tg - &ha_tgroup_info[0] + 1));
}
if (!ha_tgroup_info[tgroup-1].count) {
ha_tgroup_info[tgroup-1].base = tnum-1;
ha_tgroup_info[tgroup-1].count = 1;
}
else if (tnum >= ha_tgroup_info[tgroup-1].base + ha_tgroup_info[tgroup-1].count) {
ha_tgroup_info[tgroup-1].count = tnum - ha_tgroup_info[tgroup-1].base;
}
else if (tnum < ha_tgroup_info[tgroup-1].base) {
ha_tgroup_info[tgroup-1].count += ha_tgroup_info[tgroup-1].base - tnum-1;
ha_tgroup_info[tgroup-1].base = tnum - 1;
}
ha_thread_info[tnum-1].tgid = tgroup;
ha_thread_info[tnum-1].tg = &ha_tgroup_info[tgroup-1];
ha_thread_info[tnum-1].tg_ctx = &ha_tgroup_ctx[tgroup-1];
tot++;
}
}
if (ha_tgroup_info[tgroup-1].count > tot) {
memprintf(err, "'%s %ld' assigned sparse threads, only contiguous supported", args[0], tgroup);
return -1;
}
if (ha_tgroup_info[tgroup-1].count > MAX_THREADS_PER_GROUP) {
memprintf(err, "'%s %ld' assigned too many threads (%d, max=%d)", args[0], tgroup, tot, MAX_THREADS_PER_GROUP);
return -1;
}
return 0;
}
/* Parse the "thread-groups" global directive, which takes an integer argument
* that contains the desired number of thread groups.
*/
static int cfg_parse_thread_groups(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
long nbtgroups;
char *errptr;
if (too_many_args(1, args, err, NULL))
return -1;
if (non_global_section_parsed == 1) {
memprintf(err, "'%s' not allowed if a non-global section was previously defined. This parameter must be declared in the first global section", args[0]);
return -1;
}
nbtgroups = strtol(args[1], &errptr, 10);
if (!*args[1] || *errptr) {
memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
return -1;
}
#ifndef USE_THREAD
if (nbtgroups != 1) {
memprintf(err, "'%s' specified with a value other than 1 while HAProxy is not compiled with threads support. Please check build options for USE_THREAD", args[0]);
return -1;
}
#else
if (nbtgroups < 1 || nbtgroups > MAX_TGROUPS) {
memprintf(err, "'%s' value must be between 1 and %d (was %ld)", args[0], MAX_TGROUPS, nbtgroups);
return -1;
}
#endif
HA_DIAG_WARNING_COND(global.nbtgroups,
"parsing [%s:%d] : '%s' is already defined and will be overridden.\n",
file, line, args[0]);
global.nbtgroups = nbtgroups;
return 0;
}
/* config keyword parsers */
static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "thread-hard-limit", cfg_parse_thread_hard_limit, 0 },
{ CFG_GLOBAL, "nbthread", cfg_parse_nbthread, 0 },
{ CFG_GLOBAL, "thread-group", cfg_parse_thread_group, 0 },
{ CFG_GLOBAL, "thread-groups", cfg_parse_thread_groups, 0 },
{ 0, NULL, NULL }
}};
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);

View File

@ -1745,458 +1745,3 @@ void thread_detect_count(void)
}
return;
}
/* Parse a string representing a thread set in one of the following forms:
*
* - { "all" | "odd" | "even" | <abs_num> [ "-" <abs_num> ] }[,...]
* => these are (lists of) absolute thread numbers
*
* - <tgnum> "/" { "all" | "odd" | "even" | <rel_num> [ "-" <rel_num> ][,...]
* => these are (lists of) per-group relative thread numbers. All numbers
* must be lower than or equal to LONGBITS. When multiple list elements
* are provided, each of them must contain the thread group number.
*
* Minimum value for a thread or group number is always 1. Maximum value for an
* absolute thread number is MAX_THREADS, maximum value for a relative thread
* number is MAX_THREADS_PER_GROUP, an maximum value for a thread group is
* MAX_TGROUPS. "all", "even" and "odd" will be bound by MAX_THREADS and/or
* MAX_THREADS_PER_GROUP in any case. In ranges, a missing digit before "-"
* is implicitly 1, and a missing digit after "-" is implicitly the highest of
* its class. As such "-" is equivalent to "all", allowing to build strings
* such as "${MIN}-${MAX}" where both MIN and MAX are optional.
*
* It is not valid to mix absolute and relative numbers. As such:
* - all valid (all absolute threads)
* - 12-19,24-31 valid (abs threads 12 to 19 and 24 to 31)
* - 1/all valid (all 32 or 64 threads of group 1)
* - 1/1-4,1/8-10,2/1 valid
* - 1/1-4,8-10 invalid (mixes relatve "1/1-4" with absolute "8-10")
* - 1-4,8-10,2/1 invalid (mixes absolute "1-4,8-10" with relative "2/1")
* - 1/odd-4 invalid (mixes range with boundary)
*
* The target thread set is *completed* with supported threads, which means
* that it's the caller's responsibility for pre-initializing it. If the target
* thread set is NULL, it's not updated and the function only verifies that the
* input parses.
*
* On success, it returns 0. otherwise it returns non-zero with an error
* message in <err>.
*/
int parse_thread_set(const char *arg, struct thread_set *ts, char **err)
{
const char *set;
const char *sep;
int v, min, max, tg;
int is_rel;
/* search for the first delimiter (',', '-' or '/') to decide whether
* we're facing an absolute or relative form. The relative form always
* starts with a number followed by a slash.
*/
for (sep = arg; isdigit((uchar)*sep); sep++)
;
is_rel = (/*sep > arg &&*/ *sep == '/'); /* relative form */
/* from there we have to cut the thread spec around commas */
set = arg;
tg = 0;
while (*set) {
/* note: we can't use strtol() here because "-3" would parse as
* (-3) while we want to stop before the "-", so we find the
* separator ourselves and rely on atoi() whose value we may
* ignore depending where the separator is.
*/
for (sep = set; isdigit((uchar)*sep); sep++)
;
if (sep != set && *sep && *sep != '/' && *sep != '-' && *sep != ',') {
memprintf(err, "invalid character '%c' in thread set specification: '%s'.", *sep, set);
return -1;
}
v = (sep != set) ? atoi(set) : 0;
/* Now we know that the string is made of an optional series of digits
* optionally followed by one of the delimiters above, or that it
* starts with a different character.
*/
/* first, let's search for the thread group (digits before '/') */
if (tg || !is_rel) {
/* thread group already specified or not expected if absolute spec */
if (*sep == '/') {
if (tg)
memprintf(err, "redundant thread group specification '%s' for group %d", set, tg);
else
memprintf(err, "group-relative thread specification '%s' is not permitted after a absolute thread range.", set);
return -1;
}
} else {
/* this is a group-relative spec, first field is the group number */
if (sep == set && *sep == '/') {
memprintf(err, "thread group number expected before '%s'.", set);
return -1;
}
if (*sep != '/') {
memprintf(err, "absolute thread specification '%s' is not permitted after a group-relative thread range.", set);
return -1;
}
if (v < 1 || v > MAX_TGROUPS) {
memprintf(err, "invalid thread group number '%d', permitted range is 1..%d in '%s'.", v, MAX_TGROUPS, set);
return -1;
}
tg = v;
/* skip group number and go on with set,sep,v as if
* there was no group number.
*/
set = sep + 1;
continue;
}
/* Now 'set' starts at the min thread number, whose value is in v if any,
* and preset the max to it, unless the range is filled at once via "all"
* (stored as 1:0), "odd" (stored as) 1:-1, or "even" (stored as 1:-2).
* 'sep' points to the next non-digit which may be set itself e.g. for
* "all" etc or "-xx".
*/
if (!*set) {
/* empty set sets no restriction */
min = 1;
max = is_rel ? MAX_THREADS_PER_GROUP : MAX_THREADS;
}
else {
if (sep != set && *sep && *sep != '-' && *sep != ',') {
// Only delimiters are permitted around digits.
memprintf(err, "invalid character '%c' in thread set specification: '%s'.", *sep, set);
return -1;
}
/* for non-digits, find next delim */
for (; *sep && *sep != '-' && *sep != ','; sep++)
;
min = max = 1;
if (sep != set) {
/* non-empty first thread */
if (isteq(ist2(set, sep-set), ist("all")))
max = 0;
else if (isteq(ist2(set, sep-set), ist("odd")))
max = -1;
else if (isteq(ist2(set, sep-set), ist("even")))
max = -2;
else if (v)
min = max = v;
else
max = min = 0; // throw an error below
}
if (min < 1 || min > MAX_THREADS || (is_rel && min > MAX_THREADS_PER_GROUP)) {
memprintf(err, "invalid first thread number '%s', permitted range is 1..%d, or 'all', 'odd', 'even'.",
set, is_rel ? MAX_THREADS_PER_GROUP : MAX_THREADS);
return -1;
}
/* is this a range ? */
if (*sep == '-') {
if (min != max) {
memprintf(err, "extraneous range after 'all', 'odd' or 'even': '%s'.", set);
return -1;
}
/* this is a seemingly valid range, there may be another number */
for (set = ++sep; isdigit((uchar)*sep); sep++)
;
v = atoi(set);
if (sep == set) { // no digit: to the max
max = is_rel ? MAX_THREADS_PER_GROUP : MAX_THREADS;
if (*sep && *sep != ',')
max = 0; // throw an error below
} else
max = v;
if (max < 1 || max > MAX_THREADS || (is_rel && max > MAX_THREADS_PER_GROUP)) {
memprintf(err, "invalid last thread number '%s', permitted range is 1..%d.",
set, is_rel ? MAX_THREADS_PER_GROUP : MAX_THREADS);
return -1;
}
}
/* here sep points to the first non-digit after the thread spec,
* must be a valid delimiter.
*/
if (*sep && *sep != ',') {
memprintf(err, "invalid character '%c' after thread set specification: '%s'.", *sep, set);
return -1;
}
}
/* store values */
if (ts) {
if (is_rel) {
/* group-relative thread numbers */
ts->grps |= 1UL << (tg - 1);
if (max >= min) {
for (v = min; v <= max; v++)
ts->rel[tg - 1] |= 1UL << (v - 1);
} else {
memset(&ts->rel[tg - 1],
(max == 0) ? 0xff /* all */ : (max == -1) ? 0x55 /* odd */: 0xaa /* even */,
sizeof(ts->rel[tg - 1]));
}
} else {
/* absolute thread numbers */
if (max >= min) {
for (v = min; v <= max; v++)
ts->abs[(v - 1) / LONGBITS] |= 1UL << ((v - 1) % LONGBITS);
} else {
memset(&ts->abs,
(max == 0) ? 0xff /* all */ : (max == -1) ? 0x55 /* odd */: 0xaa /* even */,
sizeof(ts->abs));
}
}
}
set = *sep ? sep + 1 : sep;
tg = 0;
}
return 0;
}
/* Parse the "nbthread" global directive, which takes an integer argument that
* contains the desired number of threads.
*/
static int cfg_parse_nbthread(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
long nbthread;
char *errptr;
if (too_many_args(1, args, err, NULL))
return -1;
if (non_global_section_parsed == 1) {
memprintf(err, "'%s' not allowed if a non-global section was previously defined. This parameter must be declared in the first global section", args[0]);
return -1;
}
nbthread = strtol(args[1], &errptr, 10);
if (!*args[1] || *errptr) {
memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
return -1;
}
#ifndef USE_THREAD
if (nbthread != 1) {
memprintf(err, "'%s' specified with a value other than 1 while HAProxy is not compiled with threads support. Please check build options for USE_THREAD", args[0]);
return -1;
}
#else
if (nbthread < 1 || nbthread > MAX_THREADS) {
memprintf(err, "'%s' value must be between 1 and %d (was %ld)", args[0], MAX_THREADS, nbthread);
return -1;
}
#endif
HA_DIAG_WARNING_COND(global.nbthread,
"parsing [%s:%d] : '%s' is already defined and will be overridden.\n",
file, line, args[0]);
global.nbthread = nbthread;
return 0;
}
/* Parse the "thread-hard-limit" global directive, which takes an integer
* argument that contains the desired maximum number of threads that will
* not be crossed.
*/
static int cfg_parse_thread_hard_limit(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
long nbthread;
char *errptr;
if (too_many_args(1, args, err, NULL))
return -1;
nbthread = strtol(args[1], &errptr, 10);
if (!*args[1] || *errptr) {
memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
return -1;
}
if (nbthread < 1 || nbthread > MAX_THREADS) {
memprintf(err, "'%s' value must be at least 1 (was %ld)", args[0], nbthread);
return -1;
}
global.thread_limit = nbthread;
return 0;
}
/* Parse the "thread-group" global directive, which takes an integer argument
* that designates a thread group, and a list of threads to put into that group.
*/
static int cfg_parse_thread_group(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
char *errptr;
long tnum, tend, tgroup;
int arg, tot;
if (non_global_section_parsed == 1) {
memprintf(err, "'%s' not allowed if a non-global section was previously defined. This parameter must be declared in the first global section", args[0]);
return -1;
}
tgroup = strtol(args[1], &errptr, 10);
if (!*args[1] || *errptr) {
memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
return -1;
}
if (tgroup < 1 || tgroup > MAX_TGROUPS) {
memprintf(err, "'%s' thread-group number must be between 1 and %d (was %ld)", args[0], MAX_TGROUPS, tgroup);
return -1;
}
/* look for a preliminary definition of any thread pointing to this
* group, and remove them.
*/
if (ha_tgroup_info[tgroup-1].count) {
ha_warning("parsing [%s:%d] : '%s %ld' was already defined and will be overridden.\n",
file, line, args[0], tgroup);
for (tnum = ha_tgroup_info[tgroup-1].base;
tnum < ha_tgroup_info[tgroup-1].base + ha_tgroup_info[tgroup-1].count;
tnum++) {
if (ha_thread_info[tnum-1].tg == &ha_tgroup_info[tgroup-1]) {
ha_thread_info[tnum-1].tg = NULL;
ha_thread_info[tnum-1].tgid = 0;
ha_thread_info[tnum-1].tg_ctx = NULL;
}
}
ha_tgroup_info[tgroup-1].count = ha_tgroup_info[tgroup-1].base = 0;
}
tot = 0;
for (arg = 2; args[arg] && *args[arg]; arg++) {
tend = tnum = strtol(args[arg], &errptr, 10);
if (*errptr == '-')
tend = strtol(errptr + 1, &errptr, 10);
if (*errptr || tnum < 1 || tend < 1 || tnum > MAX_THREADS || tend > MAX_THREADS) {
memprintf(err, "'%s %ld' passed an unparsable or invalid thread number '%s' (valid range is 1 to %d)", args[0], tgroup, args[arg], MAX_THREADS);
return -1;
}
for(; tnum <= tend; tnum++) {
if (ha_thread_info[tnum-1].tg == &ha_tgroup_info[tgroup-1]) {
ha_warning("parsing [%s:%d] : '%s %ld': thread %ld assigned more than once on the same line.\n",
file, line, args[0], tgroup, tnum);
} else if (ha_thread_info[tnum-1].tg) {
ha_warning("parsing [%s:%d] : '%s %ld': thread %ld was previously assigned to thread group %ld and will be overridden.\n",
file, line, args[0], tgroup, tnum,
(long)(ha_thread_info[tnum-1].tg - &ha_tgroup_info[0] + 1));
}
if (!ha_tgroup_info[tgroup-1].count) {
ha_tgroup_info[tgroup-1].base = tnum-1;
ha_tgroup_info[tgroup-1].count = 1;
}
else if (tnum >= ha_tgroup_info[tgroup-1].base + ha_tgroup_info[tgroup-1].count) {
ha_tgroup_info[tgroup-1].count = tnum - ha_tgroup_info[tgroup-1].base;
}
else if (tnum < ha_tgroup_info[tgroup-1].base) {
ha_tgroup_info[tgroup-1].count += ha_tgroup_info[tgroup-1].base - tnum-1;
ha_tgroup_info[tgroup-1].base = tnum - 1;
}
ha_thread_info[tnum-1].tgid = tgroup;
ha_thread_info[tnum-1].tg = &ha_tgroup_info[tgroup-1];
ha_thread_info[tnum-1].tg_ctx = &ha_tgroup_ctx[tgroup-1];
tot++;
}
}
if (ha_tgroup_info[tgroup-1].count > tot) {
memprintf(err, "'%s %ld' assigned sparse threads, only contiguous supported", args[0], tgroup);
return -1;
}
if (ha_tgroup_info[tgroup-1].count > MAX_THREADS_PER_GROUP) {
memprintf(err, "'%s %ld' assigned too many threads (%d, max=%d)", args[0], tgroup, tot, MAX_THREADS_PER_GROUP);
return -1;
}
return 0;
}
/* Parse the "thread-groups" global directive, which takes an integer argument
* that contains the desired number of thread groups.
*/
static int cfg_parse_thread_groups(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
long nbtgroups;
char *errptr;
if (too_many_args(1, args, err, NULL))
return -1;
if (non_global_section_parsed == 1) {
memprintf(err, "'%s' not allowed if a non-global section was previously defined. This parameter must be declared in the first global section", args[0]);
return -1;
}
nbtgroups = strtol(args[1], &errptr, 10);
if (!*args[1] || *errptr) {
memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
return -1;
}
#ifndef USE_THREAD
if (nbtgroups != 1) {
memprintf(err, "'%s' specified with a value other than 1 while HAProxy is not compiled with threads support. Please check build options for USE_THREAD", args[0]);
return -1;
}
#else
if (nbtgroups < 1 || nbtgroups > MAX_TGROUPS) {
memprintf(err, "'%s' value must be between 1 and %d (was %ld)", args[0], MAX_TGROUPS, nbtgroups);
return -1;
}
#endif
HA_DIAG_WARNING_COND(global.nbtgroups,
"parsing [%s:%d] : '%s' is already defined and will be overridden.\n",
file, line, args[0]);
global.nbtgroups = nbtgroups;
return 0;
}
/* config keyword parsers */
static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "thread-hard-limit", cfg_parse_thread_hard_limit, 0 },
{ CFG_GLOBAL, "nbthread", cfg_parse_nbthread, 0 },
{ CFG_GLOBAL, "thread-group", cfg_parse_thread_group, 0 },
{ CFG_GLOBAL, "thread-groups", cfg_parse_thread_groups, 0 },
{ 0, NULL, NULL }
}};
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);