diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 15:48:10 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 15:48:10 -0700 |
commit | 0eb0061381b2bac082cc26d573fc48fe99db3922 (patch) | |
tree | 997ea80d81025e69a3404eb142f01035e75e3412 /drivers | |
parent | 8450493076cd1dc8465db688f919708b5be17d46 (diff) | |
parent | 048f7c3e352eeef50ed2c14dd89683f8a3af2f9b (diff) |
Merge tag 'for-linus-4.18' of git://github.com/cminyard/linux-ipmi
Pull IPMI updates from Corey Minyard:
"It's been a busy release for the IPMI driver. Some notable changes:
- A user was running into timeout issues doing maintenance commands
over the IPMB network behind an IPMI controller.
Extend the maintenance mode concept to messages over IPMB and allow
the timeouts to be tuned.
- Lots of cleanup, style fixing, some bugfixes, and such.
- At least one user was having trouble with the way the IPMI driver
would lock the i2c driver module it used.
The IPMI driver was not designed for hotplug. However, hotplug is a
reality now, so the IPMI driver was modified to support hotplug.
- The proc interface code is now completely removed. Long live sysfs!"
* tag 'for-linus-4.18' of git://github.com/cminyard/linux-ipmi: (35 commits)
ipmi: Properly release srcu locks on error conditions
ipmi: NPCM7xx KCS BMC: enable interrupt to the host
ipmi:bt: Set the timeout before doing a capabilities check
ipmi: Remove the proc interface
ipmi_ssif: Fix uninitialized variable issue
ipmi: add an NPCM7xx KCS BMC driver
ipmi_si: Clean up shutdown a bit
ipmi_si: Rename intf_num to si_num
ipmi: Remove smi->intf checks
ipmi_ssif: Get rid of unused intf_num
ipmi: Get rid of ipmi_user_t and ipmi_smi_t in include files
ipmi: ipmi_unregister_smi() cannot fail, have it return void
ipmi_devintf: Add an error return on invalid ioctls
ipmi: Remove usecount function from interfaces
ipmi_ssif: Remove usecount handling
ipmi: Remove condition on interface shutdown
ipmi_ssif: Convert over to a shutdown handler
ipmi_si: Convert over to a shutdown handler
ipmi: Rework locking and shutdown for hot remove
ipmi: Fix some counter issues
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/ipmi/Kconfig | 23 | ||||
-rw-r--r-- | drivers/char/ipmi/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_bt_sm.c | 3 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_devintf.c | 129 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 2121 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 32 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 198 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_ssif.c | 183 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 407 | ||||
-rw-r--r-- | drivers/char/ipmi/kcs_bmc_npcm7xx.c | 215 |
10 files changed, 1547 insertions, 1765 deletions
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 3bda116c8aa0..c108441882cc 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -22,14 +22,6 @@ config IPMI_DMI_DECODE if IPMI_HANDLER -config IPMI_PROC_INTERFACE - bool 'Provide an interface for IPMI stats in /proc (deprecated)' - depends on PROC_FS - default y - help - Do not use this any more, use sysfs for this info. It will be - removed in future kernel versions. - config IPMI_PANIC_EVENT bool 'Generate a panic event to all BMCs on a panic' help @@ -111,6 +103,21 @@ config ASPEED_KCS_IPMI_BMC The driver implements the BMC side of the KCS contorller, it provides the access of KCS IO space for BMC side. +config NPCM7XX_KCS_IPMI_BMC + depends on ARCH_NPCM7XX || COMPILE_TEST + select IPMI_KCS_BMC + select REGMAP_MMIO + tristate "NPCM7xx KCS IPMI BMC driver" + help + Provides a driver for the KCS (Keyboard Controller Style) IPMI + interface found on Nuvoton NPCM7xx SOCs. + + The driver implements the BMC side of the KCS contorller, it + provides the access of KCS IO space for BMC side. + + This support is also available as a module. If so, the module + will be called kcs_bmc_npcm7xx. + config ASPEED_BT_IPMI_BMC depends on ARCH_ASPEED || COMPILE_TEST depends on REGMAP && REGMAP_MMIO && MFD_SYSCON diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 21e9e872d973..7a3baf301a8f 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o +obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o diff --git a/drivers/char/ipmi/ipmi_bt_sm.c b/drivers/char/ipmi/ipmi_bt_sm.c index fd4ea8d87d4b..a3397664f800 100644 --- a/drivers/char/ipmi/ipmi_bt_sm.c +++ b/drivers/char/ipmi/ipmi_bt_sm.c @@ -504,11 +504,12 @@ static enum si_sm_result bt_event(struct si_sm_data *bt, long time) if (status & BT_H_BUSY) /* clear a leftover H_BUSY */ BT_CONTROL(BT_H_BUSY); + bt->timeout = bt->BT_CAP_req2rsp; + /* Read BT capabilities if it hasn't been done yet */ if (!bt->BT_CAP_outreqs) BT_STATE_CHANGE(BT_STATE_CAPABILITIES_BEGIN, SI_SM_CALL_WITHOUT_DELAY); - bt->timeout = bt->BT_CAP_req2rsp; BT_SI_SM_RETURN(SI_SM_IDLE); case BT_STATE_XACTION_START: diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 8ecfd47806fa..1a486aec99b6 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -26,7 +26,7 @@ struct ipmi_file_private { - ipmi_user_t user; + struct ipmi_user *user; spinlock_t recv_msg_lock; struct list_head recv_msgs; struct file *file; @@ -37,7 +37,6 @@ struct ipmi_file_private unsigned int default_retry_time_ms; }; -static DEFINE_MUTEX(ipmi_mutex); static void file_receive_handler(struct ipmi_recv_msg *msg, void *handler_data) { @@ -45,17 +44,15 @@ static void file_receive_handler(struct ipmi_recv_msg *msg, int was_empty; unsigned long flags; - spin_lock_irqsave(&(priv->recv_msg_lock), flags); - - was_empty = list_empty(&(priv->recv_msgs)); - list_add_tail(&(msg->link), &(priv->recv_msgs)); + spin_lock_irqsave(&priv->recv_msg_lock, flags); + was_empty = list_empty(&priv->recv_msgs); + list_add_tail(&msg->link, &priv->recv_msgs); + spin_unlock_irqrestore(&priv->recv_msg_lock, flags); if (was_empty) { wake_up_interruptible(&priv->wait); kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN); } - - spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); } static __poll_t ipmi_poll(struct file *file, poll_table *wait) @@ -68,7 +65,7 @@ static __poll_t ipmi_poll(struct file *file, poll_table *wait) spin_lock_irqsave(&priv->recv_msg_lock, flags); - if (!list_empty(&(priv->recv_msgs))) + if (!list_empty(&priv->recv_msgs)) mask |= (EPOLLIN | EPOLLRDNORM); spin_unlock_irqrestore(&priv->recv_msg_lock, flags); @@ -79,13 +76,8 @@ static __poll_t ipmi_poll(struct file *file, poll_table *wait) static int ipmi_fasync(int fd, struct file *file, int on) { struct ipmi_file_private *priv = file->private_data; - int result; - mutex_lock(&ipmi_mutex); /* could race against open() otherwise */ - result = fasync_helper(fd, file, on, &priv->fasync_queue); - mutex_unlock(&ipmi_mutex); - - return (result); + return fasync_helper(fd, file, on, &priv->fasync_queue); } static const struct ipmi_user_hndl ipmi_hndlrs = @@ -99,18 +91,16 @@ static int ipmi_open(struct inode *inode, struct file *file) int rv; struct ipmi_file_private *priv; - priv = kmalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - mutex_lock(&ipmi_mutex); priv->file = file; rv = ipmi_create_user(if_num, &ipmi_hndlrs, priv, - &(priv->user)); + &priv->user); if (rv) { kfree(priv); goto out; @@ -118,8 +108,8 @@ static int ipmi_open(struct inode *inode, struct file *file) file->private_data = priv; - spin_lock_init(&(priv->recv_msg_lock)); - INIT_LIST_HEAD(&(priv->recv_msgs)); + spin_lock_init(&priv->recv_msg_lock); + INIT_LIST_HEAD(&priv->recv_msgs); init_waitqueue_head(&priv->wait); priv->fasync_queue = NULL; mutex_init(&priv->recv_mutex); @@ -129,7 +119,6 @@ static int ipmi_open(struct inode *inode, struct file *file) priv->default_retry_time_ms = 0; out: - mutex_unlock(&ipmi_mutex); return rv; } @@ -137,7 +126,7 @@ static int ipmi_release(struct inode *inode, struct file *file) { struct ipmi_file_private *priv = file->private_data; int rv; - struct ipmi_recv_msg *msg, *next; + struct ipmi_recv_msg *msg, *next; rv = ipmi_destroy_user(priv->user); if (rv) @@ -146,13 +135,12 @@ static int ipmi_release(struct inode *inode, struct file *file) list_for_each_entry_safe(msg, next, &priv->recv_msgs, link) ipmi_free_recv_msg(msg); - kfree(priv); return 0; } -static int handle_send_req(ipmi_user_t user, +static int handle_send_req(struct ipmi_user *user, struct ipmi_req *req, int retries, unsigned int retry_time_ms) @@ -189,8 +177,7 @@ static int handle_send_req(ipmi_user_t user, if (copy_from_user(msg.data, req->msg.data, - req->msg.data_len)) - { + req->msg.data_len)) { rv = -EFAULT; goto out; } @@ -233,25 +220,24 @@ static int handle_recv(struct ipmi_file_private *priv, mutex_lock(&priv->recv_mutex); /* Grab the message off the list. */ - spin_lock_irqsave(&(priv->recv_msg_lock), flags); + spin_lock_irqsave(&priv->recv_msg_lock, flags); if (list_empty(&(priv->recv_msgs))) { - spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); + spin_unlock_irqrestore(&priv->recv_msg_lock, flags); rv = -EAGAIN; goto recv_err; } entry = priv->recv_msgs.next; msg = list_entry(entry, struct ipmi_recv_msg, link); list_del(entry); - spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); + spin_unlock_irqrestore(&priv->recv_msg_lock, flags); addr_len = ipmi_addr_length(msg->addr.addr_type); - if (rsp->addr_len < addr_len) - { + if (rsp->addr_len < addr_len) { rv = -EINVAL; goto recv_putback_on_err; } - if (copy_to_user(rsp->addr, &(msg->addr), addr_len)) { + if (copy_to_user(rsp->addr, &msg->addr, addr_len)) { rv = -EFAULT; goto recv_putback_on_err; } @@ -273,8 +259,7 @@ static int handle_recv(struct ipmi_file_private *priv, if (copy_to_user(rsp->msg.data, msg->msg.data, - msg->msg.data_len)) - { + msg->msg.data_len)) { rv = -EFAULT; goto recv_putback_on_err; } @@ -294,9 +279,9 @@ static int handle_recv(struct ipmi_file_private *priv, recv_putback_on_err: /* If we got an error, put the message back onto the head of the queue. */ - spin_lock_irqsave(&(priv->recv_msg_lock), flags); - list_add(entry, &(priv->recv_msgs)); - spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); + spin_lock_irqsave(&priv->recv_msg_lock, flags); + list_add(entry, &priv->recv_msgs); + spin_unlock_irqrestore(&priv->recv_msg_lock, flags); recv_err: mutex_unlock(&priv->recv_mutex); return rv; @@ -307,9 +292,9 @@ static int copyout_recv(struct ipmi_recv *rsp, void __user *to) return copy_to_user(to, rsp, sizeof(struct ipmi_recv)) ? -EFAULT : 0; } -static int ipmi_ioctl(struct file *file, - unsigned int cmd, - unsigned long data) +static long ipmi_ioctl(struct file *file, + unsigned int cmd, + unsigned long data) { int rv = -EINVAL; struct ipmi_file_private *priv = file->private_data; @@ -320,16 +305,20 @@ static int ipmi_ioctl(struct file *file, case IPMICTL_SEND_COMMAND: { struct ipmi_req req; + int retries; + unsigned int retry_time_ms; if (copy_from_user(&req, arg, sizeof(req))) { rv = -EFAULT; break; } - rv = handle_send_req(priv->user, - &req, - priv->default_retries, - priv->default_retry_time_ms); + mutex_lock(&priv->recv_mutex); + retries = priv->default_retries; + retry_time_ms = priv->default_retry_time_ms; + mutex_unlock(&priv->recv_mutex); + + rv = handle_send_req(priv->user, &req, retries, retry_time_ms); break; } @@ -569,8 +558,10 @@ static int ipmi_ioctl(struct file *file, break; } + mutex_lock(&priv->recv_mutex); priv->default_retries = parms.retries; priv->default_retry_time_ms = parms.retry_time_ms; + mutex_unlock(&priv->recv_mutex); rv = 0; break; } @@ -579,8 +570,10 @@ static int ipmi_ioctl(struct file *file, { struct ipmi_timing_parms parms; + mutex_lock(&priv->recv_mutex); parms.retries = priv->default_retries; parms.retry_time_ms = priv->default_retry_time_ms; + mutex_unlock(&priv->recv_mutex); if (copy_to_user(arg, &parms, sizeof(parms))) { rv = -EFAULT; @@ -615,30 +608,16 @@ static int ipmi_ioctl(struct file *file, rv = ipmi_set_maintenance_mode(priv->user, mode); break; } + + default: + rv = -ENOTTY; + break; } return rv; } -/* - * Note: it doesn't make sense to take the BKL here but - * not in compat_ipmi_ioctl. -arnd - */ -static long ipmi_unlocked_ioctl(struct file *file, - unsigned int cmd, - unsigned long data) -{ - int ret; - - mutex_lock(&ipmi_mutex); - ret = ipmi_ioctl(file, cmd, data); - mutex_unlock(&ipmi_mutex); - - return ret; -} - #ifdef CONFIG_COMPAT - /* * The following code contains code for supporting 32-bit compatible * ioctls on 64-bit kernels. This allows running 32-bit apps on the @@ -749,15 +728,21 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, { struct ipmi_req rp; struct compat_ipmi_req r32; + int retries; + unsigned int retry_time_ms; if (copy_from_user(&r32, compat_ptr(arg), sizeof(r32))) return -EFAULT; get_compat_ipmi_req(&rp, &r32); + mutex_lock(&priv->recv_mutex); + retries = priv->default_retries; + retry_time_ms = priv->default_retry_time_ms; + mutex_unlock(&priv->recv_mutex); + return handle_send_req(priv->user, &rp, - priv->default_retries, - priv->default_retry_time_ms); + retries, retry_time_ms); } case COMPAT_IPMICTL_SEND_COMMAND_SETTIME: { @@ -791,25 +776,13 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, return ipmi_ioctl(filep, cmd, arg); } } - -static long unlocked_compat_ipmi_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - int ret; - - mutex_lock(&ipmi_mutex); - ret = compat_ipmi_ioctl(filep, cmd, arg); - mutex_unlock(&ipmi_mutex); - - return ret; -} #endif static const struct file_operations ipmi_fops = { .owner = THIS_MODULE, - .unlocked_ioctl = ipmi_unlocked_ioctl, + .unlocked_ioctl = ipmi_ioctl, #ifdef CONFIG_COMPAT - .compat_ioctl = unlocked_compat_ipmi_ioctl, + .compat_ioctl = compat_ipmi_ioctl, #endif .open = ipmi_open, .release = ipmi_release, diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 361148938801..51832b8a2c62 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -37,11 +37,30 @@ static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); static int ipmi_init_msghandler(void); static void smi_recv_tasklet(unsigned long); -static void handle_new_recv_msgs(ipmi_smi_t intf); -static void need_waiter(ipmi_smi_t intf); -static int handle_one_recv_msg(ipmi_smi_t intf, +static void handle_new_recv_msgs(struct ipmi_smi *intf); +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 int initialized; enum ipmi_panic_event_op { @@ -112,14 +131,13 @@ module_param_cb(panic_op, &panic_op_ops, NULL, 0600); MODULE_PARM_DESC(panic_op, "Sets if the IPMI driver will attempt to store panic information in the event log in the event of a panic. Set to 'none' for no, 'event' for a single event, or 'string' for a generic event and the panic string in IPMI OEM events."); -#ifdef CONFIG_IPMI_PROC_INTERFACE -static struct proc_dir_entry *proc_ipmi_root; -#endif /* CONFIG_IPMI_PROC_INTERFACE */ +#define MAX_EVENTS_IN_QUEUE 25 /* Remain in auto-maintenance mode for this amount of time (in ms). */ -#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000 - -#define MAX_EVENTS_IN_QUEUE 25 +static unsigned long maintenance_mode_timeout_ms = 30000; +module_param(maintenance_mode_timeout_ms, ulong, 0644); +MODULE_PARM_DESC(maintenance_mode_timeout_ms, + "The time (milliseconds) after the last maintenance message that the connection stays in maintenance mode."); /* * Don't let a message sit in a queue forever, always time it with at lest @@ -127,6 +145,31 @@ static struct proc_dir_entry *proc_ipmi_root; */ #define MAX_MSG_TIMEOUT 60000 +/* + * Timeout times below are in milliseconds, and are done off a 1 + * second timer. So setting the value to 1000 would mean anything + * between 0 and 1000ms. So really the only reasonable minimum + * setting it 2000ms, which is between 1 and 2 seconds. + */ + +/* The default timeout for message retries. */ +static unsigned long default_retry_ms = 2000; +module_param(default_retry_ms, ulong, 0644); +MODULE_PARM_DESC(default_retry_ms, + "The time (milliseconds) between retry sends"); + +/* The default timeout for maintenance mode message retries. */ +static unsigned long default_maintenance_retry_ms = 3000; +module_param(default_maintenance_retry_ms, ulong, 0644); +MODULE_PARM_DESC(default_maintenance_retry_ms, + "The time (milliseconds) between retry sends in maintenance mode"); + +/* The default maximum number of retries */ +static unsigned int default_max_retries = 4; +module_param(default_max_retries, uint, 0644); +MODULE_PARM_DESC(default_max_retries, + "The time (milliseconds) between retry sends in maintenance mode"); + /* Call every ~1000 ms. */ #define IPMI_TIMEOUT_TIME 1000 @@ -150,8 +193,12 @@ static struct proc_dir_entry *proc_ipmi_root; struct ipmi_user { struct list_head link; - /* Set to false when the user is destroyed. */ - bool valid; + /* + * Set to NULL when the user is destroyed, a pointer to myself + * so srcu_dereference can be used on it. + */ + struct ipmi_user *self; + struct srcu_struct release_barrier; struct kref refcount; @@ -160,16 +207,33 @@ struct ipmi_user { void *handler_data; /* The interface this user is bound to. */ - ipmi_smi_t intf; + struct ipmi_smi *intf; /* Does this interface receive IPMI events? */ bool gets_events; }; +static struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user, int *index) + __acquires(user->release_barrier) +{ + struct ipmi_user *ruser; + + *index = srcu_read_lock(&user->release_barrier); + ruser = srcu_dereference(user->self, &user->release_barrier); + if (!ruser) + srcu_read_unlock(&user->release_barrier, *index); + return ruser; +} + +static void release_ipmi_user(struct ipmi_user *user, int index) +{ + srcu_read_unlock(&user->release_barrier, index); +} + struct cmd_rcvr { struct list_head link; - ipmi_user_t user; + struct ipmi_user *user; unsigned char netfn; unsigned char cmd; unsigned int chans; @@ -247,13 +311,6 @@ struct ipmi_my_addrinfo { unsigned char lun; }; -#ifdef CONFIG_IPMI_PROC_INTERFACE -struct ipmi_proc_entry { - char *name; - struct ipmi_proc_entry *next; -}; -#endif - /* * Note that the product id, manufacturer id, guid, and device id are * immutable in this structure, so dyn_mutex is not required for @@ -275,7 +332,7 @@ struct bmc_device { }; #define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev) -static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, +static int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, struct ipmi_device_id *id, bool *guid_set, guid_t *guid); @@ -397,10 +454,11 @@ struct ipmi_smi { struct list_head link; /* - * The list of upper layers that are using me. seq_lock - * protects this. + * The list of upper layers that are using me. seq_lock write + * protects this. Read protection is with srcu. */ struct list_head users; + struct srcu_struct users_srcu; /* Used for wake ups at startup. */ wait_queue_head_t waitq; @@ -420,24 +478,9 @@ struct ipmi_smi { bool in_bmc_register; /* Handle recursive situations. Yuck. */ struct work_struct bmc_reg_work; - /* - * This is the lower-layer's sender routine. Note that you - * must either be holding the ipmi_interfaces_mutex or be in - * an umpreemptible region to use this. You must fetch the - * value into a local variable and make sure it is not NULL. - */ const struct ipmi_smi_handlers *handlers; void *send_info; -#ifdef CONFIG_IPMI_PROC_INTERFACE - /* A list of proc entries for this interface. */ - struct mutex proc_entry_lock; - struct ipmi_proc_entry *proc_entries; - - struct proc_dir_entry *proc_dir; - char proc_dir_name[10]; -#endif - /* Driver-model device for the system interface. */ struct device *si_dev; @@ -503,6 +546,13 @@ struct ipmi_smi { spinlock_t maintenance_mode_lock; /* Used in a timer... */ /* + * If we are doing maintenance on something on IPMB, extend + * the timeout time to avoid timeouts writing firmware and + * such. + */ + int ipmb_maintenance_mode_timeout; + + /* * A cheap hack, if this is non-null and a message to an * interface comes in with a NULL user, call this routine with * it. Note that the message will still be freed by the @@ -510,7 +560,8 @@ struct ipmi_smi { * * Protected by bmc_reg_mutex. */ - void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg); + void (*null_user_handler)(struct ipmi_smi *intf, + struct ipmi_recv_msg *msg); /* * When we are scanning the channels for an SMI, this will @@ -536,12 +587,12 @@ struct ipmi_smi { }; #define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) -static void __get_guid(ipmi_smi_t intf); -static void __ipmi_bmc_unregister(ipmi_smi_t intf); -static int __ipmi_bmc_register(ipmi_smi_t intf, +static void __get_guid(struct ipmi_smi *intf); +static void __ipmi_bmc_unregister(struct ipmi_smi *intf); +static int __ipmi_bmc_register(struct ipmi_smi *intf, struct ipmi_device_id *id, bool guid_set, guid_t *guid, int intf_num); -static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id); +static int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id); /** @@ -560,6 +611,7 @@ static DEFINE_MUTEX(ipmidriver_mutex); static LIST_HEAD(ipmi_interfaces); static DEFINE_MUTEX(ipmi_interfaces_mutex); +DEFINE_STATIC_SRCU(ipmi_interfaces_srcu); /* * List of watchers that want to know when smi's are added and deleted. @@ -620,7 +672,7 @@ static void free_smi_msg_list(struct list_head *q) } } -static void clean_up_interface_data(ipmi_smi_t intf) +static void clean_up_interface_data(struct ipmi_smi *intf) { int i; struct cmd_rcvr *rcvr, *rcvr2; @@ -652,7 +704,7 @@ static void clean_up_interface_data(ipmi_smi_t intf) static void intf_free(struct kref *ref) { - ipmi_smi_t intf = container_of(ref, struct ipmi_smi, refcount); + struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount); clean_up_interface_data(intf); kfree(intf); @@ -660,65 +712,39 @@ static void intf_free(struct kref *ref) struct watcher_entry { int intf_num; - ipmi_smi_t intf; + struct ipmi_smi *intf; struct list_head link; }; int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) { - ipmi_smi_t intf; - LIST_HEAD(to_deliver); - struct watcher_entry *e, *e2; + struct ipmi_smi *intf; + int index; mutex_lock(&smi_watchers_mutex); - mutex_lock(&ipmi_interfaces_mutex); - - /* Build a list of things to deliver. */ - list_for_each_entry(intf, &ipmi_interfaces, link) { - if (intf->intf_num == -1) - continue; - e = kmalloc(sizeof(*e), GFP_KERNEL); - if (!e) - goto out_err; - kref_get(&intf->refcount); - e->intf = intf; - e->intf_num = intf->intf_num; - list_add_tail(&e->link, &to_deliver); - } - - /* We will succeed, so add it to the list. */ list_add(&watcher->link, &smi_watchers); - mutex_unlock(&ipmi_interfaces_mutex); + index = srcu_read_lock(&ipmi_interfaces_srcu); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + int intf_num = READ_ONCE(intf->intf_num); - list_for_each_entry_safe(e, e2, &to_deliver, link) { - list_del(&e->link); - watcher->new_smi(e->intf_num, e->intf->si_dev); - kref_put(&e->intf->refcount, intf_free); - kfree(e); + if (intf_num == -1) + continue; + watcher->new_smi(intf_num, intf->si_dev); } + srcu_read_unlock(&ipmi_interfaces_srcu, index); mutex_unlock(&smi_watchers_mutex); return 0; - - out_err: - mutex_unlock(&ipmi_interfaces_mutex); - mutex_unlock(&smi_watchers_mutex); - list_for_each_entry_safe(e, e2, &to_deliver, link) { - list_del(&e->link); - kref_put(&e->intf->refcount, intf_free); - kfree(e); - } - return -ENOMEM; } EXPORT_SYMBOL(ipmi_smi_watcher_register); int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) { mutex_lock(&smi_watchers_mutex); - list_del(&(watcher->link)); + list_del(&watcher->link); mutex_unlock(&smi_watchers_mutex); return 0; } @@ -732,12 +758,14 @@ call_smi_watchers(int i, struct device *dev) { struct ipmi_smi_watcher *w; + mutex_lock(&smi_watchers_mutex); list_for_each_entry(w, &smi_watchers, link) { if (try_module_get(w->owner)) { w->new_smi(i, dev); module_put(w->owner); } } + mutex_unlock(&smi_watchers_mutex); } static int @@ -831,18 +859,17 @@ unsigned int ipmi_addr_length(int addr_type) } EXPORT_SYMBOL(ipmi_addr_length); -static void deliver_response(struct ipmi_recv_msg *msg) +static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) { - if (!msg->user) { - ipmi_smi_t intf = msg->user_msg_data; + int rv = 0; + if (!msg->user) { /* Special handling for NULL users. */ if (intf->null_user_handler) { intf->null_user_handler(intf, msg); - ipmi_inc_stat(intf, handled_local_responses); } else { /* No handler, so give up. */ - ipmi_inc_stat(intf, unhandled_local_responses); + rv = -EINVAL; } ipmi_free_recv_msg(msg); } else if (!oops_in_progress) { @@ -851,21 +878,40 @@ static void deliver_response(struct ipmi_recv_msg *msg) * receive handler doesn't much meaning and has a deadlock * risk. At this moment, simply skip it in that case. */ + int index; + struct ipmi_user *user = acquire_ipmi_user(msg->user, &index); - ipmi_user_t user = msg->user; - user->handler->ipmi_recv_hndl(msg, user->handler_data); + if (user) { + user->handler->ipmi_recv_hndl(msg, user->handler_data); + release_ipmi_user(msg->user, index); + } else { + /* User went away, give up. */ + ipmi_free_recv_msg(msg); + rv = -EINVAL; + } } + + return rv; } -static void -deliver_err_response(struct ipmi_recv_msg *msg, int err) +static void deliver_local_response(struct ipmi_smi *intf, + struct ipmi_recv_msg *msg) +{ + if (deliver_response(intf, msg)) + ipmi_inc_stat(intf, unhandled_local_responses); + else + ipmi_inc_stat(intf, handled_local_responses); +} + +static void deliver_err_response(struct ipmi_smi *intf, + struct ipmi_recv_msg *msg, int err) { msg->recv_type = IPMI_RESPONSE_RECV_TYPE; msg->msg_data[0] = err; msg->msg.netfn |= 1; /* Convert to a response. */ msg->msg.data_len = 1; msg->msg.data = msg->msg_data; - deliver_response(msg); + deliver_local_response(intf, msg); } /* @@ -873,7 +919,7 @@ deliver_err_response(struct ipmi_recv_msg *msg, int err) * message with the given timeout to the sequence table. This must be * called with the interface's seq_lock held. */ -static int intf_next_seq(ipmi_smi_t intf, +static int intf_next_seq(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg, unsigned long timeout, int retries, @@ -884,6 +930,11 @@ static int intf_next_seq(ipmi_smi_t intf, int rv = 0; unsigned int i; + if (timeout == 0) + timeout = default_retry_ms; + if (retries < 0) + retries = default_max_retries; + for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; i = (i+1)%IPMI_IPMB_NUM_SEQ) { if (!intf->seq_table[i].inuse) @@ -921,7 +972,7 @@ static int intf_next_seq(ipmi_smi_t intf, * guard against message coming in after their timeout and the * sequence number being reused). */ -static int intf_find_seq(ipmi_smi_t intf, +static int intf_find_seq(struct ipmi_smi *intf, unsigned char seq, short channel, unsigned char cmd, @@ -935,26 +986,26 @@ static int intf_find_seq(ipmi_smi_t intf, if (seq >= IPMI_IPMB_NUM_SEQ) return -EINVAL; - spin_lock_irqsave(&(intf->seq_lock), flags); + spin_lock_irqsave(&intf->seq_lock, flags); if (intf->seq_table[seq].inuse) { struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg; if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd) && (msg->msg.netfn == netfn) - && (ipmi_addr_equal(addr, &(msg->addr)))) { + && (ipmi_addr_equal(addr, &msg->addr))) { *recv_msg = msg; intf->seq_table[seq].inuse = 0; rv = 0; } } - spin_unlock_irqrestore(&(intf->seq_lock), flags); + spin_unlock_irqrestore(&intf->seq_lock, flags); return rv; } /* Start the timer for a specific sequence table entry. */ -static int intf_start_seq_timer(ipmi_smi_t intf, +static int intf_start_seq_timer(struct ipmi_smi *intf, long msgid) { int rv = -ENODEV; @@ -965,24 +1016,24 @@ static int intf_start_seq_timer(ipmi_smi_t intf, GET_SEQ_FROM_MSGID(msgid, seq, seqid); - spin_lock_irqsave(&(intf->seq_lock), flags); + spin_lock_irqsave(&intf->seq_lock, flags); /* * We do this verification because the user can be deleted * while a message is outstanding. */ if ((intf->seq_table[seq].inuse) && (intf->seq_table[seq].seqid == seqid)) { - struct seq_table *ent = &(intf->seq_table[seq]); + struct seq_table *ent = &intf->seq_table[seq]; ent->timeout = ent->orig_timeout; rv = 0; } - spin_unlock_irqrestore(&(intf->seq_lock), flags); + spin_unlock_irqrestore(&intf->seq_lock, flags); return rv; } /* Got an error for the send message for a specific sequence number. */ -static int intf_err_seq(ipmi_smi_t intf, +static int intf_err_seq(struct ipmi_smi *intf, long msgid, unsigned int err) { @@ -995,23 +1046,23 @@ static int intf_err_seq(ipmi_smi_t intf, GET_SEQ_FROM_MSGID(msgid, seq, seqid); - spin_lock_irqsave(&(intf->seq_lock), flags); + spin_lock_irqsave(&intf->seq_lock, flags); /* * We do this verification because the user can be deleted * while a message is outstanding. */ if ((intf->seq_table[seq].inuse) && (intf->seq_table[seq].seqid == seqid)) { - struct seq_table *ent = &(intf->seq_table[seq]); + struct seq_table *ent = &intf->seq_table[seq]; ent->inuse = 0; msg = ent->recv_msg; rv = 0; } - spin_unlock_irqrestore(&(intf->seq_lock), flags); + spin_unlock_irqrestore(&intf->seq_lock, flags); if (msg) - deliver_err_response(msg, err); + deliver_err_response(intf, msg, err); return rv; } @@ -1020,12 +1071,12 @@ static int intf_err_seq(ipmi_smi_t intf, int ipmi_create_user(unsigned int if_num, const struct ipmi_user_hndl *handler, void *handler_data, - ipmi_user_t *user) + struct ipmi_user **user) { unsigned long flags; - ipmi_user_t new_user; - int rv = 0; - ipmi_smi_t intf; + struct ipmi_user *new_user; + int rv = 0, index; + struct ipmi_smi *intf; /* * There is no module usecount here, because it's not @@ -1059,7 +1110,7 @@ int ipmi_create_user(unsigned int if_num, if (!new_user) return -ENOMEM; - mutex_lock(&ipmi_interfaces_mutex); + index = srcu_read_lock(&ipmi_interfaces_srcu); list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { if (intf->intf_num == if_num) goto found; @@ -1069,6 +1120,10 @@ int ipmi_create_user(unsigned int if_num, goto out_kfree; found: + rv = init_srcu_struct(&new_user->release_barrier); + if (rv) + goto out_kfree; + /* Note that each existing user holds a refcount to the interface. */ kref_get(&intf->refcount); @@ -1078,26 +1133,7 @@ int ipmi_create_user(unsigned int if_num, new_user->intf = intf; new_user->gets_events = false; - if (!try_module_get(intf->handlers->owner)) { - rv = -ENODEV; - goto out_kref; - } - - if (intf->handlers->inc_usecount) { - rv = intf->handlers->inc_usecount(intf->send_info); - if (rv) { - module_put(intf->handlers->owner); - goto out_kref; - } - } - - /* - * Hold the lock so intf->handlers is guaranteed to be good - * until now - */ - mutex_unlock(&ipmi_interfaces_mutex); - - new_user->valid = true; + rcu_assign_pointer(new_user->self, new_user); spin_lock_irqsave(&intf->seq_lock, flags); list_add_rcu(&new_user->link, &intf->users); spin_unlock_irqrestore(&intf->seq_lock, flags); @@ -1106,13 +1142,12 @@ int ipmi_create_user(unsigned int if_num, if (atomic_inc_return(&intf->event_waiters) == 1) need_waiter(intf); } + srcu_read_unlock(&ipmi_interfaces_srcu, index); *user = new_user; return 0; -out_kref: - kref_put(&intf->refcount, intf_free); out_kfree: - mutex_unlock(&ipmi_interfaces_mutex); + srcu_read_unlock(&ipmi_interfaces_srcu, index); kfree(new_user); return rv; } @@ -1120,26 +1155,25 @@ EXPORT_SYMBOL(ipmi_create_user); int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) { - int rv = 0; - ipmi_smi_t intf; - const struct ipmi_smi_handlers *handlers; + int rv, index; + struct ipmi_smi *intf; - mutex_lock(&ipmi_interfaces_mutex); + index = srcu_read_lock(&ipmi_interfaces_srcu); list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { if (intf->intf_num == if_num) goto found; } + srcu_read_unlock(&ipmi_interfaces_srcu, index); + /* Not found, return an error */ - rv = -EINVAL; - mutex_unlock(&ipmi_interfaces_mutex); - return rv; + return -EINVAL; found: - handlers = intf->handlers; - rv = -ENOSYS; - if (handlers->get_smi_info) - rv = handlers->get_smi_info(intf->send_info, data); - mutex_unlock(&ipmi_interfaces_mutex); + if (!intf->handlers->get_smi_info) + rv = -ENOTTY; + else + rv = intf->handlers->get_smi_info(intf->send_info, data); + srcu_read_unlock(&ipmi_interfaces_srcu, index); return rv; } @@ -1147,19 +1181,34 @@ EXPORT_SYMBOL(ipmi_get_smi_info); static void free_user(struct kref *ref) { - ipmi_user_t user = container_of(ref, struct ipmi_user, refcount); + struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount); kfree(user); } -int ipmi_destroy_user(ipmi_user_t user) +static void _ipmi_destroy_user(struct ipmi_user *user) { - ipmi_smi_t intf = user->intf; + struct ipmi_smi *intf = user->intf; int i; unsigned long flags; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - user->valid = false; + if (!acquire_ipmi_user(user, &i)) { + /* + * The user has already been cleaned up, just make sure + * nothing is using it and return. + */ + synchronize_srcu(&user->release_barrier); + return; + } + + rcu_assign_pointer(user->self, NULL); + release_ipmi_user(user, i); + + synchronize_srcu(&user->release_barrier); + + if (user->handler->shutdown) + user->handler->shutdown(user->handler_data); if (user->handler->ipmi_watchdog_pretimeout) atomic_dec(&intf->event_waiters); @@ -1184,7 +1233,7 @@ int ipmi_destroy_user(ipmi_user_t user) * Remove the user from the command receiver's table. First * we build a list of everything (not using the standard link, * since other things may be using it till we do - * synchronize_rcu()) then free everything in that list. + * synchronize_srcu()) then free everything in that list. */ mutex_lock(&intf->cmd_rcvrs_mutex); list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { @@ -1202,109 +1251,156 @@ int ipmi_destroy_user(ipmi_user_t user) kfree(rcvr); } - mutex_lock(&ipmi_interfaces_mutex); - if (intf->handlers) { - module_put(intf->handlers->owner); - if (intf->handlers->dec_usecount) - intf->handlers->dec_usecount(intf->send_info); - } - mutex_unlock(&ipmi_interfaces_mutex); - kref_put(&intf->refcount, intf_free); +} +int ipmi_destroy_user(struct ipmi_user *user) +{ + _ipmi_destroy_user(user); + + cleanup_srcu_struct(&user->release_barrier); kref_put(&user->refcount, free_user); return 0; } EXPORT_SYMBOL(ipmi_destroy_user); -int ipmi_get_version(ipmi_user_t user, +int ipmi_get_version(struct ipmi_user *user, unsigned char *major, unsigned char *minor) { struct ipmi_device_id id; - int rv; + int rv, index; - rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL); - if (rv) - return rv; + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; - *major = ipmi_version_major(&id); - *minor = ipmi_version_minor(&id); + rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL); + if (!rv) { + *major = ipmi_version_major(&id); + *minor = ipmi_version_minor(&id); + } + release_ipmi_user(user, index); - return 0; + return rv; } EXPORT_SYMBOL(ipmi_get_version); -int ipmi_set_my_address(ipmi_user_t user, +int ipmi_set_my_address(struct ipmi_user *user, unsigned int channel, unsigned char address) { + int index, rv = 0; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + if (channel >= IPMI_MAX_CHANNELS) - return -EINVAL; - user->intf->addrinfo[channel].address = address; - return 0; + rv = -EINVAL; + else + user->intf->addrinfo[channel].address = address; + release_ipmi_user(user, index); + + return rv; } EXPORT_SYMBOL(ipmi_set_my_address); -int ipmi_get_my_address(ipmi_user_t user, +int ipmi_get_my_address(struct ipmi_user *user, unsigned int channel, unsigned char *address) { + int index, rv = 0; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + if (channel >= IPMI_MAX_CHANNELS) - return -EINVAL; - *address = user->intf->addrinfo[channel].address; - return 0; + rv = -EINVAL; + else + *address = user->intf->addrinfo[channel].address; + release_ipmi_user(user, index); + + return rv; } EXPORT_SYMBOL(ipmi_get_my_address); -int ipmi_set_my_LUN(ipmi_user_t user, +int ipmi_set_my_LUN(struct ipmi_user *user, unsigned int channel, unsigned char LUN) { + int index, rv = 0; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + if (channel >= IPMI_MAX_CHANNELS) - return -EINVAL; - user->intf->addrinfo[channel].lun = LUN & 0x3; + rv = -EINVAL; + else + user->intf->addrinfo[channel].lun = LUN & 0x3; + release_ipmi_user(user, index); + return 0; } EXPORT_SYMBOL(ipmi_set_my_LUN); -int ipmi_get_my_LUN(ipmi_user_t user, +int ipmi_get_my_LUN(struct ipmi_user *user, unsigned int channel, unsigned char *address) { + int index, rv = 0; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + if (channel >= IPMI_MAX_CHANNELS) - return -EINVAL; - *address = user->intf->addrinfo[channel].lun; - return 0; + rv = -EINVAL; + else + *address = user->intf->addrinfo[channel].lun; + release_ipmi_user(user, index); + + return rv; } EXPORT_SYMBOL(ipmi_get_my_LUN); -int ipmi_get_maintenance_mode(ipmi_user_t user) +int ipmi_get_maintenance_mode(struct ipmi_user *user) { - int mode; + int mode, index; unsigned long flags; + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags); mode = user->intf->maintenance_mode; spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags); + release_ipmi_user(user, index); return mode; } EXPORT_SYMBOL(ipmi_get_maintenance_mode); -static void maintenance_mode_update(ipmi_smi_t intf) +static void maintenance_mode_update(struct ipmi_smi *intf) { if (intf->handlers->set_maintenance_mode) intf->handlers->set_maintenance_mode( intf->send_info, intf->maintenance_mode_enable); } -int ipmi_set_maintenance_mode(ipmi_user_t user, int mode) +int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) { - int rv = 0; + int rv = 0, index; unsigned long flags; - ipmi_smi_t intf = user->intf; + struct ipmi_smi *intf = user->intf; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; spin_lock_irqsave(&intf->maintenance_mode_lock, flags); if (intf->maintenance_mode != mode) { @@ -1332,17 +1428,23 @@ int ipmi_set_maintenance_mode(ipmi_user_t user, int mode) } out_unlock: spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); + release_ipmi_user(user, index); return rv; } EXPORT_SYMBOL(ipmi_set_maintenance_mode); -int ipmi_set_gets_events(ipmi_user_t user, bool val) +int ipmi_set_gets_events(struct ipmi_user *user, bool val) { unsigned long flags; - ipmi_smi_t intf = user->intf; + struct ipmi_smi *intf = user->intf; struct ipmi_recv_msg *msg, *msg2; struct list_head msgs; + int index; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; INIT_LIST_HEAD(&msgs); @@ -1383,7 +1485,7 @@ int ipmi_set_gets_events(ipmi_user_t user, bool val) list_for_each_entry_safe(msg, msg2, &msgs, link) { msg->user = user; kref_get(&user->refcount); - deliver_response(msg); + deliver_local_response(intf, msg); } spin_lock_irqsave(&intf->events_lock, flags); @@ -1392,12 +1494,13 @@ int ipmi_set_gets_events(ipmi_user_t user, bool val) out: spin_unlock_irqrestore(&intf->events_lock, flags); + release_ipmi_user(user, index); return 0; } EXPORT_SYMBOL(ipmi_set_gets_events); -static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, +static struct cmd_rcvr *find_cmd_rcvr(struct ipmi_smi *intf, unsigned char netfn, unsigned char cmd, unsigned char chan) @@ -1412,7 +1515,7 @@ static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, return NULL; } -static int is_cmd_rcvr_exclusive(ipmi_smi_t intf, +static int is_cmd_rcvr_exclusive(struct ipmi_smi *intf, unsigned char netfn, unsigned char cmd, unsigned int chans) @@ -1427,19 +1530,24 @@ static int is_cmd_rcvr_exclusive(ipmi_smi_t intf, return 1; } -int ipmi_register_for_cmd(ipmi_user_t user, +int ipmi_register_for_cmd(struct ipmi_user *user, unsigned char netfn, unsigned char cmd, unsigned int chans) { - ipmi_smi_t intf = user->intf; + struct ipmi_smi *intf = user->intf; struct cmd_rcvr *rcvr; - int rv = 0; + int rv = 0, index; + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL); - if (!rcvr) - return -ENOMEM; + if (!rcvr) { + rv = -ENOMEM; + goto out_release; + } rcvr->cmd = cmd; rcvr->netfn = netfn; rcvr->chans = chans; @@ -1457,24 +1565,30 @@ int ipmi_register_for_cmd(ipmi_user_t user, list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); - out_unlock: +out_unlock: mutex_unlock(&intf->cmd_rcvrs_mutex); if (rv) kfree(rcvr); +out_release: + release_ipmi_user(user, index); return rv; } EXPORT_SYMBOL(ipmi_register_for_cmd); -int ipmi_unregister_for_cmd(ipmi_user_t user, +int ipmi_unregister_for_cmd(struct ipmi_user *user, unsigned char netfn, unsigned char cmd, unsigned int chans) { - ipmi_smi_t intf = user->intf; + struct ipmi_smi *intf = user->intf; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - int i, rv = -ENOENT; + int i, rv = -ENOENT, index; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; mutex_lock(&intf->cmd_rcvrs_mutex); for (i = 0; i < IPMI_NUM_CHANNELS; i++) { @@ -1495,12 +1609,14 @@ int ipmi_unregister_for_cmd(ipmi_user_t user, } mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); + release_ipmi_user(user, index); while (rcvrs) { atomic_dec(&intf->event_waiters); rcvr = rcvrs; rcvrs = rcvr->next; kfree(rcvr); } + return rv; } EXPORT_SYMBOL(ipmi_unregister_for_cmd); @@ -1535,21 +1651,19 @@ static inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg, smi_msg->data[3] = 0; smi_msg->data[i+3] = ipmb_addr->slave_addr; smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3); - smi_msg->data[i+5] = ipmb_checksum(&(smi_msg->data[i+3]), 2); + smi_msg->data[i+5] = ipmb_checksum(&smi_msg->data[i + 3], 2); smi_msg->data[i+6] = source_address; smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun; smi_msg->data[i+8] = msg->cmd; /* Now tack on the data to the message. */ if (msg->data_len > 0) - memcpy(&(smi_msg->data[i+9]), msg->data, - msg->data_len); + memcpy(&smi_msg->data[i + 9], msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 9; /* Now calculate the checksum and tack it on. */ smi_msg->data[i+smi_msg->data_size] - = ipmb_checksum(&(smi_msg->data[i+6]), - smi_msg->data_size-6); + = ipmb_checksum(&smi_msg->data[i + 6], smi_msg->data_size - 6); /* * Add on the checksum size and the offset from the @@ -1574,21 +1688,19 @@ static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg, smi_msg->data[3] = lan_addr->session_handle; smi_msg->data[4] = lan_addr->remote_SWID; smi_msg->data[5] = (msg->netfn << 2) | (lan_addr->lun & 0x3); - smi_msg->data[6] = ipmb_checksum(&(smi_msg->data[4]), 2); + smi_msg->data[6] = ipmb_checksum(&smi_msg->data[4], 2); smi_msg->data[7] = lan_addr->local_SWID; smi_msg->data[8] = (ipmb_seq << 2) | source_lun; smi_msg->data[9] = msg->cmd; /* Now tack on the data to the message. */ if (msg->data_len > 0) - memcpy(&(smi_msg->data[10]), msg->data, - msg->data_len); + memcpy(&smi_msg->data[10], msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 10; /* Now calculate the checksum and tack it on. */ smi_msg->data[smi_msg->data_size] - = ipmb_checksum(&(smi_msg->data[7]), - smi_msg->data_size-7); + = ipmb_checksum(&smi_msg->data[7], smi_msg->data_size - 7); /* * Add on the checksum size and the offset from the @@ -1599,7 +1711,7 @@ static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg, smi_msg->msgid = msgid; } -static struct ipmi_smi_msg *smi_add_send_msg(ipmi_smi_t intf, +static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf, struct ipmi_smi_msg *smi_msg, int priority) { @@ -1617,7 +1729,8 @@ static struct ipmi_smi_msg *smi_add_send_msg(ipmi_smi_t intf, } -static void smi_send(ipmi_smi_t intf, const struct ipmi_smi_handlers *handlers, +static void smi_send(struct ipmi_smi *intf, + const struct ipmi_smi_handlers *handlers, struct ipmi_smi_msg *smi_msg, int priority) { int run_to_completion = intf->run_to_completion; @@ -1636,405 +1749,435 @@ static void smi_send(ipmi_smi_t intf, const struct ipmi_smi_handlers *handlers, handlers->sender(intf->send_info, smi_msg); } -/* - * Separate from ipmi_request so that the user does not have to be - * supplied in certain circumstances (mainly at panic time). If - * messages are supplied, they will be freed, even if an error - * occurs. - */ -static int i_ipmi_request(ipmi_user_t user, - ipmi_smi_t intf, - struct ipmi_addr *addr, - long msgid, - struct kernel_ipmi_msg *msg, - void *user_msg_data, - void *supplied_smi, - struct ipmi_recv_msg *supplied_recv, - int priority, - unsigned char source_address, - unsigned char source_lun, - int retries, - unsigned int retry_time_ms) +static bool is_maintenance_mode_cmd(struct kernel_ipmi_msg *msg) { - int rv = 0; - struct ipmi_smi_msg *smi_msg; - struct ipmi_recv_msg *recv_msg; - unsigned long flags; + return (((msg->netfn == IPMI_NETFN_APP_REQUEST) + && ((msg->cmd == IPMI_COLD_RESET_CMD) + || (msg->cmd == IPMI_WARM_RESET_CMD))) + || (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST)); +} +static int i_ipmi_req_sysintf(struct ipmi_smi *intf, + struct ipmi_addr *addr, + long msgid, + struct kernel_ipmi_msg *msg, + struct ipmi_smi_msg *smi_msg, + struct ipmi_recv_msg *recv_msg, + int retries, + unsigned int retry_time_ms) +{ + struct ipmi_system_interface_addr *smi_addr; - if (supplied_recv) - recv_msg = supplied_recv; - else { - recv_msg = ipmi_alloc_recv_msg(); - if (recv_msg == NULL) - return -ENOMEM; - } - recv_msg->user_msg_data = user_msg_data; + if (msg->netfn & 1) + /* Responses are not allowed to the SMI. */ + return -EINVAL; - if (supplied_smi) - smi_msg = (struct ipmi_smi_msg *) supplied_smi; - else { - smi_msg = ipmi_alloc_smi_msg(); - if (smi_msg == NULL) { - ipmi_free_recv_msg(recv_msg); - return -ENOMEM; - } + smi_addr = (struct ipmi_system_interface_addr *) addr; + if (smi_addr->lun > 3) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EINVAL; } - rcu_read_lock(); - if (intf->in_shutdown) { - rv = -ENODEV; - goto out_err; - } + memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr)); - recv_msg->user = user; - if (user) - kref_get(&user->refcount); - recv_msg->msgid = msgid; - /* - * Store the message to send in the receive message so timeout - * responses can get the proper response data. - */ - recv_msg->msg = *msg; + if ((msg->netfn == IPMI_NETFN_APP_REQUEST) + && ((msg->cmd == IPMI_SEND_MSG_CMD) + || (msg->cmd == IPMI_GET_MSG_CMD) + || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) { + /* + * We don't let the user do these, since we manage + * the sequence numbers. + */ + ipmi_inc_stat(intf, sent_invalid_commands); + return -EINVAL; + } - if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { - struct ipmi_system_interface_addr *smi_addr; + if (is_maintenance_mode_cmd(msg)) { + unsigned long flags; - if (msg->netfn & 1) { - /* Responses are not allowed to the SMI. */ - rv = -EINVAL; - goto out_err; + spin_lock_irqsave(&intf->maintenance_mode_lock, flags); + intf->auto_maintenance_timeout + = maintenance_mode_timeout_ms; + if (!intf->maintenance_mode + && !intf->maintenance_mode_enable) { + intf->maintenance_mode_enable = true; + maintenance_mode_update(intf); } + spin_unlock_irqrestore(&intf->maintenance_mode_lock, + flags); + } - smi_addr = (struct ipmi_system_interface_addr *) addr; - if (smi_addr->lun > 3) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EINVAL; - goto out_err; - } + if (msg->data_len + 2 > IPMI_MAX_MSG_LENGTH) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EMSGSIZE; + } - memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr)); + smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3); + smi_msg->data[1] = msg->cmd; + smi_msg->msgid = msgid; + smi_msg->user_data = recv_msg; + if (msg->data_len > 0) + memcpy(&smi_msg->data[2], msg->data, msg->data_len); + smi_msg->data_size = msg->data_len + 2; + ipmi_inc_stat(intf, sent_local_commands); - if ((msg->netfn == IPMI_NETFN_APP_REQUEST) - && ((msg->cmd == IPMI_SEND_MSG_CMD) - || (msg->cmd == IPMI_GET_MSG_CMD) - || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) { - /* - * We don't let the user do these, since we manage - * the sequence numbers. - */ - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EINVAL; - goto out_err; - } + return 0; +} - if (((msg->netfn == IPMI_NETFN_APP_REQUEST) - && ((msg->cmd == IPMI_COLD_RESET_CMD) - || (msg->cmd == IPMI_WARM_RESET_CMD))) - || (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST)) { - spin_lock_irqsave(&intf->maintenance_mode_lock, flags); - intf->auto_maintenance_timeout - = IPMI_MAINTENANCE_MODE_TIMEOUT; - if (!intf->maintenance_mode - && !intf->maintenance_mode_enable) { - intf->maintenance_mode_enable = true; - maintenance_mode_update(intf); - } - spin_unlock_irqrestore(&intf->maintenance_mode_lock, - flags); - } +static int i_ipmi_req_ipmb(struct ipmi_smi *intf, + struct ipmi_addr *addr, + long msgid, + struct kernel_ipmi_msg *msg, + struct ipmi_smi_msg *smi_msg, + struct ipmi_recv_msg *recv_msg, + unsigned char source_address, + unsigned char source_lun, + int retries, + unsigned int retry_time_ms) +{ + struct ipmi_ipmb_addr *ipmb_addr; + unsigned char ipmb_seq; + long seqid; + int broadcast = 0; + struct ipmi_channel *chans; + int rv = 0; - if ((msg->data_len + 2) > IPMI_MAX_MSG_LENGTH) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EMSGSIZE; - goto out_err; - } + if (addr->channel >= IPMI_MAX_CHANNELS) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EINVAL; + } - smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3); - smi_msg->data[1] = msg->cmd; - smi_msg->msgid = msgid; - smi_msg->user_data = recv_msg; - if (msg->data_len > 0) - memcpy(&(smi_msg->data[2]), msg->data, msg->data_len); - smi_msg->data_size = msg->data_len + 2; - ipmi_inc_stat(intf, sent_local_commands); - } else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { - struct ipmi_ipmb_addr *ipmb_addr; - unsigned char ipmb_seq; - long seqid; - int broadcast = 0; - struct ipmi_channel *chans; + chans = READ_ONCE(intf->channel_list)->c; - if (addr->channel >= IPMI_MAX_CHANNELS) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EINVAL; - goto out_err; - } + if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EINVAL; + } - chans = READ_ONCE(intf->channel_list)->c; + if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) { + /* + * Broadcasts add a zero at the beginning of the + * message, but otherwise is the same as an IPMB + * address. + */ + addr->addr_type = IPMI_IPMB_ADDR_TYPE; + broadcast = 1; + retries = 0; /* Don't retry broadcasts. */ + } - if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EINVAL; - goto out_err; - } + /* + * 9 for the header and 1 for the checksum, plus + * possibly one for the broadcast. + */ + if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EMSGSIZE; + } - if (retries < 0) { - if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) - retries = 0; /* Don't retry broadcasts. */ - else - retries = 4; - } - if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) { - /* - * Broadcasts add a zero at the beginning of the - * message, but otherwise is the same as an IPMB - * address. - */ - addr->addr_type = IPMI_IPMB_ADDR_TYPE; - broadcast = 1; - } + ipmb_addr = (struct ipmi_ipmb_addr *) addr; + if (ipmb_addr->lun > 3) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EINVAL; + } + memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr)); - /* Default to 1 second retries. */ - if (retry_time_ms == 0) - retry_time_ms = 1000; + if (recv_msg->msg.netfn & 0x1) { + /* + * It's a response, so use the user's sequence + * from msgid. + */ + ipmi_inc_stat(intf, sent_ipmb_responses); + format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid, + msgid, broadcast, + source_address, source_lun); /* - * 9 for the header and 1 for the checksum, plus - * possibly one for the broadcast. + * Save the receive message so we can use it + * to deliver the response. */ - if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EMSGSIZE; - goto out_err; - } + smi_msg->user_data = recv_msg; + } else { + /* It's a command, so get a sequence for it. */ + unsigned long flags; - ipmb_addr = (struct ipmi_ipmb_addr *) addr; - if (ipmb_addr->lun > 3) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EINVAL; - goto out_err; - } + spin_lock_irqsave(&intf->seq_lock, flags); - memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr)); + if (is_maintenance_mode_cmd(msg)) + intf->ipmb_maintenance_mode_timeout = + maintenance_mode_timeout_ms; - if (recv_msg->msg.netfn & 0x1) { - /* - * It's a response, so use the user's sequence - * from msgid. - */ - ipmi_inc_stat(intf, sent_ipmb_responses); - format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid, - msgid, broadcast, - source_address, source_lun); + if (intf->ipmb_maintenance_mode_timeout && retry_time_ms == 0) + /* Different default in maintenance mode */ + retry_time_ms = default_maintenance_retry_ms; + /* + * Create a sequence number with a 1 second + * timeout and 4 retries. + */ + rv = intf_next_seq(intf, + recv_msg, + retry_time_ms, + retries, + broadcast, + &ipmb_seq, + &seqid); + if (rv) /* - * Save the receive message so we can use it - * to deliver the response. + * We have used up all the sequence numbers, + * probably, so abort. */ - smi_msg->user_data = recv_msg; - } else { - /* It's a command, so get a sequence for it. */ + goto out_err; - spin_lock_irqsave(&(intf->seq_lock), flags); + ipmi_inc_stat(intf, sent_ipmb_commands); - /* - * Create a sequence number with a 1 second - * timeout and 4 retries. - */ - rv = intf_next_seq(intf, - recv_msg, - retry_time_ms, - retries, - broadcast, - &ipmb_seq, - &seqid); - if (rv) { - /* - * We have used up all the sequence numbers, - * probably, so abort. - */ - spin_unlock_irqrestore(&(intf->seq_lock), - flags); - goto out_err; - } + /* + * Store the sequence number in the message, + * so that when the send message response + * comes back we can start the timer. + */ + format_ipmb_msg(smi_msg, msg, ipmb_addr, + STORE_SEQ_IN_MSGID(ipmb_seq, seqid), + ipmb_seq, broadcast, + source_address, source_lun); - ipmi_inc_stat(intf, sent_ipmb_commands); + /* + * Copy the message into the recv message data, so we + * can retransmit it later if necessary. + */ + memcpy(recv_msg->msg_data, smi_msg->data, + smi_msg->data_size); + recv_msg->msg.data = recv_msg->msg_data; + recv_msg->msg.data_len = smi_msg->data_size; - /* - * Store the sequence number in the message, - * so that when the send message response - * comes back we can start the timer. - */ - format_ipmb_msg(smi_msg, msg, ipmb_addr, - STORE_SEQ_IN_MSGID(ipmb_seq, seqid), - ipmb_seq, broadcast, - source_address, source_lun); + /* + * We don't unlock until here, because we need + * to copy the completed message into the + * recv_msg before we release the lock. + * Otherwise, race conditions may bite us. I + * know that's pretty paranoid, but I prefer + * to be correct. + */ +out_err: + spin_unlock_irqrestore(&intf->seq_lock, flags); + } - /* - * Copy the message into the recv message data, so we - * can retransmit it later if necessary. - */ - memcpy(recv_msg->msg_data, smi_msg->data, - smi_msg->data_size); - recv_msg->msg.data = recv_msg->msg_data; - recv_msg->msg.data_len = smi_msg->data_size; + return rv; +} - /* - * We don't unlock until here, because we need - * to copy the completed message into the - * recv_msg before we release the lock. - * Otherwise, race conditions may bite us. I - * know that's pretty paranoid, but I prefer - * to be correct. - */ - spin_unlock_irqrestore(&(intf->seq_lock), flags); - } - } else if (is_lan_addr(addr)) { - struct ipmi_lan_addr *lan_addr; - unsigned char ipmb_seq; - long seqid; - struct ipmi_channel *chans; +static int i_ipmi_req_lan(struct ipmi_smi *intf, + struct ipmi_addr *addr, + long msgid, + struct kernel_ipmi_msg *msg, + struct ipmi_smi_msg *smi_msg, + struct ipmi_recv_msg *recv_msg, + unsigned char source_lun, + int retries, + unsigned int retry_time_ms) +{ + struct ipmi_lan_addr *lan_addr; + unsigned char ipmb_seq; + long seqid; + struct ipmi_channel *chans; + int rv = 0; - if (addr->channel >= IPMI_MAX_CHANNELS) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EINVAL; - goto out_err; - } + if (addr->channel >= IPMI_MAX_CHANNELS) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EINVAL; + } - chans = READ_ONCE(intf->channel_list)->c; + chans = READ_ONCE(intf->channel_list)->c; - if ((chans[addr->channel].medium + if ((chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_8023LAN) - && (chans[addr->channel].medium - != IPMI_CHANNEL_MEDIUM_ASYNC)) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EINVAL; - goto out_err; - } + && (chans[addr->channel].medium + != IPMI_CHANNEL_MEDIUM_ASYNC)) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EINVAL; + } - retries = 4; + /* 11 for the header and 1 for the checksum. */ + if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EMSGSIZE; + } - /* Default to 1 second retries. */ - if (retry_time_ms == 0) - retry_time_ms = 1000; + lan_addr = (struct ipmi_lan_addr *) addr; + if (lan_addr->lun > 3) { + ipmi_inc_stat(intf, sent_invalid_commands); + return -EINVAL; + } - /* 11 for the header and 1 for the checksum. */ - if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EMSGSIZE; - goto out_err; - } + memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr)); - lan_addr = (struct ipmi_lan_addr *) addr; - if (lan_addr->lun > 3) { - ipmi_inc_stat(intf, sent_invalid_commands); - rv = -EINVAL; - goto out_err; - } + if (recv_msg->msg.netfn & 0x1) { + /* + * It's a response, so use the user's sequence + * from msgid. + */ + ipmi_inc_stat(intf, sent_lan_responses); + format_lan_msg(smi_msg, msg, lan_addr, msgid, + msgid, source_lun); - memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr)); + /* + * Save the receive message so we can use it + * to deliver the response. + */ + smi_msg->user_data = recv_msg; + } else { + /* It's a command, so get a sequence for it. */ + unsigned long flags; - if (recv_msg->msg.netfn & 0x1) { - /* - * It's a response, so use the user's sequence - * from msgid. - */ - ipmi_inc_stat(intf, sent_lan_responses); - format_lan_msg(smi_msg, msg, lan_addr, msgid, - msgid, source_lun); + spin_lock_irqsave(&intf->seq_lock, flags); + /* + * Create a sequence number with a 1 second + * timeout and 4 retries. + */ + rv = intf_next_seq(intf, + recv_msg, + retry_time_ms, + retries, + 0, + &ipmb_seq, + &seqid); + if (rv) /* - * Save the receive message so we can use it - * to deliver the response. + * We have used up all the sequence numbers, + * probably, so abort. */ - smi_msg->user_data = recv_msg; - } else { - /* It's a command, so get a sequence for it. */ + goto out_err; - spin_lock_irqsave(&(intf->seq_lock), flags); + ipmi_inc_stat(intf, sent_lan_commands); - /* - * Create a sequence number with a 1 second - * timeout and 4 retries. - */ - rv = intf_next_seq(intf, - recv_msg, - retry_time_ms, - retries, - 0, - &ipmb_seq, - &seqid); - if (rv) { - /* - * We have used up all the sequence numbers, - * probably, so abort. - */ - spin_unlock_irqrestore(&(intf->seq_lock), - flags); - goto out_err; - } + /* + * Store the sequence number in the message, + * so that when the send message response + * comes back we can start the timer. + */ + format_lan_msg(smi_msg, msg, lan_addr, + STORE_SEQ_IN_MSGID(ipmb_seq, seqid), + ipmb_seq, source_lun); - ipmi_inc_stat(intf, sent_lan_commands); + /* + * Copy the message into the recv message data, so we + * can retransmit it later if necessary. + */ + memcpy(recv_msg->msg_data, smi_msg->data, + smi_msg->data_size); + recv_msg->msg.data = recv_msg->msg_data; + recv_msg->msg.data_len = smi_msg->data_size; - /* - * Store the sequence number in the message, - * so that when the send message response - * comes back we can start the timer. - */ - format_lan_msg(smi_msg, msg, lan_addr, - STORE_SEQ_IN_MSGID(ipmb_seq, seqid), - ipmb_seq, source_lun); + /* + * We don't unlock until here, because we need + * to copy the completed message into the + * recv_msg before we release the lock. + * Otherwise, race conditions may bite us. I + * know that's pretty paranoid, but I prefer + * to be correct. + */ +out_err: + spin_unlock_irqrestore(&intf->seq_lock, flags); + } - /* - * Copy the message into the recv message data, so we - * can retransmit it later if necessary. - */ - memcpy(recv_msg->msg_data, smi_msg->data, - smi_msg->data_size); - recv_msg->msg.data = recv_msg->msg_data; - recv_msg->msg.data_len = smi_msg->data_size; + return rv; +} - /* - * We don't unlock until here, because we need - * to copy the completed message into the - * recv_msg before we release the lock. - * Otherwise, race conditions may bite us. I - * know that's pretty paranoid, but I prefer - * to be correct. - */ - spin_unlock_irqrestore(&(intf->seq_lock), flags); +/* + * Separate from ipmi_request so that the user does not have to be + * supplied in certain circumstances (mainly at panic time). If + * messages are supplied, they will be freed, even if an error + * occurs. + */ +static int i_ipmi_request(struct ipmi_user *user, + struct ipmi_smi *intf, + struct ipmi_addr *addr, + long msgid, + struct kernel_ipmi_msg *msg, + void *user_msg_data, + void *supplied_smi, + struct ipmi_recv_msg *supplied_recv, + int priority, + unsigned char source_address, + unsigned char source_lun, + int retries, + unsigned int retry_time_ms) +{ + struct ipmi_smi_msg *smi_msg; + struct ipmi_recv_msg *recv_msg; + int rv = 0; + + if (supplied_recv) + recv_msg = supplied_recv; + else { + recv_msg = ipmi_alloc_recv_msg(); + if (recv_msg == NULL) { + rv = -ENOMEM; + goto out; } + } + recv_msg->user_msg_data = user_msg_data; + + if (supplied_smi) + smi_msg = (struct ipmi_smi_msg *) supplied_smi; + else { + smi_msg = ipmi_alloc_smi_msg(); + if (smi_msg == NULL) { + ipmi_free_recv_msg(recv_msg); + rv = -ENOMEM; + goto out; + } + } + + rcu_read_lock(); + if (intf->in_shutdown) { + rv = -ENODEV; + goto out_err; + } + + recv_msg->user = user; + if (user) + /* The put happens when the message is freed. */ + kref_get(&user->refcount); + recv_msg->msgid = msgid; + /* + * Store the message to send in the receive message so timeout + * responses can get the proper response data. + */ + recv_msg->msg = *msg; + + if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { + rv = i_ipmi_req_sysintf(intf, addr, msgid, msg, smi_msg, + recv_msg, retries, retry_time_ms); + } else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { + rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg, + source_address, source_lun, + retries, retry_time_ms); + } else if (is_lan_addr(addr)) { + rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg, + source_lun, retries, retry_time_ms); } else { /* Unknown address type. */ ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; - goto out_err; } -#ifdef DEBUG_MSGING - { - int m; - for (m = 0; m < smi_msg->data_size; m++) - printk(" %2.2x", smi_msg->data[m]); - printk("\n"); - } -#endif + if (rv) { +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); - smi_send(intf, intf->handlers, smi_msg, priority); + smi_send(intf, intf->handlers, smi_msg, priority); + } rcu_read_unlock(); - return 0; - - out_err: - rcu_read_unlock(); - ipmi_free_smi_msg(smi_msg); - ipmi_free_recv_msg(recv_msg); +out: return rv; } -static int check_addr(ipmi_smi_t intf, +static int check_addr(struct ipmi_smi *intf, struct ipmi_addr *addr, unsigned char *saddr, unsigned char *lun) @@ -2046,7 +2189,7 @@ static int check_addr(ipmi_smi_t intf, return 0; } -int ipmi_request_settime(ipmi_user_t user, +int ipmi_request_settime(struct ipmi_user *user, struct ipmi_addr *addr, long msgid, struct kernel_ipmi_msg *msg, @@ -2056,29 +2199,36 @@ int ipmi_request_settime(ipmi_user_t user, unsigned int retry_time_ms) { unsigned char saddr = 0, lun = 0; - int rv; + int rv, index; if (!user) return -EINVAL; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + rv = check_addr(user->intf, addr, &saddr, &lun); - if (rv) - return rv; - return i_ipmi_request(user, - user->intf, - addr, - msgid, - msg, - user_msg_data, - NULL, NULL, - priority, - saddr, - lun, - retries, - retry_time_ms); + if (!rv) + rv = i_ipmi_request(user, + user->intf, + addr, + msgid, + msg, + user_msg_data, + NULL, NULL, + priority, + saddr, + lun, + retries, + retry_time_ms); + + release_ipmi_user(user, index); + return rv; } EXPORT_SYMBOL(ipmi_request_settime); -int ipmi_request_supply_msgs(ipmi_user_t user, +int ipmi_request_supply_msgs(struct ipmi_user *user, struct ipmi_addr *addr, long msgid, struct kernel_ipmi_msg *msg, @@ -2088,29 +2238,37 @@ int ipmi_request_supply_msgs(ipmi_user_t user, int priority) { unsigned char saddr = 0, lun = 0; - int rv; + int rv, index; if (!user) return -EINVAL; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + rv = check_addr(user->intf, addr, &saddr, &lun); - if (rv) - return rv; - return i_ipmi_request(user, - user->intf, - addr, - msgid, - msg, - user_msg_data, - supplied_smi, - supplied_recv, - priority, - saddr, - lun, - -1, 0); + if (!rv) + rv = i_ipmi_request(user, + user->intf, + addr, + msgid, + msg, + user_msg_data, + supplied_smi, + supplied_recv, + priority, + saddr, + lun, + -1, 0); + + release_ipmi_user(user, index); + return rv; } EXPORT_SYMBOL(ipmi_request_supply_msgs); -static void bmc_device_id_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +static void bmc_device_id_handler(struct ipmi_smi *intf, + struct ipmi_recv_msg *msg) { int rv; @@ -2142,7 +2300,7 @@ static void bmc_device_id_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) } static int -send_get_device_id_cmd(ipmi_smi_t intf) +send_get_device_id_cmd(struct ipmi_smi *intf) { struct ipmi_system_interface_addr si; struct kernel_ipmi_msg msg; @@ -2170,7 +2328,7 @@ send_get_device_id_cmd(ipmi_smi_t intf) -1, 0); } -static int __get_device_id(ipmi_smi_t intf, struct bmc_device *bmc) +static int __get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc) { int rv; @@ -2204,7 +2362,7 @@ static int __get_device_id(ipmi_smi_t intf, struct bmc_device *bmc) * Except for the first time this is called (in ipmi_register_smi()), * this will always return good data; */ -static int __bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, +static int __bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, struct ipmi_device_id *id, bool *guid_set, guid_t *guid, int intf_num) { @@ -2337,223 +2495,13 @@ out_noprocessing: return rv; } -static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, +static int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, struct ipmi_device_id *id, bool *guid_set, guid_t *guid) { return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1); } -#ifdef CONFIG_IPMI_PROC_INTERFACE -static int smi_ipmb_proc_show(struct seq_file *m, void *v) -{ - ipmi_smi_t intf = m->private; - int i; - - seq_printf(m, "%x", intf->addrinfo[0].address); - for (i = 1; i < IPMI_MAX_CHANNELS; i++) - seq_printf(m, " %x", intf->addrinfo[i].address); - seq_putc(m, '\n'); - - return 0; -} - -static int smi_ipmb_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, smi_ipmb_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations smi_ipmb_proc_ops = { - .open = smi_ipmb_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int smi_version_proc_show(struct seq_file *m, void *v) -{ - ipmi_smi_t intf = m->private; - struct ipmi_device_id id; - int rv; - - rv = bmc_get_device_id(intf, NULL, &id, NULL, NULL); - if (rv) - return rv; - - seq_printf(m, "%u.%u\n", - ipmi_version_major(&id), - ipmi_version_minor(&id)); - - return 0; -} - -static int smi_version_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, smi_version_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations smi_version_proc_ops = { - .open = smi_version_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int smi_stats_proc_show(struct seq_file *m, void *v) -{ - ipmi_smi_t intf = m->private; - - seq_printf(m, "sent_invalid_commands: %u\n", - ipmi_get_stat(intf, sent_invalid_commands)); - seq_printf(m, "sent_local_commands: %u\n", - ipmi_get_stat(intf, sent_local_commands)); - seq_printf(m, "handled_local_responses: %u\n", - ipmi_get_stat(intf, handled_local_responses)); - seq_printf(m, "unhandled_local_responses: %u\n", - ipmi_get_stat(intf, unhandled_local_responses)); - seq_printf(m, "sent_ipmb_commands: %u\n", - ipmi_get_stat(intf, sent_ipmb_commands)); - seq_printf(m, "sent_ipmb_command_errs: %u\n", - ipmi_get_stat(intf, sent_ipmb_command_errs)); - seq_printf(m, "retransmitted_ipmb_commands: %u\n", - ipmi_get_stat(intf, retransmitted_ipmb_commands)); - seq_printf(m, "timed_out_ipmb_commands: %u\n", - ipmi_get_stat(intf, timed_out_ipmb_commands)); - seq_printf(m, "timed_out_ipmb_broadcasts: %u\n", - ipmi_get_stat(intf, timed_out_ipmb_broadcasts)); - seq_printf(m, "sent_ipmb_responses: %u\n", - ipmi_get_stat(intf, sent_ipmb_responses)); - seq_printf(m, "handled_ipmb_responses: %u\n", - ipmi_get_stat(intf, handled_ipmb_responses)); - seq_printf(m, "invalid_ipmb_responses: %u\n", - ipmi_get_stat(intf, invalid_ipmb_responses)); - seq_printf(m, "unhandled_ipmb_responses: %u\n", - ipmi_get_stat(intf, unhandled_ipmb_responses)); - seq_printf(m, "sent_lan_commands: %u\n", - ipmi_get_stat(intf, sent_lan_commands)); - seq_printf(m, "sent_lan_command_errs: %u\n", - ipmi_get_stat(intf, sent_lan_command_errs)); - seq_printf(m, "retransmitted_lan_commands: %u\n", - ipmi_get_stat(intf, retransmitted_lan_commands)); - seq_printf(m, "timed_out_lan_commands: %u\n", - ipmi_get_stat(intf, timed_out_lan_commands)); - seq_printf(m, "sent_lan_responses: %u\n", - ipmi_get_stat(intf, sent_lan_responses)); - seq_printf(m, "handled_lan_responses: %u\n", - ipmi_get_stat(intf, handled_lan_responses)); - seq_printf(m, "invalid_lan_responses: %u\n", - ipmi_get_stat(intf, invalid_lan_responses)); - seq_printf(m, "unhandled_lan_responses: %u\n", - ipmi_get_stat(intf, unhandled_lan_responses)); - seq_printf(m, "handled_commands: %u\n", - ipmi_get_stat(intf, handled_commands)); - seq_printf(m, "invalid_commands: %u\n", - ipmi_get_stat(intf, invalid_commands)); - seq_printf(m, "unhandled_commands: %u\n", - ipmi_get_stat(intf, unhandled_commands)); - seq_printf(m, "invalid_events: %u\n", - ipmi_get_stat(intf, invalid_events)); - seq_printf(m, "events: %u\n", - ipmi_get_stat(intf, events)); - seq_printf(m, "failed rexmit LAN msgs: %u\n", - ipmi_get_stat(intf, dropped_rexmit_lan_commands)); - seq_printf(m, "failed rexmit IPMB msgs: %u\n", - ipmi_get_stat(intf, dropped_rexmit_ipmb_commands)); - return 0; -} - -static int smi_stats_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, smi_stats_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations smi_stats_proc_ops = { - .open = smi_stats_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, - const struct file_operations *proc_ops, - void *data) -{ - int rv = 0; - struct proc_dir_entry *file; - struct ipmi_proc_entry *entry; - - /* Create a list element. */ - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - entry->name = kstrdup(name, GFP_KERNEL); - if (!entry->name) { - kfree(entry); - return -ENOMEM; - } - - file = proc_create_data(name, 0, smi->proc_dir, proc_ops, data); - if (!file) { - kfree(entry->name); - kfree(entry); - rv = -ENOMEM; - } else { - mutex_lock(&smi->proc_entry_lock); - /* Stick it on the list. */ - entry->next = smi->proc_entries; - smi->proc_entries = entry; - mutex_unlock(&smi->proc_entry_lock); - } - - return rv; -} -EXPORT_SYMBOL(ipmi_smi_add_proc_entry); - -static int add_proc_entries(ipmi_smi_t smi, int num) -{ - int rv = 0; - - sprintf(smi->proc_dir_name, "%d", num); - smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); - if (!smi->proc_dir) - rv = -ENOMEM; - - if (rv == 0) - rv = ipmi_smi_add_proc_entry(smi, "stats", - &smi_stats_proc_ops, - smi); - - if (rv == 0) - rv = ipmi_smi_add_proc_entry(smi, "ipmb", - &smi_ipmb_proc_ops, - smi); - - if (rv == 0) - rv = ipmi_smi_add_proc_entry(smi, "version", - &smi_version_proc_ops, - smi); - - return rv; -} - -static void remove_proc_entries(ipmi_smi_t smi) -{ - struct ipmi_proc_entry *entry; - - mutex_lock(&smi->proc_entry_lock); - while (smi->proc_entries) { - entry = smi->proc_entries; - smi->proc_entries = entry->next; - - remove_proc_entry(entry->name, smi->proc_dir); - kfree(entry->name); - kfree(entry); - } - mutex_unlock(&smi->proc_entry_lock); - remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); -} -#endif /* CONFIG_IPMI_PROC_INTERFACE */ - static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2885,7 +2833,7 @@ cleanup_bmc_device(struct kref *ref) /* * Must be called with intf->bmc_reg_mutex held. */ -static void __ipmi_bmc_unregister(ipmi_smi_t intf) +static void __ipmi_bmc_unregister(struct ipmi_smi *intf) { struct bmc_device *bmc = intf->bmc; @@ -2905,7 +2853,7 @@ static void __ipmi_bmc_unregister(ipmi_smi_t intf) intf->bmc_registered = false; } -static void ipmi_bmc_unregister(ipmi_smi_t intf) +static void ipmi_bmc_unregister(struct ipmi_smi *intf) { mutex_lock(&intf->bmc_reg_mutex); __ipmi_bmc_unregister(intf); @@ -2915,7 +2863,7 @@ static void ipmi_bmc_unregister(ipmi_smi_t intf) /* * Must be called with intf->bmc_reg_mutex held. */ -static int __ipmi_bmc_register(ipmi_smi_t intf, +static int __ipmi_bmc_register(struct ipmi_smi *intf, struct ipmi_device_id *id, bool guid_set, guid_t *guid, int intf_num) { @@ -3077,7 +3025,7 @@ out_list_del: } static int -send_guid_cmd(ipmi_smi_t intf, int chan) +send_guid_cmd(struct ipmi_smi *intf, int chan) { struct kernel_ipmi_msg msg; struct ipmi_system_interface_addr si; @@ -3104,7 +3052,7 @@ send_guid_cmd(ipmi_smi_t intf, int chan) -1, 0); } -static void guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +static void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) { struct bmc_device *bmc = intf->bmc; @@ -3139,7 +3087,7 @@ static void guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) wake_up(&intf->waitq); } -static void __get_guid(ipmi_smi_t intf) +static void __get_guid(struct ipmi_smi *intf) { int rv; struct bmc_device *bmc = intf->bmc; @@ -3160,7 +3108,7 @@ static void __get_guid(ipmi_smi_t intf) } static int -send_channel_info_cmd(ipmi_smi_t intf, int chan) +send_channel_info_cmd(struct ipmi_smi *intf, int chan) { struct kernel_ipmi_msg msg; unsigned char data[1]; @@ -3190,7 +3138,7 @@ send_channel_info_cmd(ipmi_smi_t intf, int chan) } static void -channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +channel_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) { int rv = 0; int ch; @@ -3262,7 +3210,7 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) /* * Must be holding intf->bmc_reg_mutex to call this. */ -static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id) +static int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id) { int rv; @@ -3306,7 +3254,7 @@ static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id) return 0; } -static void ipmi_poll(ipmi_smi_t intf) +static void ipmi_poll(struct ipmi_smi *intf) { if (intf->handlers->poll) intf->handlers->poll(intf->send_info); @@ -3314,7 +3262,7 @@ static void ipmi_poll(ipmi_smi_t intf) handle_new_recv_msgs(intf); } -void ipmi_poll_interface(ipmi_user_t user) +void ipmi_poll_interface(struct ipmi_user *user) { ipmi_poll(user->intf); } @@ -3322,7 +3270,8 @@ EXPORT_SYMBOL(ipmi_poll_interface); static void redo_bmc_reg(struct work_struct *work) { - ipmi_smi_t intf = container_of(work, struct ipmi_smi, bmc_reg_work); + struct ipmi_smi *intf = container_of(work, struct ipmi_smi, + bmc_reg_work); if (!intf->in_shutdown) bmc_get_device_id(intf, NULL, NULL, NULL, NULL); @@ -3337,8 +3286,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, { int i, j; int rv; - ipmi_smi_t intf; - ipmi_smi_t tintf; + struct ipmi_smi *intf, *tintf; struct list_head *link; struct ipmi_device_id id; @@ -3362,6 +3310,13 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, if (!intf) return -ENOMEM; + rv = init_srcu_struct(&intf->users_srcu); + if (rv) { + kfree(intf); + return rv; + } + + intf->bmc = &intf->tmp_bmc; INIT_LIST_HEAD(&intf->bmc->intfs); mutex_init(&intf->bmc->dyn_mutex); @@ -3386,9 +3341,6 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, intf->seq_table[j].seqid = 0; } intf->curr_seq = 0; -#ifdef CONFIG_IPMI_PROC_INTERFACE - mutex_init(&intf->proc_entry_lock); -#endif spin_lock_init(&intf->waiting_rcv_msgs_lock); INIT_LIST_HEAD(&intf->waiting_rcv_msgs); tasklet_init(&intf->recv_tasklet, @@ -3410,11 +3362,6 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, for (i = 0; i < IPMI_NUM_STATS; i++) atomic_set(&intf->stats[i], 0); -#ifdef CONFIG_IPMI_PROC_INTERFACE - intf->proc_dir = NULL; -#endif - - mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); /* Look for a hole in the numbers. */ i = 0; @@ -3445,25 +3392,14 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, mutex_lock(&intf->bmc_reg_mutex); rv = __scan_channels(intf, &id); mutex_unlock(&intf->bmc_reg_mutex); - if (rv) - goto out; - -#ifdef CONFIG_IPMI_PROC_INTERFACE - rv = add_proc_entries(intf, i); -#endif out: if (rv) { ipmi_bmc_unregister(intf); -#ifdef CONFIG_IPMI_PROC_INTERFACE - if (intf->proc_dir) - remove_proc_entries(intf); -#endif - intf->handlers = NULL; list_del_rcu(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); - mutex_unlock(&smi_watchers_mutex); - synchronize_rcu(); + synchronize_srcu(&ipmi_interfaces_srcu); + cleanup_srcu_struct(&intf->users_srcu); kref_put(&intf->refcount, intf_free); } else { /* @@ -3474,16 +3410,16 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, smp_wmb(); intf->intf_num = i; mutex_unlock(&ipmi_interfaces_mutex); + /* After this point the interface is legal to use. */ call_smi_watchers(i, intf->si_dev); - mutex_unlock(&smi_watchers_mutex); } return rv; } EXPORT_SYMBOL(ipmi_register_smi); -static void deliver_smi_err_response(ipmi_smi_t intf, +static void deliver_smi_err_response(struct ipmi_smi *intf, struct ipmi_smi_msg *msg, unsigned char err) { @@ -3495,7 +3431,7 @@ static void deliver_smi_err_response(ipmi_smi_t intf, handle_one_recv_msg(intf, msg); } -static void cleanup_smi_msgs(ipmi_smi_t intf) +static void cleanup_smi_msgs(struct ipmi_smi *intf) { int i; struct seq_table *ent; @@ -3528,60 +3464,58 @@ static void cleanup_smi_msgs(ipmi_smi_t intf) } for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { - ent = &(intf->seq_table[i]); + ent = &intf->seq_table[i]; if (!ent->inuse) continue; - deliver_err_response(ent->recv_msg, IPMI_ERR_UNSPECIFIED); + deliver_err_response(intf, ent->recv_msg, IPMI_ERR_UNSPECIFIED); } } -int ipmi_unregister_smi(ipmi_smi_t intf) +void ipmi_unregister_smi(struct ipmi_smi *intf) { struct ipmi_smi_watcher *w; - int intf_num = intf->intf_num; - ipmi_user_t user; + int intf_num = intf->intf_num, index; - mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); intf->intf_num = -1; intf->in_shutdown = true; list_del_rcu(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); - synchronize_rcu(); + synchronize_srcu(&ipmi_interfaces_srcu); - cleanup_smi_msgs(intf); - - /* Clean up the effects of users on the lower-level software. */ - mutex_lock(&ipmi_interfaces_mutex); - rcu_read_lock(); - list_for_each_entry_rcu(user, &intf->users, link) { - module_put(intf->handlers->owner); - if (intf->handlers->dec_usecount) - intf->handlers->dec_usecount(intf->send_info); - } - rcu_read_unlock(); - intf->handlers = NULL; - mutex_unlock(&ipmi_interfaces_mutex); - -#ifdef CONFIG_IPMI_PROC_INTERFACE - remove_proc_entries(intf); -#endif - ipmi_bmc_unregister(intf); + /* At this point no users can be added to the interface. */ /* * Call all the watcher interfaces to tell them that - * an interface is gone. + * an interface is going away. */ + mutex_lock(&smi_watchers_mutex); list_for_each_entry(w, &smi_watchers, link) w->smi_gone(intf_num); mutex_unlock(&smi_watchers_mutex); + index = srcu_read_lock(&intf->users_srcu); + while (!list_empty(&intf->users)) { + struct ipmi_user *user = + container_of(list_next_rcu(&intf->users), + struct ipmi_user, link); + + _ipmi_destroy_user(user); + } + srcu_read_unlock(&intf->users_srcu, index); + + intf->handlers->shutdown(intf->send_info); + + cleanup_smi_msgs(intf); + + ipmi_bmc_unregister(intf); + + cleanup_srcu_struct(&intf->users_srcu); kref_put(&intf->refcount, intf_free); - return 0; } EXPORT_SYMBOL(ipmi_unregister_smi); -static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, +static int handle_ipmb_get_msg_rsp(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct ipmi_ipmb_addr ipmb_addr; @@ -3616,7 +3550,7 @@ static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, msg->rsp[3] & 0x0f, msg->rsp[8], (msg->rsp[4] >> 2) & (~1), - (struct ipmi_addr *) &(ipmb_addr), + (struct ipmi_addr *) &ipmb_addr, &recv_msg)) { /* * We were unable to find the sequence number, @@ -3626,9 +3560,7 @@ static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, return 0; } - memcpy(recv_msg->msg_data, - &(msg->rsp[9]), - msg->rsp_size - 9); + memcpy(recv_msg->msg_data, &msg->rsp[9], msg->rsp_size - 9); /* * The other fields matched, so no need to set them, except * for netfn, which needs to be the response that was @@ -3638,13 +3570,15 @@ static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 10; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - ipmi_inc_stat(intf, handled_ipmb_responses); - deliver_response(recv_msg); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_ipmb_responses); + else + ipmi_inc_stat(intf, handled_ipmb_responses); return 0; } -static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, +static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct cmd_rcvr *rcvr; @@ -3652,7 +3586,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, unsigned char netfn; unsigned char cmd; unsigned char chan; - ipmi_user_t user = NULL; + struct ipmi_user *user = NULL; struct ipmi_ipmb_addr *ipmb_addr; struct ipmi_recv_msg *recv_msg; @@ -3689,24 +3623,17 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, msg->data[2] = msg->rsp[3]; msg->data[3] = msg->rsp[6]; msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); - msg->data[5] = ipmb_checksum(&(msg->data[3]), 2); + msg->data[5] = ipmb_checksum(&msg->data[3], 2); msg->data[6] = intf->addrinfo[msg->rsp[3] & 0xf].address; /* rqseq/lun */ msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); msg->data[8] = msg->rsp[8]; /* cmd */ msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE; - msg->data[10] = ipmb_checksum(&(msg->data[6]), 4); + msg->data[10] = ipmb_checksum(&msg->data[6], 4); msg->data_size = 11; -#ifdef DEBUG_MSGING - { - int m; - printk("Invalid command:"); - for (m = 0; m < msg->data_size; m++) - printk(" %2.2x", msg->data[m]); - printk("\n"); - } -#endif + ipmi_debug_msg("Invalid command:", msg->data, msg->data_size); + rcu_read_lock(); if (!intf->in_shutdown) { smi_send(intf, intf->handlers, msg, 0); @@ -3719,9 +3646,6 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, } rcu_read_unlock(); } else { - /* Deliver the message to the user. */ - ipmi_inc_stat(intf, handled_commands); - recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { /* @@ -3755,17 +3679,19 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, * at the end also needs to be removed. */ recv_msg->msg.data_len = msg->rsp_size - 10; - memcpy(recv_msg->msg_data, - &(msg->rsp[9]), + memcpy(recv_msg->msg_data, &msg->rsp[9], msg->rsp_size - 10); - deliver_response(recv_msg); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_commands); + else + ipmi_inc_stat(intf, handled_commands); } } return rv; } -static int handle_lan_get_msg_rsp(ipmi_smi_t intf, +static int handle_lan_get_msg_rsp(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct ipmi_lan_addr lan_addr; @@ -3804,7 +3730,7 @@ static int handle_lan_get_msg_rsp(ipmi_smi_t intf, msg->rsp[3] & 0x0f, msg->rsp[10], (msg->rsp[6] >> 2) & (~1), - (struct ipmi_addr *) &(lan_addr), + (struct ipmi_addr *) &lan_addr, &recv_msg)) { /* * We were unable to find the sequence number, @@ -3814,9 +3740,7 @@ static int handle_lan_get_msg_rsp(ipmi_smi_t intf, return 0; } - memcpy(recv_msg->msg_data, - &(msg->rsp[11]), - msg->rsp_size - 11); + memcpy(recv_msg->msg_data, &msg->rsp[11], msg->rsp_size - 11); /* * The other fields matched, so no need to set them, except * for netfn, which needs to be the response that was @@ -3826,13 +3750,15 @@ static int handle_lan_get_msg_rsp(ipmi_smi_t intf, recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 12; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - ipmi_inc_stat(intf, handled_lan_responses); - deliver_response(recv_msg); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_lan_responses); + else + ipmi_inc_stat(intf, handled_lan_responses); return 0; } -static int handle_lan_get_msg_cmd(ipmi_smi_t intf, +static int handle_lan_get_msg_cmd(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct cmd_rcvr *rcvr; @@ -3840,7 +3766,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, unsigned char netfn; unsigned char cmd; unsigned char chan; - ipmi_user_t user = NULL; + struct ipmi_user *user = NULL; struct ipmi_lan_addr *lan_addr; struct ipmi_recv_msg *recv_msg; @@ -3878,9 +3804,6 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, */ rv = 0; } else { - /* Deliver the message to the user. */ - ipmi_inc_stat(intf, handled_commands); - recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { /* @@ -3916,10 +3839,12 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, * at the end also needs to be removed. */ recv_msg->msg.data_len = msg->rsp_size - 12; - memcpy(recv_msg->msg_data, - &(msg->rsp[11]), + memcpy(recv_msg->msg_data, &msg->rsp[11], msg->rsp_size - 12); - deliver_response(recv_msg); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_commands); + else + ipmi_inc_stat(intf, handled_commands); } } @@ -3932,7 +3857,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, * the OEM. See IPMI 2.0 specification, Chapter 6 and * Chapter 22, sections 22.6 and 22.24 for more details. */ -static int handle_oem_get_msg_cmd(ipmi_smi_t intf, +static int handle_oem_get_msg_cmd(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct cmd_rcvr *rcvr; @@ -3940,7 +3865,7 @@ static int handle_oem_get_msg_cmd(ipmi_smi_t intf, unsigned char netfn; unsigned char cmd; unsigned char chan; - ipmi_user_t user = NULL; + struct ipmi_user *user = NULL; struct ipmi_system_interface_addr *smi_addr; struct ipmi_recv_msg *recv_msg; @@ -3987,9 +3912,6 @@ static int handle_oem_get_msg_cmd(ipmi_smi_t intf, rv = 0; } else { - /* Deliver the message to the user. */ - ipmi_inc_stat(intf, handled_commands); - recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { /* @@ -4007,7 +3929,7 @@ static int handle_oem_get_msg_cmd(ipmi_smi_t intf, * requirements */ smi_addr = ((struct ipmi_system_interface_addr *) - &(recv_msg->addr)); + &recv_msg->addr); smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; smi_addr->channel = IPMI_BMC_CHANNEL; smi_addr->lun = msg->rsp[0] & 3; @@ -4024,10 +3946,12 @@ static int handle_oem_get_msg_cmd(ipmi_smi_t intf, * the Channel Byte in the "GET MESSAGE" command */ recv_msg->msg.data_len = msg->rsp_size - 4; - memcpy(recv_msg->msg_data, - &(msg->rsp[4]), + memcpy(recv_msg->msg_data, &msg->rsp[4], msg->rsp_size - 4); - deliver_response(recv_msg); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_commands); + else + ipmi_inc_stat(intf, handled_commands); } } @@ -4040,26 +3964,25 @@ static void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg, struct ipmi_system_interface_addr *smi_addr; recv_msg->msgid = 0; - smi_addr = (struct ipmi_system_interface_addr *) &(recv_msg->addr); + smi_addr = (struct ipmi_system_interface_addr *) &recv_msg->addr; smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; smi_addr->channel = IPMI_BMC_CHANNEL; smi_addr->lun = msg->rsp[0] & 3; recv_msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE; recv_msg->msg.netfn = msg->rsp[0] >> 2; recv_msg->msg.cmd = msg->rsp[1]; - memcpy(recv_msg->msg_data, &(msg->rsp[3]), msg->rsp_size - 3); + memcpy(recv_msg->msg_data, &msg->rsp[3], msg->rsp_size - 3); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 3; } -static int handle_read_event_rsp(ipmi_smi_t intf, +static int handle_read_event_rsp(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct ipmi_recv_msg *recv_msg, *recv_msg2; struct list_head msgs; - ipmi_user_t user; - int rv = 0; - int deliver_count = 0; + struct ipmi_user *user; + int rv = 0, deliver_count = 0, index; unsigned long flags; if (msg->rsp_size < 19) { @@ -4083,7 +4006,7 @@ static int handle_read_event_rsp(ipmi_smi_t intf, * Allocate and fill in one message for every user that is * getting events. */ - rcu_read_lock(); + index = srcu_read_lock(&intf->users_srcu); list_for_each_entry_rcu(user, &intf->users, link) { if (!user->gets_events) continue; @@ -4110,15 +4033,15 @@ static int handle_read_event_rsp(ipmi_smi_t intf, copy_event_into_recv_msg(recv_msg, msg); recv_msg->user = user; kref_get(&user->refcount); - list_add_tail(&(recv_msg->link), &msgs); + list_add_tail(&recv_msg->link, &msgs); } - rcu_read_unlock(); + srcu_read_unlock(&intf->users_srcu, index); if (deliver_count) { /* Now deliver all the messages. */ list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { list_del(&recv_msg->link); - deliver_response(recv_msg); + deliver_local_response(intf, recv_msg); } } else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) { /* @@ -4137,7 +4060,7 @@ static int handle_read_event_rsp(ipmi_smi_t intf, } copy_event_into_recv_msg(recv_msg, msg); - list_add_tail(&(recv_msg->link), &(intf->waiting_events)); + list_add_tail(&recv_msg->link, &intf->waiting_events); intf->waiting_events_count++; } else if (!intf->event_msg_printed) { /* @@ -4150,16 +4073,16 @@ static int handle_read_event_rsp(ipmi_smi_t intf, } out: - spin_unlock_irqrestore(&(intf->events_lock), flags); + spin_unlock_irqrestore(&intf->events_lock, flags); return rv; } -static int handle_bmc_rsp(ipmi_smi_t intf, +static int handle_bmc_rsp(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct ipmi_recv_msg *recv_msg; - struct ipmi_user *user; + struct ipmi_system_interface_addr *smi_addr; recv_msg = (struct ipmi_recv_msg *) msg->user_data; if (recv_msg == NULL) { @@ -4168,32 +4091,19 @@ static int handle_bmc_rsp(ipmi_smi_t intf, return 0; } - user = recv_msg->user; - /* Make sure the user still exists. */ - if (user && !user->valid) { - /* The user for the message went away, so give up. */ - ipmi_inc_stat(intf, unhandled_local_responses); - ipmi_free_recv_msg(recv_msg); - } else { - struct ipmi_system_interface_addr *smi_addr; - - ipmi_inc_stat(intf, handled_local_responses); - recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - recv_msg->msgid = msg->msgid; - smi_addr = ((struct ipmi_system_interface_addr *) - &(recv_msg->addr)); - smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - smi_addr->channel = IPMI_BMC_CHANNEL; - smi_addr->lun = msg->rsp[0] & 3; - recv_msg->msg.netfn = msg->rsp[0] >> 2; - recv_msg->msg.cmd = msg->rsp[1]; - memcpy(recv_msg->msg_data, - &(msg->rsp[2]), - msg->rsp_size - 2); - recv_msg->msg.data = recv_msg->msg_data; - recv_msg->msg.data_len = msg->rsp_size - 2; - deliver_response(recv_msg); - } + recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; + recv_msg->msgid = msg->msgid; + smi_addr = ((struct ipmi_system_interface_addr *) + &recv_msg->addr); + smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr->channel = IPMI_BMC_CHANNEL; + smi_addr->lun = msg->rsp[0] & 3; + recv_msg->msg.netfn = msg->rsp[0] >> 2; + recv_msg->msg.cmd = msg->rsp[1]; + memcpy(recv_msg->msg_data, &msg->rsp[2], msg->rsp_size - 2); + recv_msg->msg.data = recv_msg->msg_data; + recv_msg->msg.data_len = msg->rsp_size - 2; + deliver_local_response(intf, recv_msg); return 0; } @@ -4203,19 +4113,13 @@ static int handle_bmc_rsp(ipmi_smi_t intf, * 0 if the message should be freed, or -1 if the message should not * be freed or requeued. */ -static int handle_one_recv_msg(ipmi_smi_t intf, +static int handle_one_recv_msg(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { int requeue; int chan; -#ifdef DEBUG_MSGING - int m; - printk("Recv:"); - for (m = 0; m < msg->rsp_size; m++) - printk(" %2.2x", msg->rsp[m]); - printk("\n"); -#endif + ipmi_debug_msg("Recv:", msg->rsp, msg->rsp_size); if (msg->rsp_size < 2) { /* Message is too small to be correct. */ dev_warn(intf->si_dev, @@ -4252,7 +4156,7 @@ static int handle_one_recv_msg(ipmi_smi_t intf, * It's a response to a response we sent. For this we * deliver a send message response to the user. */ - struct ipmi_recv_msg *recv_msg = msg->user_data; + struct ipmi_recv_msg *recv_msg = msg->user_data; requeue = 0; if (msg->rsp_size < 2) @@ -4267,15 +4171,11 @@ static int handle_one_recv_msg(ipmi_smi_t intf, if (!recv_msg) goto out; - /* Make sure the user still exists. */ - if (!recv_msg->user || !recv_msg->user->valid) - goto out; - recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = 1; recv_msg->msg_data[0] = msg->rsp[2]; - deliver_response(recv_msg); + deliver_local_response(intf, recv_msg); } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { struct ipmi_channel *chans; @@ -4367,7 +4267,7 @@ static int handle_one_recv_msg(ipmi_smi_t intf, /* * If there are messages in the queue or pretimeouts, handle them. */ -static void handle_new_recv_msgs(ipmi_smi_t intf) +static void handle_new_recv_msgs(struct ipmi_smi *intf) { struct ipmi_smi_msg *smi_msg; unsigned long flags = 0; @@ -4412,22 +4312,23 @@ static void handle_new_recv_msgs(ipmi_smi_t intf) * deliver pretimeouts to all the users. */ if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { - ipmi_user_t user; + struct ipmi_user *user; + int index; - rcu_read_lock(); + index = srcu_read_lock(&intf->users_srcu); list_for_each_entry_rcu(user, &intf->users, link) { if (user->handler->ipmi_watchdog_pretimeout) user->handler->ipmi_watchdog_pretimeout( user->handler_data); } - rcu_read_unlock(); + srcu_read_unlock(&intf->users_srcu, index); } } static void smi_recv_tasklet(unsigned long val) { unsigned long flags = 0; /* keep us warning-free. */ - ipmi_smi_t intf = (ipmi_smi_t) val; + struct ipmi_smi *intf = (struct ipmi_smi *) val; int run_to_completion = intf->run_to_completion; struct ipmi_smi_msg *newmsg = NULL; @@ -4469,7 +4370,7 @@ static void smi_recv_tasklet(unsigned long val) } /* Handle a new message from the lower layer. */ -void ipmi_smi_msg_received(ipmi_smi_t intf, +void ipmi_smi_msg_received(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { unsigned long flags = 0; /* keep us warning-free. */ @@ -4550,7 +4451,7 @@ free_msg: } EXPORT_SYMBOL(ipmi_smi_msg_received); -void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) +void ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf) { if (intf->in_shutdown) return; @@ -4561,7 +4462,7 @@ void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); static struct ipmi_smi_msg * -smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, +smi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg, unsigned char seq, long seqid) { struct ipmi_smi_msg *smi_msg = ipmi_alloc_smi_msg(); @@ -4576,26 +4477,18 @@ smi_from_recv_msg(ipmi_smi_t 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); -#ifdef DEBUG_MSGING - { - int m; - printk("Resend: "); - for (m = 0; m < smi_msg->data_size; m++) - printk(" %2.2x", smi_msg->data[m]); - printk("\n"); - } -#endif + ipmi_debug_msg("Resend: ", smi_msg->data, smi_msg->data_size); + return smi_msg; } -static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, +static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, struct list_head *timeouts, unsigned long timeout_period, int slot, unsigned long *flags, unsigned int *waiting_msgs) { - struct ipmi_recv_msg *msg; - const struct ipmi_smi_handlers *handlers; + struct ipmi_recv_msg *msg; if (intf->in_shutdown) return; @@ -4653,8 +4546,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, * only for messages to the local MC, which don't get * resent. */ - handlers = intf->handlers; - if (handlers) { + if (intf->handlers) { if (is_lan_addr(&ent->recv_msg->addr)) ipmi_inc_stat(intf, retransmitted_lan_commands); @@ -4662,7 +4554,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, ipmi_inc_stat(intf, retransmitted_ipmb_commands); - smi_send(intf, handlers, smi_msg, 0); + smi_send(intf, intf->handlers, smi_msg, 0); } else ipmi_free_smi_msg(smi_msg); @@ -4670,7 +4562,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, } } -static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, +static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, unsigned long timeout_period) { struct list_head timeouts; @@ -4694,14 +4586,20 @@ static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, */ INIT_LIST_HEAD(&timeouts); spin_lock_irqsave(&intf->seq_lock, flags); + if (intf->ipmb_maintenance_mode_timeout) { + if (intf->ipmb_maintenance_mode_timeout <= timeout_period) + intf->ipmb_maintenance_mode_timeout = 0; + else + intf->ipmb_maintenance_mode_timeout -= timeout_period; + } for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) - check_msg_timeout(intf, &(intf->seq_table[i]), + check_msg_timeout(intf, &intf->seq_table[i], &timeouts, timeout_period, i, &flags, &waiting_msgs); spin_unlock_irqrestore(&intf->seq_lock, flags); list_for_each_entry_safe(msg, msg2, &timeouts, link) - deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE); + deliver_err_response(intf, msg, IPMI_TIMEOUT_COMPLETION_CODE); /* * Maintenance mode handling. Check the timeout @@ -4731,7 +4629,7 @@ static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, return waiting_msgs; } -static void ipmi_request_event(ipmi_smi_t intf) +static void ipmi_request_event(struct ipmi_smi *intf) { /* No event requests when in maintenance mode. */ if (intf->maintenance_mode_enable) @@ -4747,13 +4645,13 @@ static atomic_t stop_operation; static void ipmi_timeout(struct timer_list *unused) { - ipmi_smi_t intf; - int nt = 0; + struct ipmi_smi *intf; + int nt = 0, index; if (atomic_read(&stop_operation)) return; - rcu_read_lock(); + index = srcu_read_lock(&ipmi_interfaces_srcu); list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { int lnt = 0; @@ -4776,13 +4674,13 @@ static void ipmi_timeout(struct timer_list *unused) nt += lnt; } - rcu_read_unlock(); + srcu_read_unlock(&ipmi_interfaces_srcu, index); if (nt) mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } -static void need_waiter(ipmi_smi_t intf) +static void need_waiter(struct ipmi_smi *intf) { /* Racy, but worst case we start the timer twice. */ if (!timer_pending(&ipmi_timer)) @@ -4853,8 +4751,8 @@ static void dummy_recv_done_handler(struct ipmi_recv_msg *msg) /* * Inside a panic, send a message and wait for a response. */ -static void ipmi_panic_request_and_wait(ipmi_smi_t intf, - struct ipmi_addr *addr, +static void ipmi_panic_request_and_wait(struct ipmi_smi *intf, + struct ipmi_addr *addr, struct kernel_ipmi_msg *msg) { struct ipmi_smi_msg smi_msg; @@ -4885,7 +4783,8 @@ static void ipmi_panic_request_and_wait(ipmi_smi_t intf, ipmi_poll(intf); } -static void event_receiver_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +static void event_receiver_fetcher(struct ipmi_smi *intf, + struct ipmi_recv_msg *msg) { if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_SENSOR_EVENT_RESPONSE) @@ -4897,7 +4796,7 @@ static void event_receiver_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) } } -static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +static void device_id_fetcher(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) { if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) @@ -4912,13 +4811,15 @@ static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) } } -static void send_panic_events(char *str) +static void send_panic_events(struct ipmi_smi *intf, char *str) { - struct kernel_ipmi_msg msg; - ipmi_smi_t intf; - unsigned char data[16]; + struct kernel_ipmi_msg msg; + unsigned char data[16]; struct ipmi_system_interface_addr *si; - struct ipmi_addr addr; + struct ipmi_addr addr; + char *p = str; + struct ipmi_ipmb_addr *ipmb; + int j; if (ipmi_send_panic_event == IPMI_SEND_PANIC_EVENT_NONE) return; @@ -4949,15 +4850,8 @@ static void send_panic_events(char *str) data[7] = str[2]; } - /* For every registered interface, send the event. */ - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - if (!intf->handlers || !intf->handlers->poll) - /* Interface is not ready or can't run at panic time. */ - continue; - - /* Send the event announcing the panic. */ - ipmi_panic_request_and_wait(intf, &addr, &msg); - } + /* Send the event announcing the panic. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); /* * On every interface, dump a bunch of OEM event holding the @@ -4966,111 +4860,100 @@ static void send_panic_events(char *str) if (ipmi_send_panic_event != IPMI_SEND_PANIC_EVENT_STRING || !str) return; - /* For every registered interface, send the event. */ - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - char *p = str; - struct ipmi_ipmb_addr *ipmb; - int j; - - if (intf->intf_num == -1) - /* Interface was not ready yet. */ - continue; + /* + * intf_num is used as an marker to tell if the + * interface is valid. Thus we need a read barrier to + * make sure data fetched before checking intf_num + * won't be used. + */ + smp_rmb(); - /* - * intf_num is used as an marker to tell if the - * interface is valid. Thus we need a read barrier to - * make sure data fetched before checking intf_num - * won't be used. - */ - smp_rmb(); + /* + * First job here is to figure out where to send the + * OEM events. There's no way in IPMI to send OEM + * events using an event send command, so we have to + * find the SEL to put them in and stick them in + * there. + */ - /* - * First job here is to figure out where to send the - * OEM events. There's no way in IPMI to send OEM - * events using an event send command, so we have to - * find the SEL to put them in and stick them in - * there. - */ + /* Get capabilities from the get device id. */ + intf->local_sel_device = 0; + intf->local_event_generator = 0; + intf->event_receiver = 0; - /* Get capabilities from the get device id. */ - intf->local_sel_device = 0; - intf->local_event_generator = 0; - intf->event_receiver = 0; + /* Request the device info from the local MC. */ + msg.netfn = IPMI_NETFN_APP_REQUEST; + msg.cmd = IPMI_GET_DEVICE_ID_CMD; + msg.data = NULL; + msg.data_len = 0; + intf->null_user_handler = device_id_fetcher; + ipmi_panic_request_and_wait(intf, &addr, &msg); - /* Request the device info from the local MC. */ - msg.netfn = IPMI_NETFN_APP_REQUEST; - msg.cmd = IPMI_GET_DEVICE_ID_CMD; + if (intf->local_event_generator) { + /* Request the event receiver from the local MC. */ + msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST; + msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD; msg.data = NULL; msg.data_len = 0; - intf->null_user_handler = device_id_fetcher; + intf->null_user_handler = event_receiver_fetcher; ipmi_panic_request_and_wait(intf, &addr, &msg); + } + intf->null_user_handler = NULL; - if (intf->local_event_generator) { - /* Request the event receiver from the local MC. */ - msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST; - msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD; - msg.data = NULL; - msg.data_len = 0; - intf->null_user_handler = event_receiver_fetcher; - ipmi_panic_request_and_wait(intf, &addr, &msg); - } - intf->null_user_handler = NULL; + /* + * Validate the event receiver. The low bit must not + * be 1 (it must be a valid IPMB address), it cannot + * be zero, and it must not be my address. + */ + if (((intf->event_receiver & 1) == 0) + && (intf->event_receiver != 0) + && (intf->event_receiver != intf->addrinfo[0].address)) { + /* + * The event receiver is valid, send an IPMB + * message. + */ + ipmb = (struct ipmi_ipmb_addr *) &addr; + ipmb->addr_type = IPMI_IPMB_ADDR_TYPE; + ipmb->channel = 0; /* FIXME - is this right? */ + ipmb->lun = intf->event_receiver_lun; + ipmb->slave_addr = intf->event_receiver; + } else if (intf->local_sel_device) { + /* + * The event receiver was not valid (or was + * me), but I am an SEL device, just dump it + * in my SEL. + */ + si = (struct ipmi_system_interface_addr *) &addr; + si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + si->channel = IPMI_BMC_CHANNEL; + si->lun = 0; + } else + return; /* No where to send the event. */ + msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */ + msg.cmd = IPMI_ADD_SEL_ENTRY_CMD; + msg.data = data; + msg.data_len = 16; + + j = 0; + while (*p) { + int size = strlen(p); + + if (size > 11) + size = 11; + data[0] = 0; + data[1] = 0; + data[2] = 0xf0; /* OEM event without timestamp. */ + data[3] = intf->addrinfo[0].address; + data[4] = j++; /* sequence # */ /* - * Validate the event receiver. The low bit must not - * be 1 (it must be a valid IPMB address), it cannot - * be zero, and it must not be my address. + * Always give 11 bytes, so strncpy will fill + * it with zeroes for me. */ - if (((intf->event_receiver & 1) == 0) - && (intf->event_receiver != 0) - && (intf->event_receiver != intf->addrinfo[0].address)) { - /* - * The event receiver is valid, send an IPMB - * message. - */ - ipmb = (struct ipmi_ipmb_addr *) &addr; - ipmb->addr_type = IPMI_IPMB_ADDR_TYPE; - ipmb->channel = 0; /* FIXME - is this right? */ - ipmb->lun = intf->event_receiver_lun; - ipmb->slave_addr = intf->event_receiver; - } else if (intf->local_sel_device) { - /* - * The event receiver was not valid (or was - * me), but I am an SEL device, just dump it - * in my SEL. - */ - si = (struct ipmi_system_interface_addr *) &addr; - si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - si->channel = IPMI_BMC_CHANNEL; - si->lun = 0; - } else - continue; /* No where to send the event. */ - - msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */ - msg.cmd = IPMI_ADD_SEL_ENTRY_CMD; - msg.data = data; - msg.data_len = 16; - - j = 0; - while (*p) { - int size = strlen(p); - - if (size > 11) - size = 11; - data[0] = 0; - data[1] = 0; - data[2] = 0xf0; /* OEM event without timestamp. */ - data[3] = intf->addrinfo[0].address; - data[4] = j++; /* sequence # */ - /* - * Always give 11 bytes, so strncpy will fill - * it with zeroes for me. - */ - strncpy(data+5, p, 11); - p += size; + strncpy(data+5, p, 11); + p += size; - ipmi_panic_request_and_wait(intf, &addr, &msg); - } + ipmi_panic_request_and_wait(intf, &addr, &msg); } } @@ -5080,7 +4963,8 @@ static int panic_event(struct notifier_block *this, unsigned long event, void *ptr) { - ipmi_smi_t intf; + struct ipmi_smi *intf; + struct ipmi_user *user; if (has_panicked) return NOTIFY_DONE; @@ -5088,10 +4972,13 @@ static int panic_event(struct notifier_block *this, /* For every registered interface, set it to run to completion. */ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - if (!intf->handlers) + if (!intf->handlers || intf->intf_num == -1) /* Interface is not ready. */ continue; + if (!intf->handlers->poll) + continue; + /* * If we were interrupted while locking xmit_msgs_lock or * waiting_rcv_msgs_lock, the corresponding list may be @@ -5113,9 +5000,15 @@ static int panic_event(struct notifier_block *this, if (intf->handlers->set_run_to_completion) intf->handlers->set_run_to_completion(intf->send_info, 1); - } - send_panic_events(ptr); + list_for_each_entry_rcu(user, &intf->users, link) { + if (user->handler->ipmi_panic_handler) + user->handler->ipmi_panic_handler( + user->handler_data); + } + + send_panic_events(intf, ptr); + } return NOTIFY_DONE; } @@ -5141,16 +5034,6 @@ static int ipmi_init_msghandler(void) pr_info("ipmi message handler version " IPMI_DRIVER_VERSION "\n"); -#ifdef CONFIG_IPMI_PROC_INTERFACE - proc_ipmi_root = proc_mkdir("ipmi", NULL); - if (!proc_ipmi_root) { - pr_err(PFX "Unable to create IPMI proc dir"); - driver_unregister(&ipmidriver.driver); - return -ENOMEM; - } - -#endif /* CONFIG_IPMI_PROC_INTERFACE */ - timer_setup(&ipmi_timer, ipmi_timeout, 0); mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); @@ -5189,10 +5072,6 @@ static void __exit cleanup_ipmi(void) atomic_inc(&stop_operation); del_timer_sync(&ipmi_timer); -#ifdef CONFIG_IPMI_PROC_INTERFACE - proc_remove(proc_ipmi_root); -#endif /* CONFIG_IPMI_PROC_INTERFACE */ - driver_unregister(&ipmidriver.driver); initialized = 0; diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 7996337852f2..f6e19410dc57 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -39,9 +39,9 @@ static int ifnum_to_use = -1; /* Our local state. */ static int ready; -static ipmi_user_t ipmi_user; +static struct ipmi_user *ipmi_user; static int ipmi_ifnum; -static void (*specific_poweroff_func)(ipmi_user_t user); +static void (*specific_poweroff_func)(struct ipmi_user *user); /* Holds the old poweroff function so we can restore it on removal. */ static void (*old_poweroff_func)(void); @@ -118,7 +118,7 @@ static const struct ipmi_user_hndl ipmi_poweroff_handler = { }; -static int ipmi_request_wait_for_response(ipmi_user_t user, +static int ipmi_request_wait_for_response(struct ipmi_user *user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { @@ -138,7 +138,7 @@ static int ipmi_request_wait_for_response(ipmi_user_t user, } /* Wait for message to complete, spinning. */ -static int ipmi_request_in_rc_mode(ipmi_user_t user, +static int ipmi_request_in_rc_mode(struct ipmi_user *user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { @@ -178,9 +178,9 @@ static int ipmi_request_in_rc_mode(ipmi_user_t user, #define IPMI_MOTOROLA_MANUFACTURER_ID 0x0000A1 #define IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID 0x0051 -static void (*atca_oem_poweroff_hook)(ipmi_user_t user); +static void (*atca_oem_poweroff_hook)(struct ipmi_user *user); -static void pps_poweroff_atca(ipmi_user_t user) +static void pps_poweroff_atca(struct ipmi_user *user) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; @@ -208,7 +208,7 @@ static void pps_poweroff_atca(ipmi_user_t user) return; } -static int ipmi_atca_detect(ipmi_user_t user) +static int ipmi_atca_detect(struct ipmi_user *user) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; @@ -245,7 +245,7 @@ static int ipmi_atca_detect(ipmi_user_t user) return !rv; } -static void ipmi_poweroff_atca(ipmi_user_t user) +static void ipmi_poweroff_atca(struct ipmi_user *user) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; @@ -309,13 +309,13 @@ static void ipmi_poweroff_atca(ipmi_user_t user) #define IPMI_CPI1_PRODUCT_ID 0x000157 #define IPMI_CPI1_MANUFACTURER_ID 0x0108 -static int ipmi_cpi1_detect(ipmi_user_t user) +static int ipmi_cpi1_detect(struct ipmi_user *user) { return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID) && (prod_id == IPMI_CPI1_PRODUCT_ID)); } -static void ipmi_poweroff_cpi1(ipmi_user_t user) +static void ipmi_poweroff_cpi1(struct ipmi_user *user) { struct ipmi_system_interface_addr smi_addr; struct ipmi_ipmb_addr ipmb_addr; @@ -424,7 +424,7 @@ static void ipmi_poweroff_cpi1(ipmi_user_t user) */ #define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00} -static int ipmi_dell_chassis_detect(ipmi_user_t user) +static int ipmi_dell_chassis_detect(struct ipmi_user *user) { const char ipmi_version_major = ipmi_version & 0xF; const char ipmi_version_minor = (ipmi_version >> 4) & 0xF; @@ -445,7 +445,7 @@ static int ipmi_dell_chassis_detect(ipmi_user_t user) #define HP_IANA_MFR_ID 0x0b #define HP_BMC_PROD_ID 0x8201 -static int ipmi_hp_chassis_detect(ipmi_user_t user) +static int ipmi_hp_chassis_detect(struct ipmi_user *user) { if (mfg_id == HP_IANA_MFR_ID && prod_id == HP_BMC_PROD_ID @@ -461,13 +461,13 @@ static int ipmi_hp_chassis_detect(ipmi_user_t user) #define IPMI_NETFN_CHASSIS_REQUEST 0 #define IPMI_CHASSIS_CONTROL_CMD 0x02 -static int ipmi_chassis_detect(ipmi_user_t user) +static int ipmi_chassis_detect(struct ipmi_user *user) { /* Chassis support, use it. */ return (capabilities & 0x80); } -static void ipmi_poweroff_chassis(ipmi_user_t user) +static void ipmi_poweroff_chassis(struct ipmi_user *user) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; @@ -517,8 +517,8 @@ static void ipmi_poweroff_chassis(ipmi_user_t user) /* Table of possible power off functions. */ struct poweroff_function { char *platform_type; - int (*detect)(ipmi_user_t user); - void (*poweroff_func)(ipmi_user_t user); + int (*detect)(struct ipmi_user *user); + void (*poweroff_func)(struct ipmi_user *user); }; static struct poweroff_function poweroff_functions[] = { diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index ff870aa91cfe..ad353be871bf 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -122,8 +122,8 @@ enum si_stat_indexes { }; struct smi_info { - int intf_num; - ipmi_smi_t intf; + int si_num; + struct ipmi_smi *intf; struct si_sm_data *si_sm; const struct si_sm_handlers *handlers; spinlock_t si_lock; @@ -261,7 +261,6 @@ static int num_max_busy_us; static bool unload_when_empty = true; static int try_smi_init(struct smi_info *smi); -static void shutdown_one_si(struct smi_info *smi_info); static void cleanup_one_si(struct smi_info *smi_info); static void cleanup_ipmi_si(void); @@ -287,10 +286,7 @@ static void deliver_recv_msg(struct smi_info *smi_info, struct ipmi_smi_msg *msg) { /* Deliver the message to the upper layer. */ - if (smi_info->intf) - ipmi_smi_msg_received(smi_info->intf, msg); - else - ipmi_free_smi_msg(msg); + ipmi_smi_msg_received(smi_info->intf, msg); } static void return_hosed_msg(struct smi_info *smi_info, int cCode) @@ -471,8 +467,7 @@ retry: start_clear_flags(smi_info); smi_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT; - if (smi_info->intf) - ipmi_smi_watchdog_pretimeout(smi_info->intf); + ipmi_smi_watchdog_pretimeout(smi_info->intf); } else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) { /* Messages available. */ smi_info->curr_msg = alloc_msg_handle_irq(smi_info); @@ -798,8 +793,7 @@ restart: * We prefer handling attn over new messages. But don't do * this if there is not yet an upper layer to handle anything. */ - if (likely(smi_info->intf) && - (si_sm_result == SI_SM_ATTN || smi_info->got_attn)) { + if (si_sm_result == SI_SM_ATTN || smi_info->got_attn) { unsigned char msg[2]; if (smi_info->si_state != SI_NORMAL) { @@ -962,8 +956,8 @@ static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result, { unsigned int max_busy_us = 0; - if (smi_info->intf_num < num_max_busy_us) - max_busy_us = kipmid_max_busy_us[smi_info->intf_num]; + 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)) { @@ -1143,8 +1137,8 @@ irqreturn_t ipmi_si_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static int smi_start_processing(void *send_info, - ipmi_smi_t intf) +static int smi_start_processing(void *send_info, + struct ipmi_smi *intf) { struct smi_info *new_smi = send_info; int enable = 0; @@ -1165,8 +1159,8 @@ static int smi_start_processing(void *send_info, /* * Check if the user forcefully enabled the daemon. */ - if (new_smi->intf_num < num_force_kipmid) - enable = force_kipmid[new_smi->intf_num]; + if (new_smi->si_num < num_force_kipmid) + enable = force_kipmid[new_smi->si_num]; /* * The BT interface is efficient enough to not need a thread, * and there is no need for a thread if we have interrupts. @@ -1176,7 +1170,7 @@ static int smi_start_processing(void *send_info, if (enable) { new_smi->thread = kthread_run(ipmi_thread, new_smi, - "kipmi%d", new_smi->intf_num); + "kipmi%d", new_smi->si_num); if (IS_ERR(new_smi->thread)) { dev_notice(new_smi->io.dev, "Could not start" " kernel thread due to error %ld, only using" @@ -1209,9 +1203,11 @@ static void set_maintenance_mode(void *send_info, bool enable) atomic_set(&smi_info->req_events, 0); } +static void shutdown_smi(void *send_info); static const struct ipmi_smi_handlers handlers = { .owner = THIS_MODULE, .start_processing = smi_start_processing, + .shutdown = shutdown_smi, .get_smi_info = get_smi_info, .sender = sender, .request_events = request_events, @@ -1592,102 +1588,6 @@ out: return rv; } -#ifdef CONFIG_IPMI_PROC_INTERFACE -static int smi_type_proc_show(struct seq_file *m, void *v) -{ - struct smi_info *smi = m->private; - - seq_printf(m, "%s\n", si_to_str[smi->io.si_type]); - - return 0; -} - -static int smi_type_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, smi_type_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations smi_type_proc_ops = { - .open = smi_type_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int smi_si_stats_proc_show(struct seq_file *m, void *v) -{ - struct smi_info *smi = m->private; - - seq_printf(m, "interrupts_enabled: %d\n", - smi->io.irq && !smi->interrupt_disabled); - seq_printf(m, "short_timeouts: %u\n", - smi_get_stat(smi, short_timeouts)); - seq_printf(m, "long_timeouts: %u\n", - smi_get_stat(smi, long_timeouts)); - seq_printf(m, "idles: %u\n", - smi_get_stat(smi, idles)); - seq_printf(m, "interrupts: %u\n", - smi_get_stat(smi, interrupts)); - seq_printf(m, "attentions: %u\n", - smi_get_stat(smi, attentions)); - seq_printf(m, "flag_fetches: %u\n", - smi_get_stat(smi, flag_fetches)); - seq_printf(m, "hosed_count: %u\n", - smi_get_stat(smi, hosed_count)); - seq_printf(m, "complete_transactions: %u\n", - smi_get_stat(smi, complete_transactions)); - seq_printf(m, "events: %u\n", - smi_get_stat(smi, events)); - seq_printf(m, "watchdog_pretimeouts: %u\n", - smi_get_stat(smi, watchdog_pretimeouts)); - seq_printf(m, "incoming_messages: %u\n", - smi_get_stat(smi, incoming_messages)); - return 0; -} - -static int smi_si_stats_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, smi_si_stats_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations smi_si_stats_proc_ops = { - .open = smi_si_stats_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int smi_params_proc_show(struct seq_file *m, void *v) -{ - struct smi_info *smi = m->private; - - seq_printf(m, - "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", - si_to_str[smi->io.si_type], - addr_space_to_str[smi->io.addr_type], - smi->io.addr_data, - smi->io.regspacing, - smi->io.regsize, - smi->io.regshift, - smi->io.irq, - smi->io.slave_addr); - - return 0; -} - -static int smi_params_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, smi_params_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations smi_params_proc_ops = { - .open = smi_params_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif - #define IPMI_SI_ATTR(name) \ static ssize_t ipmi_##name##_show(struct device *dev, \ struct device_attribute *attr, \ @@ -2006,14 +1906,8 @@ int ipmi_si_add_smi(struct si_sm_io *io) list_add_tail(&new_smi->link, &smi_infos); - if (initialized) { + if (initialized) rv = try_smi_init(new_smi); - if (rv) { - cleanup_one_si(new_smi); - mutex_unlock(&smi_infos_lock); - return rv; - } - } out_err: mutex_unlock(&smi_infos_lock); return rv; @@ -2056,19 +1950,19 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err; } - new_smi->intf_num = smi_num; + new_smi->si_num = smi_num; /* Do this early so it's available for logs. */ if (!new_smi->io.dev) { init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d", - new_smi->intf_num); + new_smi->si_num); /* * If we don't already have a device from something * else (like PCI), then register a new one. */ new_smi->pdev = platform_device_alloc("ipmi_si", - new_smi->intf_num); + new_smi->si_num); if (!new_smi->pdev) { pr_err(PFX "Unable to allocate platform device\n"); rv = -ENOMEM; @@ -2182,35 +2076,6 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err; } -#ifdef CONFIG_IPMI_PROC_INTERFACE - rv = ipmi_smi_add_proc_entry(new_smi->intf, "type", - &smi_type_proc_ops, - new_smi); - if (rv) { - dev_err(new_smi->io.dev, - "Unable to create proc entry: %d\n", rv); - goto out_err; - } - - rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats", - &smi_si_stats_proc_ops, - new_smi); - if (rv) { - dev_err(new_smi->io.dev, - "Unable to create proc entry: %d\n", rv); - goto out_err; - } - - rv = ipmi_smi_add_proc_entry(new_smi->intf, "params", - &smi_params_proc_ops, - new_smi); - if (rv) { - dev_err(new_smi->io.dev, - "Unable to create proc entry: %d\n", rv); - goto out_err; - } -#endif - /* Don't increment till we know we have succeeded. */ smi_num++; @@ -2223,7 +2088,8 @@ static int try_smi_init(struct smi_info *new_smi) return 0; out_err: - shutdown_one_si(new_smi); + ipmi_unregister_smi(new_smi->intf); + new_smi->intf = NULL; kfree(init_name); @@ -2301,20 +2167,9 @@ skip_fallback_noirq: } module_init(init_ipmi_si); -static void shutdown_one_si(struct smi_info *smi_info) +static void shutdown_smi(void *send_info) { - int rv = 0; - - if (smi_info->intf) { - ipmi_smi_t intf = smi_info->intf; - - smi_info->intf = NULL; - rv = ipmi_unregister_smi(intf); - if (rv) { - pr_err(PFX "Unable to unregister device: errno=%d\n", - rv); - } - } + struct smi_info *smi_info = send_info; if (smi_info->dev_group_added) { device_remove_group(smi_info->io.dev, &ipmi_si_dev_attr_group); @@ -2372,6 +2227,10 @@ static void shutdown_one_si(struct smi_info *smi_info) smi_info->si_sm = NULL; } +/* + * Must be called with smi_infos_lock held, to serialize the + * smi_info->intf check. + */ static void cleanup_one_si(struct smi_info *smi_info) { if (!smi_info) @@ -2379,7 +2238,10 @@ static void cleanup_one_si(struct smi_info *smi_info) list_del(&smi_info->link); - shutdown_one_si(smi_info); + if (smi_info->intf) { + ipmi_unregister_smi(smi_info->intf); + smi_info->intf = NULL; + } if (smi_info->pdev) { if (smi_info->pdev_registered) diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 35a82f4bfd78..22f634eb09fd 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -193,8 +193,7 @@ typedef void (*ssif_i2c_done)(struct ssif_info *ssif_info, int result, unsigned char *data, unsigned int len); struct ssif_info { - ipmi_smi_t intf; - int intf_num; + struct ipmi_smi *intf; spinlock_t lock; struct ipmi_smi_msg *waiting_msg; struct ipmi_smi_msg *curr_msg; @@ -290,8 +289,6 @@ struct ssif_info { static bool initialized; -static atomic_t next_intf = ATOMIC_INIT(0); - static void return_hosed_msg(struct ssif_info *ssif_info, struct ipmi_smi_msg *msg); static void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags); @@ -315,17 +312,13 @@ static void ipmi_ssif_unlock_cond(struct ssif_info *ssif_info, static void deliver_recv_msg(struct ssif_info *ssif_info, struct ipmi_smi_msg *msg) { - ipmi_smi_t intf = ssif_info->intf; - - if (!intf) { - ipmi_free_smi_msg(msg); - } else if (msg->rsp_size < 0) { + if (msg->rsp_size < 0) { return_hosed_msg(ssif_info, msg); pr_err(PFX "Malformed message in deliver_recv_msg: rsp_size = %d\n", msg->rsp_size); } else { - ipmi_smi_msg_received(intf, msg); + ipmi_smi_msg_received(ssif_info->intf, msg); } } @@ -452,12 +445,10 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info, static void handle_flags(struct ssif_info *ssif_info, unsigned long *flags) { if (ssif_info->msg_flags & WDT_PRE_TIMEOUT_INT) { - ipmi_smi_t intf = ssif_info->intf; /* Watchdog pre-timeout */ ssif_inc_stat(ssif_info, watchdog_pretimeouts); start_clear_flags(ssif_info, flags); - if (intf) - ipmi_smi_watchdog_pretimeout(intf); + ipmi_smi_watchdog_pretimeout(ssif_info->intf); } else if (ssif_info->msg_flags & RECEIVE_MSG_AVAIL) /* Messages available. */ start_recv_msg_fetch(ssif_info, flags); @@ -1094,27 +1085,8 @@ static void request_events(void *send_info) } } -static int inc_usecount(void *send_info) -{ - struct ssif_info *ssif_info = send_info; - - if (!i2c_get_adapter(i2c_adapter_id(ssif_info->client->adapter))) - return -ENODEV; - - i2c_use_client(ssif_info->client); - return 0; -} - -static void dec_usecount(void *send_info) -{ - struct ssif_info *ssif_info = send_info; - - i2c_release_client(ssif_info->client); - i2c_put_adapter(ssif_info->client->adapter); -} - -static int ssif_start_processing(void *send_info, - ipmi_smi_t intf) +static int ssif_start_processing(void *send_info, + struct ipmi_smi *intf) { struct ssif_info *ssif_info = send_info; @@ -1225,25 +1197,9 @@ static const struct attribute_group ipmi_ssif_dev_attr_group = { .attrs = ipmi_ssif_dev_attrs, }; -static int ssif_remove(struct i2c_client *client) +static void shutdown_ssif(void *send_info) { - struct ssif_info *ssif_info = i2c_get_clientdata(client); - struct ssif_addr_info *addr_info; - int rv; - - if (!ssif_info) - return 0; - - /* - * After this point, we won't deliver anything asychronously - * to the message handler. We can unregister ourself. - */ - rv = ipmi_unregister_smi(ssif_info->intf); - if (rv) { - pr_err(PFX "Unable to unregister device: errno=%d\n", rv); - return rv; - } - ssif_info->intf = NULL; + struct ssif_info *ssif_info = send_info; device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); dev_set_drvdata(&ssif_info->client->dev, NULL); @@ -1259,6 +1215,30 @@ static int ssif_remove(struct i2c_client *client) kthread_stop(ssif_info->thread); } + /* + * No message can be outstanding now, we have removed the + * upper layer and it permitted us to do so. + */ + kfree(ssif_info); +} + +static int ssif_remove(struct i2c_client *client) +{ + struct ssif_info *ssif_info = i2c_get_clientdata(client); + struct ipmi_smi *intf; + struct ssif_addr_info *addr_info; + + if (!ssif_info) + return 0; + + /* + * After this point, we won't deliver anything asychronously + * to the message handler. We can unregister ourself. + */ + intf = ssif_info->intf; + ssif_info->intf = NULL; + ipmi_unregister_smi(intf); + list_for_each_entry(addr_info, &ssif_infos, link) { if (addr_info->client == client) { addr_info->client = NULL; @@ -1266,11 +1246,6 @@ static int ssif_remove(struct i2c_client *client) } } - /* - * No message can be outstanding now, we have removed the - * upper layer and it permitted us to do so. - */ - kfree(ssif_info); return 0; } @@ -1341,72 +1316,6 @@ static int ssif_detect(struct i2c_client *client, struct i2c_board_info *info) return rv; } -#ifdef CONFIG_IPMI_PROC_INTERFACE -static int smi_type_proc_show(struct seq_file *m, void *v) -{ - seq_puts(m, "ssif\n"); - - return 0; -} - -static int smi_type_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, smi_type_proc_show, inode->i_private); -} - -static const struct file_operations smi_type_proc_ops = { - .open = smi_type_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int smi_stats_proc_show(struct seq_file *m, void *v) -{ - struct ssif_info *ssif_info = m->private; - - seq_printf(m, "sent_messages: %u\n", - ssif_get_stat(ssif_info, sent_messages)); - seq_printf(m, "sent_messages_parts: %u\n", - ssif_get_stat(ssif_info, sent_messages_parts)); - seq_printf(m, "send_retries: %u\n", - ssif_get_stat(ssif_info, send_retries)); - seq_printf(m, "send_errors: %u\n", - ssif_get_stat(ssif_info, send_errors)); - seq_printf(m, "received_messages: %u\n", - ssif_get_stat(ssif_info, received_messages)); - seq_printf(m, "received_message_parts: %u\n", - ssif_get_stat(ssif_info, received_message_parts)); - seq_printf(m, "receive_retries: %u\n", - ssif_get_stat(ssif_info, receive_retries)); - seq_printf(m, "receive_errors: %u\n", - ssif_get_stat(ssif_info, receive_errors)); - seq_printf(m, "flag_fetches: %u\n", - ssif_get_stat(ssif_info, flag_fetches)); - seq_printf(m, "hosed: %u\n", - ssif_get_stat(ssif_info, hosed)); - seq_printf(m, "events: %u\n", - ssif_get_stat(ssif_info, events)); - seq_printf(m, "watchdog_pretimeouts: %u\n", - ssif_get_stat(ssif_info, watchdog_pretimeouts)); - seq_printf(m, "alerts: %u\n", - ssif_get_stat(ssif_info, alerts)); - return 0; -} - -static int smi_stats_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, smi_stats_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations smi_stats_proc_ops = { - .open = smi_stats_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif - static int strcmp_nospace(char *s1, char *s2) { while (*s1 && *s2) { @@ -1678,8 +1587,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) } found: - ssif_info->intf_num = atomic_inc_return(&next_intf); - if (ssif_dbg_probe) { pr_info("ssif_probe: i2c_probe found device at i2c address %x\n", client->addr); @@ -1697,11 +1604,10 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ssif_info->handlers.owner = THIS_MODULE; ssif_info->handlers.start_processing = ssif_start_processing; + ssif_info->handlers.shutdown = shutdown_ssif; ssif_info->handlers.get_smi_info = get_smi_info; ssif_info->handlers.sender = sender; ssif_info->handlers.request_events = request_events; - ssif_info->handlers.inc_usecount = inc_usecount; - ssif_info->handlers.dec_usecount = dec_usecount; { unsigned int thread_num; @@ -1740,24 +1646,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_remove_attr; } -#ifdef CONFIG_IPMI_PROC_INTERFACE - rv = ipmi_smi_add_proc_entry(ssif_info->intf, "type", - &smi_type_proc_ops, - ssif_info); - if (rv) { - pr_err(PFX "Unable to create proc entry: %d\n", rv); - goto out_err_unreg; - } - - rv = ipmi_smi_add_proc_entry(ssif_info->intf, "ssif_stats", - &smi_stats_proc_ops, - ssif_info); - if (rv) { - pr_err(PFX "Unable to create proc entry: %d\n", rv); - goto out_err_unreg; - } -#endif - out: if (rv) { /* @@ -1775,11 +1663,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) kfree(resp); return rv; -#ifdef CONFIG_IPMI_PROC_INTERFACE -out_err_unreg: - ipmi_unregister_smi(ssif_info->intf); -#endif - out_remove_attr: device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); dev_set_drvdata(&ssif_info->client->dev, NULL); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 22bc287eac2d..ca1c5c5109f0 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -125,7 +125,7 @@ static DEFINE_MUTEX(ipmi_watchdog_mutex); static bool nowayout = WATCHDOG_NOWAYOUT; -static ipmi_user_t watchdog_user; +static struct ipmi_user *watchdog_user; static int watchdog_ifnum; /* Default the timeout to 10 seconds. */ @@ -153,7 +153,7 @@ static DEFINE_SPINLOCK(ipmi_read_lock); static char data_to_read; static DECLARE_WAIT_QUEUE_HEAD(read_q); static struct fasync_struct *fasync_q; -static char pretimeout_since_last_heartbeat; +static atomic_t pretimeout_since_last_heartbeat; static char expect_close; static int ifnum_to_use = -1; @@ -303,9 +303,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " /* Default state of the timer. */ static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE; -/* If shutting down via IPMI, we ignore the heartbeat. */ -static int ipmi_ignore_heartbeat; - /* Is someone using the watchdog? Only one user is allowed. */ static unsigned long ipmi_wdog_open; @@ -329,35 +326,33 @@ static int testing_nmi; static int nmi_handler_registered; #endif -static int ipmi_heartbeat(void); +static int __ipmi_heartbeat(void); /* - * We use a mutex to make sure that only one thing can send a set - * timeout at one time, because we only have one copy of the data. - * The mutex is claimed when the set_timeout is sent and freed - * when both messages are free. + * We use a mutex to make sure that only one thing can send a set a + * message at one time. The mutex is claimed when a message is sent + * and freed when both the send and receive messages are free. */ -static atomic_t set_timeout_tofree = ATOMIC_INIT(0); -static DEFINE_MUTEX(set_timeout_lock); -static DECLARE_COMPLETION(set_timeout_wait); -static void set_timeout_free_smi(struct ipmi_smi_msg *msg) +static atomic_t msg_tofree = ATOMIC_INIT(0); +static DECLARE_COMPLETION(msg_wait); +static void msg_free_smi(struct ipmi_smi_msg *msg) { - if (atomic_dec_and_test(&set_timeout_tofree)) - complete(&set_timeout_wait); + if (atomic_dec_and_test(&msg_tofree)) + complete(&msg_wait); } -static void set_timeout_free_recv(struct ipmi_recv_msg *msg) +static void msg_free_recv(struct ipmi_recv_msg *msg) { - if (atomic_dec_and_test(&set_timeout_tofree)) - complete(&set_timeout_wait); + if (atomic_dec_and_test(&msg_tofree)) + complete(&msg_wait); } -static struct ipmi_smi_msg set_timeout_smi_msg = { - .done = set_timeout_free_smi +static struct ipmi_smi_msg smi_msg = { + .done = msg_free_smi }; -static struct ipmi_recv_msg set_timeout_recv_msg = { - .done = set_timeout_free_recv +static struct ipmi_recv_msg recv_msg = { + .done = msg_free_recv }; -static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, +static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, struct ipmi_recv_msg *recv_msg, int *send_heartbeat_now) { @@ -368,9 +363,6 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, int hbnow = 0; - /* These can be cleared as we are setting the timeout. */ - pretimeout_since_last_heartbeat = 0; - data[0] = 0; WDOG_SET_TIMER_USE(data[0], WDOG_TIMER_USE_SMS_OS); @@ -414,46 +406,48 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, smi_msg, recv_msg, 1); - if (rv) { - printk(KERN_WARNING PFX "set timeout error: %d\n", - rv); - } - - if (send_heartbeat_now) - *send_heartbeat_now = hbnow; + if (rv) + pr_warn(PFX "set timeout error: %d\n", rv); + else if (send_heartbeat_now) + *send_heartbeat_now = hbnow; return rv; } -static int ipmi_set_timeout(int do_heartbeat) +static int _ipmi_set_timeout(int do_heartbeat) { int send_heartbeat_now; int rv; + if (!watchdog_user) + return -ENODEV; - /* We can only send one of these at a time. */ - mutex_lock(&set_timeout_lock); - - atomic_set(&set_timeout_tofree, 2); + atomic_set(&msg_tofree, 2); - rv = i_ipmi_set_timeout(&set_timeout_smi_msg, - &set_timeout_recv_msg, + rv = __ipmi_set_timeout(&smi_msg, + &recv_msg, &send_heartbeat_now); - if (rv) { - mutex_unlock(&set_timeout_lock); - goto out; - } - - wait_for_completion(&set_timeout_wait); + if (rv) + return rv; - mutex_unlock(&set_timeout_lock); + wait_for_completion(&msg_wait); if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) - || ((send_heartbeat_now) - && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) - rv = ipmi_heartbeat(); + || ((send_heartbeat_now) + && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) + rv = __ipmi_heartbeat(); + + return rv; +} + +static int ipmi_set_timeout(int do_heartbeat) +{ + int rv; + + mutex_lock(&ipmi_watchdog_mutex); + rv = _ipmi_set_timeout(do_heartbeat); + mutex_unlock(&ipmi_watchdog_mutex); -out: return rv; } @@ -531,13 +525,12 @@ static void panic_halt_ipmi_set_timeout(void) while (atomic_read(&panic_done_count) != 0) ipmi_poll_interface(watchdog_user); atomic_add(1, &panic_done_count); - rv = i_ipmi_set_timeout(&panic_halt_smi_msg, + rv = __ipmi_set_timeout(&panic_halt_smi_msg, &panic_halt_recv_msg, &send_heartbeat_now); if (rv) { atomic_sub(1, &panic_done_count); - printk(KERN_WARNING PFX - "Unable to extend the watchdog timeout."); + pr_warn(PFX "Unable to extend the watchdog timeout."); } else { if (send_heartbeat_now) panic_halt_ipmi_heartbeat(); @@ -546,69 +539,22 @@ static void panic_halt_ipmi_set_timeout(void) ipmi_poll_interface(watchdog_user); } -/* - * We use a mutex to make sure that only one thing can send a - * heartbeat at one time, because we only have one copy of the data. - * The semaphore is claimed when the set_timeout is sent and freed - * when both messages are free. - */ -static atomic_t heartbeat_tofree = ATOMIC_INIT(0); -static DEFINE_MUTEX(heartbeat_lock); -static DECLARE_COMPLETION(heartbeat_wait); -static void heartbeat_free_smi(struct ipmi_smi_msg *msg) -{ - if (atomic_dec_and_test(&heartbeat_tofree)) - complete(&heartbeat_wait); -} -static void heartbeat_free_recv(struct ipmi_recv_msg *msg) -{ - if (atomic_dec_and_test(&heartbeat_tofree)) - complete(&heartbeat_wait); -} -static struct ipmi_smi_msg heartbeat_smi_msg = { - .done = heartbeat_free_smi -}; -static struct ipmi_recv_msg heartbeat_recv_msg = { - .done = heartbeat_free_recv -}; - -static int ipmi_heartbeat(void) +static int __ipmi_heartbeat(void) { - struct kernel_ipmi_msg msg; - int rv; + struct kernel_ipmi_msg msg; + int rv; struct ipmi_system_interface_addr addr; - int timeout_retries = 0; - - if (ipmi_ignore_heartbeat) - return 0; - - if (ipmi_start_timer_on_heartbeat) { - ipmi_start_timer_on_heartbeat = 0; - ipmi_watchdog_state = action_val; - return ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); - } else if (pretimeout_since_last_heartbeat) { - /* - * A pretimeout occurred, make sure we set the timeout. - * We don't want to set the action, though, we want to - * leave that alone (thus it can't be combined with the - * above operation. - */ - return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); - } - - mutex_lock(&heartbeat_lock); + int timeout_retries = 0; restart: - atomic_set(&heartbeat_tofree, 2); - /* * Don't reset the timer if we have the timer turned off, that * re-enables the watchdog. */ - if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) { - mutex_unlock(&heartbeat_lock); + if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) return 0; - } + + atomic_set(&msg_tofree, 2); addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; addr.channel = IPMI_BMC_CHANNEL; @@ -623,26 +569,23 @@ restart: 0, &msg, NULL, - &heartbeat_smi_msg, - &heartbeat_recv_msg, + &smi_msg, + &recv_msg, 1); if (rv) { - mutex_unlock(&heartbeat_lock); - printk(KERN_WARNING PFX "heartbeat failure: %d\n", - rv); + pr_warn(PFX "heartbeat send failure: %d\n", rv); return rv; } /* Wait for the heartbeat to be sent. */ - wait_for_completion(&heartbeat_wait); + wait_for_completion(&msg_wait); - if (heartbeat_recv_msg.msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) { + if (recv_msg.msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) { timeout_retries++; if (timeout_retries > 3) { - printk(KERN_ERR PFX ": Unable to restore the IPMI" - " watchdog's settings, giving up.\n"); + pr_err(PFX ": Unable to restore the IPMI watchdog's settings, giving up.\n"); rv = -EIO; - goto out_unlock; + goto out; } /* @@ -651,18 +594,17 @@ restart: * to restore the timer's info. Note that we still hold * the heartbeat lock, to keep a heartbeat from happening * in this process, so must say no heartbeat to avoid a - * deadlock on this mutex. + * deadlock on this mutex */ - rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + rv = _ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); if (rv) { - printk(KERN_ERR PFX ": Unable to send the command to" - " set the watchdog's settings, giving up.\n"); - goto out_unlock; + pr_err(PFX ": Unable to send the command to set the watchdog's settings, giving up.\n"); + goto out; } - /* We might need a new heartbeat, so do it now */ + /* Might need a heartbeat send, go ahead and do it. */ goto restart; - } else if (heartbeat_recv_msg.msg.data[0] != 0) { + } else if (recv_msg.msg.data[0] != 0) { /* * Got an error in the heartbeat response. It was already * reported in ipmi_wdog_msg_handler, but we should return @@ -671,8 +613,43 @@ restart: rv = -EINVAL; } -out_unlock: - mutex_unlock(&heartbeat_lock); +out: + return rv; +} + +static int _ipmi_heartbeat(void) +{ + int rv; + + if (!watchdog_user) + return -ENODEV; + + if (ipmi_start_timer_on_heartbeat) { + ipmi_start_timer_on_heartbeat = 0; + ipmi_watchdog_state = action_val; + rv = _ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); + } else if (atomic_cmpxchg(&pretimeout_since_last_heartbeat, 1, 0)) { + /* + * A pretimeout occurred, make sure we set the timeout. + * We don't want to set the action, though, we want to + * leave that alone (thus it can't be combined with the + * above operation. + */ + rv = _ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); + } else { + rv = __ipmi_heartbeat(); + } + + return rv; +} + +static int ipmi_heartbeat(void) +{ + int rv; + + mutex_lock(&ipmi_watchdog_mutex); + rv = _ipmi_heartbeat(); + mutex_unlock(&ipmi_watchdog_mutex); return rv; } @@ -700,7 +677,7 @@ static int ipmi_ioctl(struct file *file, if (i) return -EFAULT; timeout = val; - return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); + return _ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); case WDIOC_GETTIMEOUT: i = copy_to_user(argp, &timeout, sizeof(timeout)); @@ -713,7 +690,7 @@ static int ipmi_ioctl(struct file *file, if (i) return -EFAULT; pretimeout = val; - return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); + return _ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); case WDIOC_GETPRETIMEOUT: i = copy_to_user(argp, &pretimeout, sizeof(pretimeout)); @@ -722,7 +699,7 @@ static int ipmi_ioctl(struct file *file, return 0; case WDIOC_KEEPALIVE: - return ipmi_heartbeat(); + return _ipmi_heartbeat(); case WDIOC_SETOPTIONS: i = copy_from_user(&val, argp, sizeof(int)); @@ -730,13 +707,13 @@ static int ipmi_ioctl(struct file *file, return -EFAULT; if (val & WDIOS_DISABLECARD) { ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + _ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); ipmi_start_timer_on_heartbeat = 0; } if (val & WDIOS_ENABLECARD) { ipmi_watchdog_state = action_val; - ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); + _ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); } return 0; @@ -810,7 +787,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(&ipmi_read_lock); + spin_lock_irq(&ipmi_read_lock); if (!data_to_read) { if (file->f_flags & O_NONBLOCK) { rv = -EAGAIN; @@ -821,9 +798,9 @@ static ssize_t ipmi_read(struct file *file, add_wait_queue(&read_q, &wait); while (!data_to_read) { set_current_state(TASK_INTERRUPTIBLE); - spin_unlock(&ipmi_read_lock); + spin_unlock_irq(&ipmi_read_lock); schedule(); - spin_lock(&ipmi_read_lock); + spin_lock_irq(&ipmi_read_lock); } remove_wait_queue(&read_q, &wait); @@ -835,7 +812,7 @@ static ssize_t ipmi_read(struct file *file, data_to_read = 0; out: - spin_unlock(&ipmi_read_lock); + spin_unlock_irq(&ipmi_read_lock); if (rv == 0) { if (copy_to_user(buf, &data_to_read, 1)) @@ -873,10 +850,10 @@ static __poll_t ipmi_poll(struct file *file, poll_table *wait) poll_wait(file, &read_q, wait); - spin_lock(&ipmi_read_lock); + spin_lock_irq(&ipmi_read_lock); if (data_to_read) mask |= (EPOLLIN | EPOLLRDNORM); - spin_unlock(&ipmi_read_lock); + spin_unlock_irq(&ipmi_read_lock); return mask; } @@ -894,11 +871,13 @@ static int ipmi_close(struct inode *ino, struct file *filep) { if (iminor(ino) == WATCHDOG_MINOR) { if (expect_close == 42) { + mutex_lock(&ipmi_watchdog_mutex); ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + _ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + mutex_unlock(&ipmi_watchdog_mutex); } else { - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); + pr_crit(PFX + "Unexpected close, not stopping watchdog!\n"); ipmi_heartbeat(); } clear_bit(0, &ipmi_wdog_open); @@ -932,11 +911,9 @@ static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, { if (msg->msg.cmd == IPMI_WDOG_RESET_TIMER && msg->msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) - printk(KERN_INFO PFX "response: The IPMI controller appears" - " to have been reset, will attempt to reinitialize" - " the watchdog timer\n"); + pr_info(PFX "response: The IPMI controller appears to have been reset, will attempt to reinitialize the watchdog timer\n"); else if (msg->msg.data[0] != 0) - printk(KERN_ERR PFX "response: Error %x on cmd %x\n", + pr_err(PFX "response: Error %x on cmd %x\n", msg->msg.data[0], msg->msg.cmd); @@ -950,12 +927,13 @@ 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) { - spin_lock(&ipmi_read_lock); + unsigned long flags; + + spin_lock_irqsave(&ipmi_read_lock, flags); data_to_read = 1; wake_up_interruptible(&read_q); kill_fasync(&fasync_q, SIGIO, POLL_IN); - - spin_unlock(&ipmi_read_lock); + spin_unlock_irqrestore(&ipmi_read_lock, flags); } } @@ -963,12 +941,34 @@ static void ipmi_wdog_pretimeout_handler(void *handler_data) * On some machines, the heartbeat will give an error and not * work unless we re-enable the timer. So do so. */ - pretimeout_since_last_heartbeat = 1; + atomic_set(&pretimeout_since_last_heartbeat, 1); +} + +static void ipmi_wdog_panic_handler(void *user_data) +{ + static int panic_event_handled; + + /* + * On a panic, if we have a panic timeout, make sure to extend + * the watchdog timer to a reasonable value to complete the + * panic, if the watchdog timer is running. Plus the + * pretimeout is meaningless at panic time. + */ + if (watchdog_user && !panic_event_handled && + ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { + /* Make sure we do this only once. */ + panic_event_handled = 1; + + timeout = panic_wdt_timeout; + pretimeout = 0; + panic_halt_ipmi_set_timeout(); + } } static const struct ipmi_user_hndl ipmi_hndlrs = { .ipmi_recv_hndl = ipmi_wdog_msg_handler, - .ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler + .ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler, + .ipmi_panic_handler = ipmi_wdog_panic_handler }; static void ipmi_register_watchdog(int ipmi_intf) @@ -985,7 +985,7 @@ static void ipmi_register_watchdog(int ipmi_intf) rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &watchdog_user); if (rv < 0) { - printk(KERN_CRIT PFX "Unable to register with ipmi\n"); + pr_crit(PFX "Unable to register with ipmi\n"); goto out; } @@ -1002,7 +1002,7 @@ static void ipmi_register_watchdog(int ipmi_intf) if (rv < 0) { ipmi_destroy_user(watchdog_user); watchdog_user = NULL; - printk(KERN_CRIT PFX "Unable to register misc device\n"); + pr_crit(PFX "Unable to register misc device\n"); } #ifdef HAVE_DIE_NMI @@ -1024,9 +1024,8 @@ static void ipmi_register_watchdog(int ipmi_intf) rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); if (rv) { - printk(KERN_WARNING PFX "Error starting timer to" - " test NMI: 0x%x. The NMI pretimeout will" - " likely not work\n", rv); + pr_warn(PFX "Error starting timer to test NMI: 0x%x. The NMI pretimeout will likely not work\n", + rv); rv = 0; goto out_restore; } @@ -1034,9 +1033,7 @@ static void ipmi_register_watchdog(int ipmi_intf) msleep(1500); if (testing_nmi != 2) { - printk(KERN_WARNING PFX "IPMI NMI didn't seem to" - " occur. The NMI pretimeout will" - " likely not work\n"); + pr_warn(PFX "IPMI NMI didn't seem to occur. The NMI pretimeout will likely not work\n"); } out_restore: testing_nmi = 0; @@ -1052,7 +1049,7 @@ static void ipmi_register_watchdog(int ipmi_intf) start_now = 0; /* Disable this function after first startup. */ ipmi_watchdog_state = action_val; ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); - printk(KERN_INFO PFX "Starting now!\n"); + pr_info(PFX "Starting now!\n"); } else { /* Stop the timer now. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; @@ -1063,34 +1060,38 @@ static void ipmi_register_watchdog(int ipmi_intf) static void ipmi_unregister_watchdog(int ipmi_intf) { int rv; + struct ipmi_user *loc_user = watchdog_user; - if (!watchdog_user) - goto out; + if (!loc_user) + return; if (watchdog_ifnum != ipmi_intf) - goto out; + return; /* Make sure no one can call us any more. */ misc_deregister(&ipmi_wdog_miscdev); + watchdog_user = NULL; + /* * Wait to make sure the message makes it out. The lower layer has * pointers to our buffers, we want to make sure they are done before * we release our memory. */ - while (atomic_read(&set_timeout_tofree)) - schedule_timeout_uninterruptible(1); + while (atomic_read(&msg_tofree)) + msg_free_smi(NULL); + + mutex_lock(&ipmi_watchdog_mutex); /* Disconnect from IPMI. */ - rv = ipmi_destroy_user(watchdog_user); - if (rv) { - printk(KERN_WARNING PFX "error unlinking from IPMI: %d\n", - rv); - } - watchdog_user = NULL; + rv = ipmi_destroy_user(loc_user); + if (rv) + pr_warn(PFX "error unlinking from IPMI: %d\n", rv); - out: - return; + /* If it comes back, restart it properly. */ + ipmi_start_timer_on_heartbeat = 1; + + mutex_unlock(&ipmi_watchdog_mutex); } #ifdef HAVE_DIE_NMI @@ -1124,7 +1125,7 @@ ipmi_nmi(unsigned int val, struct pt_regs *regs) /* On some machines, the heartbeat will give an error and not work unless we re-enable the timer. So do so. */ - pretimeout_since_last_heartbeat = 1; + atomic_set(&pretimeout_since_last_heartbeat, 1); if (atomic_inc_and_test(&preop_panic_excl)) nmi_panic(regs, PFX "pre-timeout"); } @@ -1167,36 +1168,6 @@ static struct notifier_block wdog_reboot_notifier = { .priority = 0 }; -static int wdog_panic_handler(struct notifier_block *this, - unsigned long event, - void *unused) -{ - static int panic_event_handled; - - /* On a panic, if we have a panic timeout, make sure to extend - the watchdog timer to a reasonable value to complete the - panic, if the watchdog timer is running. Plus the - pretimeout is meaningless at panic time. */ - if (watchdog_user && !panic_event_handled && - ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { - /* Make sure we do this only once. */ - panic_event_handled = 1; - - timeout = panic_wdt_timeout; - pretimeout = 0; - panic_halt_ipmi_set_timeout(); - } - - return NOTIFY_OK; -} - -static struct notifier_block wdog_panic_notifier = { - .notifier_call = wdog_panic_handler, - .next = NULL, - .priority = 150 /* priority: INT_MAX >= x >= 0 */ -}; - - static void ipmi_new_smi(int if_num, struct device *device) { ipmi_register_watchdog(if_num); @@ -1288,9 +1259,7 @@ static void check_parms(void) if (preaction_val == WDOG_PRETIMEOUT_NMI) { do_nmi = 1; if (preop_val == WDOG_PREOP_GIVE_DATA) { - printk(KERN_WARNING PFX "Pretimeout op is to give data" - " but NMI pretimeout is enabled, setting" - " pretimeout op to none\n"); + pr_warn(PFX "Pretimeout op is to give data but NMI pretimeout is enabled, setting pretimeout op to none\n"); preop_op("preop_none", NULL); do_nmi = 0; } @@ -1299,8 +1268,7 @@ static void check_parms(void) rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, "ipmi"); if (rv) { - printk(KERN_WARNING PFX - "Can't register nmi handler\n"); + pr_warn(PFX "Can't register nmi handler\n"); return; } else nmi_handler_registered = 1; @@ -1317,27 +1285,24 @@ static int __init ipmi_wdog_init(void) if (action_op(action, NULL)) { action_op("reset", NULL); - printk(KERN_INFO PFX "Unknown action '%s', defaulting to" - " reset\n", action); + pr_info(PFX "Unknown action '%s', defaulting to reset\n", + action); } if (preaction_op(preaction, NULL)) { preaction_op("pre_none", NULL); - printk(KERN_INFO PFX "Unknown preaction '%s', defaulting to" - " none\n", preaction); + pr_info(PFX "Unknown preaction '%s', defaulting to none\n", + preaction); } if (preop_op(preop, NULL)) { preop_op("preop_none", NULL); - printk(KERN_INFO PFX "Unknown preop '%s', defaulting to" - " none\n", preop); + pr_info(PFX "Unknown preop '%s', defaulting to none\n", preop); } check_parms(); register_reboot_notifier(&wdog_reboot_notifier); - atomic_notifier_chain_register(&panic_notifier_list, - &wdog_panic_notifier); rv = ipmi_smi_watcher_register(&smi_watcher); if (rv) { @@ -1345,14 +1310,12 @@ static int __init ipmi_wdog_init(void) if (nmi_handler_registered) unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); #endif - atomic_notifier_chain_unregister(&panic_notifier_list, - &wdog_panic_notifier); unregister_reboot_notifier(&wdog_reboot_notifier); - printk(KERN_WARNING PFX "can't register smi watcher\n"); + pr_warn(PFX "can't register smi watcher\n"); return rv; } - printk(KERN_INFO PFX "driver initialized\n"); + pr_info(PFX "driver initialized\n"); return 0; } @@ -1367,8 +1330,6 @@ static void __exit ipmi_wdog_exit(void) unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); #endif - atomic_notifier_chain_unregister(&panic_notifier_list, - &wdog_panic_notifier); unregister_reboot_notifier(&wdog_reboot_notifier); } module_exit(ipmi_wdog_exit); diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c new file mode 100644 index 000000000000..722f7391fe1f --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, Nuvoton Corporation. + * Copyright (c) 2018, Intel Corporation. + */ + +#define pr_fmt(fmt) "nuvoton-kcs-bmc: " fmt + +#include <linux/atomic.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "kcs_bmc.h" + +#define DEVICE_NAME "npcm-kcs-bmc" +#define KCS_CHANNEL_MAX 3 + +#define KCS1ST 0x0C +#define KCS2ST 0x1E +#define KCS3ST 0x30 + +#define KCS1DO 0x0E +#define KCS2DO 0x20 +#define KCS3DO 0x32 + +#define KCS1DI 0x10 +#define KCS2DI 0x22 +#define KCS3DI 0x34 + +#define KCS1CTL 0x18 +#define KCS2CTL 0x2A +#define KCS3CTL 0x3C +#define KCS_CTL_IBFIE BIT(0) + +#define KCS1IE 0x1C +#define KCS2IE 0x2E +#define KCS3IE 0x40 +#define KCS_IE_IRQE BIT(0) +#define KCS_IE_HIRQE BIT(3) + +/* + * 7.2.4 Core KCS Registers + * Registers in this module are 8 bits. An 8-bit register must be accessed + * by an 8-bit read or write. + * + * sts: KCS Channel n Status Register (KCSnST). + * dob: KCS Channel n Data Out Buffer Register (KCSnDO). + * dib: KCS Channel n Data In Buffer Register (KCSnDI). + * ctl: KCS Channel n Control Register (KCSnCTL). + * ie : KCS Channel n Interrupt Enable Register (KCSnIE). + */ +struct npcm7xx_kcs_reg { + u32 sts; + u32 dob; + u32 dib; + u32 ctl; + u32 ie; +}; + +struct npcm7xx_kcs_bmc { + struct regmap *map; + + const struct npcm7xx_kcs_reg *reg; +}; + +static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = { + { .sts = KCS1ST, .dob = KCS1DO, .dib = KCS1DI, .ctl = KCS1CTL, .ie = KCS1IE }, + { .sts = KCS2ST, .dob = KCS2DO, .dib = KCS2DI, .ctl = KCS2CTL, .ie = KCS2IE }, + { .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE }, +}; + +static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg) +{ + struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + u32 val = 0; + int rc; + + rc = regmap_read(priv->map, reg, &val); + WARN(rc != 0, "regmap_read() failed: %d\n", rc); + + return rc == 0 ? (u8)val : 0; +} + +static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data) +{ + struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + int rc; + + rc = regmap_write(priv->map, reg, data); + WARN(rc != 0, "regmap_write() failed: %d\n", rc); +} + +static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable) +{ + struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE, + enable ? KCS_CTL_IBFIE : 0); + + regmap_update_bits(priv->map, priv->reg->ie, KCS_IE_IRQE | KCS_IE_HIRQE, + enable ? KCS_IE_IRQE | KCS_IE_HIRQE : 0); +} + +static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg) +{ + struct kcs_bmc *kcs_bmc = arg; + + if (!kcs_bmc_handle_event(kcs_bmc)) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + return devm_request_irq(dev, irq, npcm7xx_kcs_irq, IRQF_SHARED, + dev_name(dev), kcs_bmc); +} + +static int npcm7xx_kcs_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct npcm7xx_kcs_bmc *priv; + struct kcs_bmc *kcs_bmc; + u32 chan; + int rc; + + rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan); + if (rc != 0 || chan == 0 || chan > KCS_CHANNEL_MAX) { + dev_err(dev, "no valid 'kcs_chan' configured\n"); + return -ENODEV; + } + + kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan); + if (!kcs_bmc) + return -ENOMEM; + + priv = kcs_bmc_priv(kcs_bmc); + priv->map = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->map)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + priv->reg = &npcm7xx_kcs_reg_tbl[chan - 1]; + + kcs_bmc->ioreg.idr = priv->reg->dib; + kcs_bmc->ioreg.odr = priv->reg->dob; + kcs_bmc->ioreg.str = priv->reg->sts; + kcs_bmc->io_inputb = npcm7xx_kcs_inb; + kcs_bmc->io_outputb = npcm7xx_kcs_outb; + + dev_set_drvdata(dev, kcs_bmc); + + npcm7xx_kcs_enable_channel(kcs_bmc, true); + rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev); + if (rc) + return rc; + + rc = misc_register(&kcs_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + return rc; + } + + pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n", + chan, + kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str); + + return 0; +} + +static int npcm7xx_kcs_remove(struct platform_device *pdev) +{ + struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev); + + misc_deregister(&kcs_bmc->miscdev); + + return 0; +} + +static const struct of_device_id npcm_kcs_bmc_match[] = { + { .compatible = "nuvoton,npcm750-kcs-bmc" }, + { } +}; +MODULE_DEVICE_TABLE(of, npcm_kcs_bmc_match); + +static struct platform_driver npcm_kcs_bmc_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = npcm_kcs_bmc_match, + }, + .probe = npcm7xx_kcs_probe, + .remove = npcm7xx_kcs_remove, +}; +module_platform_driver(npcm_kcs_bmc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Avi Fishman <avifishman70@gmail.com>"); +MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); +MODULE_DESCRIPTION("NPCM7xx device interface to the KCS BMC device"); |