aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorCorey Minyard <minyard@acm.org>2007-02-10 04:45:45 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-11 13:51:31 -0500
commit78ba2faf71c63990cba9997f18cf1d610e06e3f2 (patch)
tree8cf435b8ade77c358dc3c4cc6a3349d65702170f /drivers/char
parent3678d62f028689abc8ac5693b254e48f605f94ba (diff)
[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>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c29
1 files changed, 22 insertions, 7 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 53582b53da95..230064ede08d 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -406,13 +406,14 @@ static void clean_up_interface_data(ipmi_smi_t intf)
406 free_smi_msg_list(&intf->waiting_msgs); 406 free_smi_msg_list(&intf->waiting_msgs);
407 free_recv_msg_list(&intf->waiting_events); 407 free_recv_msg_list(&intf->waiting_events);
408 408
409 /* Wholesale remove all the entries from the list in the 409 /*
410 * interface and wait for RCU to know that none are in use. */ 410 * Wholesale remove all the entries from the list in the
411 * interface and wait for RCU to know that none are in use.
412 */
411 mutex_lock(&intf->cmd_rcvrs_mutex); 413 mutex_lock(&intf->cmd_rcvrs_mutex);
412 list_add_rcu(&list, &intf->cmd_rcvrs); 414 INIT_LIST_HEAD(&list);
413 list_del_rcu(&intf->cmd_rcvrs); 415 list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu);
414 mutex_unlock(&intf->cmd_rcvrs_mutex); 416 mutex_unlock(&intf->cmd_rcvrs_mutex);
415 synchronize_rcu();
416 417
417 list_for_each_entry_safe(rcvr, rcvr2, &list, link) 418 list_for_each_entry_safe(rcvr, rcvr2, &list, link)
418 kfree(rcvr); 419 kfree(rcvr);
@@ -451,7 +452,7 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
451 mutex_lock(&ipmi_interfaces_mutex); 452 mutex_lock(&ipmi_interfaces_mutex);
452 453
453 /* Build a list of things to deliver. */ 454 /* Build a list of things to deliver. */
454 list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 455 list_for_each_entry(intf, &ipmi_interfaces, link) {
455 if (intf->intf_num == -1) 456 if (intf->intf_num == -1)
456 continue; 457 continue;
457 e = kmalloc(sizeof(*e), GFP_KERNEL); 458 e = kmalloc(sizeof(*e), GFP_KERNEL);
@@ -2760,9 +2761,15 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
2760 synchronize_rcu(); 2761 synchronize_rcu();
2761 kref_put(&intf->refcount, intf_free); 2762 kref_put(&intf->refcount, intf_free);
2762 } else { 2763 } else {
2763 /* After this point the interface is legal to use. */ 2764 /*
2765 * Keep memory order straight for RCU readers. Make
2766 * sure everything else is committed to memory before
2767 * setting intf_num to mark the interface valid.
2768 */
2769 smp_wmb();
2764 intf->intf_num = i; 2770 intf->intf_num = i;
2765 mutex_unlock(&ipmi_interfaces_mutex); 2771 mutex_unlock(&ipmi_interfaces_mutex);
2772 /* After this point the interface is legal to use. */
2766 call_smi_watchers(i, intf->si_dev); 2773 call_smi_watchers(i, intf->si_dev);
2767 mutex_unlock(&smi_watchers_mutex); 2774 mutex_unlock(&smi_watchers_mutex);
2768 } 2775 }
@@ -3923,6 +3930,14 @@ static void send_panic_events(char *str)
3923 /* Interface was not ready yet. */ 3930 /* Interface was not ready yet. */
3924 continue; 3931 continue;
3925 3932
3933 /*
3934 * intf_num is used as an marker to tell if the
3935 * interface is valid. Thus we need a read barrier to
3936 * make sure data fetched before checking intf_num
3937 * won't be used.
3938 */
3939 smp_rmb();
3940
3926 /* First job here is to figure out where to send the 3941 /* First job here is to figure out where to send the
3927 OEM events. There's no way in IPMI to send OEM 3942 OEM events. There's no way in IPMI to send OEM
3928 events using an event send command, so we have to 3943 events using an event send command, so we have to