linux-stable/drivers/base/cpu.c
Vivek Goyal 51be5606d9 [PATCH] kdump: export per cpu crash notes pointer through sysfs
- Kexec on panic functionality allocates memory for saving cpu registers in
  case of system crash event.  Address of this allocated memory needs to be
  exported to user space, which is used by kexec-tools.

- Previously, a single /sys/kernel/crash_notes entry was being exported as
  memory allocated was a single continuous array.  Now memory allocation being
  dyanmic and per cpu based, address of per cpu buffer is exported through
  "/sys/devices/system/cpu/cpuX/crash_notes"

Signed-off-by: Vivek Goyal <vgoyal@in.ibm.com>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 08:01:26 -08:00

158 lines
3.6 KiB
C

/*
* drivers/base/cpu.c - basic CPU class support
*/
#include <linux/sysdev.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/topology.h>
#include <linux/device.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
int __attribute__((weak)) smp_prepare_cpu (int cpu)
{
return 0;
}
static ssize_t show_online(struct sys_device *dev, char *buf)
{
struct cpu *cpu = container_of(dev, struct cpu, sysdev);
return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id));
}
static ssize_t store_online(struct sys_device *dev, const char *buf,
size_t count)
{
struct cpu *cpu = container_of(dev, struct cpu, sysdev);
ssize_t ret;
switch (buf[0]) {
case '0':
ret = cpu_down(cpu->sysdev.id);
if (!ret)
kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
break;
case '1':
ret = smp_prepare_cpu(cpu->sysdev.id);
if (!ret)
ret = cpu_up(cpu->sysdev.id);
if (!ret)
kobject_uevent(&dev->kobj, KOBJ_ONLINE);
break;
default:
ret = -EINVAL;
}
if (ret >= 0)
ret = count;
return ret;
}
static SYSDEV_ATTR(online, 0600, show_online, store_online);
static void __devinit register_cpu_control(struct cpu *cpu)
{
sysdev_create_file(&cpu->sysdev, &attr_online);
}
void unregister_cpu(struct cpu *cpu, struct node *root)
{
int logical_cpu = cpu->sysdev.id;
if (root)
sysfs_remove_link(&root->sysdev.kobj,
kobject_name(&cpu->sysdev.kobj));
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.
*/
get_cpu();
addr = __pa(per_cpu_ptr(crash_notes, cpunum));
rc = sprintf(buf, "%Lx\n", addr);
put_cpu();
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
* generate a control file in sysfs for this CPU.
* @num - CPU number to use when creating the device.
*
* Initialize and register the CPU device.
*/
int __devinit register_cpu(struct cpu *cpu, int num, struct node *root)
{
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;
#ifdef CONFIG_KEXEC
if (!error)
error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
#endif
return error;
}
struct sys_device *get_cpu_sysdev(int 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);
}