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        cpu_hotplug_driver_lock();
  39        switch (buf[0]) {
  40        case '0':
  41                ret = cpu_down(cpu->sysdev.id);
  42                if (!ret)
  43                        kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
  44                break;
  45        case '1':
  46                ret = cpu_up(cpu->sysdev.id);
  47                if (!ret)
  48                        kobject_uevent(&dev->kobj, KOBJ_ONLINE);
  49                break;
  50        default:
  51                ret = -EINVAL;
  52        }
  53        cpu_hotplug_driver_unlock();
  54
  55        if (ret >= 0)
  56                ret = count;
  57        return ret;
  58}
  59static SYSDEV_ATTR(online, 0644, show_online, store_online);
  60
  61static void __cpuinit register_cpu_control(struct cpu *cpu)
  62{
  63        sysdev_create_file(&cpu->sysdev, &attr_online);
  64}
  65void unregister_cpu(struct cpu *cpu)
  66{
  67        int logical_cpu = cpu->sysdev.id;
  68
  69        unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
  70
  71        sysdev_remove_file(&cpu->sysdev, &attr_online);
  72
  73        sysdev_unregister(&cpu->sysdev);
  74        per_cpu(cpu_sys_devices, logical_cpu) = NULL;
  75        return;
  76}
  77
  78#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
  79static ssize_t cpu_probe_store(struct class *class, const char *buf,
  80                               size_t count)
  81{
  82        return arch_cpu_probe(buf, count);
  83}
  84
  85static ssize_t cpu_release_store(struct class *class, const char *buf,
  86                                 size_t count)
  87{
  88        return arch_cpu_release(buf, count);
  89}
  90
  91static CLASS_ATTR(probe, S_IWUSR, NULL, cpu_probe_store);
  92static CLASS_ATTR(release, S_IWUSR, NULL, cpu_release_store);
  93
  94int __init cpu_probe_release_init(void)
  95{
  96        int rc;
  97
  98        rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
  99                               &class_attr_probe.attr);
 100        if (!rc)
 101                rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
 102                                       &class_attr_release.attr);
 103
 104        return rc;
 105}
 106device_initcall(cpu_probe_release_init);
 107#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
 108
 109#else /* ... !CONFIG_HOTPLUG_CPU */
 110static inline void register_cpu_control(struct cpu *cpu)
 111{
 112}
 113#endif /* CONFIG_HOTPLUG_CPU */
 114
 115#ifdef CONFIG_KEXEC
 116#include <linux/kexec.h>
 117
 118static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr,
 119                                char *buf)
 120{
 121        struct cpu *cpu = container_of(dev, struct cpu, sysdev);
 122        ssize_t rc;
 123        unsigned long long addr;
 124        int cpunum;
 125
 126        cpunum = cpu->sysdev.id;
 127
 128        /*
 129         * Might be reading other cpu's data based on which cpu read thread
 130         * has been scheduled. But cpu data (memory) is allocated once during
 131         * boot up and this data does not change there after. Hence this
 132         * operation should be safe. No locking required.
 133         */
 134        addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpunum));
 135        rc = sprintf(buf, "%Lx\n", addr);
 136        return rc;
 137}
 138static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
 139#endif
 140
 141/*
 142 * Print cpu online, possible, present, and system maps
 143 */
 144static ssize_t print_cpus_map(char *buf, const struct cpumask *map)
 145{
 146        int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map);
 147
 148        buf[n++] = '\n';
 149        buf[n] = '\0';
 150        return n;
 151}
 152
 153#define print_cpus_func(type) \
 154static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \
 155{                                                                       \
 156        return print_cpus_map(buf, cpu_##type##_mask);                  \
 157}                                                                       \
 158static struct sysdev_class_attribute attr_##type##_map =                \
 159        _SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)
 160
 161print_cpus_func(online);
 162print_cpus_func(possible);
 163print_cpus_func(present);
 164
 165/*
 166 * Print values for NR_CPUS and offlined cpus
 167 */
 168static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf)
 169{
 170        int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
 171        return n;
 172}
 173static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
 174
 175/* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
 176unsigned int total_cpus;
 177
 178static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf)
 179{
 180        int n = 0, len = PAGE_SIZE-2;
 181        cpumask_var_t offline;
 182
 183        /* display offline cpus < nr_cpu_ids */
 184        if (!alloc_cpumask_var(&offline, GFP_KERNEL))
 185                return -ENOMEM;
 186        cpumask_complement(offline, cpu_online_mask);
 187        n = cpulist_scnprintf(buf, len, offline);
 188        free_cpumask_var(offline);
 189
 190        /* display offline cpus >= nr_cpu_ids */
 191        if (total_cpus && nr_cpu_ids < total_cpus) {
 192                if (n && n < len)
 193                        buf[n++] = ',';
 194
 195                if (nr_cpu_ids == total_cpus-1)
 196                        n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids);
 197                else
 198                        n += snprintf(&buf[n], len - n, "%d-%d",
 199                                                      nr_cpu_ids, total_cpus-1);
 200        }
 201
 202        n += snprintf(&buf[n], len - n, "\n");
 203        return n;
 204}
 205static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL);
 206
 207static struct sysdev_class_attribute *cpu_state_attr[] = {
 208        &attr_online_map,
 209        &attr_possible_map,
 210        &attr_present_map,
 211        &attr_kernel_max,
 212        &attr_offline,
 213};
 214
 215static int cpu_states_init(void)
 216{
 217        int i;
 218        int err = 0;
 219
 220        for (i = 0;  i < ARRAY_SIZE(cpu_state_attr); i++) {
 221                int ret;
 222                ret = sysdev_class_create_file(&cpu_sysdev_class,
 223                                                cpu_state_attr[i]);
 224                if (!err)
 225                        err = ret;
 226        }
 227        return err;
 228}
 229
 230/*
 231 * register_cpu - Setup a sysfs device for a CPU.
 232 * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
 233 *        sysfs for this CPU.
 234 * @num - CPU number to use when creating the device.
 235 *
 236 * Initialize and register the CPU device.
 237 */
 238int __cpuinit register_cpu(struct cpu *cpu, int num)
 239{
 240        int error;
 241        cpu->node_id = cpu_to_node(num);
 242        cpu->sysdev.id = num;
 243        cpu->sysdev.cls = &cpu_sysdev_class;
 244
 245        error = sysdev_register(&cpu->sysdev);
 246
 247        if (!error && cpu->hotpluggable)
 248                register_cpu_control(cpu);
 249        if (!error)
 250                per_cpu(cpu_sys_devices, num) = &cpu->sysdev;
 251        if (!error)
 252                register_cpu_under_node(num, cpu_to_node(num));
 253
 254#ifdef CONFIG_KEXEC
 255        if (!error)
 256                error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
 257#endif
 258        return error;
 259}
 260
 261struct sys_device *get_cpu_sysdev(unsigned cpu)
 262{
 263        if (cpu < nr_cpu_ids && cpu_possible(cpu))
 264                return per_cpu(cpu_sys_devices, cpu);
 265        else
 266                return NULL;
 267}
 268EXPORT_SYMBOL_GPL(get_cpu_sysdev);
 269
 270int __init cpu_dev_init(void)
 271{
 272        int err;
 273
 274        err = sysdev_class_register(&cpu_sysdev_class);
 275        if (!err)
 276                err = cpu_states_init();
 277
 278#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
 279        if (!err)
 280                err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
 281#endif
 282
 283        return err;
 284}
 285
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.