diff options
author | Vivek Goyal <vgoyal@redhat.com> | 2010-12-01 13:34:52 -0500 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2010-12-01 13:34:52 -0500 |
commit | 04a6b516cdc6efc2500b52a540cf65be8c5aaf9e (patch) | |
tree | fa5d6675308df0ff42eceeec1b406d2ce5eb2ff6 /block | |
parent | d1ae8ffdfaa16b2ab2e9346e81cf0ab6eaaae347 (diff) |
blk-throttle: Correct the placement of smp_rmb()
o I was discussing what are the variable being updated without spin lock and
why do we need barriers and Oleg pointed out that location of smp_rmb()
should be between read of td->limits_changed and tg->limits_changed. This
patch fixes it.
o Following is one possible sequence of events. Say cpu0 is executing
throtl_update_blkio_group_read_bps() and cpu1 is executing
throtl_process_limit_change().
cpu0 cpu1
tg->limits_changed = true;
smp_mb__before_atomic_inc();
atomic_inc(&td->limits_changed);
if (!atomic_read(&td->limits_changed))
return;
if (tg->limits_changed)
do_something;
If cpu0 has updated tg->limits_changed and td->limits_changed, we want to
make sure that if update to td->limits_changed is visible on cpu1, then
update to tg->limits_changed should also be visible.
Oleg pointed out to ensure that we need to insert an smp_rmb() between
td->limits_changed read and tg->limits_changed read.
o I had erroneously put smp_rmb() before atomic_read(&td->limits_changed).
This patch fixes it.
Reported-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'block')
-rw-r--r-- | block/blk-throttle.c | 23 |
1 files changed, 9 insertions, 14 deletions
diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 2d134b7c40a3..381b09bb562b 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c | |||
@@ -725,26 +725,21 @@ static void throtl_process_limit_change(struct throtl_data *td) | |||
725 | struct throtl_grp *tg; | 725 | struct throtl_grp *tg; |
726 | struct hlist_node *pos, *n; | 726 | struct hlist_node *pos, *n; |
727 | 727 | ||
728 | /* | ||
729 | * Make sure atomic_inc() effects from | ||
730 | * throtl_update_blkio_group_read_bps(), group of functions are | ||
731 | * visible. | ||
732 | * Is this required or smp_mb__after_atomic_inc() was suffcient | ||
733 | * after the atomic_inc(). | ||
734 | */ | ||
735 | smp_rmb(); | ||
736 | if (!atomic_read(&td->limits_changed)) | 728 | if (!atomic_read(&td->limits_changed)) |
737 | return; | 729 | return; |
738 | 730 | ||
739 | throtl_log(td, "limit changed =%d", atomic_read(&td->limits_changed)); | 731 | throtl_log(td, "limit changed =%d", atomic_read(&td->limits_changed)); |
740 | 732 | ||
741 | hlist_for_each_entry_safe(tg, pos, n, &td->tg_list, tg_node) { | 733 | /* |
742 | /* | 734 | * Make sure updates from throtl_update_blkio_group_read_bps() group |
743 | * Do I need an smp_rmb() here to make sure tg->limits_changed | 735 | * of functions to tg->limits_changed are visible. We do not |
744 | * update is visible. I am relying on smp_rmb() at the | 736 | * want update td->limits_changed to be visible but update to |
745 | * beginning of function and not putting a new one here. | 737 | * tg->limits_changed not being visible yet on this cpu. Hence |
746 | */ | 738 | * the read barrier. |
739 | */ | ||
740 | smp_rmb(); | ||
747 | 741 | ||
742 | hlist_for_each_entry_safe(tg, pos, n, &td->tg_list, tg_node) { | ||
748 | if (throtl_tg_on_rr(tg) && tg->limits_changed) { | 743 | if (throtl_tg_on_rr(tg) && tg->limits_changed) { |
749 | throtl_log_tg(td, tg, "limit change rbps=%llu wbps=%llu" | 744 | throtl_log_tg(td, tg, "limit change rbps=%llu wbps=%llu" |
750 | " riops=%u wiops=%u", tg->bps[READ], | 745 | " riops=%u wiops=%u", tg->bps[READ], |