Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / base / cpu.c
index aa66d71..4bef76a 100644 (file)
@@ -8,13 +8,17 @@
 #include <linux/cpu.h>
 #include <linux/topology.h>
 #include <linux/device.h>
+#include <linux/node.h>
 
+#include "base.h"
 
 struct sysdev_class cpu_sysdev_class = {
        set_kset_name("cpu"),
 };
 EXPORT_SYMBOL(cpu_sysdev_class);
 
+static struct sys_device *cpu_sys_devices[NR_CPUS];
+
 #ifdef CONFIG_HOTPLUG_CPU
 static ssize_t show_online(struct sys_device *dev, char *buf)
 {
@@ -32,9 +36,13 @@ static ssize_t store_online(struct sys_device *dev, const char *buf,
        switch (buf[0]) {
        case '0':
                ret = cpu_down(cpu->sysdev.id);
+               if (!ret)
+                       kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
                break;
        case '1':
                ret = cpu_up(cpu->sysdev.id);
+               if (!ret)
+                       kobject_uevent(&dev->kobj, KOBJ_ONLINE);
                break;
        default:
                ret = -EINVAL;
@@ -46,16 +54,53 @@ static ssize_t store_online(struct sys_device *dev, const char *buf,
 }
 static SYSDEV_ATTR(online, 0600, show_online, store_online);
 
-static void __init register_cpu_control(struct cpu *cpu)
+static void __devinit register_cpu_control(struct cpu *cpu)
 {
        sysdev_create_file(&cpu->sysdev, &attr_online);
 }
+void unregister_cpu(struct cpu *cpu)
+{
+       int logical_cpu = cpu->sysdev.id;
+
+       unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
+
+       sysdev_remove_file(&cpu->sysdev, &attr_online);
+
+       sysdev_unregister(&cpu->sysdev);
+       cpu_sys_devices[logical_cpu] = NULL;
+       return;
+}
 #else /* ... !CONFIG_HOTPLUG_CPU */
 static inline void register_cpu_control(struct cpu *cpu)
 {
 }
 #endif /* CONFIG_HOTPLUG_CPU */
 
+#ifdef CONFIG_KEXEC
+#include <linux/kexec.h>
+
+static ssize_t show_crash_notes(struct sys_device *dev, char *buf)
+{
+       struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+       ssize_t rc;
+       unsigned long long addr;
+       int cpunum;
+
+       cpunum = cpu->sysdev.id;
+
+       /*
+        * Might be reading other cpu's data based on which cpu read thread
+        * has been scheduled. But cpu data (memory) is allocated once during
+        * boot up and this data does not change there after. Hence this
+        * operation should be safe. No locking required.
+        */
+       addr = __pa(per_cpu_ptr(crash_notes, cpunum));
+       rc = sprintf(buf, "%Lx\n", addr);
+       return rc;
+}
+static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
+#endif
+
 /*
  * register_cpu - Setup a driverfs device for a CPU.
  * @cpu - Callers can set the cpu->no_control field to 1, to indicate not to
@@ -64,27 +109,47 @@ static inline void register_cpu_control(struct cpu *cpu)
  *
  * Initialize and register the CPU device.
  */
-int __init register_cpu(struct cpu *cpu, int num, struct node *root)
+int __devinit register_cpu(struct cpu *cpu, int num)
 {
        int error;
-
        cpu->node_id = cpu_to_node(num);
        cpu->sysdev.id = num;
        cpu->sysdev.cls = &cpu_sysdev_class;
 
        error = sysdev_register(&cpu->sysdev);
-       if (!error && root)
-               error = sysfs_create_link(&root->sysdev.kobj,
-                                         &cpu->sysdev.kobj,
-                                         kobject_name(&cpu->sysdev.kobj));
+
        if (!error && !cpu->no_control)
                register_cpu_control(cpu);
+       if (!error)
+               cpu_sys_devices[num] = &cpu->sysdev;
+       if (!error)
+               register_cpu_under_node(num, cpu_to_node(num));
+
+#ifdef CONFIG_KEXEC
+       if (!error)
+               error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
+#endif
        return error;
 }
 
-
+struct sys_device *get_cpu_sysdev(unsigned cpu)
+{
+       if (cpu < NR_CPUS)
+               return cpu_sys_devices[cpu];
+       else
+               return NULL;
+}
+EXPORT_SYMBOL_GPL(get_cpu_sysdev);
 
 int __init cpu_dev_init(void)
 {
-       return sysdev_class_register(&cpu_sysdev_class);
+       int err;
+
+       err = sysdev_class_register(&cpu_sysdev_class);
+#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
+       if (!err)
+               err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
+#endif
+
+       return err;
 }