aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorLars Ellenberg <lars.ellenberg@linbit.com>2011-05-02 05:51:31 -0400
committerPhilipp Reisner <philipp.reisner@linbit.com>2011-05-24 04:14:32 -0400
commit9a0d9d0389ef769e4b01abf50fcc11407706270b (patch)
treee766296ff1e684a0a081a6961ec664a4131faf74 /drivers/block
parent99432fcc528d7a5ac8494a4c07ad4726670c96e2 (diff)
drbd: fix schedule in atomic
An administrative detach used to request a state change directly to D_DISKLESS, first suspending IO to avoid the last put_ldev() occuring from an endio handler, potentially in irq context. This is not enough on the receiving side (typically secondary), we may miss some peer_req on the way to local disk, which then may do the last put_ldev() from their drbd_peer_request_endio(). This patch makes the detach always go through the intermediate D_FAILED state. We may consider to rename it D_DETACHING. Alternative approach would be to create yet an other work item to be scheduled on the worker, do the destructor work from there, and get the timing right. manually picked commit 564040f from the drbd 8.4 branch. Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/drbd/drbd_int.h4
-rw-r--r--drivers/block/drbd/drbd_nl.c14
2 files changed, 15 insertions, 3 deletions
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index 8aa10391115b..a74d3ee04ba8 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -2157,6 +2157,10 @@ static inline int get_net_conf(struct drbd_conf *mdev)
2157static inline void put_ldev(struct drbd_conf *mdev) 2157static inline void put_ldev(struct drbd_conf *mdev)
2158{ 2158{
2159 int i = atomic_dec_return(&mdev->local_cnt); 2159 int i = atomic_dec_return(&mdev->local_cnt);
2160
2161 /* This may be called from some endio handler,
2162 * so we must not sleep here. */
2163
2160 __release(local); 2164 __release(local);
2161 D_ASSERT(i >= 0); 2165 D_ASSERT(i >= 0);
2162 if (i == 0) { 2166 if (i == 0) {
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 7c64ec042124..13569635b922 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -1334,11 +1334,19 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
1334static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, 1334static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
1335 struct drbd_nl_cfg_reply *reply) 1335 struct drbd_nl_cfg_reply *reply)
1336{ 1336{
1337 enum drbd_ret_code retcode;
1338 int ret;
1337 drbd_suspend_io(mdev); /* so no-one is stuck in drbd_al_begin_io */ 1339 drbd_suspend_io(mdev); /* so no-one is stuck in drbd_al_begin_io */
1338 reply->ret_code = drbd_request_state(mdev, NS(disk, D_DISKLESS)); 1340 retcode = drbd_request_state(mdev, NS(disk, D_FAILED));
1339 if (mdev->state.disk == D_DISKLESS) 1341 /* D_FAILED will transition to DISKLESS. */
1340 wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); 1342 ret = wait_event_interruptible(mdev->misc_wait,
1343 mdev->state.disk != D_FAILED);
1341 drbd_resume_io(mdev); 1344 drbd_resume_io(mdev);
1345 if (retcode == SS_IS_DISKLESS)
1346 retcode = SS_NOTHING_TO_DO;
1347 if (ret)
1348 retcode = ERR_INTR;
1349 reply->ret_code = retcode;
1342 return 0; 1350 return 0;
1343} 1351}
1344 1352