diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 8435fba73dafdcd8e847403e25dee71d2feee90c..46d14ac162123b387337a8b015f334195a21fe37 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2614,6 +2614,14 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) return; } +void ipmi_poll_interface(ipmi_user_t user) +{ + ipmi_smi_t intf = user->intf; + + if (intf->handlers->poll) + intf->handlers->poll(intf->send_info); +} + int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, struct ipmi_device_id *device_id, @@ -4166,6 +4174,7 @@ EXPORT_SYMBOL(ipmi_destroy_user); EXPORT_SYMBOL(ipmi_get_version); EXPORT_SYMBOL(ipmi_request_settime); EXPORT_SYMBOL(ipmi_request_supply_msgs); +EXPORT_SYMBOL(ipmi_poll_interface); EXPORT_SYMBOL(ipmi_register_smi); EXPORT_SYMBOL(ipmi_unregister_smi); EXPORT_SYMBOL(ipmi_register_for_cmd); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index e018932af795c73b024ca95860c2b8319b0773d0..da09eb0ef7881c363248acab6766898422d81978 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -675,7 +675,8 @@ static void handle_transaction_done(struct smi_info *smi_info) } /* Called on timeouts and events. Timeouts should pass the elapsed - time, interrupts should pass in zero. */ + time, interrupts should pass in zero. Must be called with + si_lock held and interrupts disabled. */ static enum si_sm_result smi_event_handler(struct smi_info *smi_info, int time) { @@ -892,13 +893,16 @@ static int ipmi_thread(void *data) static void poll(void *send_info) { struct smi_info *smi_info = send_info; + unsigned long flags; /* * Make sure there is some delay in the poll loop so we can * drive time forward and timeout things. */ udelay(10); + spin_lock_irqsave(&smi_info->si_lock, flags); smi_event_handler(smi_info, 10); + spin_unlock_irqrestore(&smi_info->si_lock, flags); } static void request_events(void *send_info) diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 806106b8c98013aa4fba7cb1024bd7ada3abaa5d..19a8683bd25d3c81ac6b8a84462bbd01ce763f81 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -314,8 +314,6 @@ static unsigned char ipmi_version_minor; static atomic_t preop_panic_excl = ATOMIC_INIT(-1); static int ipmi_heartbeat(void); -static void panic_halt_ipmi_heartbeat(void); - /* We use a mutex to make sure that only one thing can send a set timeout at one time, because we only have one copy of the data. @@ -440,19 +438,64 @@ static int ipmi_set_timeout(int do_heartbeat) return rv; } -static void dummy_smi_free(struct ipmi_smi_msg *msg) +static atomic_t panic_done_count = ATOMIC_INIT(0); + +static void panic_smi_free(struct ipmi_smi_msg *msg) { + atomic_dec(&panic_done_count); } -static void dummy_recv_free(struct ipmi_recv_msg *msg) +static void panic_recv_free(struct ipmi_recv_msg *msg) +{ + atomic_dec(&panic_done_count); +} + +static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg = +{ + .done = panic_smi_free +}; +static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg = { + .done = panic_recv_free +}; + +static void panic_halt_ipmi_heartbeat(void) +{ + struct kernel_ipmi_msg msg; + struct ipmi_system_interface_addr addr; + int rv; + + /* Don't reset the timer if we have the timer turned off, that + re-enables the watchdog. */ + if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) + return; + + addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + addr.channel = IPMI_BMC_CHANNEL; + addr.lun = 0; + + msg.netfn = 0x06; + msg.cmd = IPMI_WDOG_RESET_TIMER; + msg.data = NULL; + msg.data_len = 0; + rv = ipmi_request_supply_msgs(watchdog_user, + (struct ipmi_addr *) &addr, + 0, + &msg, + NULL, + &panic_halt_heartbeat_smi_msg, + &panic_halt_heartbeat_recv_msg, + 1); + if (!rv) + atomic_add(2, &panic_done_count); } + static struct ipmi_smi_msg panic_halt_smi_msg = { - .done = dummy_smi_free + .done = panic_smi_free }; static struct ipmi_recv_msg panic_halt_recv_msg = { - .done = dummy_recv_free + .done = panic_recv_free }; /* Special call, doesn't claim any locks. This is only to be called @@ -464,13 +507,21 @@ static void panic_halt_ipmi_set_timeout(void) int send_heartbeat_now; int rv; + /* Wait for the messages to be free. */ + while (atomic_read(&panic_done_count) != 0) + ipmi_poll_interface(watchdog_user); rv = i_ipmi_set_timeout(&panic_halt_smi_msg, &panic_halt_recv_msg, &send_heartbeat_now); if (!rv) { + atomic_add(2, &panic_done_count); if (send_heartbeat_now) panic_halt_ipmi_heartbeat(); - } + } else + printk(KERN_WARNING PFX + "Unable to extend the watchdog timeout."); + while (atomic_read(&panic_done_count) != 0) + ipmi_poll_interface(watchdog_user); } /* We use a semaphore to make sure that only one thing can send a @@ -499,15 +550,6 @@ static struct ipmi_recv_msg heartbeat_recv_msg = .done = heartbeat_free_recv }; -static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg = -{ - .done = dummy_smi_free -}; -static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg = -{ - .done = dummy_recv_free -}; - static int ipmi_heartbeat(void) { struct kernel_ipmi_msg msg; @@ -580,35 +622,6 @@ static int ipmi_heartbeat(void) return rv; } -static void panic_halt_ipmi_heartbeat(void) -{ - struct kernel_ipmi_msg msg; - struct ipmi_system_interface_addr addr; - - - /* Don't reset the timer if we have the timer turned off, that - re-enables the watchdog. */ - if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) - return; - - addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - addr.channel = IPMI_BMC_CHANNEL; - addr.lun = 0; - - msg.netfn = 0x06; - msg.cmd = IPMI_WDOG_RESET_TIMER; - msg.data = NULL; - msg.data_len = 0; - ipmi_request_supply_msgs(watchdog_user, - (struct ipmi_addr *) &addr, - 0, - &msg, - NULL, - &panic_halt_heartbeat_smi_msg, - &panic_halt_heartbeat_recv_msg, - 1); -} - static struct watchdog_info ident = { .options = 0, /* WDIOF_SETTIMEOUT, */ @@ -998,7 +1011,7 @@ static int wdog_reboot_handler(struct notifier_block *this, /* Make sure we only do this once. */ reboot_event_handled = 1; - if (code == SYS_DOWN || code == SYS_HALT) { + if (code == SYS_POWER_OFF || code == SYS_HALT) { /* Disable the WDT if we are shutting down. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; panic_halt_ipmi_set_timeout(); diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index 7a9db390c56a491e3221ed8154ac76ed89295c8f..c5bd28b69aec469087da75e93e28e7c789a4ca3f 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -364,6 +364,16 @@ int ipmi_request_supply_msgs(ipmi_user_t user, struct ipmi_recv_msg *supplied_recv, int priority); +/* + * Poll the IPMI interface for the user. This causes the IPMI code to + * do an immediate check for information from the driver and handle + * anything that is immediately pending. This will not block in any + * way. This is useful if you need to implement polling from the user + * for things like modifying the watchdog timeout when a panic occurs + * or disabling the watchdog timer on a reboot. + */ +void ipmi_poll_interface(ipmi_user_t user); + /* * When commands come in to the SMS, the user can register to receive * them. Only one user can be listening on a specific netfn/cmd/chan tuple