Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 895dcfd1 authored by Corey Minyard's avatar Corey Minyard Committed by Linus Torvalds
Browse files

ipmi: fix message handling during panics



The part of the IPMI driver that delivered panic information to the event
log and extended the watchdog timeout during a panic was not properly
handling the messages.  It used static messages to avoid allocation, but
wasn't properly waiting for these, or wasn't properly handling the
refcounts.

Signed-off-by: default avatarCorey Minyard <cminyard@mvista.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 7adf579c
Loading
Loading
Loading
Loading
+46 −57
Original line number Original line Diff line number Diff line
@@ -2794,16 +2794,18 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
	return;
	return;
}
}


void ipmi_poll_interface(ipmi_user_t user)
static void ipmi_poll(ipmi_smi_t intf)
{
{
	ipmi_smi_t intf = user->intf;

	if (intf->handlers->poll)
	if (intf->handlers->poll)
		intf->handlers->poll(intf->send_info);
		intf->handlers->poll(intf->send_info);

	/* In case something came in */
	/* In case something came in */
	handle_new_recv_msgs(intf);
	handle_new_recv_msgs(intf);
}
}

void ipmi_poll_interface(ipmi_user_t user)
{
	ipmi_poll(user->intf);
}
EXPORT_SYMBOL(ipmi_poll_interface);
EXPORT_SYMBOL(ipmi_poll_interface);


int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
@@ -4204,12 +4206,48 @@ EXPORT_SYMBOL(ipmi_free_recv_msg);


#ifdef CONFIG_IPMI_PANIC_EVENT
#ifdef CONFIG_IPMI_PANIC_EVENT


static atomic_t panic_done_count = ATOMIC_INIT(0);

static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
{
{
	atomic_dec(&panic_done_count);
}
}


static void dummy_recv_done_handler(struct ipmi_recv_msg *msg)
static void dummy_recv_done_handler(struct ipmi_recv_msg *msg)
{
{
	atomic_dec(&panic_done_count);
}

/*
 * Inside a panic, send a message and wait for a response.
 */
static void ipmi_panic_request_and_wait(ipmi_smi_t           intf,
					struct ipmi_addr     *addr,
					struct kernel_ipmi_msg *msg)
{
	struct ipmi_smi_msg  smi_msg;
	struct ipmi_recv_msg recv_msg;
	int rv;

	smi_msg.done = dummy_smi_done_handler;
	recv_msg.done = dummy_recv_done_handler;
	atomic_add(2, &panic_done_count);
	rv = i_ipmi_request(NULL,
			    intf,
			    addr,
			    0,
			    msg,
			    intf,
			    &smi_msg,
			    &recv_msg,
			    0,
			    intf->channels[0].address,
			    intf->channels[0].lun,
			    0, 1); /* Don't retry, and don't wait. */
	if (rv)
		atomic_sub(2, &panic_done_count);
	while (atomic_read(&panic_done_count) != 0)
		ipmi_poll(intf);
}
}


#ifdef CONFIG_IPMI_PANIC_STRING
#ifdef CONFIG_IPMI_PANIC_STRING
@@ -4248,8 +4286,6 @@ static void send_panic_events(char *str)
	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;
	struct ipmi_smi_msg               smi_msg;
	struct ipmi_recv_msg              recv_msg;


	si = (struct ipmi_system_interface_addr *) &addr;
	si = (struct ipmi_system_interface_addr *) &addr;
	si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
	si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
@@ -4277,9 +4313,6 @@ static void send_panic_events(char *str)
		data[7] = str[2];
		data[7] = str[2];
	}
	}


	smi_msg.done = dummy_smi_done_handler;
	recv_msg.done = dummy_recv_done_handler;

	/* For every registered interface, send the event. */
	/* For every registered interface, send the event. */
	list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
	list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
		if (!intf->handlers)
		if (!intf->handlers)
@@ -4289,18 +4322,7 @@ static void send_panic_events(char *str)
		intf->run_to_completion = 1;
		intf->run_to_completion = 1;
		/* Send the event announcing the panic. */
		/* Send the event announcing the panic. */
		intf->handlers->set_run_to_completion(intf->send_info, 1);
		intf->handlers->set_run_to_completion(intf->send_info, 1);
		i_ipmi_request(NULL,
		ipmi_panic_request_and_wait(intf, &addr, &msg);
			       intf,
			       &addr,
			       0,
			       &msg,
			       intf,
			       &smi_msg,
			       &recv_msg,
			       0,
			       intf->channels[0].address,
			       intf->channels[0].lun,
			       0, 1); /* Don't retry, and don't wait. */
	}
	}


#ifdef CONFIG_IPMI_PANIC_STRING
#ifdef CONFIG_IPMI_PANIC_STRING
@@ -4348,18 +4370,7 @@ static void send_panic_events(char *str)
		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 = device_id_fetcher;
		i_ipmi_request(NULL,
		ipmi_panic_request_and_wait(intf, &addr, &msg);
			       intf,
			       &addr,
			       0,
			       &msg,
			       intf,
			       &smi_msg,
			       &recv_msg,
			       0,
			       intf->channels[0].address,
			       intf->channels[0].lun,
			       0, 1); /* Don't retry, and don't wait. */


		if (intf->local_event_generator) {
		if (intf->local_event_generator) {
			/* Request the event receiver from the local MC. */
			/* Request the event receiver from the local MC. */
@@ -4368,18 +4379,7 @@ static void send_panic_events(char *str)
			msg.data = NULL;
			msg.data = NULL;
			msg.data_len = 0;
			msg.data_len = 0;
			intf->null_user_handler = event_receiver_fetcher;
			intf->null_user_handler = event_receiver_fetcher;
			i_ipmi_request(NULL,
			ipmi_panic_request_and_wait(intf, &addr, &msg);
				       intf,
				       &addr,
				       0,
				       &msg,
				       intf,
				       &smi_msg,
				       &recv_msg,
				       0,
				       intf->channels[0].address,
				       intf->channels[0].lun,
				       0, 1); /* no retry, and no wait. */
		}
		}
		intf->null_user_handler = NULL;
		intf->null_user_handler = NULL;


@@ -4436,18 +4436,7 @@ static void send_panic_events(char *str)
			strncpy(data+5, p, 11);
			strncpy(data+5, p, 11);
			p += size;
			p += size;


			i_ipmi_request(NULL,
			ipmi_panic_request_and_wait(intf, &addr, &msg);
				       intf,
				       &addr,
				       0,
				       &msg,
				       intf,
				       &smi_msg,
				       &recv_msg,
				       0,
				       intf->channels[0].address,
				       intf->channels[0].lun,
				       0, 1); /* no retry, and no wait. */
		}
		}
	}
	}
#endif /* CONFIG_IPMI_PANIC_STRING */
#endif /* CONFIG_IPMI_PANIC_STRING */
+10 −7
Original line number Original line Diff line number Diff line
@@ -520,6 +520,7 @@ static void panic_halt_ipmi_heartbeat(void)
	msg.cmd = IPMI_WDOG_RESET_TIMER;
	msg.cmd = IPMI_WDOG_RESET_TIMER;
	msg.data = NULL;
	msg.data = NULL;
	msg.data_len = 0;
	msg.data_len = 0;
	atomic_add(2, &panic_done_count);
	rv = ipmi_request_supply_msgs(watchdog_user,
	rv = ipmi_request_supply_msgs(watchdog_user,
				      (struct ipmi_addr *) &addr,
				      (struct ipmi_addr *) &addr,
				      0,
				      0,
@@ -528,8 +529,8 @@ static void panic_halt_ipmi_heartbeat(void)
				      &panic_halt_heartbeat_smi_msg,
				      &panic_halt_heartbeat_smi_msg,
				      &panic_halt_heartbeat_recv_msg,
				      &panic_halt_heartbeat_recv_msg,
				      1);
				      1);
	if (!rv)
	if (rv)
		atomic_add(2, &panic_done_count);
		atomic_sub(2, &panic_done_count);
}
}


static struct ipmi_smi_msg panic_halt_smi_msg = {
static struct ipmi_smi_msg panic_halt_smi_msg = {
@@ -553,16 +554,18 @@ static void panic_halt_ipmi_set_timeout(void)
	/* Wait for the messages to be free. */
	/* Wait for the messages to be free. */
	while (atomic_read(&panic_done_count) != 0)
	while (atomic_read(&panic_done_count) != 0)
		ipmi_poll_interface(watchdog_user);
		ipmi_poll_interface(watchdog_user);
	atomic_add(2, &panic_done_count);
	rv = i_ipmi_set_timeout(&panic_halt_smi_msg,
	rv = i_ipmi_set_timeout(&panic_halt_smi_msg,
				&panic_halt_recv_msg,
				&panic_halt_recv_msg,
				&send_heartbeat_now);
				&send_heartbeat_now);
	if (!rv) {
	if (rv) {
		atomic_add(2, &panic_done_count);
		atomic_sub(2, &panic_done_count);
		if (send_heartbeat_now)
			panic_halt_ipmi_heartbeat();
	} else
		printk(KERN_WARNING PFX
		printk(KERN_WARNING PFX
		       "Unable to extend the watchdog timeout.");
		       "Unable to extend the watchdog timeout.");
	} else {
		if (send_heartbeat_now)
			panic_halt_ipmi_heartbeat();
	}
	while (atomic_read(&panic_done_count) != 0)
	while (atomic_read(&panic_done_count) != 0)
		ipmi_poll_interface(watchdog_user);
		ipmi_poll_interface(watchdog_user);
}
}