Commit 02897f5e authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull IPMI updates from Corey Minyard:
 "Restructure the IPMI driver.

  This is a restructure of the IPMI driver, mostly to remove SRCU. The
  locking had issues, and they were not going to be straightforward to
  fix. Plus it used tons of memory and was generally a pain.

  Most of this moves handling of messages out of bh and interrupt
  context and runs it in thread context. Then getting rid of SRCU is
  easy.

  This also has a minor cleanup to remove a warning on newer GCCs and to
  fix some documentation"

* tag 'for-linus-6.16-1' of https://github.com/cminyard/linux-ipmi: (26 commits)
  docs: ipmi: fix spelling and grammar mistakes
  ipmi:msghandler: Fix potential memory corruption in ipmi_create_user()
  ipmi:watchdog: Use the new interface for panic messages
  ipmi:msghandler: Export and fix panic messaging capability
  Documentation:ipmi: Remove comments about interrupt level
  ipmi:ssif: Fix a shutdown race
  ipmi:msghandler: Don't deliver messages to deleted users
  ipmi:si: Rework startup of IPMI devices
  ipmi:msghandler: Add a error return from unhandle LAN cmds
  ipmi:msghandler: Shut down lower layer first at unregister
  ipmi:msghandler: Remove proc_fs.h
  ipmi:msghandler: Don't check for shutdown when returning responses
  ipmi:msghandler: Don't acquire a user refcount for queued messages
  ipmi:msghandler: Fix locking around users and interfaces
  ipmi:msghandler: Remove some user level processing in panic mode
  ipmi: Add a note about the pretimeout callback
  ipmi:watchdog: Change lock to mutex
  ipmi:msghandler: Remove srcu for the ipmi_interfaces list
  ipmi:msghandler: Remove srcu from the ipmi user structure
  ipmi:msghandler: Use the system_wq, not system_bh_wq
  ...
parents ae5ec8ad 08effa6b
Loading
Loading
Loading
Loading
+13 −16
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ manual), choose the 'IPMI SI handler' option. A driver also exists
for direct I2C access to the IPMI management controller.  Some boards
support this, but it is unknown if it will work on every board.  For
this, choose 'IPMI SMBus handler', but be ready to try to do some
figuring to see if it will work on your system if the SMBIOS/APCI
figuring to see if it will work on your system if the SMBIOS/ACPI
information is wrong or not present.  It is fairly safe to have both
these enabled and let the drivers auto-detect what is present.

@@ -63,7 +63,7 @@ situation, you need to read the section below named 'The SI Driver' or
IPMI defines a standard watchdog timer.  You can enable this with the
'IPMI Watchdog Timer' config option.  If you compile the driver into
the kernel, then via a kernel command-line option you can have the
watchdog timer start as soon as it initializes.  It also have a lot
watchdog timer start as soon as it initializes.  It also has a lot
of other options, see the 'Watchdog' section below for more details.
Note that you can also have the watchdog continue to run if it is
closed (by default it is disabled on close).  Go into the 'Watchdog
@@ -280,10 +280,8 @@ Creating the User
To use the message handler, you must first create a user using
ipmi_create_user.  The interface number specifies which SMI you want
to connect to, and you must supply callback functions to be called
when data comes in.  The callback function can run at interrupt level,
so be careful using the callbacks.  This also allows to you pass in a
piece of data, the handler_data, that will be passed back to you on
all calls.
when data comes in.  This also allows to you pass in a piece of data,
the handler_data, that will be passed back to you on all calls.

Once you are done, call ipmi_destroy_user() to get rid of the user.

@@ -303,8 +301,7 @@ use it for anything you like.

Responses come back in the function pointed to by the ipmi_recv_hndl
field of the "handler" that you passed in to ipmi_create_user().
Remember again, these may be running at interrupt level.  Remember to
look at the receive type, too.
Remember to look at the receive type, too.

From userland, you fill out an ipmi_req_t structure and use the
IPMICTL_SEND_COMMAND ioctl.  For incoming stuff, you can use select()
@@ -317,13 +314,13 @@ This gives the receiver a place to actually put the message.

If the message cannot fit into the data you provide, you will get an
EMSGSIZE error and the driver will leave the data in the receive
queue.  If you want to get it and have it truncate the message, us
queue.  If you want to get it and have it truncate the message, use
the IPMICTL_RECEIVE_MSG_TRUNC ioctl.

When you send a command (which is defined by the lowest-order bit of
the netfn per the IPMI spec) on the IPMB bus, the driver will
automatically assign the sequence number to the command and save the
command.  If the response is not receive in the IPMI-specified 5
command.  If the response is not received in the IPMI-specified 5
seconds, it will generate a response automatically saying the command
timed out.  If an unsolicited response comes in (if it was after 5
seconds, for instance), that response will be ignored.
@@ -367,7 +364,7 @@ channel bitmasks do not overlap.

To respond to a received command, set the response bit in the returned
netfn, use the address from the received message, and use the same
msgid that you got in the receive message.
msgid that you got in the received message.

From userland, equivalent IOCTLs are provided to do these functions.

@@ -440,7 +437,7 @@ register would be 0xca6. This defaults to 1.

The regsizes parameter gives the size of a register, in bytes.  The
data used by IPMI is 8-bits wide, but it may be inside a larger
register.  This parameter allows the read and write type to specified.
register.  This parameter allows the read and write type to be specified.
It may be 1, 2, 4, or 8.  The default is 1.

Since the register size may be larger than 32 bits, the IPMI data may not
@@ -481,8 +478,8 @@ If your IPMI interface does not support interrupts and is a KCS or
SMIC interface, the IPMI driver will start a kernel thread for the
interface to help speed things up.  This is a low-priority kernel
thread that constantly polls the IPMI driver while an IPMI operation
is in progress.  The force_kipmid module parameter will all the user to
force this thread on or off.  If you force it off and don't have
is in progress.  The force_kipmid module parameter will allow the user
to force this thread on or off.  If you force it off and don't have
interrupts, the driver will run VERY slowly.  Don't blame me,
these interfaces suck.

@@ -583,7 +580,7 @@ kernel command line as::
These are the same options as on the module command line.

The I2C driver does not support non-blocking access or polling, so
this driver cannod to IPMI panic events, extend the watchdog at panic
this driver cannot do IPMI panic events, extend the watchdog at panic
time, or other panic-related IPMI functions without special kernel
patches and driver modifications.  You can get those at the openipmi
web page.
@@ -610,7 +607,7 @@ Parameters are::
	ipmi_ipmb.retry_time_ms=<Time between retries on IPMB>
	ipmi_ipmb.max_retries=<Number of times to retry a message>

Loading the module will not result in the driver automatcially
Loading the module will not result in the driver automatically
starting unless there is device tree information setting it up.  If
you want to instantiate one of these by hand, do::

+400 −361

File changed.

Preview size limit exceeded, changes collapsed.

+9 −1
Original line number Diff line number Diff line
@@ -26,6 +26,14 @@ enum si_type {
/* Array is defined in the ipmi_si_intf.c */
extern const char *const si_to_str[];

struct ipmi_match_info {
	enum si_type type;
};

extern const struct ipmi_match_info ipmi_kcs_si_info;
extern const struct ipmi_match_info ipmi_smic_si_info;
extern const struct ipmi_match_info ipmi_bt_si_info;

enum ipmi_addr_space {
	IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
};
@@ -64,7 +72,7 @@ struct si_sm_io {
	void (*irq_cleanup)(struct si_sm_io *io);

	u8 slave_addr;
	enum si_type si_type;
	const struct ipmi_match_info *si_info;
	struct device *dev;
};

+81 −35
Original line number Diff line number Diff line
@@ -73,6 +73,10 @@ enum si_intf_state {
/* 'invalid' to allow a firmware-specified interface to be disabled */
const char *const si_to_str[] = { "invalid", "kcs", "smic", "bt", NULL };

const struct ipmi_match_info ipmi_kcs_si_info = { .type = SI_KCS };
const struct ipmi_match_info ipmi_smic_si_info = { .type = SI_SMIC };
const struct ipmi_match_info ipmi_bt_si_info = { .type = SI_BT };

static bool initialized;

/*
@@ -692,7 +696,7 @@ static void handle_transaction_done(struct smi_info *smi_info)
			break;
		}
		enables = current_global_enables(smi_info, 0, &irq_on);
		if (smi_info->io.si_type == SI_BT)
		if (smi_info->io.si_info->type == SI_BT)
			/* BT has its own interrupt enable bit. */
			check_bt_irq(smi_info, irq_on);
		if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) {
@@ -1119,7 +1123,7 @@ irqreturn_t ipmi_si_irq_handler(int irq, void *data)
	struct smi_info *smi_info = data;
	unsigned long   flags;

	if (smi_info->io.si_type == SI_BT)
	if (smi_info->io.si_info->type == SI_BT)
		/* We need to clear the IRQ flag for the BT interface. */
		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
				     IPMI_BT_INTMASK_CLEAR_IRQ_BIT
@@ -1164,7 +1168,7 @@ static int smi_start_processing(void *send_info,
	 * The BT interface is efficient enough to not need a thread,
	 * and there is no need for a thread if we have interrupts.
	 */
	else if ((new_smi->io.si_type != SI_BT) && (!new_smi->io.irq))
	else if (new_smi->io.si_info->type != SI_BT && !new_smi->io.irq)
		enable = 1;

	if (enable) {
@@ -1235,7 +1239,7 @@ MODULE_PARM_DESC(kipmid_max_busy_us,

void ipmi_irq_finish_setup(struct si_sm_io *io)
{
	if (io->si_type == SI_BT)
	if (io->si_info->type == SI_BT)
		/* Enable the interrupt in the BT interface. */
		io->outputb(io, IPMI_BT_INTMASK_REG,
			    IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
@@ -1243,7 +1247,7 @@ void ipmi_irq_finish_setup(struct si_sm_io *io)

void ipmi_irq_start_cleanup(struct si_sm_io *io)
{
	if (io->si_type == SI_BT)
	if (io->si_info->type == SI_BT)
		/* Disable the interrupt in the BT interface. */
		io->outputb(io, IPMI_BT_INTMASK_REG, 0);
}
@@ -1614,7 +1618,7 @@ static ssize_t type_show(struct device *dev,
{
	struct smi_info *smi_info = dev_get_drvdata(dev);

	return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_type]);
	return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_info->type]);
}
static DEVICE_ATTR_RO(type);

@@ -1649,7 +1653,7 @@ static ssize_t params_show(struct device *dev,

	return sysfs_emit(buf,
			"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
			si_to_str[smi_info->io.si_type],
			si_to_str[smi_info->io.si_info->type],
			addr_space_to_str[smi_info->io.addr_space],
			smi_info->io.addr_data,
			smi_info->io.regspacing,
@@ -1803,7 +1807,7 @@ setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
{
	struct ipmi_device_id *id = &smi_info->device_id;
	if (id->manufacturer_id == DELL_IANA_MFR_ID &&
	    smi_info->io.si_type == SI_BT)
	    smi_info->io.si_info->type == SI_BT)
		register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
}

@@ -1907,13 +1911,13 @@ int ipmi_si_add_smi(struct si_sm_io *io)
			/* We prefer ACPI over SMBIOS. */
			dev_info(dup->io.dev,
				 "Removing SMBIOS-specified %s state machine in favor of ACPI\n",
				 si_to_str[new_smi->io.si_type]);
				 si_to_str[new_smi->io.si_info->type]);
			cleanup_one_si(dup);
		} else {
			dev_info(new_smi->io.dev,
				 "%s-specified %s state machine: duplicate\n",
				 ipmi_addr_src_to_str(new_smi->io.addr_source),
				 si_to_str[new_smi->io.si_type]);
				 si_to_str[new_smi->io.si_info->type]);
			rv = -EBUSY;
			kfree(new_smi);
			goto out_err;
@@ -1922,7 +1926,7 @@ int ipmi_si_add_smi(struct si_sm_io *io)

	pr_info("Adding %s-specified %s state machine\n",
		ipmi_addr_src_to_str(new_smi->io.addr_source),
		si_to_str[new_smi->io.si_type]);
		si_to_str[new_smi->io.si_info->type]);

	list_add_tail(&new_smi->link, &smi_infos);

@@ -1945,12 +1949,12 @@ static int try_smi_init(struct smi_info *new_smi)

	pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
		ipmi_addr_src_to_str(new_smi->io.addr_source),
		si_to_str[new_smi->io.si_type],
		si_to_str[new_smi->io.si_info->type],
		addr_space_to_str[new_smi->io.addr_space],
		new_smi->io.addr_data,
		new_smi->io.slave_addr, new_smi->io.irq);

	switch (new_smi->io.si_type) {
	switch (new_smi->io.si_info->type) {
	case SI_KCS:
		new_smi->handlers = &kcs_smi_handlers;
		break;
@@ -2073,7 +2077,7 @@ static int try_smi_init(struct smi_info *new_smi)
	smi_num++;

	dev_info(new_smi->io.dev, "IPMI %s interface initialized\n",
		 si_to_str[new_smi->io.si_type]);
		 si_to_str[new_smi->io.si_info->type]);

	WARN_ON(new_smi->io.dev->init_name != NULL);

@@ -2091,9 +2095,18 @@ static int try_smi_init(struct smi_info *new_smi)
	return rv;
}

/*
 * Devices in the same address space at the same address are the same.
 */
static bool __init ipmi_smi_info_same(struct smi_info *e1, struct smi_info *e2)
{
	return (e1->io.addr_space == e2->io.addr_space &&
		e1->io.addr_data == e2->io.addr_data);
}

static int __init init_ipmi_si(void)
{
	struct smi_info *e;
	struct smi_info *e, *e2;
	enum ipmi_addr_src type = SI_INVALID;

	if (initialized)
@@ -2109,37 +2122,70 @@ static int __init init_ipmi_si(void)

	ipmi_si_parisc_init();

	/* We prefer devices with interrupts, but in the case of a machine
	   with multiple BMCs we assume that there will be several instances
	   of a given type so if we succeed in registering a type then also
	   try to register everything else of the same type */
	mutex_lock(&smi_infos_lock);

	/*
	 * Scan through all the devices.  We prefer devices with
	 * interrupts, so go through those first in case there are any
	 * duplicates that don't have the interrupt set.
	 */
	list_for_each_entry(e, &smi_infos, link) {
		/* Try to register a device if it has an IRQ and we either
		   haven't successfully registered a device yet or this
		   device has the same type as one we successfully registered */
		if (e->io.irq && (!type || e->io.addr_source == type)) {
			if (!try_smi_init(e)) {
				type = e->io.addr_source;
		bool dup = false;

		/* Register ones with interrupts first. */
		if (!e->io.irq)
			continue;

		/*
		 * Go through the ones we have already seen to see if this
		 * is a dup.
		 */
		list_for_each_entry(e2, &smi_infos, link) {
			if (e2 == e)
				break;
			if (e2->io.irq && ipmi_smi_info_same(e, e2)) {
				dup = true;
				break;
			}
		}
		if (!dup)
			try_smi_init(e);
	}

	/* type will only have been set if we successfully registered an si */
	if (type)
		goto skip_fallback_noirq;
	/*
	 * Now try devices without interrupts.
	 */
	list_for_each_entry(e, &smi_infos, link) {
		bool dup = false;

	/* Fall back to the preferred device */
		if (e->io.irq)
			continue;

	list_for_each_entry(e, &smi_infos, link) {
		if (!e->io.irq && (!type || e->io.addr_source == type)) {
			if (!try_smi_init(e)) {
				type = e->io.addr_source;
		/*
		 * Go through the ones we have already seen to see if
		 * this is a dup.  We have already looked at the ones
		 * with interrupts.
		 */
		list_for_each_entry(e2, &smi_infos, link) {
			if (!e2->io.irq)
				continue;
			if (ipmi_smi_info_same(e, e2)) {
				dup = true;
				break;
			}
		}
		list_for_each_entry(e2, &smi_infos, link) {
			if (e2 == e)
				break;
			if (ipmi_smi_info_same(e, e2)) {
				dup = true;
				break;
			}
		}
		if (!dup)
			try_smi_init(e);
	}

skip_fallback_noirq:
	initialized = true;
	mutex_unlock(&smi_infos_lock);

@@ -2267,7 +2313,7 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
	list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
		if (e->io.addr_space != addr_space)
			continue;
		if (e->io.si_type != si_type)
		if (e->io.si_info->type != si_type)
			continue;
		if (e->io.addr_data == addr) {
			dev = get_device(e->io.dev);
+1 −1
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev)

	memset(&io, 0, sizeof(io));

	io.si_type	= SI_KCS;
	io.si_info	= &ipmi_kcs_si_info;
	io.addr_source	= SI_DEVICETREE;
	io.addr_space	= IPMI_MEM_ADDR_SPACE;
	io.addr_data	= dev->hpa.start;
Loading