* The present bitmask indicates that the CPU is physically present.
* The online bitmask indicates that the CPU is up and running.
*/
-cpumask_t cpu_present_mask;
+cpumask_t cpu_possible_map;
cpumask_t cpu_online_map;
/*
static struct smp_call_struct * volatile smp_call_function_data;
static DEFINE_SPINLOCK(smp_call_function_lock);
-int __init __cpu_up(unsigned int cpu)
+int __cpuinit __cpu_up(unsigned int cpu)
{
- struct task_struct *idle;
+ struct cpuinfo_arm *ci = &per_cpu(cpu_data, cpu);
+ struct task_struct *idle = ci->idle;
pgd_t *pgd;
pmd_t *pmd;
int ret;
/*
- * Spawn a new process manually. Grab a pointer to
- * its task struct so we can mess with it
+ * Spawn a new process manually, if not already done.
+ * Grab a pointer to its task struct so we can mess with it
*/
- idle = fork_idle(cpu);
- if (IS_ERR(idle)) {
- printk(KERN_ERR "CPU%u: fork() failed\n", cpu);
- return PTR_ERR(idle);
+ if (!idle) {
+ idle = fork_idle(cpu);
+ if (IS_ERR(idle)) {
+ printk(KERN_ERR "CPU%u: fork() failed\n", cpu);
+ return PTR_ERR(idle);
+ }
+ ci->idle = idle;
}
/*
* We need to tell the secondary core where to find
* its stack and the page tables.
*/
- secondary_data.stack = (void *)idle->thread_info + THREAD_SIZE - 8;
+ secondary_data.stack = (void *)idle->thread_info + THREAD_START_SP;
secondary_data.pgdir = virt_to_phys(pgd);
wmb();
ret = -EIO;
}
- secondary_data.stack = 0;
+ secondary_data.stack = NULL;
secondary_data.pgdir = 0;
*pmd_offset(pgd, PHYS_OFFSET) = __pmd(0);
return ret;
}
+#ifdef CONFIG_HOTPLUG_CPU
+/*
+ * __cpu_disable runs on the processor to be shutdown.
+ */
+int __cpuexit __cpu_disable(void)
+{
+ unsigned int cpu = smp_processor_id();
+ struct task_struct *p;
+ int ret;
+
+ ret = mach_cpu_disable(cpu);
+ if (ret)
+ return ret;
+
+ /*
+ * Take this CPU offline. Once we clear this, we can't return,
+ * and we must not schedule until we're ready to give up the cpu.
+ */
+ cpu_clear(cpu, cpu_online_map);
+
+ /*
+ * OK - migrate IRQs away from this CPU
+ */
+ migrate_irqs();
+
+ /*
+ * Flush user cache and TLB mappings, and then remove this CPU
+ * from the vm mask set of all processes.
+ */
+ flush_cache_all();
+ local_flush_tlb_all();
+
+ read_lock(&tasklist_lock);
+ for_each_process(p) {
+ if (p->mm)
+ cpu_clear(cpu, p->mm->cpu_vm_mask);
+ }
+ read_unlock(&tasklist_lock);
+
+ return 0;
+}
+
+/*
+ * called on the thread which is asking for a CPU to be shutdown -
+ * waits until shutdown has completed, or it is timed out.
+ */
+void __cpuexit __cpu_die(unsigned int cpu)
+{
+ if (!platform_cpu_kill(cpu))
+ printk("CPU%u: unable to kill\n", cpu);
+}
+
+/*
+ * Called from the idle thread for the CPU which has been shutdown.
+ *
+ * Note that we disable IRQs here, but do not re-enable them
+ * before returning to the caller. This is also the behaviour
+ * of the other hotplug-cpu capable cores, so presumably coming
+ * out of idle fixes this.
+ */
+void __cpuexit cpu_die(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ local_irq_disable();
+ idle_task_exit();
+
+ /*
+ * actual CPU shutdown procedure is at least platform (if not
+ * CPU) specific
+ */
+ platform_cpu_die(cpu);
+
+ /*
+ * Do not return to the idle loop - jump back to the secondary
+ * cpu initialisation. There's some initialisation which needs
+ * to be repeated to undo the effects of taking the CPU offline.
+ */
+ __asm__("mov sp, %0\n"
+ " b secondary_start_kernel"
+ :
+ : "r" ((void *)current->thread_info + THREAD_SIZE - 8));
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
*/
-asmlinkage void __init secondary_start_kernel(void)
+asmlinkage void __cpuinit secondary_start_kernel(void)
{
struct mm_struct *mm = &init_mm;
unsigned int cpu = smp_processor_id();
cpu_set(cpu, mm->cpu_vm_mask);
cpu_switch_mm(mm->pgd, mm);
enter_lazy_tlb(mm, current);
+ local_flush_tlb_all();
cpu_init();
* Called by both boot and secondaries to move global data into
* per-processor storage.
*/
-void __init smp_store_cpu_info(unsigned int cpuid)
+void __cpuinit smp_store_cpu_info(unsigned int cpuid)
{
struct cpuinfo_arm *cpu_info = &per_cpu(cpu_data, cpuid);
{
unsigned int cpu = smp_processor_id();
- cpu_set(cpu, cpu_present_mask);
+ per_cpu(cpu_data, cpu).idle = current;
+
+ cpu_set(cpu, cpu_possible_map);
+ cpu_set(cpu, cpu_present_map);
cpu_set(cpu, cpu_online_map);
}
* You must not call this function with disabled interrupts, from a
* hardware interrupt handler, nor from a bottom half handler.
*/
-int smp_call_function_on_cpu(void (*func)(void *info), void *info, int retry,
- int wait, cpumask_t callmap)
+static int smp_call_function_on_cpu(void (*func)(void *info), void *info,
+ int retry, int wait, cpumask_t callmap)
{
struct smp_call_struct data;
unsigned long timeout;
printk(KERN_CRIT
"CPU%u: smp_call_function timeout for %p(%p)\n"
" callmap %lx pending %lx, %swait\n",
- smp_processor_id(), func, info, callmap, data.pending,
- wait ? "" : "no ");
+ smp_processor_id(), func, info, *cpus_addr(callmap),
+ *cpus_addr(data.pending), wait ? "" : "no ");
/*
* TRACE
seq_puts(p, "IPI:");
- for_each_online_cpu(cpu)
+ for_each_present_cpu(cpu)
seq_printf(p, " %10lu", per_cpu(ipi_data, cpu).ipi_count);
seq_putc(p, '\n');
{
return -EINVAL;
}
+
+static int
+on_each_cpu_mask(void (*func)(void *), void *info, int retry, int wait,
+ cpumask_t mask)
+{
+ int ret = 0;
+
+ preempt_disable();
+
+ ret = smp_call_function_on_cpu(func, info, retry, wait, mask);
+ if (cpu_isset(smp_processor_id(), mask))
+ func(info);
+
+ preempt_enable();
+
+ return ret;
+}
+
+/**********************************************************************/
+
+/*
+ * TLB operations
+ */
+struct tlb_args {
+ struct vm_area_struct *ta_vma;
+ unsigned long ta_start;
+ unsigned long ta_end;
+};
+
+static inline void ipi_flush_tlb_all(void *ignored)
+{
+ local_flush_tlb_all();
+}
+
+static inline void ipi_flush_tlb_mm(void *arg)
+{
+ struct mm_struct *mm = (struct mm_struct *)arg;
+
+ local_flush_tlb_mm(mm);
+}
+
+static inline void ipi_flush_tlb_page(void *arg)
+{
+ struct tlb_args *ta = (struct tlb_args *)arg;
+
+ local_flush_tlb_page(ta->ta_vma, ta->ta_start);
+}
+
+static inline void ipi_flush_tlb_kernel_page(void *arg)
+{
+ struct tlb_args *ta = (struct tlb_args *)arg;
+
+ local_flush_tlb_kernel_page(ta->ta_start);
+}
+
+static inline void ipi_flush_tlb_range(void *arg)
+{
+ struct tlb_args *ta = (struct tlb_args *)arg;
+
+ local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
+}
+
+static inline void ipi_flush_tlb_kernel_range(void *arg)
+{
+ struct tlb_args *ta = (struct tlb_args *)arg;
+
+ local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
+}
+
+void flush_tlb_all(void)
+{
+ on_each_cpu(ipi_flush_tlb_all, NULL, 1, 1);
+}
+
+void flush_tlb_mm(struct mm_struct *mm)
+{
+ cpumask_t mask = mm->cpu_vm_mask;
+
+ on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, 1, mask);
+}
+
+void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
+{
+ cpumask_t mask = vma->vm_mm->cpu_vm_mask;
+ struct tlb_args ta;
+
+ ta.ta_vma = vma;
+ ta.ta_start = uaddr;
+
+ on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, 1, mask);
+}
+
+void flush_tlb_kernel_page(unsigned long kaddr)
+{
+ struct tlb_args ta;
+
+ ta.ta_start = kaddr;
+
+ on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1, 1);
+}
+
+void flush_tlb_range(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
+{
+ cpumask_t mask = vma->vm_mm->cpu_vm_mask;
+ struct tlb_args ta;
+
+ ta.ta_vma = vma;
+ ta.ta_start = start;
+ ta.ta_end = end;
+
+ on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, 1, mask);
+}
+
+void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+ struct tlb_args ta;
+
+ ta.ta_start = start;
+ ta.ta_end = end;
+
+ on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1, 1);
+}