diff options
Diffstat (limited to 'arch/um/kernel')
-rw-r--r-- | arch/um/kernel/early_printk.c | 5 | ||||
-rw-r--r-- | arch/um/kernel/exec.c | 2 | ||||
-rw-r--r-- | arch/um/kernel/irq.c | 9 | ||||
-rw-r--r-- | arch/um/kernel/maccess.c | 5 | ||||
-rw-r--r-- | arch/um/kernel/mem.c | 22 | ||||
-rw-r--r-- | arch/um/kernel/process.c | 42 | ||||
-rw-r--r-- | arch/um/kernel/ptrace.c | 7 | ||||
-rw-r--r-- | arch/um/kernel/skas/Makefile | 2 | ||||
-rw-r--r-- | arch/um/kernel/skas/mmu.c | 2 | ||||
-rw-r--r-- | arch/um/kernel/skas/syscall.c | 11 | ||||
-rw-r--r-- | arch/um/kernel/stacktrace.c | 5 | ||||
-rw-r--r-- | arch/um/kernel/sysrq.c | 5 | ||||
-rw-r--r-- | arch/um/kernel/time.c | 131 | ||||
-rw-r--r-- | arch/um/kernel/tlb.c | 4 | ||||
-rw-r--r-- | arch/um/kernel/trap.c | 16 |
15 files changed, 200 insertions, 68 deletions
diff --git a/arch/um/kernel/early_printk.c b/arch/um/kernel/early_printk.c index 4a0800bc37b2..c350c2331bbe 100644 --- a/arch/um/kernel/early_printk.c +++ b/arch/um/kernel/early_printk.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2011 Richard Weinberger <richrd@nod.at> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/kernel.h> diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c index a43d42bf0a86..783b9247161f 100644 --- a/arch/um/kernel/exec.c +++ b/arch/um/kernel/exec.c @@ -32,7 +32,7 @@ void flush_thread(void) if (ret) { printk(KERN_ERR "flush_thread - clearing address space failed, " "err = %d\n", ret); - force_sig(SIGKILL, current); + force_sig(SIGKILL); } get_safe_registers(current_pt_regs()->regs.gp, current_pt_regs()->regs.fp); diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index 598d7b3d9355..efde1f16c603 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -21,6 +21,8 @@ #include <irq_user.h> +extern void free_irqs(void); + /* When epoll triggers we do not know why it did so * we can also have different IRQs for read and write. * This is why we keep a small irq_fd array for each fd - @@ -100,6 +102,8 @@ void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) } } } + + free_irqs(); } static int assign_epoll_events_to_irq(struct irq_entry *irq_entry) @@ -380,10 +384,8 @@ EXPORT_SYMBOL(deactivate_fd); */ int deactivate_all_fds(void) { - unsigned long flags; struct irq_entry *to_free; - spin_lock_irqsave(&irq_lock, flags); /* Stop IO. The IRQ loop has no lock so this is our * only way of making sure we are safe to dispose * of all IRQ handlers @@ -399,8 +401,7 @@ int deactivate_all_fds(void) ); to_free = to_free->next; } - garbage_collect_irq_entries(); - spin_unlock_irqrestore(&irq_lock, flags); + /* don't garbage collect - we can no longer call kfree() here */ os_close_epoll_fd(); return 0; } diff --git a/arch/um/kernel/maccess.c b/arch/um/kernel/maccess.c index 1f3d5c4910d1..67b2e0fa92bb 100644 --- a/arch/um/kernel/maccess.c +++ b/arch/um/kernel/maccess.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2013 Richard Weinberger <richrd@nod.at> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/uaccess.h> diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index a9c9a94c096f..de58e976b9bc 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -208,28 +208,6 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd) free_page((unsigned long) pgd); } -pte_t *pte_alloc_one_kernel(struct mm_struct *mm) -{ - pte_t *pte; - - pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_ZERO); - return pte; -} - -pgtable_t pte_alloc_one(struct mm_struct *mm) -{ - struct page *pte; - - pte = alloc_page(GFP_KERNEL|__GFP_ZERO); - if (!pte) - return NULL; - if (!pgtable_page_ctor(pte)) { - __free_page(pte); - return NULL; - } - return pte; -} - #ifdef CONFIG_3_LEVEL_PGTABLES pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) { diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index 691b83b10649..67c0d1a860e9 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -203,10 +203,50 @@ void initial_thread_cb(void (*proc)(void *), void *arg) kmalloc_ok = save_kmalloc_ok; } +static void time_travel_sleep(unsigned long long duration) +{ + unsigned long long next = time_travel_time + duration; + + if (time_travel_mode != TT_MODE_INFCPU) + os_timer_disable(); + + if (time_travel_timer_mode != TT_TMR_DISABLED || + time_travel_timer_expiry < next) { + if (time_travel_timer_mode == TT_TMR_ONESHOT) + time_travel_set_timer(TT_TMR_DISABLED, 0); + /* + * time_travel_time will be adjusted in the timer + * IRQ handler so it works even when the signal + * comes from the OS timer + */ + deliver_alarm(); + } else { + time_travel_set_time(next); + } + + if (time_travel_mode != TT_MODE_INFCPU) { + if (time_travel_timer_mode == TT_TMR_PERIODIC) + os_timer_set_interval(time_travel_timer_interval); + else if (time_travel_timer_mode == TT_TMR_ONESHOT) + os_timer_one_shot(time_travel_timer_expiry - next); + } +} + +static void um_idle_sleep(void) +{ + unsigned long long duration = UM_NSEC_PER_SEC; + + if (time_travel_mode != TT_MODE_OFF) { + time_travel_sleep(duration); + } else { + os_idle_sleep(duration); + } +} + void arch_cpu_idle(void) { cpu_tasks[current_thread_info()->cpu].pid = os_getpid(); - os_idle_sleep(UM_NSEC_PER_SEC); + um_idle_sleep(); local_irq_enable(); } diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 5f47422401e1..da1e96b1ec3e 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -112,13 +112,12 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } -static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs, - int error_code) +static void send_sigtrap(struct uml_pt_regs *regs, int error_code) { /* Send us the fake SIGTRAP */ force_sig_fault(SIGTRAP, TRAP_BRKPT, /* User-mode eip? */ - UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL, tsk); + UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL); } /* @@ -147,7 +146,7 @@ void syscall_trace_leave(struct pt_regs *regs) /* Fake a debug trap */ if (ptraced & PT_DTRACE) - send_sigtrap(current, ®s->regs, 0); + send_sigtrap(®s->regs, 0); if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; diff --git a/arch/um/kernel/skas/Makefile b/arch/um/kernel/skas/Makefile index 0b76d8869c94..5bd3edfcfedf 100644 --- a/arch/um/kernel/skas/Makefile +++ b/arch/um/kernel/skas/Makefile @@ -12,4 +12,6 @@ obj-y := clone.o mmu.o process.o syscall.o uaccess.o CFLAGS_clone.o := $(CFLAGS_NO_HARDENING) UNPROFILE_OBJS := clone.o +KCOV_INSTRUMENT := n + include arch/um/scripts/Makefile.rules diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 7a1f2a936fd1..29e7f5f9f188 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -119,7 +119,7 @@ void uml_setup_stubs(struct mm_struct *mm) return; out: - force_sigsegv(SIGSEGV, current); + force_sigsegv(SIGSEGV); } void arch_exit_mmap(struct mm_struct *mm) diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c index b783ac87d98a..44bb10785075 100644 --- a/arch/um/kernel/skas/syscall.c +++ b/arch/um/kernel/skas/syscall.c @@ -10,12 +10,23 @@ #include <sysdep/ptrace.h> #include <sysdep/ptrace_user.h> #include <sysdep/syscalls.h> +#include <shared/timer-internal.h> void handle_syscall(struct uml_pt_regs *r) { struct pt_regs *regs = container_of(r, struct pt_regs, regs); int syscall; + /* + * If we have infinite CPU resources, then make every syscall also a + * preemption point, since we don't have any other preemption in this + * case, and kernel threads would basically never run until userspace + * went to sleep, even if said userspace interacts with the kernel in + * various ways. + */ + if (time_travel_mode == TT_MODE_INFCPU) + schedule(); + /* Initialize the syscall number and default return value. */ UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp); PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS); diff --git a/arch/um/kernel/stacktrace.c b/arch/um/kernel/stacktrace.c index bd95e020d509..86df52168bd9 100644 --- a/arch/um/kernel/stacktrace.c +++ b/arch/um/kernel/stacktrace.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Copyright (C) 2013 Richard Weinberger <richard@nod.at> * Copyright (C) 2014 Google Inc., Author: Daniel Walter <dwalter@google.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/kallsyms.h> diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c index 05585eef11d9..c71b5ef7ea8c 100644 --- a/arch/um/kernel/sysrq.c +++ b/arch/um/kernel/sysrq.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Copyright (C) 2013 Richard Weinberger <richrd@nod.at> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/kallsyms.h> diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c index 0c572a48158e..6a051b078359 100644 --- a/arch/um/kernel/time.c +++ b/arch/um/kernel/time.c @@ -19,11 +19,29 @@ #include <kern_util.h> #include <os.h> #include <timer-internal.h> +#include <shared/init.h> + +#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT +enum time_travel_mode time_travel_mode; +unsigned long long time_travel_time; +enum time_travel_timer_mode time_travel_timer_mode; +unsigned long long time_travel_timer_expiry; +unsigned long long time_travel_timer_interval; + +static bool time_travel_start_set; +static unsigned long long time_travel_start; +#else +#define time_travel_start_set 0 +#define time_travel_start 0 +#endif void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) { unsigned long flags; + if (time_travel_mode != TT_MODE_OFF) + time_travel_set_time(time_travel_timer_expiry); + local_irq_save(flags); do_IRQ(TIMER_IRQ, regs); local_irq_restore(flags); @@ -31,26 +49,47 @@ void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) static int itimer_shutdown(struct clock_event_device *evt) { - os_timer_disable(); + if (time_travel_mode != TT_MODE_OFF) + time_travel_set_timer(TT_TMR_DISABLED, 0); + + if (time_travel_mode != TT_MODE_INFCPU) + os_timer_disable(); + return 0; } static int itimer_set_periodic(struct clock_event_device *evt) { - os_timer_set_interval(NULL, NULL); + unsigned long long interval = NSEC_PER_SEC / HZ; + + if (time_travel_mode != TT_MODE_OFF) + time_travel_set_timer(TT_TMR_PERIODIC, + time_travel_time + interval); + + if (time_travel_mode != TT_MODE_INFCPU) + os_timer_set_interval(interval); + return 0; } static int itimer_next_event(unsigned long delta, struct clock_event_device *evt) { - return os_timer_one_shot(delta); + delta += 1; + + if (time_travel_mode != TT_MODE_OFF) + time_travel_set_timer(TT_TMR_ONESHOT, + time_travel_time + delta); + + if (time_travel_mode != TT_MODE_INFCPU) + return os_timer_one_shot(delta); + + return 0; } static int itimer_one_shot(struct clock_event_device *evt) { - os_timer_one_shot(1); - return 0; + return itimer_next_event(0, evt); } static struct clock_event_device timer_clockevent = { @@ -87,6 +126,17 @@ static irqreturn_t um_timer(int irq, void *dev) static u64 timer_read(struct clocksource *cs) { + if (time_travel_mode != TT_MODE_OFF) { + /* + * We make reading the timer cost a bit so that we don't get + * stuck in loops that expect time to move more than the + * exact requested sleep amount, e.g. python's socket server, + * see https://bugs.python.org/issue37026. + */ + time_travel_set_time(time_travel_time + TIMER_MULTIPLIER); + return time_travel_time / TIMER_MULTIPLIER; + } + return os_nsecs() / TIMER_MULTIPLIER; } @@ -107,7 +157,7 @@ static void __init um_timer_setup(void) printk(KERN_ERR "register_timer : request_irq failed - " "errno = %d\n", -err); - err = os_timer_create(NULL); + err = os_timer_create(); if (err != 0) { printk(KERN_ERR "creation of timer failed - errno = %d\n", -err); return; @@ -123,7 +173,12 @@ static void __init um_timer_setup(void) void read_persistent_clock64(struct timespec64 *ts) { - long long nsecs = os_persistent_clock_emulation(); + long long nsecs; + + if (time_travel_start_set) + nsecs = time_travel_start + time_travel_time; + else + nsecs = os_persistent_clock_emulation(); set_normalized_timespec64(ts, nsecs / NSEC_PER_SEC, nsecs % NSEC_PER_SEC); @@ -134,3 +189,65 @@ void __init time_init(void) timer_set_signal_handler(); late_time_init = um_timer_setup; } + +#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT +unsigned long calibrate_delay_is_known(void) +{ + if (time_travel_mode == TT_MODE_INFCPU) + return 1; + return 0; +} + +int setup_time_travel(char *str) +{ + if (strcmp(str, "=inf-cpu") == 0) { + time_travel_mode = TT_MODE_INFCPU; + timer_clockevent.name = "time-travel-timer-infcpu"; + timer_clocksource.name = "time-travel-clock"; + return 1; + } + + if (!*str) { + time_travel_mode = TT_MODE_BASIC; + timer_clockevent.name = "time-travel-timer"; + timer_clocksource.name = "time-travel-clock"; + return 1; + } + + return -EINVAL; +} + +__setup("time-travel", setup_time_travel); +__uml_help(setup_time_travel, +"time-travel\n" +"This option just enables basic time travel mode, in which the clock/timers\n" +"inside the UML instance skip forward when there's nothing to do, rather than\n" +"waiting for real time to elapse. However, instance CPU speed is limited by\n" +"the real CPU speed, so e.g. a 10ms timer will always fire after ~10ms wall\n" +"clock (but quicker when there's nothing to do).\n" +"\n" +"time-travel=inf-cpu\n" +"This enables time travel mode with infinite processing power, in which there\n" +"are no wall clock timers, and any CPU processing happens - as seen from the\n" +"guest - instantly. This can be useful for accurate simulation regardless of\n" +"debug overhead, physical CPU speed, etc. but is somewhat dangerous as it can\n" +"easily lead to getting stuck (e.g. if anything in the system busy loops).\n"); + +int setup_time_travel_start(char *str) +{ + int err; + + err = kstrtoull(str, 0, &time_travel_start); + if (err) + return err; + + time_travel_start_set = 1; + return 1; +} + +__setup("time-travel-start", setup_time_travel_start); +__uml_help(setup_time_travel_start, +"time-travel-start=<seconds>\n" +"Configure the UML instance's wall clock to start at this value rather than\n" +"the host's wall clock at the time of UML boot.\n"); +#endif diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c index 8347161c2ae0..45f739bf302f 100644 --- a/arch/um/kernel/tlb.c +++ b/arch/um/kernel/tlb.c @@ -329,7 +329,7 @@ void fix_range_common(struct mm_struct *mm, unsigned long start_addr, "process: %d\n", task_tgid_vnr(current)); /* We are under mmap_sem, release it such that current can terminate */ up_write(¤t->mm->mmap_sem); - force_sig(SIGKILL, current); + force_sig(SIGKILL); do_signal(¤t->thread.regs); } } @@ -487,7 +487,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) kill: printk(KERN_ERR "Failed to flush page for address 0x%lx\n", address); - force_sig(SIGKILL, current); + force_sig(SIGKILL); } pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index 0e8b6158f224..58fe36856182 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -163,13 +163,12 @@ static void show_segv_info(struct uml_pt_regs *regs) static void bad_segv(struct faultinfo fi, unsigned long ip) { current->thread.arch.faultinfo = fi; - force_sig_fault(SIGSEGV, SEGV_ACCERR, (void __user *) FAULT_ADDRESS(fi), - current); + force_sig_fault(SIGSEGV, SEGV_ACCERR, (void __user *) FAULT_ADDRESS(fi)); } void fatal_sigsegv(void) { - force_sigsegv(SIGSEGV, current); + force_sigsegv(SIGSEGV); do_signal(¤t->thread.regs); /* * This is to tell gcc that we're not returning - do_signal @@ -268,13 +267,11 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, if (err == -EACCES) { current->thread.arch.faultinfo = fi; - force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address, - current); + force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address); } else { BUG_ON(err != -EFAULT); current->thread.arch.faultinfo = fi; - force_sig_fault(SIGSEGV, si_code, (void __user *) address, - current); + force_sig_fault(SIGSEGV, si_code, (void __user *) address); } out: @@ -304,12 +301,11 @@ void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs) if ((err == 0) && (siginfo_layout(sig, code) == SIL_FAULT)) { struct faultinfo *fi = UPT_FAULTINFO(regs); current->thread.arch.faultinfo = *fi; - force_sig_fault(sig, code, (void __user *)FAULT_ADDRESS(*fi), - current); + force_sig_fault(sig, code, (void __user *)FAULT_ADDRESS(*fi)); } else { printk(KERN_ERR "Attempted to relay unknown signal %d (si_code = %d) with errno %d\n", sig, code, err); - force_sig(sig, current); + force_sig(sig); } } |