Commit b0239faa authored by Mike Snitzer's avatar Mike Snitzer Committed by Alasdair G Kergon

dm persistent data: fix allocation failure in space map checker init

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: default avatarVivek Goyal <vgoyal@redhat.com>
Cc: stable@kernel.org
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
parent 62662303
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/device-mapper.h> #include <linux/device-mapper.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_DM_DEBUG_SPACE_MAPS #ifdef CONFIG_DM_DEBUG_SPACE_MAPS
...@@ -89,13 +90,23 @@ static int ca_create(struct count_array *ca, struct dm_space_map *sm) ...@@ -89,13 +90,23 @@ static int ca_create(struct count_array *ca, struct dm_space_map *sm)
ca->nr = nr_blocks; ca->nr = nr_blocks;
ca->nr_free = nr_blocks; ca->nr_free = nr_blocks;
ca->counts = kzalloc(sizeof(*ca->counts) * nr_blocks, GFP_KERNEL);
if (!nr_blocks)
ca->counts = NULL;
else {
ca->counts = vzalloc(sizeof(*ca->counts) * nr_blocks);
if (!ca->counts) if (!ca->counts)
return -ENOMEM; return -ENOMEM;
}
return 0; 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) static int ca_load(struct count_array *ca, struct dm_space_map *sm)
{ {
int r; int r;
...@@ -126,12 +137,14 @@ static int ca_load(struct count_array *ca, struct dm_space_map *sm) ...@@ -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) static int ca_extend(struct count_array *ca, dm_block_t extra_blocks)
{ {
dm_block_t nr_blocks = ca->nr + 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) if (!counts)
return -ENOMEM; return -ENOMEM;
if (ca->counts) {
memcpy(counts, ca->counts, sizeof(*counts) * ca->nr); memcpy(counts, ca->counts, sizeof(*counts) * ca->nr);
kfree(ca->counts); ca_destroy(ca);
}
ca->nr = nr_blocks; ca->nr = nr_blocks;
ca->nr_free += extra_blocks; ca->nr_free += extra_blocks;
ca->counts = counts; ca->counts = counts;
...@@ -151,11 +164,6 @@ static int ca_commit(struct count_array *old, struct count_array *new) ...@@ -151,11 +164,6 @@ static int ca_commit(struct count_array *old, struct count_array *new)
return 0; return 0;
} }
static void ca_destroy(struct count_array *ca)
{
kfree(ca->counts);
}
/*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/
struct sm_checker { struct sm_checker {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment