diff options
-rw-r--r-- | drivers/block/drbd/drbd_int.h | 9 | ||||
-rw-r--r-- | drivers/block/drbd/drbd_state.c | 11 |
2 files changed, 14 insertions, 6 deletions
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index a16f9ae3c98a..a0ffc19ccf0e 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h | |||
@@ -1946,6 +1946,11 @@ static inline bool is_sync_state(enum drbd_conns connection_state) | |||
1946 | 1946 | ||
1947 | static inline void put_ldev(struct drbd_device *device) | 1947 | static inline void put_ldev(struct drbd_device *device) |
1948 | { | 1948 | { |
1949 | enum drbd_disk_state ds = device->state.disk; | ||
1950 | /* We must check the state *before* the atomic_dec becomes visible, | ||
1951 | * or we have a theoretical race where someone hitting zero, | ||
1952 | * while state still D_FAILED, will then see D_DISKLESS in the | ||
1953 | * condition below and calling into destroy, where he must not, yet. */ | ||
1949 | int i = atomic_dec_return(&device->local_cnt); | 1954 | int i = atomic_dec_return(&device->local_cnt); |
1950 | 1955 | ||
1951 | /* This may be called from some endio handler, | 1956 | /* This may be called from some endio handler, |
@@ -1954,10 +1959,10 @@ static inline void put_ldev(struct drbd_device *device) | |||
1954 | __release(local); | 1959 | __release(local); |
1955 | D_ASSERT(device, i >= 0); | 1960 | D_ASSERT(device, i >= 0); |
1956 | if (i == 0) { | 1961 | if (i == 0) { |
1957 | if (device->state.disk == D_DISKLESS) | 1962 | if (ds == D_DISKLESS) |
1958 | /* even internal references gone, safe to destroy */ | 1963 | /* even internal references gone, safe to destroy */ |
1959 | drbd_ldev_destroy(device); | 1964 | drbd_ldev_destroy(device); |
1960 | if (device->state.disk == D_FAILED) { | 1965 | if (ds == D_FAILED) { |
1961 | /* all application IO references gone. */ | 1966 | /* all application IO references gone. */ |
1962 | if (!test_and_set_bit(GO_DISKLESS, &device->flags)) | 1967 | if (!test_and_set_bit(GO_DISKLESS, &device->flags)) |
1963 | drbd_queue_work(&first_peer_device(device)->connection->sender_work, | 1968 | drbd_queue_work(&first_peer_device(device)->connection->sender_work, |
diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c index 1bddd6cf8ac7..6629f4668102 100644 --- a/drivers/block/drbd/drbd_state.c +++ b/drivers/block/drbd/drbd_state.c | |||
@@ -958,7 +958,6 @@ __drbd_set_state(struct drbd_device *device, union drbd_state ns, | |||
958 | enum drbd_state_rv rv = SS_SUCCESS; | 958 | enum drbd_state_rv rv = SS_SUCCESS; |
959 | enum sanitize_state_warnings ssw; | 959 | enum sanitize_state_warnings ssw; |
960 | struct after_state_chg_work *ascw; | 960 | struct after_state_chg_work *ascw; |
961 | bool did_remote, should_do_remote; | ||
962 | 961 | ||
963 | os = drbd_read_state(device); | 962 | os = drbd_read_state(device); |
964 | 963 | ||
@@ -1010,18 +1009,22 @@ __drbd_set_state(struct drbd_device *device, union drbd_state ns, | |||
1010 | (os.disk != D_DISKLESS && ns.disk == D_DISKLESS)) | 1009 | (os.disk != D_DISKLESS && ns.disk == D_DISKLESS)) |
1011 | atomic_inc(&device->local_cnt); | 1010 | atomic_inc(&device->local_cnt); |
1012 | 1011 | ||
1013 | did_remote = drbd_should_do_remote(device->state); | ||
1014 | if (!is_sync_state(os.conn) && is_sync_state(ns.conn)) | 1012 | if (!is_sync_state(os.conn) && is_sync_state(ns.conn)) |
1015 | clear_bit(RS_DONE, &device->flags); | 1013 | clear_bit(RS_DONE, &device->flags); |
1016 | 1014 | ||
1015 | /* changes to local_cnt and device flags should be visible before | ||
1016 | * changes to state, which again should be visible before anything else | ||
1017 | * depending on that change happens. */ | ||
1018 | smp_wmb(); | ||
1017 | device->state.i = ns.i; | 1019 | device->state.i = ns.i; |
1018 | should_do_remote = drbd_should_do_remote(device->state); | ||
1019 | device->resource->susp = ns.susp; | 1020 | device->resource->susp = ns.susp; |
1020 | device->resource->susp_nod = ns.susp_nod; | 1021 | device->resource->susp_nod = ns.susp_nod; |
1021 | device->resource->susp_fen = ns.susp_fen; | 1022 | device->resource->susp_fen = ns.susp_fen; |
1023 | smp_wmb(); | ||
1022 | 1024 | ||
1023 | /* put replicated vs not-replicated requests in seperate epochs */ | 1025 | /* put replicated vs not-replicated requests in seperate epochs */ |
1024 | if (did_remote != should_do_remote) | 1026 | if (drbd_should_do_remote((union drbd_dev_state)os.i) != |
1027 | drbd_should_do_remote((union drbd_dev_state)ns.i)) | ||
1025 | start_new_tl_epoch(connection); | 1028 | start_new_tl_epoch(connection); |
1026 | 1029 | ||
1027 | if (os.disk == D_ATTACHING && ns.disk >= D_NEGOTIATING) | 1030 | if (os.disk == D_ATTACHING && ns.disk >= D_NEGOTIATING) |