git: e318f24c0f53 - main - amd64: support for tracking per-thread 'disable splitlocks' state

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Tue, 22 Sep 2026 15:41:50 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=e318f24c0f53b52bd280b827509ad752db0497bf

commit e318f24c0f53b52bd280b827509ad752db0497bf
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-09-18 14:33:54 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-09-22 15:40:40 +0000

    amd64: support for tracking per-thread 'disable splitlocks' state
    
    If hw supports it, on atomic operation that requires exclusive ownership
    of more than one cache line, #AC is generated.
    The state is maintained as the arch-private TDF_MD_SPLITLOCK_AC flag.
    
    The state is inherited on thread creation from the thread spawning the
    new one.  It is cleared on exec.
    
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D59815
---
 sys/amd64/amd64/exec_machdep.c     | 10 ++++++++++
 sys/amd64/amd64/machdep.c          | 36 ++++++++++++++++++++++++++++++++++++
 sys/amd64/amd64/pmap.c             | 14 ++++++++++++++
 sys/amd64/amd64/vm_machdep.c       |  2 ++
 sys/amd64/ia32/ia32_signal.c       |  1 +
 sys/amd64/include/md_var.h         |  5 +++++
 sys/amd64/include/proc.h           |  2 ++
 sys/amd64/linux/linux_sysvec.c     |  1 +
 sys/amd64/linux32/linux32_sysvec.c |  1 +
 9 files changed, 72 insertions(+)

diff --git a/sys/amd64/amd64/exec_machdep.c b/sys/amd64/amd64/exec_machdep.c
index d5d3342b7dff..19b087c783d3 100644
--- a/sys/amd64/amd64/exec_machdep.c
+++ b/sys/amd64/amd64/exec_machdep.c
@@ -406,6 +406,15 @@ x86_clear_dbregs(struct pcb *pcb)
 	clear_pcb_flags(pcb, PCB_DBREGS);
 }
 
+void
+exec_splitlock(struct thread *td)
+{
+	if (ia32_splitlock_force)
+		enable_splitlock(td);
+	else if (ia32_splitlock)
+		disable_splitlock(td);
+}
+
 /*
  * Reset registers to default values on exec.
  */
@@ -443,6 +452,7 @@ exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack)
 	regs->tf_flags = TF_HASSEGS;
 
 	x86_clear_dbregs(pcb);
+	exec_splitlock(td);
 
 	/*
 	 * Drop the FP state if we hold it, so that the process gets a
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index c34dd7818b8c..b10c58955ce0 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
@@ -1928,6 +1928,42 @@ safe_read(vm_offset_t addr, char *valp)
 	return (uiomove_mem(UIO_MEM_KMEM, &uio));
 }
 
+void
+enable_splitlock_ac(void)
+{
+	MPASS(ia32_splitlock);
+	wrmsr(MSR_MEMORY_CTL, rdmsr(MSR_MEMORY_CTL) |
+	    MSR_MEMORY_CTL_SPLITLOCK);
+}
+
+void
+enable_splitlock(struct thread *td)
+{
+	MPASS(td == curthread);
+	td->td_md.md_td_flags |= TDF_MD_SPLITLOCK_AC;
+	critical_enter();
+	enable_splitlock_ac();
+	critical_exit();
+}
+
+void
+disable_splitlock_ac(void)
+{
+	MPASS(ia32_splitlock);
+	wrmsr(MSR_MEMORY_CTL, rdmsr(MSR_MEMORY_CTL) &
+	    ~MSR_MEMORY_CTL_SPLITLOCK);
+}
+
+void
+disable_splitlock(struct thread *td)
+{
+	MPASS(td == curthread);
+	td->td_md.md_td_flags &= ~TDF_MD_SPLITLOCK_AC;
+	critical_enter();
+	disable_splitlock_ac();
+	critical_exit();
+}
+
 #ifdef KDB
 
 /*
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
index 8f8d91c768b7..74c1fc0d755e 100644
--- a/sys/amd64/amd64/pmap.c
+++ b/sys/amd64/amd64/pmap.c
@@ -10236,8 +10236,22 @@ DEFINE_IFUNC(static, void, pmap_activate_sw_mode, (struct thread *, pmap_t,
 void
 pmap_activate_sw(struct thread *td)
 {
+	struct thread *oldtd;
 	pmap_t oldpmap, pmap;
 	u_int cpuid;
+	bool oldtd_sl, td_sl;
+
+	oldtd = curthread;
+	if (ia32_splitlock && oldtd != td) {
+		oldtd_sl = (atomic_load_int(&oldtd->td_md.md_td_flags) &
+		    TDF_MD_SPLITLOCK_AC) != 0;
+		td_sl = (atomic_load_int(&td->td_md.md_td_flags) &
+		    TDF_MD_SPLITLOCK_AC) != 0;
+		if (oldtd_sl && !td_sl)
+			disable_splitlock_ac();
+		else if (!oldtd_sl && td_sl)
+			enable_splitlock_ac();
+	}
 
 	oldpmap = PCPU_GET(curpmap);
 	pmap = vmspace_pmap(td->td_proc->p_vmspace);
diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c
index 58af1cb8665c..49d46b31233d 100644
--- a/sys/amd64/amd64/vm_machdep.c
+++ b/sys/amd64/amd64/vm_machdep.c
@@ -182,6 +182,8 @@ copy_thread(struct thread *td1, struct thread *td2)
 	/* Setup to release spin count in fork_exit(). */
 	td2->td_md.md_spinlock_count = 1;
 	td2->td_md.md_saved_flags = PSL_KERNEL | PSL_I;
+	td2->td_md.md_td_flags = td1->td_md.md_td_flags &
+	    TDF_MD_SPLITLOCK_AC;
 	pmap_thread_init_invl_gen(td2);
 
 	/*
diff --git a/sys/amd64/ia32/ia32_signal.c b/sys/amd64/ia32/ia32_signal.c
index 3b26244932b4..84912b292861 100644
--- a/sys/amd64/ia32/ia32_signal.c
+++ b/sys/amd64/ia32/ia32_signal.c
@@ -981,6 +981,7 @@ ia32_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack)
 	regs->tf_flags = TF_HASSEGS;
 
 	x86_clear_dbregs(pcb);
+	exec_splitlock(td);
 
 	fpstate_drop(td);
 
diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h
index a46ae8dfe794..b85e442e559a 100644
--- a/sys/amd64/include/md_var.h
+++ b/sys/amd64/include/md_var.h
@@ -105,6 +105,11 @@ void	get_fpcontext(struct thread *td, struct __mcontext *mcp,
 	    char **xfpusave, size_t *xfpusave_len);
 int	set_fpcontext(struct thread *td, struct __mcontext *mcp,
 	    char *xfpustate, size_t xfpustate_len);
+void	enable_splitlock_ac(void);
+void	enable_splitlock(struct thread *td);
+void	disable_splitlock_ac(void);
+void	disable_splitlock(struct thread *td);
+void	exec_splitlock(struct thread *td);
 
 void	wrmsr_early_safe_start(void);
 void	wrmsr_early_safe_end(void);
diff --git a/sys/amd64/include/proc.h b/sys/amd64/include/proc.h
index 9ecc5da15749..ce96fa0fedff 100644
--- a/sys/amd64/include/proc.h
+++ b/sys/amd64/include/proc.h
@@ -80,6 +80,8 @@ struct mdthread {
 	void *md_usr_fpu_save;
 };
 
+#define	TDF_MD_SPLITLOCK_AC	0x00000001	/* Disable split cache lines */
+
 struct mdproc {
 	struct proc_ldt *md_ldt;	/* (t) per-process ldt */
 	struct system_segment_descriptor md_ldt_sd;
diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c
index ecb497c61a1a..1134efd1c726 100644
--- a/sys/amd64/linux/linux_sysvec.c
+++ b/sys/amd64/linux/linux_sysvec.c
@@ -266,6 +266,7 @@ linux_exec_setregs(struct thread *td, struct image_params *imgp,
 	regs->tf_flags = TF_HASSEGS;
 
 	x86_clear_dbregs(pcb);
+	exec_splitlock(td);
 
 	/*
 	 * Drop the FP state if we hold it, so that the process gets a
diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c
index c4de547b2bf5..f42654967e8f 100644
--- a/sys/amd64/linux32/linux32_sysvec.c
+++ b/sys/amd64/linux32/linux32_sysvec.c
@@ -608,6 +608,7 @@ linux_exec_setregs(struct thread *td, struct image_params *imgp,
 	regs->tf_rbx = (register_t)imgp->ps_strings;
 
 	x86_clear_dbregs(pcb);
+	exec_splitlock(td);
 
 	fpstate_drop(td);