Commit a9f8b38a authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'for-linus-5.4-1' of git://github.com/cminyard/linux-ipmi

Pull IPMI updates from Corey Minyard:
 "A few minor fixes and some cosmetic changes.

  Nothing big here, but some minor things that people have found and
  some minor reworks for names and include files"

* tag 'for-linus-5.4-1' of git://github.com/cminyard/linux-ipmi:
  ipmi_si_intf: Fix race in timer shutdown handling
  ipmi: move message error checking to avoid deadlock
  ipmi_ssif: avoid registering duplicate ssif interface
  ipmi: Free receive messages when in an oops
  ipmi_si: Only schedule continuously in the thread in maintenance mode
  ipmi_si: Remove ipmi_ from the device attr names
  ipmi_si: Convert device attr permissions to octal
  ipmi_si: Rework some include files
  ipmi_si: Convert timespec64 to timespec
parents b682242f c9acc3c4
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -12,7 +12,6 @@
#include <linux/dmi.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include "ipmi_si_sm.h"
#include "ipmi_dmi.h"
#include "ipmi_plat_data.h"

+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
/*
 * DMI defines for use by IPMI
 */
#include "ipmi_si.h"

#ifdef CONFIG_IPMI_DMI_DECODE
int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
+62 −59
Original line number Diff line number Diff line
@@ -904,12 +904,14 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
			rv = -EINVAL;
		}
		ipmi_free_recv_msg(msg);
	} else if (!oops_in_progress) {
	} else if (oops_in_progress) {
		/*
		 * If we are running in the panic context, calling the
		 * receive handler doesn't much meaning and has a deadlock
		 * risk.  At this moment, simply skip it in that case.
		 */
		ipmi_free_recv_msg(msg);
	} else {
		int index;
		struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);

@@ -2220,6 +2222,7 @@ static int i_ipmi_request(struct ipmi_user *user,
	else {
		smi_msg = ipmi_alloc_smi_msg();
		if (smi_msg == NULL) {
			if (!supplied_recv)
				ipmi_free_recv_msg(recv_msg);
			rv = -ENOMEM;
			goto out;
@@ -4215,7 +4218,53 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
	int chan;

	ipmi_debug_msg("Recv:", msg->rsp, msg->rsp_size);
	if (msg->rsp_size < 2) {

	if ((msg->data_size >= 2)
	    && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
	    && (msg->data[1] == IPMI_SEND_MSG_CMD)
	    && (msg->user_data == NULL)) {

		if (intf->in_shutdown)
			goto free_msg;

		/*
		 * This is the local response to a command send, start
		 * the timer for these.  The user_data will not be
		 * NULL if this is a response send, and we will let
		 * response sends just go through.
		 */

		/*
		 * Check for errors, if we get certain errors (ones
		 * that mean basically we can try again later), we
		 * ignore them and start the timer.  Otherwise we
		 * report the error immediately.
		 */
		if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0)
		    && (msg->rsp[2] != IPMI_NODE_BUSY_ERR)
		    && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
		    && (msg->rsp[2] != IPMI_BUS_ERR)
		    && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
			int ch = msg->rsp[3] & 0xf;
			struct ipmi_channel *chans;

			/* Got an error sending the message, handle it. */

			chans = READ_ONCE(intf->channel_list)->c;
			if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
			    || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
				ipmi_inc_stat(intf, sent_lan_command_errs);
			else
				ipmi_inc_stat(intf, sent_ipmb_command_errs);
			intf_err_seq(intf, msg->msgid, msg->rsp[2]);
		} else
			/* The message was sent, start the timer. */
			intf_start_seq_timer(intf, msg->msgid);
free_msg:
		requeue = 0;
		goto out;

	} else if (msg->rsp_size < 2) {
		/* Message is too small to be correct. */
		dev_warn(intf->si_dev,
			 "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
@@ -4472,51 +4521,6 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
	unsigned long flags = 0; /* keep us warning-free. */
	int run_to_completion = intf->run_to_completion;

	if ((msg->data_size >= 2)
	    && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
	    && (msg->data[1] == IPMI_SEND_MSG_CMD)
	    && (msg->user_data == NULL)) {

		if (intf->in_shutdown)
			goto free_msg;

		/*
		 * This is the local response to a command send, start
		 * the timer for these.  The user_data will not be
		 * NULL if this is a response send, and we will let
		 * response sends just go through.
		 */

		/*
		 * Check for errors, if we get certain errors (ones
		 * that mean basically we can try again later), we
		 * ignore them and start the timer.  Otherwise we
		 * report the error immediately.
		 */
		if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0)
		    && (msg->rsp[2] != IPMI_NODE_BUSY_ERR)
		    && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
		    && (msg->rsp[2] != IPMI_BUS_ERR)
		    && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
			int ch = msg->rsp[3] & 0xf;
			struct ipmi_channel *chans;

			/* Got an error sending the message, handle it. */

			chans = READ_ONCE(intf->channel_list)->c;
			if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
			    || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
				ipmi_inc_stat(intf, sent_lan_command_errs);
			else
				ipmi_inc_stat(intf, sent_ipmb_command_errs);
			intf_err_seq(intf, msg->msgid, msg->rsp[2]);
		} else
			/* The message was sent, start the timer. */
			intf_start_seq_timer(intf, msg->msgid);

free_msg:
		ipmi_free_smi_msg(msg);
	} else {
	/*
	 * To preserve message order, we keep a queue and deliver from
	 * a tasklet.
@@ -4527,7 +4531,6 @@ free_msg:
	if (!run_to_completion)
		spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
				       flags);
	}

	if (!run_to_completion)
		spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
+55 −2
Original line number Diff line number Diff line
@@ -6,14 +6,65 @@
 * etc) to the base ipmi system interface code.
 */

#ifndef __IPMI_SI_H__
#define __IPMI_SI_H__

#include <linux/ipmi.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include "ipmi_si_sm.h"

#define SI_DEVICE_NAME "ipmi_si"

#define DEFAULT_REGSPACING	1
#define DEFAULT_REGSIZE		1

#define DEVICE_NAME "ipmi_si"
enum si_type {
	SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
};

enum ipmi_addr_space {
	IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
};

/*
 * The structure for doing I/O in the state machine.  The state
 * machine doesn't have the actual I/O routines, they are done through
 * this interface.
 */
struct si_sm_io {
	unsigned char (*inputb)(const struct si_sm_io *io, unsigned int offset);
	void (*outputb)(const struct si_sm_io *io,
			unsigned int  offset,
			unsigned char b);

	/*
	 * Generic info used by the actual handling routines, the
	 * state machine shouldn't touch these.
	 */
	void __iomem *addr;
	unsigned int regspacing;
	unsigned int regsize;
	unsigned int regshift;
	enum ipmi_addr_space addr_space;
	unsigned long addr_data;
	enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
	void (*addr_source_cleanup)(struct si_sm_io *io);
	void *addr_source_data;
	union ipmi_smi_info_union addr_info;

	int (*io_setup)(struct si_sm_io *info);
	void (*io_cleanup)(struct si_sm_io *info);
	unsigned int io_size;

	int irq;
	int (*irq_setup)(struct si_sm_io *io);
	void *irq_handler_data;
	void (*irq_cleanup)(struct si_sm_io *io);

	u8 slave_addr;
	enum si_type si_type;
	struct device *dev;
};

int ipmi_si_add_smi(struct si_sm_io *io);
irqreturn_t ipmi_si_irq_handler(int irq, void *data);
@@ -50,3 +101,5 @@ static inline void ipmi_si_parisc_shutdown(void) { }

int ipmi_si_port_setup(struct si_sm_io *io);
int ipmi_si_mem_setup(struct si_sm_io *io);

#endif /* __IPMI_SI_H__ */
+56 −42
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
#include <linux/ipmi.h>
#include <linux/ipmi_smi.h>
#include "ipmi_si.h"
#include "ipmi_si_sm.h"
#include <linux/string.h>
#include <linux/ctype.h>

@@ -221,6 +222,9 @@ struct smi_info {
	 */
	bool irq_enable_broken;

	/* Is the driver in maintenance mode? */
	bool in_maintenance_mode;

	/*
	 * Did we get an attention that we did not handle?
	 */
@@ -261,10 +265,10 @@ static void cleanup_ipmi_si(void);
#ifdef DEBUG_TIMING
void debug_timestamp(char *msg)
{
	struct timespec64 t;
	struct timespec t;

	ktime_get_ts64(&t);
	pr_debug("**%s: %lld.%9.9ld\n", msg, (long long) t.tv_sec, t.tv_nsec);
	ktime_get_ts(&t);
	pr_debug("**%s: %ld.%9.9ld\n", msg, (long) t.tv_sec, t.tv_nsec);
}
#else
#define debug_timestamp(x)
@@ -935,18 +939,18 @@ static void set_run_to_completion(void *send_info, bool i_run_to_completion)
 * we are spinning in kipmid looking for something and not delaying
 * between checks
 */
static inline void ipmi_si_set_not_busy(struct timespec64 *ts)
static inline void ipmi_si_set_not_busy(struct timespec *ts)
{
	ts->tv_nsec = -1;
}
static inline int ipmi_si_is_busy(struct timespec64 *ts)
static inline int ipmi_si_is_busy(struct timespec *ts)
{
	return ts->tv_nsec != -1;
}

static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result,
static inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result,
					 const struct smi_info *smi_info,
					struct timespec64 *busy_until)
					 struct timespec *busy_until)
{
	unsigned int max_busy_us = 0;

@@ -955,18 +959,18 @@ static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result,
	if (max_busy_us == 0 || smi_result != SI_SM_CALL_WITH_DELAY)
		ipmi_si_set_not_busy(busy_until);
	else if (!ipmi_si_is_busy(busy_until)) {
		ktime_get_ts64(busy_until);
		timespec64_add_ns(busy_until, max_busy_us*NSEC_PER_USEC);
		ktime_get_ts(busy_until);
		timespec_add_ns(busy_until, max_busy_us * NSEC_PER_USEC);
	} else {
		struct timespec64 now;
		struct timespec now;

		ktime_get_ts64(&now);
		if (unlikely(timespec64_compare(&now, busy_until) > 0)) {
		ktime_get_ts(&now);
		if (unlikely(timespec_compare(&now, busy_until) > 0)) {
			ipmi_si_set_not_busy(busy_until);
			return 0;
			return false;
		}
	}
	return 1;
	return true;
}


@@ -984,7 +988,7 @@ static int ipmi_thread(void *data)
	struct smi_info *smi_info = data;
	unsigned long flags;
	enum si_sm_result smi_result;
	struct timespec64 busy_until;
	struct timespec busy_until = { 0, 0 };

	ipmi_si_set_not_busy(&busy_until);
	set_user_nice(current, MAX_NICE);
@@ -1007,11 +1011,20 @@ static int ipmi_thread(void *data)
		spin_unlock_irqrestore(&(smi_info->si_lock), flags);
		busy_wait = ipmi_thread_busy_wait(smi_result, smi_info,
						  &busy_until);
		if (smi_result == SI_SM_CALL_WITHOUT_DELAY)
		if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
			; /* do nothing */
		else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait)
		} else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) {
			/*
			 * In maintenance mode we run as fast as
			 * possible to allow firmware updates to
			 * complete as fast as possible, but normally
			 * don't bang on the scheduler.
			 */
			if (smi_info->in_maintenance_mode)
				schedule();
		else if (smi_result == SI_SM_IDLE) {
			else
				usleep_range(100, 200);
		} else if (smi_result == SI_SM_IDLE) {
			if (atomic_read(&smi_info->need_watch)) {
				schedule_timeout_interruptible(100);
			} else {
@@ -1019,9 +1032,10 @@ static int ipmi_thread(void *data)
				__set_current_state(TASK_INTERRUPTIBLE);
				schedule();
			}
		} else
		} else {
			schedule_timeout_interruptible(1);
		}
	}
	return 0;
}

@@ -1198,6 +1212,7 @@ static void set_maintenance_mode(void *send_info, bool enable)

	if (!enable)
		atomic_set(&smi_info->req_events, 0);
	smi_info->in_maintenance_mode = enable;
}

static void shutdown_smi(void *send_info);
@@ -1266,12 +1281,12 @@ int ipmi_std_irq_setup(struct si_sm_io *io)
	rv = request_irq(io->irq,
			 ipmi_si_irq_handler,
			 IRQF_SHARED,
			 DEVICE_NAME,
			 SI_DEVICE_NAME,
			 io->irq_handler_data);
	if (rv) {
		dev_warn(io->dev, "%s unable to claim interrupt %d,"
			 " running polled\n",
			 DEVICE_NAME, io->irq);
			 SI_DEVICE_NAME, io->irq);
		io->irq = 0;
	} else {
		io->irq_cleanup = std_irq_cleanup;
@@ -1586,7 +1601,7 @@ out:
}

#define IPMI_SI_ATTR(name) \
static ssize_t ipmi_##name##_show(struct device *dev,			\
static ssize_t name##_show(struct device *dev,			\
			   struct device_attribute *attr,		\
			   char *buf)					\
{									\
@@ -1594,9 +1609,9 @@ static ssize_t ipmi_##name##_show(struct device *dev, \
									\
	return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name));	\
}									\
static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL)
static DEVICE_ATTR(name, 0444, name##_show, NULL)

static ssize_t ipmi_type_show(struct device *dev,
static ssize_t type_show(struct device *dev,
			 struct device_attribute *attr,
			 char *buf)
{
@@ -1604,9 +1619,9 @@ static ssize_t ipmi_type_show(struct device *dev,

	return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]);
}
static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL);
static DEVICE_ATTR(type, 0444, type_show, NULL);

static ssize_t ipmi_interrupts_enabled_show(struct device *dev,
static ssize_t interrupts_enabled_show(struct device *dev,
				       struct device_attribute *attr,
				       char *buf)
{
@@ -1615,8 +1630,8 @@ static ssize_t ipmi_interrupts_enabled_show(struct device *dev,

	return snprintf(buf, 10, "%d\n", enabled);
}
static DEVICE_ATTR(interrupts_enabled, S_IRUGO,
		   ipmi_interrupts_enabled_show, NULL);
static DEVICE_ATTR(interrupts_enabled, 0444,
		   interrupts_enabled_show, NULL);

IPMI_SI_ATTR(short_timeouts);
IPMI_SI_ATTR(long_timeouts);
@@ -1630,7 +1645,7 @@ IPMI_SI_ATTR(events);
IPMI_SI_ATTR(watchdog_pretimeouts);
IPMI_SI_ATTR(incoming_messages);

static ssize_t ipmi_params_show(struct device *dev,
static ssize_t params_show(struct device *dev,
			   struct device_attribute *attr,
			   char *buf)
{
@@ -1647,7 +1662,7 @@ static ssize_t ipmi_params_show(struct device *dev,
			smi_info->io.irq,
			smi_info->io.slave_addr);
}
static DEVICE_ATTR(params, S_IRUGO, ipmi_params_show, NULL);
static DEVICE_ATTR(params, 0444, params_show, NULL);

static struct attribute *ipmi_si_dev_attrs[] = {
	&dev_attr_type.attr,
@@ -1828,7 +1843,6 @@ static inline void stop_timer_and_thread(struct smi_info *smi_info)
	}

	smi_info->timer_can_start = false;
	if (smi_info->timer_running)
	del_timer_sync(&smi_info->si_timer);
}

Loading