RHEL4/kernel/cpu.c
<<
>>
Prefs
   1/* CPU control.
   2 * (C) 2001, 2002, 2003, 2004 Rusty Russell
   3 *
   4 * This code is licenced under the GPL.
   5 */
   6#include <linux/proc_fs.h>
   7#include <linux/smp.h>
   8#include <linux/init.h>
   9#include <linux/notifier.h>
  10#include <linux/sched.h>
  11#include <linux/unistd.h>
  12#include <linux/cpu.h>
  13#include <linux/module.h>
  14#include <linux/kmod.h>         /* for hotplug_path */
  15#include <linux/kthread.h>
  16#include <linux/stop_machine.h>
  17#include <asm/semaphore.h>
  18
  19/* This protects CPUs going up and down... */
  20DECLARE_MUTEX(cpucontrol);
  21
  22static struct notifier_block *cpu_chain;
  23
  24/* Need to know about CPUs going up/down? */
  25int register_cpu_notifier(struct notifier_block *nb)
  26{
  27        int ret;
  28
  29        if ((ret = down_interruptible(&cpucontrol)) != 0)
  30                return ret;
  31        ret = notifier_chain_register(&cpu_chain, nb);
  32        up(&cpucontrol);
  33        return ret;
  34}
  35EXPORT_SYMBOL(register_cpu_notifier);
  36
  37void unregister_cpu_notifier(struct notifier_block *nb)
  38{
  39        down(&cpucontrol);
  40        notifier_chain_unregister(&cpu_chain, nb);
  41        up(&cpucontrol);
  42}
  43EXPORT_SYMBOL(unregister_cpu_notifier);
  44
  45#ifdef CONFIG_HOTPLUG_CPU
  46
  47extern int  __cpu_disable(void);
  48extern void __cpu_die(unsigned int);
  49
  50static inline void check_for_tasks(int cpu)
  51{
  52        struct task_struct *p;
  53
  54        write_lock_irq(&tasklist_lock);
  55        for_each_process(p) {
  56                if (task_cpu(p) == cpu && (p->utime != 0 || p->stime != 0))
  57                        printk(KERN_WARNING "Task %s (pid = %d) is on cpu %d\
  58                                (state = %ld, flags = %lx) \n",
  59                                 p->comm, p->pid, cpu, p->state, p->flags);
  60        }
  61        write_unlock_irq(&tasklist_lock);
  62}
  63
  64/* Notify userspace when a cpu event occurs, by running '/sbin/hotplug
  65 * cpu' with certain environment variables set.  */
  66static int cpu_run_sbin_hotplug(unsigned int cpu, const char *action)
  67{
  68        char *argv[3], *envp[6], cpu_str[12], action_str[32], devpath_str[40];
  69        int i;
  70
  71        sprintf(cpu_str, "CPU=%d", cpu);
  72        sprintf(action_str, "ACTION=%s", action);
  73        sprintf(devpath_str, "DEVPATH=devices/system/cpu/cpu%d", cpu);
  74        
  75        i = 0;
  76        argv[i++] = hotplug_path;
  77        argv[i++] = "cpu";
  78        argv[i] = NULL;
  79
  80        i = 0;
  81        /* minimal command environment */
  82        envp[i++] = "HOME=/";
  83        envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
  84        envp[i++] = cpu_str;
  85        envp[i++] = action_str;
  86        envp[i++] = devpath_str;
  87        envp[i] = NULL;
  88
  89        return call_usermodehelper(argv[0], argv, envp, 0);
  90}
  91
  92/* Take this CPU down. */
  93static int take_cpu_down(void *unused)
  94{
  95        int err;
  96
  97        /* Take offline: makes arch_cpu_down somewhat easier. */
  98        cpu_clear(smp_processor_id(), cpu_online_map);
  99
 100        /* Ensure this CPU doesn't handle any more interrupts. */
 101        err = __cpu_disable();
 102        if (err < 0)
 103                cpu_set(smp_processor_id(), cpu_online_map);
 104        else
 105                /* Force idle task to run as soon as we yield: it should
 106                   immediately notice cpu is offline and die quickly. */
 107                sched_idle_next();
 108
 109        return err;
 110}
 111
 112int cpu_down(unsigned int cpu)
 113{
 114        int err;
 115        struct task_struct *p;
 116        cpumask_t old_allowed, tmp;
 117
 118        if ((err = lock_cpu_hotplug_interruptible()) != 0)
 119                return err;
 120
 121        if (num_online_cpus() == 1) {
 122                err = -EBUSY;
 123                goto out;
 124        }
 125
 126        if (!cpu_online(cpu)) {
 127                err = -EINVAL;
 128                goto out;
 129        }
 130
 131        err = notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE,
 132                                                (void *)(long)cpu);
 133        if (err == NOTIFY_BAD) {
 134                printk("%s: attempt to take down CPU %u failed\n",
 135                                __FUNCTION__, cpu);
 136                err = -EINVAL;
 137                goto out;
 138        }
 139
 140        /* Ensure that we are not runnable on dying cpu */
 141        old_allowed = current->cpus_allowed;
 142        tmp = CPU_MASK_ALL;
 143        cpu_clear(cpu, tmp);
 144        set_cpus_allowed(current, tmp);
 145
 146        p = __stop_machine_run(take_cpu_down, NULL, cpu);
 147        if (IS_ERR(p)) {
 148                /* CPU didn't die: tell everyone.  Can't complain. */
 149                if (notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED,
 150                                (void *)(long)cpu) == NOTIFY_BAD)
 151                        BUG();
 152
 153                err = PTR_ERR(p);
 154                goto out_allowed;
 155        }
 156
 157        if (cpu_online(cpu))
 158                goto out_thread;
 159
 160        /* Wait for it to sleep (leaving idle task). */
 161        while (!idle_cpu(cpu))
 162                yield();
 163
 164        /* This actually kills the CPU. */
 165        __cpu_die(cpu);
 166
 167        /* Move it here so it can run. */
 168        kthread_bind(p, smp_processor_id());
 169
 170        /* CPU is completely dead: tell everyone.  Too late to complain. */
 171        if (notifier_call_chain(&cpu_chain, CPU_DEAD, (void *)(long)cpu)
 172            == NOTIFY_BAD)
 173                BUG();
 174
 175        check_for_tasks(cpu);
 176
 177        cpu_run_sbin_hotplug(cpu, "offline");
 178
 179out_thread:
 180        err = kthread_stop(p);
 181out_allowed:
 182        set_cpus_allowed(current, old_allowed);
 183out:
 184        unlock_cpu_hotplug();
 185        return err;
 186}
 187#else
 188static inline int cpu_run_sbin_hotplug(unsigned int cpu, const char *action)
 189{
 190        return 0;
 191}
 192#endif /*CONFIG_HOTPLUG_CPU*/
 193
 194int __devinit cpu_up(unsigned int cpu)
 195{
 196        int ret;
 197        void *hcpu = (void *)(long)cpu;
 198
 199        if ((ret = down_interruptible(&cpucontrol)) != 0)
 200                return ret;
 201
 202        if (cpu_online(cpu) || !cpu_present(cpu)) {
 203                ret = -EINVAL;
 204                goto out;
 205        }
 206        ret = notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu);
 207        if (ret == NOTIFY_BAD) {
 208                printk("%s: attempt to bring up CPU %u failed\n",
 209                                __FUNCTION__, cpu);
 210                ret = -EINVAL;
 211                goto out_notify;
 212        }
 213
 214        /* Arch-specific enabling code. */
 215        ret = __cpu_up(cpu);
 216        if (ret != 0)
 217                goto out_notify;
 218        if (!cpu_online(cpu))
 219                BUG();
 220
 221        /* Now call notifier in preparation. */
 222        notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu);
 223
 224        cpu_run_sbin_hotplug(cpu, "online");
 225
 226out_notify:
 227        if (ret != 0)
 228                notifier_call_chain(&cpu_chain, CPU_UP_CANCELED, hcpu);
 229out:
 230        up(&cpucontrol);
 231        return ret;
 232}
 233