mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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 <tiwei.btw@antgroup.com> Link: https://patch.msgid.link/20251027001815.1666872-6-tiwei.bie@linux.dev Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
| s390: | ok |
|
||||
| sh: | ok |
|
||||
| sparc: | ok |
|
||||
| um: | TODO |
|
||||
| um: | ok |
|
||||
| x86: | ok |
|
||||
| xtensa: | ok |
|
||||
-----------------------
|
||||
|
||||
+43
-3
@@ -28,6 +28,7 @@ config UML
|
||||
select OF_EARLY_FLATTREE if OF
|
||||
select GENERIC_IRQ_SHOW
|
||||
select GENERIC_CPU_DEVICES
|
||||
select GENERIC_SMP_IDLE_THREAD
|
||||
select HAVE_GCC_PLUGINS
|
||||
select ARCH_SUPPORTS_LTO_CLANG
|
||||
select ARCH_SUPPORTS_LTO_CLANG_THIN
|
||||
@@ -81,10 +82,48 @@ config HZ
|
||||
int
|
||||
default 100
|
||||
|
||||
config NR_CPUS
|
||||
config UML_SUBARCH_SUPPORTS_SMP
|
||||
bool
|
||||
|
||||
config SMP
|
||||
bool "Symmetric multi-processing support"
|
||||
default n
|
||||
depends on UML_SUBARCH_SUPPORTS_SMP
|
||||
help
|
||||
This option enables UML SMP support.
|
||||
|
||||
With this enabled, users can tell UML to start multiple virtual
|
||||
processors. Each virtual processor is 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 available both within the kernel and
|
||||
across different processes, but remains limited within threads
|
||||
of the same process in userspace.
|
||||
|
||||
config NR_CPUS_RANGE_BEGIN
|
||||
int
|
||||
range 1 1
|
||||
default 1
|
||||
default 1 if !SMP
|
||||
default 2
|
||||
|
||||
config NR_CPUS_RANGE_END
|
||||
int
|
||||
default 1 if !SMP
|
||||
default 64
|
||||
|
||||
config NR_CPUS_DEFAULT
|
||||
int
|
||||
default 1 if !SMP
|
||||
default 2
|
||||
|
||||
config NR_CPUS
|
||||
int "Maximum number of CPUs" if SMP
|
||||
range NR_CPUS_RANGE_BEGIN NR_CPUS_RANGE_END
|
||||
default NR_CPUS_DEFAULT
|
||||
|
||||
source "arch/$(HEADER_ARCH)/um/Kconfig"
|
||||
|
||||
@@ -254,6 +293,7 @@ source "arch/um/drivers/Kconfig"
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
depends on !SMP
|
||||
|
||||
menu "Power management options"
|
||||
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -267,6 +270,7 @@ extern void os_warn(const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
/* time.c */
|
||||
void os_idle_prepare(void);
|
||||
extern void os_idle_sleep(void);
|
||||
extern int os_timer_create(void);
|
||||
extern int os_timer_set_interval(int cpu, unsigned long long nsecs);
|
||||
@@ -339,4 +343,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,5 +15,7 @@ extern void handle_syscall(struct uml_pt_regs *regs);
|
||||
extern unsigned long current_stub_stack(void);
|
||||
extern struct mm_id *current_mm_id(void);
|
||||
extern void current_mm_sync(void);
|
||||
void initial_jmpbuf_lock(void);
|
||||
void initial_jmpbuf_unlock(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -701,3 +704,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;
|
||||
}
|
||||
|
||||
@@ -218,6 +218,11 @@ void arch_cpu_idle(void)
|
||||
um_idle_sleep();
|
||||
}
|
||||
|
||||
void arch_cpu_idle_prepare(void)
|
||||
{
|
||||
os_idle_prepare();
|
||||
}
|
||||
|
||||
int __uml_cant_sleep(void) {
|
||||
return in_atomic() || irqs_disabled() || in_interrupt();
|
||||
/* Is in_interrupt() really needed? */
|
||||
|
||||
@@ -23,17 +23,36 @@ 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;
|
||||
|
||||
new_id->stack = stack;
|
||||
new_id->syscall_data_len = 0;
|
||||
new_id->syscall_fd_num = 0;
|
||||
|
||||
scoped_guard(spinlock_irqsave, &mm_list_lock) {
|
||||
/* Insert into list, used for lookups when the child dies */
|
||||
@@ -73,6 +92,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 +104,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 +128,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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -64,3 +65,15 @@ void current_mm_sync(void)
|
||||
|
||||
um_tlb_sync(current->mm);
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(initial_jmpbuf_spinlock);
|
||||
|
||||
void initial_jmpbuf_lock(void)
|
||||
{
|
||||
spin_lock_irq(&initial_jmpbuf_spinlock);
|
||||
}
|
||||
|
||||
void initial_jmpbuf_unlock(void)
|
||||
{
|
||||
spin_unlock_irq(&initial_jmpbuf_spinlock);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2025 Ant Group
|
||||
* Author: Tiwei Bie <tiwei.btw@antgroup.com>
|
||||
*
|
||||
* Based on the previous implementation in TT mode
|
||||
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/processor.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp-internal.h>
|
||||
#include <init.h>
|
||||
#include <kern.h>
|
||||
#include <os.h>
|
||||
#include <smp.h>
|
||||
|
||||
enum {
|
||||
UML_IPI_RES = 0,
|
||||
UML_IPI_CALL_SINGLE,
|
||||
UML_IPI_CALL,
|
||||
UML_IPI_STOP,
|
||||
};
|
||||
|
||||
void arch_smp_send_reschedule(int cpu)
|
||||
{
|
||||
os_send_ipi(cpu, UML_IPI_RES);
|
||||
}
|
||||
|
||||
void arch_send_call_function_single_ipi(int cpu)
|
||||
{
|
||||
os_send_ipi(cpu, UML_IPI_CALL_SINGLE);
|
||||
}
|
||||
|
||||
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_cpu(cpu, mask)
|
||||
os_send_ipi(cpu, UML_IPI_CALL);
|
||||
}
|
||||
|
||||
void smp_send_stop(void)
|
||||
{
|
||||
int cpu, me = smp_processor_id();
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
if (cpu == me)
|
||||
continue;
|
||||
os_send_ipi(cpu, UML_IPI_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipi_handler(int vector, struct uml_pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *old_regs = set_irq_regs((struct pt_regs *)regs);
|
||||
int cpu = raw_smp_processor_id();
|
||||
|
||||
irq_enter();
|
||||
|
||||
if (current->mm)
|
||||
os_alarm_process(current->mm->context.id.pid);
|
||||
|
||||
switch (vector) {
|
||||
case UML_IPI_RES:
|
||||
inc_irq_stat(irq_resched_count);
|
||||
scheduler_ipi();
|
||||
break;
|
||||
|
||||
case UML_IPI_CALL_SINGLE:
|
||||
inc_irq_stat(irq_call_count);
|
||||
generic_smp_call_function_single_interrupt();
|
||||
break;
|
||||
|
||||
case UML_IPI_CALL:
|
||||
inc_irq_stat(irq_call_count);
|
||||
generic_smp_call_function_interrupt();
|
||||
break;
|
||||
|
||||
case UML_IPI_STOP:
|
||||
set_cpu_online(cpu, false);
|
||||
while (1)
|
||||
pause();
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("CPU#%d received unknown IPI (vector=%d)!\n", cpu, vector);
|
||||
break;
|
||||
}
|
||||
|
||||
irq_exit();
|
||||
set_irq_regs(old_regs);
|
||||
}
|
||||
|
||||
void uml_ipi_handler(int vector)
|
||||
{
|
||||
struct uml_pt_regs r = { .is_user = 0 };
|
||||
|
||||
preempt_disable();
|
||||
ipi_handler(vector, &r);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
/* AP states used only during CPU startup */
|
||||
enum {
|
||||
UML_CPU_PAUSED = 0,
|
||||
UML_CPU_RUNNING,
|
||||
};
|
||||
|
||||
static int cpu_states[NR_CPUS];
|
||||
|
||||
static int start_secondary(void *unused)
|
||||
{
|
||||
int err, cpu = raw_smp_processor_id();
|
||||
|
||||
notify_cpu_starting(cpu);
|
||||
set_cpu_online(cpu, true);
|
||||
|
||||
err = um_setup_timer();
|
||||
if (err)
|
||||
panic("CPU#%d failed to setup timer, err = %d", cpu, err);
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uml_start_secondary(void *opaque)
|
||||
{
|
||||
int cpu = raw_smp_processor_id();
|
||||
struct mm_struct *mm = &init_mm;
|
||||
struct task_struct *idle;
|
||||
|
||||
stack_protections((unsigned long) &cpu_irqstacks[cpu]);
|
||||
set_sigstack(&cpu_irqstacks[cpu], THREAD_SIZE);
|
||||
|
||||
set_cpu_present(cpu, true);
|
||||
os_futex_wait(&cpu_states[cpu], UML_CPU_PAUSED);
|
||||
|
||||
smp_rmb(); /* paired with smp_wmb() in __cpu_up() */
|
||||
|
||||
idle = cpu_tasks[cpu];
|
||||
idle->thread_info.cpu = cpu;
|
||||
|
||||
mmgrab(mm);
|
||||
idle->active_mm = mm;
|
||||
|
||||
idle->thread.request.thread.proc = start_secondary;
|
||||
idle->thread.request.thread.arg = NULL;
|
||||
|
||||
new_thread(task_stack_page(idle), &idle->thread.switch_buf,
|
||||
new_thread_handler);
|
||||
os_start_secondary(opaque, &idle->thread.switch_buf);
|
||||
}
|
||||
|
||||
void __init smp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
int err, cpu, me = smp_processor_id();
|
||||
unsigned long deadline;
|
||||
|
||||
os_init_smp();
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpu == me)
|
||||
continue;
|
||||
|
||||
pr_debug("Booting processor %d...\n", cpu);
|
||||
err = os_start_cpu_thread(cpu);
|
||||
if (err) {
|
||||
pr_crit("CPU#%d failed to start cpu thread, err = %d",
|
||||
cpu, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
deadline = jiffies + msecs_to_jiffies(1000);
|
||||
spin_until_cond(cpu_present(cpu) ||
|
||||
time_is_before_jiffies(deadline));
|
||||
|
||||
if (!cpu_present(cpu))
|
||||
pr_crit("CPU#%d failed to boot\n", cpu);
|
||||
}
|
||||
}
|
||||
|
||||
int __cpu_up(unsigned int cpu, struct task_struct *tidle)
|
||||
{
|
||||
cpu_tasks[cpu] = tidle;
|
||||
smp_wmb(); /* paired with smp_rmb() in uml_start_secondary() */
|
||||
cpu_states[cpu] = UML_CPU_RUNNING;
|
||||
os_futex_wake(&cpu_states[cpu]);
|
||||
spin_until_cond(cpu_online(cpu));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init smp_cpus_done(unsigned int max_cpus)
|
||||
{
|
||||
}
|
||||
|
||||
/* Set in uml_ncpus_setup */
|
||||
int uml_ncpus = 1;
|
||||
|
||||
void __init prefill_possible_map(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for (cpu = 0; cpu < uml_ncpus; cpu++)
|
||||
set_cpu_possible(cpu, true);
|
||||
for (; cpu < NR_CPUS; cpu++)
|
||||
set_cpu_possible(cpu, false);
|
||||
}
|
||||
|
||||
static int __init uml_ncpus_setup(char *line, int *add)
|
||||
{
|
||||
*add = 0;
|
||||
|
||||
if (kstrtoint(line, 10, ¨_ncpus)) {
|
||||
os_warn("%s: Couldn't parse '%s'\n", __func__, line);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uml_ncpus = clamp(uml_ncpus, 1, NR_CPUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("ncpus=", uml_ncpus_setup,
|
||||
"ncpus=<# of desired CPUs>\n"
|
||||
" This tells UML how many virtual processors to start. The maximum\n"
|
||||
" number of supported virtual processors can be obtained by querying\n"
|
||||
" the CONFIG_NR_CPUS option using --showconfig.\n\n"
|
||||
);
|
||||
|
||||
EXPORT_SYMBOL(uml_curr_cpu);
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user