diff options
author | Corey Minyard <minyard@acm.org> | 2007-02-10 04:45:45 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-11 13:51:31 -0500 |
commit | 78ba2faf71c63990cba9997f18cf1d610e06e3f2 (patch) | |
tree | 8cf435b8ade77c358dc3c4cc6a3349d65702170f /drivers | |
parent | 3678d62f028689abc8ac5693b254e48f605f94ba (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')
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 29 |
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 |