From 2670917c2fc8902558f3aba4f41e5cc5bf6e18fa Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Mon, 27 Oct 2025 08:18:10 +0800 Subject: um: Determine sleep based on need_resched() 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. Additionally, because disabling IRQs does not block SIGALRM, it is also necessary to check for any pending timer alarms. This is a preparation for adding SMP support. Signed-off-by: Tiwei Bie Link: https://patch.msgid.link/20251027001815.1666872-4-tiwei.bie@linux.dev Signed-off-by: Johannes Berg --- arch/um/os-Linux/time.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'arch/um/os-Linux/time.c') diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c index 4d5591d96d8c..f3d4547e5227 100644 --- a/arch/um/os-Linux/time.c +++ b/arch/um/os-Linux/time.c @@ -15,6 +15,7 @@ #include #include #include +#include "internal.h" static timer_t event_high_res_timer = 0; @@ -98,18 +99,20 @@ 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) + /* + * Because disabling IRQs does not block SIGALRM, it is also + * necessary to check for any pending timer alarms. + */ + if (!uml_need_resched() && !timer_alarm_pending()) sigsuspend(&old); - /* either way, restore the signal mask */ + + /* Restore the signal mask. */ sigprocmask(SIG_UNBLOCK, &set, NULL); } -- cgit From 9c82de55d4783e906f18219f833ad97fd8d9c5df Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Mon, 27 Oct 2025 08:18:11 +0800 Subject: um: Define timers on a per-CPU basis Define timers on a per-CPU basis to enable each CPU to have its own timer. This is a preparation for adding SMP support. Signed-off-by: Tiwei Bie Link: https://patch.msgid.link/20251027001815.1666872-5-tiwei.bie@linux.dev Signed-off-by: Johannes Berg --- arch/um/os-Linux/time.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'arch/um/os-Linux/time.c') diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c index f3d4547e5227..e0197bfe4ac9 100644 --- a/arch/um/os-Linux/time.c +++ b/arch/um/os-Linux/time.c @@ -17,7 +17,7 @@ #include #include "internal.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) { @@ -32,20 +32,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; @@ -55,13 +65,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, @@ -71,19 +81,20 @@ 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 + * @cpu: the CPU for which the timer is to be disabled */ -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) -- cgit From 1e4ee5135d814fe4785890790cec81c3132888fb Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Mon, 27 Oct 2025 08:18:12 +0800 Subject: um: Add initial SMP support 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. Signed-off-by: Tiwei Bie Link: https://patch.msgid.link/20251027001815.1666872-6-tiwei.bie@linux.dev Signed-off-by: Johannes Berg --- arch/um/os-Linux/time.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) (limited to 'arch/um/os-Linux/time.c') diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c index e0197bfe4ac9..13ebc86918d4 100644 --- a/arch/um/os-Linux/time.c +++ b/arch/um/os-Linux/time.c @@ -11,9 +11,11 @@ #include #include #include +#include #include #include #include +#include #include #include "internal.h" @@ -41,7 +43,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, @@ -105,24 +108,49 @@ long long os_nsecs(void) return timespec_to_ns(&ts); } +static __thread int wake_signals; + +void os_idle_prepare(void) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, IPI_SIGNAL); + + /* + * We need to use signalfd rather than sigsuspend in idle sleep + * because the IPI signal is a real-time signal that carries data, + * and unlike handling SIGALRM, we cannot simply flag it in + * signals_pending. + */ + wake_signals = signalfd(-1, &set, SFD_CLOEXEC); + if (wake_signals < 0) + panic("Failed to create signal FD, errno = %d", errno); +} + /** * os_idle_sleep() - sleep until interrupted */ void os_idle_sleep(void) { - sigset_t set, old; + sigset_t set; - /* Block SIGALRM while performing the need_resched check. */ + /* + * Block SIGALRM while performing the need_resched check. + * Note that, because IRQs are disabled, the IPI signal is + * already blocked. + */ sigemptyset(&set); sigaddset(&set, SIGALRM); - sigprocmask(SIG_BLOCK, &set, &old); + sigprocmask(SIG_BLOCK, &set, NULL); /* * Because disabling IRQs does not block SIGALRM, it is also * necessary to check for any pending timer alarms. */ if (!uml_need_resched() && !timer_alarm_pending()) - sigsuspend(&old); + os_poll(1, &wake_signals); /* Restore the signal mask. */ sigprocmask(SIG_UNBLOCK, &set, NULL); -- cgit