diff options
author | Corey Minyard <cminyard@mvista.com> | 2018-04-06 06:05:04 +0300 |
---|---|---|
committer | Corey Minyard <cminyard@mvista.com> | 2018-04-18 18:22:58 +0300 |
commit | e86ee2d44b44056243da17c120ad258717cedf9b (patch) | |
tree | 8ea784b7af816612379aa0ecb26f4a754c973ae2 | |
parent | ac93bd0c9e163fd3e2edfb4b5af22955b408431a (diff) | |
download | linux-e86ee2d44b44056243da17c120ad258717cedf9b.tar.xz |
ipmi: Rework locking and shutdown for hot remove
To handle hot remove of interfaces, a lot of rework had to be
done to the locking. Several things were switched over to srcu
and shutdown for users and interfaces was added for cleaner
shutdown.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 490 |
1 files changed, 280 insertions, 210 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index c21c4e021dab..a27b50ac2b7f 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -197,8 +197,12 @@ MODULE_PARM_DESC(default_max_retries, 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; @@ -213,6 +217,23 @@ struct ipmi_user { 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; @@ -444,10 +465,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; @@ -467,12 +489,6 @@ 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; @@ -615,6 +631,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. @@ -715,58 +732,32 @@ static void intf_free(struct kref *ref) struct watcher_entry { int intf_num; - struct ipmi_smi *intf; + struct ipmi_smi *intf; struct list_head link; }; int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) { struct ipmi_smi *intf; - LIST_HEAD(to_deliver); - struct watcher_entry *e, *e2; + 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); @@ -787,12 +778,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 @@ -905,9 +898,17 @@ static int deliver_response(struct ipmi_smi *intf, 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); - struct ipmi_user *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; @@ -1094,7 +1095,7 @@ int ipmi_create_user(unsigned int if_num, { unsigned long flags; struct ipmi_user *new_user; - int rv = 0; + int rv = 0, index; struct ipmi_smi *intf; /* @@ -1129,7 +1130,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; @@ -1139,6 +1140,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); @@ -1148,26 +1153,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); @@ -1176,13 +1162,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; } @@ -1190,26 +1175,25 @@ EXPORT_SYMBOL(ipmi_create_user); int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) { - int rv = 0; + int rv, index; struct ipmi_smi *intf; - const struct ipmi_smi_handlers *handlers; - 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; } @@ -1221,7 +1205,7 @@ static void free_user(struct kref *ref) kfree(user); } -int ipmi_destroy_user(struct ipmi_user *user) +static void _ipmi_destroy_user(struct ipmi_user *user) { struct ipmi_smi *intf = user->intf; int i; @@ -1229,7 +1213,22 @@ int ipmi_destroy_user(struct ipmi_user *user) 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); @@ -1254,7 +1253,7 @@ int 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_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) { @@ -1272,16 +1271,14 @@ int ipmi_destroy_user(struct ipmi_user *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; @@ -1293,16 +1290,20 @@ int ipmi_get_version(struct ipmi_user *user, 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); @@ -1310,9 +1311,17 @@ int ipmi_set_my_address(struct ipmi_user *user, unsigned int channel, unsigned char address) { + int index; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; user->intf->addrinfo[channel].address = address; + release_ipmi_user(user, index); + return 0; } EXPORT_SYMBOL(ipmi_set_my_address); @@ -1321,9 +1330,17 @@ int ipmi_get_my_address(struct ipmi_user *user, unsigned int channel, unsigned char *address) { + int index; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; *address = user->intf->addrinfo[channel].address; + release_ipmi_user(user, index); + return 0; } EXPORT_SYMBOL(ipmi_get_my_address); @@ -1332,9 +1349,17 @@ int ipmi_set_my_LUN(struct ipmi_user *user, unsigned int channel, unsigned char LUN) { + int index; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; user->intf->addrinfo[channel].lun = LUN & 0x3; + release_ipmi_user(user, index); + return 0; } EXPORT_SYMBOL(ipmi_set_my_LUN); @@ -1343,21 +1368,34 @@ int ipmi_get_my_LUN(struct ipmi_user *user, unsigned int channel, unsigned char *address) { + int index; + + user = acquire_ipmi_user(user, &index); + if (!user) + return -ENODEV; + if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; *address = user->intf->addrinfo[channel].lun; + release_ipmi_user(user, index); + return 0; } EXPORT_SYMBOL(ipmi_get_my_LUN); 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; } @@ -1372,10 +1410,14 @@ static void maintenance_mode_update(struct ipmi_smi *intf) int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) { - int rv = 0; + int rv = 0, index; unsigned long flags; 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) { switch (mode) { @@ -1402,6 +1444,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); return rv; } @@ -1413,6 +1456,11 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val) 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); @@ -1462,6 +1510,7 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val) out: spin_unlock_irqrestore(&intf->events_lock, flags); + release_ipmi_user(user, index); return 0; } @@ -1504,8 +1553,11 @@ int ipmi_register_for_cmd(struct ipmi_user *user, { 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) @@ -1531,6 +1583,7 @@ int ipmi_register_for_cmd(struct ipmi_user *user, mutex_unlock(&intf->cmd_rcvrs_mutex); if (rv) kfree(rcvr); + release_ipmi_user(user, index); return rv; } @@ -1544,7 +1597,11 @@ 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; + 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++) { @@ -1565,12 +1622,14 @@ int ipmi_unregister_for_cmd(struct ipmi_user *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); @@ -2065,8 +2124,10 @@ static int i_ipmi_request(struct ipmi_user *user, recv_msg = supplied_recv; else { recv_msg = ipmi_alloc_recv_msg(); - if (recv_msg == NULL) - return -ENOMEM; + if (recv_msg == NULL) { + rv = -ENOMEM; + goto out; + } } recv_msg->user_msg_data = user_msg_data; @@ -2076,7 +2137,8 @@ static int i_ipmi_request(struct ipmi_user *user, smi_msg = ipmi_alloc_smi_msg(); if (smi_msg == NULL) { ipmi_free_recv_msg(recv_msg); - return -ENOMEM; + rv = -ENOMEM; + goto out; } } @@ -2088,6 +2150,7 @@ static int i_ipmi_request(struct ipmi_user *user, recv_msg->user = user; if (user) + /* The put happens when the message is freed. */ kref_get(&user->refcount); recv_msg->msgid = msgid; /* @@ -2123,6 +2186,7 @@ out_err: } rcu_read_unlock(); +out: return rv; } @@ -2148,25 +2212,32 @@ int ipmi_request_settime(struct ipmi_user *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); @@ -2180,25 +2251,32 @@ int ipmi_request_supply_msgs(struct ipmi_user *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); @@ -3455,6 +3533,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); @@ -3507,7 +3592,6 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, intf->proc_dir = NULL; #endif - mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); /* Look for a hole in the numbers. */ i = 0; @@ -3552,11 +3636,10 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, 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 { /* @@ -3567,9 +3650,9 @@ 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; @@ -3631,45 +3714,49 @@ static void cleanup_smi_msgs(struct ipmi_smi *intf) int ipmi_unregister_smi(struct ipmi_smi *intf) { struct ipmi_smi_watcher *w; - int intf_num = intf->intf_num; - struct ipmi_user *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(); - - 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); + synchronize_srcu(&ipmi_interfaces_srcu); -#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); + + if (intf->handlers->shutdown) + intf->handlers->shutdown(intf->send_info); + + cleanup_smi_msgs(intf); + +#ifdef CONFIG_IPMI_PROC_INTERFACE + remove_proc_entries(intf); +#endif + ipmi_bmc_unregister(intf); + + cleanup_srcu_struct(&intf->users_srcu); kref_put(&intf->refcount, intf_free); + return 0; } EXPORT_SYMBOL(ipmi_unregister_smi); @@ -4141,8 +4228,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; - int deliver_count = 0; + int rv = 0, deliver_count = 0, index; unsigned long flags; if (msg->rsp_size < 19) { @@ -4166,7 +4252,7 @@ static int handle_read_event_rsp(struct ipmi_smi *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; @@ -4195,7 +4281,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, kref_get(&user->refcount); 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. */ @@ -4242,7 +4328,7 @@ 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) { @@ -4251,30 +4337,19 @@ static int handle_bmc_rsp(struct ipmi_smi *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_local_response(intf, 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; } @@ -4327,7 +4402,7 @@ static int handle_one_recv_msg(struct ipmi_smi *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) @@ -4342,10 +4417,6 @@ static int handle_one_recv_msg(struct ipmi_smi *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; @@ -4488,14 +4559,15 @@ static void handle_new_recv_msgs(struct ipmi_smi *intf) */ if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { 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); } } @@ -4662,8 +4734,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, 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; @@ -4721,8 +4792,7 @@ static void check_msg_timeout(struct ipmi_smi *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); @@ -4730,7 +4800,7 @@ static void check_msg_timeout(struct ipmi_smi *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); @@ -4822,12 +4892,12 @@ static atomic_t stop_operation; static void ipmi_timeout(struct timer_list *unused) { struct ipmi_smi *intf; - int nt = 0; + 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; @@ -4850,7 +4920,7 @@ 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); |