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
This commit is contained in:
Bojan Novković 2024-09-08 17:51:46 +02:00
parent 0fde36fe04
commit aae23170c8
6 changed files with 140 additions and 173 deletions

View file

@ -1,6 +1,6 @@
PROG= cpuset
LIBADD= jail
LIBADD= jail util
SYMLINKS+= ../..${BINDIR}/cpuset /usr/bin/cpuset

View file

@ -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;

View file

@ -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

View file

@ -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 jeff@FreeBSD.org

View file

@ -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));
}

View file

@ -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