aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJun'ichi Nomura <j-nomura@ce.jp.nec.com>2007-12-13 09:15:25 -0500
committerAlasdair G Kergon <agk@redhat.com>2007-12-20 12:32:08 -0500
commit512875bd9661368da6f993205a61213b79ba1df0 (patch)
tree7a2e010060b6233cd02e2e36b62f5dcaa96c2c36
parentfbdcf18df73758b2e187ab94678b30cd5f6ff9f9 (diff)
dm: table detect io beyond device
This patch fixes a panic on shrinking a DM device if there is outstanding I/O to the part of the device that is being removed. (Normally this doesn't happen - a filesystem would be resized first, for example.) The bug is that __clone_and_map() assumes dm_table_find_target() always returns a valid pointer. It may fail if a bio arrives from the block layer but its target sector is no longer included in the DM btree. This patch appends an empty entry to table->targets[] which will be returned by a lookup beyond the end of the device. After calling dm_table_find_target(), __clone_and_map() and target_message() check for this condition using dm_target_is_valid(). Sample test script to trigger oops:
-rw-r--r--drivers/md/dm-ioctl.c10
-rw-r--r--drivers/md/dm-table.c7
-rw-r--r--drivers/md/dm.c24
-rw-r--r--drivers/md/dm.h5
4 files changed, 32 insertions, 14 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 138200bf5e0b..be730fdd4830 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1250,21 +1250,17 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
1250 if (!table) 1250 if (!table)
1251 goto out_argv; 1251 goto out_argv;
1252 1252
1253 if (tmsg->sector >= dm_table_get_size(table)) { 1253 ti = dm_table_find_target(table, tmsg->sector);
1254 if (!dm_target_is_valid(ti)) {
1254 DMWARN("Target message sector outside device."); 1255 DMWARN("Target message sector outside device.");
1255 r = -EINVAL; 1256 r = -EINVAL;
1256 goto out_table; 1257 } else if (ti->type->message)
1257 }
1258
1259 ti = dm_table_find_target(table, tmsg->sector);
1260 if (ti->type->message)
1261 r = ti->type->message(ti, argc, argv); 1258 r = ti->type->message(ti, argc, argv);
1262 else { 1259 else {
1263 DMWARN("Target type does not support messages"); 1260 DMWARN("Target type does not support messages");
1264 r = -EINVAL; 1261 r = -EINVAL;
1265 } 1262 }
1266 1263
1267 out_table:
1268 dm_table_put(table); 1264 dm_table_put(table);
1269 out_argv: 1265 out_argv:
1270 kfree(argv); 1266 kfree(argv);
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index e298d8d11f24..f3f952e347ed 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -189,8 +189,10 @@ static int alloc_targets(struct dm_table *t, unsigned int num)
189 189
190 /* 190 /*
191 * Allocate both the target array and offset array at once. 191 * Allocate both the target array and offset array at once.
192 * Append an empty entry to catch sectors beyond the end of
193 * the device.
192 */ 194 */
193 n_highs = (sector_t *) dm_vcalloc(num, sizeof(struct dm_target) + 195 n_highs = (sector_t *) dm_vcalloc(num + 1, sizeof(struct dm_target) +
194 sizeof(sector_t)); 196 sizeof(sector_t));
195 if (!n_highs) 197 if (!n_highs)
196 return -ENOMEM; 198 return -ENOMEM;
@@ -867,6 +869,9 @@ struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
867 869
868/* 870/*
869 * Search the btree for the correct target. 871 * Search the btree for the correct target.
872 *
873 * Caller should check returned pointer with dm_target_is_valid()
874 * to trap I/O beyond end of device.
870 */ 875 */
871struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector) 876struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
872{ 877{
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 07cbbb8eb3e0..cff2a714c107 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -672,13 +672,19 @@ static struct bio *clone_bio(struct bio *bio, sector_t sector,
672 return clone; 672 return clone;
673} 673}
674 674
675static void __clone_and_map(struct clone_info *ci) 675static int __clone_and_map(struct clone_info *ci)
676{ 676{
677 struct bio *clone, *bio = ci->bio; 677 struct bio *clone, *bio = ci->bio;
678 struct dm_target *ti = dm_table_find_target(ci->map, ci->sector); 678 struct dm_target *ti;
679 sector_t len = 0, max = max_io_len(ci->md, ci->sector, ti); 679 sector_t len = 0, max;
680 struct dm_target_io *tio; 680 struct dm_target_io *tio;
681 681
682 ti = dm_table_find_target(ci->map, ci->sector);
683 if (!dm_target_is_valid(ti))
684 return -EIO;
685
686 max = max_io_len(ci->md, ci->sector, ti);
687
682 /* 688 /*
683 * Allocate a target io object. 689 * Allocate a target io object.
684 */ 690 */
@@ -736,6 +742,9 @@ static void __clone_and_map(struct clone_info *ci)
736 do { 742 do {
737 if (offset) { 743 if (offset) {
738 ti = dm_table_find_target(ci->map, ci->sector); 744 ti = dm_table_find_target(ci->map, ci->sector);
745 if (!dm_target_is_valid(ti))
746 return -EIO;
747
739 max = max_io_len(ci->md, ci->sector, ti); 748 max = max_io_len(ci->md, ci->sector, ti);
740 749
741 tio = alloc_tio(ci->md); 750 tio = alloc_tio(ci->md);
@@ -759,6 +768,8 @@ static void __clone_and_map(struct clone_info *ci)
759 768
760 ci->idx++; 769 ci->idx++;
761 } 770 }
771
772 return 0;
762} 773}
763 774
764/* 775/*
@@ -767,6 +778,7 @@ static void __clone_and_map(struct clone_info *ci)
767static int __split_bio(struct mapped_device *md, struct bio *bio) 778static int __split_bio(struct mapped_device *md, struct bio *bio)
768{ 779{
769 struct clone_info ci; 780 struct clone_info ci;
781 int error = 0;
770 782
771 ci.map = dm_get_table(md); 783 ci.map = dm_get_table(md);
772 if (unlikely(!ci.map)) 784 if (unlikely(!ci.map))
@@ -784,11 +796,11 @@ static int __split_bio(struct mapped_device *md, struct bio *bio)
784 ci.idx = bio->bi_idx; 796 ci.idx = bio->bi_idx;
785 797
786 start_io_acct(ci.io); 798 start_io_acct(ci.io);
787 while (ci.sector_count) 799 while (ci.sector_count && !error)
788 __clone_and_map(&ci); 800 error = __clone_and_map(&ci);
789 801
790 /* drop the extra reference count */ 802 /* drop the extra reference count */
791 dec_pending(ci.io, 0); 803 dec_pending(ci.io, error);
792 dm_table_put(ci.map); 804 dm_table_put(ci.map);
793 805
794 return 0; 806 return 0;
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 4b3faa45277e..177297a88ebd 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -112,6 +112,11 @@ int dm_table_resume_targets(struct dm_table *t);
112int dm_table_any_congested(struct dm_table *t, int bdi_bits); 112int dm_table_any_congested(struct dm_table *t, int bdi_bits);
113void dm_table_unplug_all(struct dm_table *t); 113void dm_table_unplug_all(struct dm_table *t);
114 114
115/*
116 * To check the return value from dm_table_find_target().
117 */
118#define dm_target_is_valid(t) ((t)->table)
119
115/*----------------------------------------------------------------- 120/*-----------------------------------------------------------------
116 * A registry of target types. 121 * A registry of target types.
117 *---------------------------------------------------------------*/ 122 *---------------------------------------------------------------*/