diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_msghandler.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 761 |
1 files changed, 400 insertions, 361 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 3ba9d7e9a6c7..064944ae9fdc 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -27,7 +27,6 @@ #include <linux/ipmi_smi.h> #include <linux/notifier.h> #include <linux/init.h> -#include <linux/proc_fs.h> #include <linux/rcupdate.h> #include <linux/interrupt.h> #include <linux/moduleparam.h> @@ -41,11 +40,12 @@ static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); static int ipmi_init_msghandler(void); -static void smi_recv_work(struct work_struct *t); +static void smi_work(struct work_struct *t); 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); +static void intf_free(struct kref *ref); static bool initialized; static bool drvregistered; @@ -180,14 +180,8 @@ MODULE_PARM_DESC(max_msgs_per_user, struct ipmi_user { struct list_head link; - /* - * 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; + refcount_t destroyed; /* The upper layer that handles receive messages. */ const struct ipmi_user_hndl *handler; @@ -200,30 +194,8 @@ struct ipmi_user { bool gets_events; atomic_t nr_msgs; - - /* Free must run in process context for RCU cleanup. */ - struct work_struct remove_work; }; -static struct workqueue_struct *remove_work_wq; - -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; @@ -327,6 +299,8 @@ struct bmc_device { }; #define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev) +static struct workqueue_struct *bmc_remove_work_wq; + 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); @@ -451,11 +425,10 @@ struct ipmi_smi { struct list_head link; /* - * The list of upper layers that are using me. seq_lock write - * protects this. Read protection is with srcu. + * The list of upper layers that are using me. */ struct list_head users; - struct srcu_struct users_srcu; + struct mutex users_mutex; atomic_t nr_users; struct device_attribute nr_users_devattr; struct device_attribute nr_msgs_devattr; @@ -496,15 +469,22 @@ struct ipmi_smi { int curr_seq; /* - * Messages queued for delivery. If delivery fails (out of memory - * for instance), They will stay in here to be processed later in a - * periodic timer interrupt. The workqueue is for handling received - * messages directly from the handler. + * Messages queued for deliver to the user. + */ + struct mutex user_msgs_mutex; + struct list_head user_msgs; + + /* + * Messages queued for processing. If processing fails (out + * of memory for instance), They will stay in here to be + * processed later in a periodic timer interrupt. The + * workqueue is for handling received messages directly from + * the handler. */ spinlock_t waiting_rcv_msgs_lock; struct list_head waiting_rcv_msgs; atomic_t watchdog_pretimeouts_to_deliver; - struct work_struct recv_work; + struct work_struct smi_work; spinlock_t xmit_msgs_lock; struct list_head xmit_msgs; @@ -522,10 +502,9 @@ struct ipmi_smi { * Events that were queues because no one was there to receive * them. */ - spinlock_t events_lock; /* For dealing with event stuff. */ + struct mutex events_mutex; /* For dealing with event stuff. */ struct list_head waiting_events; unsigned int waiting_events_count; /* How many events in queue? */ - char delivering_events; char event_msg_printed; /* How many users are waiting for events? */ @@ -613,6 +592,28 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf, bool guid_set, guid_t *guid, int intf_num); static int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id); +static void free_ipmi_user(struct kref *ref) +{ + struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount); + struct module *owner; + + owner = user->intf->owner; + kref_put(&user->intf->refcount, intf_free); + module_put(owner); + vfree(user); +} + +static void release_ipmi_user(struct ipmi_user *user) +{ + kref_put(&user->refcount, free_ipmi_user); +} + +static struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user) +{ + if (!kref_get_unless_zero(&user->refcount)) + return NULL; + return user; +} /* * The driver model view of the IPMI messaging driver. @@ -630,9 +631,6 @@ static DEFINE_MUTEX(ipmidriver_mutex); static LIST_HEAD(ipmi_interfaces); static DEFINE_MUTEX(ipmi_interfaces_mutex); -#define ipmi_interfaces_mutex_held() \ - lockdep_is_held(&ipmi_interfaces_mutex) -static struct srcu_struct ipmi_interfaces_srcu; /* * List of watchers that want to know when smi's are added and deleted. @@ -698,27 +696,20 @@ static void free_smi_msg_list(struct list_head *q) } } -static void clean_up_interface_data(struct ipmi_smi *intf) +static void intf_free(struct kref *ref) { + struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount); int i; struct cmd_rcvr *rcvr, *rcvr2; - struct list_head list; - - cancel_work_sync(&intf->recv_work); free_smi_msg_list(&intf->waiting_rcv_msgs); free_recv_msg_list(&intf->waiting_events); /* * Wholesale remove all the entries from the list in the - * interface and wait for RCU to know that none are in use. + * interface. No need for locks, this is single-threaded. */ - mutex_lock(&intf->cmd_rcvrs_mutex); - INIT_LIST_HEAD(&list); - list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu); - mutex_unlock(&intf->cmd_rcvrs_mutex); - - list_for_each_entry_safe(rcvr, rcvr2, &list, link) + list_for_each_entry_safe(rcvr, rcvr2, &intf->cmd_rcvrs, link) kfree(rcvr); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { @@ -726,20 +717,17 @@ static void clean_up_interface_data(struct ipmi_smi *intf) && (intf->seq_table[i].recv_msg)) ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } -} - -static void intf_free(struct kref *ref) -{ - struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount); - clean_up_interface_data(intf); kfree(intf); } int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) { struct ipmi_smi *intf; - int index, rv; + unsigned int count = 0, i; + int *interfaces = NULL; + struct device **devices = NULL; + int rv = 0; /* * Make sure the driver is actually initialized, this handles @@ -753,20 +741,53 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) list_add(&watcher->link, &smi_watchers); - index = srcu_read_lock(&ipmi_interfaces_srcu); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link, - lockdep_is_held(&smi_watchers_mutex)) { - int intf_num = READ_ONCE(intf->intf_num); + /* + * Build an array of ipmi interfaces and fill it in, and + * another array of the devices. We can't call the callback + * with ipmi_interfaces_mutex held. smi_watchers_mutex will + * keep things in order for the user. + */ + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry(intf, &ipmi_interfaces, link) + count++; + if (count > 0) { + interfaces = kmalloc_array(count, sizeof(*interfaces), + GFP_KERNEL); + if (!interfaces) { + rv = -ENOMEM; + } else { + devices = kmalloc_array(count, sizeof(*devices), + GFP_KERNEL); + if (!devices) { + kfree(interfaces); + interfaces = NULL; + rv = -ENOMEM; + } + } + count = 0; + } + if (interfaces) { + list_for_each_entry(intf, &ipmi_interfaces, link) { + int intf_num = READ_ONCE(intf->intf_num); - if (intf_num == -1) - continue; - watcher->new_smi(intf_num, intf->si_dev); + if (intf_num == -1) + continue; + devices[count] = intf->si_dev; + interfaces[count++] = intf_num; + } + } + mutex_unlock(&ipmi_interfaces_mutex); + + if (interfaces) { + for (i = 0; i < count; i++) + watcher->new_smi(interfaces[i], devices[i]); + kfree(interfaces); + kfree(devices); } - srcu_read_unlock(&ipmi_interfaces_srcu, index); mutex_unlock(&smi_watchers_mutex); - return 0; + return rv; } EXPORT_SYMBOL(ipmi_smi_watcher_register); @@ -779,22 +800,17 @@ int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) } EXPORT_SYMBOL(ipmi_smi_watcher_unregister); -/* - * Must be called with smi_watchers_mutex held. - */ static void 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 @@ -941,18 +957,14 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) ipmi_free_recv_msg(msg); atomic_dec(&msg->user->nr_msgs); } else { - int index; - struct ipmi_user *user = acquire_ipmi_user(msg->user, &index); - - if (user) { - atomic_dec(&user->nr_msgs); - user->handler->ipmi_recv_hndl(msg, user->handler_data); - release_ipmi_user(user, index); - } else { - /* User went away, give up. */ - ipmi_free_recv_msg(msg); - rv = -EINVAL; - } + /* + * Deliver it in smi_work. The message will hold a + * refcount to the user. + */ + mutex_lock(&intf->user_msgs_mutex); + list_add_tail(&msg->link, &intf->user_msgs); + mutex_unlock(&intf->user_msgs_mutex); + queue_work(system_wq, &intf->smi_work); } return rv; @@ -1192,23 +1204,14 @@ static int intf_err_seq(struct ipmi_smi *intf, return rv; } -static void free_user_work(struct work_struct *work) -{ - struct ipmi_user *user = container_of(work, struct ipmi_user, - remove_work); - - cleanup_srcu_struct(&user->release_barrier); - vfree(user); -} - int ipmi_create_user(unsigned int if_num, const struct ipmi_user_hndl *handler, void *handler_data, struct ipmi_user **user) { unsigned long flags; - struct ipmi_user *new_user; - int rv, index; + struct ipmi_user *new_user = NULL; + int rv = 0; struct ipmi_smi *intf; /* @@ -1230,30 +1233,31 @@ int ipmi_create_user(unsigned int if_num, if (rv) return rv; - new_user = vzalloc(sizeof(*new_user)); - if (!new_user) - return -ENOMEM; - - index = srcu_read_lock(&ipmi_interfaces_srcu); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry(intf, &ipmi_interfaces, link) { if (intf->intf_num == if_num) goto found; } /* Not found, return an error */ rv = -EINVAL; - goto out_kfree; + goto out_unlock; found: + if (intf->in_shutdown) { + rv = -ENODEV; + goto out_unlock; + } + if (atomic_add_return(1, &intf->nr_users) > max_users) { rv = -EBUSY; goto out_kfree; } - INIT_WORK(&new_user->remove_work, free_user_work); - - rv = init_srcu_struct(&new_user->release_barrier); - if (rv) + new_user = vzalloc(sizeof(*new_user)); + if (!new_user) { + rv = -ENOMEM; goto out_kfree; + } if (!try_module_get(intf->owner)) { rv = -ENODEV; @@ -1265,64 +1269,58 @@ int ipmi_create_user(unsigned int if_num, atomic_set(&new_user->nr_msgs, 0); kref_init(&new_user->refcount); + refcount_set(&new_user->destroyed, 1); + kref_get(&new_user->refcount); /* Destroy owns a refcount. */ new_user->handler = handler; new_user->handler_data = handler_data; new_user->intf = intf; new_user->gets_events = false; - rcu_assign_pointer(new_user->self, new_user); + mutex_lock(&intf->users_mutex); spin_lock_irqsave(&intf->seq_lock, flags); - list_add_rcu(&new_user->link, &intf->users); + list_add(&new_user->link, &intf->users); spin_unlock_irqrestore(&intf->seq_lock, flags); + mutex_unlock(&intf->users_mutex); + if (handler->ipmi_watchdog_pretimeout) /* User wants pretimeouts, so make sure to watch for them. */ smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG); - srcu_read_unlock(&ipmi_interfaces_srcu, index); - *user = new_user; - return 0; out_kfree: - atomic_dec(&intf->nr_users); - srcu_read_unlock(&ipmi_interfaces_srcu, index); - vfree(new_user); + if (rv) { + atomic_dec(&intf->nr_users); + vfree(new_user); + } else { + *user = new_user; + } +out_unlock: + mutex_unlock(&ipmi_interfaces_mutex); return rv; } EXPORT_SYMBOL(ipmi_create_user); int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) { - int rv, index; + int rv = -EINVAL; struct ipmi_smi *intf; - index = srcu_read_lock(&ipmi_interfaces_srcu); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - if (intf->intf_num == if_num) - goto found; + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry(intf, &ipmi_interfaces, link) { + if (intf->intf_num == if_num) { + if (!intf->handlers->get_smi_info) + rv = -ENOTTY; + else + rv = intf->handlers->get_smi_info(intf->send_info, data); + break; + } } - srcu_read_unlock(&ipmi_interfaces_srcu, index); - - /* Not found, return an error */ - return -EINVAL; - -found: - 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); + mutex_unlock(&ipmi_interfaces_mutex); return rv; } EXPORT_SYMBOL(ipmi_get_smi_info); -static void free_user(struct kref *ref) -{ - struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount); - - /* SRCU cleanup must happen in workqueue context. */ - queue_work(remove_work_wq, &user->remove_work); -} - +/* Must be called with intf->users_mutex held. */ static void _ipmi_destroy_user(struct ipmi_user *user) { struct ipmi_smi *intf = user->intf; @@ -1330,21 +1328,10 @@ static void _ipmi_destroy_user(struct ipmi_user *user) unsigned long flags; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - struct module *owner; + struct ipmi_recv_msg *msg, *msg2; - 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); + if (!refcount_dec_if_one(&user->destroyed)) 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); @@ -1355,11 +1342,11 @@ static void _ipmi_destroy_user(struct ipmi_user *user) if (user->gets_events) atomic_dec(&intf->event_waiters); - /* Remove the user from the interface's sequence table. */ - spin_lock_irqsave(&intf->seq_lock, flags); - list_del_rcu(&user->link); + /* Remove the user from the interface's list and sequence table. */ + list_del(&user->link); atomic_dec(&intf->nr_users); + spin_lock_irqsave(&intf->seq_lock, flags); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { if (intf->seq_table[i].inuse && (intf->seq_table[i].recv_msg->user == user)) { @@ -1374,7 +1361,7 @@ static void _ipmi_destroy_user(struct ipmi_user *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_srcu()) then free everything in that list. + * synchronize_rcu()) then free everything in that list. */ mutex_lock(&intf->cmd_rcvrs_mutex); list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link, @@ -1386,23 +1373,33 @@ static void _ipmi_destroy_user(struct ipmi_user *user) } } mutex_unlock(&intf->cmd_rcvrs_mutex); - synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; rcvrs = rcvr->next; kfree(rcvr); } - owner = intf->owner; - kref_put(&intf->refcount, intf_free); - module_put(owner); + mutex_lock(&intf->user_msgs_mutex); + list_for_each_entry_safe(msg, msg2, &intf->user_msgs, link) { + if (msg->user != user) + continue; + list_del(&msg->link); + ipmi_free_recv_msg(msg); + } + mutex_unlock(&intf->user_msgs_mutex); + + release_ipmi_user(user); } void ipmi_destroy_user(struct ipmi_user *user) { + struct ipmi_smi *intf = user->intf; + + mutex_lock(&intf->users_mutex); _ipmi_destroy_user(user); + mutex_unlock(&intf->users_mutex); - kref_put(&user->refcount, free_user); + kref_put(&user->refcount, free_ipmi_user); } EXPORT_SYMBOL(ipmi_destroy_user); @@ -1411,9 +1408,9 @@ int ipmi_get_version(struct ipmi_user *user, unsigned char *minor) { struct ipmi_device_id id; - int rv, index; + int rv; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1422,7 +1419,7 @@ int ipmi_get_version(struct ipmi_user *user, *major = ipmi_version_major(&id); *minor = ipmi_version_minor(&id); } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1432,9 +1429,9 @@ int ipmi_set_my_address(struct ipmi_user *user, unsigned int channel, unsigned char address) { - int index, rv = 0; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1444,7 +1441,7 @@ int ipmi_set_my_address(struct ipmi_user *user, channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); user->intf->addrinfo[channel].address = address; } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1454,9 +1451,9 @@ int ipmi_get_my_address(struct ipmi_user *user, unsigned int channel, unsigned char *address) { - int index, rv = 0; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1466,7 +1463,7 @@ int ipmi_get_my_address(struct ipmi_user *user, channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); *address = user->intf->addrinfo[channel].address; } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1476,9 +1473,9 @@ int ipmi_set_my_LUN(struct ipmi_user *user, unsigned int channel, unsigned char LUN) { - int index, rv = 0; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1488,7 +1485,7 @@ int ipmi_set_my_LUN(struct ipmi_user *user, channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); user->intf->addrinfo[channel].lun = LUN & 0x3; } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1498,9 +1495,9 @@ int ipmi_get_my_LUN(struct ipmi_user *user, unsigned int channel, unsigned char *address) { - int index, rv = 0; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1510,7 +1507,7 @@ int ipmi_get_my_LUN(struct ipmi_user *user, channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); *address = user->intf->addrinfo[channel].lun; } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1518,17 +1515,17 @@ EXPORT_SYMBOL(ipmi_get_my_LUN); int ipmi_get_maintenance_mode(struct ipmi_user *user) { - int mode, index; + int mode; unsigned long flags; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); 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); + release_ipmi_user(user); return mode; } @@ -1543,11 +1540,11 @@ static void maintenance_mode_update(struct ipmi_smi *intf) int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) { - int rv = 0, index; + int rv = 0; unsigned long flags; struct ipmi_smi *intf = user->intf; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1577,7 +1574,7 @@ int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) } out_unlock: spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1585,19 +1582,17 @@ EXPORT_SYMBOL(ipmi_set_maintenance_mode); int ipmi_set_gets_events(struct ipmi_user *user, bool val) { - unsigned long flags; struct ipmi_smi *intf = user->intf; struct ipmi_recv_msg *msg, *msg2; struct list_head msgs; - int index; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; INIT_LIST_HEAD(&msgs); - spin_lock_irqsave(&intf->events_lock, flags); + mutex_lock(&intf->events_mutex); if (user->gets_events == val) goto out; @@ -1610,13 +1605,6 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val) atomic_dec(&intf->event_waiters); } - if (intf->delivering_events) - /* - * Another thread is delivering events for this, so - * let it handle any new events. - */ - goto out; - /* Deliver any queued events. */ while (user->gets_events && !list_empty(&intf->waiting_events)) { list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) @@ -1627,22 +1615,16 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val) intf->event_msg_printed = 0; } - intf->delivering_events = 1; - spin_unlock_irqrestore(&intf->events_lock, flags); - list_for_each_entry_safe(msg, msg2, &msgs, link) { msg->user = user; kref_get(&user->refcount); deliver_local_response(intf, msg); } - - spin_lock_irqsave(&intf->events_lock, flags); - intf->delivering_events = 0; } out: - spin_unlock_irqrestore(&intf->events_lock, flags); - release_ipmi_user(user, index); + mutex_unlock(&intf->events_mutex); + release_ipmi_user(user); return 0; } @@ -1687,9 +1669,9 @@ int ipmi_register_for_cmd(struct ipmi_user *user, { struct ipmi_smi *intf = user->intf; struct cmd_rcvr *rcvr; - int rv = 0, index; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1719,7 +1701,7 @@ out_unlock: if (rv) kfree(rcvr); out_release: - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1733,9 +1715,9 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user, struct ipmi_smi *intf = user->intf; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - int i, rv = -ENOENT, index; + int i, rv = -ENOENT; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1758,7 +1740,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user, } mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); - release_ipmi_user(user, index); + release_ipmi_user(user); while (rcvrs) { smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS); rcvr = rcvrs; @@ -1882,13 +1864,12 @@ 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; + int run_to_completion = READ_ONCE(intf->run_to_completion); unsigned long flags = 0; if (!run_to_completion) spin_lock_irqsave(&intf->xmit_msgs_lock, flags); smi_msg = smi_add_send_msg(intf, smi_msg, priority); - if (!run_to_completion) spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); @@ -2304,6 +2285,7 @@ static int i_ipmi_request(struct ipmi_user *user, { struct ipmi_smi_msg *smi_msg; struct ipmi_recv_msg *recv_msg; + int run_to_completion = READ_ONCE(intf->run_to_completion); int rv = 0; if (user) { @@ -2337,7 +2319,8 @@ static int i_ipmi_request(struct ipmi_user *user, } } - rcu_read_lock(); + if (!run_to_completion) + mutex_lock(&intf->users_mutex); if (intf->in_shutdown) { rv = -ENODEV; goto out_err; @@ -2383,7 +2366,8 @@ out_err: smi_send(intf, intf->handlers, smi_msg, priority); } - rcu_read_unlock(); + if (!run_to_completion) + mutex_unlock(&intf->users_mutex); out: if (rv && user) @@ -2414,12 +2398,12 @@ int ipmi_request_settime(struct ipmi_user *user, unsigned int retry_time_ms) { unsigned char saddr = 0, lun = 0; - int rv, index; + int rv; if (!user) return -EINVAL; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -2438,7 +2422,7 @@ int ipmi_request_settime(struct ipmi_user *user, retries, retry_time_ms); - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } EXPORT_SYMBOL(ipmi_request_settime); @@ -2453,12 +2437,12 @@ int ipmi_request_supply_msgs(struct ipmi_user *user, int priority) { unsigned char saddr = 0, lun = 0; - int rv, index; + int rv; if (!user) return -EINVAL; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -2477,7 +2461,7 @@ int ipmi_request_supply_msgs(struct ipmi_user *user, lun, -1, 0); - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } EXPORT_SYMBOL(ipmi_request_supply_msgs); @@ -3064,7 +3048,7 @@ cleanup_bmc_device(struct kref *ref) * with removing the device attributes while reading a device * attribute. */ - queue_work(remove_work_wq, &bmc->remove_work); + queue_work(bmc_remove_work_wq, &bmc->remove_work); } /* @@ -3520,15 +3504,14 @@ static ssize_t nr_msgs_show(struct device *dev, char *buf) { struct ipmi_smi *intf = container_of(attr, - struct ipmi_smi, nr_msgs_devattr); + struct ipmi_smi, nr_msgs_devattr); struct ipmi_user *user; - int index; unsigned int count = 0; - index = srcu_read_lock(&intf->users_srcu); - list_for_each_entry_rcu(user, &intf->users, link) + mutex_lock(&intf->users_mutex); + list_for_each_entry(user, &intf->users, link) count += atomic_read(&user->nr_msgs); - srcu_read_unlock(&intf->users_srcu, index); + mutex_unlock(&intf->users_mutex); return sysfs_emit(buf, "%u\n", count); } @@ -3569,12 +3552,6 @@ int ipmi_add_smi(struct module *owner, if (!intf) return -ENOMEM; - rv = init_srcu_struct(&intf->users_srcu); - if (rv) { - kfree(intf); - return rv; - } - intf->owner = owner; intf->bmc = &intf->tmp_bmc; INIT_LIST_HEAD(&intf->bmc->intfs); @@ -3591,7 +3568,10 @@ int ipmi_add_smi(struct module *owner, } if (slave_addr != 0) intf->addrinfo[0].address = slave_addr; + INIT_LIST_HEAD(&intf->user_msgs); + mutex_init(&intf->user_msgs_mutex); INIT_LIST_HEAD(&intf->users); + mutex_init(&intf->users_mutex); atomic_set(&intf->nr_users, 0); intf->handlers = handlers; intf->send_info = send_info; @@ -3603,12 +3583,12 @@ int ipmi_add_smi(struct module *owner, intf->curr_seq = 0; spin_lock_init(&intf->waiting_rcv_msgs_lock); INIT_LIST_HEAD(&intf->waiting_rcv_msgs); - INIT_WORK(&intf->recv_work, smi_recv_work); + INIT_WORK(&intf->smi_work, smi_work); atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0); spin_lock_init(&intf->xmit_msgs_lock); INIT_LIST_HEAD(&intf->xmit_msgs); INIT_LIST_HEAD(&intf->hp_xmit_msgs); - spin_lock_init(&intf->events_lock); + mutex_init(&intf->events_mutex); spin_lock_init(&intf->watch_lock); atomic_set(&intf->event_waiters, 0); intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; @@ -3621,12 +3601,16 @@ int ipmi_add_smi(struct module *owner, for (i = 0; i < IPMI_NUM_STATS; i++) atomic_set(&intf->stats[i], 0); + /* + * Grab the watchers mutex so we can deliver the new interface + * without races. + */ + mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); /* Look for a hole in the numbers. */ i = 0; link = &ipmi_interfaces; - list_for_each_entry_rcu(tintf, &ipmi_interfaces, link, - ipmi_interfaces_mutex_held()) { + list_for_each_entry(tintf, &ipmi_interfaces, link) { if (tintf->intf_num != i) { link = &tintf->link; break; @@ -3635,9 +3619,9 @@ int ipmi_add_smi(struct module *owner, } /* Add the new interface in numeric order. */ if (i == 0) - list_add_rcu(&intf->link, &ipmi_interfaces); + list_add(&intf->link, &ipmi_interfaces); else - list_add_tail_rcu(&intf->link, link); + list_add_tail(&intf->link, link); rv = handlers->start_processing(send_info, intf); if (rv) @@ -3669,18 +3653,14 @@ int ipmi_add_smi(struct module *owner, goto out_err_bmc_reg; } - /* - * Keep memory order straight for RCU readers. Make - * sure everything else is committed to memory before - * setting intf_num to mark the interface valid. - */ - 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 0; out_err_bmc_reg: @@ -3689,10 +3669,9 @@ int ipmi_add_smi(struct module *owner, if (intf->handlers->shutdown) intf->handlers->shutdown(intf->send_info); out_err: - list_del_rcu(&intf->link); + list_del(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); - synchronize_srcu(&ipmi_interfaces_srcu); - cleanup_srcu_struct(&intf->users_srcu); + mutex_unlock(&smi_watchers_mutex); kref_put(&intf->refcount, intf_free); return rv; @@ -3758,19 +3737,28 @@ static void cleanup_smi_msgs(struct ipmi_smi *intf) void ipmi_unregister_smi(struct ipmi_smi *intf) { struct ipmi_smi_watcher *w; - int intf_num, index; + int intf_num; if (!intf) return; + intf_num = intf->intf_num; mutex_lock(&ipmi_interfaces_mutex); + cancel_work_sync(&intf->smi_work); + /* smi_work() can no longer be in progress after this. */ + intf->intf_num = -1; intf->in_shutdown = true; - list_del_rcu(&intf->link); + list_del(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); - synchronize_srcu(&ipmi_interfaces_srcu); - /* At this point no users can be added to the interface. */ + /* + * At this point no users can be added to the interface and no + * new messages can be sent. + */ + + if (intf->handlers->shutdown) + intf->handlers->shutdown(intf->send_info); device_remove_file(intf->si_dev, &intf->nr_msgs_devattr); device_remove_file(intf->si_dev, &intf->nr_users_devattr); @@ -3784,24 +3772,19 @@ void ipmi_unregister_smi(struct ipmi_smi *intf) w->smi_gone(intf_num); mutex_unlock(&smi_watchers_mutex); - index = srcu_read_lock(&intf->users_srcu); + mutex_lock(&intf->users_mutex); while (!list_empty(&intf->users)) { - struct ipmi_user *user = - container_of(list_next_rcu(&intf->users), - struct ipmi_user, link); + struct ipmi_user *user = list_first_entry(&intf->users, + struct ipmi_user, link); _ipmi_destroy_user(user); } - srcu_read_unlock(&intf->users_srcu, index); - - if (intf->handlers->shutdown) - intf->handlers->shutdown(intf->send_info); + mutex_unlock(&intf->users_mutex); cleanup_smi_msgs(intf); ipmi_bmc_unregister(intf); - cleanup_srcu_struct(&intf->users_srcu); kref_put(&intf->refcount, intf_free); } EXPORT_SYMBOL(ipmi_unregister_smi); @@ -3926,17 +3909,12 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, dev_dbg(intf->si_dev, "Invalid command: %*ph\n", msg->data_size, msg->data); - rcu_read_lock(); - if (!intf->in_shutdown) { - smi_send(intf, intf->handlers, msg, 0); - /* - * We used the message, so return the value - * that causes it to not be freed or - * queued. - */ - rv = -1; - } - rcu_read_unlock(); + smi_send(intf, intf->handlers, msg, 0); + /* + * We used the message, so return the value that + * causes it to not be freed or queued. + */ + rv = -1; } else { recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { @@ -3946,7 +3924,7 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, * later. */ rv = 1; - kref_put(&user->refcount, free_user); + kref_put(&user->refcount, free_ipmi_user); } else { /* Extract the source address from the data. */ ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr; @@ -4017,17 +3995,12 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf, msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE; msg->data_size = 5; - rcu_read_lock(); - if (!intf->in_shutdown) { - smi_send(intf, intf->handlers, msg, 0); - /* - * We used the message, so return the value - * that causes it to not be freed or - * queued. - */ - rv = -1; - } - rcu_read_unlock(); + smi_send(intf, intf->handlers, msg, 0); + /* + * We used the message, so return the value that + * causes it to not be freed or queued. + */ + rv = -1; } else { recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { @@ -4037,7 +4010,7 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf, * later. */ rv = 1; - kref_put(&user->refcount, free_user); + kref_put(&user->refcount, free_ipmi_user); } else { /* Extract the source address from the data. */ daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr; @@ -4206,14 +4179,33 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf, rcu_read_unlock(); if (user == NULL) { - /* We didn't find a user, just give up. */ + /* We didn't find a user, just give up and return an error. */ ipmi_inc_stat(intf, unhandled_commands); + msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); + msg->data[1] = IPMI_SEND_MSG_CMD; + msg->data[2] = chan; + msg->data[3] = msg->rsp[4]; /* handle */ + msg->data[4] = msg->rsp[8]; /* rsSWID */ + msg->data[5] = ((netfn + 1) << 2) | (msg->rsp[9] & 0x3); + msg->data[6] = ipmb_checksum(&msg->data[3], 3); + msg->data[7] = msg->rsp[5]; /* rqSWID */ + /* rqseq/lun */ + msg->data[8] = (msg->rsp[9] & 0xfc) | (msg->rsp[6] & 0x3); + msg->data[9] = cmd; + msg->data[10] = IPMI_INVALID_CMD_COMPLETION_CODE; + msg->data[11] = ipmb_checksum(&msg->data[7], 4); + msg->data_size = 12; + + dev_dbg(intf->si_dev, "Invalid command: %*ph\n", + msg->data_size, msg->data); + + smi_send(intf, intf->handlers, msg, 0); /* - * Don't do anything with these messages, just allow - * them to be freed. + * We used the message, so return the value that + * causes it to not be freed or queued. */ - rv = 0; + rv = -1; } else { recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { @@ -4222,7 +4214,7 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf, * message, so requeue it for handling later. */ rv = 1; - kref_put(&user->refcount, free_user); + kref_put(&user->refcount, free_ipmi_user); } else { /* Extract the source address from the data. */ lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr; @@ -4331,7 +4323,7 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf, * later. */ rv = 1; - kref_put(&user->refcount, free_user); + kref_put(&user->refcount, free_ipmi_user); } else { /* * OEM Messages are expected to be delivered via @@ -4393,8 +4385,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg, *recv_msg2; struct list_head msgs; struct ipmi_user *user; - int rv = 0, deliver_count = 0, index; - unsigned long flags; + int rv = 0, deliver_count = 0; if (msg->rsp_size < 19) { /* Message is too small to be an IPMB event. */ @@ -4409,7 +4400,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, INIT_LIST_HEAD(&msgs); - spin_lock_irqsave(&intf->events_lock, flags); + mutex_lock(&intf->events_mutex); ipmi_inc_stat(intf, events); @@ -4417,18 +4408,20 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, * Allocate and fill in one message for every user that is * getting events. */ - index = srcu_read_lock(&intf->users_srcu); - list_for_each_entry_rcu(user, &intf->users, link) { + mutex_lock(&intf->users_mutex); + list_for_each_entry(user, &intf->users, link) { if (!user->gets_events) continue; recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { - rcu_read_unlock(); + mutex_unlock(&intf->users_mutex); list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { + user = recv_msg->user; list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); + kref_put(&user->refcount, free_ipmi_user); } /* * We couldn't allocate memory for the @@ -4446,7 +4439,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, kref_get(&user->refcount); list_add_tail(&recv_msg->link, &msgs); } - srcu_read_unlock(&intf->users_srcu, index); + mutex_unlock(&intf->users_mutex); if (deliver_count) { /* Now deliver all the messages. */ @@ -4484,7 +4477,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, } out: - spin_unlock_irqrestore(&intf->events_lock, flags); + mutex_unlock(&intf->events_mutex); return rv; } @@ -4570,7 +4563,7 @@ return_unspecified: && (msg->data[1] == IPMI_SEND_MSG_CMD) && (msg->user_data == NULL)) { - if (intf->in_shutdown) + if (intf->in_shutdown || intf->run_to_completion) goto out; /* @@ -4642,6 +4635,9 @@ return_unspecified: */ struct ipmi_recv_msg *recv_msg; + if (intf->run_to_completion) + goto out; + chan = msg->data[2] & 0x0f; if (chan >= IPMI_MAX_CHANNELS) /* Invalid channel number */ @@ -4664,6 +4660,9 @@ process_response_response: && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { struct ipmi_channel *chans; + if (intf->run_to_completion) + goto out; + /* It's from the receive queue. */ chan = msg->rsp[3] & 0xf; if (chan >= IPMI_MAX_CHANNELS) { @@ -4738,6 +4737,9 @@ process_response_response: } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { /* It's an asynchronous event. */ + if (intf->run_to_completion) + goto out; + requeue = handle_read_event_rsp(intf, msg); } else { /* It's a response from the local BMC. */ @@ -4753,10 +4755,10 @@ process_response_response: */ static void handle_new_recv_msgs(struct ipmi_smi *intf) { - struct ipmi_smi_msg *smi_msg; - unsigned long flags = 0; - int rv; - int run_to_completion = intf->run_to_completion; + struct ipmi_smi_msg *smi_msg; + unsigned long flags = 0; + int rv; + int run_to_completion = READ_ONCE(intf->run_to_completion); /* See if any waiting messages need to be processed. */ if (!run_to_completion) @@ -4790,31 +4792,15 @@ static void handle_new_recv_msgs(struct ipmi_smi *intf) } if (!run_to_completion) spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, flags); - - /* - * If the pretimout count is non-zero, decrement one from it and - * deliver pretimeouts to all the users. - */ - if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { - struct ipmi_user *user; - int index; - - 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); - } - srcu_read_unlock(&intf->users_srcu, index); - } } -static void smi_recv_work(struct work_struct *t) +static void smi_work(struct work_struct *t) { unsigned long flags = 0; /* keep us warning-free. */ - struct ipmi_smi *intf = from_work(intf, t, recv_work); - int run_to_completion = intf->run_to_completion; + struct ipmi_smi *intf = from_work(intf, t, smi_work); + int run_to_completion = READ_ONCE(intf->run_to_completion); struct ipmi_smi_msg *newmsg = NULL; + struct ipmi_recv_msg *msg, *msg2; /* * Start the next message if available. @@ -4824,8 +4810,6 @@ static void smi_recv_work(struct work_struct *t) * message delivery. */ - rcu_read_lock(); - if (!run_to_completion) spin_lock_irqsave(&intf->xmit_msgs_lock, flags); if (intf->curr_msg == NULL && !intf->in_shutdown) { @@ -4843,15 +4827,57 @@ static void smi_recv_work(struct work_struct *t) intf->curr_msg = newmsg; } } - if (!run_to_completion) spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); + if (newmsg) intf->handlers->sender(intf->send_info, newmsg); - rcu_read_unlock(); - handle_new_recv_msgs(intf); + + /* Nothing below applies during panic time. */ + if (run_to_completion) + return; + + /* + * If the pretimout count is non-zero, decrement one from it and + * deliver pretimeouts to all the users. + */ + if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { + struct ipmi_user *user; + + mutex_lock(&intf->users_mutex); + list_for_each_entry(user, &intf->users, link) { + if (user->handler->ipmi_watchdog_pretimeout) + user->handler->ipmi_watchdog_pretimeout( + user->handler_data); + } + mutex_unlock(&intf->users_mutex); + } + + /* + * Freeing the message can cause a user to be released, which + * can then cause the interface to be freed. Make sure that + * doesn't happen until we are ready. + */ + kref_get(&intf->refcount); + + mutex_lock(&intf->user_msgs_mutex); + list_for_each_entry_safe(msg, msg2, &intf->user_msgs, link) { + struct ipmi_user *user = msg->user; + + list_del(&msg->link); + + if (refcount_read(&user->destroyed) == 0) { + ipmi_free_recv_msg(msg); + } else { + atomic_dec(&user->nr_msgs); + user->handler->ipmi_recv_hndl(msg, user->handler_data); + } + } + mutex_unlock(&intf->user_msgs_mutex); + + kref_put(&intf->refcount, intf_free); } /* Handle a new message from the lower layer. */ @@ -4859,7 +4885,7 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { unsigned long flags = 0; /* keep us warning-free. */ - int run_to_completion = intf->run_to_completion; + int run_to_completion = READ_ONCE(intf->run_to_completion); /* * To preserve message order, we keep a queue and deliver from @@ -4884,9 +4910,9 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf, spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); if (run_to_completion) - smi_recv_work(&intf->recv_work); + smi_work(&intf->smi_work); else - queue_work(system_bh_wq, &intf->recv_work); + queue_work(system_wq, &intf->smi_work); } EXPORT_SYMBOL(ipmi_smi_msg_received); @@ -4896,7 +4922,7 @@ void ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf) return; atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); - queue_work(system_bh_wq, &intf->recv_work); + queue_work(system_wq, &intf->smi_work); } EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); @@ -5065,7 +5091,7 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf, flags); } - queue_work(system_bh_wq, &intf->recv_work); + queue_work(system_wq, &intf->smi_work); return need_timer; } @@ -5084,17 +5110,19 @@ static struct timer_list ipmi_timer; static atomic_t stop_operation; -static void ipmi_timeout(struct timer_list *unused) +static void ipmi_timeout_work(struct work_struct *work) { + if (atomic_read(&stop_operation)) + return; + struct ipmi_smi *intf; bool need_timer = false; - int index; if (atomic_read(&stop_operation)) return; - index = srcu_read_lock(&ipmi_interfaces_srcu); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry(intf, &ipmi_interfaces, link) { if (atomic_read(&intf->event_waiters)) { intf->ticks_to_req_ev--; if (intf->ticks_to_req_ev == 0) { @@ -5106,12 +5134,22 @@ static void ipmi_timeout(struct timer_list *unused) need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); } - srcu_read_unlock(&ipmi_interfaces_srcu, index); + mutex_unlock(&ipmi_interfaces_mutex); if (need_timer) mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } +static DECLARE_WORK(ipmi_timer_work, ipmi_timeout_work); + +static void ipmi_timeout(struct timer_list *unused) +{ + if (atomic_read(&stop_operation)) + return; + + queue_work(system_wq, &ipmi_timer_work); +} + static void need_waiter(struct ipmi_smi *intf) { /* Racy, but worst case we start the timer twice. */ @@ -5168,7 +5206,7 @@ static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) void ipmi_free_recv_msg(struct ipmi_recv_msg *msg) { if (msg->user && !oops_in_progress) - kref_put(&msg->user->refcount, free_user); + kref_put(&msg->user->refcount, free_ipmi_user); msg->done(msg); } EXPORT_SYMBOL(ipmi_free_recv_msg); @@ -5188,9 +5226,9 @@ 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(struct ipmi_smi *intf, - struct ipmi_addr *addr, - struct kernel_ipmi_msg *msg) +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; struct ipmi_recv_msg recv_msg; @@ -5220,6 +5258,15 @@ static void ipmi_panic_request_and_wait(struct ipmi_smi *intf, ipmi_poll(intf); } +void ipmi_panic_request_and_wait(struct ipmi_user *user, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *msg) +{ + user->intf->run_to_completion = 1; + _ipmi_panic_request_and_wait(user->intf, addr, msg); +} +EXPORT_SYMBOL(ipmi_panic_request_and_wait); + static void event_receiver_fetcher(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) { @@ -5288,7 +5335,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str) } /* Send the event announcing the panic. */ - ipmi_panic_request_and_wait(intf, &addr, &msg); + _ipmi_panic_request_and_wait(intf, &addr, &msg); /* * On every interface, dump a bunch of OEM event holding the @@ -5324,7 +5371,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = device_id_fetcher; - ipmi_panic_request_and_wait(intf, &addr, &msg); + _ipmi_panic_request_and_wait(intf, &addr, &msg); if (intf->local_event_generator) { /* Request the event receiver from the local MC. */ @@ -5333,7 +5380,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = event_receiver_fetcher; - ipmi_panic_request_and_wait(intf, &addr, &msg); + _ipmi_panic_request_and_wait(intf, &addr, &msg); } intf->null_user_handler = NULL; @@ -5385,7 +5432,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str) memcpy_and_pad(data+5, 11, p, size, '\0'); p += size; - ipmi_panic_request_and_wait(intf, &addr, &msg); + _ipmi_panic_request_and_wait(intf, &addr, &msg); } } @@ -5403,7 +5450,7 @@ static int panic_event(struct notifier_block *this, has_panicked = 1; /* For every registered interface, set it to run to completion. */ - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + list_for_each_entry(intf, &ipmi_interfaces, link) { if (!intf->handlers || intf->intf_num == -1) /* Interface is not ready. */ continue; @@ -5433,7 +5480,7 @@ static int panic_event(struct notifier_block *this, intf->handlers->set_run_to_completion(intf->send_info, 1); - list_for_each_entry_rcu(user, &intf->users, link) { + list_for_each_entry(user, &intf->users, link) { if (user->handler->ipmi_panic_handler) user->handler->ipmi_panic_handler( user->handler_data); @@ -5478,15 +5525,11 @@ static int ipmi_init_msghandler(void) if (initialized) goto out; - rv = init_srcu_struct(&ipmi_interfaces_srcu); - if (rv) - goto out; - - remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq"); - if (!remove_work_wq) { + bmc_remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq"); + if (!bmc_remove_work_wq) { pr_err("unable to create ipmi-msghandler-remove-wq workqueue"); rv = -ENOMEM; - goto out_wq; + goto out; } timer_setup(&ipmi_timer, ipmi_timeout, 0); @@ -5496,9 +5539,6 @@ static int ipmi_init_msghandler(void) initialized = true; -out_wq: - if (rv) - cleanup_srcu_struct(&ipmi_interfaces_srcu); out: mutex_unlock(&ipmi_interfaces_mutex); return rv; @@ -5522,7 +5562,7 @@ static void __exit cleanup_ipmi(void) int count; if (initialized) { - destroy_workqueue(remove_work_wq); + destroy_workqueue(bmc_remove_work_wq); atomic_notifier_chain_unregister(&panic_notifier_list, &panic_block); @@ -5539,6 +5579,7 @@ static void __exit cleanup_ipmi(void) */ atomic_set(&stop_operation, 1); timer_delete_sync(&ipmi_timer); + cancel_work_sync(&ipmi_timer_work); initialized = false; @@ -5549,8 +5590,6 @@ static void __exit cleanup_ipmi(void) count = atomic_read(&recv_msg_inuse_count); if (count != 0) pr_warn("recv message count %d at exit\n", count); - - cleanup_srcu_struct(&ipmi_interfaces_srcu); } if (drvregistered) driver_unregister(&ipmidriver.driver); |