diff options
Diffstat (limited to 'arch/um/kernel/smp.c')
-rw-r--r-- | arch/um/kernel/smp.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c new file mode 100644 index 00000000000..72113b0a96e --- /dev/null +++ b/arch/um/kernel/smp.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/percpu.h" +#include "asm/pgalloc.h" +#include "asm/tlb.h" + +/* For some reason, mmu_gathers are referenced when CONFIG_SMP is off. */ +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); + +#ifdef CONFIG_SMP + +#include "linux/sched.h" +#include "linux/module.h" +#include "linux/threads.h" +#include "linux/interrupt.h" +#include "linux/err.h" +#include "linux/hardirq.h" +#include "asm/smp.h" +#include "asm/processor.h" +#include "asm/spinlock.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "irq_user.h" +#include "os.h" + +/* CPU online map, set by smp_boot_cpus */ +cpumask_t cpu_online_map = CPU_MASK_NONE; +cpumask_t cpu_possible_map = CPU_MASK_NONE; + +EXPORT_SYMBOL(cpu_online_map); +EXPORT_SYMBOL(cpu_possible_map); + +/* Per CPU bogomips and other parameters + * The only piece used here is the ipi pipe, which is set before SMP is + * started and never changed. + */ +struct cpuinfo_um cpu_data[NR_CPUS]; + +/* A statistic, can be a little off */ +int num_reschedules_sent = 0; + +/* Not changed after boot */ +struct task_struct *idle_threads[NR_CPUS]; + +void smp_send_reschedule(int cpu) +{ + os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1); + num_reschedules_sent++; +} + +void smp_send_stop(void) +{ + int i; + + printk(KERN_INFO "Stopping all CPUs..."); + for(i = 0; i < num_online_cpus(); i++){ + if(i == current_thread->cpu) + continue; + os_write_file(cpu_data[i].ipi_pipe[1], "S", 1); + } + printk("done\n"); +} + +static cpumask_t smp_commenced_mask = CPU_MASK_NONE; +static cpumask_t cpu_callin_map = CPU_MASK_NONE; + +static int idle_proc(void *cpup) +{ + int cpu = (int) cpup, err; + + err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1); + if(err < 0) + panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err); + + activate_ipi(cpu_data[cpu].ipi_pipe[0], + current->thread.mode.tt.extern_pid); + + wmb(); + if (cpu_test_and_set(cpu, cpu_callin_map)) { + printk("huh, CPU#%d already present??\n", cpu); + BUG(); + } + + while (!cpu_isset(cpu, smp_commenced_mask)) + cpu_relax(); + + cpu_set(cpu, cpu_online_map); + default_idle(); + return(0); +} + +static struct task_struct *idle_thread(int cpu) +{ + struct task_struct *new_task; + unsigned char c; + + current->thread.request.u.thread.proc = idle_proc; + current->thread.request.u.thread.arg = (void *) cpu; + new_task = fork_idle(cpu); + if(IS_ERR(new_task)) + panic("copy_process failed in idle_thread, error = %ld", + PTR_ERR(new_task)); + + cpu_tasks[cpu] = ((struct cpu_task) + { .pid = new_task->thread.mode.tt.extern_pid, + .task = new_task } ); + idle_threads[cpu] = new_task; + CHOOSE_MODE(os_write_file(new_task->thread.mode.tt.switch_pipe[1], &c, + sizeof(c)), + ({ panic("skas mode doesn't support SMP"); })); + return(new_task); +} + +void smp_prepare_cpus(unsigned int maxcpus) +{ + struct task_struct *idle; + unsigned long waittime; + int err, cpu, me = smp_processor_id(); + int i; + + for (i = 0; i < ncpus; ++i) + cpu_set(i, cpu_possible_map); + + cpu_clear(me, cpu_online_map); + cpu_set(me, cpu_online_map); + cpu_set(me, cpu_callin_map); + + err = os_pipe(cpu_data[me].ipi_pipe, 1, 1); + if(err < 0) + panic("CPU#0 failed to create IPI pipe, errno = %d", -err); + + activate_ipi(cpu_data[me].ipi_pipe[0], + current->thread.mode.tt.extern_pid); + + for(cpu = 1; cpu < ncpus; cpu++){ + printk("Booting processor %d...\n", cpu); + + idle = idle_thread(cpu); + + init_idle(idle, cpu); + unhash_process(idle); + + waittime = 200000000; + while (waittime-- && !cpu_isset(cpu, cpu_callin_map)) + cpu_relax(); + + if (cpu_isset(cpu, cpu_callin_map)) + printk("done\n"); + else printk("failed\n"); + } +} + +void smp_prepare_boot_cpu(void) +{ + cpu_set(smp_processor_id(), cpu_online_map); +} + +int __cpu_up(unsigned int cpu) +{ + cpu_set(cpu, smp_commenced_mask); + while (!cpu_isset(cpu, cpu_online_map)) + mb(); + return(0); +} + +int setup_profiling_timer(unsigned int multiplier) +{ + printk(KERN_INFO "setup_profiling_timer\n"); + return(0); +} + +void smp_call_function_slave(int cpu); + +void IPI_handler(int cpu) +{ + unsigned char c; + int fd; + + fd = cpu_data[cpu].ipi_pipe[0]; + while (os_read_file(fd, &c, 1) == 1) { + switch (c) { + case 'C': + smp_call_function_slave(cpu); + break; + + case 'R': + set_tsk_need_resched(current); + break; + + case 'S': + printk("CPU#%d stopping\n", cpu); + while(1) + pause(); + break; + + default: + printk("CPU#%d received unknown IPI [%c]!\n", cpu, c); + break; + } + } +} + +int hard_smp_processor_id(void) +{ + return(pid_to_processor_id(os_getpid())); +} + +static DEFINE_SPINLOCK(call_lock); +static atomic_t scf_started; +static atomic_t scf_finished; +static void (*func)(void *info); +static void *info; + +void smp_call_function_slave(int cpu) +{ + atomic_inc(&scf_started); + (*func)(info); + atomic_inc(&scf_finished); +} + +int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, + int wait) +{ + int cpus = num_online_cpus() - 1; + int i; + + if (!cpus) + return 0; + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + spin_lock_bh(&call_lock); + atomic_set(&scf_started, 0); + atomic_set(&scf_finished, 0); + func = _func; + info = _info; + + for_each_online_cpu(i) + os_write_file(cpu_data[i].ipi_pipe[1], "C", 1); + + while (atomic_read(&scf_started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&scf_finished) != cpus) + barrier(); + + spin_unlock_bh(&call_lock); + return 0; +} + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ |