diff options
author | Lars Ellenberg <lars.ellenberg@linbit.com> | 2010-09-03 04:00:09 -0400 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2010-10-14 12:38:30 -0400 |
commit | ee15b038164fcf19b798021762dee3cf5cbc6433 (patch) | |
tree | 8d2cca781af96296a591a14928ece247fc682ef3 /drivers/block/drbd/drbd_main.c | |
parent | 63106d3c6c769b6219bd04edde513b12abae3f61 (diff) |
drbd: fix race on meta-data update, addendum
addendum to baa33ae4eaa4477b60af7c434c0ddd1d182c1ae7
The race:
drbd_md_sync()
if (!test_and_clear_bit(MD_DIRTY, &mdev->flags))
return;
==> RACE with drbd_md_mark_dirty() rearming the timer.
del_timer(&mdev->md_sync_timer);
Fixed by moving the del_timer before the test_and_clear_bit.
Additionally only rearm the timer in drbd_md_mark_dirty, if MD_DIRTY was
not already set, reduce the grace period from five to one second, and
add an ifdef'ed debuging aid to find code paths missing an explicit
drbd_md_sync, if any, as those are the only relevant ones for this race.
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block/drbd/drbd_main.c')
-rw-r--r-- | drivers/block/drbd/drbd_main.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 5dd071e5c921..ab1244e0045c 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c | |||
@@ -3402,9 +3402,10 @@ void drbd_md_sync(struct drbd_conf *mdev) | |||
3402 | sector_t sector; | 3402 | sector_t sector; |
3403 | int i; | 3403 | int i; |
3404 | 3404 | ||
3405 | del_timer(&mdev->md_sync_timer); | ||
3406 | /* timer may be rearmed by drbd_md_mark_dirty() now. */ | ||
3405 | if (!test_and_clear_bit(MD_DIRTY, &mdev->flags)) | 3407 | if (!test_and_clear_bit(MD_DIRTY, &mdev->flags)) |
3406 | return; | 3408 | return; |
3407 | del_timer(&mdev->md_sync_timer); | ||
3408 | 3409 | ||
3409 | /* We use here D_FAILED and not D_ATTACHING because we try to write | 3410 | /* We use here D_FAILED and not D_ATTACHING because we try to write |
3410 | * metadata even if we detach due to a disk failure! */ | 3411 | * metadata even if we detach due to a disk failure! */ |
@@ -3529,12 +3530,22 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev) | |||
3529 | * the meta-data super block. This function sets MD_DIRTY, and starts a | 3530 | * the meta-data super block. This function sets MD_DIRTY, and starts a |
3530 | * timer that ensures that within five seconds you have to call drbd_md_sync(). | 3531 | * timer that ensures that within five seconds you have to call drbd_md_sync(). |
3531 | */ | 3532 | */ |
3533 | #ifdef DRBD_DEBUG_MD_SYNC | ||
3534 | void drbd_md_mark_dirty_(struct drbd_conf *mdev, unsigned int line, const char *func) | ||
3535 | { | ||
3536 | if (!test_and_set_bit(MD_DIRTY, &mdev->flags)) { | ||
3537 | mod_timer(&mdev->md_sync_timer, jiffies + HZ); | ||
3538 | mdev->last_md_mark_dirty.line = line; | ||
3539 | mdev->last_md_mark_dirty.func = func; | ||
3540 | } | ||
3541 | } | ||
3542 | #else | ||
3532 | void drbd_md_mark_dirty(struct drbd_conf *mdev) | 3543 | void drbd_md_mark_dirty(struct drbd_conf *mdev) |
3533 | { | 3544 | { |
3534 | set_bit(MD_DIRTY, &mdev->flags); | 3545 | if (!test_and_set_bit(MD_DIRTY, &mdev->flags)) |
3535 | mod_timer(&mdev->md_sync_timer, jiffies + 5*HZ); | 3546 | mod_timer(&mdev->md_sync_timer, jiffies + HZ); |
3536 | } | 3547 | } |
3537 | 3548 | #endif | |
3538 | 3549 | ||
3539 | static void drbd_uuid_move_history(struct drbd_conf *mdev) __must_hold(local) | 3550 | static void drbd_uuid_move_history(struct drbd_conf *mdev) __must_hold(local) |
3540 | { | 3551 | { |
@@ -3775,8 +3786,11 @@ static void md_sync_timer_fn(unsigned long data) | |||
3775 | static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused) | 3786 | static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused) |
3776 | { | 3787 | { |
3777 | dev_warn(DEV, "md_sync_timer expired! Worker calls drbd_md_sync().\n"); | 3788 | dev_warn(DEV, "md_sync_timer expired! Worker calls drbd_md_sync().\n"); |
3789 | #ifdef DEBUG | ||
3790 | dev_warn(DEV, "last md_mark_dirty: %s:%u\n", | ||
3791 | mdev->last_md_mark_dirty.func, mdev->last_md_mark_dirty.line); | ||
3792 | #endif | ||
3778 | drbd_md_sync(mdev); | 3793 | drbd_md_sync(mdev); |
3779 | |||
3780 | return 1; | 3794 | return 1; |
3781 | } | 3795 | } |
3782 | 3796 | ||