Skip to contentSkip to conversation

um: Add SMP support

PATCH Superseded Tiwei Bie 5 participants 43 messages Aug 10, 2025 – Oct 21, 2025

Subsystems (4)

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Tiwei Bie

author
#0 lore raw

v2 cover um: Add SMP support

From: Tiwei Bie <[email protected]>

This series adds initial symmetric multi-processing (SMP) support to UML. With this support, it is now possible for users to tell UML to start multiple virtual CPUs, each represented as a separate host thread.

In UML, kthreads and normal threads (when running in kernel mode) can be scheduled and executed simultaneously on different virtual CPUs. However, the userspace code of normal threads currently still runs within their respective single-threaded stubs.

So SMP within the kernel is supported. SMP across different processes in userspace is also supported. Currently, the limitation is that multi-threading within the same process does not yet support true SMP in userspace, because it would require converting the stub into a multi-threaded implementation, which is not covered in this series.

Extending the stub to support true SMP in userspace within the same process could be done later and requires careful design. It may be better to handle it in a follow-up series.

Here are some steps to try out SMP support in UML:

1. Build UML with CONFIG_SMP=y and, for example, CONFIG_NR_CPUS=8.

2. Launch a UML instance with, for example, 8 virtual CPUs.

 $ ./linux mem=16G ncpus=8 seccomp=on init=/bin/sh ubd0=$your_rootfs_image

v2:
- Implement IPI based on RT signal; (Johannes)
- Fix raw_smp_processor_id() for non-SMP case; (Johannes)
- Add sparse check for turnstile; (Johannes)
- Implement spinlock in $SUBARCH;
- Fix potential race on stub's siginfo;
- Rewrite AP bring-up (e.g. switch to generic idle thread);
- Misc fixes and cleanups;

v1: https://lore.kernel.org/linux-um/[email protected]/

RFC: https://lore.kernel.org/linux-um/[email protected]/

Tiwei Bie (10):
um: Stop tracking virtual CPUs via mm_cpumask()
um: Remove unused cpu_data and current_cpu_data macros
um: vdso: Implement __vdso_getcpu() via syscall
um: Turn signals_* into thread-local variables
um: Determine sleep based on need_resched()
um: Define timers on a per-CPU basis
um: Remove unused ipi_pipe field from cpuinfo_um
um: Add initial SMP support
asm-generic: percpu: Add assembly guard
um: Enable SMP support on x86

 .../core/generic-idle-thread/arch-support.txt |   2 +-
 arch/um/Kconfig                               |  46 +++-
 arch/um/include/asm/current.h                 |   5 +-
 arch/um/include/asm/hardirq.h                 |  24 +-
 arch/um/include/asm/irqflags.h                |   4 +-
 arch/um/include/asm/mmu.h                     |  10 +
 arch/um/include/asm/mmu_context.h             |  11 -
 arch/um/include/asm/pgtable.h                 |   2 +
 arch/um/include/asm/processor-generic.h       |   3 -
 arch/um/include/asm/smp.h                     |  15 +-
 arch/um/include/linux/smp-internal.h          |  17 ++
 arch/um/include/linux/time-internal.h         |   3 +
 arch/um/include/shared/kern_util.h            |   2 +
 arch/um/include/shared/longjmp.h              |   3 +-
 arch/um/include/shared/os.h                   |  23 +-
 arch/um/include/shared/skas/mm_id.h           |   5 +
 arch/um/include/shared/smp.h                  |  20 ++
 arch/um/kernel/Makefile                       |   1 +
 arch/um/kernel/irq.c                          |  27 +-
 arch/um/kernel/ksyms.c                        |   2 +-
 arch/um/kernel/mem.c                          |   2 +
 arch/um/kernel/process.c                      |  19 +-
 arch/um/kernel/skas/mmu.c                     |  32 ++-
 arch/um/kernel/skas/process.c                 |   7 +-
 arch/um/kernel/smp.c                          | 239 ++++++++++++++++++
 arch/um/kernel/time.c                         |  58 +++--
 arch/um/kernel/tlb.c                          |   5 +-
 arch/um/kernel/trap.c                         |   2 +-
 arch/um/kernel/um_arch.c                      |  28 +-
 arch/um/os-Linux/Makefile                     |   4 +-
 arch/um/os-Linux/internal.h                   |   7 +
 arch/um/os-Linux/main.c                       |   5 +-
 arch/um/os-Linux/process.c                    |  20 ++
 arch/um/os-Linux/signal.c                     |  40 ++-
 arch/um/os-Linux/skas/process.c               |  22 +-
 arch/um/os-Linux/smp.c                        | 142 +++++++++++
 arch/um/os-Linux/start_up.c                   |   4 +
 arch/um/os-Linux/time.c                       |  46 ++--
 arch/x86/um/Kconfig                           |   3 +
 arch/x86/um/asm/spinlock.h                    |   8 +
 arch/x86/um/vdso/um_vdso.c                    |  18 +-
 include/asm-generic/percpu.h                  |   3 +
 42 files changed, 827 insertions(+), 112 deletions(-)
 create mode 100644 arch/um/include/linux/smp-internal.h
 create mode 100644 arch/um/include/shared/smp.h
 create mode 100644 arch/um/kernel/smp.c
 create mode 100644 arch/um/os-Linux/smp.c
 create mode 100644 arch/x86/um/asm/spinlock.h
Signature · 1 line
2.34.1
author
#1 lore raw

PATCH v2 1/10 um: Stop tracking virtual CPUs via mm_cpumask()

From: Tiwei Bie <[email protected]>

In UML, each user address space is represented as a separate stub process on the host. Therefore, user address spaces do not require TLB management on UML virtual CPUs, and it's unnecessary to track which virtual CPUs they have executed on.

---
 arch/um/include/asm/mmu_context.h | 11 -----------
 1 file changed, 11 deletions(-)
arch/um/include/asm/mmu_context.h +0 -11
index 0bbb24868557..c727e56ba116 100644
@@ -13,20 +13,9 @@
 #include <asm/mm_hooks.h>
 #include <asm/mmu.h>
 
-#define activate_mm activate_mm
-static inline void activate_mm(struct mm_struct *old, struct mm_struct *new)
-{
-}
-
 static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 
 			     struct task_struct *tsk)
 {
-	unsigned cpu = smp_processor_id();
-
-	if (prev != next) {
-		cpumask_clear_cpu(cpu, mm_cpumask(prev));
-		cpumask_set_cpu(cpu, mm_cpumask(next));
-	}
 }
 
 #define init_new_context init_new_context
Signature · 1 line
2.34.1
author
#2 lore raw

PATCH v2 2/10 um: Remove unused cpu_data and current_cpu_data macros

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

These two macros have no users. Remove them.

---
 arch/um/include/asm/processor-generic.h | 2 --
 1 file changed, 2 deletions(-)
arch/um/include/asm/processor-generic.h +0 -2
index 8a789c17acd8..236fdfd7cdbe 100644
@@ -81,8 +81,6 @@ struct cpuinfo_um {
 
 extern struct cpuinfo_um boot_cpu_data;
 
-#define cpu_data(cpu)    boot_cpu_data
-#define current_cpu_data boot_cpu_data
 #define cache_line_size()	(boot_cpu_data.cache_alignment)
 
 #define KSTK_REG(tsk, reg) get_thread_reg(reg, &tsk->thread.switch_buf)
Signature · 1 line
2.34.1
author
#3 lore raw

PATCH v2 3/10 um: vdso: Implement __vdso_getcpu() via syscall

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

We are going to support SMP in UML, so we can not hard code the CPU and NUMA node in __vdso_getcpu() anymore.

---
 arch/x86/um/vdso/um_vdso.c | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)
arch/x86/um/vdso/um_vdso.c +8 -10
index cbae2584124f..ee40ac446c1c 100644
@@ -17,7 +17,7 @@
 int __vdso_clock_gettime(clockid_t clock, struct __kernel_old_timespec *ts);
 int __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz);
 __kernel_old_time_t __vdso_time(__kernel_old_time_t *t);
-long __vdso_getcpu(unsigned int *cpu, unsigned int *node, struct getcpu_cache *unused);
+long __vdso_getcpu(unsigned int *cpu, unsigned int *node, struct getcpu_cache *tcache);
 
 int __vdso_clock_gettime(clockid_t clock, struct __kernel_old_timespec *ts)
 {
@@ -60,18 +60,16 @@ __kernel_old_time_t __vdso_time(__kernel_old_time_t *t)
 __kernel_old_time_t time(__kernel_old_time_t *t) __attribute__((weak, alias("__vdso_time")));
 
 long
-__vdso_getcpu(unsigned int *cpu, unsigned int *node, struct getcpu_cache *unused)
+__vdso_getcpu(unsigned int *cpu, unsigned int *node, struct getcpu_cache *tcache)
 {
-	/*
-	 * UML does not support SMP, we can cheat here. :)
-	 */
+	long ret;
 
-	if (cpu)
-		*cpu = 0;
-	if (node)
-		*node = 0;
+	asm volatile("syscall"
+		: "=a" (ret)
+		: "0" (__NR_getcpu), "D" (cpu), "S" (node), "d" (tcache)
+		: "rcx", "r11", "memory");
 
-	return 0;
+	return ret;
 }
 
 long getcpu(unsigned int *cpu, unsigned int *node, struct getcpu_cache *tcache)
Signature · 1 line
2.34.1
maintainer
#4 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 4 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

We are going to support SMP in UML, so we can not hard code the CPU and NUMA node in __vdso_getcpu() anymore.

Correct. But does that mean we actually have to implement it via syscall in the VDSO? That seems a bit odd? ARM doesn't seem to have getcpu in the VDSO at all, for example, so could we do the same and just remove it?

johannes

author
#5 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 10 lines · Johannes Berg
Quoted 4 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

We are going to support SMP in UML, so we can not hard code the CPU and NUMA node in __vdso_getcpu() anymore.

Correct. But does that mean we actually have to implement it via syscall in the VDSO? That seems a bit odd? ARM doesn't seem to have getcpu in the VDSO at all, for example, so could we do the same and just remove it?

Good idea. I checked the implementations in glibc and musl, and they automatically fall back to the syscall when __vdso_getcpu is not available:

https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/sysdep-vdso.h;h=5a33871872da9ccef36293c3ca5eba6503f956e6;hb=HEAD#l36
https://git.musl-libc.org/cgit/musl/tree/src/sched/sched_getcpu.c?h=v1.2.5#n32

I will just remove it in the next version.

Regards,
Tiwei

#6 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

In reply to Johannes Berg
Quoted 10 lines · Johannes Berg
Quoted 4 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

We are going to support SMP in UML, so we can not hard code the CPU and NUMA node in __vdso_getcpu() anymore.

Correct. But does that mean we actually have to implement it via syscall in the VDSO? That seems a bit odd? ARM doesn't seem to have getcpu in the VDSO at all, for example, so could we do the same and just remove it?

It is my understanding that the UM VDSO exists to cope with old versions of glibc which would fall back to the old vsyscall mechanism if no VDSO was present. That could fall through to the host kernels vsyscalls. See commit f1c2bb8b9964 ("um: implement a x86_64 vDSO").

If this is not necessary anymore, the whole VDSO on UM can probably go away.

Thomas

author
#7 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 12 lines · Thomas Weißschuh
Quoted 10 lines · Johannes Berg
Quoted 4 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

We are going to support SMP in UML, so we can not hard code the CPU and NUMA node in __vdso_getcpu() anymore.

Correct. But does that mean we actually have to implement it via syscall in the VDSO? That seems a bit odd? ARM doesn't seem to have getcpu in the VDSO at all, for example, so could we do the same and just remove it?

Quoted 7 lines

It is my understanding that the UM VDSO exists to cope with old versions of glibc which would fall back to the old vsyscall mechanism if no VDSO was present. That could fall through to the host kernels vsyscalls. See commit f1c2bb8b9964 ("um: implement a x86_64 vDSO").

If this is not necessary anymore, the whole VDSO on UM can probably go away.

The vsyscall usage was removed from glibc a decade ago:

https://sourceware.org/git/?p=glibc.git;a=commit;h=7cbeabac0fb28e24c99aaa5085e613ea543a2346

"This patch removes the vsyscall usage for x86_64 port. As indicated
by kernel code comments [1], vsyscalls are a legacy ABI and its concept is problematic:

- It interferes with ASLR.
- It's awkward to write code that lives in kernel addresses but is callable by userspace at fixed addresses.
- The whole concept is impossible for 32-bit compat userspace.
- UML cannot easily virtualize a vsyscall.

......"

The original issue could now be considered resolved. So in v3, we no longer turn __vdso_getcpu into a syscall wrapper; we simply removed it. Perhaps we could remove the whole VDSO before we implement the "real" VDSO. However, its implementation is clean, so keeping it wouldn't hurt and it could serve as a useful starting point for the "real" VDSO.

Regards,
Tiwei

#8 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 29 lines · Tiwei Bie
Quoted 12 lines · Thomas Weißschuh
Quoted 10 lines · Johannes Berg
Quoted 4 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

We are going to support SMP in UML, so we can not hard code the CPU and NUMA node in __vdso_getcpu() anymore.

Correct. But does that mean we actually have to implement it via syscall in the VDSO? That seems a bit odd? ARM doesn't seem to have getcpu in the VDSO at all, for example, so could we do the same and just remove it?

Quoted 7 lines

It is my understanding that the UM VDSO exists to cope with old versions of glibc which would fall back to the old vsyscall mechanism if no VDSO was present. That could fall through to the host kernels vsyscalls. See commit f1c2bb8b9964 ("um: implement a x86_64 vDSO").

If this is not necessary anymore, the whole VDSO on UM can probably go away.

The vsyscall usage was removed from glibc a decade ago:

https://sourceware.org/git/?p=glibc.git;a=commit;h=7cbeabac0fb28e24c99aaa5085e613ea543a2346

"This patch removes the vsyscall usage for x86_64 port. As indicated
by kernel code comments [1], vsyscalls are a legacy ABI and its concept is problematic:

Quoted 7 lines

- It interferes with ASLR.
- It's awkward to write code that lives in kernel addresses but is callable by userspace at fixed addresses.
- The whole concept is impossible for 32-bit compat userspace.
- UML cannot easily virtualize a vsyscall.

......"

Also modern kernels dont even implement the vsyscall page anymore. At most it is implemented as a stub which will trigger the real syscall which then gets handled properly.

Quoted 5 lines

The original issue could now be considered resolved. So in v3, we no longer turn __vdso_getcpu into a syscall wrapper; we simply removed it. Perhaps we could remove the whole VDSO before we implement the "real" VDSO. However, its implementation is clean, so keeping it wouldn't hurt and it could serve as a useful starting point for the "real" VDSO.

A "real" vDSO would require quite some more infrastructure. And it is not even clear if such a vDSO will make a difference on UML. In my opinion if __vdso_getcpu() gets removed, the whole vDSO should go with it. The code can still be easily restored from git.

Also the functionality to map the host vDSO and vsyscall page into UML userspace looks very weird and error-prone. Maybe it can also go away.

Thomas

maintainer
#9 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 8 lines · Thomas Weißschuh
Quoted 5 lines

The original issue could now be considered resolved. So in v3, we no longer turn __vdso_getcpu into a syscall wrapper; we simply removed it. Perhaps we could remove the whole VDSO before we implement the "real" VDSO. However, its implementation is clean, so keeping it wouldn't hurt and it could serve as a useful starting point for the "real" VDSO.

A "real" vDSO would require quite some more infrastructure.

What's not "real" about the vDSO now? Yes it just implement syscalls after the getcpu removal, but ... it's still a vDSO? I _have_ played with getting data into it for the time-travel case, at least.

Quoted 1 line

And it is not even clear if such a vDSO will make a difference on UML.

Syscall overhead is _huge_ in UML, if it does anything but syscalls it will _certainly_ make a difference.

Quoted 3 lines

In my
opinion if __vdso_getcpu() gets removed, the whole vDSO should go with it. The code can still be easily restored from git.

I mean ... on the one hand, sure, it doesn't really do much after this, but OTOH it lets userspace actually use that path? So might be useful.

Quoted 2 lines

Also the functionality to map the host vDSO and vsyscall page into UML userspace looks very weird and error-prone. Maybe it can also go away.

Surely host vDSO etc. is never mapped into UML userspace and never is, not sure what you're thinking of, but clearly that's wrong as written.

johannes

#10 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 7 lines · Johannes Berg
Quoted 6 lines · Thomas Weißschuh
Quoted 5 lines

The original issue could now be considered resolved. So in v3, we no longer turn __vdso_getcpu into a syscall wrapper; we simply removed it. Perhaps we could remove the whole VDSO before we implement the "real" VDSO. However, its implementation is clean, so keeping it wouldn't hurt and it could serve as a useful starting point for the "real" VDSO.

Quoted 6 lines
Quoted 2 lines

A "real" vDSO would require quite some more infrastructure.

What's not "real" about the vDSO now? Yes it just implement syscalls after the getcpu removal, but ... it's still a vDSO? I _have_ played with getting data into it for the time-travel case, at least.

Right now it does not provide any advantage over a regular syscall. Essentially it is just overhead. That said, if you do want to make a real vDSO out of it, I'd be happy to help in that.
(I did most of the recent work on the generic vDSO infrastructure)

Quoted 4 lines
Quoted 1 line

And it is not even clear if such a vDSO will make a difference on UML.

Syscall overhead is _huge_ in UML, if it does anything but syscalls it will _certainly_ make a difference.

Ack.

Quoted 6 lines
Quoted 3 lines

In my
opinion if __vdso_getcpu() gets removed, the whole vDSO should go with it. The code can still be easily restored from git.

I mean ... on the one hand, sure, it doesn't really do much after this, but OTOH it lets userspace actually use that path? So might be useful.

What advantage does userspace have from it?

Quoted 5 lines
Quoted 2 lines

Also the functionality to map the host vDSO and vsyscall page into UML userspace looks very weird and error-prone. Maybe it can also go away.

Surely host vDSO etc. is never mapped into UML userspace and never is, not sure what you're thinking of, but clearly that's wrong as written.

This is how I understand the 32bit implementation using ARCH_REUSE_HOST_VSYSCALL_AREA and NEW_AUX_ENT(AT_SYSINFO_EHDR, vsyscall_ehdr) where vsyscall_ehdr comes from the hosts getauxval(AT_SYSINFO_EHDR). But I didn't actually test this. I'll look at it again, but currently I'm travelling.

Thomas

maintainer
#11 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 3 lines · Thomas Weißschuh

Right now it does not provide any advantage over a regular syscall. Essentially it is just overhead. That said, if you do want to make a real vDSO out of it, I'd be happy to help in that.

I don't know if I'd say "just overhead" - depends on which path is more optimised in a typical libc implementation? I'd basically think it's identical, no? You either link to the vDSO, or a __weak same function in the libc?

Quoted 4 lines
Quoted 2 lines

I mean ... on the one hand, sure, it doesn't really do much after this, but OTOH it lets userspace actually use that path? So might be useful.

What advantage does userspace have from it?

Right now, none? But it's easier to play with if you have the infrastructure, and I'm not convinced there's a _disadvantage_?

Quoted 9 lines
Quoted 5 lines
Quoted 2 lines

Also the functionality to map the host vDSO and vsyscall page into UML userspace looks very weird and error-prone. Maybe it can also go away.

Surely host vDSO etc. is never mapped into UML userspace and never is, not sure what you're thinking of, but clearly that's wrong as written.

This is how I understand the 32bit implementation using ARCH_REUSE_HOST_VSYSCALL_AREA and NEW_AUX_ENT(AT_SYSINFO_EHDR, vsyscall_ehdr) where vsyscall_ehdr comes from the hosts getauxval(AT_SYSINFO_EHDR).

Huh, hm, yeah I forgot about that ... 32-bit. Yeah, agree we should just kill that. I'm not even sure it works with the host kernel trapping there? Oh well.

johannes

#12 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 9 lines · Johannes Berg
Quoted 3 lines · Thomas Weißschuh

Right now it does not provide any advantage over a regular syscall. Essentially it is just overhead. That said, if you do want to make a real vDSO out of it, I'd be happy to help in that.

I don't know if I'd say "just overhead" - depends on which path is more optimised in a typical libc implementation? I'd basically think it's identical, no? You either link to the vDSO, or a __weak same function in the libc?

The code also needs to be built and maintained. AFAIK __weak is only for the compile-time linker. The vDSO call will be an indirect call.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

I mean ... on the one hand, sure, it doesn't really do much after this, but OTOH it lets userspace actually use that path? So might be useful.

What advantage does userspace have from it?

Right now, none? But it's easier to play with if you have the infrastructure, and I'm not convinced there's a _disadvantage_?

So far that hasn't happened. The disadvantages are the ones from above, nothing critical. But of course it is your subsystem and your call to make.

Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
Quoted 2 lines

Also the functionality to map the host vDSO and vsyscall page into UML userspace looks very weird and error-prone. Maybe it can also go away.

Surely host vDSO etc. is never mapped into UML userspace and never is, not sure what you're thinking of, but clearly that's wrong as written.

Quoted 7 lines
Quoted 3 lines

This is how I understand the 32bit implementation using ARCH_REUSE_HOST_VSYSCALL_AREA and NEW_AUX_ENT(AT_SYSINFO_EHDR, vsyscall_ehdr) where vsyscall_ehdr comes from the hosts getauxval(AT_SYSINFO_EHDR).

Huh, hm, yeah I forgot about that ... 32-bit. Yeah, agree we should just kill that. I'm not even sure it works with the host kernel trapping there? Oh well.

Ack, do you want me to send a patch? This was my real gripe with the UM vDSO. I want to enable time namespaces for all architectures but these need to be handled in the vDSO properly. For the 64-bit stub vDSO it's not a problem as the syscalls will work correctly.
But the interaction with the weird 32-bit logic on the other hand...

Thomas

maintainer
#13 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 6 lines · Thomas Weißschuh
Quoted 4 lines

I don't know if I'd say "just overhead" - depends on which path is more optimised in a typical libc implementation? I'd basically think it's identical, no? You either link to the vDSO, or a __weak same function in the libc?

The code also needs to be built and maintained.

Yeah, fair, though the vDSO doesn't really do anything, and if we do want to use it then having a working version beats having to re-do it from scratch, which

Quoted 2 lines

AFAIK __weak is only for
the compile-time linker. The vDSO call will be an indirect call.

yeah, I looked at the links you'd sent earlier only later ...

Quoted 10 lines
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

I mean ... on the one hand, sure, it doesn't really do much after this, but OTOH it lets userspace actually use that path? So might be useful.

What advantage does userspace have from it?

Right now, none? But it's easier to play with if you have the infrastructure, and I'm not convinced there's a _disadvantage_?

So far that hasn't happened. The disadvantages are the ones from above, nothing critical. But of course it is your subsystem and your call to make.

Yeah, kind of agree, though I'd like to actually use it - especially in time-travel mode - but haven't really gotten time to add it. Having it maintained in-tree is a bit nicer in case of global updates, but yeah, ultimately it's not really all that important either way.

I guess we could get getrandom() pretty easily by taking the x86 one.

I actually have half a patch somewhere that rejiggers the UM vDSO to be more like normal architectures, using lib/vdso/gettimeofday.c and making the build more regular etc. Maybe I should dig that up and try to make it work entirely - it was part of a previous attempt of adding the time- travel thing I mentioned.

Quoted 9 lines
Quoted 3 lines

Huh, hm, yeah I forgot about that ... 32-bit. Yeah, agree we should just kill that. I'm not even sure it works with the host kernel trapping there? Oh well.

Ack, do you want me to send a patch? This was my real gripe with the UM vDSO. I want to enable time namespaces for all architectures but these need to be handled in the vDSO properly. For the 64-bit stub vDSO it's not a problem as the syscalls will work correctly.
But the interaction with the weird 32-bit logic on the other hand...

I guess? But I'm confused by what you say about it being related to time namespaces, the vsyscall stuff doesn't really _do_ anything, assuming it works at all? It's not like the host actually could be doing anything other than syscalls there, which are intercepted? If it were doing anything else, it wouldn't work in UML in the first place?

johannes

#14 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

Quoted 1 line · Johannes Berg

On Mon, 2025-09-22 at 18:04 +0200, Thomas Weißschuh wrote:

(...)

Quoted 11 lines
Quoted 10 lines
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

I mean ... on the one hand, sure, it doesn't really do much after this, but OTOH it lets userspace actually use that path? So might be useful.

What advantage does userspace have from it?

Right now, none? But it's easier to play with if you have the infrastructure, and I'm not convinced there's a _disadvantage_?

So far that hasn't happened. The disadvantages are the ones from above, nothing critical. But of course it is your subsystem and your call to make.

Quoted 6 lines

Yeah, kind of agree, though I'd like to actually use it - especially in time-travel mode - but haven't really gotten time to add it. Having it maintained in-tree is a bit nicer in case of global updates, but yeah, ultimately it's not really all that important either way.

I guess we could get getrandom() pretty easily by taking the x86 one.

Yeah, the only architecture-specific part there is the assembly chacha implementation. And that will be the same one as used by regular x86.

Quoted 5 lines

I actually have half a patch somewhere that rejiggers the UM vDSO to be more like normal architectures, using lib/vdso/gettimeofday.c and making the build more regular etc. Maybe I should dig that up and try to make it work entirely - it was part of a previous attempt of adding the time- travel thing I mentioned.

Sounds good. And let me know if you want me to look at it. Using the generic vDSO library and datastore is mandatory nowadays for "real" vDSOs.

Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

Huh, hm, yeah I forgot about that ... 32-bit. Yeah, agree we should just kill that. I'm not even sure it works with the host kernel trapping there? Oh well.

Ack, do you want me to send a patch? This was my real gripe with the UM vDSO. I want to enable time namespaces for all architectures but these need to be handled in the vDSO properly. For the 64-bit stub vDSO it's not a problem as the syscalls will work correctly.
But the interaction with the weird 32-bit logic on the other hand...

Quoted 5 lines

I guess? But I'm confused by what you say about it being related to time namespaces, the vsyscall stuff doesn't really _do_ anything, assuming it works at all? It's not like the host actually could be doing anything other than syscalls there, which are intercepted? If it were doing anything else, it wouldn't work in UML in the first place?

In emulation mode the trapping kernel will not actually trigger a syscall but calculate the time in kernel space and write the results to the respective registers. If I understand correctly the trap is handled by the host kernel, so that would bypass UML completely.
My wording was a bit wonky. I stumbled upon this while looking for potential time namespace compatibility issues. And with time namespaces the chance for a clock mismatch between UML and the host are higher.

Thomas

maintainer
#15 lore raw

Re: [PATCH v2 03/10] um: vdso: Implement __vdso_getcpu() via syscall

In reply to Johannes Berg
Quoted 6 lines · Johannes Berg

I actually have half a patch somewhere that rejiggers the UM vDSO to be more like normal architectures, using lib/vdso/gettimeofday.c and making the build more regular etc. Maybe I should dig that up and try to make it work entirely - it was part of a previous attempt of adding the time- travel thing I mentioned.

So that patch is almost 5 years old and with all the VDSO rework since then, there's no way I can make that work again right now ... I guess it'd be better to start from scratch and make a real VDSO for UML.

I'll take a look at your cleanups though.

joahnnes

author
#16 lore raw

PATCH v2 4/10 um: Turn signals_* into thread-local variables

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

Turn signals_enabled, signals_pending and signals_active into thread-local variables. This enables us to control and track signals independently on each CPU thread. This is a preparation for adding SMP support.

---
 arch/um/include/asm/irqflags.h   |  4 ++--
 arch/um/include/shared/longjmp.h |  3 +--
 arch/um/include/shared/os.h      |  1 +
 arch/um/kernel/ksyms.c           |  2 +-
 arch/um/os-Linux/signal.c        | 11 ++++++++---
 5 files changed, 13 insertions(+), 8 deletions(-)
arch/um/include/asm/irqflags.h +2 -2
index 1e69ef5bc35e..31e49e0894c5 100644
@@ -2,7 +2,7 @@
 #ifndef __UM_IRQFLAGS_H
 #define __UM_IRQFLAGS_H
 
-extern int signals_enabled;
+int um_get_signals(void);
 int um_set_signals(int enable);
 void block_signals(void);
 void unblock_signals(void);
@@ -10,7 +10,7 @@ void unblock_signals(void);
 #define arch_local_save_flags arch_local_save_flags
 static inline unsigned long arch_local_save_flags(void)
 {
-	return signals_enabled;
+	return um_get_signals();
 }
 
 #define arch_local_irq_restore arch_local_irq_restore
arch/um/include/shared/longjmp.h +1 -2
index 8863319039f3..c53e43d980c8 100644
@@ -5,7 +5,6 @@
 #include <sysdep/archsetjmp.h>
 #include <os.h>
 
-extern int signals_enabled;
 extern int setjmp(jmp_buf);
 extern void longjmp(jmp_buf, int);
 
@@ -15,7 +14,7 @@ extern void longjmp(jmp_buf, int);
 
 #define UML_SETJMP(buf) ({				\
 	int n, enable;					\
-	enable = *(volatile int *)&signals_enabled;	\
+	enable = um_get_signals();			\
 	n = setjmp(*buf);				\
 	if(n != 0)					\
 		um_set_signals_trace(enable);		\
arch/um/include/shared/os.h +1 -0
index b35cc8ce333b..324d4eed3385 100644
@@ -243,6 +243,7 @@ extern void send_sigio_to_self(void);
 extern int change_sig(int signal, int on);
 extern void block_signals(void);
 extern void unblock_signals(void);
+extern int um_get_signals(void);
 extern int um_set_signals(int enable);
 extern int um_set_signals_trace(int enable);
 extern void deliver_alarm(void);
arch/um/kernel/ksyms.c +1 -1
index f2fb77da08cf..96314c31e61c 100644
@@ -6,8 +6,8 @@
 #include <linux/module.h>
 #include <os.h>
 
+EXPORT_SYMBOL(um_get_signals);
 EXPORT_SYMBOL(um_set_signals);
-EXPORT_SYMBOL(signals_enabled);
 
 EXPORT_SYMBOL(os_stat_fd);
 EXPORT_SYMBOL(os_stat_file);
arch/um/os-Linux/signal.c +8 -3
index 11f07f498270..58da8c6ece98 100644
@@ -68,12 +68,12 @@ static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc)
 #define SIGCHLD_BIT 2
 #define SIGCHLD_MASK (1 << SIGCHLD_BIT)
 
-int signals_enabled;
+static __thread int signals_enabled;
 #if IS_ENABLED(CONFIG_UML_TIME_TRAVEL_SUPPORT)
 static int signals_blocked, signals_blocked_pending;
 #endif
-static unsigned int signals_pending;
-static unsigned int signals_active = 0;
+static __thread unsigned int signals_pending;
+static __thread unsigned int signals_active;
 
 static void sig_handler(int sig, struct siginfo *si, mcontext_t *mc)
 {
@@ -342,6 +342,11 @@ void unblock_signals(void)
 	}
 }
 
+int um_get_signals(void)
+{
+	return signals_enabled;
+}
+
 int um_set_signals(int enable)
 {
 	int ret;
Signature · 1 line
2.34.1
maintainer
#17 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Turn signals_enabled, signals_pending and signals_active into thread-local variables. This enables us to control and track signals independently on each CPU thread. This is a preparation for adding SMP support.

[...]

Quoted 1 line

+static __thread int signals_enabled;

How much glibc infrastructure does __thread rely on? More specifically:
Some time ago we had a discussion about building UML as a nolibc binary, what would that mean for the __thread usage here?

johannes

author
#18 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

Quoted 8 lines · Johannes Berg
Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Turn signals_enabled, signals_pending and signals_active into thread-local variables. This enables us to control and track signals independently on each CPU thread. This is a preparation for adding SMP support.

Quoted 7 lines

[...]

Quoted 1 line

+static __thread int signals_enabled;

How much glibc infrastructure does __thread rely on? More specifically:
Some time ago we had a discussion about building UML as a nolibc binary, what would that mean for the __thread usage here?

We would need to parse TLS data (PT_TLS) from the ELF file ourselves and properly set up TLS when creating threads using clone().

Regards,
Tiwei

#19 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

Hi,

Quoted 13 lines · Tiwei Bie
Quoted 12 lines · Johannes Berg
Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Turn signals_enabled, signals_pending and signals_active into thread-local variables. This enables us to control and track signals independently on each CPU thread. This is a preparation for adding SMP support.

[...]

Quoted 1 line

+static __thread int signals_enabled;

Quoted 8 lines
Quoted 5 lines

How much glibc infrastructure does __thread rely on? More specifically:
Some time ago we had a discussion about building UML as a nolibc binary,
what would that mean for the __thread usage here?

We would need to parse TLS data (PT_TLS) from the ELF file ourselves and properly set up TLS when creating threads using clone().

I guess right now we cannot use PER_CPU variables in these files. However, my expectation that this is possible when using nolibc, and then it should be simple enough to replace the __thread.

Benjamin

#20 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

Hi,

Quoted 22 lines · Benjamin Berg

Hi,

Quoted 19 lines · Tiwei Bie
Quoted 17 lines · Johannes Berg
Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Turn signals_enabled, signals_pending and signals_active into thread-local variables. This enables us to control and track signals independently on each CPU thread. This is a preparation for adding SMP support.

[...]

Quoted 1 line

+static __thread int signals_enabled;

How much glibc infrastructure does __thread rely on? More specifically:
Some time ago we had a discussion about building UML as a nolibc binary,
what would that mean for the __thread usage here?

Quoted 7 lines
Quoted 3 lines

We would need to parse TLS data (PT_TLS) from the ELF file ourselves
and properly set up TLS when creating threads using clone().

I guess right now we cannot use PER_CPU variables in these files. However, my expectation that this is possible when using nolibc, and then it should be simple enough to replace the __thread.

That said, I do believe that the allocations from the libc itself are problematic. A lot of the mappings from UML are there already (i.e. the physical memory is mapped). However, I believe the vmalloc area for example is not guarded.

So when pthread allocates the thread specific memory (stack, TLS, ...), we really do not know where this will be mapped into the address space. If it happens to be in an area that UML wants to use later, then UML could map e.g. vmalloc data over it.

Now, it could be that (currently) the addresses picked by pthread (or the host kernel) do not actually clash with anything. However, I do not think there is any guarantee for that.

Benjamin

author
#21 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

Hi,

Quoted 21 lines · Benjamin Berg
Quoted 20 lines · Benjamin Berg
Quoted 19 lines · Tiwei Bie
Quoted 17 lines · Johannes Berg
Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Turn signals_enabled, signals_pending and signals_active into thread-local variables. This enables us to control and track signals independently on each CPU thread. This is a preparation for adding SMP support.

[...]

Quoted 1 line

+static __thread int signals_enabled;

How much glibc infrastructure does __thread rely on? More specifically:
Some time ago we had a discussion about building UML as a nolibc binary,
what would that mean for the __thread usage here?

Quoted 7 lines
Quoted 7 lines
Quoted 3 lines

We would need to parse TLS data (PT_TLS) from the ELF file ourselves
and properly set up TLS when creating threads using clone().

I guess right now we cannot use PER_CPU variables in these files. However, my expectation that this is possible when using nolibc, and then it should be simple enough to replace the __thread.

Good idea!

Quoted 6 lines

That said, I do believe that the allocations from the libc itself are problematic. A lot of the mappings from UML are there already (i.e. the physical memory is mapped). However, I believe the vmalloc area for example is not guarded.

Quoted 8 lines

So when pthread allocates the thread specific memory (stack, TLS, ...), we really do not know where this will be mapped into the address space. If it happens to be in an area that UML wants to use later, then UML could map e.g. vmalloc data over it.

Now, it could be that (currently) the addresses picked by pthread (or the host kernel) do not actually clash with anything. However, I do not think there is any guarantee for that.

Indeed. The mmap from libc (pthread, shared libs, ...) can potentially conflict with UML. The reason it has been working on x86_64 so far might be that we did this in linux_main():

	task_size = task_size & PGDIR_MASK;

The current layout is:

shared libs and pthreads are located at 7ffxxxxxxxxx
TASK_SIZE = 7f8000000000
VMALLOC_END = 7f7fffffe000 (which is TASK_SIZE-2*PAGE_SIZE)

However, on i386, the risk of conflicts looks much higher:

TASK_SIZE = ffc00000
VMALLOC_END = ffbfe000

......

f7c00000-f7c20000 r--p 00000000 08:01 9114                               /usr/lib32/libc.so.6
f7c20000-f7d9e000 r-xp 00020000 08:01 9114                               /usr/lib32/libc.so.6
f7d9e000-f7e23000 r--p 0019e000 08:01 9114                               /usr/lib32/libc.so.6
f7e23000-f7e24000 ---p 00223000 08:01 9114                               /usr/lib32/libc.so.6
f7e24000-f7e26000 r--p 00223000 08:01 9114                               /usr/lib32/libc.so.6
f7e26000-f7e27000 rw-p 00225000 08:01 9114                               /usr/lib32/libc.so.6
f7e27000-f7e31000 rw-p 00000000 00:00 0 
f7fbe000-f7fc0000 rw-p 00000000 00:00 0 
f7fc0000-f7fc4000 r--p 00000000 00:00 0                                  [vvar]
f7fc4000-f7fc6000 r-xp 00000000 00:00 0                                  [vdso]
f7fc6000-f7fc7000 r--p 00000000 08:01 9107                               /usr/lib32/ld-linux.so.2
f7fc7000-f7fec000 r-xp 00001000 08:01 9107                               /usr/lib32/ld-linux.so.2
f7fec000-f7ffb000 r--p 00026000 08:01 9107                               /usr/lib32/ld-linux.so.2
f7ffb000-f7ffd000 r--p 00034000 08:01 9107                               /usr/lib32/ld-linux.so.2
f7ffd000-f7ffe000 rw-p 00036000 08:01 9107                               /usr/lib32/ld-linux.so.2
fffdd000-ffffe000 rw-p 00000000 00:00 0                                  [stack]

Ideally, we could completely eliminate the dependency on libc. Before that, perhaps we could reserve a region of address space for UML with mmap(PROT_NONE).

Regards,
Tiwei

#22 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

Quoted 21 lines · Tiwei Bie
Quoted 6 lines · Benjamin Berg

[SNIP]
That said, I do believe that the allocations from the libc itself are problematic. A lot of the mappings from UML are there already (i.e. the physical memory is mapped). However, I believe the vmalloc area for example is not guarded.

Quoted 8 lines

So when pthread allocates the thread specific memory (stack, TLS, ...), we really do not know where this will be mapped into the address space. If it happens to be in an area that UML wants to use later, then UML could map e.g. vmalloc data over it.

Now, it could be that (currently) the addresses picked by pthread (or the host kernel) do not actually clash with anything. However, I do not think there is any guarantee for that.

Indeed. The mmap from libc (pthread, shared libs, ...) can potentially conflict with UML. The reason it has been working on x86_64 so far might be that we did this in linux_main():

Quoted 7 lines
	task_size = task_size & PGDIR_MASK;

The current layout is:

shared libs and pthreads are located at 7ffxxxxxxxxx
TASK_SIZE                             = 7f8000000000
VMALLOC_END                           = 7f7fffffe000 (which is TASK_SIZE-2*PAGE_SIZE)

Uh, right, yes. Because of the masking we are capping ourselves to 0x7f8000000000. And then all of the interesting bits (vdso, ...) happen to be mapped above that address and are effectively protected. And, there is also plenty of space for other allocations technically.

That is kind of horrible, as it only works because all of this happens to be mapped into the top of the address space. But, maybe something to just wilfully ignore and only fix as part of a nolibc port?

Quoted 23 lines

However, on i386, the risk of conflicts looks much higher:

TASK_SIZE   = ffc00000
VMALLOC_END = ffbfe000

......
f7c00000-f7c20000 r--p 00000000 08:01 9114                               /usr/lib32/libc.so.6 f7c20000-f7d9e000 r-xp 00020000 08:01 9114                               /usr/lib32/libc.so.6 f7d9e000-f7e23000 r--p 0019e000 08:01 9114                               /usr/lib32/libc.so.6 f7e23000-f7e24000 ---p 00223000 08:01 9114                               /usr/lib32/libc.so.6 f7e24000-f7e26000 r--p 00223000 08:01 9114                               /usr/lib32/libc.so.6 f7e26000-f7e27000 rw-p 00225000 08:01 9114                               /usr/lib32/libc.so.6 f7e27000-f7e31000 rw-p 00000000 00:00 0
f7fbe000-f7fc0000 rw-p 00000000 00:00 0
f7fc0000-f7fc4000 r--p 00000000 00:00 0                                  [vvar] f7fc4000-f7fc6000 r-xp 00000000 00:00 0                                  [vdso] f7fc6000-f7fc7000 r--p 00000000 08:01 9107                               /usr/lib32/ld-linux.so.2 f7fc7000-f7fec000 r-xp 00001000 08:01 9107                               /usr/lib32/ld-linux.so.2 f7fec000-f7ffb000 r--p 00026000 08:01 9107                               /usr/lib32/ld-linux.so.2 f7ffb000-f7ffd000 r--p 00034000 08:01 9107                               /usr/lib32/ld-linux.so.2 f7ffd000-f7ffe000 rw-p 00036000 08:01 9107                               /usr/lib32/ld-linux.so.2 fffdd000-ffffe000 rw-p 00000000 00:00 0                                  [stack]

Quoted 2 lines

Ideally, we could completely eliminate the dependency on libc. Before that, perhaps we could reserve a region of address space for UML with mmap(PROT_NONE).

Yeah, that does seem reasonable. That should at least protect us from libc using our vmalloc area. And it is easy to do, as it just needs an initial mmap and changing the kern_unmap implementation in tlb.c

Benjamin

author
#23 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

Quoted 30 lines · Benjamin Berg
Quoted 21 lines · Tiwei Bie
Quoted 6 lines · Benjamin Berg

[SNIP]
That said, I do believe that the allocations from the libc itself are problematic. A lot of the mappings from UML are there already (i.e. the physical memory is mapped). However, I believe the vmalloc area for example is not guarded.

Quoted 8 lines

So when pthread allocates the thread specific memory (stack, TLS, ...), we really do not know where this will be mapped into the address space. If it happens to be in an area that UML wants to use later, then UML could map e.g. vmalloc data over it.

Now, it could be that (currently) the addresses picked by pthread (or the host kernel) do not actually clash with anything. However, I do not think there is any guarantee for that.

Indeed. The mmap from libc (pthread, shared libs, ...) can potentially conflict with UML. The reason it has been working on x86_64 so far might be that we did this in linux_main():

Quoted 7 lines
	task_size = task_size & PGDIR_MASK;

The current layout is:

shared libs and pthreads are located at 7ffxxxxxxxxx
TASK_SIZE = 7f8000000000
VMALLOC_END = 7f7fffffe000 (which is TASK_SIZE-2*PAGE_SIZE)

Quoted 7 lines

Uh, right, yes. Because of the masking we are capping ourselves to 0x7f8000000000. And then all of the interesting bits (vdso, ...) happen to be mapped above that address and are effectively protected. And, there is also plenty of space for other allocations technically.

That is kind of horrible, as it only works because all of this happens to be mapped into the top of the address space.

+1.

Quoted 2 lines

But, maybe something to
just wilfully ignore and only fix as part of a nolibc port?

Sure. Thanks!

Regards,
Tiwei

maintainer
#24 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

In reply to Benjamin Berg
Quoted 22 lines · Benjamin Berg

Hi,

Quoted 19 lines · Tiwei Bie
Quoted 17 lines · Johannes Berg
Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Turn signals_enabled, signals_pending and signals_active into thread-local variables. This enables us to control and track signals independently on each CPU thread. This is a preparation for adding SMP support.

[...]

Quoted 1 line

+static __thread int signals_enabled;

How much glibc infrastructure does __thread rely on? More specifically:
Some time ago we had a discussion about building UML as a nolibc binary,
what would that mean for the __thread usage here?

Quoted 4 lines
Quoted 2 lines

We would need to parse TLS data (PT_TLS) from the ELF file ourselves and properly set up TLS when creating threads using clone().

I guess right now we cannot use PER_CPU variables in these files.

Maybe? The only thing would be to know which "CPU" we're executing on? getpid() is async signal safe (i.e. you can call it), but there could be better ways of doing this such as setting different signal handler functions in different CPUs.

johannes

#25 lore raw

Re: [PATCH v2 04/10] um: Turn signals_* into thread-local variables

Quoted 26 lines · Johannes Berg
Quoted 25 lines · Benjamin Berg

Hi,

Quoted 21 lines · Tiwei Bie
Quoted 10 lines · Johannes Berg
Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Turn signals_enabled, signals_pending and signals_active into thread-local variables. This enables us to control and track signals independently on each CPU thread. This is a preparation for adding SMP support.

[...]

Quoted 7 lines
Quoted 1 line

+static __thread int signals_enabled;

How much glibc infrastructure does __thread rely on? More specifically:
Some time ago we had a discussion about building UML as a nolibc binary,
what would that mean for the __thread usage here?

We would need to parse TLS data (PT_TLS) from the ELF file ourselves and properly set up TLS when creating threads using clone().

Quoted 6 lines
Quoted 1 line

I guess right now we cannot use PER_CPU variables in these files.

Maybe? The only thing would be to know which "CPU" we're executing on? getpid() is async signal safe (i.e. you can call it), but there could be better ways of doing this such as setting different signal handler functions in different CPUs.

There are a number of ways to solve that problem.

One way would be to use a signal stack and calculating it from the stack pointer. Though I am still considering that we should use the tasks stack as the signal stack, in which case we may need to track the CPU that a task is executing on.

On 64bit, one could also use the FS/GS registers for per-CPU data. I believe the libc uses FS only on 64bit, so we could probably already use the GS register to for per-CPU data.

So, I am not really worried about this, we probably need a nice solution for per-CPU data anyway.

Benjamin

author
#26 lore raw

PATCH v2 5/10 um: Determine sleep based on need_resched()

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

With SMP and NO_HZ enabled, the CPU may still need to sleep even if the timer is disarmed. Switch to deciding whether to sleep based on pending resched. This is a preparation for adding SMP support.

---
 arch/um/include/shared/kern_util.h |  1 +
 arch/um/kernel/process.c           | 12 ++++++++++--
 arch/um/os-Linux/time.c            | 11 +++++------
 3 files changed, 16 insertions(+), 8 deletions(-)
arch/um/include/shared/kern_util.h +1 -0
index 00ca3e12fd9a..3daaa5c4b35d 100644
@@ -55,6 +55,7 @@ extern int __uml_cant_sleep(void);
 extern int get_current_pid(void);
 extern int copy_from_user_proc(void *to, void *from, int size);
 extern char *uml_strdup(const char *string);
+int uml_need_resched(void);
 
 extern unsigned long to_irq_stack(unsigned long *mask_out);
 extern unsigned long from_irq_stack(int nested);
arch/um/kernel/process.c +10 -2
index 1be644de9e41..01b935c00454 100644
@@ -209,10 +209,13 @@ int arch_dup_task_struct(struct task_struct *dst,
 
 void um_idle_sleep(void)
 {
-	if (time_travel_mode != TT_MODE_OFF)
+	if (time_travel_mode != TT_MODE_OFF) {
 		time_travel_sleep();
-	else
+	} else {
+		raw_local_irq_enable();
 		os_idle_sleep();
+		raw_local_irq_disable();
+	}
 }
 
 void arch_cpu_idle(void)
@@ -225,6 +228,11 @@ int __uml_cant_sleep(void) {
 	/* Is in_interrupt() really needed? */
 }
 
+int uml_need_resched(void)
+{
+	return need_resched();
+}
+
 extern exitcall_t __uml_exitcall_begin, __uml_exitcall_end;
 
 void do_uml_exitcalls(void)
arch/um/os-Linux/time.c +5 -6
index 4d5591d96d8c..f25a4196bab7 100644
@@ -98,18 +98,17 @@ long long os_nsecs(void)
  */
 void os_idle_sleep(void)
 {
-	struct itimerspec its;
 	sigset_t set, old;
 
-	/* block SIGALRM while we analyze the timer state */
+	/* Block SIGALRM while performing the need_resched check. */
 	sigemptyset(&set);
 	sigaddset(&set, SIGALRM);
 	sigprocmask(SIG_BLOCK, &set, &old);
 
-	/* check the timer, and if it'll fire then wait for it */
-	timer_gettime(event_high_res_timer, &its);
-	if (its.it_value.tv_sec || its.it_value.tv_nsec)
+	/* Sleep if no resched is pending. */
+	if (!uml_need_resched())
 		sigsuspend(&old);
-	/* either way, restore the signal mask */
+
+	/* Restore the signal mask. */
 	sigprocmask(SIG_UNBLOCK, &set, NULL);
 }
Signature · 1 line
2.34.1
maintainer
#27 lore raw

Re: [PATCH v2 05/10] um: Determine sleep based on need_resched()

Quoted 5 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

With SMP and NO_HZ enabled, the CPU may still need to sleep even if the timer is disarmed. Switch to deciding whether to sleep based on pending resched. This is a preparation for adding SMP support.

What's the rationale for need_resched()? Is that somehow defined for this? Is it what other architectures use? I guess I'm just not entirely sure what it means.

johannes

author
#28 lore raw

Re: [PATCH v2 05/10] um: Determine sleep based on need_resched()

Quoted 10 lines · Johannes Berg
Quoted 5 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

With SMP and NO_HZ enabled, the CPU may still need to sleep even if the timer is disarmed. Switch to deciding whether to sleep based on pending resched. This is a preparation for adding SMP support.

What's the rationale for need_resched()? Is that somehow defined for this? Is it what other architectures use? I guess I'm just not entirely sure what it means.

Here is a relevant document:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/scheduler/sched-arch.rst?h=v6.17-rc5#n37

There is a similar check on x86:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/process.c?h=v6.17-rc5#n916

Regards,
Tiwei

maintainer
#29 lore raw

Re: [PATCH v2 05/10] um: Determine sleep based on need_resched()

Quoted 12 lines · Tiwei Bie
Quoted 10 lines · Johannes Berg
Quoted 5 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

With SMP and NO_HZ enabled, the CPU may still need to sleep even if the timer is disarmed. Switch to deciding whether to sleep based on pending resched. This is a preparation for adding SMP support.

What's the rationale for need_resched()? Is that somehow defined for this? Is it what other architectures use? I guess I'm just not entirely sure what it means.

Quoted 5 lines

Here is a relevant document:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/scheduler/sched-arch.rst?h=v6.17-rc5#n37

There is a similar check on x86:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/process.c?h=v6.17-rc5#n916

Ah cool, thanks for the pointers. Though "1." part there is a bit confusing, I guess I'll send a patch to reword it :)

FWIW I already applied a few patches, but I'll take another look, hopefully today.

johannes

author
#30 lore raw

Re: [PATCH v2 05/10] um: Determine sleep based on need_resched()

Quoted 16 lines · Johannes Berg
Quoted 15 lines · Tiwei Bie
Quoted 10 lines · Johannes Berg
Quoted 5 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

With SMP and NO_HZ enabled, the CPU may still need to sleep even if the timer is disarmed. Switch to deciding whether to sleep based on pending resched. This is a preparation for adding SMP support.

What's the rationale for need_resched()? Is that somehow defined for this? Is it what other architectures use? I guess I'm just not entirely sure what it means.

Here is a relevant document:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/scheduler/sched-arch.rst?h=v6.17-rc5#n37

Quoted 8 lines
Quoted 2 lines

There is a similar check on x86:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/process.c?h=v6.17-rc5#n916

Ah cool, thanks for the pointers. Though "1." part there is a bit confusing, I guess I'll send a patch to reword it :)

FWIW I already applied a few patches, but I'll take another look, hopefully today.

Thanks for the review! :)

Regards,
Tiwei

maintainer
#31 lore raw

Re: [PATCH v2 05/10] um: Determine sleep based on need_resched()

In reply to Tiwei Bie

arch/um/kernel/process.c int arch_dup_task_struct(struct task_struct *dst, · line 218

Quoted 12 lines · Tiwei Bie

void um_idle_sleep(void)
{

arch/um/kernel/process.c +5 -2
-	if (time_travel_mode != TT_MODE_OFF)
+	if (time_travel_mode != TT_MODE_OFF) {
 		time_travel_sleep();
-	else
+	} else {
+		raw_local_irq_enable();
 		os_idle_sleep();
+		raw_local_irq_disable();
+	}

This seems wrong, with it, lockdep gets really unhappy, and if I remove this change it seems to work OK?

I'll note that arch_cpu_idle() for x86 also doesn't change anything with interrupts.

Was there something else that required this change that I'm missing?

johannes

author
#32 lore raw

Re: [PATCH v2 05/10] um: Determine sleep based on need_resched()

Quoted 17 lines · Johannes Berg

arch/um/kernel/process.c int arch_dup_task_struct(struct task_struct *dst, · line 218

Quoted 12 lines · Tiwei Bie

void um_idle_sleep(void)
{

arch/um/kernel/process.c +5 -2
-	if (time_travel_mode != TT_MODE_OFF)
+	if (time_travel_mode != TT_MODE_OFF) {
 		time_travel_sleep();
-	else
+	} else {
+		raw_local_irq_enable();
 		os_idle_sleep();
+		raw_local_irq_disable();
+	}

This seems wrong, with it, lockdep gets really unhappy, and if I remove this change it seems to work OK?

Quoted 2 lines

I'll note that arch_cpu_idle() for x86 also doesn't change anything with interrupts.

Thanks for catching that! I missed it. I'll take a closer look.

Regards,
Tiwei

author
#33 lore raw

PATCH v2 6/10 um: Define timers on a per-CPU basis

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

Define timers on a per-CPU basis to enable each CPU to have its own timer. This is a preparation for adding SMP support.

---
 arch/um/include/linux/time-internal.h |  3 ++
 arch/um/include/shared/os.h           |  6 +--
 arch/um/kernel/irq.c                  |  2 +-
 arch/um/kernel/time.c                 | 58 +++++++++++++++++++--------
 arch/um/os-Linux/main.c               |  2 +-
 arch/um/os-Linux/time.c               | 28 ++++++++-----
 6 files changed, 68 insertions(+), 31 deletions(-)
arch/um/include/linux/time-internal.h +3 -0
index 138908b999d7..c274eb5ad55e 100644
@@ -90,4 +90,7 @@ extern unsigned long tt_extra_sched_jiffies;
  * which is intentional since we really shouldn't link it in that case.
  */
 void time_travel_ndelay(unsigned long nsec);
+
+int um_setup_timer(void);
+
 #endif /* __TIMER_INTERNAL_H__ */
arch/um/include/shared/os.h +3 -3
index 324d4eed3385..0ca6e4548671 100644
@@ -269,9 +269,9 @@ extern void os_warn(const char *fmt, ...)
 /* time.c */
 extern void os_idle_sleep(void);
 extern int os_timer_create(void);
-extern int os_timer_set_interval(unsigned long long nsecs);
-extern int os_timer_one_shot(unsigned long long nsecs);
-extern void os_timer_disable(void);
+extern int os_timer_set_interval(int cpu, unsigned long long nsecs);
+extern int os_timer_one_shot(int cpu, unsigned long long nsecs);
+extern void os_timer_disable(int cpu);
 extern long long os_persistent_clock_emulation(void);
 extern long long os_nsecs(void);
 
arch/um/kernel/irq.c +1 -1
index 0dfaf96bb7da..5ed8014e43e4 100644
@@ -683,7 +683,7 @@ void __init init_IRQ(void)
 {
 	int i;
 
-	irq_set_chip_and_handler(TIMER_IRQ, &alarm_irq_type, handle_edge_irq);
+	irq_set_chip_and_handler(TIMER_IRQ, &alarm_irq_type, handle_percpu_irq);
 
 	for (i = 1; i < UM_LAST_SIGNAL_IRQ; i++)
 		irq_set_chip_and_handler(i, &normal_irq_type, handle_edge_irq);
arch/um/kernel/time.c +41 -17

170 lines not on this page · raw message

arch/um/os-Linux/main.c +1 -1
index 3c63ce19e3bf..730723106228 100644
@@ -171,7 +171,7 @@ int __init main(int argc, char **argv, char **envp)
 	 */
 
 	/* stop timers and set timer signal to be ignored */
-	os_timer_disable();
+	os_timer_disable(0);
 
 	/* disable SIGIO for the fds and set SIGIO to be ignored */
 	err = deactivate_all_fds();
arch/um/os-Linux/time.c +19 -9
index f25a4196bab7..e38b6f84287b 100644
@@ -16,7 +16,7 @@
 #include <os.h>
 #include <string.h>
 
-static timer_t event_high_res_timer = 0;
+static timer_t event_high_res_timer[CONFIG_NR_CPUS] = { 0 };
 
 static inline long long timespec_to_ns(const struct timespec *ts)
 {
@@ -31,20 +31,30 @@ long long os_persistent_clock_emulation(void)
 	return timespec_to_ns(&realtime_tp);
 }
 
+#ifndef sigev_notify_thread_id
+#define sigev_notify_thread_id _sigev_un._tid
+#endif
+
 /**
  * os_timer_create() - create an new posix (interval) timer
  */
 int os_timer_create(void)
 {
-	timer_t *t = &event_high_res_timer;
+	timer_t *t = &event_high_res_timer[0];
+	struct sigevent sev = {
+		.sigev_notify = SIGEV_THREAD_ID,
+		.sigev_signo = SIGALRM,
+		.sigev_value.sival_ptr = t,
+		.sigev_notify_thread_id = gettid(),
+	};
 
-	if (timer_create(CLOCK_MONOTONIC, NULL, t) == -1)
+	if (timer_create(CLOCK_MONOTONIC, &sev, t) == -1)
 		return -1;
 
 	return 0;
 }
 
-int os_timer_set_interval(unsigned long long nsecs)
+int os_timer_set_interval(int cpu, unsigned long long nsecs)
 {
 	struct itimerspec its;
 
@@ -54,13 +64,13 @@ int os_timer_set_interval(unsigned long long nsecs)
 	its.it_interval.tv_sec = nsecs / UM_NSEC_PER_SEC;
 	its.it_interval.tv_nsec = nsecs % UM_NSEC_PER_SEC;
 
-	if (timer_settime(event_high_res_timer, 0, &its, NULL) == -1)
+	if (timer_settime(event_high_res_timer[cpu], 0, &its, NULL) == -1)
 		return -errno;
 
 	return 0;
 }
 
-int os_timer_one_shot(unsigned long long nsecs)
+int os_timer_one_shot(int cpu, unsigned long long nsecs)
 {
 	struct itimerspec its = {
 		.it_value.tv_sec = nsecs / UM_NSEC_PER_SEC,
@@ -70,19 +80,19 @@ int os_timer_one_shot(unsigned long long nsecs)
 		.it_interval.tv_nsec = 0, // we cheat here
 	};
 
-	timer_settime(event_high_res_timer, 0, &its, NULL);
+	timer_settime(event_high_res_timer[cpu], 0, &its, NULL);
 	return 0;
 }
 
 /**
  * os_timer_disable() - disable the posix (interval) timer
  */
-void os_timer_disable(void)
+void os_timer_disable(int cpu)
 {
 	struct itimerspec its;
 
 	memset(&its, 0, sizeof(struct itimerspec));
-	timer_settime(event_high_res_timer, 0, &its, NULL);
+	timer_settime(event_high_res_timer[cpu], 0, &its, NULL);
 }
 
 long long os_nsecs(void)
Signature · 1 line
2.34.1
#34 lore raw

Re: [PATCH v2 06/10] um: Define timers on a per-CPU basis

kernel test robot noticed the following build warnings:

Hi Tiwei,

kernel test robot noticed the following build warnings:

[auto build test WARNING on uml/next]
[also build test WARNING on linus/master next-20250808] [cannot apply to uml/fixes arnd-asm-generic/master v6.16] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Tiwei-Bie/um-Stop-tracking-virtual-CPUs-via-mm_cpumask/20250810-135542
base:   https://git.kernel.org/pub/scm/linux/kernel/git/uml/linux next
patch link:    https://lore.kernel.org/r/20250810055136.897712-7-tiwei.bie%40linux.dev
patch subject: [PATCH v2 06/10] um: Define timers on a per-CPU basis
config: um-randconfig-001-20250810 (https://download.01.org/0day-ci/archive/20250810/[email protected]/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250810/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

>> Warning: arch/um/os-Linux/time.c:90 function parameter 'cpu' not described in 'os_timer_disable'
Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
author
#35 lore raw

PATCH v2 7/10 um: Remove unused ipi_pipe field from cpuinfo_um

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

It's no longer used after the removal of the SMP implementation in TT mode by commit 28fa468f5316 ("um: Remove broken SMP support").

While at it, remove the outdated comment.

---
 arch/um/include/asm/processor-generic.h | 1 -
 arch/um/kernel/um_arch.c                | 3 ---
 2 files changed, 4 deletions(-)
arch/um/include/asm/processor-generic.h +0 -1
index 236fdfd7cdbe..7854d51b6639 100644
@@ -71,7 +71,6 @@ extern void start_thread(struct pt_regs *regs, unsigned long entry,
 
 struct cpuinfo_um {
 	unsigned long loops_per_jiffy;
-	int ipi_pipe[2];
 	int cache_alignment;
 	union {
 		__u32		x86_capability[NCAPINTS + NBUGINTS];
arch/um/kernel/um_arch.c +0 -3
index 2f5ee045bc7a..e46718b3c711 100644
@@ -54,12 +54,9 @@ static void __init add_arg(char *arg)
 
 /*
  * These fields are initialized at boot time and not changed.
- * XXX This structure is used only in the non-SMP case.  Maybe this
- * should be moved to smp.c.
  */
 struct cpuinfo_um boot_cpu_data = {
 	.loops_per_jiffy	= 0,
-	.ipi_pipe		= { -1, -1 },
 	.cache_alignment	= L1_CACHE_BYTES,
 	.x86_capability		= { 0 }
 };
Signature · 1 line
2.34.1
author
#36 lore raw

PATCH v2 8/10 um: Add initial SMP support

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

Add initial symmetric multi-processing (SMP) support to UML. With this support enabled, users can tell UML to start multiple virtual processors, each represented as a separate host thread.

In UML, kthreads and normal threads (when running in kernel mode) can be scheduled and executed simultaneously on different virtual processors. However, the userspace code of normal threads still runs within their respective single-threaded stubs.

That is, SMP support is currently available both within the kernel and across different processes, but still remains limited within threads of the same process in userspace.

---
 .../core/generic-idle-thread/arch-support.txt |   2 +-
 arch/um/Kconfig                               |  46 +++-
 arch/um/include/asm/current.h                 |   5 +-
 arch/um/include/asm/hardirq.h                 |  24 +-
 arch/um/include/asm/mmu.h                     |  10 +
 arch/um/include/asm/pgtable.h                 |   2 +
 arch/um/include/asm/smp.h                     |  15 +-
 arch/um/include/linux/smp-internal.h          |  17 ++
 arch/um/include/shared/kern_util.h            |   1 +
 arch/um/include/shared/os.h                   |  16 ++
 arch/um/include/shared/skas/mm_id.h           |   5 +
 arch/um/include/shared/smp.h                  |  20 ++
 arch/um/kernel/Makefile                       |   1 +
 arch/um/kernel/irq.c                          |  25 ++
 arch/um/kernel/mem.c                          |   2 +
 arch/um/kernel/process.c                      |   7 +-
 arch/um/kernel/skas/mmu.c                     |  32 ++-
 arch/um/kernel/skas/process.c                 |   7 +-
 arch/um/kernel/smp.c                          | 239 ++++++++++++++++++
 arch/um/kernel/tlb.c                          |   5 +-
 arch/um/kernel/trap.c                         |   2 +-
 arch/um/kernel/um_arch.c                      |  25 +-
 arch/um/os-Linux/Makefile                     |   4 +-
 arch/um/os-Linux/internal.h                   |   7 +
 arch/um/os-Linux/main.c                       |   3 +-
 arch/um/os-Linux/process.c                    |  20 ++
 arch/um/os-Linux/signal.c                     |  29 ++-
 arch/um/os-Linux/skas/process.c               |  22 +-
 arch/um/os-Linux/smp.c                        | 142 +++++++++++
 arch/um/os-Linux/start_up.c                   |   4 +
 arch/um/os-Linux/time.c                       |  11 +-
 31 files changed, 710 insertions(+), 40 deletions(-)
 create mode 100644 arch/um/include/linux/smp-internal.h
 create mode 100644 arch/um/include/shared/smp.h
 create mode 100644 arch/um/kernel/smp.c
 create mode 100644 arch/um/os-Linux/smp.c
Documentation/features/core/generic-idle-thread/arch-support.txt +1 -1
index 0735cb5367b4..425442e31fa2 100644
@@ -24,7 +24,7 @@
     |        s390: |  ok  |
     |          sh: |  ok  |
     |       sparc: |  ok  |
-    |          um: | TODO |
+    |          um: |  ok  |
     |         x86: |  ok  |
     |      xtensa: |  ok  |
     -----------------------
arch/um/Kconfig +43 -3

65 lines not on this page · raw message

arch/um/include/asm/current.h +3 -2
index 8accc6d6f502..159a29b3d4cc 100644
@@ -7,15 +7,16 @@
 
 #ifndef __ASSEMBLER__
 
+#include <shared/smp.h>
+
 struct task_struct;
 extern struct task_struct *cpu_tasks[NR_CPUS];
 
 static __always_inline struct task_struct *get_current(void)
 {
-	return cpu_tasks[0];
+	return cpu_tasks[uml_curr_cpu()];
 }
 
-
 #define current get_current()
 
 #endif /* __ASSEMBLER__ */
arch/um/include/asm/hardirq.h +23 -1
index 52e2c36267a9..8de71752a9b8 100644
@@ -2,8 +2,30 @@
 #ifndef __ASM_UM_HARDIRQ_H
 #define __ASM_UM_HARDIRQ_H
 
-#include <asm-generic/hardirq.h>
+#include <linux/cache.h>
+#include <linux/threads.h>
 
 #define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
 
+typedef struct {
+	unsigned int __softirq_pending;
+#if IS_ENABLED(CONFIG_SMP)
+	unsigned int irq_resched_count;
+	unsigned int irq_call_count;
+#endif
+} ____cacheline_aligned irq_cpustat_t;
+
+DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
+
+#define __ARCH_IRQ_STAT
+
+#define inc_irq_stat(member)	this_cpu_inc(irq_stat.member)
+
+#include <linux/irq.h>
+
+static inline void ack_bad_irq(unsigned int irq)
+{
+	pr_crit("unexpected IRQ trap at vector %02x\n", irq);
+}
+
 #endif /* __ASM_UM_HARDIRQ_H */
arch/um/include/asm/mmu.h +10 -0
index 4d0e4239f3cc..07d48738b402 100644
@@ -7,16 +7,26 @@
 #define __ARCH_UM_MMU_H
 
 #include "linux/types.h"
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <mm_id.h>
 
 typedef struct mm_context {
 	struct mm_id id;
+	struct mutex turnstile;
 
 	struct list_head list;
 
 	/* Address range in need of a TLB sync */
+	spinlock_t sync_tlb_lock;
 	unsigned long sync_tlb_range_from;
 	unsigned long sync_tlb_range_to;
 } mm_context_t;
 
+#define INIT_MM_CONTEXT(mm)						\
+	.context = {							\
+		.turnstile = __MUTEX_INITIALIZER(mm.context.turnstile),	\
+		.sync_tlb_lock = __SPIN_LOCK_INITIALIZER(mm.context.sync_tlb_lock), \
+	}
+
 #endif
arch/um/include/asm/pgtable.h +2 -0
index 24fdea6f88c3..91aec3698475 100644
@@ -225,6 +225,8 @@ static inline void set_pte(pte_t *pteptr, pte_t pteval)
 static inline void um_tlb_mark_sync(struct mm_struct *mm, unsigned long start,
 				    unsigned long end)
 {
+	guard(spinlock_irqsave)(&mm->context.sync_tlb_lock);
+
 	if (!mm->context.sync_tlb_range_to) {
 		mm->context.sync_tlb_range_from = start;
 		mm->context.sync_tlb_range_to = end;
arch/um/include/asm/smp.h +14 -1
index a8cc1d46ddcb..be1743a6ff3c 100644
@@ -2,6 +2,19 @@
 #ifndef __UM_SMP_H
 #define __UM_SMP_H
 
-#define hard_smp_processor_id()		0
+#if IS_ENABLED(CONFIG_SMP)
+
+#include <linux/cpumask.h>
+#include <shared/smp.h>
+
+#define raw_smp_processor_id() uml_curr_cpu()
+
+void arch_smp_send_reschedule(int cpu);
+
+void arch_send_call_function_single_ipi(int cpu);
+
+void arch_send_call_function_ipi_mask(const struct cpumask *mask);
+
+#endif /* CONFIG_SMP */
 
 #endif
arch/um/include/linux/smp-internal.h new file +17 -0
new file mode 100644
index 000000000000..1dbcbc23f9c9
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __UM_SMP_INTERNAL_H
+#define __UM_SMP_INTERNAL_H
+
+#if IS_ENABLED(CONFIG_SMP)
+
+void prefill_possible_map(void);
+
+#else /* !CONFIG_SMP */
+
+static inline void prefill_possible_map(void) { }
+
+#endif /* CONFIG_SMP */
+
+extern char cpu_irqstacks[NR_CPUS][THREAD_SIZE] __aligned(THREAD_SIZE);
+
+#endif /* __UM_SMP_INTERNAL_H */
arch/um/include/shared/kern_util.h +1 -0
index 3daaa5c4b35d..14a8df1927a6 100644
@@ -14,6 +14,7 @@ struct siginfo;
 extern int uml_exitcode;
 
 extern int kmalloc_ok;
+extern int kmalloc_disabled[];
 
 #define UML_ROUND_UP(addr) \
 	((((unsigned long) addr) + PAGE_SIZE - 1) & PAGE_MASK)
arch/um/include/shared/os.h +16 -0
index 0ca6e4548671..2be7309b7863 100644
@@ -216,6 +216,9 @@ extern int can_drop_memory(void);
 
 void os_set_pdeathsig(void);
 
+int os_futex_wait(void *uaddr, unsigned int val);
+int os_futex_wake(void *uaddr);
+
 /* execvp.c */
 extern int execvp_noalloc(char *buf, const char *file, char *const argv[]);
 /* helper.c */
@@ -339,4 +342,17 @@ extern void um_trace_signals_off(void);
 /* time-travel */
 extern void deliver_time_travel_irqs(void);
 
+/* smp.c */
+#if IS_ENABLED(CONFIG_SMP)
+void os_init_smp(void);
+int os_start_cpu_thread(int cpu);
+void os_start_secondary(void *arg, jmp_buf *switch_buf);
+int os_send_ipi(int cpu, int vector);
+void os_local_ipi_enable(void);
+void os_local_ipi_disable(void);
+#else /* !CONFIG_SMP */
+static inline void os_local_ipi_enable(void) { }
+static inline void os_local_ipi_disable(void) { }
+#endif /* CONFIG_SMP */
+
 #endif
arch/um/include/shared/skas/mm_id.h +5 -0
index 4f977ef5dda5..fb96c0bd8222 100644
@@ -6,6 +6,8 @@
 #ifndef __MM_ID_H
 #define __MM_ID_H
 
+#include <linux/compiler_types.h>
+
 #define STUB_MAX_FDS 4
 
 struct mm_id {
@@ -19,6 +21,9 @@ struct mm_id {
 	int syscall_fd_map[STUB_MAX_FDS];
 };
 
+void enter_turnstile(struct mm_id *mm_id) __acquires(turnstile);
+void exit_turnstile(struct mm_id *mm_id) __releases(turnstile);
+
 void notify_mm_kill(int pid);
 
 #endif
arch/um/include/shared/smp.h new file +20 -0
new file mode 100644
index 000000000000..06e3faa95091
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __UM_SHARED_SMP_H
+#define __UM_SHARED_SMP_H
+
+#if IS_ENABLED(CONFIG_SMP)
+
+extern int uml_ncpus;
+
+int uml_curr_cpu(void);
+void uml_start_secondary(void *opaque);
+void uml_ipi_handler(int vector);
+
+#else /* !CONFIG_SMP */
+
+#define uml_ncpus 1
+#define uml_curr_cpu() 0
+
+#endif /* CONFIG_SMP */
+
+#endif /* __UM_SHARED_SMP_H */
arch/um/kernel/Makefile +1 -0
index b8f4e9281599..be60bc451b3f 100644
@@ -25,6 +25,7 @@ obj-$(CONFIG_GPROF)	+= gprof_syms.o
 obj-$(CONFIG_OF) += dtb.o
 obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
 obj-$(CONFIG_STACKTRACE) += stacktrace.o
+obj-$(CONFIG_SMP) += smp.o
 
 USER_OBJS := config.o
 
arch/um/kernel/irq.c +25 -0
index 5ed8014e43e4..ed1a3d69d09b 100644
@@ -22,6 +22,9 @@
 #include <irq_kern.h>
 #include <linux/time-internal.h>
 
+DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
+
+#define irq_stats(x)		(&per_cpu(irq_stat, x))
 
 /* When epoll triggers we do not know why it did so
  * we can also have different IRQs for read and write.
@@ -696,3 +699,25 @@ void sigchld_handler(int sig, struct siginfo *unused_si,
 {
 	do_IRQ(SIGCHLD_IRQ, regs);
 }
+
+/*
+ * /proc/interrupts printing for arch specific interrupts
+ */
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+#if IS_ENABLED(CONFIG_SMP)
+	int cpu;
+
+	seq_printf(p, "%*s: ", prec, "RES");
+	for_each_online_cpu(cpu)
+		seq_printf(p, "%10u ", irq_stats(cpu)->irq_resched_count);
+	seq_puts(p, "  Rescheduling interrupts\n");
+
+	seq_printf(p, "%*s: ", prec, "CAL");
+	for_each_online_cpu(cpu)
+		seq_printf(p, "%10u ", irq_stats(cpu)->irq_call_count);
+	seq_puts(p, "  Function call interrupts\n");
+#endif
+
+	return 0;
+}
arch/um/kernel/mem.c +2 -0
index 76bec7de81b5..d35c1bd225e0 100644
@@ -53,6 +53,8 @@ pgd_t swapper_pg_dir[PTRS_PER_PGD];
 /* Initialized at boot time, and readonly after that */
 int kmalloc_ok = 0;
 
+int kmalloc_disabled[NR_CPUS];
+
 /* Used during early boot */
 static unsigned long brk_end;
 
arch/um/kernel/process.c +4 -3
index 01b935c00454..5570c6065243 100644
@@ -185,11 +185,12 @@ int copy_thread(struct task_struct * p, const struct kernel_clone_args *args)
 
 void initial_thread_cb(void (*proc)(void *), void *arg)
 {
-	int save_kmalloc_ok = kmalloc_ok;
+	int cpu = raw_smp_processor_id();
+	int save_kmalloc = kmalloc_disabled[cpu];
 
-	kmalloc_ok = 0;
+	kmalloc_disabled[cpu] = 1;
 	initial_thread_cb_skas(proc, arg);
-	kmalloc_ok = save_kmalloc_ok;
+	kmalloc_disabled[cpu] = save_kmalloc;
 }
 
 int arch_dup_task_struct(struct task_struct *dst,
arch/um/kernel/skas/mmu.c +24 -8
index afe9a2f251ef..511b2e21fea0 100644
@@ -23,12 +23,30 @@ static_assert(sizeof(struct stub_data) == STUB_DATA_PAGES * UM_KERN_PAGE_SIZE);
 static spinlock_t mm_list_lock;
 static struct list_head mm_list;
 
+void enter_turnstile(struct mm_id *mm_id) __acquires(turnstile)
+{
+	struct mm_context *ctx = container_of(mm_id, struct mm_context, id);
+
+	mutex_lock(&ctx->turnstile);
+}
+
+void exit_turnstile(struct mm_id *mm_id) __releases(turnstile)
+{
+	struct mm_context *ctx = container_of(mm_id, struct mm_context, id);
+
+	mutex_unlock(&ctx->turnstile);
+}
+
 int init_new_context(struct task_struct *task, struct mm_struct *mm)
 {
 	struct mm_id *new_id = &mm->context.id;
 	unsigned long stack = 0;
 	int ret = -ENOMEM;
 
+	mutex_init(&mm->context.turnstile);
+
+	spin_lock_init(&mm->context.sync_tlb_lock);
+
 	stack = __get_free_pages(GFP_KERNEL | __GFP_ZERO, ilog2(STUB_DATA_PAGES));
 	if (stack == 0)
 		goto out;
@@ -73,6 +91,9 @@ void destroy_context(struct mm_struct *mm)
 		return;
 	}
 
+	scoped_guard(spinlock_irqsave, &mm_list_lock)
+		list_del(&mm->context.list);
+
 	if (mmu->id.pid > 0) {
 		os_kill_ptraced_process(mmu->id.pid, 1);
 		mmu->id.pid = -1;
@@ -82,10 +103,6 @@ void destroy_context(struct mm_struct *mm)
 		os_close_file(mmu->id.sock);
 
 	free_pages(mmu->id.stack, ilog2(STUB_DATA_PAGES));
-
-	guard(spinlock_irqsave)(&mm_list_lock);
-
-	list_del(&mm->context.list);
 }
 
 static irqreturn_t mm_sigchld_irq(int irq, void* dev)
@@ -110,12 +127,11 @@ static irqreturn_t mm_sigchld_irq(int irq, void* dev)
 				/* Marks the MM as dead */
 				mm_context->id.pid = -1;
 
-				/*
-				 * NOTE: If SMP is implemented, a futex_wake
-				 * needs to be added here.
-				 */
 				stub_data = (void *)mm_context->id.stack;
 				stub_data->futex = FUTEX_IN_KERN;
+#if IS_ENABLED(CONFIG_SMP)
+				os_futex_wake(&stub_data->futex);
+#endif
 
 				/*
 				 * NOTE: Currently executing syscalls by
arch/um/kernel/skas/process.c +4 -3
index 5881b17eb987..444e82b5b7ee 100644
@@ -7,6 +7,7 @@
 #include <linux/sched/mm.h>
 #include <linux/sched/task_stack.h>
 #include <linux/sched/task.h>
+#include <linux/smp-internal.h>
 
 #include <asm/tlbflush.h>
 
@@ -26,12 +27,12 @@ static int __init start_kernel_proc(void *unused)
 	return 0;
 }
 
-static char cpu0_irqstack[THREAD_SIZE] __aligned(THREAD_SIZE);
+char cpu_irqstacks[NR_CPUS][THREAD_SIZE] __aligned(THREAD_SIZE);
 
 int __init start_uml(void)
 {
-	stack_protections((unsigned long) &cpu0_irqstack);
-	set_sigstack(cpu0_irqstack, THREAD_SIZE);
+	stack_protections((unsigned long) &cpu_irqstacks[0]);
+	set_sigstack(cpu_irqstacks[0], THREAD_SIZE);
 
 	init_new_thread_signals();
 
arch/um/kernel/smp.c new file +239 -0

239 lines not on this page · raw message

arch/um/kernel/tlb.c +4 -1
index cf7e0d4407f2..39608cccf2c6 100644
@@ -162,9 +162,11 @@ int um_tlb_sync(struct mm_struct *mm)
 {
 	pgd_t *pgd;
 	struct vm_ops ops;
-	unsigned long addr = mm->context.sync_tlb_range_from, next;
+	unsigned long addr, next;
 	int ret = 0;
 
+	guard(spinlock_irqsave)(&mm->context.sync_tlb_lock);
+
 	if (mm->context.sync_tlb_range_to == 0)
 		return 0;
 
@@ -177,6 +179,7 @@ int um_tlb_sync(struct mm_struct *mm)
 		ops.unmap = unmap;
 	}
 
+	addr = mm->context.sync_tlb_range_from;
 	pgd = pgd_offset(mm, addr);
 	do {
 		next = pgd_addr_end(addr, mm->context.sync_tlb_range_to);
arch/um/kernel/trap.c +1 -1
index 5b80a3a89c20..177615820a4c 100644
@@ -316,7 +316,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
 	if (!is_user && regs)
 		current->thread.segv_regs = container_of(regs, struct pt_regs, regs);
 
-	if (!is_user && init_mm.context.sync_tlb_range_to) {
+	if (!is_user && address >= start_vm && address < end_vm) {
 		/*
 		 * Kernel has pending updates from set_ptes that were not
 		 * flushed yet. Syncing them should fix the pagefault (if not
arch/um/kernel/um_arch.c +23 -2
index e46718b3c711..b4a80dcfe1c0 100644
@@ -19,6 +19,7 @@
 #include <linux/kmsg_dump.h>
 #include <linux/suspend.h>
 #include <linux/random.h>
+#include <linux/smp-internal.h>
 
 #include <asm/processor.h>
 #include <asm/cpufeature.h>
@@ -71,6 +72,12 @@ static int show_cpuinfo(struct seq_file *m, void *v)
 {
 	int i = 0;
 
+#if IS_ENABLED(CONFIG_SMP)
+	i = (uintptr_t) v - 1;
+	if (!cpu_online(i))
+		return 0;
+#endif
+
 	seq_printf(m, "processor\t: %d\n", i);
 	seq_printf(m, "vendor_id\t: User Mode Linux\n");
 	seq_printf(m, "model name\t: UML\n");
@@ -87,13 +94,14 @@ static int show_cpuinfo(struct seq_file *m, void *v)
 		   loops_per_jiffy/(500000/HZ),
 		   (loops_per_jiffy/(5000/HZ)) % 100);
 
-
 	return 0;
 }
 
 static void *c_start(struct seq_file *m, loff_t *pos)
 {
-	return *pos < nr_cpu_ids ? &boot_cpu_data + *pos : NULL;
+	if (*pos < nr_cpu_ids)
+		return (void *)(uintptr_t)(*pos + 1);
+	return NULL;
 }
 
 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
@@ -423,6 +431,7 @@ void __init setup_arch(char **cmdline_p)
 	strscpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
 	*cmdline_p = command_line;
 	setup_hostinfo(host_info, sizeof host_info);
+	prefill_possible_map();
 
 	if (os_getrandom(rng_seed, sizeof(rng_seed), 0) == sizeof(rng_seed)) {
 		add_bootloader_randomness(rng_seed, sizeof(rng_seed));
@@ -457,6 +466,18 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
 {
 }
 
+#if IS_ENABLED(CONFIG_SMP)
+void alternatives_smp_module_add(struct module *mod, char *name,
+				 void *locks, void *locks_end,
+				 void *text,  void *text_end)
+{
+}
+
+void alternatives_smp_module_del(struct module *mod)
+{
+}
+#endif
+
 void *text_poke(void *addr, const void *opcode, size_t len)
 {
 	/*
arch/um/os-Linux/Makefile +3 -1
index fae836713487..70c73c22f715 100644
@@ -16,8 +16,10 @@ CFLAGS_main.o += -Wno-frame-larger-than
 
 obj-$(CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA) += elf_aux.o
 
+obj-$(CONFIG_SMP) += smp.o
+
 USER_OBJS := $(user-objs-y) elf_aux.o execvp.o file.o helper.o irq.o \
 	main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \
-	tty.o umid.o util.o
+	tty.o umid.o util.o smp.o
 
 include $(srctree)/arch/um/scripts/Makefile.rules
arch/um/os-Linux/internal.h +7 -0
index 5d8d3b0817a9..0c21b4545d7c 100644
@@ -4,6 +4,7 @@
 
 #include <mm_id.h>
 #include <stub-data.h>
+#include <signal.h>
 
 /*
  * elf_aux.c
@@ -20,4 +21,10 @@ void check_tmpexec(void);
  */
 void wait_stub_done(int pid);
 void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys);
+
+/*
+ * smp.c
+ */
+#define IPI_SIGNAL	SIGRTMIN
+
 #endif /* __UM_OS_LINUX_INTERNAL_H */
arch/um/os-Linux/main.c +2 -1
index 730723106228..f46a0b30a2a1 100644
@@ -16,6 +16,7 @@
 #include <init.h>
 #include <kern_util.h>
 #include <os.h>
+#include <smp.h>
 #include <um_malloc.h>
 #include "internal.h"
 
@@ -207,7 +208,7 @@ void *__wrap_malloc(int size)
 {
 	void *ret;
 
-	if (!kmalloc_ok)
+	if (!kmalloc_ok || kmalloc_disabled[uml_curr_cpu()])
 		return __real_malloc(size);
 	else if (size <= UM_KERN_PAGE_SIZE)
 		/* finding contiguous pages can be hard*/
arch/um/os-Linux/process.c +20 -0
index 00b49e90d05f..3a2a84ab9325 100644
@@ -10,6 +10,8 @@
 #include <errno.h>
 #include <signal.h>
 #include <fcntl.h>
+#include <limits.h>
+#include <linux/futex.h>
 #include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/prctl.h>
@@ -189,3 +191,21 @@ void os_set_pdeathsig(void)
 {
 	prctl(PR_SET_PDEATHSIG, SIGKILL);
 }
+
+int os_futex_wait(void *uaddr, unsigned int val)
+{
+	int r;
+
+	CATCH_EINTR(r = syscall(__NR_futex, uaddr, FUTEX_WAIT, val,
+				NULL, NULL, 0));
+	return r < 0 ? -errno : r;
+}
+
+int os_futex_wake(void *uaddr)
+{
+	int r;
+
+	CATCH_EINTR(r = syscall(__NR_futex, uaddr, FUTEX_WAKE, INT_MAX,
+				NULL, NULL, 0));
+	return r < 0 ? -errno : r;
+}
arch/um/os-Linux/signal.c +25 -4
index 58da8c6ece98..b05e32d51d5b 100644
@@ -253,9 +253,29 @@ int change_sig(int signal, int on)
 	return 0;
 }
 
-void block_signals(void)
+static inline void __block_signals(void)
 {
+	if (!signals_enabled)
+		return;
+
+	os_local_ipi_disable();
+	barrier();
 	signals_enabled = 0;
+}
+
+static inline void __unblock_signals(void)
+{
+	if (signals_enabled)
+		return;
+
+	signals_enabled = 1;
+	barrier();
+	os_local_ipi_enable();
+}
+
+void block_signals(void)
+{
+	__block_signals();
 	/*
 	 * This must return with signals disabled, so this barrier
 	 * ensures that writes are flushed out before the return.
@@ -272,7 +292,8 @@ void unblock_signals(void)
 	if (signals_enabled == 1)
 		return;
 
-	signals_enabled = 1;
+	__unblock_signals();
+
 #if IS_ENABLED(CONFIG_UML_TIME_TRAVEL_SUPPORT)
 	deliver_time_travel_irqs();
 #endif
@@ -306,7 +327,7 @@ void unblock_signals(void)
 		 * tracing that happens inside the handlers we call for the
 		 * pending signals will mess up the tracing state.
 		 */
-		signals_enabled = 0;
+		__block_signals();
 		um_trace_signals_off();
 
 		/*
@@ -338,7 +359,7 @@ void unblock_signals(void)
 
 		/* Re-enable signals and trace that we're doing so. */
 		um_trace_signals_on();
-		signals_enabled = 1;
+		__unblock_signals();
 	}
 }
 
arch/um/os-Linux/skas/process.c +16 -6
index 78f48fa9db8b..42fa99227b4c 100644
@@ -553,7 +553,7 @@ extern unsigned long tt_extra_sched_jiffies;
 void userspace(struct uml_pt_regs *regs)
 {
 	int err, status, op;
-	siginfo_t si_ptrace;
+	siginfo_t si_local;
 	siginfo_t *si;
 	int sig;
 
@@ -563,6 +563,13 @@ void userspace(struct uml_pt_regs *regs)
 	while (1) {
 		struct mm_id *mm_id = current_mm_id();
 
+		/*
+		 * At any given time, only one CPU thread can enter the
+		 * turnstile to operate on the same stub process, including
+		 * executing stub system calls (mmap and munmap).
+		 */
+		enter_turnstile(mm_id);
+
 		/*
 		 * When we are in time-travel mode, userspace can theoretically
 		 * do a *lot* of work without being scheduled. The problem with
@@ -630,9 +637,10 @@ void userspace(struct uml_pt_regs *regs)
 			}
 
 			if (proc_data->si_offset > sizeof(proc_data->sigstack) - sizeof(*si))
-				panic("%s - Invalid siginfo offset from child",
-				      __func__);
-			si = (void *)&proc_data->sigstack[proc_data->si_offset];
+				panic("%s - Invalid siginfo offset from child", __func__);
+
+			si = &si_local;
+			memcpy(si, &proc_data->sigstack[proc_data->si_offset], sizeof(*si));
 
 			regs->is_user = 1;
 
@@ -728,8 +736,8 @@ void userspace(struct uml_pt_regs *regs)
 				case SIGFPE:
 				case SIGWINCH:
 					ptrace(PTRACE_GETSIGINFO, pid, 0,
-					       (struct siginfo *)&si_ptrace);
-					si = &si_ptrace;
+					       (struct siginfo *)&si_local);
+					si = &si_local;
 					break;
 				default:
 					si = NULL;
@@ -740,6 +748,8 @@ void userspace(struct uml_pt_regs *regs)
 			}
 		}
 
+		exit_turnstile(mm_id);
+
 		UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */
 
 		if (sig) {
arch/um/os-Linux/smp.c new file +142 -0

142 lines not on this page · raw message

arch/um/os-Linux/start_up.c +4 -0
index a827c2e01aa5..61c61e9c246c 100644
@@ -22,6 +22,7 @@
 #include <asm/unistd.h>
 #include <init.h>
 #include <os.h>
+#include <smp.h>
 #include <kern_util.h>
 #include <mem_user.h>
 #include <ptrace_user.h>
@@ -481,6 +482,9 @@ void __init os_early_checks(void)
 			fatal("SECCOMP userspace requested but not functional!\n");
 	}
 
+	if (uml_ncpus > 1)
+		fatal("SMP is not supported with PTRACE userspace.\n");
+
 	using_seccomp = 0;
 	check_ptrace();
 
arch/um/os-Linux/time.c +9 -2
index e38b6f84287b..4b49380eeff5 100644
@@ -14,7 +14,9 @@
 #include <sys/time.h>
 #include <kern_util.h>
 #include <os.h>
+#include <smp.h>
 #include <string.h>
+#include "internal.h"
 
 static timer_t event_high_res_timer[CONFIG_NR_CPUS] = { 0 };
 
@@ -40,7 +42,8 @@ long long os_persistent_clock_emulation(void)
  */
 int os_timer_create(void)
 {
-	timer_t *t = &event_high_res_timer[0];
+	int cpu = uml_curr_cpu();
+	timer_t *t = &event_high_res_timer[cpu];
 	struct sigevent sev = {
 		.sigev_notify = SIGEV_THREAD_ID,
 		.sigev_signo = SIGALRM,
@@ -110,9 +113,13 @@ void os_idle_sleep(void)
 {
 	sigset_t set, old;
 
-	/* Block SIGALRM while performing the need_resched check. */
+	/*
+	 * Block SIGALRM and the IPI signal while performing
+	 * the need_resched check.
+	 */
 	sigemptyset(&set);
 	sigaddset(&set, SIGALRM);
+	sigaddset(&set, IPI_SIGNAL);
 	sigprocmask(SIG_BLOCK, &set, &old);
 
 	/* Sleep if no resched is pending. */
Signature · 1 line
2.34.1
maintainer
#37 lore raw

Re: [PATCH v2 08/10] um: Add initial SMP support

Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Add initial symmetric multi-processing (SMP) support to UML. With this support enabled, users can tell UML to start multiple virtual processors, each represented as a separate host thread.

Quoted 8 lines

In UML, kthreads and normal threads (when running in kernel mode) can be scheduled and executed simultaneously on different virtual processors. However, the userspace code of normal threads still runs within their respective single-threaded stubs.

That is, SMP support is currently available both within the kernel and across different processes, but still remains limited within threads of the same process in userspace.

Another thing that isn't covered is anything relating to interrupt affinity, I guess? Is that automatically not working, or will it look like you can change things but that not do anything?

I don't think it's important now (though eventually I would actually like to have it for our simulations), but was just thinking about it.

johannes

author
#38 lore raw

Re: [PATCH v2 08/10] um: Add initial SMP support

Quoted 16 lines · Johannes Berg
Quoted 6 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Add initial symmetric multi-processing (SMP) support to UML. With this support enabled, users can tell UML to start multiple virtual processors, each represented as a separate host thread.

Quoted 8 lines

In UML, kthreads and normal threads (when running in kernel mode) can be scheduled and executed simultaneously on different virtual processors. However, the userspace code of normal threads still runs within their respective single-threaded stubs.

That is, SMP support is currently available both within the kernel and across different processes, but still remains limited within threads of the same process in userspace.

Quoted 6 lines

Another thing that isn't covered is anything relating to interrupt affinity, I guess? Is that automatically not working, or will it look like you can change things but that not do anything?

I don't think it's important now (though eventually I would actually like to have it for our simulations), but was just thinking about it.

Currently, our irq_chips haven't implemented the irq_set_affinity method, so setting IRQ affinity is not supported at the moment, e.g., attempting to set affinity through /proc/irq/IRQ#/smp_affinity will fail with EPERM. And yeah, we should support it eventually. :)

Regards,
Tiwei

maintainer
#39 lore raw

Re: [PATCH v2 08/10] um: Add initial SMP support

Quoted 5 lines · Tiwei Bie
Quoted 5 lines

Another thing that isn't covered is anything relating to interrupt affinity, I guess? Is that automatically not working, or will it look like you can change things but that not do anything?

Quoted 7 lines
Quoted 2 lines

I don't think it's important now (though eventually I would actually like to have it for our simulations), but was just thinking about it.

Currently, our irq_chips haven't implemented the irq_set_affinity method, so setting IRQ affinity is not supported at the moment, e.g., attempting to set affinity through /proc/irq/IRQ#/smp_affinity will fail with EPERM. And yeah, we should support it eventually. :)

OK cool, thanks for taking a look!

johannes

author
#40 lore raw

PATCH v2 9/10 asm-generic: percpu: Add assembly guard

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

Currently, asm/percpu.h is directly or indirectly included by some assembly files on x86. Some of them (e.g., checksum_32.S) are also used on um. But x86 and um provide different versions of asm/percpu.h -- um uses asm-generic/percpu.h directly.

When SMP is enabled, asm-generic/percpu.h will introduce C code that cannot be assembled. Since asm-generic/percpu.h currently is not designed for use in assembly, and these assembly files do not actually need asm/percpu.h on um, let's add the assembly guard in asm-generic/percpu.h to fix this issue.

---
 include/asm-generic/percpu.h | 3 +++
 1 file changed, 3 insertions(+)
include/asm-generic/percpu.h +3 -0
index 02aeca21479a..6628670bcb90 100644
@@ -2,6 +2,8 @@
 #ifndef _ASM_GENERIC_PERCPU_H_
 #define _ASM_GENERIC_PERCPU_H_
 
+#ifndef __ASSEMBLER__
+
 #include <linux/compiler.h>
 #include <linux/threads.h>
 #include <linux/percpu-defs.h>
@@ -557,4 +559,5 @@ do {									\
 	this_cpu_generic_cmpxchg(pcp, oval, nval)
 #endif
 
+#endif /* __ASSEMBLER__ */
 #endif /* _ASM_GENERIC_PERCPU_H_ */
Signature · 1 line
2.34.1
maintainer
#41 lore raw

Re: [PATCH v2 09/10] asm-generic: percpu: Add assembly guard

Quoted 7 lines · Tiwei Bie

From: Tiwei Bie <[email protected]>

Currently, asm/percpu.h is directly or indirectly included by some assembly files on x86. Some of them (e.g., checksum_32.S) are also used on um. But x86 and um provide different versions of asm/percpu.h -- um uses asm-generic/percpu.h directly.

Quoted 8 lines

When SMP is enabled, asm-generic/percpu.h will introduce C code that cannot be assembled. Since asm-generic/percpu.h currently is not designed for use in assembly, and these assembly files do not actually need asm/percpu.h on um, let's add the assembly guard in asm-generic/percpu.h to fix this issue.

Can we get an ACK from someone for this? :) Or a reject and we need to find other ways of doing things?

johannes

author
#42 lore raw

PATCH v2 10/10 um: Enable SMP support on x86

In reply to Tiwei Bie

From: Tiwei Bie <[email protected]>

Implement spinlock support for SMP on UML/x86, leveraging x86's spinlock implementation. In addition, to support SMP on CPUs that do not support CX8, some additional work is required. However, considering that such CPUs are already very outdated, and x86 is also removing support for them [1], let's enable SMP support only on CPUs that support CX8.

[1] https://lore.kernel.org/lkml/[email protected]/

---
 arch/x86/um/Kconfig        | 3 +++
 arch/x86/um/asm/spinlock.h | 8 ++++++++
 2 files changed, 11 insertions(+)
 create mode 100644 arch/x86/um/asm/spinlock.h
arch/x86/um/Kconfig +3 -0
index 986045d5e638..c52fb5cb8d21 100644
@@ -9,8 +9,11 @@ endmenu
 config UML_X86
 	def_bool y
 	select ARCH_BINFMT_ELF_EXTRA_PHDRS if X86_32
+	select ARCH_USE_QUEUED_RWLOCKS
+	select ARCH_USE_QUEUED_SPINLOCKS
 	select DCACHE_WORD_ACCESS
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS
+	select UML_SUBARCH_SUPPORTS_SMP if X86_CX8
 
 config 64BIT
 	bool "64-bit kernel" if "$(SUBARCH)" = "x86"
arch/x86/um/asm/spinlock.h new file +8 -0
new file mode 100644
index 000000000000..20fc77514214
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_UM_SPINLOCK_H
+#define __ASM_UM_SPINLOCK_H
+
+#include <asm/qspinlock.h>
+#include <asm/qrwlock.h>
+
+#endif /* __ASM_UM_SPINLOCK_H */
Signature · 1 line
2.34.1