aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Ellenberg <lars.ellenberg@linbit.com>2015-06-08 08:48:38 -0400
committerJens Axboe <axboe@fb.com>2015-11-25 11:22:03 -0500
commit8011e2490907c267e8be02a549246082d537e082 (patch)
tree129376759401915b665e06c5f65c91fe65c90494
parent5f7c01249bea67c32a1a1551a8f2fe0b8b801ab4 (diff)
drbd: fix error path during resize
In case the lower level device size changed, but some other internal details of the resize did not work out, drbd_determine_dev_size() would try to restore the previous settings, trusting drbd_md_set_sector_offsets() to "do the right thing", but overlooked that this internally may set the meta data base offset based on device size. This could end up with incomplete on-disk meta data layout change, and ultimately lead to data corruption (if the failure was not noticed or ignored by the operator, and other things go wrong as well). Just remember all meta data related offsets/sizes, and on error restore them all. Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com> Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r--drivers/block/drbd/drbd_nl.c68
1 files changed, 38 insertions, 30 deletions
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index f4ca27359541..c055c5e12f24 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -891,12 +891,18 @@ void drbd_resume_io(struct drbd_device *device)
891enum determine_dev_size 891enum determine_dev_size
892drbd_determine_dev_size(struct drbd_device *device, enum dds_flags flags, struct resize_parms *rs) __must_hold(local) 892drbd_determine_dev_size(struct drbd_device *device, enum dds_flags flags, struct resize_parms *rs) __must_hold(local)
893{ 893{
894 sector_t prev_first_sect, prev_size; /* previous meta location */ 894 struct md_offsets_and_sizes {
895 sector_t la_size_sect, u_size; 895 u64 last_agreed_sect;
896 u64 md_offset;
897 s32 al_offset;
898 s32 bm_offset;
899 u32 md_size_sect;
900
901 u32 al_stripes;
902 u32 al_stripe_size_4k;
903 } prev;
904 sector_t u_size, size;
896 struct drbd_md *md = &device->ldev->md; 905 struct drbd_md *md = &device->ldev->md;
897 u32 prev_al_stripe_size_4k;
898 u32 prev_al_stripes;
899 sector_t size;
900 char ppb[10]; 906 char ppb[10];
901 void *buffer; 907 void *buffer;
902 908
@@ -918,16 +924,17 @@ drbd_determine_dev_size(struct drbd_device *device, enum dds_flags flags, struct
918 return DS_ERROR; 924 return DS_ERROR;
919 } 925 }
920 926
921 prev_first_sect = drbd_md_first_sector(device->ldev); 927 /* remember current offset and sizes */
922 prev_size = device->ldev->md.md_size_sect; 928 prev.last_agreed_sect = md->la_size_sect;
923 la_size_sect = device->ldev->md.la_size_sect; 929 prev.md_offset = md->md_offset;
930 prev.al_offset = md->al_offset;
931 prev.bm_offset = md->bm_offset;
932 prev.md_size_sect = md->md_size_sect;
933 prev.al_stripes = md->al_stripes;
934 prev.al_stripe_size_4k = md->al_stripe_size_4k;
924 935
925 if (rs) { 936 if (rs) {
926 /* rs is non NULL if we should change the AL layout only */ 937 /* rs is non NULL if we should change the AL layout only */
927
928 prev_al_stripes = md->al_stripes;
929 prev_al_stripe_size_4k = md->al_stripe_size_4k;
930
931 md->al_stripes = rs->al_stripes; 938 md->al_stripes = rs->al_stripes;
932 md->al_stripe_size_4k = rs->al_stripe_size / 4; 939 md->al_stripe_size_4k = rs->al_stripe_size / 4;
933 md->al_size_4k = (u64)rs->al_stripes * rs->al_stripe_size / 4; 940 md->al_size_4k = (u64)rs->al_stripes * rs->al_stripe_size / 4;
@@ -940,7 +947,7 @@ drbd_determine_dev_size(struct drbd_device *device, enum dds_flags flags, struct
940 rcu_read_unlock(); 947 rcu_read_unlock();
941 size = drbd_new_dev_size(device, device->ldev, u_size, flags & DDSF_FORCED); 948 size = drbd_new_dev_size(device, device->ldev, u_size, flags & DDSF_FORCED);
942 949
943 if (size < la_size_sect) { 950 if (size < prev.last_agreed_sect) {
944 if (rs && u_size == 0) { 951 if (rs && u_size == 0) {
945 /* Remove "rs &&" later. This check should always be active, but 952 /* Remove "rs &&" later. This check should always be active, but
946 right now the receiver expects the permissive behavior */ 953 right now the receiver expects the permissive behavior */
@@ -961,30 +968,29 @@ drbd_determine_dev_size(struct drbd_device *device, enum dds_flags flags, struct
961 err = drbd_bm_resize(device, size, !(flags & DDSF_NO_RESYNC)); 968 err = drbd_bm_resize(device, size, !(flags & DDSF_NO_RESYNC));
962 if (unlikely(err)) { 969 if (unlikely(err)) {
963 /* currently there is only one error: ENOMEM! */ 970 /* currently there is only one error: ENOMEM! */
964 size = drbd_bm_capacity(device)>>1; 971 size = drbd_bm_capacity(device);
965 if (size == 0) { 972 if (size == 0) {
966 drbd_err(device, "OUT OF MEMORY! " 973 drbd_err(device, "OUT OF MEMORY! "
967 "Could not allocate bitmap!\n"); 974 "Could not allocate bitmap!\n");
968 } else { 975 } else {
969 drbd_err(device, "BM resizing failed. " 976 drbd_err(device, "BM resizing failed. "
970 "Leaving size unchanged at size = %lu KB\n", 977 "Leaving size unchanged\n");
971 (unsigned long)size);
972 } 978 }
973 rv = DS_ERROR; 979 rv = DS_ERROR;
974 } 980 }
975 /* racy, see comments above. */ 981 /* racy, see comments above. */
976 drbd_set_my_capacity(device, size); 982 drbd_set_my_capacity(device, size);
977 device->ldev->md.la_size_sect = size; 983 md->la_size_sect = size;
978 drbd_info(device, "size = %s (%llu KB)\n", ppsize(ppb, size>>1), 984 drbd_info(device, "size = %s (%llu KB)\n", ppsize(ppb, size>>1),
979 (unsigned long long)size>>1); 985 (unsigned long long)size>>1);
980 } 986 }
981 if (rv <= DS_ERROR) 987 if (rv <= DS_ERROR)
982 goto err_out; 988 goto err_out;
983 989
984 la_size_changed = (la_size_sect != device->ldev->md.la_size_sect); 990 la_size_changed = (prev.last_agreed_sect != md->la_size_sect);
985 991
986 md_moved = prev_first_sect != drbd_md_first_sector(device->ldev) 992 md_moved = prev.md_offset != md->md_offset
987 || prev_size != device->ldev->md.md_size_sect; 993 || prev.md_size_sect != md->md_size_sect;
988 994
989 if (la_size_changed || md_moved || rs) { 995 if (la_size_changed || md_moved || rs) {
990 u32 prev_flags; 996 u32 prev_flags;
@@ -1024,20 +1030,22 @@ drbd_determine_dev_size(struct drbd_device *device, enum dds_flags flags, struct
1024 md->al_stripes, md->al_stripe_size_4k * 4); 1030 md->al_stripes, md->al_stripe_size_4k * 4);
1025 } 1031 }
1026 1032
1027 if (size > la_size_sect) 1033 if (size > prev.last_agreed_sect)
1028 rv = la_size_sect ? DS_GREW : DS_GREW_FROM_ZERO; 1034 rv = prev.last_agreed_sect ? DS_GREW : DS_GREW_FROM_ZERO;
1029 if (size < la_size_sect) 1035 if (size < prev.last_agreed_sect)
1030 rv = DS_SHRUNK; 1036 rv = DS_SHRUNK;
1031 1037
1032 if (0) { 1038 if (0) {
1033 err_out: 1039 err_out:
1034 if (rs) { 1040 /* restore previous offset and sizes */
1035 md->al_stripes = prev_al_stripes; 1041 md->la_size_sect = prev.last_agreed_sect;
1036 md->al_stripe_size_4k = prev_al_stripe_size_4k; 1042 md->md_offset = prev.md_offset;
1037 md->al_size_4k = (u64)prev_al_stripes * prev_al_stripe_size_4k; 1043 md->al_offset = prev.al_offset;
1038 1044 md->bm_offset = prev.bm_offset;
1039 drbd_md_set_sector_offsets(device, device->ldev); 1045 md->md_size_sect = prev.md_size_sect;
1040 } 1046 md->al_stripes = prev.al_stripes;
1047 md->al_stripe_size_4k = prev.al_stripe_size_4k;
1048 md->al_size_4k = (u64)prev.al_stripes * prev.al_stripe_size_4k;
1041 } 1049 }
1042 lc_unlock(device->act_log); 1050 lc_unlock(device->act_log);
1043 wake_up(&device->al_wait); 1051 wake_up(&device->al_wait);