linux/drivers/base/cpu.c
<<
>>
Prefs
   1/*
   2 * drivers/base/cpu.c - basic CPU class support
   3 */
   4
   5#include <linux/sysdev.h>
   6#include <linux/module.h>
   7#include <linux/init.h>
   8#include <linux/sched.h>
   9#include <linux/cpu.h>
  10#include <linux/topology.h>
  11#include <linux/device.h>
  12#include <linux/node.h>
  13
  14#include "base.h"
  15
  16struct sysdev_class cpu_sysdev_class = {
  17        .name = "cpu",
  18};
  19EXPORT_SYMBOL(cpu_sysdev_class);
  20
  21static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices);
  22
  23#ifdef CONFIG_HOTPLUG_CPU
  24static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr,
  25                           char *buf)
  26{
  27        struct cpu *cpu = container_of(dev, struct cpu, sysdev);
  28
  29        return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id));
  30}
  31
  32static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr,
  33                                 const char *buf, size_t count)
  34{
  35        struct cpu *cpu = container_of(dev, struct cpu, sysdev);
  36        ssize_t ret;
  37
  38        switch (buf[0]) {
  39        case '0':
  40                ret = cpu_down(cpu->sysdev.id);
  41                if (!ret)
  42                        kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
  43                break;
  44        case '1':
  45                ret = cpu_up(cpu->sysdev.id);
  46                if (!ret)
  47                        kobject_uevent(&dev->kobj, KOBJ_ONLINE);
  48                break;
  49        default:
  50                ret = -EINVAL;
  51        }
  52
  53        if (ret >= 0)
  54                ret = count;
  55        return ret;
  56}
  57static SYSDEV_ATTR(online, 0644, show_online, store_online);
  58
  59static void __cpuinit register_cpu_control(struct cpu *cpu)
  60{
  61        sysdev_create_file(&cpu->sysdev, &attr_online);
  62}
  63void unregister_cpu(struct cpu *cpu)
  64{
  65        int logical_cpu = cpu->sysdev.id;
  66
  67        unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
  68
  69        sysdev_remove_file(&cpu->sysdev, &attr_online);
  70
  71        sysdev_unregister(&cpu->sysdev);
  72        per_cpu(cpu_sys_devices, logical_cpu) = NULL;
  73        return;
  74}
  75#else /* ... !CONFIG_HOTPLUG_CPU */
  76static inline void register_cpu_control(struct cpu *cpu)
  77{
  78}
  79#endif /* CONFIG_HOTPLUG_CPU */
  80
  81#ifdef CONFIG_KEXEC
  82#include <linux/kexec.h>
  83
  84static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr,
  85                                char *buf)
  86{
  87        struct cpu *cpu = container_of(dev, struct cpu, sysdev);
  88        ssize_t rc;
  89        unsigned long long addr;
  90        int cpunum;
  91
  92        cpunum = cpu->sysdev.id;
  93
  94        /*
  95         * Might be reading other cpu's data based on which cpu read thread
  96         * has been scheduled. But cpu data (memory) is allocated once during
  97         * boot up and this data does not change there after. Hence this
  98         * operation should be safe. No locking required.
  99         */
 100        addr = __pa(per_cpu_ptr(crash_notes, cpunum));
 101        rc = sprintf(buf, "%Lx\n", addr);
 102        return rc;
 103}
 104static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
 105#endif
 106
 107/*
 108 * Print cpu online, possible, present, and system maps
 109 */
 110static ssize_t print_cpus_map(char *buf, cpumask_t *map)
 111{
 112        int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *map);
 113
 114        buf[n++] = '\n';
 115        buf[n] = '\0';
 116        return n;
 117}
 118
 119#define print_cpus_func(type) \
 120static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \
 121{                                                                       \
 122        return print_cpus_map(buf, &cpu_##type##_map);                  \
 123}                                                                       \
 124static struct sysdev_class_attribute attr_##type##_map =                \
 125        _SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)
 126
 127print_cpus_func(online);
 128print_cpus_func(possible);
 129print_cpus_func(present);
 130
 131static struct sysdev_class_attribute *cpu_state_attr[] = {
 132        &attr_online_map,
 133        &attr_possible_map,
 134        &attr_present_map,
 135};
 136
 137static int cpu_states_init(void)
 138{
 139        int i;
 140        int err = 0;
 141
 142        for (i = 0;  i < ARRAY_SIZE(cpu_state_attr); i++) {
 143                int ret;
 144                ret = sysdev_class_create_file(&cpu_sysdev_class,
 145                                                cpu_state_attr[i]);
 146                if (!err)
 147                        err = ret;
 148        }
 149        return err;
 150}
 151
 152/*
 153 * register_cpu - Setup a sysfs device for a CPU.
 154 * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
 155 *        sysfs for this CPU.
 156 * @num - CPU number to use when creating the device.
 157 *
 158 * Initialize and register the CPU device.
 159 */
 160int __cpuinit register_cpu(struct cpu *cpu, int num)
 161{
 162        int error;
 163        cpu->node_id = cpu_to_node(num);
 164        cpu->sysdev.id = num;
 165        cpu->sysdev.cls = &cpu_sysdev_class;
 166
 167        error = sysdev_register(&cpu->sysdev);
 168
 169        if (!error && cpu->hotpluggable)
 170                register_cpu_control(cpu);
 171        if (!error)
 172                per_cpu(cpu_sys_devices, num) = &cpu->sysdev;
 173        if (!error)
 174                register_cpu_under_node(num, cpu_to_node(num));
 175
 176#ifdef CONFIG_KEXEC
 177        if (!error)
 178                error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
 179#endif
 180        return error;
 181}
 182
 183struct sys_device *get_cpu_sysdev(unsigned cpu)
 184{
 185        if (cpu < nr_cpu_ids && cpu_possible(cpu))
 186                return per_cpu(cpu_sys_devices, cpu);
 187        else
 188                return NULL;
 189}
 190EXPORT_SYMBOL_GPL(get_cpu_sysdev);
 191
 192int __init cpu_dev_init(void)
 193{
 194        int err;
 195
 196        err = sysdev_class_register(&cpu_sysdev_class);
 197        if (!err)
 198                err = cpu_states_init();
 199
 200#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
 201        if (!err)
 202                err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
 203#endif
 204
 205        return err;
 206}
 207