Port-i386 archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[PATCH] Handling large XSAVE space (Intel AMX)
tl;dr -- Handle machines with large XSAVE size (Intel AMX) by
allocating separate pages for XSAVE instead of trying to cram them
into the struct pcb page. OK to commit? Any testers? Share output
of `cpuctl identify 0' if you test?
The attached patch addresses
PR port-amd64/57661: Crash when booting on Xeon Silver 4416+ in
KVM/Qemu (https://gnats.NetBSD.org/57661)
and related issues[*] that arise on machines where the XSAVE size is
larger than the space reserved in the PCB page for a thread's FPU
state -- that is, larger than 4096 - 128 = 3968 bytes. In particular,
although AVX512 doesn't overflow this, Intel AMX -- Advanced Matrix
Extensions -- has TILEDATA state which can be up to 8192 bytes.
Context: The uarea is a contiguous region of wired kernel memory
allocated for each thread to store (a) struct pcb, (b) the thread's
kernel stack, (c) some guard pages (`red zones') and/or metadata for
kASAN/kMSAN. Currently the XSAVE state is stored in the page that
holds the struct pcb. (This space goes unused in kernel threads, but
that's fine; we can't make it any more compact anyway if we want a
guard page between the struct pcb and the thread's kernel stack.)
In order to avoid incurring unnecessary costs on machines without
Intel AMX (or whatever other large extensions might overflow the
pcb_savefpu space):
1. This only really applies to amd64 -- there is essentially no change
on i386, because Intel AMX instructions only work in 64-bit mode
anyway. (If we want to run an i386 kernel on a machine with Intel
AMX, at worst we may have to clear the XCR0[18:17] bits before
querying cpuid 0x0d in cpu_probe_fpu.)
2. For machines where struct pcb_savefpu is large enough, the only
cost is a pointer indirection: struct pcb::pcb_savefpu is a
pointer (taken from the unused padding bytes of struct pcb), and it
points to struct pcb::pcb_savefpusmall which is the actual storage.
3. For machines where struct pcb_savefpu is too small, i.e., machines
with very large XSAVE (likely because of Intel AMX), we allocate
separate pages -- enough to fit the XSAVE size -- and make struct
pcb::pcb_savefpu point at those, in cpu_uarea_alloc/free, for those
threads that need them -- i.e., user threads only.
This way, the additional costs incurred for use-cases that don't
require the changes are:
- i386: none
- amd64, with small XSAVE size: pointer indirection during
save/restore
- amd64, with large XSAVE size, for system threads: pointer
indirection during save/restore
As a side effect, this might address the problem underlying
PR kern/57258: kthread_fpu_enter/exit problem
(https://gnats.NetBSD.org/57258)
which blocked me from finishing machine-dependent optimizations for
kthread_fpu_enter/exit on x86; this costs a hefty overhead that, from
memory of working on this a few years ago, was in the realm of 10-20%
of cgd(4) throughput.
Alternatives I considered and why I rejected them:
1. Simply make USPACE larger by two pages.
=> This wastes two pages of wired kernel RAM for every thread in
the system, even if the machine will never use them because it
doesn't support Intel AMX.
2. Dynamically resize USPACE according to the XSAVE size.
=> This required considerably more engineering, and likely future
maintenance burden, to handle dynamic USPACE.
=> On machines with large XSAVE size, this still either wastes
pages of wired kernel RAM for system threads, or requires extra
engineering to handle different pcb/guard/stack arrangements in
memory for different threads -- which likely gets in the way of
future kthread_fpu_enter/exit_md optimizations.
By reusing existing space, the attached patch uses _at worst_ the
amount of wired kernel RAM as these options on the same machine under
the same workloads, but in most cases will use considerably less.
3. Disable Intel AMX altogether.
=> Not great to prevent applications from taking advantage of
modern architecture extensions that don't otherwise require any
supervisor support! But, maybe this would be lower-risk for
pullup-9.
[*] Similar cases -- I think I've seen more but these are all I can
find for now:
PR kern/57776: Under KVM: [ 1.3711794]
uvm_fault(0xffffffff8190fb40, 0xffffaf80b451e000, 2) -> e
https://gnats.NetBSD.org/57776
https://mail-index.NetBSD.org/tech-kern/2025/04/11/msg030349.html
diff -r 1cb0546d18b6 sys/arch/amd64/amd64/genassym.cf
--- a/sys/arch/amd64/amd64/genassym.cf Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/amd64/amd64/genassym.cf Fri Apr 11 22:00:30 2025 +0000
@@ -186,7 +186,6 @@ define PCB_FLAGS offsetof(struct pcb, p
define PCB_COMPAT32 PCB_COMPAT32
define PCB_FS offsetof(struct pcb, pcb_fs)
define PCB_GS offsetof(struct pcb, pcb_gs)
-define PCB_SAVEFPU offsetof(struct pcb, pcb_savefpu)
define TF_RDI offsetof(struct trapframe, tf_rdi)
define TF_RSI offsetof(struct trapframe, tf_rsi)
diff -r 1cb0546d18b6 sys/arch/amd64/include/param.h
--- a/sys/arch/amd64/include/param.h Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/amd64/include/param.h Fri Apr 11 22:00:30 2025 +0000
@@ -69,11 +69,27 @@
#define SINCR 1 /* increment of stack/NBPG */
#if defined(KASAN) || defined(KMSAN)
-#define UPAGES 8
+#define UPAGES_KxSAN 2
+#else
+#define UPAGES_KxSAN 0
+#endif
+#if defined(SVS)
+#define UPAGES_SVS 1
+#else
+#define UPAGES_SVS 0
+#endif
+#define UPAGES_PCB 1 /* one page for the PCB */
+#define UPAGES_RED 1 /* one page for red zone between pcb/stack */
+#define UPAGES_STACK 3 /* three pages (12 KiB) of stack space */
+#define UPAGES \
+ (UPAGES_PCB + UPAGES_RED + UPAGES_STACK + UPAGES_SVS + UPAGES_KxSAN)
+
+#if defined(KASAN) || defined(KMSAN)
+__CTASSERT(UPAGES == 8);
#elif defined(SVS)
-#define UPAGES 6 /* 1 page used internally by SVS */
+__CTASSERT(UPAGES == 6);
#else
-#define UPAGES 5 /* pages of u-area (1 for redzone) */
+__CTASSERT(UPAGES == 5);
#endif
#define USPACE (UPAGES * NBPG) /* total size of u-area */
diff -r 1cb0546d18b6 sys/arch/amd64/include/pcb.h
--- a/sys/arch/amd64/include/pcb.h Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/amd64/include/pcb.h Fri Apr 11 22:00:30 2025 +0000
@@ -97,13 +97,16 @@ struct pcb {
struct dbreg *pcb_dbregs;
uint16_t pcb_fpu_dflt_cw;
int pcb_iopl;
+ union savefpu *pcb_savefpu;
- uint32_t pcb_unused[8]; /* unused */
+ uint32_t pcb_unused[6]; /* unused */
- union savefpu pcb_savefpu __aligned(64); /* floating point state */
+ /* fpu state, if it fits; otherwise allocated separately */
+ union savefpu pcb_savefpusmall __aligned(64);
/* **** DO NOT ADD ANYTHING HERE **** */
};
#ifndef __lint__
+__CTASSERT(offsetof(struct pcb, pcb_savefpusmall) == 128);
__CTASSERT(sizeof(struct pcb) - sizeof (union savefpu) == 128);
#endif
diff -r 1cb0546d18b6 sys/arch/i386/i386/genassym.cf
--- a/sys/arch/i386/i386/genassym.cf Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/i386/i386/genassym.cf Fri Apr 11 22:00:30 2025 +0000
@@ -192,7 +192,6 @@ define PCB_ESP0 offsetof(struct pcb, pc
define PCB_FSD offsetof(struct pcb, pcb_fsd)
define PCB_GSD offsetof(struct pcb, pcb_gsd)
define PCB_IOMAP offsetof(struct pcb, pcb_iomap)
-define PCB_SAVEFPU offsetof(struct pcb, pcb_savefpu)
define TF_CS offsetof(struct trapframe, tf_cs)
define TF_EIP offsetof(struct trapframe, tf_eip)
diff -r 1cb0546d18b6 sys/arch/i386/include/pcb.h
--- a/sys/arch/i386/include/pcb.h Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/i386/include/pcb.h Fri Apr 11 22:00:30 2025 +0000
@@ -99,12 +99,14 @@ struct pcb {
int not_used[15];
/* floating point state */
- union savefpu pcb_savefpu __aligned(64);
+ union savefpu pcb_savefpu[1] __aligned(64);
+#define pcb_savefpusmall pcb_savefpu
/* **** DO NOT ADD ANYTHING HERE **** */
};
#ifndef __lint__
/* This doesn't really matter, but there is a lot of implied padding */
+__CTASSERT(offsetof(struct pcb, pcb_savefpu) == 128);
__CTASSERT(sizeof(struct pcb) - sizeof (union savefpu) == 128);
#endif
diff -r 1cb0546d18b6 sys/arch/x86/include/cpu.h
--- a/sys/arch/x86/include/cpu.h Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/x86/include/cpu.h Fri Apr 11 22:00:30 2025 +0000
@@ -481,6 +481,7 @@ extern uint64_t x86_xsave_features;
extern size_t x86_xsave_offsets[];
extern size_t x86_xsave_sizes[];
extern uint32_t x86_fpu_mxcsr_mask;
+bool x86_fpu_save_separate_p(void);
extern void (*x86_cpu_idle)(void);
#define cpu_idle() (*x86_cpu_idle)()
diff -r 1cb0546d18b6 sys/arch/x86/include/cpu_extended_state.h
--- a/sys/arch/x86/include/cpu_extended_state.h Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/x86/include/cpu_extended_state.h Fri Apr 11 22:00:30 2025 +0000
@@ -226,6 +226,8 @@ struct xstate {
* It is defined this way to separate the definitions and to
* minimise the number of union/struct selectors.
* NB: Some userspace stuff (eg firefox) uses it to parse ucontext.
+ * NB: This is not actually the largest possible save space;
+ * x86_fpu_save_size may be larger.
*/
union savefpu {
struct save87 sv_87;
diff -r 1cb0546d18b6 sys/arch/x86/x86/fpu.c
--- a/sys/arch/x86/x86/fpu.c Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/x86/x86/fpu.c Fri Apr 11 22:00:30 2025 +0000
@@ -136,11 +136,35 @@ void fpu_switch(struct lwp *, struct lwp
uint32_t x86_fpu_mxcsr_mask __read_mostly = 0;
+static const union savefpu safe_fpu_storage __aligned(64) = {
+ .sv_xmm = {
+ .fx_mxcsr = __SAFE_MXCSR__,
+ },
+};
+static const union savefpu zero_fpu_storage __aligned(64);
+
+static const void *safe_fpu __read_mostly = &safe_fpu_storage;
+static const void *zero_fpu __read_mostly = &zero_fpu_storage;
+
+/*
+ * x86_fpu_save_separate_p()
+ *
+ * True if we allocate the FPU save space separately, outside the
+ * struct pcb itself, because it doesn't fit in a single page.
+ */
+bool
+x86_fpu_save_separate_p(void)
+{
+
+ return x86_fpu_save_size >
+ PAGE_SIZE - offsetof(struct pcb, pcb_savefpusmall);
+}
+
static inline union savefpu *
fpu_lwp_area(struct lwp *l)
{
struct pcb *pcb = lwp_getpcb(l);
- union savefpu *area = &pcb->pcb_savefpu;
+ union savefpu *area = pcb->pcb_savefpu;
KASSERT((l->l_flag & LW_SYSTEM) == 0);
if (l == curlwp) {
@@ -155,7 +179,7 @@ static inline void
fpu_save_lwp(struct lwp *l)
{
struct pcb *pcb = lwp_getpcb(l);
- union savefpu *area = &pcb->pcb_savefpu;
+ union savefpu *area = pcb->pcb_savefpu;
int s;
s = splvm();
@@ -189,14 +213,91 @@ fpuinit(struct cpu_info *ci)
stts();
}
+/*
+ * fpuinit_mxcsr_mask()
+ *
+ * Called once by cpu_init on the primary CPU. Initializes
+ * x86_fpu_mxcsr_mask based on the initial FPU state, and
+ * initializes save_fpu and zero_fpu if necessary when the
+ * hardware's FPU save size is larger than union savefpu.
+ *
+ * XXX Rename this function!
+ */
void
fpuinit_mxcsr_mask(void)
{
+ /*
+ * If the CPU's x86 fpu save size is larger than union savefpu,
+ * we have to allocate larger buffers for the safe and zero FPU
+ * states used here and by fpu_kern_enter/leave.
+ *
+ * Note: This is NOT the same as x86_fpu_save_separate_p(),
+ * which may have a little more space than union savefpu.
+ */
+ const bool allocfpusave = x86_fpu_save_size > sizeof(union savefpu);
+ vaddr_t va;
+
+#ifdef __i386__
+ if (x86_fpu_save_separate_p()) {
+ /*
+ * XXX Need to teach cpu_uarea_alloc/free to allocate a
+ * separate fpu save space, and make pcb_savefpu a
+ * pointer indirection -- currently only done on amd64,
+ * not on i386.
+ *
+ * But the primary motivation on amd64 is the 8192-byte
+ * TILEDATA state for Intel AMX (Advanced Matrix
+ * Extensions), which doesn't work in 32-bit mode
+ * anyway, so on such machines we ought to just disable
+ * it in the first place and keep x86_fpu_save_size
+ * down:
+ *
+ * While Intel AMX instructions can be executed
+ * only in 64-bit mode, instructions of the XSAVE
+ * feature set can operate on TILECFG and TILEDATA
+ * in any mode. It is recommended that only
+ * 64-bit operating systems enable Intel AMX by
+ * setting XCR0[18:17].
+ *
+ * --Intel 64 and IA-32 Architectures Software
+ * Developer's Manual, Volume 1: Basic
+ * Architecture, Order Number: 253665-087US, March
+ * 2025, Sec. 13.3 `Enabling the XSAVE feature set
+ * and XSAVE-enabled features', p. 13-6.
+ * https://cdrdv2.intel.com/v1/dl/getContent/671436
+ * https://web.archive.org/web/20250404141850/https://cdrdv2-public.intel.com/851056/253665-087-sdm-vol-1.pdf
+ * https://web.archive.org/web/20250404141850if_/https://cdrdv2-public.intel.com/851056/253665-087-sdm-vol-1.pdf#page=324
+ */
+ panic("NetBSD/i386 does not support fpu save size %u",
+ x86_fpu_save_size);
+ }
+#endif
+
#ifndef XENPV
- union savefpu fpusave __aligned(64);
+ union savefpu fpusave_stack __aligned(64);
+ union savefpu *fpusave;
u_long psl;
- memset(&fpusave, 0, sizeof(fpusave));
+ /*
+ * Allocate a temporary save space from the stack if it fits,
+ * or from the heap otherwise, so we can query its mxcsr mask.
+ */
+ if (allocfpusave) {
+ /*
+ * Need 64-byte alignment for XSAVE instructions.
+ * kmem_* doesn't guarantee that and we don't have a
+ * handy posix_memalign in the kernel unless we hack it
+ * ourselves with vmem(9), so just ask for page
+ * alignment with uvm_km(9).
+ */
+ __CTASSERT(PAGE_SIZE >= 64);
+ va = uvm_km_alloc(kernel_map, x86_fpu_save_size, PAGE_SIZE,
+ UVM_KMF_WIRED|UVM_KMF_ZERO|UVM_KMF_WAITVA);
+ fpusave = (void *)va;
+ } else {
+ fpusave = &fpusave_stack;
+ memset(fpusave, 0, sizeof(*fpusave));
+ }
/* Disable interrupts, and enable FPU */
psl = x86_read_psl();
@@ -204,16 +305,25 @@ fpuinit_mxcsr_mask(void)
clts();
/* Fill in the FPU area */
- fxsave(&fpusave);
+ fxsave(fpusave);
/* Restore previous state */
stts();
x86_write_psl(psl);
- if (fpusave.sv_xmm.fx_mxcsr_mask == 0) {
+ if (fpusave->sv_xmm.fx_mxcsr_mask == 0) {
x86_fpu_mxcsr_mask = __INITIAL_MXCSR_MASK__;
} else {
- x86_fpu_mxcsr_mask = fpusave.sv_xmm.fx_mxcsr_mask;
+ x86_fpu_mxcsr_mask = fpusave->sv_xmm.fx_mxcsr_mask;
+ }
+
+ /*
+ * Free the temporary save space.
+ */
+ if (allocfpusave) {
+ uvm_km_free(kernel_map, va, x86_fpu_save_size, UVM_KMF_WIRED);
+ fpusave = NULL;
+ va = 0;
}
#else
/*
@@ -223,6 +333,32 @@ fpuinit_mxcsr_mask(void)
*/
x86_fpu_mxcsr_mask = __INITIAL_MXCSR_MASK__;
#endif
+
+ /*
+ * If necessary, allocate FPU save spaces for safe or zero FPU
+ * state, for fpu_kern_enter/leave.
+ */
+ if (allocfpusave) {
+ __CTASSERT(PAGE_SIZE >= 64);
+
+ va = uvm_km_alloc(kernel_map, x86_fpu_save_size, PAGE_SIZE,
+ UVM_KMF_WIRED|UVM_KMF_ZERO|UVM_KMF_WAITVA);
+ memcpy((void *)va, &safe_fpu_storage,
+ sizeof(safe_fpu_storage));
+ uvm_km_protect(kernel_map, va, x86_fpu_save_size,
+ VM_PROT_READ);
+ safe_fpu = (void *)va;
+
+ va = uvm_km_alloc(kernel_map, x86_fpu_save_size, PAGE_SIZE,
+ UVM_KMF_WIRED|UVM_KMF_ZERO|UVM_KMF_WAITVA);
+ /*
+ * No initialization -- just want zeroes! In fact we
+ * could share this with other all-zero pages.
+ */
+ uvm_km_protect(kernel_map, va, x86_fpu_save_size,
+ VM_PROT_READ);
+ zero_fpu = (void *)va;
+ }
}
static inline void
@@ -305,7 +441,7 @@ void
fpu_handle_deferred(void)
{
struct pcb *pcb = lwp_getpcb(curlwp);
- fpu_area_restore(&pcb->pcb_savefpu, x86_xsave_features,
+ fpu_area_restore(pcb->pcb_savefpu, x86_xsave_features,
!(curlwp->l_proc->p_flag & PK_32));
}
@@ -321,7 +457,7 @@ fpu_switch(struct lwp *oldlwp, struct lw
if (oldlwp->l_md.md_flags & MDL_FPU_IN_CPU) {
KASSERT(!(oldlwp->l_flag & LW_SYSTEM));
pcb = lwp_getpcb(oldlwp);
- fpu_area_save(&pcb->pcb_savefpu, x86_xsave_features,
+ fpu_area_save(pcb->pcb_savefpu, x86_xsave_features,
!(oldlwp->l_proc->p_flag & PK_32));
oldlwp->l_md.md_flags &= ~MDL_FPU_IN_CPU;
}
@@ -338,14 +474,15 @@ fpu_lwp_fork(struct lwp *l1, struct lwp
if (__predict_false(l2->l_flag & LW_SYSTEM)) {
return;
}
+
/* For init(8). */
if (__predict_false(l1->l_flag & LW_SYSTEM)) {
- memset(&pcb2->pcb_savefpu, 0, x86_fpu_save_size);
+ memset(pcb2->pcb_savefpu, 0, x86_fpu_save_size);
return;
}
fpu_save = fpu_lwp_area(l1);
- memcpy(&pcb2->pcb_savefpu, fpu_save, x86_fpu_save_size);
+ memcpy(pcb2->pcb_savefpu, fpu_save, x86_fpu_save_size);
l2->l_md.md_flags &= ~MDL_FPU_IN_CPU;
}
@@ -378,11 +515,6 @@ fpu_lwp_abandon(struct lwp *l)
void
fpu_kern_enter(void)
{
- static const union savefpu safe_fpu __aligned(64) = {
- .sv_xmm = {
- .fx_mxcsr = __SAFE_MXCSR__,
- },
- };
struct lwp *l = curlwp;
struct cpu_info *ci;
int s;
@@ -421,7 +553,7 @@ fpu_kern_enter(void)
/*
* Zero the FPU registers and install safe control words.
*/
- fpu_area_restore(&safe_fpu, x86_xsave_features, /*is_64bit*/false);
+ fpu_area_restore(safe_fpu, x86_xsave_features, /*is_64bit*/false);
}
/*
@@ -432,7 +564,6 @@ fpu_kern_enter(void)
void
fpu_kern_leave(void)
{
- static const union savefpu zero_fpu __aligned(64);
struct cpu_info *ci = curcpu();
int s;
@@ -451,7 +582,7 @@ fpu_kern_leave(void)
* through Spectre-class attacks to userland, even if there are
* no bugs in fpu state management.
*/
- fpu_area_restore(&zero_fpu, x86_xsave_features, /*is_64bit*/false);
+ fpu_area_restore(zero_fpu, x86_xsave_features, /*is_64bit*/false);
/*
* Set CR0_TS again so that the kernel can't accidentally use
diff -r 1cb0546d18b6 sys/arch/x86/x86/vm_machdep.c
--- a/sys/arch/x86/x86/vm_machdep.c Thu Apr 10 18:53:29 2025 +0000
+++ b/sys/arch/x86/x86/vm_machdep.c Fri Apr 11 22:00:30 2025 +0000
@@ -366,10 +366,42 @@ cpu_uarea_alloc(bool system)
{
vaddr_t base, va;
paddr_t pa;
+ struct pcb *pcb;
base = uvm_km_alloc(kernel_map, USPACE + PAGE_SIZE, 0,
UVM_KMF_WIRED|UVM_KMF_WAITVA);
+ /*
+ * Prepare the FPU save area:
+ *
+ * 1. If this is a system thread, no save area.
+ * XXX Allocate/free one in kthread_fpu_enter/exit_md.
+ *
+ * 2. If this is a user thread, and the fpu save size is large
+ * enough, allocate an extra block of memory for it.
+ *
+ * 3. Otherwise, this is a user thread and the fpu save size
+ * fits inside the pcb page, so use that.
+ *
+ * XXX Note that this is currently amd64-only -- if you extend
+ * this FPU save space allocation to i386, you'll need to
+ * remove the panic in fpuinit_mxcsr_mask on
+ * x86_fpu_save_separate_p and make pcb_savefpu a pointer
+ * indirection in struct pcb.
+ */
+ pcb = (void *)base;
+ if (system) { /* (1) */
+ pcb->pcb_savefpu = NULL;
+ } else if (x86_fpu_save_separate_p()) { /* (2) */
+ __CTASSERT(PAGE_SIZE >= 64);
+ /* No need to zero -- caller will initialize. */
+ va = uvm_km_alloc(kernel_map, x86_fpu_save_size, PAGE_SIZE,
+ UVM_KMF_WIRED|UVM_KMF_WAITVA);
+ pcb->pcb_savefpu = (void *)va;
+ } else { /* (3) */
+ pcb->pcb_savefpu = &pcb->pcb_savefpusmall;
+ }
+
/* Page[1] = RedZone */
va = base + PAGE_SIZE;
if (!pmap_extract(pmap_kernel(), va, &pa)) {
@@ -394,8 +426,20 @@ cpu_uarea_alloc(bool system)
bool
cpu_uarea_free(void *addr)
{
+ const struct pcb *const pcb = addr;
vaddr_t base = (vaddr_t)addr;
+ /*
+ * If we allocated a separate FPU save area, free it.
+ */
+ if (pcb->pcb_savefpu != NULL &&
+ pcb->pcb_savefpu != &pcb->pcb_savefpusmall) {
+ KASSERTMSG(x86_fpu_save_separate_p(), "pcb=%p pcb_savefpu=%p",
+ pcb, pcb->pcb_savefpu);
+ uvm_km_free(kernel_map, (vaddr_t)pcb->pcb_savefpu,
+ x86_fpu_save_size, UVM_KMF_WIRED);
+ }
+
KASSERT(!pmap_extract(pmap_kernel(), base + PAGE_SIZE, NULL));
KASSERT(!pmap_extract(pmap_kernel(), base + USPACE, NULL));
uvm_km_free(kernel_map, base, USPACE + PAGE_SIZE, UVM_KMF_WIRED);
Home |
Main Index |
Thread Index |
Old Index