Skip to content

Commit

Permalink
dm persistent data: fix allocation failure in space map checker init
Browse files Browse the repository at this point in the history
If CONFIG_DM_DEBUG_SPACE_MAPS is enabled and memory is fragmented and a
sufficiently-large metadata device is used in a thin pool then the space
map checker will fail to allocate the memory it requires.

Switch from kmalloc to vmalloc to allow larger virtually contiguous
allocations for the space map checker's internal count arrays.

Reported-by: Vivek Goyal <vgoyal@redhat.com>
Cc: stable@kernel.org
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
  • Loading branch information
Mike Snitzer authored and Alasdair G Kergon committed Jul 3, 2012
1 parent 6266230 commit b0239fa
Showing 1 changed file with 19 additions and 11 deletions.
30 changes: 19 additions & 11 deletions drivers/md/persistent-data/dm-space-map-checker.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <linux/device-mapper.h>
#include <linux/export.h>
#include <linux/vmalloc.h>

#ifdef CONFIG_DM_DEBUG_SPACE_MAPS

Expand Down Expand Up @@ -89,13 +90,23 @@ static int ca_create(struct count_array *ca, struct dm_space_map *sm)

ca->nr = nr_blocks;
ca->nr_free = nr_blocks;
ca->counts = kzalloc(sizeof(*ca->counts) * nr_blocks, GFP_KERNEL);
if (!ca->counts)
return -ENOMEM;

if (!nr_blocks)
ca->counts = NULL;
else {
ca->counts = vzalloc(sizeof(*ca->counts) * nr_blocks);
if (!ca->counts)
return -ENOMEM;
}

return 0;
}

static void ca_destroy(struct count_array *ca)
{
vfree(ca->counts);
}

static int ca_load(struct count_array *ca, struct dm_space_map *sm)
{
int r;
Expand Down Expand Up @@ -126,12 +137,14 @@ static int ca_load(struct count_array *ca, struct dm_space_map *sm)
static int ca_extend(struct count_array *ca, dm_block_t extra_blocks)
{
dm_block_t nr_blocks = ca->nr + extra_blocks;
uint32_t *counts = kzalloc(sizeof(*counts) * nr_blocks, GFP_KERNEL);
uint32_t *counts = vzalloc(sizeof(*counts) * nr_blocks);
if (!counts)
return -ENOMEM;

memcpy(counts, ca->counts, sizeof(*counts) * ca->nr);
kfree(ca->counts);
if (ca->counts) {
memcpy(counts, ca->counts, sizeof(*counts) * ca->nr);
ca_destroy(ca);
}
ca->nr = nr_blocks;
ca->nr_free += extra_blocks;
ca->counts = counts;
Expand All @@ -151,11 +164,6 @@ static int ca_commit(struct count_array *old, struct count_array *new)
return 0;
}

static void ca_destroy(struct count_array *ca)
{
kfree(ca->counts);
}

/*----------------------------------------------------------------*/

struct sm_checker {
Expand Down

0 comments on commit b0239fa

Please sign in to comment.