diff --git a/share/man/man9/domainset.9 b/share/man/man9/domainset.9 --- a/share/man/man9/domainset.9 +++ b/share/man/man9/domainset.9 @@ -22,7 +22,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd April 14, 2021 +.Dd June 24, 2025 .Dt DOMAINSET 9 .Os .Sh NAME @@ -54,6 +54,8 @@ .Ft struct domainset * .Fn domainset_create "const struct domainset *key" .Ft int +.Fn domainset_populate "struct domainset *domain" "domainset_t *mask" "int policy" "size_t mask_size" +.Ft int .Fn sysctl_handle_domainset "SYSCTL_HANDLER_ARGS" .Sh DESCRIPTION The @@ -137,6 +139,7 @@ to avoid blocking indefinitely on a .Dv M_WAITOK request. +.Pp The .Fn domainset_create function takes a partially filled in domainset as a key and returns a @@ -148,6 +151,17 @@ not be modified after return. .Pp The +.Fn domainset_populate +function fills a +.Vt domainset +struct using a domain mask and policy. +It is used for validating and +translating a domain mask and policy into a +.Vt domainset +struct when creating a custom domainset using +.Vt domainset_create . +.Pp +The .Fn sysctl_handle_domainset function is provided as a convenience for modifying or viewing domainsets that are not accessible via diff --git a/sys/kern/kern_cpuset.c b/sys/kern/kern_cpuset.c --- a/sys/kern/kern_cpuset.c +++ b/sys/kern/kern_cpuset.c @@ -530,7 +530,7 @@ * remove them and update the domainset accordingly. If only empty * domains are present, we must return failure. */ -static bool +bool domainset_empty_vm(struct domainset *domain) { domainset_t empty; @@ -2409,82 +2409,92 @@ } int -kern_cpuset_setdomain(struct thread *td, cpulevel_t level, cpuwhich_t which, - id_t id, size_t domainsetsize, const domainset_t *maskp, int policy, - const struct cpuset_copy_cb *cb) +domainset_populate(struct domainset *domain, const domainset_t *mask, int policy, + size_t mask_size) { - struct cpuset *nset; - struct cpuset *set; - struct thread *ttd; - struct proc *p; - struct domainset domain; - domainset_t *mask; - int error; - if (domainsetsize < sizeof(domainset_t) || - domainsetsize > DOMAINSET_MAXSIZE / NBBY) - return (ERANGE); if (policy <= DOMAINSET_POLICY_INVALID || - policy > DOMAINSET_POLICY_MAX) + policy > DOMAINSET_POLICY_MAX) { return (EINVAL); - error = cpuset_check_capabilities(td, level, which, id); - if (error != 0) - return (error); - memset(&domain, 0, sizeof(domain)); - mask = malloc(domainsetsize, M_TEMP, M_WAITOK | M_ZERO); - error = cb->cpuset_copyin(maskp, mask, domainsetsize); - if (error) - goto out; + } + /* * Verify that no high bits are set. */ - if (domainsetsize > sizeof(domainset_t)) { - char *end; - char *cp; + if (mask_size > sizeof(domainset_t)) { + const char *end; + const char *cp; - end = cp = (char *)&mask->__bits; - end += domainsetsize; + end = cp = (const char *)&mask->__bits; + end += mask_size; cp += sizeof(domainset_t); - while (cp != end) + while (cp != end) { if (*cp++ != 0) { - error = EINVAL; - goto out; + return (EINVAL); } + } } if (DOMAINSET_EMPTY(mask)) { - error = EDEADLK; - goto out; + return (EDEADLK); } - DOMAINSET_COPY(mask, &domain.ds_mask); - domain.ds_policy = policy; + DOMAINSET_COPY(mask, &domain->ds_mask); + domain->ds_policy = policy; /* * Sanitize the provided mask. */ - if (!DOMAINSET_SUBSET(&all_domains, &domain.ds_mask)) { - error = EINVAL; - goto out; + if (!DOMAINSET_SUBSET(&all_domains, &domain->ds_mask)) { + return (EINVAL); } /* Translate preferred policy into a mask and fallback. */ if (policy == DOMAINSET_POLICY_PREFER) { /* Only support a single preferred domain. */ - if (DOMAINSET_COUNT(&domain.ds_mask) != 1) { - error = EINVAL; - goto out; + if (DOMAINSET_COUNT(&domain->ds_mask) != 1) { + return (EINVAL); } - domain.ds_prefer = DOMAINSET_FFS(&domain.ds_mask) - 1; + domain->ds_prefer = DOMAINSET_FFS(&domain->ds_mask) - 1; /* This will be constrained by domainset_shadow(). */ - DOMAINSET_COPY(&all_domains, &domain.ds_mask); + DOMAINSET_COPY(&all_domains, &domain->ds_mask); } + return (0); +} + +int +kern_cpuset_setdomain(struct thread *td, cpulevel_t level, cpuwhich_t which, + id_t id, size_t domainsetsize, const domainset_t *maskp, int policy, + const struct cpuset_copy_cb *cb) +{ + struct cpuset *nset; + struct cpuset *set; + struct thread *ttd; + struct proc *p; + struct domainset domain; + domainset_t *mask; + int error; + + error = cpuset_check_capabilities(td, level, which, id); + if (error != 0) + return (error); + if (domainsetsize < sizeof(domainset_t) || + domainsetsize > DOMAINSET_MAXSIZE / NBBY) + return (ERANGE); + memset(&domain, 0, sizeof(domain)); + mask = malloc(domainsetsize, M_TEMP, M_WAITOK | M_ZERO); + error = cb->cpuset_copyin(maskp, mask, domainsetsize); + if (error) + goto out; + error = domainset_populate(&domain, mask, policy, domainsetsize); + if (error) + goto out; + /* * When given an impossible policy, fall back to interleaving * across all domains. */ if (domainset_empty_vm(&domain)) domainset_copy(domainset2, &domain); - switch (level) { case CPU_LEVEL_ROOT: case CPU_LEVEL_CPUSET: diff --git a/sys/sys/domainset.h b/sys/sys/domainset.h --- a/sys/sys/domainset.h +++ b/sys/sys/domainset.h @@ -113,6 +113,20 @@ * returned value will not match the key pointer. */ struct domainset *domainset_create(const struct domainset *); + +/* + * Remove empty domains from a given domainset. + * Returns 'false' if the domainset consists entirely of empty domains. + */ +bool domainset_empty_vm(struct domainset *domain); + +/* + * Validate and populate a domainset structure according to the specified + * policy and mask. + */ +int domainset_populate(struct domainset *domain, const domainset_t *mask, int policy, + size_t mask_size); + #ifdef _SYS_SYSCTL_H_ int sysctl_handle_domainset(SYSCTL_HANDLER_ARGS); #endif