Commit b2c03941 authored by Corey Minyard's avatar Corey Minyard Committed by Linus Torvalds

[PATCH] IPMI: Allow hot system interface remove

This modifies the IPMI driver so that a lower-level interface can be
dynamically removed while in use so it can support hot-removal of hardware.

It also adds the ability to specify and dynamically change the IPMI interface
the watchdog timer and the poweroff code use.
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 759643b8
......@@ -502,7 +502,10 @@ used to control it:
modprobe ipmi_watchdog timeout=<t> pretimeout=<t> action=<action type>
preaction=<preaction type> preop=<preop type> start_now=x
nowayout=x
nowayout=x ifnum_to_use=n
ifnum_to_use specifies which interface the watchdog timer should use.
The default is -1, which means to pick the first one registered.
The timeout is the number of seconds to the action, and the pretimeout
is the amount of seconds before the reset that the pre-timeout panic will
......@@ -624,5 +627,9 @@ command line. The parameter is also available via the proc filesystem
in /proc/sys/dev/ipmi/poweroff_powercycle. Note that if the system
does not support power cycling, it will always do the power off.
The "ifnum_to_use" parameter specifies which interface the poweroff
code should use. The default is -1, which means to pick the first one
registered.
Note that if you have ACPI enabled, the system will prefer using ACPI to
power off.
This diff is collapsed.
......@@ -43,6 +43,9 @@
#define PFX "IPMI poweroff: "
static void ipmi_po_smi_gone(int if_num);
static void ipmi_po_new_smi(int if_num, struct device *device);
/* 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. */
......@@ -51,6 +54,37 @@
/* the IPMI data command */
static int poweroff_powercycle;
/* Which interface to use, -1 means the first we see. */
static int ifnum_to_use = -1;
/* Our local state. */
static int ready = 0;
static ipmi_user_t ipmi_user;
static int ipmi_ifnum;
static void (*specific_poweroff_func)(ipmi_user_t user) = NULL;
/* Holds the old poweroff function so we can restore it on removal. */
static void (*old_poweroff_func)(void);
static int set_param_ifnum(const char *val, struct kernel_param *kp)
{
int rv = param_set_int(val, kp);
if (rv)
return rv;
if ((ifnum_to_use < 0) || (ifnum_to_use == ipmi_ifnum))
return 0;
ipmi_po_smi_gone(ipmi_ifnum);
ipmi_po_new_smi(ifnum_to_use, NULL);
return 0;
}
module_param_call(ifnum_to_use, set_param_ifnum, param_get_int,
&ifnum_to_use, 0644);
MODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog "
"timer. Setting to -1 defaults to the first registered "
"interface");
/* parameter definition to allow user to flag power cycle */
module_param(poweroff_powercycle, int, 0644);
MODULE_PARM_DESC(poweroff_powercycle, " Set to non-zero to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down.");
......@@ -440,15 +474,6 @@ static struct poweroff_function poweroff_functions[] = {
/ sizeof(struct poweroff_function))
/* Our local state. */
static int ready = 0;
static ipmi_user_t ipmi_user;
static void (*specific_poweroff_func)(ipmi_user_t user) = NULL;
/* Holds the old poweroff function so we can restore it on removal. */
static void (*old_poweroff_func)(void);
/* Called on a powerdown request. */
static void ipmi_poweroff_function (void)
{
......@@ -473,6 +498,9 @@ static void ipmi_po_new_smi(int if_num, struct device *device)
if (ready)
return;
if ((ifnum_to_use >= 0) && (ifnum_to_use != if_num))
return;
rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL,
&ipmi_user);
if (rv) {
......@@ -481,6 +509,8 @@ static void ipmi_po_new_smi(int if_num, struct device *device)
return;
}
ipmi_ifnum = if_num;
/*
* Do a get device ide and store some results, since this is
* used by several functions.
......@@ -541,9 +571,15 @@ static void ipmi_po_new_smi(int if_num, struct device *device)
static void ipmi_po_smi_gone(int if_num)
{
/* This can never be called, because once poweroff driver is
registered, the interface can't go away until the power
driver is unregistered. */
if (!ready)
return;
if (ipmi_ifnum != if_num)
return;
ready = 0;
ipmi_destroy_user(ipmi_user);
pm_power_off = old_poweroff_func;
}
static struct ipmi_smi_watcher smi_watcher =
......
......@@ -135,6 +135,7 @@
static int nowayout = WATCHDOG_NOWAYOUT;
static ipmi_user_t watchdog_user = NULL;
static int watchdog_ifnum;
/* Default the timeout to 10 seconds. */
static int timeout = 10;
......@@ -161,6 +162,8 @@ static struct fasync_struct *fasync_q = NULL;
static char pretimeout_since_last_heartbeat = 0;
static char expect_close;
static int ifnum_to_use = -1;
static DECLARE_RWSEM(register_sem);
/* Parameters to ipmi_set_timeout */
......@@ -169,6 +172,8 @@ static DECLARE_RWSEM(register_sem);
#define IPMI_SET_TIMEOUT_FORCE_HB 2
static int ipmi_set_timeout(int do_heartbeat);
static void ipmi_register_watchdog(int ipmi_intf);
static void ipmi_unregister_watchdog(int ipmi_intf);
/* If true, the driver will start running as soon as it is configured
and ready. */
......@@ -245,6 +250,26 @@ static int get_param_str(char *buffer, struct kernel_param *kp)
return strlen(buffer);
}
static int set_param_wdog_ifnum(const char *val, struct kernel_param *kp)
{
int rv = param_set_int(val, kp);
if (rv)
return rv;
if ((ifnum_to_use < 0) || (ifnum_to_use == watchdog_ifnum))
return 0;
ipmi_unregister_watchdog(watchdog_ifnum);
ipmi_register_watchdog(ifnum_to_use);
return 0;
}
module_param_call(ifnum_to_use, set_param_wdog_ifnum, get_param_int,
&ifnum_to_use, 0644);
MODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog "
"timer. Setting to -1 defaults to the first registered "
"interface");
module_param_call(timeout, set_param_int, get_param_int, &timeout, 0644);
MODULE_PARM_DESC(timeout, "Timeout value in seconds.");
......@@ -263,12 +288,13 @@ module_param_call(preop, set_param_str, get_param_str, preop_op, 0644);
MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: "
"preop_none, preop_panic, preop_give_data.");
module_param(start_now, int, 0);
module_param(start_now, int, 0444);
MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as"
"soon as the driver is loaded.");
module_param(nowayout, int, 0644);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=CONFIG_WATCHDOG_NOWAYOUT)");
/* Default state of the timer. */
static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
......@@ -872,6 +898,11 @@ static void ipmi_register_watchdog(int ipmi_intf)
if (watchdog_user)
goto out;
if ((ifnum_to_use >= 0) && (ifnum_to_use != ipmi_intf))
goto out;
watchdog_ifnum = ipmi_intf;
rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &watchdog_user);
if (rv < 0) {
printk(KERN_CRIT PFX "Unable to register with ipmi\n");
......@@ -901,6 +932,39 @@ static void ipmi_register_watchdog(int ipmi_intf)
}
}
static void ipmi_unregister_watchdog(int ipmi_intf)
{
int rv;
down_write(&register_sem);
if (!watchdog_user)
goto out;
if (watchdog_ifnum != ipmi_intf)
goto out;
/* Make sure no one can call us any more. */
misc_deregister(&ipmi_wdog_miscdev);
/* Wait to make sure the message makes it out. The lower layer has
pointers to our buffers, we want to make sure they are done before
we release our memory. */
while (atomic_read(&set_timeout_tofree))
schedule_timeout_uninterruptible(1);
/* Disconnect from IPMI. */
rv = ipmi_destroy_user(watchdog_user);
if (rv) {
printk(KERN_WARNING PFX "error unlinking from IPMI: %d\n",
rv);
}
watchdog_user = NULL;
out:
up_write(&register_sem);
}
#ifdef HAVE_NMI_HANDLER
static int
ipmi_nmi(void *dev_id, int cpu, int handled)
......@@ -1004,9 +1068,7 @@ static void ipmi_new_smi(int if_num, struct device *device)
static void ipmi_smi_gone(int if_num)
{
/* This can never be called, because once the watchdog is
registered, the interface can't go away until the watchdog
is unregistered. */
ipmi_unregister_watchdog(if_num);
}
static struct ipmi_smi_watcher smi_watcher =
......@@ -1148,30 +1210,32 @@ static int __init ipmi_wdog_init(void)
check_parms();
register_reboot_notifier(&wdog_reboot_notifier);
atomic_notifier_chain_register(&panic_notifier_list,
&wdog_panic_notifier);
rv = ipmi_smi_watcher_register(&smi_watcher);
if (rv) {
#ifdef HAVE_NMI_HANDLER
if (preaction_val == WDOG_PRETIMEOUT_NMI)
release_nmi(&ipmi_nmi_handler);
#endif
atomic_notifier_chain_unregister(&panic_notifier_list,
&wdog_panic_notifier);
unregister_reboot_notifier(&wdog_reboot_notifier);
printk(KERN_WARNING PFX "can't register smi watcher\n");
return rv;
}
register_reboot_notifier(&wdog_reboot_notifier);
atomic_notifier_chain_register(&panic_notifier_list,
&wdog_panic_notifier);
printk(KERN_INFO PFX "driver initialized\n");
return 0;
}
static __exit void ipmi_unregister_watchdog(void)
static void __exit ipmi_wdog_exit(void)
{
int rv;
down_write(&register_sem);
ipmi_smi_watcher_unregister(&smi_watcher);
ipmi_unregister_watchdog(watchdog_ifnum);
#ifdef HAVE_NMI_HANDLER
if (nmi_handler_registered)
......@@ -1179,37 +1243,8 @@ static __exit void ipmi_unregister_watchdog(void)
#endif
atomic_notifier_chain_unregister(&panic_notifier_list,
&wdog_panic_notifier);
&wdog_panic_notifier);
unregister_reboot_notifier(&wdog_reboot_notifier);
if (! watchdog_user)
goto out;
/* Make sure no one can call us any more. */
misc_deregister(&ipmi_wdog_miscdev);
/* Wait to make sure the message makes it out. The lower layer has
pointers to our buffers, we want to make sure they are done before
we release our memory. */
while (atomic_read(&set_timeout_tofree))
schedule_timeout_uninterruptible(1);
/* Disconnect from IPMI. */
rv = ipmi_destroy_user(watchdog_user);
if (rv) {
printk(KERN_WARNING PFX "error unlinking from IPMI: %d\n",
rv);
}
watchdog_user = NULL;
out:
up_write(&register_sem);
}
static void __exit ipmi_wdog_exit(void)
{
ipmi_smi_watcher_unregister(&smi_watcher);
ipmi_unregister_watchdog();
}
module_exit(ipmi_wdog_exit);
module_init(ipmi_wdog_init);
......
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