[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_smi_msg_list(&intf->waiting_msgs);
|
||||||
free_recv_msg_list(&intf->waiting_events);
|
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);
|
mutex_lock(&intf->cmd_rcvrs_mutex);
|
||||||
list_add_rcu(&list, &intf->cmd_rcvrs);
|
INIT_LIST_HEAD(&list);
|
||||||
list_del_rcu(&intf->cmd_rcvrs);
|
list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu);
|
||||||
mutex_unlock(&intf->cmd_rcvrs_mutex);
|
mutex_unlock(&intf->cmd_rcvrs_mutex);
|
||||||
synchronize_rcu();
|
|
||||||
|
|
||||||
list_for_each_entry_safe(rcvr, rcvr2, &list, link)
|
list_for_each_entry_safe(rcvr, rcvr2, &list, link)
|
||||||
kfree(rcvr);
|
kfree(rcvr);
|
||||||
|
@ -451,7 +452,7 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
|
||||||
mutex_lock(&ipmi_interfaces_mutex);
|
mutex_lock(&ipmi_interfaces_mutex);
|
||||||
|
|
||||||
/* Build a list of things to deliver. */
|
/* 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)
|
if (intf->intf_num == -1)
|
||||||
continue;
|
continue;
|
||||||
e = kmalloc(sizeof(*e), GFP_KERNEL);
|
e = kmalloc(sizeof(*e), GFP_KERNEL);
|
||||||
|
@ -2760,9 +2761,15 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
kref_put(&intf->refcount, intf_free);
|
kref_put(&intf->refcount, intf_free);
|
||||||
} else {
|
} 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;
|
intf->intf_num = i;
|
||||||
mutex_unlock(&ipmi_interfaces_mutex);
|
mutex_unlock(&ipmi_interfaces_mutex);
|
||||||
|
/* After this point the interface is legal to use. */
|
||||||
call_smi_watchers(i, intf->si_dev);
|
call_smi_watchers(i, intf->si_dev);
|
||||||
mutex_unlock(&smi_watchers_mutex);
|
mutex_unlock(&smi_watchers_mutex);
|
||||||
}
|
}
|
||||||
|
@ -3923,6 +3930,14 @@ static void send_panic_events(char *str)
|
||||||
/* Interface was not ready yet. */
|
/* Interface was not ready yet. */
|
||||||
continue;
|
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
|
/* First job here is to figure out where to send the
|
||||||
OEM events. There's no way in IPMI to send OEM
|
OEM events. There's no way in IPMI to send OEM
|
||||||
events using an event send command, so we have to
|
events using an event send command, so we have to
|
||||||
|
|
Loading…
Reference in New Issue