11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * drivers/base/cpu.c - basic CPU class support 31da177e4SLinus Torvalds */ 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds #include <linux/sysdev.h> 61da177e4SLinus Torvalds #include <linux/module.h> 71da177e4SLinus Torvalds #include <linux/init.h> 8f6a57033SAl Viro #include <linux/sched.h> 91da177e4SLinus Torvalds #include <linux/cpu.h> 101da177e4SLinus Torvalds #include <linux/topology.h> 111da177e4SLinus Torvalds #include <linux/device.h> 1276b67ed9SKAMEZAWA Hiroyuki #include <linux/node.h> 131da177e4SLinus Torvalds 14a1bdc7aaSBen Dooks #include "base.h" 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds struct sysdev_class cpu_sysdev_class = { 17af5ca3f4SKay Sievers .name = "cpu", 181da177e4SLinus Torvalds }; 191da177e4SLinus Torvalds EXPORT_SYMBOL(cpu_sysdev_class); 201da177e4SLinus Torvalds 21e37d05daSMike Travis static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices); 22ad74557aSAshok Raj 231da177e4SLinus Torvalds #ifdef CONFIG_HOTPLUG_CPU 244a0b2b4dSAndi Kleen static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr, 254a0b2b4dSAndi Kleen char *buf) 261da177e4SLinus Torvalds { 271da177e4SLinus Torvalds struct cpu *cpu = container_of(dev, struct cpu, sysdev); 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id)); 301da177e4SLinus Torvalds } 311da177e4SLinus Torvalds 324a0b2b4dSAndi Kleen static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr, 334a0b2b4dSAndi Kleen const char *buf, size_t count) 341da177e4SLinus Torvalds { 351da177e4SLinus Torvalds struct cpu *cpu = container_of(dev, struct cpu, sysdev); 361da177e4SLinus Torvalds ssize_t ret; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds switch (buf[0]) { 391da177e4SLinus Torvalds case '0': 401da177e4SLinus Torvalds ret = cpu_down(cpu->sysdev.id); 411da177e4SLinus Torvalds if (!ret) 42312c004dSKay Sievers kobject_uevent(&dev->kobj, KOBJ_OFFLINE); 431da177e4SLinus Torvalds break; 441da177e4SLinus Torvalds case '1': 451da177e4SLinus Torvalds ret = cpu_up(cpu->sysdev.id); 46fb69c390SNathan Lynch if (!ret) 47312c004dSKay Sievers kobject_uevent(&dev->kobj, KOBJ_ONLINE); 481da177e4SLinus Torvalds break; 491da177e4SLinus Torvalds default: 501da177e4SLinus Torvalds ret = -EINVAL; 511da177e4SLinus Torvalds } 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds if (ret >= 0) 541da177e4SLinus Torvalds ret = count; 551da177e4SLinus Torvalds return ret; 561da177e4SLinus Torvalds } 579eb3ff40SUlrich Drepper static SYSDEV_ATTR(online, 0644, show_online, store_online); 581da177e4SLinus Torvalds 596c847402SSam Ravnborg static void __cpuinit register_cpu_control(struct cpu *cpu) 601da177e4SLinus Torvalds { 611da177e4SLinus Torvalds sysdev_create_file(&cpu->sysdev, &attr_online); 621da177e4SLinus Torvalds } 6376b67ed9SKAMEZAWA Hiroyuki void unregister_cpu(struct cpu *cpu) 641da177e4SLinus Torvalds { 65ad74557aSAshok Raj int logical_cpu = cpu->sysdev.id; 661da177e4SLinus Torvalds 6776b67ed9SKAMEZAWA Hiroyuki unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); 6876b67ed9SKAMEZAWA Hiroyuki 691da177e4SLinus Torvalds sysdev_remove_file(&cpu->sysdev, &attr_online); 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds sysdev_unregister(&cpu->sysdev); 72e37d05daSMike Travis per_cpu(cpu_sys_devices, logical_cpu) = NULL; 731da177e4SLinus Torvalds return; 741da177e4SLinus Torvalds } 751da177e4SLinus Torvalds #else /* ... !CONFIG_HOTPLUG_CPU */ 761da177e4SLinus Torvalds static inline void register_cpu_control(struct cpu *cpu) 771da177e4SLinus Torvalds { 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds #endif /* CONFIG_HOTPLUG_CPU */ 801da177e4SLinus Torvalds 8151be5606SVivek Goyal #ifdef CONFIG_KEXEC 8251be5606SVivek Goyal #include <linux/kexec.h> 8351be5606SVivek Goyal 844a0b2b4dSAndi Kleen static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr, 854a0b2b4dSAndi Kleen char *buf) 8651be5606SVivek Goyal { 8751be5606SVivek Goyal struct cpu *cpu = container_of(dev, struct cpu, sysdev); 8851be5606SVivek Goyal ssize_t rc; 8951be5606SVivek Goyal unsigned long long addr; 9051be5606SVivek Goyal int cpunum; 9151be5606SVivek Goyal 9251be5606SVivek Goyal cpunum = cpu->sysdev.id; 9351be5606SVivek Goyal 9451be5606SVivek Goyal /* 9551be5606SVivek Goyal * Might be reading other cpu's data based on which cpu read thread 9651be5606SVivek Goyal * has been scheduled. But cpu data (memory) is allocated once during 9751be5606SVivek Goyal * boot up and this data does not change there after. Hence this 9851be5606SVivek Goyal * operation should be safe. No locking required. 9951be5606SVivek Goyal */ 10051be5606SVivek Goyal addr = __pa(per_cpu_ptr(crash_notes, cpunum)); 10151be5606SVivek Goyal rc = sprintf(buf, "%Lx\n", addr); 10251be5606SVivek Goyal return rc; 10351be5606SVivek Goyal } 10451be5606SVivek Goyal static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL); 10551be5606SVivek Goyal #endif 10651be5606SVivek Goyal 1071da177e4SLinus Torvalds /* 1089d1fe323SMike Travis * Print cpu online, possible, present, and system maps 1099d1fe323SMike Travis */ 1109d1fe323SMike Travis static ssize_t print_cpus_map(char *buf, cpumask_t *map) 1119d1fe323SMike Travis { 11229c0177eSRusty Russell int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map); 1139d1fe323SMike Travis 1149d1fe323SMike Travis buf[n++] = '\n'; 1159d1fe323SMike Travis buf[n] = '\0'; 1169d1fe323SMike Travis return n; 1179d1fe323SMike Travis } 1189d1fe323SMike Travis 1199d1fe323SMike Travis #define print_cpus_func(type) \ 1209d1fe323SMike Travis static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \ 1219d1fe323SMike Travis { \ 1229d1fe323SMike Travis return print_cpus_map(buf, &cpu_##type##_map); \ 1239d1fe323SMike Travis } \ 124143aa5c5SMike Travis static struct sysdev_class_attribute attr_##type##_map = \ 1259d1fe323SMike Travis _SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL) 1269d1fe323SMike Travis 1279d1fe323SMike Travis print_cpus_func(online); 1289d1fe323SMike Travis print_cpus_func(possible); 1299d1fe323SMike Travis print_cpus_func(present); 1309d1fe323SMike Travis 131e057d7aeSMike Travis /* 132e057d7aeSMike Travis * Print values for NR_CPUS and offlined cpus 133e057d7aeSMike Travis */ 134e057d7aeSMike Travis static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf) 135e057d7aeSMike Travis { 1368fd2d2d5SMike Travis int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1); 137e057d7aeSMike Travis return n; 138e057d7aeSMike Travis } 139e057d7aeSMike Travis static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL); 140e057d7aeSMike Travis 141e057d7aeSMike Travis /* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */ 142e057d7aeSMike Travis unsigned int total_cpus; 143e057d7aeSMike Travis 144e057d7aeSMike Travis static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf) 145e057d7aeSMike Travis { 146e057d7aeSMike Travis int n = 0, len = PAGE_SIZE-2; 147e057d7aeSMike Travis cpumask_var_t offline; 148e057d7aeSMike Travis 149e057d7aeSMike Travis /* display offline cpus < nr_cpu_ids */ 150e057d7aeSMike Travis if (!alloc_cpumask_var(&offline, GFP_KERNEL)) 151e057d7aeSMike Travis return -ENOMEM; 152e057d7aeSMike Travis cpumask_complement(offline, cpu_online_mask); 153e057d7aeSMike Travis n = cpulist_scnprintf(buf, len, offline); 154e057d7aeSMike Travis free_cpumask_var(offline); 155e057d7aeSMike Travis 156e057d7aeSMike Travis /* display offline cpus >= nr_cpu_ids */ 157e057d7aeSMike Travis if (total_cpus && nr_cpu_ids < total_cpus) { 158e057d7aeSMike Travis if (n && n < len) 159e057d7aeSMike Travis buf[n++] = ','; 160e057d7aeSMike Travis 161e057d7aeSMike Travis if (nr_cpu_ids == total_cpus-1) 162e057d7aeSMike Travis n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids); 163e057d7aeSMike Travis else 164e057d7aeSMike Travis n += snprintf(&buf[n], len - n, "%d-%d", 165e057d7aeSMike Travis nr_cpu_ids, total_cpus-1); 166e057d7aeSMike Travis } 167e057d7aeSMike Travis 168e057d7aeSMike Travis n += snprintf(&buf[n], len - n, "\n"); 169e057d7aeSMike Travis return n; 170e057d7aeSMike Travis } 171e057d7aeSMike Travis static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL); 172e057d7aeSMike Travis 173143aa5c5SMike Travis static struct sysdev_class_attribute *cpu_state_attr[] = { 1749d1fe323SMike Travis &attr_online_map, 1759d1fe323SMike Travis &attr_possible_map, 1769d1fe323SMike Travis &attr_present_map, 177e057d7aeSMike Travis &attr_kernel_max, 178e057d7aeSMike Travis &attr_offline, 1799d1fe323SMike Travis }; 1809d1fe323SMike Travis 1819d1fe323SMike Travis static int cpu_states_init(void) 1829d1fe323SMike Travis { 1839d1fe323SMike Travis int i; 1849d1fe323SMike Travis int err = 0; 1859d1fe323SMike Travis 1869d1fe323SMike Travis for (i = 0; i < ARRAY_SIZE(cpu_state_attr); i++) { 1879d1fe323SMike Travis int ret; 1889d1fe323SMike Travis ret = sysdev_class_create_file(&cpu_sysdev_class, 1899d1fe323SMike Travis cpu_state_attr[i]); 1909d1fe323SMike Travis if (!err) 1919d1fe323SMike Travis err = ret; 1929d1fe323SMike Travis } 1939d1fe323SMike Travis return err; 1949d1fe323SMike Travis } 1959d1fe323SMike Travis 1969d1fe323SMike Travis /* 197405ae7d3SRobert P. J. Day * register_cpu - Setup a sysfs device for a CPU. 19872486f1fSSiddha, Suresh B * @cpu - cpu->hotpluggable field set to 1 will generate a control file in 19972486f1fSSiddha, Suresh B * sysfs for this CPU. 2001da177e4SLinus Torvalds * @num - CPU number to use when creating the device. 2011da177e4SLinus Torvalds * 2021da177e4SLinus Torvalds * Initialize and register the CPU device. 2031da177e4SLinus Torvalds */ 20433b5f31bSRandy Dunlap int __cpuinit register_cpu(struct cpu *cpu, int num) 2051da177e4SLinus Torvalds { 2061da177e4SLinus Torvalds int error; 2071da177e4SLinus Torvalds cpu->node_id = cpu_to_node(num); 2081da177e4SLinus Torvalds cpu->sysdev.id = num; 2091da177e4SLinus Torvalds cpu->sysdev.cls = &cpu_sysdev_class; 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds error = sysdev_register(&cpu->sysdev); 21276b67ed9SKAMEZAWA Hiroyuki 21372486f1fSSiddha, Suresh B if (!error && cpu->hotpluggable) 2141da177e4SLinus Torvalds register_cpu_control(cpu); 215ad74557aSAshok Raj if (!error) 216e37d05daSMike Travis per_cpu(cpu_sys_devices, num) = &cpu->sysdev; 21776b67ed9SKAMEZAWA Hiroyuki if (!error) 21876b67ed9SKAMEZAWA Hiroyuki register_cpu_under_node(num, cpu_to_node(num)); 21951be5606SVivek Goyal 22051be5606SVivek Goyal #ifdef CONFIG_KEXEC 22151be5606SVivek Goyal if (!error) 22251be5606SVivek Goyal error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes); 22351be5606SVivek Goyal #endif 2241da177e4SLinus Torvalds return error; 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 227a29d642aSAndrew Morton struct sys_device *get_cpu_sysdev(unsigned cpu) 228ad74557aSAshok Raj { 229e37d05daSMike Travis if (cpu < nr_cpu_ids && cpu_possible(cpu)) 230e37d05daSMike Travis return per_cpu(cpu_sys_devices, cpu); 231ad74557aSAshok Raj else 232ad74557aSAshok Raj return NULL; 233ad74557aSAshok Raj } 234ad74557aSAshok Raj EXPORT_SYMBOL_GPL(get_cpu_sysdev); 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds int __init cpu_dev_init(void) 2371da177e4SLinus Torvalds { 2385c45bf27SSiddha, Suresh B int err; 2395c45bf27SSiddha, Suresh B 2405c45bf27SSiddha, Suresh B err = sysdev_class_register(&cpu_sysdev_class); 2419d1fe323SMike Travis if (!err) 2429d1fe323SMike Travis err = cpu_states_init(); 2439d1fe323SMike Travis 2445c45bf27SSiddha, Suresh B #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) 2455c45bf27SSiddha, Suresh B if (!err) 2465c45bf27SSiddha, Suresh B err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class); 2475c45bf27SSiddha, Suresh B #endif 2485c45bf27SSiddha, Suresh B 2495c45bf27SSiddha, Suresh B return err; 2501da177e4SLinus Torvalds } 251