This is purely to save memory - each supported CPU adds
approximately sixteen kilobytes to the kernel image.
+config HOTPLUG_CPU
+ bool "Support for hot-pluggable CPUs (EXPERIMENTAL)"
+ depends on SMP && HOTPLUG && EXPERIMENTAL
+ default n
+ help
+ Say Y here to experiment with turning CPUs off and on. CPUs
+ can be controlled through /sys/devices/system/cpu/cpu#.
+ Say N if you want to disable CPU hotplug.
+
config MATHEMU
bool "IEEE FPU emulation"
depends on MARCH_G5
#include <linux/sysctl.h>
#include <asm/timer.h>
//#include <linux/kernel_stat.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
#include "appldata.h"
*/
DEFINE_PER_CPU(struct vtimer_list, appldata_timer);
static atomic_t appldata_expire_count = ATOMIC_INIT(0);
-static struct appldata_mod_vtimer_args {
- struct vtimer_list *timer;
- u64 expires;
-} appldata_mod_vtimer_args;
static spinlock_t appldata_timer_lock = SPIN_LOCK_UNLOCKED;
static int appldata_interval = APPLDATA_CPU_INTERVAL;
static void appldata_timer_function(unsigned long data, struct pt_regs *regs)
{
P_DEBUG(" -= Timer =-\n");
- P_DEBUG("CPU: %i, expire: %i\n", smp_processor_id(),
+ P_DEBUG("CPU: %i, expire_count: %i\n", smp_processor_id(),
atomic_read(&appldata_expire_count));
if (atomic_dec_and_test(&appldata_expire_count)) {
atomic_set(&appldata_expire_count, num_online_cpus());
spin_unlock(&appldata_ops_lock);
}
-/*
- * appldata_mod_vtimer_wrap()
- *
- * wrapper function for mod_virt_timer(), because smp_call_function_on()
- * accepts only one parameter.
- */
-static void appldata_mod_vtimer_wrap(void *p) {
- struct appldata_mod_vtimer_args *args = p;
- mod_virt_timer(args->timer, args->expires);
-}
-
/*
* appldata_diag()
*
/****************************** /proc stuff **********************************/
+
+/*
+ * appldata_mod_vtimer_wrap()
+ *
+ * wrapper function for mod_virt_timer(), because smp_call_function_on()
+ * accepts only one parameter.
+ */
+static void __appldata_mod_vtimer_wrap(void *p) {
+ struct {
+ struct vtimer_list *timer;
+ u64 expires;
+ } *args = p;
+ mod_virt_timer(args->timer, args->expires);
+}
+
+#define APPLDATA_ADD_TIMER 0
+#define APPLDATA_DEL_TIMER 1
+#define APPLDATA_MOD_TIMER 2
+
+/*
+ * __appldata_vtimer_setup()
+ *
+ * Add, delete or modify virtual timers on all online cpus.
+ * The caller needs to get the appldata_timer_lock spinlock.
+ */
+static void
+__appldata_vtimer_setup(int cmd)
+{
+ u64 per_cpu_interval;
+ int i;
+
+ switch (cmd) {
+ case APPLDATA_ADD_TIMER:
+ if (appldata_timer_active)
+ break;
+ per_cpu_interval = (u64) (appldata_interval*1000 /
+ num_online_cpus()) * TOD_MICRO;
+ for_each_online_cpu(i) {
+ per_cpu(appldata_timer, i).expires = per_cpu_interval;
+ smp_call_function_on(add_virt_timer_periodic,
+ &per_cpu(appldata_timer, i),
+ 0, 1, i);
+ }
+ appldata_timer_active = 1;
+ P_INFO("Monitoring timer started.\n");
+ break;
+ case APPLDATA_DEL_TIMER:
+ for_each_online_cpu(i)
+ del_virt_timer(&per_cpu(appldata_timer, i));
+ if (!appldata_timer_active)
+ break;
+ appldata_timer_active = 0;
+ atomic_set(&appldata_expire_count, num_online_cpus());
+ P_INFO("Monitoring timer stopped.\n");
+ break;
+ case APPLDATA_MOD_TIMER:
+ per_cpu_interval = (u64) (appldata_interval*1000 /
+ num_online_cpus()) * TOD_MICRO;
+ if (!appldata_timer_active)
+ break;
+ for_each_online_cpu(i) {
+ struct {
+ struct vtimer_list *timer;
+ u64 expires;
+ } args;
+ args.timer = &per_cpu(appldata_timer, i);
+ args.expires = per_cpu_interval;
+ smp_call_function_on(__appldata_mod_vtimer_wrap,
+ &args, 0, 1, i);
+ }
+ }
+}
+
/*
* appldata_timer_handler()
*
appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
void __user *buffer, size_t *lenp)
{
- int len, i;
+ int len;
char buf[2];
- u64 per_cpu_interval;
if (!*lenp || filp->f_pos) {
*lenp = 0;
return -EFAULT;
goto out;
}
- per_cpu_interval = (u64) (appldata_interval*1000 /
- num_online_cpus()) * TOD_MICRO;
len = *lenp;
if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
return -EFAULT;
spin_lock(&appldata_timer_lock);
- per_cpu_interval = (u64) (appldata_interval*1000 /
- num_online_cpus()) * TOD_MICRO;
- if ((buf[0] == '1') && (!appldata_timer_active)) {
- for (i = 0; i < num_online_cpus(); i++) {
- per_cpu(appldata_timer, i).expires = per_cpu_interval;
- smp_call_function_on(add_virt_timer_periodic,
- &per_cpu(appldata_timer, i),
- 0, 1, i);
- }
- appldata_timer_active = 1;
- P_INFO("Monitoring timer started.\n");
- } else if ((buf[0] == '0') && (appldata_timer_active)) {
- for (i = 0; i < num_online_cpus(); i++) {
- del_virt_timer(&per_cpu(appldata_timer, i));
- }
- appldata_timer_active = 0;
- P_INFO("Monitoring timer stopped.\n");
- }
+ if (buf[0] == '1')
+ __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
+ else if (buf[0] == '0')
+ __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
spin_unlock(&appldata_timer_lock);
out:
*lenp = len;
appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
void __user *buffer, size_t *lenp)
{
- int len, i, interval;
+ int len, interval;
char buf[16];
- u64 per_cpu_interval;
if (!*lenp || filp->f_pos) {
*lenp = 0;
}
spin_lock(&appldata_timer_lock);
- per_cpu_interval = (u64) (interval*1000 / num_online_cpus()) * TOD_MICRO;
appldata_interval = interval;
- if (appldata_timer_active) {
- for (i = 0; i < num_online_cpus(); i++) {
- appldata_mod_vtimer_args.timer =
- &per_cpu(appldata_timer, i);
- appldata_mod_vtimer_args.expires =
- per_cpu_interval;
- smp_call_function_on(
- appldata_mod_vtimer_wrap,
- &appldata_mod_vtimer_args,
- 0, 1, i);
- }
- }
+ __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
spin_unlock(&appldata_timer_lock);
P_INFO("Monitoring CPU interval set to %u milliseconds.\n",
/******************************* init / exit *********************************/
+
+static void
+appldata_online_cpu(int cpu)
+{
+ init_virt_timer(&per_cpu(appldata_timer, cpu));
+ per_cpu(appldata_timer, cpu).function = appldata_timer_function;
+ per_cpu(appldata_timer, cpu).data = (unsigned long)
+ &appldata_tasklet_struct;
+ atomic_inc(&appldata_expire_count);
+ spin_lock(&appldata_timer_lock);
+ __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
+ spin_unlock(&appldata_timer_lock);
+}
+
+static void
+appldata_offline_cpu(int cpu)
+{
+ del_virt_timer(&per_cpu(appldata_timer, cpu));
+ if (atomic_dec_and_test(&appldata_expire_count)) {
+ atomic_set(&appldata_expire_count, num_online_cpus());
+ tasklet_schedule(&appldata_tasklet_struct);
+ }
+ spin_lock(&appldata_timer_lock);
+ __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
+ spin_unlock(&appldata_timer_lock);
+}
+
+static int
+appldata_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ switch (action) {
+ case CPU_ONLINE:
+ appldata_online_cpu((long) hcpu);
+ break;
+#ifdef CONFIG_HOTPLUG_CPU
+ case CPU_DEAD:
+ appldata_offline_cpu((long) hcpu);
+ break;
+#endif
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __devinitdata appldata_nb = {
+ .notifier_call = appldata_cpu_notify,
+};
+
/*
* appldata_init()
*
P_DEBUG("sizeof(parameter_list) = %lu\n",
sizeof(struct appldata_parameter_list));
- for (i = 0; i < num_online_cpus(); i++) {
- init_virt_timer(&per_cpu(appldata_timer, i));
- per_cpu(appldata_timer, i).function = appldata_timer_function;
- per_cpu(appldata_timer, i).data = (unsigned long)
- &appldata_tasklet_struct;
- }
- atomic_set(&appldata_expire_count, num_online_cpus());
+ for_each_online_cpu(i)
+ appldata_online_cpu(i);
+
+ /* Register cpu hotplug notifier */
+ register_cpu_notifier(&appldata_nb);
appldata_sysctl_header = register_sysctl_table(appldata_dir_table, 1);
#ifdef MODULE
}
spin_unlock_bh(&appldata_ops_lock);
- for (i = 0; i < num_online_cpus(); i++) {
- del_virt_timer(&per_cpu(appldata_timer, i));
- }
+ for_each_online_cpu(i)
+ appldata_offline_cpu(i);
+
appldata_timer_active = 0;
unregister_sysctl_table(appldata_sysctl_header);
LOAD_INT(a2), LOAD_FRAC(a2));
P_DEBUG("nr_cpus = %u\n", os_data->nr_cpus);
- for (i = 0; i < NR_CPUS; i++) {
- if (!cpu_online(i)) continue;
+ for (i = 0; i < os_data->nr_cpus; i++) {
P_DEBUG("cpu%u : user = %u, nice = %u, system = %u, "
"idle = %u, irq = %u, softirq = %u, iowait = %u\n",
i,
*/
static void appldata_get_os_data(void *data)
{
- int i;
+ int i, j;
struct appldata_os_data *os_data;
os_data = data;
os_data->avenrun[1] = avenrun[1] + (FIXED_1/200);
os_data->avenrun[2] = avenrun[2] + (FIXED_1/200);
- for (i = 0; i < num_online_cpus(); i++) {
- os_data->os_cpu[i].per_cpu_user =
+ j = 0;
+ for_each_online_cpu(i) {
+ os_data->os_cpu[j].per_cpu_user =
kstat_cpu(i).cpustat.user;
- os_data->os_cpu[i].per_cpu_nice =
+ os_data->os_cpu[j].per_cpu_nice =
kstat_cpu(i).cpustat.nice;
- os_data->os_cpu[i].per_cpu_system =
+ os_data->os_cpu[j].per_cpu_system =
kstat_cpu(i).cpustat.system;
- os_data->os_cpu[i].per_cpu_idle =
+ os_data->os_cpu[j].per_cpu_idle =
kstat_cpu(i).cpustat.idle;
- os_data->os_cpu[i].per_cpu_irq =
+ os_data->os_cpu[j].per_cpu_irq =
kstat_cpu(i).cpustat.irq;
- os_data->os_cpu[i].per_cpu_softirq =
+ os_data->os_cpu[j].per_cpu_softirq =
kstat_cpu(i).cpustat.softirq;
- os_data->os_cpu[i].per_cpu_iowait =
+ os_data->os_cpu[j].per_cpu_iowait =
kstat_cpu(i).cpustat.iowait;
+ j++;
}
os_data->timestamp = get_clock();
CONFIG_OBSOLETE_MODPARM=y
# CONFIG_MODVERSIONS is not set
CONFIG_KMOD=y
-CONFIG_STOP_MACHINE=y
#
# Base setup
# CONFIG_MARCH_Z990 is not set
CONFIG_SMP=y
CONFIG_NR_CPUS=32
+# CONFIG_HOTPLUG_CPU is not set
CONFIG_MATHEMU=y
#
# CONFIG_CRYPTO_AES is not set
# CONFIG_CRYPTO_CAST5 is not set
# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
# CONFIG_CRYPTO_ARC4 is not set
# CONFIG_CRYPTO_DEFLATE is not set
# CONFIG_CRYPTO_MICHAEL_MIC is not set
#include <linux/config.h>
#include <linux/compiler.h>
+#include <linux/cpu.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
/* enable monitor call class 0 */
__ctl_set_bit(8, 15);
+#ifdef CONFIG_HOTPLUG_CPU
+ if (cpu_is_offline(smp_processor_id()))
+ cpu_die();
+#endif
+
/*
* Wait for external, I/O or machine check interrupt and
* switch off machine check bit after the wait has ended.
EXPORT_SYMBOL(overflowgid);
EXPORT_SYMBOL(empty_zero_page);
-/*
- * virtual CPU timer
- */
-#ifdef CONFIG_VIRT_TIMER
-EXPORT_SYMBOL(init_virt_timer);
-EXPORT_SYMBOL(add_virt_timer);
-EXPORT_SYMBOL(add_virt_timer_periodic);
-EXPORT_SYMBOL(mod_virt_timer);
-EXPORT_SYMBOL(del_virt_timer);
-#endif
-
/*
* misc.
*/
} memory_chunk[MEMORY_CHUNKS] = { { 0 } };
#define CHUNK_READ_WRITE 0
#define CHUNK_READ_ONLY 1
-int cpus_initialized = 0;
-static cpumask_t cpu_initialized;
volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
/*
*/
void __devinit cpu_init (void)
{
- int nr = smp_processor_id();
int addr = hard_smp_processor_id();
- if (cpu_test_and_set(nr,cpu_initialized)) {
- printk("CPU#%d ALREADY INITIALIZED!!!!!!!!!\n", nr);
- for (;;) local_irq_enable();
- }
- cpus_initialized++;
-
/*
* Store processor id in lowcore (used e.g. in timer_interrupt)
*/
* Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com)
+ * Heiko Carstens (heiko.carstens@de.ibm.com)
*
* based on other smp stuff by
* (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net>
cpumask_t cpu_possible_map;
unsigned long cache_decay_ticks = 0;
+static struct task_struct *current_set[NR_CPUS];
+
EXPORT_SYMBOL(cpu_online_map);
/*
struct call_data_struct data;
int cpus = num_online_cpus()-1;
- /* FIXME: get cpu lock -hc */
if (cpus <= 0)
return 0;
static inline void do_send_stop(void)
{
- unsigned long dummy;
int i, rc;
/* stop all processors */
if (!cpu_online(i) || smp_processor_id() == i)
continue;
do {
- rc = signal_processor_ps(&dummy, 0, i, sigp_stop);
+ rc = signal_processor(i, sigp_stop);
} while (rc == sigp_busy);
}
}
static inline void do_store_status(void)
{
- unsigned long low_core_addr;
- unsigned long dummy;
int i, rc;
/* store status of all processors in their lowcores (real 0) */
for (i = 0; i < NR_CPUS; i++) {
if (!cpu_online(i) || smp_processor_id() == i)
continue;
- low_core_addr = (unsigned long) lowcore_ptr[i];
do {
- rc = signal_processor_ps(&dummy, low_core_addr, i,
- sigp_store_status_at_address);
+ rc = signal_processor_p(
+ (__u32)(unsigned long) lowcore_ptr[i], i,
+ sigp_store_status_at_address);
} while(rc == sigp_busy);
}
}
static void do_machine_restart(void * __unused)
{
+ static atomic_t cpuid = ATOMIC_INIT(-1);
+
cpu_clear(smp_processor_id(), cpu_restart_map);
- if (smp_processor_id() == 0) {
+ if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) {
/* Wait for all other cpus to enter do_machine_restart. */
while (!cpus_empty(cpu_restart_map))
cpu_relax();
static void do_machine_halt(void * __unused)
{
- if (smp_processor_id() == 0) {
+ static atomic_t cpuid = ATOMIC_INIT(-1);
+
+ if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) {
smp_send_stop();
if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
cpcmd(vmhalt_cmd, NULL, 0);
static void do_machine_power_off(void * __unused)
{
- if (smp_processor_id() == 0) {
+ static atomic_t cpuid = ATOMIC_INIT(-1);
+
+ if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) {
smp_send_stop();
if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
cpcmd(vmpoff_cmd, NULL, 0);
* Lets check how many CPUs we have.
*/
-void __init smp_check_cpus(unsigned int max_cpus)
+#ifdef CONFIG_HOTPLUG_CPU
+
+void
+__init smp_check_cpus(unsigned int max_cpus)
+{
+ int cpu;
+
+ /*
+ * cpu 0 is the boot cpu. See smp_prepare_boot_cpu.
+ */
+ for (cpu = 1; cpu < max_cpus; cpu++)
+ cpu_set(cpu, cpu_possible_map);
+}
+
+#else /* CONFIG_HOTPLUG_CPU */
+
+void
+__init smp_check_cpus(unsigned int max_cpus)
{
int curr_cpu, num_cpus;
__u16 boot_cpu_addr;
printk("Boot cpu address %2X\n", boot_cpu_addr);
}
+#endif /* CONFIG_HOTPLUG_CPU */
+
/*
* Activate a secondary processor.
*/
return cpu_idle(NULL);
}
-static struct task_struct *__devinit fork_by_hand(void)
+static void __init smp_create_idle(unsigned int cpu)
{
- struct pt_regs regs;
- /* don't care about the psw and regs settings since we'll never
- reschedule the forked task. */
- memset(®s,0,sizeof(struct pt_regs));
- return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL);
+ struct pt_regs regs;
+ struct task_struct *p;
+
+ /*
+ * don't care about the psw and regs settings since we'll never
+ * reschedule the forked task.
+ */
+ memset(®s, 0, sizeof(struct pt_regs));
+ p = copy_process(CLONE_VM | CLONE_IDLETASK, 0, ®s, 0, NULL, NULL);
+ if (IS_ERR(p))
+ panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p));
+
+ wake_up_forked_process(p);
+ init_idle(p, cpu);
+ unhash_process(p);
+ current_set[cpu] = p;
}
-int __cpu_up(unsigned int cpu)
+/* Reserving and releasing of CPUs */
+
+static atomic_t smp_cpu_reserved[NR_CPUS];
+
+int
+smp_get_cpu(cpumask_t cpu_mask)
+{
+ int val, cpu;
+
+ /* Try to find an already reserved cpu. */
+ for_each_cpu_mask(cpu, cpu_mask) {
+ while ((val = atomic_read(&smp_cpu_reserved[cpu])) != 0) {
+ if (!atomic_compare_and_swap(val, val + 1,
+ &smp_cpu_reserved[cpu]))
+ /* Found one. */
+ goto out;
+ }
+ }
+ /* Reserve a new cpu from cpu_mask. */
+ for_each_cpu_mask(cpu, cpu_mask) {
+ atomic_inc(&smp_cpu_reserved[cpu]);
+ if (cpu_online(cpu))
+ goto out;
+ atomic_dec(&smp_cpu_reserved[cpu]);
+ }
+ cpu = -ENODEV;
+out:
+ return cpu;
+}
+
+void
+smp_put_cpu(int cpu)
+{
+ atomic_dec(&smp_cpu_reserved[cpu]);
+}
+
+static inline int
+cpu_stopped(int cpu)
{
- struct task_struct *idle;
+ __u32 status;
+
+ /* Check for stopped state */
+ if (signal_processor_ps(&status, 0, cpu, sigp_sense) == sigp_status_stored) {
+ if (status & 0x40)
+ return 1;
+ }
+ return 0;
+}
+
+/* Upping and downing of CPUs */
+
+int
+__cpu_up(unsigned int cpu)
+{
+ struct task_struct *idle;
struct _lowcore *cpu_lowcore;
sigp_ccode ccode;
+ int curr_cpu;
- /*
- * Set prefix page for new cpu
- */
+ for (curr_cpu = 0; curr_cpu <= 65535; curr_cpu++) {
+ __cpu_logical_map[cpu] = (__u16) curr_cpu;
+ if (cpu_stopped(cpu))
+ break;
+ }
+
+ if (!cpu_stopped(cpu))
+ return -ENODEV;
- ccode = signal_processor_p((unsigned long)(lowcore_ptr[cpu]),
+ ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]),
cpu, sigp_set_prefix);
if (ccode){
printk("sigp_set_prefix failed for cpu %d "
return -EIO;
}
- /* We can't use kernel_thread since we must _avoid_ to reschedule
- the child. */
- idle = fork_by_hand();
- if (IS_ERR(idle)){
- printk("failed fork for CPU %d", cpu);
- return -EIO;
- }
- wake_up_forked_process(idle);
-
- /*
- * We remove it from the pidhash and the runqueue
- * once we got the process:
- */
- init_idle(idle, cpu);
-
- unhash_process(idle);
-
+ idle = current_set[cpu];
cpu_lowcore = lowcore_ptr[cpu];
cpu_lowcore->save_area[15] = idle->thread.ksp;
cpu_lowcore->kernel_stack = (unsigned long)
return 0;
}
+int
+__cpu_disable(void)
+{
+ unsigned long flags;
+ ec_creg_mask_parms cr_parms;
+
+ local_irq_save(flags);
+
+ if (atomic_read(&smp_cpu_reserved[smp_processor_id()])) {
+ local_irq_restore(flags);
+ return -EBUSY;
+ }
+
+ /* disable all external interrupts */
+
+ cr_parms.start_ctl = 0;
+ cr_parms.end_ctl = 0;
+ cr_parms.orvals[0] = 0;
+ cr_parms.andvals[0] = ~(1<<15 | 1<<14 | 1<<13 | 1<<12 |
+ 1<<11 | 1<<10 | 1<< 6 | 1<< 4);
+ smp_ctl_bit_callback(&cr_parms);
+
+ /* disable all I/O interrupts */
+
+ cr_parms.start_ctl = 6;
+ cr_parms.end_ctl = 6;
+ cr_parms.orvals[6] = 0;
+ cr_parms.andvals[6] = ~(1<<31 | 1<<30 | 1<<29 | 1<<28 |
+ 1<<27 | 1<<26 | 1<<25 | 1<<24);
+ smp_ctl_bit_callback(&cr_parms);
+
+ /* disable most machine checks */
+
+ cr_parms.start_ctl = 14;
+ cr_parms.end_ctl = 14;
+ cr_parms.orvals[14] = 0;
+ cr_parms.andvals[14] = ~(1<<28 | 1<<27 | 1<<26 | 1<<25 | 1<<24);
+ smp_ctl_bit_callback(&cr_parms);
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+void
+__cpu_die(unsigned int cpu)
+{
+ /* Wait until target cpu is down */
+ while (!cpu_stopped(cpu));
+ printk("Processor %d spun down\n", cpu);
+}
+
+void
+cpu_die(void)
+{
+ signal_processor(smp_processor_id(), sigp_stop);
+ BUG();
+ for(;;);
+}
+
/*
* Cycle through the processors and setup structures.
*/
void __init smp_prepare_cpus(unsigned int max_cpus)
{
unsigned long async_stack;
+ unsigned int cpu;
int i;
/* request the 0x1202 external interrupt */
lowcore_ptr[i]->async_stack = async_stack + (ASYNC_SIZE);
}
set_prefix((u32)(unsigned long) lowcore_ptr[smp_processor_id()]);
+
+ for_each_cpu(cpu)
+ if (cpu != smp_processor_id())
+ smp_create_idle(cpu);
}
void __devinit smp_prepare_boot_cpu(void)
{
- cpu_set(smp_processor_id(), cpu_online_map);
- cpu_set(smp_processor_id(), cpu_possible_map);
- S390_lowcore.percpu_offset = __per_cpu_offset[smp_processor_id()];
+ BUG_ON(smp_processor_id() != 0);
+
+ cpu_set(0, cpu_online_map);
+ cpu_set(0, cpu_possible_map);
+ S390_lowcore.percpu_offset = __per_cpu_offset[0];
+ current_set[0] = current;
}
void smp_cpus_done(unsigned int max_cpus)
EXPORT_SYMBOL(smp_ctl_set_bit);
EXPORT_SYMBOL(smp_ctl_clear_bit);
EXPORT_SYMBOL(smp_call_function);
+EXPORT_SYMBOL(smp_get_cpu);
+EXPORT_SYMBOL(smp_put_cpu);
+
static int
cmm_init (void)
{
+ int rc;
+
+ /* Prevent logical cpu 0 from being set offline. */
+ rc = smp_get_cpu(cpumask_of_cpu(0));
+ if (rc) {
+ printk(KERN_ERR "CMM: unable to reserve cpu 0\n");
+ return rc;
+ }
#ifdef CONFIG_CMM_PROC
cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
#endif
#ifdef CONFIG_CMM_IUCV
smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
#endif
+ /* Allow logical cpu 0 to be set offline again. */
+ smp_put_cpu(0);
}
module_init(cmm_init);
static void
do_load_quiesce_psw(void * __unused)
{
+ static atomic_t cpuid = ATOMIC_INIT(-1);
psw_t quiesce_psw;
- unsigned long status;
+ __u32 status;
int i;
- if (smp_processor_id() != 0)
+ if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid))
signal_processor(smp_processor_id(), sigp_stop);
/* Wait for all other cpus to enter stopped state */
i = 1;
case sigp_order_code_accepted:
case sigp_status_stored:
/* Check for stopped and check stop state */
- if (test_bit(6, &status) || test_bit(4, &status))
+ if (status & 0x50)
i++;
break;
case sigp_busy:
/*
- * $Id: iucv.c,v 1.33 2004/05/24 10:19:18 braunu Exp $
+ * $Id: iucv.c,v 1.34 2004/06/24 10:53:48 braunu Exp $
*
* IUCV network driver
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.33 $
+ * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.34 $
*
*/
\f
static unsigned long max_connections;
/**
- * declare_flag: is 0 when iucv_declare_buffer has not been called
+ * iucv_cpuid: contains the logical cpu number of the cpu which
+ * has declared the iucv buffer by issuing DECLARE_BUFFER.
+ * If no cpu has done the initialization iucv_cpuid contains -1.
*/
-static int declare_flag;
+static int iucv_cpuid = -1;
/**
* register_flag: is 0 when external interrupt has not been registered
*/
static void
iucv_banner(void)
{
- char vbuf[] = "$Revision: 1.33 $";
+ char vbuf[] = "$Revision: 1.34 $";
char *version = vbuf;
if ((version = strchr(version, ':'))) {
}
/**
- * iucv_declare_buffer_cpu0
- * Register at VM for subsequent IUCV operations. This is always
- * executed on CPU 0. Called from iucv_declare_buffer().
+ * iucv_declare_buffer_cpuid
+ * Register at VM for subsequent IUCV operations. This is executed
+ * on the reserved CPU iucv_cpuid. Called from iucv_declare_buffer().
*/
static void
-iucv_declare_buffer_cpu0 (void *result)
+iucv_declare_buffer_cpuid (void *result)
{
iparml_db *parm;
- if (!(result && (smp_processor_id() == 0)))
+ if (smp_processor_id() != iucv_cpuid)
return;
parm = (iparml_db *)grab_param();
parm->ipbfadr1 = virt_to_phys(iucv_external_int_buffer);
}
/**
- * iucv_retrieve_buffer_cpu0:
- * Unregister IUCV usage at VM. This is always executed on CPU 0.
+ * iucv_retrieve_buffer_cpuid:
+ * Unregister IUCV usage at VM. This is always executed on the same
+ * cpu that registered the buffer to VM.
* Called from iucv_retrieve_buffer().
*/
static void
-iucv_retrieve_buffer_cpu0 (void *result)
+iucv_retrieve_buffer_cpuid (void *cpu)
{
iparml_control *parm;
- if (smp_processor_id() != 0)
+ if (smp_processor_id() != iucv_cpuid)
return;
parm = (iparml_control *)grab_param();
b2f0(RETRIEVE_BUFFER, parm);
static int
iucv_declare_buffer (void)
{
- ulong b2f0_result = 0x0deadbeef;
+ unsigned long flags;
+ ulong b2f0_result;
iucv_debug(1, "entering");
- preempt_disable();
- if (smp_processor_id() == 0)
- iucv_declare_buffer_cpu0(&b2f0_result);
- else
- smp_call_function(iucv_declare_buffer_cpu0, &b2f0_result, 0, 1);
- preempt_enable();
- iucv_debug(1, "Address of EIB = %p", iucv_external_int_buffer);
- if (b2f0_result == 0x0deadbeef)
- b2f0_result = 0xaa;
+ spin_lock_irqsave (&iucv_lock, flags);
+ if (iucv_cpuid == -1) {
+ /* Reserve any cpu for use by iucv. */
+ iucv_cpuid = smp_get_cpu(CPU_MASK_ALL);
+ spin_unlock_irqrestore (&iucv_lock, flags);
+ smp_call_function(iucv_declare_buffer_cpuid,
+ &b2f0_result, 0, 1);
+ iucv_debug(1, "Address of EIB = %p", iucv_external_int_buffer);
+ } else {
+ spin_unlock_irqrestore (&iucv_lock, flags);
+ b2f0_result = 0;
+ }
iucv_debug(1, "exiting");
return b2f0_result;
}
iucv_retrieve_buffer (void)
{
iucv_debug(1, "entering");
- if (declare_flag) {
- preempt_disable();
- if (smp_processor_id() == 0)
- iucv_retrieve_buffer_cpu0(0);
- else
- smp_call_function(iucv_retrieve_buffer_cpu0, 0, 0, 1);
- declare_flag = 0;
- preempt_enable();
+ if (iucv_cpuid != -1) {
+ smp_call_function(iucv_retrieve_buffer_cpuid, 0, 0, 1);
+ /* Release the cpu reserved by iucv_declare_buffer. */
+ smp_put_cpu(iucv_cpuid);
+ iucv_cpuid = -1;
}
iucv_debug(1, "exiting");
return 0;
return NULL;
}
- if (declare_flag == 0) {
- rc = iucv_declare_buffer();
- if (rc) {
- char *err = "Unknown";
- iucv_remove_handler(new_handler);
- kfree(new_handler);
- switch(rc) {
- case 0x03:
- err = "Directory error";
- break;
- case 0x0a:
- err = "Invalid length";
- break;
- case 0x13:
- err = "Buffer already exists";
- break;
- case 0x3e:
- err = "Buffer overlap";
- break;
- case 0x5c:
- err = "Paging or storage error";
- break;
- case 0xaa:
- err = "Function not called";
- break;
- }
- printk(KERN_WARNING "%s: iucv_declare_buffer "
- "returned error 0x%02lx (%s)\n", __FUNCTION__, rc,
- err);
- return NULL;
+ rc = iucv_declare_buffer();
+ if (rc) {
+ char *err = "Unknown";
+ iucv_remove_handler(new_handler);
+ kfree(new_handler);
+ switch(rc) {
+ case 0x03:
+ err = "Directory error";
+ break;
+ case 0x0a:
+ err = "Invalid length";
+ break;
+ case 0x13:
+ err = "Buffer already exists";
+ break;
+ case 0x3e:
+ err = "Buffer overlap";
+ break;
+ case 0x5c:
+ err = "Paging or storage error";
+ break;
}
- declare_flag = 1;
+ printk(KERN_WARNING "%s: iucv_declare_buffer "
+ "returned error 0x%02lx (%s)\n", __FUNCTION__, rc, err);
+ return NULL;
}
if (register_flag == 0) {
/* request the 0x4000 external interrupt */
}
void
-iucv_setmask_cpu0 (void *result)
+iucv_setmask_cpuid (void *result)
{
iparml_set_mask *parm;
- if (smp_processor_id() != 0)
+ if (smp_processor_id() != iucv_cpuid)
return;
iucv_debug(1, "entering");
ulong result;
__u8 param;
} u;
+ int cpu;
u.param = SetMaskFlag;
- preempt_disable();
- if (smp_processor_id() == 0)
- iucv_setmask_cpu0(&u);
+ cpu = get_cpu();
+ if (cpu == iucv_cpuid)
+ iucv_setmask_cpuid(&u);
else
- smp_call_function(iucv_setmask_cpu0, &u, 0, 1);
- preempt_enable();
+ smp_call_function(iucv_setmask_cpuid, &u, 0, 1);
+ put_cpu();
return u.result;
}
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com)
+ * Heiko Carstens (heiko.carstens@de.ibm.com)
*
* sigp.h by D.J. Barrow (c) IBM 1999
* contains routines / structures for signalling other S/390 processors in an
sigp_ccode ccode;
__asm__ __volatile__(
-#ifndef __s390x__
" sr 1,1\n" /* parameter=0 in gpr 1 */
" sigp 1,%1,0(%2)\n"
" ipm %0\n"
" srl %0,28\n"
-#else /* __s390x__ */
- " sgr 1,1\n" /* parameter=0 in gpr 1 */
- " sigp 1,%1,0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
-#endif /* __s390x__ */
: "=d" (ccode)
: "d" (__cpu_logical_map[cpu_addr]), "a" (order_code)
: "cc" , "memory", "1" );
* Signal processor with parameter
*/
extern __inline__ sigp_ccode
-signal_processor_p(unsigned long parameter,__u16 cpu_addr,
+signal_processor_p(__u32 parameter, __u16 cpu_addr,
sigp_order_code order_code)
{
sigp_ccode ccode;
__asm__ __volatile__(
-#ifndef __s390x__
" lr 1,%1\n" /* parameter in gpr 1 */
" sigp 1,%2,0(%3)\n"
" ipm %0\n"
" srl %0,28\n"
-#else /* __s390x__ */
- " lgr 1,%1\n" /* parameter in gpr 1 */
- " sigp 1,%2,0(%3)\n"
- " ipm %0\n"
- " srl %0,28\n"
-#endif /* __s390x__ */
: "=d" (ccode)
: "d" (parameter), "d" (__cpu_logical_map[cpu_addr]),
"a" (order_code)
* Signal processor with parameter and return status
*/
extern __inline__ sigp_ccode
-signal_processor_ps(unsigned long *statusptr, unsigned long parameter,
+signal_processor_ps(__u32 *statusptr, __u32 parameter,
__u16 cpu_addr, sigp_order_code order_code)
{
sigp_ccode ccode;
__asm__ __volatile__(
-#ifndef __s390x__
- " sr 2,2\n" /* clear status so it doesn't contain rubbish if not saved. */
+ " sr 2,2\n" /* clear status */
" lr 3,%2\n" /* parameter in gpr 3 */
" sigp 2,%3,0(%4)\n"
" st 2,%1\n"
" ipm %0\n"
" srl %0,28\n"
-#else /* __s390x__ */
- " sgr 2,2\n" /* clear status so it doesn't contain rubbish if not saved. */
- " lgr 3,%2\n" /* parameter in gpr 3 */
- " sigp 2,%3,0(%4)\n"
- " stg 2,%1\n"
- " ipm %0\n"
- " srl %0,28\n"
-#endif /* __s390x__ */
: "=d" (ccode), "=m" (*statusptr)
: "d" (parameter), "d" (__cpu_logical_map[cpu_addr]),
"a" (order_code)
}
#endif /* __SIGP__ */
-
-
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com)
+ * Heiko Carstens (heiko.carstens@de.ibm.com)
*/
#ifndef __ASM_SMP_H
#define __ASM_SMP_H
#define smp_processor_id() (S390_lowcore.cpu_data.cpu_nr)
+extern int smp_get_cpu(cpumask_t cpu_map);
+extern void smp_put_cpu(int cpu);
+
extern __inline__ __u16 hard_smp_processor_id(void)
{
__u16 cpu_address;
#define cpu_logical_map(cpu) (cpu)
+extern int __cpu_disable (void);
+extern void __cpu_die (unsigned int cpu);
+extern void cpu_die (void) __attribute__ ((noreturn));
+extern int __cpu_up (unsigned int cpu);
+
#endif
#ifndef CONFIG_SMP
#define smp_call_function_on(func,info,nonatomic,wait,cpu) ({ 0; })
+#define smp_get_cpu(cpu) ({ 0; })
+#define smp_put_cpu(cpu) ({ 0; })
#endif
#endif