The branch main has been updated by bnovkov: URL: https://cgit.FreeBSD.org/src/commit/?id=aae23170c8b5ac320dbf9c5f6cec05bea0b4b62f
commit aae23170c8b5ac320dbf9c5f6cec05bea0b4b62f Author: Bojan Novković <bnov...@freebsd.org> AuthorDate: 2024-09-08 15:51:46 +0000 Commit: Bojan Novković <bnov...@freebsd.org> CommitDate: 2025-07-27 16:31:48 +0000 libutil: Move cpuset(1) domain policy parsing code into libutil cpuset(1) implements a domainset(9) policy parser that is used to translate a <policy>:<domain_list> string into a domainset_t mask and a valid domainset policy. This change moves the domainset parsing code into a new cpuset.c function - 'domainset_parselist'. The existing cpuset.c code was refactored into a generalized list parsing function which is now used to parse both CPU sets and domain sets. This is the same approach used in cpuset(1). Reviewed by: markj, ziaee (manpages) Differential Revision: https://reviews.freebsd.org/D46607 --- bin/cpuset/Makefile | 2 +- bin/cpuset/cpuset.c | 153 +------------------------------------------------- lib/libutil/Makefile | 1 + lib/libutil/cpuset.3 | 51 ++++++++++++++--- lib/libutil/cpuset.c | 98 ++++++++++++++++++++++++++++---- lib/libutil/libutil.h | 8 ++- 6 files changed, 140 insertions(+), 173 deletions(-) diff --git a/bin/cpuset/Makefile b/bin/cpuset/Makefile index d6f58db62901..639dd9812171 100644 --- a/bin/cpuset/Makefile +++ b/bin/cpuset/Makefile @@ -1,6 +1,6 @@ PROG= cpuset -LIBADD= jail +LIBADD= jail util SYMLINKS+= ../..${BINDIR}/cpuset /usr/bin/cpuset diff --git a/bin/cpuset/cpuset.c b/bin/cpuset/cpuset.c index 82ffcaeec252..7416e100a3c6 100644 --- a/bin/cpuset/cpuset.c +++ b/bin/cpuset/cpuset.c @@ -43,6 +43,7 @@ #include <err.h> #include <errno.h> #include <jail.h> +#include <libutil.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> @@ -69,154 +70,6 @@ static cpuwhich_t which; static void usage(void) __dead2; -struct numa_policy { - const char *name; - int policy; -}; - -static struct numa_policy policies[] = { - { "round-robin", DOMAINSET_POLICY_ROUNDROBIN }, - { "rr", DOMAINSET_POLICY_ROUNDROBIN }, - { "first-touch", DOMAINSET_POLICY_FIRSTTOUCH }, - { "ft", DOMAINSET_POLICY_FIRSTTOUCH }, - { "prefer", DOMAINSET_POLICY_PREFER }, - { "interleave", DOMAINSET_POLICY_INTERLEAVE}, - { "il", DOMAINSET_POLICY_INTERLEAVE}, - { NULL, DOMAINSET_POLICY_INVALID } -}; - -static void printset(struct bitset *mask, int size); - -static void -parselist(char *list, struct bitset *mask, int size) -{ - enum { NONE, NUM, DASH } state; - int lastnum; - int curnum; - char *l; - - state = NONE; - curnum = lastnum = 0; - for (l = list; *l != '\0';) { - if (isdigit(*l)) { - curnum = atoi(l); - if (curnum >= size) - errx(EXIT_FAILURE, - "List entry %d exceeds maximum of %d", - curnum, size - 1); - while (isdigit(*l)) - l++; - switch (state) { - case NONE: - lastnum = curnum; - state = NUM; - break; - case DASH: - for (; lastnum <= curnum; lastnum++) - BIT_SET(size, lastnum, mask); - state = NONE; - break; - case NUM: - default: - goto parserr; - } - continue; - } - switch (*l) { - case ',': - switch (state) { - case NONE: - break; - case NUM: - BIT_SET(size, curnum, mask); - state = NONE; - break; - case DASH: - goto parserr; - break; - } - break; - case '-': - if (state != NUM) - goto parserr; - state = DASH; - break; - default: - goto parserr; - } - l++; - } - switch (state) { - case NONE: - break; - case NUM: - BIT_SET(size, curnum, mask); - break; - case DASH: - goto parserr; - } - return; -parserr: - errx(EXIT_FAILURE, "Malformed list %s", list); -} - -static void -parsecpulist(char *list, cpuset_t *mask) -{ - - if (strcasecmp(list, "all") == 0) { - if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, - sizeof(*mask), mask) != 0) - err(EXIT_FAILURE, "getaffinity"); - return; - } - parselist(list, (struct bitset *)mask, CPU_SETSIZE); -} - -/* - * permissively parse policy:domain list - * allow: - * round-robin:0-4 explicit - * round-robin:all explicit root domains - * 0-4 implicit root policy - * round-robin implicit root domains - * all explicit root domains and implicit policy - */ -static void -parsedomainlist(char *list, domainset_t *mask, int *policyp) -{ - domainset_t rootmask; - struct numa_policy *policy; - char *l; - int p; - - /* - * Use the rootset's policy as the default for unspecified policies. - */ - if (cpuset_getdomain(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, - sizeof(rootmask), &rootmask, &p) != 0) - err(EXIT_FAILURE, "getdomain"); - - l = list; - for (policy = &policies[0]; policy->name != NULL; policy++) { - if (strncasecmp(l, policy->name, strlen(policy->name)) == 0) { - p = policy->policy; - l += strlen(policy->name); - if (*l != ':' && *l != '\0') - errx(EXIT_FAILURE, "Malformed list %s", list); - if (*l == ':') - l++; - break; - } - } - *policyp = p; - if (strcasecmp(l, "all") == 0 || *l == '\0') { - DOMAINSET_COPY(&rootmask, mask); - return; - } - parselist(l, (struct bitset *)mask, DOMAINSET_SETSIZE); -} - static void printset(struct bitset *mask, int size) { @@ -327,11 +180,11 @@ main(int argc, char *argv[]) break; case 'l': lflag = 1; - parsecpulist(optarg, &mask); + cpuset_parselist(optarg, &mask); break; case 'n': nflag = 1; - parsedomainlist(optarg, &domains, &policy); + domainset_parselist(optarg, &domains, &policy); break; case 'p': pflag = 1; diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index 0639745d08fc..2d92c5ba1916 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -38,6 +38,7 @@ MAN+= cpuset.3 expand_number.3 flopen.3 fparseln.3 ftime.3 getlocalbase.3 \ property.3 pty.3 quotafile.3 realhostname.3 realhostname_sa.3 \ _secure_path.3 trimdomain.3 uucplock.3 pw_util.3 MAN+= login.conf.5 +MLINKS+=cpuset.3 domainset_parselist.3 MLINKS+=flopen.3 flopenat.3 MLINKS+=kld.3 kld_isloaded.3 kld.3 kld_load.3 MLINKS+=login_auth.3 auth_cat.3 login_auth.3 auth_checknologin.3 diff --git a/lib/libutil/cpuset.3 b/lib/libutil/cpuset.3 index be29d5309ef0..47dffd209ee6 100644 --- a/lib/libutil/cpuset.3 +++ b/lib/libutil/cpuset.3 @@ -22,21 +22,22 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 31, 2017 +.Dd June 24, 2025 .Dt CPUSET 3 .Os .Sh NAME -.Nm cpuset_parselist -.Nd utility functions for -.Xr cpuset 2 -handling +.Nm cpuset_parselist , +.Nm domainset_parselist +.Nd utility functions for cpuset(2) handling .Sh LIBRARY .Lb libutil .Sh SYNOPSIS .In sys/cpuset.h .In libutil.h .Ft int -.Fn cpuset_parselist "const char *cpu-list" "cpuset_t *mask" +.Fn cpuset_parselist "const char *cpu_list" "cpuset_t *mask" +.Ft int +.Fn domainset_parselist "const char *domain_policy" "domainset_t *domain_mask" "int *policyp" .Sh DESCRIPTION The .Fn cpuset_parselist @@ -52,6 +53,27 @@ numbers. A special list of .Dq all may be specified in which case the list includes all CPUs from the root set. +.Pp +The +.Fn domainset_parselist +function parses a +.Xr domainset 9 +memory domain allocation policy +specified by +.Va domain_policy +filling the +.Va domain_mask +and the +.Va policyp . +A valid +.Va domain_policy +is formatted as +.Ar policy:domain-list . +See the +.Ar -n +flag in +.Xr cpuset 1 +for a list of valid domain policies. .Sh RETURN VALUES Return values can be the following .Bl -tag -width Er @@ -60,19 +82,30 @@ The parsing was successful .It Dv CPUSET_PARSE_ERROR The .Va cpu-list +or +.Va domain-policy format is invalid .It Dv CPUSET_PARSE_GETAFFINITY The .Xr cpuset_getaffinity 2 call has failed .It Dv CPUSET_PARSE_INVALID_CPU -The number of supported CPUs has been exceeded. +The number of supported CPUs or NUMA domains has been exceeded. The maximum number being -.Va CPU_SETSIZE . +.Va CPU_SETSIZE +and +.Va DOMAINSET_SETSIZE +respectively. +.It Dv CPUSET_PARSE_GETDOMAIN +The +.Xr cpuset_getdomain 2 +call has failed .El .Sh SEE ALSO .Xr cpuset 1 , .Xr cpuset 2 , -.Xr cpuset 9 +.Xr numa 4 , +.Xr cpuset 9 , +.Xr domainset 9 .Sh AUTHORS .An Jeffrey Roberson Aq Mt j...@freebsd.org diff --git a/lib/libutil/cpuset.c b/lib/libutil/cpuset.c index 3c374bfa6cac..d4840af7e175 100644 --- a/lib/libutil/cpuset.c +++ b/lib/libutil/cpuset.c @@ -27,34 +27,48 @@ * SUCH DAMAGE. */ +#include <sys/cdefs.h> +#define _WANT_FREEBSD_BITSET + #include <sys/types.h> #include <sys/cpuset.h> +#include <sys/domainset.h> #include <stdlib.h> #include <string.h> #include <libutil.h> #include <ctype.h> -int -cpuset_parselist(const char *list, cpuset_t *mask) +struct numa_policy { + const char *name; + int policy; +}; + +static const struct numa_policy policies[] = { + { "round-robin", DOMAINSET_POLICY_ROUNDROBIN }, + { "rr", DOMAINSET_POLICY_ROUNDROBIN }, + { "first-touch", DOMAINSET_POLICY_FIRSTTOUCH }, + { "ft", DOMAINSET_POLICY_FIRSTTOUCH }, + { "prefer", DOMAINSET_POLICY_PREFER }, + { "interleave", DOMAINSET_POLICY_INTERLEAVE}, + { "il", DOMAINSET_POLICY_INTERLEAVE}, + { NULL, DOMAINSET_POLICY_INVALID } +}; + +static int +parselist(const char *list, struct bitset *mask, int size) { enum { NONE, NUM, DASH } state; int lastnum; int curnum; const char *l; - if (strcasecmp(list, "all") == 0) { - if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, - sizeof(*mask), mask) != 0) - return (CPUSET_PARSE_GETAFFINITY); - return (CPUSET_PARSE_OK); - } state = NONE; curnum = lastnum = 0; for (l = list; *l != '\0';) { if (isdigit(*l)) { curnum = atoi(l); - if (curnum > CPU_SETSIZE) + if (curnum >= size) return (CPUSET_PARSE_INVALID_CPU); while (isdigit(*l)) l++; @@ -65,7 +79,7 @@ cpuset_parselist(const char *list, cpuset_t *mask) break; case DASH: for (; lastnum <= curnum; lastnum++) - CPU_SET(lastnum, mask); + BIT_SET(size, lastnum, mask); state = NONE; break; case NUM: @@ -80,7 +94,7 @@ cpuset_parselist(const char *list, cpuset_t *mask) case NONE: break; case NUM: - CPU_SET(curnum, mask); + BIT_SET(size, curnum, mask); state = NONE; break; case DASH: @@ -102,7 +116,7 @@ cpuset_parselist(const char *list, cpuset_t *mask) case NONE: break; case NUM: - CPU_SET(curnum, mask); + BIT_SET(size, curnum, mask); break; case DASH: goto parserr; @@ -111,3 +125,63 @@ cpuset_parselist(const char *list, cpuset_t *mask) parserr: return (CPUSET_PARSE_ERROR); } + +/* + * permissively parse policy:domain list + * allow: + * round-robin:0-4 explicit + * round-robin:all explicit root domains + * 0-4 implicit root policy + * round-robin implicit root domains + * all explicit root domains and implicit policy + */ +int +domainset_parselist(const char *list, domainset_t *mask, int *policyp) +{ + domainset_t rootmask; + const struct numa_policy *policy; + const char *l; + int p; + + /* + * Use the rootset's policy as the default for unspecified policies. + */ + if (cpuset_getdomain(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, + sizeof(rootmask), &rootmask, &p) != 0) + return (CPUSET_PARSE_GETDOMAIN); + + if (list == NULL || strcasecmp(list, "all") == 0 || *list == '\0') { + *policyp = p; + DOMAINSET_COPY(&rootmask, mask); + return (CPUSET_PARSE_OK); + } + + l = list; + for (policy = &policies[0]; policy->name != NULL; policy++) { + if (strncasecmp(l, policy->name, strlen(policy->name)) == 0) { + p = policy->policy; + l += strlen(policy->name); + if (*l != ':' && *l != '\0') + return (CPUSET_PARSE_ERROR); + if (*l == ':') + l++; + break; + } + } + *policyp = p; + + return (parselist(l, (struct bitset *)mask, DOMAINSET_SETSIZE)); +} + +int +cpuset_parselist(const char *list, cpuset_t *mask) +{ + if (strcasecmp(list, "all") == 0) { + if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, + sizeof(*mask), mask) != 0) + return (CPUSET_PARSE_GETAFFINITY); + return (CPUSET_PARSE_OK); + } + + return (parselist(list, (struct bitset *)mask, CPU_SETSIZE)); +} diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h index 919855184caf..7d8bfdf67fac 100644 --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -213,7 +213,13 @@ int cpuset_parselist(const char *list, cpuset_t *mask); #define CPUSET_PARSE_OK 0 #define CPUSET_PARSE_GETAFFINITY -1 #define CPUSET_PARSE_ERROR -2 -#define CPUSET_PARSE_INVALID_CPU -3 +#define CPUSET_PARSE_OUT_OF_RANGE -3 +#define CPUSET_PARSE_GETDOMAIN -4 +#define CPUSET_PARSE_INVALID_CPU CPUSET_PARSE_OUT_OF_RANGE /* backwards compat */ +#endif + +#ifdef _SYS_DOMAINSET_H_ +int domainset_parselist(const char *list, domainset_t *mask, int *policyp); #endif __END_DECLS