Restructure the IPMI driver for 6.16

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.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAmg4t4YACgkQYfOMkJGb
 /4FO9hAAlzLGbSnskRPng8WTMrq9lf2LkpJcJrNrn9Q5eZLlDIYgTHO84fqwQc9e
 UN4sYHmqAxQgr9SCSU2z8jiMH8vABmuatWTFx7X5oYfBTjmM022cY89gihyDAaeS
 V3uO7nLJJkkRSGh/Wbpa+XgJfii+27kizy7SB8sySSHU82AJC9Sy7HJeO8H0+tH8
 06uKGw/6GgABVoTFCwgOxfibJKE6EZQPHVUD1HmEQBNcLBAruZ0QdQFn3ODZPt/6
 qZqzggvB4c7+7xUdPhfXKN3AvGnSkcq9wKzMVO3eiKZx7jFr1a+vqLYFIhnCOJ6l
 XKGV14XtCL5oI+9DkZcYAd/3NbHbLofzTH8G0TUqNUzZ0hoKq93Hb2y500rZPFzx
 RhT3BtxzLZQFmDj7mO2Mv+cRCZJ1EOv0nbUI7B7t3tXEzbR6oz3wspUGy1R/g2O4
 Cgpg/ePmTWSUYaQbQqlozFdOAB7DdRvoRu6X4YIQW7t6vQhZ8r0ojfKLm1I9FWoH
 PIl8XZa8jYNZw2N2bneboINSdhcYXOCSZPDLzgTWUqUu83naUPltrTwr8IMLBO8f
 zvFJda4Z66mDtdon02H0SWq4ihbfb1LZpmL7DsgL09fL2gEnuGjRvalNHDtn1h62
 TZ7/vq3U8WoSanT0K9ltH9xBDTpnturpRidssUePm5Xn9sY+fdw=
 =e8Rg
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2025-05-29 21:37:11 -07:00
commit 02897f5e56
10 changed files with 585 additions and 525 deletions

View File

@ -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::

File diff suppressed because it is too large Load Diff

View File

@ -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;
};

View File

@ -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;
/* Fall back to the preferred device */
/*
* Now try devices without interrupts.
*/
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;
bool dup = false;
if (e->io.irq)
continue;
/*
* 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);

View File

@ -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;

View File

@ -23,30 +23,32 @@ MODULE_PARM_DESC(trypci,
static int ipmi_pci_probe_regspacing(struct si_sm_io *io)
{
if (io->si_type == SI_KCS) {
unsigned char status;
int regspacing;
unsigned char status;
int regspacing;
io->regsize = DEFAULT_REGSIZE;
io->regshift = 0;
if (io->si_info->type != SI_KCS)
return DEFAULT_REGSPACING;
/* detect 1, 4, 16byte spacing */
for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
io->regspacing = regspacing;
if (io->io_setup(io)) {
dev_err(io->dev, "Could not setup I/O space\n");
return DEFAULT_REGSPACING;
}
/* write invalid cmd */
io->outputb(io, 1, 0x10);
/* read status back */
status = io->inputb(io, 1);
io->io_cleanup(io);
if (status)
return regspacing;
regspacing *= 4;
io->regsize = DEFAULT_REGSIZE;
io->regshift = 0;
/* detect 1, 4, 16byte spacing */
for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
io->regspacing = regspacing;
if (io->io_setup(io)) {
dev_err(io->dev, "Could not setup I/O space\n");
return DEFAULT_REGSPACING;
}
/* write invalid cmd */
io->outputb(io, 1, 0x10);
/* read status back */
status = io->inputb(io, 1);
io->io_cleanup(io);
if (status)
return regspacing;
regspacing *= 4;
}
return DEFAULT_REGSPACING;
}
@ -74,15 +76,15 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
switch (pdev->class) {
case PCI_CLASS_SERIAL_IPMI_SMIC:
io.si_type = SI_SMIC;
io.si_info = &ipmi_smic_si_info;
break;
case PCI_CLASS_SERIAL_IPMI_KCS:
io.si_type = SI_KCS;
io.si_info = &ipmi_kcs_si_info;
break;
case PCI_CLASS_SERIAL_IPMI_BT:
io.si_type = SI_BT;
io.si_info = &ipmi_bt_si_info;
break;
default:

View File

@ -163,9 +163,13 @@ static int platform_ipmi_probe(struct platform_device *pdev)
switch (type) {
case SI_KCS:
io.si_info = &ipmi_kcs_si_info;
break;
case SI_SMIC:
io.si_info = &ipmi_smic_si_info;
break;
case SI_BT:
io.si_type = type;
io.si_info = &ipmi_bt_si_info;
break;
case SI_TYPE_INVALID: /* User disabled this in hardcode. */
return -ENODEV;
@ -213,13 +217,10 @@ static int platform_ipmi_probe(struct platform_device *pdev)
#ifdef CONFIG_OF
static const struct of_device_id of_ipmi_match[] = {
{ .type = "ipmi", .compatible = "ipmi-kcs",
.data = (void *)(unsigned long) SI_KCS },
{ .type = "ipmi", .compatible = "ipmi-smic",
.data = (void *)(unsigned long) SI_SMIC },
{ .type = "ipmi", .compatible = "ipmi-bt",
.data = (void *)(unsigned long) SI_BT },
{},
{ .type = "ipmi", .compatible = "ipmi-kcs", .data = &ipmi_kcs_si_info },
{ .type = "ipmi", .compatible = "ipmi-smic", .data = &ipmi_smic_si_info },
{ .type = "ipmi", .compatible = "ipmi-bt", .data = &ipmi_bt_si_info },
{}
};
MODULE_DEVICE_TABLE(of, of_ipmi_match);
@ -265,7 +266,7 @@ static int of_ipmi_probe(struct platform_device *pdev)
}
memset(&io, 0, sizeof(io));
io.si_type = (enum si_type)device_get_match_data(&pdev->dev);
io.si_info = device_get_match_data(&pdev->dev);
io.addr_source = SI_DEVICETREE;
io.irq_setup = ipmi_std_irq_setup;
@ -296,7 +297,7 @@ static int find_slave_address(struct si_sm_io *io, int slave_addr)
{
#ifdef CONFIG_IPMI_DMI_DECODE
if (!slave_addr)
slave_addr = ipmi_dmi_get_slave_addr(io->si_type,
slave_addr = ipmi_dmi_get_slave_addr(io->si_info->type,
io->addr_space,
io->addr_data);
#endif
@ -335,13 +336,13 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
switch (tmp) {
case 1:
io.si_type = SI_KCS;
io.si_info = &ipmi_kcs_si_info;
break;
case 2:
io.si_type = SI_SMIC;
io.si_info = &ipmi_smic_si_info;
break;
case 3:
io.si_type = SI_BT;
io.si_info = &ipmi_bt_si_info;
break;
case 4: /* SSIF, just ignore */
return -ENODEV;

View File

@ -481,8 +481,6 @@ static int ipmi_ssif_thread(void *data)
/* Wait for something to do */
result = wait_for_completion_interruptible(
&ssif_info->wake_thread);
if (ssif_info->stopping)
break;
if (result == -ERESTARTSYS)
continue;
init_completion(&ssif_info->wake_thread);
@ -1270,10 +1268,8 @@ static void shutdown_ssif(void *send_info)
ssif_info->stopping = true;
timer_delete_sync(&ssif_info->watch_timer);
timer_delete_sync(&ssif_info->retry_timer);
if (ssif_info->thread) {
complete(&ssif_info->wake_thread);
if (ssif_info->thread)
kthread_stop(ssif_info->thread);
}
}
static void ssif_remove(struct i2c_client *client)

View File

@ -150,7 +150,7 @@ static char preaction[16] = "pre_none";
static unsigned char preop_val = WDOG_PREOP_NONE;
static char preop[16] = "preop_none";
static DEFINE_SPINLOCK(ipmi_read_lock);
static DEFINE_MUTEX(ipmi_read_mutex);
static char data_to_read;
static DECLARE_WAIT_QUEUE_HEAD(read_q);
static struct fasync_struct *fasync_q;
@ -363,7 +363,7 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg,
{
struct kernel_ipmi_msg msg;
unsigned char data[6];
int rv;
int rv = 0;
struct ipmi_system_interface_addr addr;
int hbnow = 0;
@ -405,14 +405,18 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg,
msg.cmd = IPMI_WDOG_SET_TIMER;
msg.data = data;
msg.data_len = sizeof(data);
rv = ipmi_request_supply_msgs(watchdog_user,
(struct ipmi_addr *) &addr,
0,
&msg,
NULL,
smi_msg,
recv_msg,
1);
if (smi_msg)
rv = ipmi_request_supply_msgs(watchdog_user,
(struct ipmi_addr *) &addr,
0,
&msg,
NULL,
smi_msg,
recv_msg,
1);
else
ipmi_panic_request_and_wait(watchdog_user,
(struct ipmi_addr *) &addr, &msg);
if (rv)
pr_warn("set timeout error: %d\n", rv);
else if (send_heartbeat_now)
@ -431,9 +435,7 @@ static int _ipmi_set_timeout(int do_heartbeat)
atomic_set(&msg_tofree, 2);
rv = __ipmi_set_timeout(&smi_msg,
&recv_msg,
&send_heartbeat_now);
rv = __ipmi_set_timeout(&smi_msg, &recv_msg, &send_heartbeat_now);
if (rv) {
atomic_set(&msg_tofree, 0);
return rv;
@ -460,27 +462,10 @@ static int ipmi_set_timeout(int do_heartbeat)
return rv;
}
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 panic_recv_free(struct ipmi_recv_msg *msg)
{
atomic_dec(&panic_done_count);
}
static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg =
INIT_IPMI_SMI_MSG(panic_smi_free);
static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg =
INIT_IPMI_RECV_MSG(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
@ -497,24 +482,10 @@ static void panic_halt_ipmi_heartbeat(void)
msg.cmd = IPMI_WDOG_RESET_TIMER;
msg.data = NULL;
msg.data_len = 0;
atomic_add(2, &panic_done_count);
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_sub(2, &panic_done_count);
ipmi_panic_request_and_wait(watchdog_user, (struct ipmi_addr *) &addr,
&msg);
}
static struct ipmi_smi_msg panic_halt_smi_msg =
INIT_IPMI_SMI_MSG(panic_smi_free);
static struct ipmi_recv_msg panic_halt_recv_msg =
INIT_IPMI_RECV_MSG(panic_recv_free);
/*
* Special call, doesn't claim any locks. This is only to be called
* at panic or halt time, in run-to-completion mode, when the caller
@ -526,22 +497,13 @@ 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);
atomic_add(2, &panic_done_count);
rv = __ipmi_set_timeout(&panic_halt_smi_msg,
&panic_halt_recv_msg,
&send_heartbeat_now);
rv = __ipmi_set_timeout(NULL, NULL, &send_heartbeat_now);
if (rv) {
atomic_sub(2, &panic_done_count);
pr_warn("Unable to extend the watchdog timeout\n");
} else {
if (send_heartbeat_now)
panic_halt_ipmi_heartbeat();
}
while (atomic_read(&panic_done_count) != 0)
ipmi_poll_interface(watchdog_user);
}
static int __ipmi_heartbeat(void)
@ -793,7 +755,7 @@ static ssize_t ipmi_read(struct file *file,
* Reading returns if the pretimeout has gone off, and it only does
* it once per pretimeout.
*/
spin_lock_irq(&ipmi_read_lock);
mutex_lock(&ipmi_read_mutex);
if (!data_to_read) {
if (file->f_flags & O_NONBLOCK) {
rv = -EAGAIN;
@ -804,9 +766,9 @@ static ssize_t ipmi_read(struct file *file,
add_wait_queue(&read_q, &wait);
while (!data_to_read && !signal_pending(current)) {
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irq(&ipmi_read_lock);
mutex_unlock(&ipmi_read_mutex);
schedule();
spin_lock_irq(&ipmi_read_lock);
mutex_lock(&ipmi_read_mutex);
}
remove_wait_queue(&read_q, &wait);
@ -818,7 +780,7 @@ static ssize_t ipmi_read(struct file *file,
data_to_read = 0;
out:
spin_unlock_irq(&ipmi_read_lock);
mutex_unlock(&ipmi_read_mutex);
if (rv == 0) {
if (copy_to_user(buf, &data_to_read, 1))
@ -856,10 +818,10 @@ static __poll_t ipmi_poll(struct file *file, poll_table *wait)
poll_wait(file, &read_q, wait);
spin_lock_irq(&ipmi_read_lock);
mutex_lock(&ipmi_read_mutex);
if (data_to_read)
mask |= (EPOLLIN | EPOLLRDNORM);
spin_unlock_irq(&ipmi_read_lock);
mutex_unlock(&ipmi_read_mutex);
return mask;
}
@ -932,13 +894,11 @@ static void ipmi_wdog_pretimeout_handler(void *handler_data)
if (atomic_inc_and_test(&preop_panic_excl))
panic("Watchdog pre-timeout");
} else if (preop_val == WDOG_PREOP_GIVE_DATA) {
unsigned long flags;
spin_lock_irqsave(&ipmi_read_lock, flags);
mutex_lock(&ipmi_read_mutex);
data_to_read = 1;
wake_up_interruptible(&read_q);
kill_fasync(&fasync_q, SIGIO, POLL_IN);
spin_unlock_irqrestore(&ipmi_read_lock, flags);
mutex_unlock(&ipmi_read_mutex);
}
}

View File

@ -93,7 +93,8 @@ struct ipmi_user_hndl {
/*
* 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. Note that you
* can't do any IPMI calls from here, it's called with locks held.
*/
void (*ipmi_watchdog_pretimeout)(void *handler_data);
@ -343,4 +344,14 @@ extern int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data);
/* Helper function for computing the IPMB checksum of some data. */
unsigned char ipmb_checksum(unsigned char *data, int size);
/*
* For things that must send messages at panic time, like the IPMI watchdog
* driver that extends the reset time on a panic, use this to send messages
* from panic context. Note that this puts the driver into a mode that
* only works at panic time, so only use it then.
*/
void ipmi_panic_request_and_wait(struct ipmi_user *user,
struct ipmi_addr *addr,
struct kernel_ipmi_msg *msg);
#endif /* __LINUX_IPMI_H */