Commit 3b625943 authored by Corey Minyard's avatar Corey Minyard Committed by Linus Torvalds

[PATCH] ipmi: add power cycle capability

This patch to adds "power cycle" functionality to the IPMI power off module
ipmi_poweroff.  It also contains changes to support procfs control of the
feature.

The power cycle action is considered an optional chassis control in the IPMI
specification.  However, it is definitely useful when the hardware supports
it.  A power cycle is usually required in order to reset a firmware in a bad
state.  This action is critical to allow remote management of servers.

The implementation adds power cycle as optional to the ipmi_poweroff module.
It can be modified dynamically through the proc entry mentioned above.  During
a power down and enabled, the power cycle command is sent to the BMC firmware.
 If it fails either due to non-support or some error, it will retry to send
the command as power off.
Signed-off-by: default avatarChristopher A. Poblete <Chris_Poblete@dell.com>
Signed-off-by: default avatarCorey Minyard <minyard@acm.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 8f43f84f
...@@ -594,3 +594,23 @@ an event generator, the event receiver from the local management ...@@ -594,3 +594,23 @@ an event generator, the event receiver from the local management
controller will be queried and the events sent to the SEL on that controller will be queried and the events sent to the SEL on that
device. Otherwise, the events go nowhere since there is nowhere to device. Otherwise, the events go nowhere since there is nowhere to
send them. send them.
Poweroff
--------
If the poweroff capability is selected, the IPMI driver will install
a shutdown function into the standard poweroff function pointer. This
is in the ipmi_poweroff module. When the system requests a powerdown,
it will send the proper IPMI commands to do this. This is supported on
several platforms.
There is a module parameter named "poweroff_control" that may either be zero
(do a power down) or 2 (do a power cycle, power the system off, then power
it on in a few seconds). Setting ipmi_poweroff.poweroff_control=x will do
the same thing on the kernel command line. The parameter is also available
via the proc filesystem in /proc/ipmi/poweroff_control. Note that if the
system does not support power cycling, it will always to the power off.
Note that if you have ACPI enabled, the system will prefer using ACPI to
power off.
...@@ -54,7 +54,9 @@ static int ipmi_init_msghandler(void); ...@@ -54,7 +54,9 @@ static int ipmi_init_msghandler(void);
static int initialized = 0; static int initialized = 0;
static struct proc_dir_entry *proc_ipmi_root = NULL; #ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_ipmi_root = NULL;
#endif /* CONFIG_PROC_FS */
#define MAX_EVENTS_IN_QUEUE 25 #define MAX_EVENTS_IN_QUEUE 25
...@@ -124,11 +126,13 @@ struct ipmi_channel ...@@ -124,11 +126,13 @@ struct ipmi_channel
unsigned char protocol; unsigned char protocol;
}; };
#ifdef CONFIG_PROC_FS
struct ipmi_proc_entry struct ipmi_proc_entry
{ {
char *name; char *name;
struct ipmi_proc_entry *next; struct ipmi_proc_entry *next;
}; };
#endif
#define IPMI_IPMB_NUM_SEQ 64 #define IPMI_IPMB_NUM_SEQ 64
#define IPMI_MAX_CHANNELS 8 #define IPMI_MAX_CHANNELS 8
...@@ -156,10 +160,13 @@ struct ipmi_smi ...@@ -156,10 +160,13 @@ struct ipmi_smi
struct ipmi_smi_handlers *handlers; struct ipmi_smi_handlers *handlers;
void *send_info; void *send_info;
#ifdef CONFIG_PROC_FS
/* A list of proc entries for this interface. This does not /* A list of proc entries for this interface. This does not
need a lock, only one thread creates it and only one thread need a lock, only one thread creates it and only one thread
destroys it. */ destroys it. */
spinlock_t proc_entry_lock;
struct ipmi_proc_entry *proc_entries; struct ipmi_proc_entry *proc_entries;
#endif
/* A table of sequence numbers for this interface. We use the /* A table of sequence numbers for this interface. We use the
sequence numbers for IPMB messages that go out of the sequence numbers for IPMB messages that go out of the
...@@ -1470,8 +1477,9 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, ...@@ -1470,8 +1477,9 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
read_proc_t *read_proc, write_proc_t *write_proc, read_proc_t *read_proc, write_proc_t *write_proc,
void *data, struct module *owner) void *data, struct module *owner)
{ {
struct proc_dir_entry *file;
int rv = 0; int rv = 0;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *file;
struct ipmi_proc_entry *entry; struct ipmi_proc_entry *entry;
/* Create a list element. */ /* Create a list element. */
...@@ -1497,10 +1505,13 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, ...@@ -1497,10 +1505,13 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
file->write_proc = write_proc; file->write_proc = write_proc;
file->owner = owner; file->owner = owner;
spin_lock(&smi->proc_entry_lock);
/* Stick it on the list. */ /* Stick it on the list. */
entry->next = smi->proc_entries; entry->next = smi->proc_entries;
smi->proc_entries = entry; smi->proc_entries = entry;
spin_unlock(&smi->proc_entry_lock);
} }
#endif /* CONFIG_PROC_FS */
return rv; return rv;
} }
...@@ -1509,6 +1520,7 @@ static int add_proc_entries(ipmi_smi_t smi, int num) ...@@ -1509,6 +1520,7 @@ static int add_proc_entries(ipmi_smi_t smi, int num)
{ {
int rv = 0; int rv = 0;
#ifdef CONFIG_PROC_FS
sprintf(smi->proc_dir_name, "%d", num); sprintf(smi->proc_dir_name, "%d", num);
smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root);
if (!smi->proc_dir) if (!smi->proc_dir)
...@@ -1531,14 +1543,17 @@ static int add_proc_entries(ipmi_smi_t smi, int num) ...@@ -1531,14 +1543,17 @@ static int add_proc_entries(ipmi_smi_t smi, int num)
rv = ipmi_smi_add_proc_entry(smi, "version", rv = ipmi_smi_add_proc_entry(smi, "version",
version_file_read_proc, NULL, version_file_read_proc, NULL,
smi, THIS_MODULE); smi, THIS_MODULE);
#endif /* CONFIG_PROC_FS */
return rv; return rv;
} }
static void remove_proc_entries(ipmi_smi_t smi) static void remove_proc_entries(ipmi_smi_t smi)
{ {
#ifdef CONFIG_PROC_FS
struct ipmi_proc_entry *entry; struct ipmi_proc_entry *entry;
spin_lock(&smi->proc_entry_lock);
while (smi->proc_entries) { while (smi->proc_entries) {
entry = smi->proc_entries; entry = smi->proc_entries;
smi->proc_entries = entry->next; smi->proc_entries = entry->next;
...@@ -1547,7 +1562,9 @@ static void remove_proc_entries(ipmi_smi_t smi) ...@@ -1547,7 +1562,9 @@ static void remove_proc_entries(ipmi_smi_t smi)
kfree(entry->name); kfree(entry->name);
kfree(entry); kfree(entry);
} }
spin_unlock(&smi->proc_entry_lock);
remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); remove_proc_entry(smi->proc_dir_name, proc_ipmi_root);
#endif /* CONFIG_PROC_FS */
} }
static int static int
...@@ -1694,6 +1711,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, ...@@ -1694,6 +1711,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
new_intf->seq_table[j].seqid = 0; new_intf->seq_table[j].seqid = 0;
} }
new_intf->curr_seq = 0; new_intf->curr_seq = 0;
#ifdef CONFIG_PROC_FS
spin_lock_init(&(new_intf->proc_entry_lock));
#endif
spin_lock_init(&(new_intf->waiting_msgs_lock)); spin_lock_init(&(new_intf->waiting_msgs_lock));
INIT_LIST_HEAD(&(new_intf->waiting_msgs)); INIT_LIST_HEAD(&(new_intf->waiting_msgs));
spin_lock_init(&(new_intf->events_lock)); spin_lock_init(&(new_intf->events_lock));
...@@ -3085,6 +3105,7 @@ static int ipmi_init_msghandler(void) ...@@ -3085,6 +3105,7 @@ static int ipmi_init_msghandler(void)
ipmi_interfaces[i] = NULL; ipmi_interfaces[i] = NULL;
} }
#ifdef CONFIG_PROC_FS
proc_ipmi_root = proc_mkdir("ipmi", NULL); proc_ipmi_root = proc_mkdir("ipmi", NULL);
if (!proc_ipmi_root) { if (!proc_ipmi_root) {
printk(KERN_ERR PFX "Unable to create IPMI proc dir"); printk(KERN_ERR PFX "Unable to create IPMI proc dir");
...@@ -3092,6 +3113,7 @@ static int ipmi_init_msghandler(void) ...@@ -3092,6 +3113,7 @@ static int ipmi_init_msghandler(void)
} }
proc_ipmi_root->owner = THIS_MODULE; proc_ipmi_root->owner = THIS_MODULE;
#endif /* CONFIG_PROC_FS */
init_timer(&ipmi_timer); init_timer(&ipmi_timer);
ipmi_timer.data = 0; ipmi_timer.data = 0;
...@@ -3129,7 +3151,9 @@ static __exit void cleanup_ipmi(void) ...@@ -3129,7 +3151,9 @@ static __exit void cleanup_ipmi(void)
atomic_inc(&stop_operation); atomic_inc(&stop_operation);
del_timer_sync(&ipmi_timer); del_timer_sync(&ipmi_timer);
#ifdef CONFIG_PROC_FS
remove_proc_entry(proc_ipmi_root->name, &proc_root); remove_proc_entry(proc_ipmi_root->name, &proc_root);
#endif /* CONFIG_PROC_FS */
initialized = 0; initialized = 0;
...@@ -3170,4 +3194,5 @@ EXPORT_SYMBOL(ipmi_get_my_address); ...@@ -3170,4 +3194,5 @@ EXPORT_SYMBOL(ipmi_get_my_address);
EXPORT_SYMBOL(ipmi_set_my_LUN); EXPORT_SYMBOL(ipmi_set_my_LUN);
EXPORT_SYMBOL(ipmi_get_my_LUN); EXPORT_SYMBOL(ipmi_get_my_LUN);
EXPORT_SYMBOL(ipmi_smi_add_proc_entry); EXPORT_SYMBOL(ipmi_smi_add_proc_entry);
EXPORT_SYMBOL(proc_ipmi_root);
EXPORT_SYMBOL(ipmi_user_set_run_to_completion); EXPORT_SYMBOL(ipmi_user_set_run_to_completion);
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/proc_fs.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/ipmi.h> #include <linux/ipmi.h>
#include <linux/ipmi_smi.h> #include <linux/ipmi_smi.h>
...@@ -44,6 +46,18 @@ ...@@ -44,6 +46,18 @@
/* Where to we insert our poweroff function? */ /* Where to we insert our poweroff function? */
extern void (*pm_power_off)(void); extern void (*pm_power_off)(void);
/* Definitions for controlling power off (if the system supports it). It
* conveniently matches the IPMI chassis control values. */
#define IPMI_CHASSIS_POWER_DOWN 0 /* power down, the default. */
#define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */
/* the IPMI data command */
static int poweroff_control = IPMI_CHASSIS_POWER_DOWN;
/* parameter definition to allow user to flag power cycle */
module_param(poweroff_control, int, IPMI_CHASSIS_POWER_DOWN);
MODULE_PARM_DESC(poweroff_control, " Set to 2 to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down.");
/* Stuff from the get device id command. */ /* Stuff from the get device id command. */
static unsigned int mfg_id; static unsigned int mfg_id;
static unsigned int prod_id; static unsigned int prod_id;
...@@ -349,26 +363,38 @@ static void ipmi_poweroff_chassis (ipmi_user_t user) ...@@ -349,26 +363,38 @@ static void ipmi_poweroff_chassis (ipmi_user_t user)
smi_addr.channel = IPMI_BMC_CHANNEL; smi_addr.channel = IPMI_BMC_CHANNEL;
smi_addr.lun = 0; smi_addr.lun = 0;
printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n"); powercyclefailed:
printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n",
((poweroff_control != IPMI_CHASSIS_POWER_CYCLE) ? "down" : "cycle"));
/* /*
* Power down * Power down
*/ */
send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST;
send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
data[0] = 0; /* Power down */ data[0] = poweroff_control;
send_msg.data = data; send_msg.data = data;
send_msg.data_len = sizeof(data); send_msg.data_len = sizeof(data);
rv = ipmi_request_in_rc_mode(user, rv = ipmi_request_in_rc_mode(user,
(struct ipmi_addr *) &smi_addr, (struct ipmi_addr *) &smi_addr,
&send_msg); &send_msg);
if (rv) { if (rv) {
printk(KERN_ERR PFX "Unable to send chassis powerdown message," switch (poweroff_control) {
" IPMI error 0x%x\n", rv); case IPMI_CHASSIS_POWER_CYCLE:
goto out; /* power cycle failed, default to power down */
printk(KERN_ERR PFX "Unable to send chassis power " \
"cycle message, IPMI error 0x%x\n", rv);
poweroff_control = IPMI_CHASSIS_POWER_DOWN;
goto powercyclefailed;
case IPMI_CHASSIS_POWER_DOWN:
default:
printk(KERN_ERR PFX "Unable to send chassis power " \
"down message, IPMI error 0x%x\n", rv);
break;
}
} }
out:
return; return;
} }
...@@ -430,7 +456,8 @@ static void ipmi_po_new_smi(int if_num) ...@@ -430,7 +456,8 @@ static void ipmi_po_new_smi(int if_num)
if (ready) if (ready)
return; return;
rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user); rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL,
&ipmi_user);
if (rv) { if (rv) {
printk(KERN_ERR PFX "could not create IPMI user, error %d\n", printk(KERN_ERR PFX "could not create IPMI user, error %d\n",
rv); rv);
...@@ -509,21 +536,84 @@ static struct ipmi_smi_watcher smi_watcher = ...@@ -509,21 +536,84 @@ static struct ipmi_smi_watcher smi_watcher =
}; };
#ifdef CONFIG_PROC_FS
/* displays properties to proc */
static int proc_read_chassctrl(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
return sprintf(page, "%d\t[ 0=powerdown 2=powercycle ]\n",
poweroff_control);
}
/* process property writes from proc */
static int proc_write_chassctrl(struct file *file, const char *buffer,
unsigned long count, void *data)
{
int rv = count;
unsigned int newval = 0;
sscanf(buffer, "%d", &newval);
switch (newval) {
case IPMI_CHASSIS_POWER_CYCLE:
printk(KERN_INFO PFX "power cycle is now enabled\n");
poweroff_control = newval;
break;
case IPMI_CHASSIS_POWER_DOWN:
poweroff_control = IPMI_CHASSIS_POWER_DOWN;
break;
default:
rv = -EINVAL;
break;
}
return rv;
}
#endif /* CONFIG_PROC_FS */
/* /*
* Startup and shutdown functions. * Startup and shutdown functions.
*/ */
static int ipmi_poweroff_init (void) static int ipmi_poweroff_init (void)
{ {
int rv; int rv;
struct proc_dir_entry *file;
printk ("Copyright (C) 2004 MontaVista Software -" printk ("Copyright (C) 2004 MontaVista Software -"
" IPMI Powerdown via sys_reboot version " " IPMI Powerdown via sys_reboot version "
IPMI_POWEROFF_VERSION ".\n"); IPMI_POWEROFF_VERSION ".\n");
switch (poweroff_control) {
case IPMI_CHASSIS_POWER_CYCLE:
printk(KERN_INFO PFX "Power cycle is enabled.\n");
break;
case IPMI_CHASSIS_POWER_DOWN:
default:
poweroff_control = IPMI_CHASSIS_POWER_DOWN;
break;
}
rv = ipmi_smi_watcher_register(&smi_watcher); rv = ipmi_smi_watcher_register(&smi_watcher);
if (rv) if (rv) {
printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv);
goto out_err;
}
#ifdef CONFIG_PROC_FS
file = create_proc_entry("poweroff_control", 0, proc_ipmi_root);
if (!file) {
printk(KERN_ERR PFX "Unable to create proc power control\n");
} else {
file->nlink = 1;
file->read_proc = proc_read_chassctrl;
file->write_proc = proc_write_chassctrl;
file->owner = THIS_MODULE;
}
#endif
out_err:
return rv; return rv;
} }
...@@ -532,6 +622,10 @@ static __exit void ipmi_poweroff_cleanup(void) ...@@ -532,6 +622,10 @@ static __exit void ipmi_poweroff_cleanup(void)
{ {
int rv; int rv;
#ifdef CONFIG_PROC_FS
remove_proc_entry("poweroff_control", proc_ipmi_root);
#endif
ipmi_smi_watcher_unregister(&smi_watcher); ipmi_smi_watcher_unregister(&smi_watcher);
if (ready) { if (ready) {
......
...@@ -209,6 +209,11 @@ struct kernel_ipmi_msg ...@@ -209,6 +209,11 @@ struct kernel_ipmi_msg
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
extern struct proc_dir_entry *proc_ipmi_root;
#endif /* CONFIG_PROC_FS */
/* Opaque type for a IPMI message user. One of these is needed to /* Opaque type for a IPMI message user. One of these is needed to
send and receive messages. */ send and receive messages. */
typedef struct ipmi_user *ipmi_user_t; typedef struct ipmi_user *ipmi_user_t;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment