[PATCH] IPMI: Fix some RCU problems
Fix some RCU problem pointed out by Paul McKenney of IBM. These are: The wholesale move of the command receivers list into a new list was not safe because the list will point to the new tail during a traversal, so the traversal will never end on a reader if this happens during a read. Memory barriers were needed to handle proper ordering of the setting of the IPMI interface as valid. Readers might not see proper ordering of data otherwise. In ipmi_smi_watcher_register(), the use of the _rcu suffix on the list is unnecessary. This require the list_splice_init_rcu() patch previously posted. Signed-off-by: Corey Minyard <minyard@acm.org> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
3678d62f02
commit
78ba2faf71
|
@ -406,13 +406,14 @@ static void clean_up_interface_data(ipmi_smi_t intf)
|
|||
free_smi_msg_list(&intf->waiting_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. */
|
||||
/*
|
||||
* Wholesale remove all the entries from the list in the
|
||||
* interface and wait for RCU to know that none are in use.
|
||||
*/
|
||||
mutex_lock(&intf->cmd_rcvrs_mutex);
|
||||
list_add_rcu(&list, &intf->cmd_rcvrs);
|
||||
list_del_rcu(&intf->cmd_rcvrs);
|
||||
INIT_LIST_HEAD(&list);
|
||||
list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu);
|
||||
mutex_unlock(&intf->cmd_rcvrs_mutex);
|
||||
synchronize_rcu();
|
||||
|
||||
list_for_each_entry_safe(rcvr, rcvr2, &list, link)
|
||||
kfree(rcvr);
|
||||
|
@ -451,7 +452,7 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
|
|||
mutex_lock(&ipmi_interfaces_mutex);
|
||||
|
||||
/* Build a list of things to deliver. */
|
||||
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
|
||||
list_for_each_entry(intf, &ipmi_interfaces, link) {
|
||||
if (intf->intf_num == -1)
|
||||
continue;
|
||||
e = kmalloc(sizeof(*e), GFP_KERNEL);
|
||||
|
@ -2760,9 +2761,15 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
|
|||
synchronize_rcu();
|
||||
kref_put(&intf->refcount, intf_free);
|
||||
} else {
|
||||
/* After this point the interface is legal to use. */
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
@ -3923,6 +3930,14 @@ static void send_panic_events(char *str)
|
|||
/* 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();
|
||||
|
||||
/* 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
|
||||
|
|
Loading…
Reference in New Issue