. stable fix for dm-thin that avoids normal IO racing with discard

. stable fix for a dm-cache related bug in dm-btree walking code that
   results from using very large fast device (e.g. 4T) with a very small
   cache blocksize (e.g. 32K) -- this is a very uncommon configuration
 
 . a couple fixes for dm-raid (one for stable and the other addresses a
   crash in 3.18-rc1 code)
 
 . stable fix for dm-thinp that addresses a very rare dm-bufio bug having
   to do with memory reclaimation (via shrinker) when using dm-thinp
   ontop of loopback devices
 
 . fix a leak in dm-stripe target constructor's error path
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJUY/p7AAoJEMUj8QotnQNaxPEIAJsmJC5ujQAIdm5yUxsOWruU
 Y/36HbPvlmV8fgWqGyjaubBrzqgWry/yW/u/Sv9+9rE3Zh6JSVLVrCA6uZZ3Yr+j
 HKYEPjm/O0zVJepfEDKtjG6dxeaql47+luwU1iP1bAYeZE3zmKn1oFT2GW5gTbxO
 2n3MiN/dyX8v0cTw6r0O69luIAu93CSY0XDk+1ynfKlKKVmgcAUPvKuobF+yHXoF
 Rd7KTqFoK6HgRhdUHvUQnCGDandZ9MHjt3oW9p3dv3ezvW1cNUARoVHMRGG6Awfu
 WZkQ/VORDeaJT+bhjGfPIla1HbgxEKJrgzTUlpj+P6K2uPK2f6ECEyBpDLWKy9g=
 =lkSu
 -----END PGP SIGNATURE-----

Merge tag 'dm-3.18-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm

Pull device mapper fixes from Mike Snitzer:

 - stable fix for dm-thin that avoids normal IO racing with discard

 - stable fix for a dm-cache related bug in dm-btree walking code that
   results from using very large fast device (eg 4T) with a very small
   cache blocksize (eg 32K) -- this is a very uncommon configuration

 - a couple fixes for dm-raid (one for stable and the other addresses a
   crash in 3.18-rc1 code)

 - stable fix for dm-thinp that addresses a very rare dm-bufio bug
   having to do with memory reclaimation (via shrinker) when using
   dm-thinp ontop of loopback devices

 - fix a leak in dm-stripe target constructor's error path

* tag 'dm-3.18-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm btree: fix a recursion depth bug in btree walking code
  dm thin: grab a virtual cell before looking up the mapping
  dm raid: fix inaccessible superblocks causing oops in configure_discard_support
  dm raid: ensure superblock's size matches device's logical block size
  dm bufio: change __GFP_IO to __GFP_FS in shrinker callbacks
  dm stripe: fix potential for leak in stripe_ctr error path
This commit is contained in:
Linus Torvalds 2014-11-13 09:19:20 -08:00
commit 5a7a662cc6
7 changed files with 50 additions and 31 deletions

View File

@ -1434,9 +1434,9 @@ static void drop_buffers(struct dm_bufio_client *c)
/* /*
* Test if the buffer is unused and too old, and commit it. * Test if the buffer is unused and too old, and commit it.
* At if noio is set, we must not do any I/O because we hold * And if GFP_NOFS is used, we must not do any I/O because we hold
* dm_bufio_clients_lock and we would risk deadlock if the I/O gets rerouted to * dm_bufio_clients_lock and we would risk deadlock if the I/O gets
* different bufio client. * rerouted to different bufio client.
*/ */
static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp, static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
unsigned long max_jiffies) unsigned long max_jiffies)
@ -1444,7 +1444,7 @@ static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
if (jiffies - b->last_accessed < max_jiffies) if (jiffies - b->last_accessed < max_jiffies)
return 0; return 0;
if (!(gfp & __GFP_IO)) { if (!(gfp & __GFP_FS)) {
if (test_bit(B_READING, &b->state) || if (test_bit(B_READING, &b->state) ||
test_bit(B_WRITING, &b->state) || test_bit(B_WRITING, &b->state) ||
test_bit(B_DIRTY, &b->state)) test_bit(B_DIRTY, &b->state))
@ -1486,7 +1486,7 @@ dm_bufio_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
unsigned long freed; unsigned long freed;
c = container_of(shrink, struct dm_bufio_client, shrinker); c = container_of(shrink, struct dm_bufio_client, shrinker);
if (sc->gfp_mask & __GFP_IO) if (sc->gfp_mask & __GFP_FS)
dm_bufio_lock(c); dm_bufio_lock(c);
else if (!dm_bufio_trylock(c)) else if (!dm_bufio_trylock(c))
return SHRINK_STOP; return SHRINK_STOP;
@ -1503,7 +1503,7 @@ dm_bufio_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
unsigned long count; unsigned long count;
c = container_of(shrink, struct dm_bufio_client, shrinker); c = container_of(shrink, struct dm_bufio_client, shrinker);
if (sc->gfp_mask & __GFP_IO) if (sc->gfp_mask & __GFP_FS)
dm_bufio_lock(c); dm_bufio_lock(c);
else if (!dm_bufio_trylock(c)) else if (!dm_bufio_trylock(c))
return 0; return 0;

View File

@ -789,8 +789,7 @@ struct dm_raid_superblock {
__le32 layout; __le32 layout;
__le32 stripe_sectors; __le32 stripe_sectors;
__u8 pad[452]; /* Round struct to 512 bytes. */ /* Remainder of a logical block is zero-filled when writing (see super_sync()). */
/* Always set to 0 when writing. */
} __packed; } __packed;
static int read_disk_sb(struct md_rdev *rdev, int size) static int read_disk_sb(struct md_rdev *rdev, int size)
@ -827,7 +826,7 @@ static void super_sync(struct mddev *mddev, struct md_rdev *rdev)
test_bit(Faulty, &(rs->dev[i].rdev.flags))) test_bit(Faulty, &(rs->dev[i].rdev.flags)))
failed_devices |= (1ULL << i); failed_devices |= (1ULL << i);
memset(sb, 0, sizeof(*sb)); memset(sb + 1, 0, rdev->sb_size - sizeof(*sb));
sb->magic = cpu_to_le32(DM_RAID_MAGIC); sb->magic = cpu_to_le32(DM_RAID_MAGIC);
sb->features = cpu_to_le32(0); /* No features yet */ sb->features = cpu_to_le32(0); /* No features yet */
@ -862,7 +861,11 @@ static int super_load(struct md_rdev *rdev, struct md_rdev *refdev)
uint64_t events_sb, events_refsb; uint64_t events_sb, events_refsb;
rdev->sb_start = 0; rdev->sb_start = 0;
rdev->sb_size = sizeof(*sb); rdev->sb_size = bdev_logical_block_size(rdev->meta_bdev);
if (rdev->sb_size < sizeof(*sb) || rdev->sb_size > PAGE_SIZE) {
DMERR("superblock size of a logical block is no longer valid");
return -EINVAL;
}
ret = read_disk_sb(rdev, rdev->sb_size); ret = read_disk_sb(rdev, rdev->sb_size);
if (ret) if (ret)
@ -1169,8 +1172,12 @@ static void configure_discard_support(struct dm_target *ti, struct raid_set *rs)
raid456 = (rs->md.level == 4 || rs->md.level == 5 || rs->md.level == 6); raid456 = (rs->md.level == 4 || rs->md.level == 5 || rs->md.level == 6);
for (i = 0; i < rs->md.raid_disks; i++) { for (i = 0; i < rs->md.raid_disks; i++) {
struct request_queue *q = bdev_get_queue(rs->dev[i].rdev.bdev); struct request_queue *q;
if (!rs->dev[i].rdev.bdev)
continue;
q = bdev_get_queue(rs->dev[i].rdev.bdev);
if (!q || !blk_queue_discard(q)) if (!q || !blk_queue_discard(q))
return; return;

View File

@ -159,8 +159,10 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
sc->stripes_shift = __ffs(stripes); sc->stripes_shift = __ffs(stripes);
r = dm_set_target_max_io_len(ti, chunk_size); r = dm_set_target_max_io_len(ti, chunk_size);
if (r) if (r) {
kfree(sc);
return r; return r;
}
ti->num_flush_bios = stripes; ti->num_flush_bios = stripes;
ti->num_discard_bios = stripes; ti->num_discard_bios = stripes;

View File

@ -1936,6 +1936,14 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
return DM_MAPIO_SUBMITTED; return DM_MAPIO_SUBMITTED;
} }
/*
* We must hold the virtual cell before doing the lookup, otherwise
* there's a race with discard.
*/
build_virtual_key(tc->td, block, &key);
if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
return DM_MAPIO_SUBMITTED;
r = dm_thin_find_block(td, block, 0, &result); r = dm_thin_find_block(td, block, 0, &result);
/* /*
@ -1959,13 +1967,10 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
* shared flag will be set in their case. * shared flag will be set in their case.
*/ */
thin_defer_bio(tc, bio); thin_defer_bio(tc, bio);
cell_defer_no_holder_no_free(tc, &cell1);
return DM_MAPIO_SUBMITTED; return DM_MAPIO_SUBMITTED;
} }
build_virtual_key(tc->td, block, &key);
if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
return DM_MAPIO_SUBMITTED;
build_data_key(tc->td, result.block, &key); build_data_key(tc->td, result.block, &key);
if (dm_bio_detain(tc->pool->prison, &key, bio, &cell2, &cell_result)) { if (dm_bio_detain(tc->pool->prison, &key, bio, &cell2, &cell_result)) {
cell_defer_no_holder_no_free(tc, &cell1); cell_defer_no_holder_no_free(tc, &cell1);
@ -1986,6 +1991,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
* of doing so. * of doing so.
*/ */
handle_unserviceable_bio(tc->pool, bio); handle_unserviceable_bio(tc->pool, bio);
cell_defer_no_holder_no_free(tc, &cell1);
return DM_MAPIO_SUBMITTED; return DM_MAPIO_SUBMITTED;
} }
/* fall through */ /* fall through */
@ -1996,6 +2002,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
* provide the hint to load the metadata into cache. * provide the hint to load the metadata into cache.
*/ */
thin_defer_bio(tc, bio); thin_defer_bio(tc, bio);
cell_defer_no_holder_no_free(tc, &cell1);
return DM_MAPIO_SUBMITTED; return DM_MAPIO_SUBMITTED;
default: default:
@ -2005,6 +2012,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
* pool is switched to fail-io mode. * pool is switched to fail-io mode.
*/ */
bio_io_error(bio); bio_io_error(bio);
cell_defer_no_holder_no_free(tc, &cell1);
return DM_MAPIO_SUBMITTED; return DM_MAPIO_SUBMITTED;
} }
} }

View File

@ -42,6 +42,12 @@ struct btree_node {
} __packed; } __packed;
/*
* Locks a block using the btree node validator.
*/
int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
struct dm_block **result);
void inc_children(struct dm_transaction_manager *tm, struct btree_node *n, void inc_children(struct dm_transaction_manager *tm, struct btree_node *n,
struct dm_btree_value_type *vt); struct dm_btree_value_type *vt);

View File

@ -92,7 +92,7 @@ struct dm_block_validator btree_node_validator = {
/*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/
static int bn_read_lock(struct dm_btree_info *info, dm_block_t b, int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
struct dm_block **result) struct dm_block **result)
{ {
return dm_tm_read_lock(info->tm, b, &btree_node_validator, result); return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);

View File

@ -847,22 +847,26 @@ EXPORT_SYMBOL_GPL(dm_btree_find_lowest_key);
* FIXME: We shouldn't use a recursive algorithm when we have limited stack * FIXME: We shouldn't use a recursive algorithm when we have limited stack
* space. Also this only works for single level trees. * space. Also this only works for single level trees.
*/ */
static int walk_node(struct ro_spine *s, dm_block_t block, static int walk_node(struct dm_btree_info *info, dm_block_t block,
int (*fn)(void *context, uint64_t *keys, void *leaf), int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context) void *context)
{ {
int r; int r;
unsigned i, nr; unsigned i, nr;
struct dm_block *node;
struct btree_node *n; struct btree_node *n;
uint64_t keys; uint64_t keys;
r = ro_step(s, block); r = bn_read_lock(info, block, &node);
n = ro_node(s); if (r)
return r;
n = dm_block_data(node);
nr = le32_to_cpu(n->header.nr_entries); nr = le32_to_cpu(n->header.nr_entries);
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++) {
if (le32_to_cpu(n->header.flags) & INTERNAL_NODE) { if (le32_to_cpu(n->header.flags) & INTERNAL_NODE) {
r = walk_node(s, value64(n, i), fn, context); r = walk_node(info, value64(n, i), fn, context);
if (r) if (r)
goto out; goto out;
} else { } else {
@ -874,7 +878,7 @@ static int walk_node(struct ro_spine *s, dm_block_t block,
} }
out: out:
ro_pop(s); dm_tm_unlock(info->tm, node);
return r; return r;
} }
@ -882,15 +886,7 @@ int dm_btree_walk(struct dm_btree_info *info, dm_block_t root,
int (*fn)(void *context, uint64_t *keys, void *leaf), int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context) void *context)
{ {
int r;
struct ro_spine spine;
BUG_ON(info->levels > 1); BUG_ON(info->levels > 1);
return walk_node(info, root, fn, context);
init_ro_spine(&spine, info);
r = walk_node(&spine, root, fn, context);
exit_ro_spine(&spine);
return r;
} }
EXPORT_SYMBOL_GPL(dm_btree_walk); EXPORT_SYMBOL_GPL(dm_btree_walk);