diff options
| author | Lars Ellenberg <lars.ellenberg@linbit.com> | 2010-10-16 06:13:47 -0400 |
|---|---|---|
| committer | Philipp Reisner <philipp.reisner@linbit.com> | 2010-10-22 09:46:11 -0400 |
| commit | 82f59cc6353889b426cf13b6596d5a3d100fa09e (patch) | |
| tree | 6d5a678516334f0a37a56a509b84322a0352719b | |
| parent | 3beec1d446fba335f07787636920892dd3b2c658 (diff) | |
drbd: fix potential deadlock on detach
If we have contention in drbd_al_begin_iod (heavy randon IO),
an administrative request to detach the disk may deadlock
for similar reasons as the recently fixed deadlock if detaching
because of IO-error.
The approach taken here is to either go through the intermediate
cleanup state D_FAILED, or first lock out application io,
don't just go directly to D_DISKLESS.
We need an additional state bit (WAS_IO_ERROR) to distinguish
the -> D_FAILED because of IO-error from other failures.
Sanitize D_ATTACHING -> D_FAILED to D_ATTACHING -> D_DISKLESS.
If only attaching, ldev may be missing still, but would be referenced
from within the after_state_ch for -> D_FAILED, potentially
dereferencing a NULL pointer.
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
| -rw-r--r-- | drivers/block/drbd/drbd_int.h | 20 | ||||
| -rw-r--r-- | drivers/block/drbd/drbd_main.c | 138 | ||||
| -rw-r--r-- | drivers/block/drbd/drbd_nl.c | 16 | ||||
| -rw-r--r-- | drivers/block/drbd/drbd_receiver.c | 2 |
4 files changed, 113 insertions, 63 deletions
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index e0e0bf6f16a1..03c15e317c37 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h | |||
| @@ -852,7 +852,8 @@ enum { | |||
| 852 | BITMAP_IO, /* suspend application io; | 852 | BITMAP_IO, /* suspend application io; |
| 853 | once no more io in flight, start bitmap io */ | 853 | once no more io in flight, start bitmap io */ |
| 854 | BITMAP_IO_QUEUED, /* Started bitmap IO */ | 854 | BITMAP_IO_QUEUED, /* Started bitmap IO */ |
| 855 | GO_DISKLESS, /* Disk failed, local_cnt reached zero, we are going diskless */ | 855 | GO_DISKLESS, /* Disk is being detached, on io-error or admin request. */ |
| 856 | WAS_IO_ERROR, /* Local disk failed returned IO error */ | ||
| 856 | RESYNC_AFTER_NEG, /* Resync after online grow after the attach&negotiate finished. */ | 857 | RESYNC_AFTER_NEG, /* Resync after online grow after the attach&negotiate finished. */ |
| 857 | NET_CONGESTED, /* The data socket is congested */ | 858 | NET_CONGESTED, /* The data socket is congested */ |
| 858 | 859 | ||
| @@ -1281,6 +1282,7 @@ extern int drbd_bmio_set_n_write(struct drbd_conf *mdev); | |||
| 1281 | extern int drbd_bmio_clear_n_write(struct drbd_conf *mdev); | 1282 | extern int drbd_bmio_clear_n_write(struct drbd_conf *mdev); |
| 1282 | extern int drbd_bitmap_io(struct drbd_conf *mdev, int (*io_fn)(struct drbd_conf *), char *why); | 1283 | extern int drbd_bitmap_io(struct drbd_conf *mdev, int (*io_fn)(struct drbd_conf *), char *why); |
| 1283 | extern void drbd_go_diskless(struct drbd_conf *mdev); | 1284 | extern void drbd_go_diskless(struct drbd_conf *mdev); |
| 1285 | extern void drbd_ldev_destroy(struct drbd_conf *mdev); | ||
| 1284 | 1286 | ||
| 1285 | 1287 | ||
| 1286 | /* Meta data layout | 1288 | /* Meta data layout |
| @@ -1798,17 +1800,17 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach, | |||
| 1798 | case EP_PASS_ON: | 1800 | case EP_PASS_ON: |
| 1799 | if (!forcedetach) { | 1801 | if (!forcedetach) { |
| 1800 | if (__ratelimit(&drbd_ratelimit_state)) | 1802 | if (__ratelimit(&drbd_ratelimit_state)) |
| 1801 | dev_err(DEV, "Local IO failed in %s." | 1803 | dev_err(DEV, "Local IO failed in %s.\n", where); |
| 1802 | "Passing error on...\n", where); | ||
| 1803 | break; | 1804 | break; |
| 1804 | } | 1805 | } |
| 1805 | /* NOTE fall through to detach case if forcedetach set */ | 1806 | /* NOTE fall through to detach case if forcedetach set */ |
| 1806 | case EP_DETACH: | 1807 | case EP_DETACH: |
| 1807 | case EP_CALL_HELPER: | 1808 | case EP_CALL_HELPER: |
| 1809 | set_bit(WAS_IO_ERROR, &mdev->flags); | ||
| 1808 | if (mdev->state.disk > D_FAILED) { | 1810 | if (mdev->state.disk > D_FAILED) { |
| 1809 | _drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL); | 1811 | _drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL); |
| 1810 | dev_err(DEV, "Local IO failed in %s." | 1812 | dev_err(DEV, |
| 1811 | "Detaching...\n", where); | 1813 | "Local IO failed in %s. Detaching...\n", where); |
| 1812 | } | 1814 | } |
| 1813 | break; | 1815 | break; |
| 1814 | } | 1816 | } |
| @@ -2127,7 +2129,11 @@ static inline void put_ldev(struct drbd_conf *mdev) | |||
| 2127 | __release(local); | 2129 | __release(local); |
| 2128 | D_ASSERT(i >= 0); | 2130 | D_ASSERT(i >= 0); |
| 2129 | if (i == 0) { | 2131 | if (i == 0) { |
| 2132 | if (mdev->state.disk == D_DISKLESS) | ||
| 2133 | /* even internal references gone, safe to destroy */ | ||
| 2134 | drbd_ldev_destroy(mdev); | ||
| 2130 | if (mdev->state.disk == D_FAILED) | 2135 | if (mdev->state.disk == D_FAILED) |
| 2136 | /* all application IO references gone. */ | ||
| 2131 | drbd_go_diskless(mdev); | 2137 | drbd_go_diskless(mdev); |
| 2132 | wake_up(&mdev->misc_wait); | 2138 | wake_up(&mdev->misc_wait); |
| 2133 | } | 2139 | } |
| @@ -2138,6 +2144,10 @@ static inline int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_stat | |||
| 2138 | { | 2144 | { |
| 2139 | int io_allowed; | 2145 | int io_allowed; |
| 2140 | 2146 | ||
| 2147 | /* never get a reference while D_DISKLESS */ | ||
| 2148 | if (mdev->state.disk == D_DISKLESS) | ||
| 2149 | return 0; | ||
| 2150 | |||
| 2141 | atomic_inc(&mdev->local_cnt); | 2151 | atomic_inc(&mdev->local_cnt); |
| 2142 | io_allowed = (mdev->state.disk >= mins); | 2152 | io_allowed = (mdev->state.disk >= mins); |
| 2143 | if (!io_allowed) | 2153 | if (!io_allowed) |
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 8d029b14e7cc..992c3aecdf7e 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c | |||
| @@ -834,6 +834,15 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state | |||
| 834 | ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING && ns.conn <= C_TEAR_DOWN) | 834 | ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING && ns.conn <= C_TEAR_DOWN) |
| 835 | ns.conn = os.conn; | 835 | ns.conn = os.conn; |
| 836 | 836 | ||
| 837 | /* we cannot fail (again) if we already detached */ | ||
| 838 | if (ns.disk == D_FAILED && os.disk == D_DISKLESS) | ||
| 839 | ns.disk = D_DISKLESS; | ||
| 840 | |||
| 841 | /* if we are only D_ATTACHING yet, | ||
| 842 | * we can (and should) go directly to D_DISKLESS. */ | ||
| 843 | if (ns.disk == D_FAILED && os.disk == D_ATTACHING) | ||
| 844 | ns.disk = D_DISKLESS; | ||
| 845 | |||
| 837 | /* After C_DISCONNECTING only C_STANDALONE may follow */ | 846 | /* After C_DISCONNECTING only C_STANDALONE may follow */ |
| 838 | if (os.conn == C_DISCONNECTING && ns.conn != C_STANDALONE) | 847 | if (os.conn == C_DISCONNECTING && ns.conn != C_STANDALONE) |
| 839 | ns.conn = os.conn; | 848 | ns.conn = os.conn; |
| @@ -1055,7 +1064,15 @@ int __drbd_set_state(struct drbd_conf *mdev, | |||
| 1055 | !test_and_set_bit(CONFIG_PENDING, &mdev->flags)) | 1064 | !test_and_set_bit(CONFIG_PENDING, &mdev->flags)) |
| 1056 | set_bit(DEVICE_DYING, &mdev->flags); | 1065 | set_bit(DEVICE_DYING, &mdev->flags); |
| 1057 | 1066 | ||
| 1058 | mdev->state.i = ns.i; | 1067 | /* if we are going -> D_FAILED or D_DISKLESS, grab one extra reference |
| 1068 | * on the ldev here, to be sure the transition -> D_DISKLESS resp. | ||
| 1069 | * drbd_ldev_destroy() won't happen before our corresponding | ||
| 1070 | * after_state_ch works run, where we put_ldev again. */ | ||
| 1071 | if ((os.disk != D_FAILED && ns.disk == D_FAILED) || | ||
| 1072 | (os.disk != D_DISKLESS && ns.disk == D_DISKLESS)) | ||
| 1073 | atomic_inc(&mdev->local_cnt); | ||
| 1074 | |||
| 1075 | mdev->state = ns; | ||
| 1059 | wake_up(&mdev->misc_wait); | 1076 | wake_up(&mdev->misc_wait); |
| 1060 | wake_up(&mdev->state_wait); | 1077 | wake_up(&mdev->state_wait); |
| 1061 | 1078 | ||
| @@ -1363,63 +1380,64 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, | |||
| 1363 | os.disk > D_INCONSISTENT && ns.disk == D_INCONSISTENT) | 1380 | os.disk > D_INCONSISTENT && ns.disk == D_INCONSISTENT) |
| 1364 | drbd_queue_bitmap_io(mdev, &drbd_bmio_set_n_write, NULL, "set_n_write from invalidate"); | 1381 | drbd_queue_bitmap_io(mdev, &drbd_bmio_set_n_write, NULL, "set_n_write from invalidate"); |
| 1365 | 1382 | ||
| 1366 | /* first half of local IO error */ | 1383 | /* first half of local IO error, failure to attach, |
| 1367 | if (os.disk > D_FAILED && ns.disk == D_FAILED) { | 1384 | * or administrative detach */ |
| 1368 | enum drbd_io_error_p eh = EP_PASS_ON; | 1385 | if (os.disk != D_FAILED && ns.disk == D_FAILED) { |
| 1386 | enum drbd_io_error_p eh; | ||
| 1387 | int was_io_error; | ||
| 1388 | /* corresponding get_ldev was in __drbd_set_state, to serialize | ||
| 1389 | * our cleanup here with the transition to D_DISKLESS, | ||
| 1390 | * so it is safe to dreference ldev here. */ | ||
| 1391 | eh = mdev->ldev->dc.on_io_error; | ||
| 1392 | was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags); | ||
| 1393 | |||
| 1394 | /* current state still has to be D_FAILED, | ||
| 1395 | * there is only one way out: to D_DISKLESS, | ||
| 1396 | * and that may only happen after our put_ldev below. */ | ||
| 1397 | if (mdev->state.disk != D_FAILED) | ||
| 1398 | dev_err(DEV, | ||
| 1399 | "ASSERT FAILED: disk is %s during detach\n", | ||
| 1400 | drbd_disk_str(mdev->state.disk)); | ||
| 1369 | 1401 | ||
| 1370 | if (drbd_send_state(mdev)) | 1402 | if (drbd_send_state(mdev)) |
| 1371 | dev_warn(DEV, "Notified peer that my disk is broken.\n"); | 1403 | dev_warn(DEV, "Notified peer that I am detaching my disk\n"); |
| 1372 | else | 1404 | else |
| 1373 | dev_err(DEV, "Sending state for drbd_io_error() failed\n"); | 1405 | dev_err(DEV, "Sending state for detaching disk failed\n"); |
| 1374 | 1406 | ||
| 1375 | drbd_rs_cancel_all(mdev); | 1407 | drbd_rs_cancel_all(mdev); |
| 1376 | 1408 | ||
| 1377 | if (get_ldev_if_state(mdev, D_FAILED)) { | 1409 | /* In case we want to get something to stable storage still, |
| 1378 | eh = mdev->ldev->dc.on_io_error; | 1410 | * this may be the last chance. |
| 1379 | put_ldev(mdev); | 1411 | * Following put_ldev may transition to D_DISKLESS. */ |
| 1380 | } | 1412 | drbd_md_sync(mdev); |
| 1381 | if (eh == EP_CALL_HELPER) | 1413 | put_ldev(mdev); |
| 1414 | |||
| 1415 | if (was_io_error && eh == EP_CALL_HELPER) | ||
| 1382 | drbd_khelper(mdev, "local-io-error"); | 1416 | drbd_khelper(mdev, "local-io-error"); |
| 1383 | } | 1417 | } |
| 1384 | 1418 | ||
| 1419 | /* second half of local IO error, failure to attach, | ||
| 1420 | * or administrative detach, | ||
| 1421 | * after local_cnt references have reached zero again */ | ||
| 1422 | if (os.disk != D_DISKLESS && ns.disk == D_DISKLESS) { | ||
| 1423 | /* We must still be diskless, | ||
| 1424 | * re-attach has to be serialized with this! */ | ||
| 1425 | if (mdev->state.disk != D_DISKLESS) | ||
| 1426 | dev_err(DEV, | ||
| 1427 | "ASSERT FAILED: disk is %s while going diskless\n", | ||
| 1428 | drbd_disk_str(mdev->state.disk)); | ||
| 1385 | 1429 | ||
| 1386 | /* second half of local IO error handling, | 1430 | mdev->rs_total = 0; |
| 1387 | * after local_cnt references have reached zero: */ | 1431 | mdev->rs_failed = 0; |
| 1388 | if (os.disk == D_FAILED && ns.disk == D_DISKLESS) { | 1432 | atomic_set(&mdev->rs_pending_cnt, 0); |
| 1389 | mdev->rs_total = 0; | ||
| 1390 | mdev->rs_failed = 0; | ||
| 1391 | atomic_set(&mdev->rs_pending_cnt, 0); | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | if (os.disk > D_DISKLESS && ns.disk == D_DISKLESS) { | ||
| 1395 | /* We must still be diskless, | ||
| 1396 | * re-attach has to be serialized with this! */ | ||
| 1397 | if (mdev->state.disk != D_DISKLESS) | ||
| 1398 | dev_err(DEV, | ||
| 1399 | "ASSERT FAILED: disk is %s while going diskless\n", | ||
| 1400 | drbd_disk_str(mdev->state.disk)); | ||
| 1401 | 1433 | ||
| 1402 | /* we cannot assert local_cnt == 0 here, as get_ldev_if_state | ||
| 1403 | * will inc/dec it frequently. Since we became D_DISKLESS, no | ||
| 1404 | * one has touched the protected members anymore, though, so we | ||
| 1405 | * are safe to free them here. */ | ||
| 1406 | if (drbd_send_state(mdev)) | 1434 | if (drbd_send_state(mdev)) |
| 1407 | dev_warn(DEV, "Notified peer that I detached my disk.\n"); | 1435 | dev_warn(DEV, "Notified peer that I'm now diskless.\n"); |
| 1408 | else | 1436 | else |
| 1409 | dev_err(DEV, "Sending state for detach failed\n"); | 1437 | dev_err(DEV, "Sending state for being diskless failed\n"); |
| 1410 | 1438 | /* corresponding get_ldev in __drbd_set_state | |
| 1411 | lc_destroy(mdev->resync); | 1439 | * this may finaly trigger drbd_ldev_destroy. */ |
| 1412 | mdev->resync = NULL; | 1440 | put_ldev(mdev); |
| 1413 | lc_destroy(mdev->act_log); | ||
| 1414 | mdev->act_log = NULL; | ||
| 1415 | __no_warn(local, | ||
| 1416 | drbd_free_bc(mdev->ldev); | ||
| 1417 | mdev->ldev = NULL;); | ||
| 1418 | |||
| 1419 | if (mdev->md_io_tmpp) { | ||
| 1420 | __free_page(mdev->md_io_tmpp); | ||
| 1421 | mdev->md_io_tmpp = NULL; | ||
| 1422 | } | ||
| 1423 | } | 1441 | } |
| 1424 | 1442 | ||
| 1425 | /* Disks got bigger while they were detached */ | 1443 | /* Disks got bigger while they were detached */ |
| @@ -2897,7 +2915,6 @@ void drbd_mdev_cleanup(struct drbd_conf *mdev) | |||
| 2897 | D_ASSERT(list_empty(&mdev->resync_work.list)); | 2915 | D_ASSERT(list_empty(&mdev->resync_work.list)); |
| 2898 | D_ASSERT(list_empty(&mdev->unplug_work.list)); | 2916 | D_ASSERT(list_empty(&mdev->unplug_work.list)); |
| 2899 | D_ASSERT(list_empty(&mdev->go_diskless.list)); | 2917 | D_ASSERT(list_empty(&mdev->go_diskless.list)); |
| 2900 | |||
| 2901 | } | 2918 | } |
| 2902 | 2919 | ||
| 2903 | 2920 | ||
| @@ -3756,19 +3773,31 @@ static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused) | |||
| 3756 | return 1; | 3773 | return 1; |
| 3757 | } | 3774 | } |
| 3758 | 3775 | ||
| 3776 | void drbd_ldev_destroy(struct drbd_conf *mdev) | ||
| 3777 | { | ||
| 3778 | lc_destroy(mdev->resync); | ||
| 3779 | mdev->resync = NULL; | ||
| 3780 | lc_destroy(mdev->act_log); | ||
| 3781 | mdev->act_log = NULL; | ||
| 3782 | __no_warn(local, | ||
| 3783 | drbd_free_bc(mdev->ldev); | ||
| 3784 | mdev->ldev = NULL;); | ||
| 3785 | |||
| 3786 | if (mdev->md_io_tmpp) { | ||
| 3787 | __free_page(mdev->md_io_tmpp); | ||
| 3788 | mdev->md_io_tmpp = NULL; | ||
| 3789 | } | ||
| 3790 | clear_bit(GO_DISKLESS, &mdev->flags); | ||
| 3791 | } | ||
| 3792 | |||
| 3759 | static int w_go_diskless(struct drbd_conf *mdev, struct drbd_work *w, int unused) | 3793 | static int w_go_diskless(struct drbd_conf *mdev, struct drbd_work *w, int unused) |
| 3760 | { | 3794 | { |
| 3761 | D_ASSERT(mdev->state.disk == D_FAILED); | 3795 | D_ASSERT(mdev->state.disk == D_FAILED); |
| 3762 | /* we cannot assert local_cnt == 0 here, as get_ldev_if_state will | 3796 | /* we cannot assert local_cnt == 0 here, as get_ldev_if_state will |
| 3763 | * inc/dec it frequently. Once we are D_DISKLESS, no one will touch | 3797 | * inc/dec it frequently. Once we are D_DISKLESS, no one will touch |
| 3764 | * the protected members anymore, though, so in the after_state_ch work | 3798 | * the protected members anymore, though, so once put_ldev reaches zero |
| 3765 | * it will be safe to free them. */ | 3799 | * again, it will be safe to free them. */ |
| 3766 | drbd_force_state(mdev, NS(disk, D_DISKLESS)); | 3800 | drbd_force_state(mdev, NS(disk, D_DISKLESS)); |
| 3767 | /* We need to wait for return of references checked out while we still | ||
| 3768 | * have been D_FAILED, though (drbd_md_sync, bitmap io). */ | ||
| 3769 | wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); | ||
| 3770 | |||
| 3771 | clear_bit(GO_DISKLESS, &mdev->flags); | ||
| 3772 | return 1; | 3801 | return 1; |
| 3773 | } | 3802 | } |
| 3774 | 3803 | ||
| @@ -3777,9 +3806,6 @@ void drbd_go_diskless(struct drbd_conf *mdev) | |||
| 3777 | D_ASSERT(mdev->state.disk == D_FAILED); | 3806 | D_ASSERT(mdev->state.disk == D_FAILED); |
| 3778 | if (!test_and_set_bit(GO_DISKLESS, &mdev->flags)) | 3807 | if (!test_and_set_bit(GO_DISKLESS, &mdev->flags)) |
| 3779 | drbd_queue_work(&mdev->data.work, &mdev->go_diskless); | 3808 | drbd_queue_work(&mdev->data.work, &mdev->go_diskless); |
| 3780 | /* don't drbd_queue_work_front, | ||
| 3781 | * we need to serialize with the after_state_ch work | ||
| 3782 | * of the -> D_FAILED transition. */ | ||
| 3783 | } | 3809 | } |
| 3784 | 3810 | ||
| 3785 | /** | 3811 | /** |
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index c498c4827de4..0cba7d3d2b5d 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c | |||
| @@ -870,6 +870,11 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp | |||
| 870 | retcode = ERR_DISK_CONFIGURED; | 870 | retcode = ERR_DISK_CONFIGURED; |
| 871 | goto fail; | 871 | goto fail; |
| 872 | } | 872 | } |
| 873 | /* It may just now have detached because of IO error. Make sure | ||
| 874 | * drbd_ldev_destroy is done already, we may end up here very fast, | ||
| 875 | * e.g. if someone calls attach from the on-io-error handler, | ||
| 876 | * to realize a "hot spare" feature (not that I'd recommend that) */ | ||
| 877 | wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); | ||
| 873 | 878 | ||
| 874 | /* allocation not in the IO path, cqueue thread context */ | 879 | /* allocation not in the IO path, cqueue thread context */ |
| 875 | nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL); | 880 | nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL); |
| @@ -1262,7 +1267,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp | |||
| 1262 | force_diskless_dec: | 1267 | force_diskless_dec: |
| 1263 | put_ldev(mdev); | 1268 | put_ldev(mdev); |
| 1264 | force_diskless: | 1269 | force_diskless: |
| 1265 | drbd_force_state(mdev, NS(disk, D_DISKLESS)); | 1270 | drbd_force_state(mdev, NS(disk, D_FAILED)); |
| 1266 | drbd_md_sync(mdev); | 1271 | drbd_md_sync(mdev); |
| 1267 | release_bdev2_fail: | 1272 | release_bdev2_fail: |
| 1268 | if (nbc) | 1273 | if (nbc) |
| @@ -1285,10 +1290,19 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp | |||
| 1285 | return 0; | 1290 | return 0; |
| 1286 | } | 1291 | } |
| 1287 | 1292 | ||
| 1293 | /* Detaching the disk is a process in multiple stages. First we need to lock | ||
| 1294 | * out application IO, in-flight IO, IO stuck in drbd_al_begin_io. | ||
| 1295 | * Then we transition to D_DISKLESS, and wait for put_ldev() to return all | ||
| 1296 | * internal references as well. | ||
| 1297 | * Only then we have finally detached. */ | ||
| 1288 | static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, | 1298 | static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, |
| 1289 | struct drbd_nl_cfg_reply *reply) | 1299 | struct drbd_nl_cfg_reply *reply) |
| 1290 | { | 1300 | { |
| 1301 | drbd_suspend_io(mdev); /* so no-one is stuck in drbd_al_begin_io */ | ||
| 1291 | reply->ret_code = drbd_request_state(mdev, NS(disk, D_DISKLESS)); | 1302 | reply->ret_code = drbd_request_state(mdev, NS(disk, D_DISKLESS)); |
| 1303 | if (mdev->state.disk == D_DISKLESS) | ||
| 1304 | wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); | ||
| 1305 | drbd_resume_io(mdev); | ||
| 1292 | return 0; | 1306 | return 0; |
| 1293 | } | 1307 | } |
| 1294 | 1308 | ||
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 6ec922c623a1..04a823b01da5 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c | |||
| @@ -3363,7 +3363,7 @@ static int receive_state(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned | |||
| 3363 | if (ns.conn == C_MASK) { | 3363 | if (ns.conn == C_MASK) { |
| 3364 | ns.conn = C_CONNECTED; | 3364 | ns.conn = C_CONNECTED; |
| 3365 | if (mdev->state.disk == D_NEGOTIATING) { | 3365 | if (mdev->state.disk == D_NEGOTIATING) { |
| 3366 | drbd_force_state(mdev, NS(disk, D_DISKLESS)); | 3366 | drbd_force_state(mdev, NS(disk, D_FAILED)); |
| 3367 | } else if (peer_state.disk == D_NEGOTIATING) { | 3367 | } else if (peer_state.disk == D_NEGOTIATING) { |
| 3368 | dev_err(DEV, "Disk attach process on the peer node was aborted.\n"); | 3368 | dev_err(DEV, "Disk attach process on the peer node was aborted.\n"); |
| 3369 | peer_state.disk = D_DISKLESS; | 3369 | peer_state.disk = D_DISKLESS; |
