ipmi: Add a panic handler for IPMI users

Users of the IPMI code had their own panic handlers, but the
order was not necessarily right, the base IPMI code would
need to handle the panic first, and the user had no way to
know if the IPMI interface could run at panic time.

Add a panic handler to the user interface, it is called if
non-NULL and the interface the user is on is capable of panic
handling.  It also cleans up the panic log handling a bit to
reuse the existing interface loop in the main panic handler.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
This commit is contained in:
Corey Minyard 2018-03-28 13:19:25 -05:00
parent e238491704
commit 91e2dd0a47
2 changed files with 108 additions and 108 deletions

View File

@ -4956,13 +4956,15 @@ static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
} }
} }
static void send_panic_events(char *str) static void send_panic_events(ipmi_smi_t intf, char *str)
{ {
struct kernel_ipmi_msg msg; struct kernel_ipmi_msg msg;
ipmi_smi_t intf; unsigned char data[16];
unsigned char data[16];
struct ipmi_system_interface_addr *si; struct ipmi_system_interface_addr *si;
struct ipmi_addr addr; struct ipmi_addr addr;
char *p = str;
struct ipmi_ipmb_addr *ipmb;
int j;
if (ipmi_send_panic_event == IPMI_SEND_PANIC_EVENT_NONE) if (ipmi_send_panic_event == IPMI_SEND_PANIC_EVENT_NONE)
return; return;
@ -4993,15 +4995,8 @@ static void send_panic_events(char *str)
data[7] = str[2]; data[7] = str[2];
} }
/* For every registered interface, send the event. */ /* Send the event announcing the panic. */
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { ipmi_panic_request_and_wait(intf, &addr, &msg);
if (!intf->handlers || !intf->handlers->poll)
/* Interface is not ready or can't run at panic time. */
continue;
/* Send the event announcing the panic. */
ipmi_panic_request_and_wait(intf, &addr, &msg);
}
/* /*
* On every interface, dump a bunch of OEM event holding the * On every interface, dump a bunch of OEM event holding the
@ -5010,111 +5005,100 @@ static void send_panic_events(char *str)
if (ipmi_send_panic_event != IPMI_SEND_PANIC_EVENT_STRING || !str) if (ipmi_send_panic_event != IPMI_SEND_PANIC_EVENT_STRING || !str)
return; return;
/* For every registered interface, send the event. */ /*
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { * intf_num is used as an marker to tell if the
char *p = str; * interface is valid. Thus we need a read barrier to
struct ipmi_ipmb_addr *ipmb; * make sure data fetched before checking intf_num
int j; * won't be used.
*/
smp_rmb();
if (intf->intf_num == -1) /*
/* Interface was not ready yet. */ * First job here is to figure out where to send the
continue; * OEM events. There's no way in IPMI to send OEM
* events using an event send command, so we have to
* find the SEL to put them in and stick them in
* there.
*/
/* /* Get capabilities from the get device id. */
* intf_num is used as an marker to tell if the intf->local_sel_device = 0;
* interface is valid. Thus we need a read barrier to intf->local_event_generator = 0;
* make sure data fetched before checking intf_num intf->event_receiver = 0;
* won't be used.
*/
smp_rmb();
/* /* Request the device info from the local MC. */
* First job here is to figure out where to send the msg.netfn = IPMI_NETFN_APP_REQUEST;
* OEM events. There's no way in IPMI to send OEM msg.cmd = IPMI_GET_DEVICE_ID_CMD;
* events using an event send command, so we have to msg.data = NULL;
* find the SEL to put them in and stick them in msg.data_len = 0;
* there. intf->null_user_handler = device_id_fetcher;
*/ ipmi_panic_request_and_wait(intf, &addr, &msg);
/* Get capabilities from the get device id. */ if (intf->local_event_generator) {
intf->local_sel_device = 0; /* Request the event receiver from the local MC. */
intf->local_event_generator = 0; msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST;
intf->event_receiver = 0; msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD;
/* Request the device info from the local MC. */
msg.netfn = IPMI_NETFN_APP_REQUEST;
msg.cmd = IPMI_GET_DEVICE_ID_CMD;
msg.data = NULL; msg.data = NULL;
msg.data_len = 0; msg.data_len = 0;
intf->null_user_handler = device_id_fetcher; intf->null_user_handler = event_receiver_fetcher;
ipmi_panic_request_and_wait(intf, &addr, &msg); ipmi_panic_request_and_wait(intf, &addr, &msg);
}
intf->null_user_handler = NULL;
if (intf->local_event_generator) { /*
/* Request the event receiver from the local MC. */ * Validate the event receiver. The low bit must not
msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST; * be 1 (it must be a valid IPMB address), it cannot
msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD; * be zero, and it must not be my address.
msg.data = NULL; */
msg.data_len = 0; if (((intf->event_receiver & 1) == 0)
intf->null_user_handler = event_receiver_fetcher; && (intf->event_receiver != 0)
ipmi_panic_request_and_wait(intf, &addr, &msg); && (intf->event_receiver != intf->addrinfo[0].address)) {
}
intf->null_user_handler = NULL;
/* /*
* Validate the event receiver. The low bit must not * The event receiver is valid, send an IPMB
* be 1 (it must be a valid IPMB address), it cannot * message.
* be zero, and it must not be my address.
*/ */
if (((intf->event_receiver & 1) == 0) ipmb = (struct ipmi_ipmb_addr *) &addr;
&& (intf->event_receiver != 0) ipmb->addr_type = IPMI_IPMB_ADDR_TYPE;
&& (intf->event_receiver != intf->addrinfo[0].address)) { ipmb->channel = 0; /* FIXME - is this right? */
/* ipmb->lun = intf->event_receiver_lun;
* The event receiver is valid, send an IPMB ipmb->slave_addr = intf->event_receiver;
* message. } else if (intf->local_sel_device) {
*/ /*
ipmb = (struct ipmi_ipmb_addr *) &addr; * The event receiver was not valid (or was
ipmb->addr_type = IPMI_IPMB_ADDR_TYPE; * me), but I am an SEL device, just dump it
ipmb->channel = 0; /* FIXME - is this right? */ * in my SEL.
ipmb->lun = intf->event_receiver_lun; */
ipmb->slave_addr = intf->event_receiver; si = (struct ipmi_system_interface_addr *) &addr;
} else if (intf->local_sel_device) { si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
/* si->channel = IPMI_BMC_CHANNEL;
* The event receiver was not valid (or was si->lun = 0;
* me), but I am an SEL device, just dump it } else
* in my SEL. return; /* No where to send the event. */
*/
si = (struct ipmi_system_interface_addr *) &addr;
si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
si->channel = IPMI_BMC_CHANNEL;
si->lun = 0;
} else
continue; /* No where to send the event. */
msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */ msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */
msg.cmd = IPMI_ADD_SEL_ENTRY_CMD; msg.cmd = IPMI_ADD_SEL_ENTRY_CMD;
msg.data = data; msg.data = data;
msg.data_len = 16; msg.data_len = 16;
j = 0; j = 0;
while (*p) { while (*p) {
int size = strlen(p); int size = strlen(p);
if (size > 11) if (size > 11)
size = 11; size = 11;
data[0] = 0; data[0] = 0;
data[1] = 0; data[1] = 0;
data[2] = 0xf0; /* OEM event without timestamp. */ data[2] = 0xf0; /* OEM event without timestamp. */
data[3] = intf->addrinfo[0].address; data[3] = intf->addrinfo[0].address;
data[4] = j++; /* sequence # */ data[4] = j++; /* sequence # */
/* /*
* Always give 11 bytes, so strncpy will fill * Always give 11 bytes, so strncpy will fill
* it with zeroes for me. * it with zeroes for me.
*/ */
strncpy(data+5, p, 11); strncpy(data+5, p, 11);
p += size; p += size;
ipmi_panic_request_and_wait(intf, &addr, &msg); ipmi_panic_request_and_wait(intf, &addr, &msg);
}
} }
} }
@ -5125,6 +5109,7 @@ static int panic_event(struct notifier_block *this,
void *ptr) void *ptr)
{ {
ipmi_smi_t intf; ipmi_smi_t intf;
ipmi_user_t user;
if (has_panicked) if (has_panicked)
return NOTIFY_DONE; return NOTIFY_DONE;
@ -5132,10 +5117,13 @@ static int panic_event(struct notifier_block *this,
/* For every registered interface, set it to run to completion. */ /* For every registered interface, set it to run to completion. */
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
if (!intf->handlers) if (!intf->handlers || intf->intf_num == -1)
/* Interface is not ready. */ /* Interface is not ready. */
continue; continue;
if (!intf->handlers->poll)
continue;
/* /*
* If we were interrupted while locking xmit_msgs_lock or * If we were interrupted while locking xmit_msgs_lock or
* waiting_rcv_msgs_lock, the corresponding list may be * waiting_rcv_msgs_lock, the corresponding list may be
@ -5157,9 +5145,15 @@ static int panic_event(struct notifier_block *this,
if (intf->handlers->set_run_to_completion) if (intf->handlers->set_run_to_completion)
intf->handlers->set_run_to_completion(intf->send_info, intf->handlers->set_run_to_completion(intf->send_info,
1); 1);
}
send_panic_events(ptr); list_for_each_entry_rcu(user, &intf->users, link) {
if (user->handler->ipmi_panic_handler)
user->handler->ipmi_panic_handler(
user->handler_data);
}
send_panic_events(intf, ptr);
}
return NOTIFY_DONE; return NOTIFY_DONE;
} }

View File

@ -77,6 +77,12 @@ struct ipmi_user_hndl {
/* Called when the interface detects a watchdog pre-timeout. If /* Called when the interface detects a watchdog pre-timeout. If
this is NULL, it will be ignored for the user. */ this is NULL, it will be ignored for the user. */
void (*ipmi_watchdog_pretimeout)(void *handler_data); void (*ipmi_watchdog_pretimeout)(void *handler_data);
/*
* If not NULL, called at panic time after the interface has
* been set up to handle run to completion.
*/
void (*ipmi_panic_handler)(void *handler_data);
}; };
/* Create a new user of the IPMI layer on the given interface number. */ /* Create a new user of the IPMI layer on the given interface number. */