1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-11-30 08:19:09 +00:00

Implement 'domainset', a cpuset based NUMA policy mechanism. This allows

userspace to control NUMA policy administratively and programmatically.

Implement domainset based iterators in the page layer.

Remove the now legacy numa_* syscalls.

Cleanup some header polution created by having seq.h in proc.h.

Reviewed by:	markj, kib
Discussed with:	alc
Tested by:	pho
Sponsored by:	Netflix, Dell/EMC Isilon
Differential Revision:	https://reviews.freebsd.org/D13403
This commit is contained in:
Jeff Roberson 2018-01-12 22:48:23 +00:00
parent fe8be58826
commit 3f289c3fcf
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327895
36 changed files with 1339 additions and 1142 deletions

View File

@ -398,6 +398,8 @@ FBSD_1.5 {
mknodat;
stat;
statfs;
cpuset_getdomain;
cpuset_setdomain;
};
FBSDprivate_1.0 {
@ -1022,4 +1024,8 @@ FBSDprivate_1.0 {
gssd_syscall;
__libc_interposing_slot;
__libc_sigwait;
_cpuset_getdomain;
__sys_cpuset_getdomain;
_cpuset_setdomain;
__sys_cpuset_setdomain;
};

View File

@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/ptrace.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/machdep.h>

View File

@ -3016,6 +3016,24 @@ freebsd32_cpuset_setaffinity(struct thread *td,
PAIR32TO64(id_t,uap->id), uap->cpusetsize, uap->mask));
}
int
freebsd32_cpuset_getdomain(struct thread *td,
struct freebsd32_cpuset_getdomain_args *uap)
{
return (kern_cpuset_getdomain(td, uap->level, uap->which,
PAIR32TO64(id_t,uap->id), uap->domainsetsize, uap->mask, uap->policy));
}
int
freebsd32_cpuset_setdomain(struct thread *td,
struct freebsd32_cpuset_setdomain_args *uap)
{
return (kern_cpuset_setdomain(td, uap->level, uap->which,
PAIR32TO64(id_t,uap->id), uap->domainsetsize, uap->mask, uap->policy));
}
int
freebsd32_nmount(struct thread *td,
struct freebsd32_nmount_args /* {

View File

@ -1086,12 +1086,8 @@
547 AUE_FUTIMESAT STD { int freebsd32_utimensat(int fd, \
char *path, \
struct timespec *times, int flag); }
548 AUE_NULL NOPROTO { int numa_getaffinity(cpuwhich_t which, \
id_t id, \
struct vm_domain_policy *policy); }
549 AUE_NULL NOPROTO { int numa_setaffinity(cpuwhich_t which, \
id_t id, \
const struct vm_domain_policy *policy); }
548 AUE_NULL UNIMPL numa_getaffinity
549 AUE_NULL UNIMPL numa_setaffinity
550 AUE_FSYNC NOPROTO { int fdatasync(int fd); }
551 AUE_FSTAT STD { int freebsd32_fstat(int fd, \
struct stat32 *ub); }
@ -1119,4 +1115,13 @@
struct kevent32 *eventlist, \
int nevents, \
const struct timespec32 *timeout); }
561 AUE_NULL STD { int freebsd32_cpuset_getdomain(cpulevel_t level, \
cpuwhich_t which, uint32_t id1, uint32_t id2, \
size_t domainsetsize, domainset_t *mask, \
int *policy); }
562 AUE_NULL STD { int freebsd32_cpuset_setdomain(cpulevel_t level, \
cpuwhich_t which, uint32_t id1, uint32_t id2, \
size_t domainsetsize, domainset_t *mask, \
int policy); }
; vim: syntax=off

View File

@ -3787,7 +3787,6 @@ kern/kern_module.c standard
kern/kern_mtxpool.c standard
kern/kern_mutex.c standard
kern/kern_ntptime.c standard
kern/kern_numa.c standard
kern/kern_osd.c standard
kern/kern_physio.c standard
kern/kern_pmc.c standard
@ -4837,7 +4836,7 @@ vm/swap_pager.c standard
vm/uma_core.c standard
vm/uma_dbg.c standard
vm/memguard.c optional DEBUG_MEMGUARD
vm/vm_domain.c standard
vm/vm_domainset.c standard
vm/vm_fault.c standard
vm/vm_glue.c standard
vm/vm_init.c standard

View File

@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kdb.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <machine/kdb.h>
#include <machine/pcb.h>

View File

@ -89,7 +89,6 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_domain.h>
#include <sys/copyright.h>
#include <ddb/ddb.h>
@ -497,10 +496,7 @@ proc0_init(void *dummy __unused)
td->td_flags = TDF_INMEM;
td->td_pflags = TDP_KTHREAD;
td->td_cpuset = cpuset_thread0();
vm_domain_policy_init(&td->td_vm_dom_policy);
vm_domain_policy_set(&td->td_vm_dom_policy, VM_POLICY_NONE, -1);
vm_domain_policy_init(&p->p_vm_dom_policy);
vm_domain_policy_set(&p->p_vm_dom_policy, VM_POLICY_NONE, -1);
td->td_domain.dr_policy = td->td_cpuset->cs_domain;
prison0_init();
p->p_peers = 0;
p->p_leader = p;

View File

@ -599,8 +599,8 @@ struct sysent sysent[] = {
{ AS(ppoll_args), (sy_call_t *)sys_ppoll, AUE_POLL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 545 = ppoll */
{ AS(futimens_args), (sy_call_t *)sys_futimens, AUE_FUTIMES, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 546 = futimens */
{ AS(utimensat_args), (sy_call_t *)sys_utimensat, AUE_FUTIMESAT, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 547 = utimensat */
{ AS(numa_getaffinity_args), (sy_call_t *)sys_numa_getaffinity, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 548 = numa_getaffinity */
{ AS(numa_setaffinity_args), (sy_call_t *)sys_numa_setaffinity, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 549 = numa_setaffinity */
{ 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 548 = numa_getaffinity */
{ 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 549 = numa_setaffinity */
{ AS(fdatasync_args), (sy_call_t *)sys_fdatasync, AUE_FSYNC, NULL, 0, 0, 0, SY_THR_STATIC }, /* 550 = fdatasync */
{ AS(fstat_args), (sy_call_t *)sys_fstat, AUE_FSTAT, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 551 = fstat */
{ AS(fstatat_args), (sy_call_t *)sys_fstatat, AUE_FSTATAT, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 552 = fstatat */
@ -612,4 +612,6 @@ struct sysent sysent[] = {
{ AS(fhstatfs_args), (sy_call_t *)sys_fhstatfs, AUE_FHSTATFS, NULL, 0, 0, 0, SY_THR_STATIC }, /* 558 = fhstatfs */
{ AS(mknodat_args), (sy_call_t *)sys_mknodat, AUE_MKNODAT, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 559 = mknodat */
{ AS(kevent_args), (sy_call_t *)sys_kevent, AUE_KEVENT, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 560 = kevent */
{ AS(cpuset_getdomain_args), (sy_call_t *)sys_cpuset_getdomain, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 561 = cpuset_getdomain */
{ AS(cpuset_setdomain_args), (sy_call_t *)sys_cpuset_setdomain, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 562 = cpuset_setdomain */
};

File diff suppressed because it is too large Load Diff

View File

@ -88,7 +88,6 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_map.h>
#include <vm/vm_page.h>
#include <vm/uma.h>
#include <vm/vm_domain.h>
#ifdef KDTRACE_HOOKS
#include <sys/dtrace_bsd.h>
@ -931,10 +930,6 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options)
#ifdef MAC
mac_proc_destroy(p);
#endif
/*
* Free any domain policy that's still hiding around.
*/
vm_domain_policy_cleanup(&p->p_vm_dom_policy);
KASSERT(FIRST_THREAD_IN_PROC(p),
("proc_reap: no residual thread!"));

View File

@ -83,7 +83,6 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_map.h>
#include <vm/vm_extern.h>
#include <vm/uma.h>
#include <vm/vm_domain.h>
#ifdef KDTRACE_HOOKS
#include <sys/dtrace_bsd.h>
@ -512,14 +511,6 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
if (p1->p_flag & P_PROFIL)
startprofclock(p2);
/*
* Whilst the proc lock is held, copy the VM domain data out
* using the VM domain method.
*/
vm_domain_policy_init(&p2->p_vm_dom_policy);
vm_domain_policy_localcopy(&p2->p_vm_dom_policy,
&p1->p_vm_dom_policy);
if (fr->fr_flags & RFSIGSHARE) {
p2->p_sigacts = sigacts_hold(p1->p_sigacts);
} else {

View File

@ -1,169 +0,0 @@
/*-
* Copyright (c) 2015, Adrian Chadd <adrian@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/refcount.h>
#include <sys/sched.h>
#include <sys/smp.h>
#include <sys/syscallsubr.h>
#include <sys/cpuset.h>
#include <sys/sx.h>
#include <sys/queue.h>
#include <sys/libkern.h>
#include <sys/limits.h>
#include <sys/bus.h>
#include <sys/interrupt.h>
#include <vm/uma.h>
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/vm_param.h>
#include <vm/vm_domain.h>
int
sys_numa_setaffinity(struct thread *td, struct numa_setaffinity_args *uap)
{
int error;
struct vm_domain_policy vp;
struct thread *ttd;
struct proc *p;
struct cpuset *set;
set = NULL;
p = NULL;
/*
* Copy in just the policy information into the policy
* struct. Userland only supplies vm_domain_policy_entry.
*/
error = copyin(uap->policy, &vp.p, sizeof(vp.p));
if (error)
goto out;
/*
* Ensure the seq number is zero - otherwise seq.h
* may get very confused.
*/
vp.seq = 0;
/*
* Validate policy.
*/
if (vm_domain_policy_validate(&vp) != 0) {
error = EINVAL;
goto out;
}
/*
* Go find the desired proc/tid for this operation.
*/
error = cpuset_which(uap->which, uap->id, &p,
&ttd, &set);
if (error)
goto out;
/* Only handle CPU_WHICH_TID and CPU_WHICH_PID */
/*
* XXX if cpuset_which is called with WHICH_CPUSET and NULL cpuset,
* it'll return ESRCH. We should just return EINVAL.
*/
switch (uap->which) {
case CPU_WHICH_TID:
vm_domain_policy_copy(&ttd->td_vm_dom_policy, &vp);
break;
case CPU_WHICH_PID:
vm_domain_policy_copy(&p->p_vm_dom_policy, &vp);
break;
default:
error = EINVAL;
break;
}
PROC_UNLOCK(p);
out:
if (set)
cpuset_rel(set);
return (error);
}
int
sys_numa_getaffinity(struct thread *td, struct numa_getaffinity_args *uap)
{
int error;
struct vm_domain_policy vp;
struct thread *ttd;
struct proc *p;
struct cpuset *set;
set = NULL;
p = NULL;
error = cpuset_which(uap->which, uap->id, &p,
&ttd, &set);
if (error)
goto out;
/* Only handle CPU_WHICH_TID and CPU_WHICH_PID */
/*
* XXX if cpuset_which is called with WHICH_CPUSET and NULL cpuset,
* it'll return ESRCH. We should just return EINVAL.
*/
switch (uap->which) {
case CPU_WHICH_TID:
vm_domain_policy_localcopy(&vp, &ttd->td_vm_dom_policy);
break;
case CPU_WHICH_PID:
vm_domain_policy_localcopy(&vp, &p->p_vm_dom_policy);
break;
default:
error = EINVAL;
break;
}
if (p)
PROC_UNLOCK(p);
/*
* Copy out only the vm_domain_policy_entry part.
*/
if (error == 0)
error = copyout(&vp.p, uap->policy, sizeof(vp.p));
out:
if (set)
cpuset_rel(set);
return (error);
}

View File

@ -57,8 +57,6 @@ __FBSDID("$FreeBSD$");
#include <sys/umtx.h>
#include <sys/limits.h>
#include <vm/vm_domain.h>
#include <machine/frame.h>
#include <security/audit/audit.h>
@ -260,12 +258,6 @@ thread_create(struct thread *td, struct rtprio *rtp,
if (p->p_ptevents & PTRACE_LWP)
newtd->td_dbgflags |= TDB_BORN;
/*
* Copy the existing thread VM policy into the new thread.
*/
vm_domain_policy_localcopy(&newtd->td_vm_dom_policy,
&td->td_vm_dom_policy);
PROC_UNLOCK(p);
tidhash_add(newtd);

View File

@ -64,7 +64,6 @@ __FBSDID("$FreeBSD$");
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/uma.h>
#include <vm/vm_domain.h>
#include <sys/eventhandler.h>
/*
@ -78,13 +77,13 @@ __FBSDID("$FreeBSD$");
* structures.
*/
#ifdef __amd64__
_Static_assert(offsetof(struct thread, td_flags) == 0xf4,
_Static_assert(offsetof(struct thread, td_flags) == 0xfc,
"struct thread KBI td_flags");
_Static_assert(offsetof(struct thread, td_pflags) == 0xfc,
_Static_assert(offsetof(struct thread, td_pflags) == 0x104,
"struct thread KBI td_pflags");
_Static_assert(offsetof(struct thread, td_frame) == 0x460,
_Static_assert(offsetof(struct thread, td_frame) == 0x468,
"struct thread KBI td_frame");
_Static_assert(offsetof(struct thread, td_emuldata) == 0x508,
_Static_assert(offsetof(struct thread, td_emuldata) == 0x510,
"struct thread KBI td_emuldata");
_Static_assert(offsetof(struct proc, p_flag) == 0xb0,
"struct proc KBI p_flag");
@ -98,13 +97,13 @@ _Static_assert(offsetof(struct proc, p_emuldata) == 0x4b8,
"struct proc KBI p_emuldata");
#endif
#ifdef __i386__
_Static_assert(offsetof(struct thread, td_flags) == 0x9c,
_Static_assert(offsetof(struct thread, td_flags) == 0x98,
"struct thread KBI td_flags");
_Static_assert(offsetof(struct thread, td_pflags) == 0xa4,
_Static_assert(offsetof(struct thread, td_pflags) == 0xa0,
"struct thread KBI td_pflags");
_Static_assert(offsetof(struct thread, td_frame) == 0x2ec,
_Static_assert(offsetof(struct thread, td_frame) == 0x2e4,
"struct thread KBI td_frame");
_Static_assert(offsetof(struct thread, td_emuldata) == 0x338,
_Static_assert(offsetof(struct thread, td_emuldata) == 0x330,
"struct thread KBI td_emuldata");
_Static_assert(offsetof(struct proc, p_flag) == 0x68,
"struct proc KBI p_flag");
@ -413,7 +412,6 @@ thread_alloc(int pages)
return (NULL);
}
cpu_thread_alloc(td);
vm_domain_policy_init(&td->td_vm_dom_policy);
return (td);
}
@ -443,7 +441,6 @@ thread_free(struct thread *td)
cpu_thread_free(td);
if (td->td_kstack != 0)
vm_thread_dispose(td);
vm_domain_policy_cleanup(&td->td_vm_dom_policy);
callout_drain(&td->td_slpcallout);
uma_zfree(thread_zone, td);
}

View File

@ -139,6 +139,7 @@ sed -e '
printf "#include <sys/signal.h>\n" > sysarg
printf "#include <sys/acl.h>\n" > sysarg
printf "#include <sys/cpuset.h>\n" > sysarg
printf "#include <sys/domainset.h>\n" > sysarg
printf "#include <sys/_ffcounter.h>\n" > sysarg
printf "#include <sys/_semaphore.h>\n" > sysarg
printf "#include <sys/ucontext.h>\n" > sysarg

View File

@ -781,6 +781,7 @@ sched_fork_thread(struct thread *td, struct thread *childtd)
childtd->td_lastcpu = NOCPU;
childtd->td_lock = &sched_lock;
childtd->td_cpuset = cpuset_ref(td->td_cpuset);
childtd->td_domain.dr_policy = td->td_cpuset->cs_domain;
childtd->td_priority = childtd->td_base_pri;
ts = td_get_sched(childtd);
bzero(ts, sizeof(*ts));

View File

@ -2131,6 +2131,7 @@ sched_fork_thread(struct thread *td, struct thread *child)
child->td_lastcpu = NOCPU;
child->td_lock = TDQ_LOCKPTR(tdq);
child->td_cpuset = cpuset_ref(td->td_cpuset);
child->td_domain.dr_policy = td->td_cpuset->cs_domain;
ts2->ts_cpu = ts->ts_cpu;
ts2->ts_flags = 0;
/*

View File

@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/pcpu.h>
#include <sys/proc.h>
#include <sys/sbuf.h>

View File

@ -997,12 +997,8 @@
547 AUE_FUTIMESAT STD { int utimensat(int fd, \
char *path, \
struct timespec *times, int flag); }
548 AUE_NULL STD { int numa_getaffinity(cpuwhich_t which, \
id_t id, \
struct vm_domain_policy_entry *policy); }
549 AUE_NULL STD { int numa_setaffinity(cpuwhich_t which, \
id_t id, const struct \
vm_domain_policy_entry *policy); }
548 AUE_NULL UNIMPL numa_getaffinity
549 AUE_NULL UNIMPL numa_setaffinity
550 AUE_FSYNC STD { int fdatasync(int fd); }
551 AUE_FSTAT STD { int fstat(int fd, struct stat *sb); }
552 AUE_FSTATAT STD { int fstatat(int fd, char *path, \
@ -1023,6 +1019,14 @@
struct kevent *changelist, int nchanges, \
struct kevent *eventlist, int nevents, \
const struct timespec *timeout); }
561 AUE_NULL STD { int cpuset_getdomain(cpulevel_t level, \
cpuwhich_t which, id_t id, \
size_t domainsetsize, domainset_t *mask, \
int *policy); }
562 AUE_NULL STD { int cpuset_setdomain(cpulevel_t level, \
cpuwhich_t which, id_t id, \
size_t domainsetsize, domainset_t *mask, \
int policy); }
; Please copy any additions and changes to the following compatability tables:
; sys/compat/freebsd32/syscalls.master

View File

@ -44,6 +44,7 @@
#include <netinet/ip_fw.h> /* flow_id */
#include <netinet/ip_dummynet.h>
#include <sys/lock.h>
#include <sys/proc.h>
#include <sys/rwlock.h>

View File

@ -1,63 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2015 Adrian Chadd <adrian@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD$
*/
#ifndef __SYS_VM_DOMAIN_H__
#define __SYS_VM_DOMAIN_H__
#include <sys/seq.h>
typedef enum {
VM_POLICY_NONE,
VM_POLICY_ROUND_ROBIN,
VM_POLICY_FIXED_DOMAIN,
VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN,
VM_POLICY_FIRST_TOUCH,
VM_POLICY_FIRST_TOUCH_ROUND_ROBIN,
VM_POLICY_MAX
} vm_domain_policy_type_t;
struct vm_domain_policy_entry {
vm_domain_policy_type_t policy;
int domain;
};
struct vm_domain_policy {
seq_t seq;
struct vm_domain_policy_entry p;
};
#define VM_DOMAIN_POLICY_STATIC_INITIALISER(vt, vd) \
{ .seq = 0, \
.p.policy = vt, \
.p.domain = vd }
#endif /* __SYS_VM_DOMAIN_H__ */

View File

@ -112,6 +112,7 @@ LIST_HEAD(setlist, cpuset);
*/
struct cpuset {
cpuset_t cs_mask; /* bitmask of valid cpus. */
struct domainset *cs_domain; /* (c) NUMA policy. */
volatile u_int cs_ref; /* (a) Reference count. */
int cs_flags; /* (s) Flags from below. */
cpusetid_t cs_id; /* (s) Id or INVALID. */

View File

@ -62,11 +62,18 @@
#include <sys/time.h> /* For structs itimerval, timeval. */
#else
#include <sys/pcpu.h>
#include <sys/systm.h>
#endif
#include <sys/ucontext.h>
#include <sys/ucred.h>
#include <sys/_vm_domain.h>
#include <sys/types.h>
#include <sys/domainset.h>
#include <machine/proc.h> /* Machine-dependent proc substruct. */
#ifdef _KERNEL
#include <machine/cpu.h>
#endif
/*
* One structure allocated per session.
@ -179,6 +186,7 @@ struct procdesc;
struct racct;
struct sbuf;
struct sleepqueue;
struct socket;
struct syscall_args;
struct td_sched;
struct thread;
@ -222,12 +230,12 @@ struct thread {
TAILQ_ENTRY(thread) td_lockq; /* (t) Lock queue. */
LIST_ENTRY(thread) td_hash; /* (d) Hash chain. */
struct cpuset *td_cpuset; /* (t) CPU affinity mask. */
struct domainset_ref td_domain; /* (a) NUMA policy */
struct seltd *td_sel; /* Select queue/channel. */
struct sleepqueue *td_sleepqueue; /* (k) Associated sleep queue. */
struct turnstile *td_turnstile; /* (k) Associated turnstile. */
struct rl_q_entry *td_rlqe; /* (k) Associated range lock entry. */
struct umtx_q *td_umtxq; /* (c?) Link for when we're blocked. */
struct vm_domain_policy td_vm_dom_policy; /* (c) current numa domain policy */
lwpid_t td_tid; /* (b) Thread ID. */
sigqueue_t td_sigqueue; /* (c) Sigs arrived, not delivered. */
#define td_siglist td_sigqueue.sq_signals
@ -286,7 +294,6 @@ struct thread {
pid_t td_dbg_forked; /* (c) Child pid for debugger. */
u_int td_vp_reserv; /* (k) Count of reserved vnodes. */
int td_no_sleeping; /* (k) Sleeping disabled count. */
int td_dom_rr_idx; /* (k) RR Numa domain selection. */
void *td_su; /* (k) FFS SU private */
sbintime_t td_sleeptimo; /* (t) Sleep timeout. */
int td_rtcgen; /* (s) rtc_generation of abs. sleep */
@ -655,7 +662,6 @@ struct proc {
uint64_t p_prev_runtime; /* (c) Resource usage accounting. */
struct racct *p_racct; /* (b) Resource accounting. */
int p_throttled; /* (c) Flag for racct pcpu throttling */
struct vm_domain_policy p_vm_dom_policy; /* (c) process default VM domain, or -1 */
/*
* An orphan is the child that has beed re-parented to the
* debugger as a result of attaching to it. Need to keep

View File

@ -36,6 +36,7 @@
#include <sys/mac.h>
#include <sys/mount.h>
#include <sys/_cpuset.h>
#include <sys/_domainset.h>
struct file;
struct filecaps;
@ -96,6 +97,12 @@ int kern_cpuset_getaffinity(struct thread *td, cpulevel_t level,
int kern_cpuset_setaffinity(struct thread *td, cpulevel_t level,
cpuwhich_t which, id_t id, size_t cpusetsize,
const cpuset_t *maskp);
int kern_cpuset_getdomain(struct thread *td, cpulevel_t level,
cpuwhich_t which, id_t id, size_t domainsetsize,
domainset_t *maskp, int *policyp);
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);
int kern_cpuset_getid(struct thread *td, cpulevel_t level,
cpuwhich_t which, id_t id, cpusetid_t *setid);
int kern_cpuset_setid(struct thread *td, cpuwhich_t which,

View File

@ -1,514 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2015 Adrian Chadd <adrian@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_vm.h"
#include "opt_ddb.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#ifdef VM_NUMA_ALLOC
#include <sys/proc.h>
#endif
#include <sys/queue.h>
#include <sys/rwlock.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/tree.h>
#include <sys/vmmeter.h>
#include <sys/seq.h>
#include <ddb/ddb.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_kern.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_phys.h>
#include <vm/vm_domain.h>
/*
* Default to first-touch + round-robin.
*/
static struct mtx vm_default_policy_mtx;
MTX_SYSINIT(vm_default_policy, &vm_default_policy_mtx, "default policy mutex",
MTX_DEF);
#ifdef VM_NUMA_ALLOC
static struct vm_domain_policy vm_default_policy =
VM_DOMAIN_POLICY_STATIC_INITIALISER(VM_POLICY_FIRST_TOUCH_ROUND_ROBIN, 0);
#else
/* Use round-robin so the domain policy code will only try once per allocation */
static struct vm_domain_policy vm_default_policy =
VM_DOMAIN_POLICY_STATIC_INITIALISER(VM_POLICY_ROUND_ROBIN, 0);
#endif
static int
sysctl_vm_default_policy(SYSCTL_HANDLER_ARGS)
{
char policy_name[32];
int error;
mtx_lock(&vm_default_policy_mtx);
/* Map policy to output string */
switch (vm_default_policy.p.policy) {
case VM_POLICY_FIRST_TOUCH:
strcpy(policy_name, "first-touch");
break;
case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
strcpy(policy_name, "first-touch-rr");
break;
case VM_POLICY_ROUND_ROBIN:
default:
strcpy(policy_name, "rr");
break;
}
mtx_unlock(&vm_default_policy_mtx);
error = sysctl_handle_string(oidp, &policy_name[0],
sizeof(policy_name), req);
if (error != 0 || req->newptr == NULL)
return (error);
mtx_lock(&vm_default_policy_mtx);
/* Set: match on the subset of policies that make sense as a default */
if (strcmp("first-touch-rr", policy_name) == 0) {
vm_domain_policy_set(&vm_default_policy,
VM_POLICY_FIRST_TOUCH_ROUND_ROBIN, 0);
} else if (strcmp("first-touch", policy_name) == 0) {
vm_domain_policy_set(&vm_default_policy,
VM_POLICY_FIRST_TOUCH, 0);
} else if (strcmp("rr", policy_name) == 0) {
vm_domain_policy_set(&vm_default_policy,
VM_POLICY_ROUND_ROBIN, 0);
} else {
error = EINVAL;
goto finish;
}
error = 0;
finish:
mtx_unlock(&vm_default_policy_mtx);
return (error);
}
SYSCTL_PROC(_vm, OID_AUTO, default_policy, CTLTYPE_STRING | CTLFLAG_RW,
0, 0, sysctl_vm_default_policy, "A",
"Default policy (rr, first-touch, first-touch-rr");
/*
* Initialise a VM domain iterator.
*
* Check the thread policy, then the proc policy,
* then default to the system policy.
*/
void
vm_policy_iterator_init(struct vm_domain_iterator *vi)
{
#ifdef VM_NUMA_ALLOC
struct vm_domain_policy lcl;
#endif
vm_domain_iterator_init(vi);
#ifdef VM_NUMA_ALLOC
/* Copy out the thread policy */
vm_domain_policy_localcopy(&lcl, &curthread->td_vm_dom_policy);
if (lcl.p.policy != VM_POLICY_NONE) {
/* Thread policy is present; use it */
vm_domain_iterator_set_policy(vi, &lcl);
return;
}
vm_domain_policy_localcopy(&lcl,
&curthread->td_proc->p_vm_dom_policy);
if (lcl.p.policy != VM_POLICY_NONE) {
/* Process policy is present; use it */
vm_domain_iterator_set_policy(vi, &lcl);
return;
}
#endif
/* Use system default policy */
vm_domain_iterator_set_policy(vi, &vm_default_policy);
}
void
vm_policy_iterator_finish(struct vm_domain_iterator *vi)
{
vm_domain_iterator_cleanup(vi);
}
#ifdef VM_NUMA_ALLOC
static __inline int
vm_domain_rr_selectdomain(int skip_domain)
{
struct thread *td;
td = curthread;
td->td_dom_rr_idx++;
td->td_dom_rr_idx %= vm_ndomains;
/*
* If skip_domain is provided then skip over that
* domain. This is intended for round robin variants
* which first try a fixed domain.
*/
if ((skip_domain > -1) && (td->td_dom_rr_idx == skip_domain)) {
td->td_dom_rr_idx++;
td->td_dom_rr_idx %= vm_ndomains;
}
return (td->td_dom_rr_idx);
}
#endif
/*
* This implements a very simple set of VM domain memory allocation
* policies and iterators.
*/
/*
* A VM domain policy represents a desired VM domain policy.
* Iterators implement searching through VM domains in a specific
* order.
*/
/*
* When setting a policy, the caller must establish their own
* exclusive write protection for the contents of the domain
* policy.
*/
int
vm_domain_policy_init(struct vm_domain_policy *vp)
{
bzero(vp, sizeof(*vp));
vp->p.policy = VM_POLICY_NONE;
vp->p.domain = -1;
return (0);
}
int
vm_domain_policy_set(struct vm_domain_policy *vp,
vm_domain_policy_type_t vt, int domain)
{
seq_write_begin(&vp->seq);
vp->p.policy = vt;
vp->p.domain = domain;
seq_write_end(&vp->seq);
return (0);
}
/*
* Take a local copy of a policy.
*
* The destination policy isn't write-barriered; this is used
* for doing local copies into something that isn't shared.
*/
void
vm_domain_policy_localcopy(struct vm_domain_policy *dst,
const struct vm_domain_policy *src)
{
seq_t seq;
for (;;) {
seq = seq_read(&src->seq);
*dst = *src;
if (seq_consistent(&src->seq, seq))
return;
}
}
/*
* Take a write-barrier copy of a policy.
*
* The destination policy is write -barriered; this is used
* for doing copies into policies that may be read by other
* threads.
*/
void
vm_domain_policy_copy(struct vm_domain_policy *dst,
const struct vm_domain_policy *src)
{
seq_t seq;
struct vm_domain_policy d;
for (;;) {
seq = seq_read(&src->seq);
d = *src;
if (seq_consistent(&src->seq, seq)) {
seq_write_begin(&dst->seq);
dst->p.domain = d.p.domain;
dst->p.policy = d.p.policy;
seq_write_end(&dst->seq);
return;
}
}
}
int
vm_domain_policy_validate(const struct vm_domain_policy *vp)
{
switch (vp->p.policy) {
case VM_POLICY_NONE:
case VM_POLICY_ROUND_ROBIN:
case VM_POLICY_FIRST_TOUCH:
case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
if (vp->p.domain == -1)
return (0);
return (-1);
case VM_POLICY_FIXED_DOMAIN:
case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
#ifdef VM_NUMA_ALLOC
if (vp->p.domain >= 0 && vp->p.domain < vm_ndomains)
return (0);
#else
if (vp->p.domain == 0)
return (0);
#endif
return (-1);
default:
return (-1);
}
return (-1);
}
int
vm_domain_policy_cleanup(struct vm_domain_policy *vp)
{
/* For now, empty */
return (0);
}
int
vm_domain_iterator_init(struct vm_domain_iterator *vi)
{
/* Nothing to do for now */
return (0);
}
/*
* Manually setup an iterator with the given details.
*/
int
vm_domain_iterator_set(struct vm_domain_iterator *vi,
vm_domain_policy_type_t vt, int domain)
{
#ifdef VM_NUMA_ALLOC
switch (vt) {
case VM_POLICY_FIXED_DOMAIN:
vi->policy = VM_POLICY_FIXED_DOMAIN;
vi->domain = domain;
vi->n = 1;
break;
case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
vi->policy = VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN;
vi->domain = domain;
vi->n = vm_ndomains;
break;
case VM_POLICY_FIRST_TOUCH:
vi->policy = VM_POLICY_FIRST_TOUCH;
vi->domain = PCPU_GET(domain);
vi->n = 1;
break;
case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
vi->policy = VM_POLICY_FIRST_TOUCH_ROUND_ROBIN;
vi->domain = PCPU_GET(domain);
vi->n = vm_ndomains;
break;
case VM_POLICY_ROUND_ROBIN:
default:
vi->policy = VM_POLICY_ROUND_ROBIN;
vi->domain = -1;
vi->n = vm_ndomains;
break;
}
#else
vi->domain = 0;
vi->n = 1;
#endif
return (0);
}
/*
* Setup an iterator based on the given policy.
*/
static inline void
_vm_domain_iterator_set_policy(struct vm_domain_iterator *vi,
const struct vm_domain_policy *vt)
{
#ifdef VM_NUMA_ALLOC
/*
* Initialise the iterator.
*
* For first-touch, the initial domain is set
* via the current thread CPU domain.
*
* For fixed-domain, it's assumed that the
* caller has initialised the specific domain
* it is after.
*/
switch (vt->p.policy) {
case VM_POLICY_FIXED_DOMAIN:
vi->policy = vt->p.policy;
vi->domain = vt->p.domain;
vi->n = 1;
break;
case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
vi->policy = vt->p.policy;
vi->domain = vt->p.domain;
vi->n = vm_ndomains;
break;
case VM_POLICY_FIRST_TOUCH:
vi->policy = vt->p.policy;
vi->domain = PCPU_GET(domain);
vi->n = 1;
break;
case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
vi->policy = vt->p.policy;
vi->domain = PCPU_GET(domain);
vi->n = vm_ndomains;
break;
case VM_POLICY_ROUND_ROBIN:
default:
/*
* Default to round-robin policy.
*/
vi->policy = VM_POLICY_ROUND_ROBIN;
vi->domain = -1;
vi->n = vm_ndomains;
break;
}
#else
vi->domain = 0;
vi->n = 1;
#endif
}
void
vm_domain_iterator_set_policy(struct vm_domain_iterator *vi,
const struct vm_domain_policy *vt)
{
seq_t seq;
struct vm_domain_policy vt_lcl;
for (;;) {
seq = seq_read(&vt->seq);
vt_lcl = *vt;
if (seq_consistent(&vt->seq, seq)) {
_vm_domain_iterator_set_policy(vi, &vt_lcl);
return;
}
}
}
/*
* Return the next VM domain to use.
*
* Returns 0 w/ domain set to the next domain to use, or
* -1 to indicate no more domains are available.
*/
int
vm_domain_iterator_run(struct vm_domain_iterator *vi, int *domain)
{
/* General catch-all */
if (vi->n <= 0)
return (-1);
#ifdef VM_NUMA_ALLOC
switch (vi->policy) {
case VM_POLICY_FIXED_DOMAIN:
case VM_POLICY_FIRST_TOUCH:
*domain = vi->domain;
vi->n--;
break;
case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
/*
* XXX TODO: skip over the rr'ed domain
* if it equals the one we started with.
*/
if (vi->n == vm_ndomains)
*domain = vi->domain;
else
*domain = vm_domain_rr_selectdomain(vi->domain);
vi->n--;
break;
case VM_POLICY_ROUND_ROBIN:
default:
*domain = vm_domain_rr_selectdomain(-1);
vi->n--;
break;
}
#else
*domain = 0;
vi->n--;
#endif
return (0);
}
/*
* Returns 1 if the iteration is done, or 0 if it has not.
* This can only be called after at least one loop through
* the iterator. Ie, it's designed to be used as a tail
* check of a loop, not the head check of a loop.
*/
int
vm_domain_iterator_isdone(struct vm_domain_iterator *vi)
{
return (vi->n <= 0);
}
int
vm_domain_iterator_cleanup(struct vm_domain_iterator *vi)
{
return (0);
}

View File

@ -1,71 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2015 Adrian Chadd <adrian@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD$
*/
#ifndef __VM_DOMAIN_H__
#define __VM_DOMAIN_H__
#include <sys/_vm_domain.h>
struct vm_domain_iterator {
vm_domain_policy_type_t policy;
int domain;
int n;
};
/*
* TODO: check to see if these should just become inline functions
* at some point.
*/
extern int vm_domain_policy_init(struct vm_domain_policy *vp);
extern int vm_domain_policy_set(struct vm_domain_policy *vp,
vm_domain_policy_type_t vt, int domain);
extern int vm_domain_policy_cleanup(struct vm_domain_policy *vp);
extern void vm_domain_policy_localcopy(struct vm_domain_policy *dst,
const struct vm_domain_policy *src);
extern void vm_domain_policy_copy(struct vm_domain_policy *dst,
const struct vm_domain_policy *src);
extern int vm_domain_policy_validate(const struct vm_domain_policy *vp);
extern int vm_domain_iterator_init(struct vm_domain_iterator *vi);
extern int vm_domain_iterator_set(struct vm_domain_iterator *vi,
vm_domain_policy_type_t vt, int domain);
extern void vm_domain_iterator_set_policy(struct vm_domain_iterator *vi,
const struct vm_domain_policy *vt);
extern int vm_domain_iterator_run(struct vm_domain_iterator *vi,
int *domain);
extern int vm_domain_iterator_isdone(struct vm_domain_iterator *vi);
extern int vm_domain_iterator_cleanup(struct vm_domain_iterator *vi);
extern void vm_policy_iterator_init(struct vm_domain_iterator *vi);
extern void vm_policy_iterator_finish(struct vm_domain_iterator *vi);
#endif /* __VM_DOMAIN_H__ */

View File

@ -1589,6 +1589,7 @@ vm_fault_copy_entry(vm_map_t dst_map, vm_map_t src_map,
KASSERT(upgrade || dst_entry->object.vm_object == NULL,
("vm_fault_copy_entry: vm_object not NULL"));
if (src_object != dst_object) {
dst_object->domain = src_object->domain;
dst_entry->object.vm_object = dst_object;
dst_entry->offset = 0;
dst_object->charge = dst_entry->end - dst_entry->start;

View File

@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cpuset.h>
#include <sys/lock.h>
#include <sys/mman.h>
#include <sys/mount.h>
@ -1364,6 +1365,7 @@ vm_object_shadow(
result->backing_object_offset = *offset;
if (source != NULL) {
VM_OBJECT_WLOCK(source);
result->domain = source->domain;
LIST_INSERT_HEAD(&source->shadow_head, result, shadow_list);
source->shadow_count++;
#if VM_NRESERVLEVEL > 0
@ -1419,6 +1421,7 @@ vm_object_split(vm_map_entry_t entry)
*/
VM_OBJECT_WLOCK(new_object);
VM_OBJECT_WLOCK(orig_object);
new_object->domain = orig_object->domain;
source = orig_object->backing_object;
if (source != NULL) {
VM_OBJECT_WLOCK(source);

View File

@ -74,6 +74,7 @@
#include <sys/_mutex.h>
#include <sys/_pctrie.h>
#include <sys/_rwlock.h>
#include <sys/_domainset.h>
#include <vm/_vm_radix.h>
@ -102,6 +103,7 @@ struct vm_object {
struct pglist memq; /* list of resident pages */
struct vm_radix rtree; /* root of the resident page radix trie*/
vm_pindex_t size; /* Object size */
struct domainset_ref domain; /* NUMA policy. */
int generation; /* generation ID */
int ref_count; /* How many refs?? */
int shadow_count; /* how many objects that this is a shadow for */

View File

@ -91,6 +91,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
#include <sys/domainset.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/linker.h>
@ -109,7 +110,7 @@ __FBSDID("$FreeBSD$");
#include <vm/vm.h>
#include <vm/pmap.h>
#include <vm/vm_param.h>
#include <vm/vm_domain.h>
#include <vm/vm_domainset.h>
#include <vm/vm_kern.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
@ -742,6 +743,12 @@ vm_page_startup(vm_offset_t vaddr)
*/
vm_reserv_init();
#endif
/*
* Set an initial domain policy for thread0 so that allocations
* can work.
*/
domainset_zero();
return (vaddr);
}
@ -1622,23 +1629,17 @@ vm_page_t
vm_page_alloc_after(vm_object_t object, vm_pindex_t pindex,
int req, vm_page_t mpred)
{
struct vm_domain_iterator vi;
struct vm_domainset_iter di;
vm_page_t m;
int domain, wait;
int domain;
m = NULL;
vm_policy_iterator_init(&vi);
wait = req & (VM_ALLOC_WAITFAIL | VM_ALLOC_WAITOK);
req &= ~wait;
while (vm_domain_iterator_run(&vi, &domain) == 0) {
if (vm_domain_iterator_isdone(&vi))
req |= wait;
vm_domainset_iter_page_init(&di, object, &domain, &req);
do {
m = vm_page_alloc_domain_after(object, pindex, domain, req,
mpred);
if (m != NULL)
break;
}
vm_policy_iterator_finish(&vi);
} while (vm_domainset_iter_page(&di, &domain, &req) == 0);
return (m);
}
@ -1835,23 +1836,17 @@ vm_page_alloc_contig(vm_object_t object, vm_pindex_t pindex, int req,
u_long npages, vm_paddr_t low, vm_paddr_t high, u_long alignment,
vm_paddr_t boundary, vm_memattr_t memattr)
{
struct vm_domain_iterator vi;
struct vm_domainset_iter di;
vm_page_t m;
int domain, wait;
int domain;
m = NULL;
vm_policy_iterator_init(&vi);
wait = req & (VM_ALLOC_WAITFAIL | VM_ALLOC_WAITOK);
req &= ~wait;
while (vm_domain_iterator_run(&vi, &domain) == 0) {
if (vm_domain_iterator_isdone(&vi))
req |= wait;
vm_domainset_iter_page_init(&di, object, &domain, &req);
do {
m = vm_page_alloc_contig_domain(object, pindex, domain, req,
npages, low, high, alignment, boundary, memattr);
if (m != NULL)
break;
}
vm_policy_iterator_finish(&vi);
} while (vm_domainset_iter_page(&di, &domain, &req) == 0);
return (m);
}
@ -2045,22 +2040,16 @@ vm_page_alloc_check(vm_page_t m)
vm_page_t
vm_page_alloc_freelist(int freelist, int req)
{
struct vm_domain_iterator vi;
struct vm_domainset_iter di;
vm_page_t m;
int domain, wait;
int domain;
m = NULL;
vm_policy_iterator_init(&vi);
wait = req & (VM_ALLOC_WAITFAIL | VM_ALLOC_WAITOK);
req &= ~wait;
while (vm_domain_iterator_run(&vi, &domain) == 0) {
if (vm_domain_iterator_isdone(&vi))
req |= wait;
vm_domainset_iter_page_init(&di, kernel_object, &domain, &req);
do {
m = vm_page_alloc_freelist_domain(domain, freelist, req);
if (m != NULL)
break;
}
vm_policy_iterator_finish(&vi);
} while (vm_domainset_iter_page(&di, &domain, &req) == 0);
return (m);
}
@ -2562,8 +2551,8 @@ CTASSERT(powerof2(NRUNS));
* must be a power of two.
*/
bool
vm_page_reclaim_contig(int req, u_long npages, vm_paddr_t low, vm_paddr_t high,
u_long alignment, vm_paddr_t boundary)
vm_page_reclaim_contig_domain(int domain, int req, u_long npages,
vm_paddr_t low, vm_paddr_t high, u_long alignment, vm_paddr_t boundary)
{
vm_paddr_t curr_low;
vm_page_t m_run, m_runs[NRUNS];
@ -2603,8 +2592,8 @@ vm_page_reclaim_contig(int req, u_long npages, vm_paddr_t low, vm_paddr_t high,
curr_low = low;
count = 0;
for (;;) {
m_run = vm_phys_scan_contig(npages, curr_low, high,
alignment, boundary, options);
m_run = vm_phys_scan_contig(domain, npages, curr_low,
high, alignment, boundary, options);
if (m_run == NULL)
break;
curr_low = VM_PAGE_TO_PHYS(m_run) + ptoa(npages);
@ -2645,6 +2634,26 @@ vm_page_reclaim_contig(int req, u_long npages, vm_paddr_t low, vm_paddr_t high,
}
}
bool
vm_page_reclaim_contig(int req, u_long npages, vm_paddr_t low, vm_paddr_t high,
u_long alignment, vm_paddr_t boundary)
{
struct vm_domainset_iter di;
int domain;
bool ret;
vm_domainset_iter_page_init(&di, kernel_object, &domain, &req);
do {
ret = vm_page_reclaim_contig_domain(domain, req, npages, low,
high, alignment, boundary);
if (ret)
break;
} while (vm_domainset_iter_page(&di, &domain, &req) == 0);
return (ret);
}
/*
* vm_wait: (also see VM_WAIT macro)
*

View File

@ -229,6 +229,7 @@ struct vm_pagequeue {
struct vm_domain {
struct vm_pagequeue vmd_pagequeues[PQ_COUNT];
struct vmem *vmd_kernel_arena;
u_int vmd_page_count;
u_int vmd_free_count;
long vmd_segs; /* bitmask of the segments */
@ -514,7 +515,7 @@ void vm_page_putfake(vm_page_t m);
void vm_page_readahead_finish(vm_page_t m);
bool vm_page_reclaim_contig(int req, u_long npages, vm_paddr_t low,
vm_paddr_t high, u_long alignment, vm_paddr_t boundary);
bool vm_page_reclaim_contig_domain(int req, u_long npages, int domain,
bool vm_page_reclaim_contig_domain(int domain, int req, u_long npages,
vm_paddr_t low, vm_paddr_t high, u_long alignment, vm_paddr_t boundary);
void vm_page_reference(vm_page_t m);
void vm_page_remove (vm_page_t);

View File

@ -68,8 +68,6 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_page.h>
#include <vm/vm_phys.h>
#include <vm/vm_domain.h>
_Static_assert(sizeof(long) * NBBY >= VM_PHYSSEG_MAX,
"Too many physsegs.");
@ -973,7 +971,7 @@ vm_phys_free_contig(vm_page_t m, u_long npages)
* be a power of two.
*/
vm_page_t
vm_phys_scan_contig(u_long npages, vm_paddr_t low, vm_paddr_t high,
vm_phys_scan_contig(int domain, u_long npages, vm_paddr_t low, vm_paddr_t high,
u_long alignment, vm_paddr_t boundary, int options)
{
vm_paddr_t pa_end;
@ -988,6 +986,8 @@ vm_phys_scan_contig(u_long npages, vm_paddr_t low, vm_paddr_t high,
return (NULL);
for (segind = 0; segind < vm_phys_nsegs; segind++) {
seg = &vm_phys_segs[segind];
if (seg->domain != domain)
continue;
if (seg->start >= high)
break;
if (low >= seg->end)

View File

@ -86,8 +86,8 @@ void vm_phys_free_contig(vm_page_t m, u_long npages);
void vm_phys_free_pages(vm_page_t m, int order);
void vm_phys_init(void);
vm_page_t vm_phys_paddr_to_vm_page(vm_paddr_t pa);
vm_page_t vm_phys_scan_contig(u_long npages, vm_paddr_t low, vm_paddr_t high,
u_long alignment, vm_paddr_t boundary, int options);
vm_page_t vm_phys_scan_contig(int domain, u_long npages, vm_paddr_t low,
vm_paddr_t high, u_long alignment, vm_paddr_t boundary, int options);
void vm_phys_set_pool(int pool, vm_page_t m, int order);
boolean_t vm_phys_unfree_page(vm_page_t m);
int vm_phys_mem_affinity(int f, int t);

View File

@ -252,7 +252,8 @@ srat_parse_entry(ACPI_SUBTABLE_HEADER *entry, void *arg)
"enabled" : "disabled");
if (!(mem->Flags & ACPI_SRAT_MEM_ENABLED))
break;
if (!overlaps_phys_avail(mem->BaseAddress,
if (mem->BaseAddress >= cpu_getmaxphyaddr() ||
!overlaps_phys_avail(mem->BaseAddress,
mem->BaseAddress + mem->Length)) {
printf("SRAT: Ignoring memory at addr 0x%jx\n",
(uintmax_t)mem->BaseAddress);

View File

@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/cpuset.h>
#include <sys/domainset.h>
#include <ctype.h>
#include <err.h>
@ -55,6 +56,7 @@ static int gflag;
static int iflag;
static int jflag;
static int lflag;
static int nflag;
static int pflag;
static int rflag;
static int sflag;
@ -66,30 +68,40 @@ static cpuwhich_t which;
static void usage(void);
static void printset(cpuset_t *mask);
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 },
{ NULL, DOMAINSET_POLICY_INVALID }
};
BITSET_DEFINE(bitset, 1);
static void printset(struct bitset *mask, int size);
static void
parselist(char *list, cpuset_t *mask)
parselist(char *list, struct bitset *mask, int size)
{
enum { NONE, NUM, DASH } state;
int lastnum;
int curnum;
char *l;
if (strcasecmp(list, "all") == 0) {
if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1,
sizeof(*mask), mask) != 0)
err(EXIT_FAILURE, "getaffinity");
return;
}
state = NONE;
curnum = lastnum = 0;
for (l = list; *l != '\0';) {
if (isdigit(*l)) {
curnum = atoi(l);
if (curnum > CPU_SETSIZE)
if (curnum > size)
errx(EXIT_FAILURE,
"Only %d cpus supported", CPU_SETSIZE);
"List entry %d exceeds maximum of %d",
curnum, size);
while (isdigit(*l))
l++;
switch (state) {
@ -99,7 +111,7 @@ parselist(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:
@ -114,7 +126,7 @@ parselist(char *list, cpuset_t *mask)
case NONE:
break;
case NUM:
CPU_SET(curnum, mask);
BIT_SET(size, curnum, mask);
state = NONE;
break;
case DASH:
@ -136,29 +148,86 @@ parselist(char *list, cpuset_t *mask)
case NONE:
break;
case NUM:
CPU_SET(curnum, mask);
BIT_SET(size, curnum, mask);
break;
case DASH:
goto parserr;
}
return;
parserr:
errx(EXIT_FAILURE, "Malformed cpu-list %s", list);
errx(EXIT_FAILURE, "Malformed list %s", list);
}
static void
printset(cpuset_t *mask)
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)
{
int once;
int cpu;
int bit;
for (once = 0, cpu = 0; cpu < CPU_SETSIZE; cpu++) {
if (CPU_ISSET(cpu, mask)) {
for (once = 0, bit = 0; bit < size; bit++) {
if (BIT_ISSET(size, bit, mask)) {
if (once == 0) {
printf("%d", cpu);
printf("%d", bit);
once = 1;
} else
printf(", %d", cpu);
printf(", %d", bit);
}
}
printf("\n");
@ -167,17 +236,30 @@ printset(cpuset_t *mask)
static const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "irq", "jail",
"domain" };
static const char *levelnames[] = { NULL, " root", " cpuset", "" };
static const char *policynames[] = { "invalid", "round-robin", "first-touch",
"prefer" };
static void
printaffinity(void)
{
domainset_t domain;
cpuset_t mask;
int policy;
if (cpuset_getaffinity(level, which, id, sizeof(mask), &mask) != 0)
err(EXIT_FAILURE, "getaffinity");
printf("%s %jd%s mask: ", whichnames[which], (intmax_t)id,
levelnames[level]);
printset(&mask);
printset((struct bitset *)&mask, CPU_SETSIZE);
if (dflag)
goto out;
if (cpuset_getdomain(level, which, id, sizeof(domain), &domain,
&policy) != 0)
err(EXIT_FAILURE, "getdomain");
printf("%s %jd%s domain policy: %s mask: ", whichnames[which],
(intmax_t)id, levelnames[level], policynames[policy]);
printset((struct bitset *)&domain, DOMAINSET_SETSIZE);
out:
exit(EXIT_SUCCESS);
}
@ -200,17 +282,21 @@ printsetid(void)
int
main(int argc, char *argv[])
{
domainset_t domains;
cpusetid_t setid;
cpuset_t mask;
int policy;
lwpid_t tid;
pid_t pid;
int ch;
CPU_ZERO(&mask);
DOMAINSET_ZERO(&domains);
policy = DOMAINSET_POLICY_INVALID;
level = CPU_LEVEL_WHICH;
which = CPU_WHICH_PID;
id = pid = tid = setid = -1;
while ((ch = getopt(argc, argv, "Ccd:gij:l:p:rs:t:x:")) != -1) {
while ((ch = getopt(argc, argv, "Ccd:gij:l:n:p:rs:t:x:")) != -1) {
switch (ch) {
case 'C':
Cflag = 1;
@ -237,7 +323,11 @@ main(int argc, char *argv[])
break;
case 'l':
lflag = 1;
parselist(optarg, &mask);
parsecpulist(optarg, &mask);
break;
case 'n':
nflag = 1;
parsedomainlist(optarg, &domains, &policy);
break;
case 'p':
pflag = 1;
@ -270,7 +360,7 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
if (gflag) {
if (argc || Cflag || lflag)
if (argc || Cflag || lflag || nflag)
usage();
/* Only one identity specifier. */
if (dflag + jflag + xflag + sflag + pflag + tflag > 1)
@ -281,6 +371,7 @@ main(int argc, char *argv[])
printaffinity();
exit(EXIT_SUCCESS);
}
if (dflag || iflag || rflag)
usage();
/*
@ -301,6 +392,11 @@ main(int argc, char *argv[])
-1, sizeof(mask), &mask) != 0)
err(EXIT_FAILURE, "setaffinity");
}
if (nflag) {
if (cpuset_setdomain(level, CPU_WHICH_PID,
-1, sizeof(domains), &domains, policy) != 0)
err(EXIT_FAILURE, "setdomain");
}
errno = 0;
execvp(*argv, argv);
err(errno == ENOENT ? 127 : 126, "%s", *argv);
@ -310,9 +406,9 @@ main(int argc, char *argv[])
*/
if (Cflag && (jflag || !pflag || sflag || tflag || xflag))
usage();
if (!lflag && cflag)
if ((!lflag && !nflag) && cflag)
usage();
if (!lflag && !(Cflag || sflag))
if ((!lflag && !nflag) && !(Cflag || sflag))
usage();
/* You can only set a mask on a thread. */
if (tflag && (sflag | pflag | xflag | jflag))
@ -344,6 +440,11 @@ main(int argc, char *argv[])
&mask) != 0)
err(EXIT_FAILURE, "setaffinity");
}
if (nflag) {
if (cpuset_setdomain(level, which, id, sizeof(domains),
&domains, policy) != 0)
err(EXIT_FAILURE, "setdomain");
}
exit(EXIT_SUCCESS);
}

View File

@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/cpuset.h>
#include <sys/numa.h>
#include <sys/domainset.h>
#include <sys/resource.h>
#include <sys/time.h>
@ -58,58 +58,67 @@ static struct option longopts[] = {
};
static const char *
policy_to_str(vm_domain_policy_type_t vt)
policy_to_str(int policy)
{
switch (vt) {
case VM_POLICY_NONE:
return ("none");
case VM_POLICY_ROUND_ROBIN:
return ("rr");
case VM_POLICY_FIXED_DOMAIN:
return ("fixed-domain");
case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
return ("fixed-domain-rr");
case VM_POLICY_FIRST_TOUCH:
switch (policy) {
case DOMAINSET_POLICY_INVALID:
return ("invalid");
case DOMAINSET_POLICY_ROUNDROBIN:
return ("round-robin");
case DOMAINSET_POLICY_FIRSTTOUCH:
return ("first-touch");
case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
return ("first-touch-rr");
case DOMAINSET_POLICY_PREFER:
return ("prefer");
default:
return ("unknown");
}
}
static void
domain_print(domainset_t *mask)
{
int once;
int bit;
for (once = 0, bit = 0; bit < DOMAINSET_SETSIZE; bit++) {
if (DOMAINSET_ISSET(bit, mask)) {
if (once == 0) {
printf("%d", bit);
once = 1;
} else
printf(", %d", bit);
}
}
printf("\n");
}
static int
parse_policy(struct vm_domain_policy_entry *vd, const char *str)
parse_policy(int *policy, const char *str)
{
if (strcmp(str, "rr") == 0) {
vd->policy = VM_POLICY_ROUND_ROBIN;
vd->domain = -1;
*policy = DOMAINSET_POLICY_ROUNDROBIN;
return (0);
}
if (strcmp(str, "first-touch-rr") == 0) {
vd->policy = VM_POLICY_FIRST_TOUCH_ROUND_ROBIN;
vd->domain = -1;
*policy = DOMAINSET_POLICY_FIRSTTOUCH;
return (0);
}
if (strcmp(str, "first-touch") == 0) {
vd->policy = VM_POLICY_FIRST_TOUCH;
vd->domain = -1;
*policy = DOMAINSET_POLICY_FIRSTTOUCH;
return (0);
}
if (strcmp(str, "fixed-domain") == 0) {
vd->policy = VM_POLICY_FIXED_DOMAIN;
vd->domain = 0;
*policy = DOMAINSET_POLICY_PREFER;
return (0);
}
if (strcmp(str, "fixed-domain-rr") == 0) {
vd->policy = VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN;
vd->domain = 0;
*policy = DOMAINSET_POLICY_PREFER;
return (0);
}
@ -150,10 +159,28 @@ set_numa_domain_cpuaffinity(int cpu_domain, cpuwhich_t which, id_t id)
return (0);
}
/*
* Attempt to maintain compatability with old style syscalls.
*/
static int
numa_setaffinity(cpuwhich_t which, id_t id, int policy, int domain)
{
domainset_t mask;
int p;
DOMAINSET_ZERO(&mask);
if (policy == DOMAINSET_POLICY_PREFER)
DOMAINSET_SET(domain, &mask);
else if (cpuset_getdomain(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1,
sizeof(mask), &mask, &p) != 0)
err(EXIT_FAILURE, "getdomain");
return cpuset_setdomain(CPU_LEVEL_WHICH, which, id, sizeof(mask),
&mask, policy);
}
int
main(int argc, char *argv[])
{
struct vm_domain_policy_entry vd;
lwpid_t tid;
pid_t pid;
cpuwhich_t which;
@ -163,6 +190,8 @@ main(int argc, char *argv[])
int mem_policy_set;
int ch;
int cpu_domain;
int policy;
int domain;
id = -1;
which = -1;
@ -172,6 +201,8 @@ main(int argc, char *argv[])
tid = -1;
pid = -1;
cpu_domain = -1;
domain = -1;
policy = DOMAINSET_POLICY_INVALID;
while ((ch = getopt_long(argc, argv, "c:gl:m:p:st:", longopts,
NULL)) != -1) {
@ -183,7 +214,7 @@ main(int argc, char *argv[])
is_get = 1;
break;
case 'l':
if (parse_policy(&vd, optarg) != 0) {
if (parse_policy(&policy, optarg) != 0) {
fprintf(stderr,
"Could not parse policy: '%s'\n", optarg);
exit(1);
@ -191,12 +222,7 @@ main(int argc, char *argv[])
mem_policy_set = 1;
break;
case 'm':
if (mem_policy_set == 0) {
fprintf(stderr,
"Error: set policy first before domain\n");
exit(1);
}
vd.domain = atoi(optarg);
domain = atoi(optarg);
break;
case 'p':
pid = atoi(optarg);
@ -223,7 +249,7 @@ main(int argc, char *argv[])
}
/* Set current memory process policy, will be inherited */
if (numa_setaffinity(CPU_WHICH_PID, -1, &vd) != 0)
if (numa_setaffinity(CPU_WHICH_PID, -1, policy, domain) != 0)
err(1, "numa_setaffinity");
/* If a CPU domain policy was given, include that too */
@ -261,19 +287,22 @@ main(int argc, char *argv[])
/* If it's get, then get the policy and return */
if (is_get) {
error = numa_getaffinity(which, id, &vd);
domainset_t mask;
error = cpuset_getdomain(CPU_LEVEL_WHICH, which, id,
sizeof(mask), &mask, &policy);
if (error != 0)
err(1, "numa_getaffinity");
printf(" Policy: %s; domain: %d\n",
policy_to_str(vd.policy),
vd.domain);
err(1, "cpuset_getdomain");
printf(" Policy: %s; domain: ",
policy_to_str(policy));
domain_print(&mask);
exit(0);
}
/* Assume it's set */
/* Syscall */
error = numa_setaffinity(which, id, &vd);
error = numa_setaffinity(which, id, policy, domain);
if (error != 0)
err(1, "numa_setaffinity");