diff options
Diffstat (limited to 'drivers/char/ipmi')
-rw-r--r-- | drivers/char/ipmi/Kconfig | 98 | ||||
-rw-r--r-- | drivers/char/ipmi/bt-bmc.c | 4 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmb_dev_int.c | 68 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_dmi.c | 1 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_dmi.h | 1 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 176 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si.h | 57 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 110 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_mem_io.c | 2 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_pci.c | 2 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_platform.c | 2 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_port_io.c | 2 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_sm.h | 54 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_ssif.c | 89 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 1 |
15 files changed, 383 insertions, 284 deletions
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 4bad0614109b..7dc2c3ec4051 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -4,38 +4,38 @@ # menuconfig IPMI_HANDLER - tristate 'IPMI top-level message handler' - depends on HAS_IOMEM - select IPMI_DMI_DECODE if DMI - help - This enables the central IPMI message handler, required for IPMI - to work. + tristate 'IPMI top-level message handler' + depends on HAS_IOMEM + select IPMI_DMI_DECODE if DMI + help + This enables the central IPMI message handler, required for IPMI + to work. - IPMI is a standard for managing sensors (temperature, - voltage, etc.) in a system. + IPMI is a standard for managing sensors (temperature, + voltage, etc.) in a system. - See <file:Documentation/IPMI.txt> for more details on the driver. + See <file:Documentation/IPMI.txt> for more details on the driver. - If unsure, say N. + If unsure, say N. config IPMI_DMI_DECODE - select IPMI_PLAT_DATA - bool + select IPMI_PLAT_DATA + bool config IPMI_PLAT_DATA - bool + bool if IPMI_HANDLER config IPMI_PANIC_EVENT - bool 'Generate a panic event to all BMCs on a panic' - help - When a panic occurs, this will cause the IPMI message handler to, - by default, generate an IPMI event describing the panic to each - interface registered with the message handler. This is always - available, the module parameter for ipmi_msghandler named - panic_op can be set to "event" to chose this value, this config - simply causes the default value to be set to "event". + bool 'Generate a panic event to all BMCs on a panic' + help + When a panic occurs, this will cause the IPMI message handler to, + by default, generate an IPMI event describing the panic to each + interface registered with the message handler. This is always + available, the module parameter for ipmi_msghandler named + panic_op can be set to "event" to chose this value, this config + simply causes the default value to be set to "event". config IPMI_PANIC_STRING bool 'Generate OEM events containing the panic string' @@ -54,43 +54,43 @@ config IPMI_PANIC_STRING causes the default value to be set to "string". config IPMI_DEVICE_INTERFACE - tristate 'Device interface for IPMI' - help - This provides an IOCTL interface to the IPMI message handler so - userland processes may use IPMI. It supports poll() and select(). + tristate 'Device interface for IPMI' + help + This provides an IOCTL interface to the IPMI message handler so + userland processes may use IPMI. It supports poll() and select(). config IPMI_SI - tristate 'IPMI System Interface handler' - select IPMI_PLAT_DATA - help - Provides a driver for System Interfaces (KCS, SMIC, BT). - Currently, only KCS and SMIC are supported. If - you are using IPMI, you should probably say "y" here. + tristate 'IPMI System Interface handler' + select IPMI_PLAT_DATA + help + Provides a driver for System Interfaces (KCS, SMIC, BT). + Currently, only KCS and SMIC are supported. If + you are using IPMI, you should probably say "y" here. config IPMI_SSIF - tristate 'IPMI SMBus handler (SSIF)' - select I2C - help - Provides a driver for a SMBus interface to a BMC, meaning that you - have a driver that must be accessed over an I2C bus instead of a - standard interface. This module requires I2C support. + tristate 'IPMI SMBus handler (SSIF)' + select I2C + help + Provides a driver for a SMBus interface to a BMC, meaning that you + have a driver that must be accessed over an I2C bus instead of a + standard interface. This module requires I2C support. config IPMI_POWERNV - depends on PPC_POWERNV - tristate 'POWERNV (OPAL firmware) IPMI interface' - help - Provides a driver for OPAL firmware-based IPMI interfaces. + depends on PPC_POWERNV + tristate 'POWERNV (OPAL firmware) IPMI interface' + help + Provides a driver for OPAL firmware-based IPMI interfaces. config IPMI_WATCHDOG - tristate 'IPMI Watchdog Timer' - help - This enables the IPMI watchdog timer. + tristate 'IPMI Watchdog Timer' + help + This enables the IPMI watchdog timer. config IPMI_POWEROFF - tristate 'IPMI Poweroff' - help - This enables a function to power off the system with IPMI if - the IPMI management controller is capable of this. + tristate 'IPMI Poweroff' + help + This enables a function to power off the system with IPMI if + the IPMI management controller is capable of this. endif # IPMI_HANDLER @@ -126,7 +126,7 @@ config NPCM7XX_KCS_IPMI_BMC config ASPEED_BT_IPMI_BMC depends on ARCH_ASPEED || COMPILE_TEST - depends on REGMAP && REGMAP_MMIO && MFD_SYSCON + depends on REGMAP && REGMAP_MMIO && MFD_SYSCON tristate "BT IPMI bmc driver" help Provides a driver for the BT (Block Transfer) IPMI interface diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index 40b9927c072c..d36aeacb290e 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -444,15 +444,13 @@ static int bt_bmc_probe(struct platform_device *pdev) bt_bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node); if (IS_ERR(bt_bmc->map)) { - struct resource *res; void __iomem *base; /* * Assume it's not the MFD-based devicetree description, in * which case generate a regmap ourselves */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c index 285e0b8f9a97..382b28f1cf2f 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -19,7 +19,7 @@ #include <linux/spinlock.h> #include <linux/wait.h> -#define MAX_MSG_LEN 128 +#define MAX_MSG_LEN 240 #define IPMB_REQUEST_LEN_MIN 7 #define NETFN_RSP_BIT_MASK 0x4 #define REQUEST_QUEUE_MAX_LEN 256 @@ -63,6 +63,7 @@ struct ipmb_dev { spinlock_t lock; wait_queue_head_t wait_queue; struct mutex file_mutex; + bool is_i2c_protocol; }; static inline struct ipmb_dev *to_ipmb_dev(struct file *file) @@ -112,6 +113,25 @@ static ssize_t ipmb_read(struct file *file, char __user *buf, size_t count, return ret < 0 ? ret : count; } +static int ipmb_i2c_write(struct i2c_client *client, u8 *msg, u8 addr) +{ + struct i2c_msg i2c_msg; + + /* + * subtract 1 byte (rq_sa) from the length of the msg passed to + * raw i2c_transfer + */ + i2c_msg.len = msg[IPMB_MSG_LEN_IDX] - 1; + + /* Assign message to buffer except first 2 bytes (length and address) */ + i2c_msg.buf = msg + 2; + + i2c_msg.addr = addr; + i2c_msg.flags = client->flags & I2C_CLIENT_PEC; + + return i2c_transfer(client->adapter, &i2c_msg, 1); +} + static ssize_t ipmb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -133,8 +153,11 @@ static ssize_t ipmb_write(struct file *file, const char __user *buf, rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); netf_rq_lun = msg[NETFN_LUN_IDX]; - if (!(netf_rq_lun & NETFN_RSP_BIT_MASK)) - return -EINVAL; + /* Check i2c block transfer vs smbus */ + if (ipmb_dev->is_i2c_protocol) { + ret = ipmb_i2c_write(ipmb_dev->client, msg, rq_sa); + return (ret == 1) ? count : ret; + } /* * subtract rq_sa and netf_rq_lun from the length of the msg passed to @@ -154,16 +177,16 @@ static ssize_t ipmb_write(struct file *file, const char __user *buf, return ret ? : count; } -static unsigned int ipmb_poll(struct file *file, poll_table *wait) +static __poll_t ipmb_poll(struct file *file, poll_table *wait) { struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); - unsigned int mask = POLLOUT; + __poll_t mask = EPOLLOUT; mutex_lock(&ipmb_dev->file_mutex); poll_wait(file, &ipmb_dev->wait_queue, wait); if (atomic_read(&ipmb_dev->request_queue_len)) - mask |= POLLIN; + mask |= EPOLLIN; mutex_unlock(&ipmb_dev->file_mutex); return mask; @@ -203,25 +226,16 @@ static u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa) ipmb_dev->request.checksum1); } -static bool is_ipmb_request(struct ipmb_dev *ipmb_dev, u8 rs_sa) +/* + * Verify if message has proper ipmb header with minimum length + * and correct checksum byte. + */ +static bool is_ipmb_msg(struct ipmb_dev *ipmb_dev, u8 rs_sa) { - if (ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) { - if (ipmb_verify_checksum1(ipmb_dev, rs_sa)) - return false; + if ((ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) && + (!ipmb_verify_checksum1(ipmb_dev, rs_sa))) + return true; - /* - * Check whether this is an IPMB request or - * response. - * The 6 MSB of netfn_rs_lun are dedicated to the netfn - * while the remaining bits are dedicated to the lun. - * If the LSB of the netfn is cleared, it is associated - * with an IPMB request. - * If the LSB of the netfn is set, it is associated with - * an IPMB response. - */ - if (!(ipmb_dev->request.netfn_rs_lun & NETFN_RSP_BIT_MASK)) - return true; - } return false; } @@ -265,7 +279,7 @@ static int ipmb_slave_cb(struct i2c_client *client, break; case I2C_SLAVE_WRITE_RECEIVED: - if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg)) + if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg) - 1) break; buf[++ipmb_dev->msg_idx] = *val; @@ -273,8 +287,7 @@ static int ipmb_slave_cb(struct i2c_client *client, case I2C_SLAVE_STOP: ipmb_dev->request.len = ipmb_dev->msg_idx; - - if (is_ipmb_request(ipmb_dev, GET_8BIT_ADDR(client->addr))) + if (is_ipmb_msg(ipmb_dev, GET_8BIT_ADDR(client->addr))) ipmb_handle_request(ipmb_dev); break; @@ -315,6 +328,9 @@ static int ipmb_probe(struct i2c_client *client, if (ret) return ret; + ipmb_dev->is_i2c_protocol + = device_property_read_bool(&client->dev, "i2c-protocol"); + ipmb_dev->client = client; i2c_set_clientdata(client, ipmb_dev); ret = i2c_slave_register(client, ipmb_slave_cb); diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c index f38e651dd1b5..bbf7029e224b 100644 --- a/drivers/char/ipmi/ipmi_dmi.c +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -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" diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h index 2dbec0461d0c..e16a9dbdcc29 100644 --- a/drivers/char/ipmi/ipmi_dmi.h +++ b/drivers/char/ipmi/ipmi_dmi.h @@ -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, diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 6707659cffd6..cad9563f8f48 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -44,25 +44,6 @@ static void need_waiter(struct ipmi_smi *intf); static int handle_one_recv_msg(struct ipmi_smi *intf, struct ipmi_smi_msg *msg); -#ifdef DEBUG -static void ipmi_debug_msg(const char *title, unsigned char *data, - unsigned int len) -{ - int i, pos; - char buf[100]; - - pos = snprintf(buf, sizeof(buf), "%s: ", title); - for (i = 0; i < len; i++) - pos += snprintf(buf + pos, sizeof(buf) - pos, - " %2.2x", data[i]); - pr_debug("%s\n", buf); -} -#else -static void ipmi_debug_msg(const char *title, unsigned char *data, - unsigned int len) -{ } -#endif - static bool initialized; static bool drvregistered; @@ -448,6 +429,8 @@ enum ipmi_stat_indexes { #define IPMI_IPMB_NUM_SEQ 64 struct ipmi_smi { + struct module *owner; + /* What interface number are we? */ int intf_num; @@ -904,12 +887,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); @@ -1218,6 +1203,11 @@ int ipmi_create_user(unsigned int if_num, if (rv) goto out_kfree; + if (!try_module_get(intf->owner)) { + rv = -ENODEV; + goto out_kfree; + } + /* Note that each existing user holds a refcount to the interface. */ kref_get(&intf->refcount); @@ -1347,6 +1337,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) } kref_put(&intf->refcount, intf_free); + module_put(intf->owner); } int ipmi_destroy_user(struct ipmi_user *user) @@ -2220,7 +2211,8 @@ static int i_ipmi_request(struct ipmi_user *user, else { smi_msg = ipmi_alloc_smi_msg(); if (smi_msg == NULL) { - ipmi_free_recv_msg(recv_msg); + if (!supplied_recv) + ipmi_free_recv_msg(recv_msg); rv = -ENOMEM; goto out; } @@ -2264,7 +2256,7 @@ out_err: ipmi_free_smi_msg(smi_msg); ipmi_free_recv_msg(recv_msg); } else { - ipmi_debug_msg("Send", smi_msg->data, smi_msg->data_size); + pr_debug("Send: %*ph\n", smi_msg->data_size, smi_msg->data); smi_send(intf, intf->handlers, smi_msg, priority); } @@ -2456,7 +2448,7 @@ static int __get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc) * been recently fetched, this will just use the cached data. Otherwise * it will run a new fetch. * - * Except for the first time this is called (in ipmi_register_smi()), + * Except for the first time this is called (in ipmi_add_smi()), * this will always return good data; */ static int __bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, @@ -3028,8 +3020,11 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf, bmc->pdev.name = "ipmi_bmc"; rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL); - if (rv < 0) + if (rv < 0) { + kfree(bmc); goto out; + } + bmc->pdev.dev.driver = &ipmidriver.driver; bmc->pdev.id = rv; bmc->pdev.dev.release = release_bmc_device; @@ -3374,10 +3369,11 @@ static void redo_bmc_reg(struct work_struct *work) kref_put(&intf->refcount, intf_free); } -int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, - void *send_info, - struct device *si_dev, - unsigned char slave_addr) +int ipmi_add_smi(struct module *owner, + const struct ipmi_smi_handlers *handlers, + void *send_info, + struct device *si_dev, + unsigned char slave_addr) { int i, j; int rv; @@ -3403,7 +3399,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, return rv; } - + intf->owner = owner; intf->bmc = &intf->tmp_bmc; INIT_LIST_HEAD(&intf->bmc->intfs); mutex_init(&intf->bmc->dyn_mutex); @@ -3511,7 +3507,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, return rv; } -EXPORT_SYMBOL(ipmi_register_smi); +EXPORT_SYMBOL(ipmi_add_smi); static void deliver_smi_err_response(struct ipmi_smi *intf, struct ipmi_smi_msg *msg, @@ -3727,7 +3723,7 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, msg->data[10] = ipmb_checksum(&msg->data[6], 4); msg->data_size = 11; - ipmi_debug_msg("Invalid command:", msg->data, msg->data_size); + pr_debug("Invalid command: %*ph\n", msg->data_size, msg->data); rcu_read_lock(); if (!intf->in_shutdown) { @@ -4214,8 +4210,54 @@ static int handle_one_recv_msg(struct ipmi_smi *intf, int requeue; int chan; - ipmi_debug_msg("Recv:", msg->rsp, msg->rsp_size); - if (msg->rsp_size < 2) { + pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp); + + 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,62 +4514,16 @@ 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. - */ - if (!run_to_completion) - spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); - list_add_tail(&msg->link, &intf->waiting_rcv_msgs); - if (!run_to_completion) - spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, - flags); - } + /* + * To preserve message order, we keep a queue and deliver from + * a tasklet. + */ + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); + list_add_tail(&msg->link, &intf->waiting_rcv_msgs); + 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); @@ -4573,7 +4569,7 @@ smi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg, smi_msg->data_size = recv_msg->msg.data_len; smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid); - ipmi_debug_msg("Resend: ", smi_msg->data, smi_msg->data_size); + pr_debug("Resend: %*ph\n", smi_msg->data_size, smi_msg->data); return smi_msg; } diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h index 357a229c9012..bac0ff86e48e 100644 --- a/drivers/char/ipmi/ipmi_si.h +++ b/drivers/char/ipmi/ipmi_si.h @@ -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__ */ diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index da5b6723329a..c7cc8538b84a 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -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? */ @@ -264,7 +268,7 @@ void debug_timestamp(char *msg) struct timespec64 t; ktime_get_ts64(&t); - pr_debug("**%s: %lld.%9.9ld\n", msg, (long long) t.tv_sec, t.tv_nsec); + pr_debug("**%s: %lld.%9.9ld\n", msg, t.tv_sec, t.tv_nsec); } #else #define debug_timestamp(x) @@ -931,42 +935,29 @@ static void set_run_to_completion(void *send_info, bool i_run_to_completion) } /* - * Use -1 in the nsec value of the busy waiting timespec to tell that - * we are spinning in kipmid looking for something and not delaying - * between checks + * Use -1 as a special constant to tell that we are spinning in kipmid + * looking for something and not delaying between checks */ -static inline void ipmi_si_set_not_busy(struct timespec64 *ts) -{ - ts->tv_nsec = -1; -} -static inline int ipmi_si_is_busy(struct timespec64 *ts) -{ - return ts->tv_nsec != -1; -} - -static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result, - const struct smi_info *smi_info, - struct timespec64 *busy_until) +#define IPMI_TIME_NOT_BUSY ns_to_ktime(-1ull) +static inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result, + const struct smi_info *smi_info, + ktime_t *busy_until) { unsigned int max_busy_us = 0; if (smi_info->si_num < num_max_busy_us) max_busy_us = kipmid_max_busy_us[smi_info->si_num]; 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); + *busy_until = IPMI_TIME_NOT_BUSY; + else if (*busy_until == IPMI_TIME_NOT_BUSY) { + *busy_until = ktime_get() + max_busy_us * NSEC_PER_USEC; } else { - struct timespec64 now; - - ktime_get_ts64(&now); - if (unlikely(timespec64_compare(&now, busy_until) > 0)) { - ipmi_si_set_not_busy(busy_until); - return 0; + if (unlikely(ktime_get() > *busy_until)) { + *busy_until = IPMI_TIME_NOT_BUSY; + return false; } } - return 1; + return true; } @@ -984,9 +975,8 @@ 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; + ktime_t busy_until = IPMI_TIME_NOT_BUSY; - ipmi_si_set_not_busy(&busy_until); set_user_nice(current, MAX_NICE); while (!kthread_should_stop()) { int busy_wait; @@ -1007,11 +997,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) - schedule(); - else if (smi_result == SI_SM_IDLE) { + } 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 + usleep_range(100, 200); + } else if (smi_result == SI_SM_IDLE) { if (atomic_read(&smi_info->need_watch)) { schedule_timeout_interruptible(100); } else { @@ -1019,8 +1018,9 @@ static int ipmi_thread(void *data) __set_current_state(TASK_INTERRUPTIBLE); schedule(); } - } else + } else { schedule_timeout_interruptible(1); + } } return 0; } @@ -1198,6 +1198,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 +1267,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,37 +1587,37 @@ out: } #define IPMI_SI_ATTR(name) \ -static ssize_t ipmi_##name##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ +static ssize_t name##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ struct smi_info *smi_info = dev_get_drvdata(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, - struct device_attribute *attr, - char *buf) +static ssize_t type_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct smi_info *smi_info = dev_get_drvdata(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, - struct device_attribute *attr, - char *buf) +static ssize_t interrupts_enabled_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct smi_info *smi_info = dev_get_drvdata(dev); int enabled = smi_info->io.irq && !smi_info->interrupt_disabled; 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,9 +1631,9 @@ IPMI_SI_ATTR(events); IPMI_SI_ATTR(watchdog_pretimeouts); IPMI_SI_ATTR(incoming_messages); -static ssize_t ipmi_params_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t params_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct smi_info *smi_info = dev_get_drvdata(dev); @@ -1647,7 +1648,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,8 +1829,7 @@ 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); + del_timer_sync(&smi_info->si_timer); } static struct smi_info *find_dup_si(struct smi_info *info) diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c index 75583612ab10..86b92e93a70d 100644 --- a/drivers/char/ipmi/ipmi_si_mem_io.c +++ b/drivers/char/ipmi/ipmi_si_mem_io.c @@ -118,7 +118,7 @@ int ipmi_si_mem_setup(struct si_sm_io *io) */ for (idx = 0; idx < io->io_size; idx++) { if (request_mem_region(addr + idx * io->regspacing, - io->regsize, DEVICE_NAME) == NULL) { + io->regsize, SI_DEVICE_NAME) == NULL) { /* Undo allocations */ mem_region_cleanup(io, idx); return -EIO; diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c index ce93fc7a1e36..95bbcfba5408 100644 --- a/drivers/char/ipmi/ipmi_si_pci.c +++ b/drivers/char/ipmi/ipmi_si_pci.c @@ -150,7 +150,7 @@ static const struct pci_device_id ipmi_pci_devices[] = { MODULE_DEVICE_TABLE(pci, ipmi_pci_devices); static struct pci_driver ipmi_pci_driver = { - .name = DEVICE_NAME, + .name = SI_DEVICE_NAME, .id_table = ipmi_pci_devices, .probe = ipmi_pci_probe, .remove = ipmi_pci_remove, diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index 22f6c9b20e9a..c78127ccbc0d 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -457,7 +457,7 @@ static const struct platform_device_id si_plat_ids[] = { struct platform_driver ipmi_platform_driver = { .driver = { - .name = DEVICE_NAME, + .name = SI_DEVICE_NAME, .of_match_table = of_ipmi_match, .acpi_match_table = ACPI_PTR(acpi_ipmi_match), }, diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c index 03924c32b6e9..7d66f68eb1f1 100644 --- a/drivers/char/ipmi/ipmi_si_port_io.c +++ b/drivers/char/ipmi/ipmi_si_port_io.c @@ -99,7 +99,7 @@ int ipmi_si_port_setup(struct si_sm_io *io) */ for (idx = 0; idx < io->io_size; idx++) { if (request_region(addr + idx * io->regspacing, - io->regsize, DEVICE_NAME) == NULL) { + io->regsize, SI_DEVICE_NAME) == NULL) { /* Undo allocations */ while (idx--) release_region(addr + idx * io->regspacing, diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h index 499db820fadb..c3cdbcab0f77 100644 --- a/drivers/char/ipmi/ipmi_si_sm.h +++ b/drivers/char/ipmi/ipmi_si_sm.h @@ -14,7 +14,10 @@ * Copyright 2002 MontaVista Software Inc. */ -#include <linux/ipmi.h> +#ifndef __IPMI_SI_SM_H__ +#define __IPMI_SI_SM_H__ + +#include "ipmi_si.h" /* * This is defined by the state machines themselves, it is an opaque @@ -22,54 +25,6 @@ */ struct si_sm_data; -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; -}; - /* Results of SMI events. */ enum si_sm_result { SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */ @@ -146,3 +101,4 @@ extern const struct si_sm_handlers kcs_smi_handlers; extern const struct si_sm_handlers smic_smi_handlers; extern const struct si_sm_handlers bt_smi_handlers; +#endif /* __IPMI_SI_SM_H__ */ diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 305fa5054274..8ac390c2b514 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -52,7 +52,6 @@ #include <linux/acpi.h> #include <linux/ctype.h> #include <linux/time64.h> -#include "ipmi_si_sm.h" #include "ipmi_dmi.h" #define DEVICE_NAME "ipmi_ssif" @@ -776,10 +775,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, flags = ipmi_ssif_lock_cond(ssif_info, &oflags); msg = ssif_info->curr_msg; if (msg) { + if (data) { + if (len > IPMI_MAX_MSG_LENGTH) + len = IPMI_MAX_MSG_LENGTH; + memcpy(msg->rsp, data, len); + } else { + len = 0; + } msg->rsp_size = len; - if (msg->rsp_size > IPMI_MAX_MSG_LENGTH) - msg->rsp_size = IPMI_MAX_MSG_LENGTH; - memcpy(msg->rsp, data, msg->rsp_size); ssif_info->curr_msg = NULL; } @@ -1428,6 +1431,10 @@ static struct ssif_addr_info *ssif_info_find(unsigned short addr, restart: list_for_each_entry(info, &ssif_infos, link) { if (info->binfo.addr == addr) { + if (info->addr_src == SI_SMBIOS) + info->adapter_name = kstrdup(adapter_name, + GFP_KERNEL); + if (info->adapter_name || adapter_name) { if (!info->adapter_name != !adapter_name) { /* One is NULL and one is not */ @@ -1603,6 +1610,60 @@ out_no_multi_part: #define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \ IPMI_BMC_EVT_MSG_INTR) +static void ssif_remove_dup(struct i2c_client *client) +{ + struct ssif_info *ssif_info = i2c_get_clientdata(client); + + ipmi_unregister_smi(ssif_info->intf); + kfree(ssif_info); +} + +static int ssif_add_infos(struct i2c_client *client) +{ + struct ssif_addr_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->addr_src = SI_ACPI; + info->client = client; + info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL); + info->binfo.addr = client->addr; + list_add_tail(&info->link, &ssif_infos); + return 0; +} + +/* + * Prefer ACPI over SMBIOS, if both are available. + * So if we get an ACPI interface and have already registered a SMBIOS + * interface at the same address, remove the SMBIOS and add the ACPI one. + */ +static int ssif_check_and_remove(struct i2c_client *client, + struct ssif_info *ssif_info) +{ + struct ssif_addr_info *info; + + list_for_each_entry(info, &ssif_infos, link) { + if (!info->client) + return 0; + if (!strcmp(info->adapter_name, client->adapter->name) && + info->binfo.addr == client->addr) { + if (info->addr_src == SI_ACPI) + return -EEXIST; + + if (ssif_info->addr_source == SI_ACPI && + info->addr_src == SI_SMBIOS) { + dev_info(&client->dev, + "Removing %s-specified SSIF interface in favor of ACPI\n", + ipmi_addr_src_to_str(info->addr_src)); + ssif_remove_dup(info->client); + return 0; + } + } + } + return 0; +} + static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) { unsigned char msg[3]; @@ -1614,13 +1675,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) u8 slave_addr = 0; struct ssif_addr_info *addr_info = NULL; + mutex_lock(&ssif_infos_mutex); resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); - if (!resp) + if (!resp) { + mutex_unlock(&ssif_infos_mutex); return -ENOMEM; + } ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL); if (!ssif_info) { kfree(resp); + mutex_unlock(&ssif_infos_mutex); return -ENOMEM; } @@ -1639,6 +1704,19 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) } } + rv = ssif_check_and_remove(client, ssif_info); + /* If rv is 0 and addr source is not SI_ACPI, continue probing */ + if (!rv && ssif_info->addr_source == SI_ACPI) { + rv = ssif_add_infos(client); + if (rv) { + dev_err(&client->dev, "Out of memory!, exiting ..\n"); + goto out; + } + } else if (rv) { + dev_err(&client->dev, "Not probing, Interface already present\n"); + goto out; + } + slave_addr = find_slave_address(client, slave_addr); dev_info(&client->dev, @@ -1851,6 +1929,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) kfree(ssif_info); } kfree(resp); + mutex_unlock(&ssif_infos_mutex); return rv; out_remove_attr: diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 74c6d1f34132..55986e10a124 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -893,6 +893,7 @@ static const struct file_operations ipmi_wdog_fops = { .poll = ipmi_poll, .write = ipmi_write, .unlocked_ioctl = ipmi_unlocked_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ipmi_open, .release = ipmi_close, .fasync = ipmi_fasync, |