Page MenuHomeFreeBSD

amd64: implement support for disabling splitlocks
ClosedPublic

Authored by kib on Fri, Sep 18, 4:53 PM.
Tags
None
Referenced Files
F173038906: D59815.diff
Wed, Sep 23, 2:14 AM
F173020803: D59815.diff
Tue, Sep 22, 10:30 PM
F173002611: D59815.id187120.diff
Tue, Sep 22, 7:12 PM
F172971876: D59815.diff
Tue, Sep 22, 1:08 PM
F172970155: D59815.id187356.diff
Tue, Sep 22, 12:41 PM
F172969172: D59815.id187206.diff
Tue, Sep 22, 12:29 PM
F172969150: D59815.id187315.diff
Tue, Sep 22, 12:29 PM
Unknown Object (File)
Mon, Sep 21, 8:09 PM
Subscribers

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

kib requested review of this revision.Fri, Sep 18, 4:53 PM

Test program I used with simics:

/* $Id: splitlock.c,v 1.5 2026/09/18 16:39:53 kostik Exp kostik $ */

#include <sys/param.h>
#include <sys/mman.h>
#include <machine/atomic.h>
#include <machine/sysarch.h>
#include <err.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifndef I386_SET_SPLITLOCK
#define	I386_SET_SPLITLOCK	14
#define	I386_GET_SPLITLOCK	15
#endif

static void
sigbus_handler(int signo, siginfo_t *si, void *mc)
{
	printf("sig%d si_code %d addr %p\n", signo, si->si_code, si->si_addr);
	exit(0);
}

int
main(void)
{
	char *m;
	struct sigaction sa;
	int error, val;

	val = 1;
	error = sysarch(I386_SET_SPLITLOCK, &val);
	if (error == -1)
		err(1, "I386_SET_SPLITLOCK");
	error = sysarch(I386_GET_SPLITLOCK, &val);
	if (error == -1)
		err(1, "I386_GET_SPLITLOCK");
	printf("setlock: %d\n", val);

	memset(&sa, 0, sizeof(sa));
	sa.sa_sigaction = sigbus_handler;
	sa.sa_flags = SA_SIGINFO;
	if (sigaction(SIGBUS, &sa, NULL) == -1)
		err(1, "sigaction");

	m = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
	    MAP_PRIVATE | MAP_ANON, -1, 0);
	if (m == MAP_FAILED)
		err(1, "mmap");

	m += 256 - 1;
	atomic_fetchadd_long((unsigned long *)m, 1);
}

Fix reversed handling of the msr bit on context switch.

Fix splitlock control on exec for non-native ABIs.
Cache MSR_MEMORY_CTL, avoiding msr read on the context switch.
Use non-seializing immediate form for WRMSR when available.

sys/amd64/amd64/pmap.c
10257

This should load flags with atomic_load_*.

sys/amd64/amd64/sys_machdep.c
457

Extra break.

sys/amd64/amd64/vm_machdep.c
185

Shouldn't it be loading td1->td_md.md_td_flags?

If I understand correctly, #AC isn't raised if a splitlock operation happens while in kernel mode. But, what about places that use casuword() and friends? They will behave the same as before even if splitlock detection is enabled.

kib marked 3 inline comments as done.Sun, Sep 20, 4:41 PM

If I understand correctly, #AC isn't raised if a splitlock operation happens while in kernel mode. But, what about places that use casuword() and friends? They will behave the same as before even if splitlock detection is enabled.

Isn't casuword() executed in kernel mode?

Hm, I also had an impression that #AC only happen when CPL is 3. But re-checking SDM 11.1.2.3 Features to Disable Bus Locks and description of the #AC exception, I started doubt it.
I think it costs nothing to add the recovery for #AC handler in kernel mode.

Add kernel-mode #AC handler.
Copy md_td_flags from the right source.
Use atomic_load() in context switch code.

In D59815#1373713, @kib wrote:

If I understand correctly, #AC isn't raised if a splitlock operation happens while in kernel mode. But, what about places that use casuword() and friends? They will behave the same as before even if splitlock detection is enabled.

Isn't casuword() executed in kernel mode?

Hm, I also had an impression that #AC only happen when CPL is 3. But re-checking SDM 11.1.2.3 Features to Disable Bus Locks and description of the #AC exception, I started doubt it.
I think it costs nothing to add the recovery for #AC handler in kernel mode.

I checked, simics does generates #AC in CPL 0, the current patch correctly returns EFAULT from umtx_op UMTX_OP_MUTEX_LOCK on mis-aligned struct umutex.

I wonder if it should be a procctl feature instead of sysarch()? If your intent is to set ia32_splitlock_force = 1 by default, then it would be nice if proccontrol could be used to override application settings without having to modify the program or change the global default. Hmm, but I guess this wouldn't work if the policy is to reset the splitlock setting upon exec.

sys/amd64/amd64/exec_machdep.c
415

What if !ia32_splitlock is true? disable_splitlock() asserts MPASS(ia32_splitlock).

sys/amd64/amd64/pmap.c
10256

I think the oldtd != td check is redundant: if oldtd == td then the oldpmap == pmap case above should be taken.

kib marked 2 inline comments as done.Mon, Sep 21, 5:16 AM

I wonder if it should be a procctl feature instead of sysarch()? If your intent is to set ia32_splitlock_force = 1 by default, then it would be nice if proccontrol could be used to override application settings without having to modify the program or change the global default. Hmm, but I guess this wouldn't work if the policy is to reset the splitlock setting upon exec.

My first version of the patch controlled it with MD procctl. But I felt it was too 'tweaky' for such minor and probably not too popular feature. After all, we do not provide a procctl knob to enable %rflags.AC, and nobody asked for that.

If somebody wants to enable either alignment checks or disable splitlocks, this can be done with a trivial LD_PRELOADed dso. The static binaries are off (like Go-compiled binaries) but I do not feel guilty.

In exec_splitlock(), do not call disable_splitlock() if hw does not support it.
Remove oldtd != td check in context switch.

sys/amd64/amd64/pmap.c
10256

Hmm, but this is a per-thread setting, not per-process. So in the oldpmap == pmap case, where we are switching between two threads in the same proc, the per-thread splitlock setting might not be applied correctly.

sys/amd64/include/proc.h
72

It should be (k) I believe.

kib marked 2 inline comments as done.

Fix handling of context switches.

markj added inline comments.
sys/amd64/amd64/sys_machdep.c
436

I guess we don't need the proc lock here.

sys/x86/include/sysarch.h
56

Why does it have the I386_ prefix? Is it just to avoid having to translate in freebsd32_sysarch()?

This revision is now accepted and ready to land.Tue, Sep 22, 1:15 PM
kib marked 2 inline comments as done.Tue, Sep 22, 1:25 PM
kib added inline comments.
sys/amd64/amd64/sys_machdep.c
436

Indeed. md_td_flags was annotated with (c).

sys/x86/include/sysarch.h
56

To mark it as available to the i386 userspace as well. I see no sense in adding the parallel AMD64_GET/SET_SPLITLOCK constants.

kib marked 2 inline comments as done.

Remove proc lock in sysarch()

This was also tested on real hardware

(uarch synth) = Intel Emerald Rapids {Raptor Cove, optim of Golden Cove}, Intel 7
(synth) = Intel Xeon Scalable (5th Gen) Bronze/Silver/Gold/Platinum (Emerald Rapids A1/R1) {Raptor Cove, optim of Golden Cove}, Intel 7

which has the splitlock control.

This revision now requires review to proceed.Tue, Sep 22, 1:27 PM

Presumably sysarch.2 should also be updated.

Presumably sysarch.2 should also be updated.

Rather, it should be written. Or might be sysarch.x86.2. Right now the man page is a stub that proclaims the existence of the syscall.
I might do it later.

In D59815#1374524, @kib wrote:

Presumably sysarch.2 should also be updated.

Rather, it should be written. Or might be sysarch.x86.2. Right now the man page is a stub that proclaims the existence of the syscall.
I might do it later.

Oops, ok. I was thinking about pkru(3) and forgot that it is a separate page.

This revision is now accepted and ready to land.Tue, Sep 22, 2:14 PM