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, const struct cpumask *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##_mask);                  \
 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
 131/*
 132 * Print values for NR_CPUS and offlined cpus
 133 */
 134static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf)
 135{
 136        int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
 137        return n;
 138}
 139static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
 140
 141/* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
 142unsigned int total_cpus;
 143
 144static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf)
 145{
 146        int n = 0, len = PAGE_SIZE-2;
 147        cpumask_var_t offline;
 148
 149        /* display offline cpus < nr_cpu_ids */
 150        if (!alloc_cpumask_var(&offline, GFP_KERNEL))
 151                return -ENOMEM;
 152        cpumask_complement(offline, cpu_online_mask);
 153        n = cpulist_scnprintf(buf, len, offline);
 154        free_cpumask_var(offline);
 155
 156        /* display offline cpus >= nr_cpu_ids */
 157        if (total_cpus && nr_cpu_ids < total_cpus) {
 158                if (n && n < len)
 159                        buf[n++] = ',';
 160
 161                if (nr_cpu_ids == total_cpus-1)
 162                        n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids);
 163                else
 164                        n += snprintf(&buf[n], len - n, "%d-%d",
 165                                                      nr_cpu_ids, total_cpus-1);
 166        }
 167
 168        n += snprintf(&buf[n], len - n, "\n");
 169        return n;
 170}
 171static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL);
 172
 173static struct sysdev_class_attribute *cpu_state_attr[] = {
 174        &attr_online_map,
 175        &attr_possible_map,
 176        &attr_present_map,
 177        &attr_kernel_max,
 178        &attr_offline,
 179};
 180
 181static int cpu_states_init(void)
 182{
 183        int i;
 184        int err = 0;
 185
 186        for (i = 0;  i < ARRAY_SIZE(cpu_state_attr); i++) {
 187                int ret;
 188                ret = sysdev_class_create_file(&cpu_sysdev_class,
 189                                                cpu_state_attr[i]);
 190                if (!err)
 191                        err = ret;
 192        }
 193        return err;
 194}
 195
 196/*
 197 * register_cpu - Setup a sysfs device for a CPU.
 198 * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
 199 *        sysfs for this CPU.
 200 * @num - CPU number to use when creating the device.
 201 *
 202 * Initialize and register the CPU device.
 203 */
 204int __cpuinit register_cpu(struct cpu *cpu, int num)
 205{
 206        int error;
 207        cpu->node_id = cpu_to_node(num);
 208        cpu->sysdev.id = num;
 209        cpu->sysdev.cls = &cpu_sysdev_class;
 210
 211        error = sysdev_register(&cpu->sysdev);
 212
 213        if (!error && cpu->hotpluggable)
 214                register_cpu_control(cpu);
 215        if (!error)
 216                per_cpu(cpu_sys_devices, num) = &cpu->sysdev;
 217        if (!error)
 218                register_cpu_under_node(num, cpu_to_node(num));
 219
 220#ifdef CONFIG_KEXEC
 221        if (!error)
 222                error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
 223#endif
 224        return error;
 225}
 226
 227struct sys_device *get_cpu_sysdev(unsigned cpu)
 228{
 229        if (cpu < nr_cpu_ids && cpu_possible(cpu))
 230                return per_cpu(cpu_sys_devices, cpu);
 231        else
 232                return NULL;
 233}
 234EXPORT_SYMBOL_GPL(get_cpu_sysdev);
 235
 236int __init cpu_dev_init(void)
 237{
 238        int err;
 239
 240        err = sysdev_class_register(&cpu_sysdev_class);
 241        if (!err)
 242                err = cpu_states_init();
 243
 244#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
 245        if (!err)
 246                err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
 247#endif
 248
 249        return err;
 250}
 251