}
EXPORT_SYMBOL(smp_call_function_single);
+/**
+ * smp_call_function_mask(): Run a function on a set of other CPUs.
+ * @mask: The set of cpus to run on. Must not include the current cpu.
+ * @func: The function to run. This must be fast and non-blocking.
+ * @info: An arbitrary pointer to pass to the function.
+ * @wait: If true, wait (atomically) until function has completed on other CPUs.
+ *
+ * Returns 0 on success, else a negative status code.
+ *
+ * If @wait is true, then returns once @func has returned; otherwise
+ * it returns just before the target cpu calls @func.
+ *
+ * You must not call this function with disabled interrupts or from a
+ * hardware interrupt handler or from a bottom half handler.
+ */
+int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info,
+ int wait)
+{
+ preempt_disable();
+ cpu_clear(smp_processor_id(), mask);
+ __smp_call_function_map(func, info, 0, wait, mask);
+ preempt_enable();
+ return 0;
+}
+EXPORT_SYMBOL(smp_call_function_mask);
+
void smp_send_stop(void)
{
int cpu, rc;
}
}
-/*
- * Reboot, halt and power_off routines for SMP.
- */
-void machine_restart_smp(char *__unused)
-{
- smp_send_stop();
- do_reipl();
-}
-
-void machine_halt_smp(void)
-{
- smp_send_stop();
- if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
- __cpcmd(vmhalt_cmd, NULL, 0, NULL);
- signal_processor(smp_processor_id(), sigp_stop_and_store_status);
- for (;;);
-}
-
-void machine_power_off_smp(void)
-{
- smp_send_stop();
- if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
- __cpcmd(vmpoff_cmd, NULL, 0, NULL);
- signal_processor(smp_processor_id(), sigp_stop_and_store_status);
- for (;;);
-}
-
/*
* This is the main routine where commands issued by other
* cpus are handled.
out:
kfree(info);
printk(KERN_INFO "CPUs: %d configured, %d standby\n", c_cpus, s_cpus);
- lock_cpu_hotplug();
+ get_online_cpus();
smp_rescan_cpus();
- unlock_cpu_hotplug();
+ put_online_cpus();
}
/*
spin_lock_init(&(&per_cpu(s390_idle, cpu))->lock);
}
+static int __cpuinit smp_alloc_lowcore(int cpu)
+{
+ unsigned long async_stack, panic_stack;
+ struct _lowcore *lowcore;
+ int lc_order;
+
+ lc_order = sizeof(long) == 8 ? 1 : 0;
+ lowcore = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, lc_order);
+ if (!lowcore)
+ return -ENOMEM;
+ async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
+ panic_stack = __get_free_page(GFP_KERNEL);
+ if (!panic_stack || !async_stack)
+ goto out;
+ /*
+ * Only need to copy the first 512 bytes from address 0. But since
+ * the compiler emits a warning if src == NULL for memcpy use copy_page
+ * instead. Copies more than needed but this code is not performance
+ * critical.
+ */
+ copy_page(lowcore, &S390_lowcore);
+ memset((void *)lowcore + 512, 0, sizeof(*lowcore) - 512);
+ lowcore->async_stack = async_stack + ASYNC_SIZE;
+ lowcore->panic_stack = panic_stack + PAGE_SIZE;
+
+#ifndef CONFIG_64BIT
+ if (MACHINE_HAS_IEEE) {
+ unsigned long save_area;
+
+ save_area = get_zeroed_page(GFP_KERNEL);
+ if (!save_area)
+ goto out_save_area;
+ lowcore->extended_save_area_addr = (u32) save_area;
+ }
+#endif
+ lowcore_ptr[cpu] = lowcore;
+ return 0;
+
+#ifndef CONFIG_64BIT
+out_save_area:
+ free_page(panic_stack);
+#endif
+out:
+ free_pages(async_stack, ASYNC_ORDER);
+ free_pages((unsigned long) lowcore, lc_order);
+ return -ENOMEM;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static void smp_free_lowcore(int cpu)
+{
+ struct _lowcore *lowcore;
+ int lc_order;
+
+ lc_order = sizeof(long) == 8 ? 1 : 0;
+ lowcore = lowcore_ptr[cpu];
+#ifndef CONFIG_64BIT
+ if (MACHINE_HAS_IEEE)
+ free_page((unsigned long) lowcore->extended_save_area_addr);
+#endif
+ free_page(lowcore->panic_stack - PAGE_SIZE);
+ free_pages(lowcore->async_stack - ASYNC_SIZE, ASYNC_ORDER);
+ free_pages((unsigned long) lowcore, lc_order);
+ lowcore_ptr[cpu] = NULL;
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
/* Upping and downing of CPUs */
-int __cpu_up(unsigned int cpu)
+int __cpuinit __cpu_up(unsigned int cpu)
{
struct task_struct *idle;
struct _lowcore *cpu_lowcore;
if (smp_cpu_state[cpu] != CPU_STATE_CONFIGURED)
return -EIO;
+ if (smp_alloc_lowcore(cpu))
+ return -ENOMEM;
ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]),
cpu, sigp_set_prefix);
cpu_lowcore = lowcore_ptr[cpu];
cpu_lowcore->kernel_stack = (unsigned long)
task_stack_page(idle) + THREAD_SIZE;
+ cpu_lowcore->thread_info = (unsigned long) task_thread_info(idle);
sf = (struct stack_frame *) (cpu_lowcore->kernel_stack
- sizeof(struct pt_regs)
- sizeof(struct stack_frame));
cpu_lowcore->percpu_offset = __per_cpu_offset[cpu];
cpu_lowcore->current_task = (unsigned long) idle;
cpu_lowcore->cpu_data.cpu_nr = cpu;
+ cpu_lowcore->kernel_asce = S390_lowcore.kernel_asce;
+ cpu_lowcore->ipl_device = S390_lowcore.ipl_device;
eieio();
while (signal_processor(cpu, sigp_restart) == sigp_busy)
/* Wait until target cpu is down */
while (!smp_cpu_not_running(cpu))
cpu_relax();
+ smp_free_lowcore(cpu);
printk(KERN_INFO "Processor %d spun down\n", cpu);
}
#endif /* CONFIG_HOTPLUG_CPU */
-/*
- * Cycle through the processors and setup structures.
- */
-
void __init smp_prepare_cpus(unsigned int max_cpus)
{
- unsigned long stack;
+#ifndef CONFIG_64BIT
+ unsigned long save_area = 0;
+#endif
+ unsigned long async_stack, panic_stack;
+ struct _lowcore *lowcore;
unsigned int cpu;
- int i;
+ int lc_order;
smp_detect_cpus();
/* request the 0x1201 emergency signal external interrupt */
if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0)
panic("Couldn't request external interrupt 0x1201");
- memset(lowcore_ptr, 0, sizeof(lowcore_ptr));
- /*
- * Initialize prefix pages and stacks for all possible cpus
- */
print_cpu_info(&S390_lowcore.cpu_data);
- for_each_possible_cpu(i) {
- lowcore_ptr[i] = (struct _lowcore *)
- __get_free_pages(GFP_KERNEL | GFP_DMA,
- sizeof(void*) == 8 ? 1 : 0);
- stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
- if (!lowcore_ptr[i] || !stack)
- panic("smp_boot_cpus failed to allocate memory\n");
-
- *(lowcore_ptr[i]) = S390_lowcore;
- lowcore_ptr[i]->async_stack = stack + ASYNC_SIZE;
- stack = __get_free_pages(GFP_KERNEL, 0);
- if (!stack)
- panic("smp_boot_cpus failed to allocate memory\n");
- lowcore_ptr[i]->panic_stack = stack + PAGE_SIZE;
+ /* Reallocate current lowcore, but keep its contents. */
+ lc_order = sizeof(long) == 8 ? 1 : 0;
+ lowcore = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, lc_order);
+ panic_stack = __get_free_page(GFP_KERNEL);
+ async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
#ifndef CONFIG_64BIT
- if (MACHINE_HAS_IEEE) {
- lowcore_ptr[i]->extended_save_area_addr =
- (__u32) __get_free_pages(GFP_KERNEL, 0);
- if (!lowcore_ptr[i]->extended_save_area_addr)
- panic("smp_boot_cpus failed to "
- "allocate memory\n");
- }
+ if (MACHINE_HAS_IEEE)
+ save_area = get_zeroed_page(GFP_KERNEL);
#endif
- }
+ local_irq_disable();
+ local_mcck_disable();
+ lowcore_ptr[smp_processor_id()] = lowcore;
+ *lowcore = S390_lowcore;
+ lowcore->panic_stack = panic_stack + PAGE_SIZE;
+ lowcore->async_stack = async_stack + ASYNC_SIZE;
#ifndef CONFIG_64BIT
if (MACHINE_HAS_IEEE)
- ctl_set_bit(14, 29); /* enable extended save area */
+ lowcore->extended_save_area_addr = (u32) save_area;
#endif
- set_prefix((u32)(unsigned long) lowcore_ptr[smp_processor_id()]);
-
+ set_prefix((u32)(unsigned long) lowcore);
+ local_mcck_enable();
+ local_irq_enable();
for_each_possible_cpu(cpu)
if (cpu != smp_processor_id())
smp_create_idle(cpu);
return -EINVAL;
mutex_lock(&smp_cpu_state_mutex);
- lock_cpu_hotplug();
+ get_online_cpus();
rc = -EBUSY;
if (cpu_online(cpu))
goto out;
break;
}
out:
- unlock_cpu_hotplug();
+ put_online_cpus();
mutex_unlock(&smp_cpu_state_mutex);
return rc ? rc : count;
}
.notifier_call = smp_cpu_notify,
};
-static int smp_add_present_cpu(int cpu)
+static int __devinit smp_add_present_cpu(int cpu)
{
struct cpu *c = &per_cpu(cpu_devices, cpu);
struct sys_device *s = &c->sysdev;
}
#ifdef CONFIG_HOTPLUG_CPU
-static ssize_t rescan_store(struct sys_device *dev, const char *buf,
- size_t count)
+static ssize_t __ref rescan_store(struct sys_device *dev,
+ const char *buf, size_t count)
{
cpumask_t newcpus;
int cpu;
int rc;
mutex_lock(&smp_cpu_state_mutex);
- lock_cpu_hotplug();
+ get_online_cpus();
newcpus = cpu_present_map;
rc = smp_rescan_cpus();
if (rc)
}
rc = 0;
out:
- unlock_cpu_hotplug();
+ put_online_cpus();
mutex_unlock(&smp_cpu_state_mutex);
return rc ? rc : count;
}