summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/maple_tree.c7
-rw-r--r--lib/test_hmm.c49
-rw-r--r--lib/test_meminit.c2
-rw-r--r--lib/test_vmalloc.c62
4 files changed, 108 insertions, 12 deletions
diff --git a/lib/maple_tree.c b/lib/maple_tree.c
index 60ae5e6fc1ee..e52876435b77 100644
--- a/lib/maple_tree.c
+++ b/lib/maple_tree.c
@@ -5727,13 +5727,16 @@ int mtree_store(struct maple_tree *mt, unsigned long index, void *entry,
EXPORT_SYMBOL(mtree_store);
/**
- * mtree_insert_range() - Insert an entry at a given range if there is no value.
+ * mtree_insert_range() - Insert an entry from [first, last] at a given range
+ * if there is no value.
* @mt: The maple tree
* @first: The start of the range
- * @last: The end of the range
+ * @last: The end of the range (inclusive)
* @entry: The entry to store
* @gfp: The GFP_FLAGS to use for allocations.
*
+ * Note that @last is inclusive. That is, @last = @first + length - 1;
+ *
* Return: 0 on success, -EEXISTS if the range is occupied, -EINVAL on invalid
* request, -ENOMEM if memory could not be allocated.
*/
diff --git a/lib/test_hmm.c b/lib/test_hmm.c
index 213504915737..9c59d1ceb5b5 100644
--- a/lib/test_hmm.c
+++ b/lib/test_hmm.c
@@ -1063,6 +1063,25 @@ static vm_fault_t dmirror_devmem_fault_alloc_and_copy(struct migrate_vma *args,
/* Try with smaller pages if large allocation fails */
if (!dpage && order) {
dpage = alloc_page_vma(GFP_HIGHUSER_MOVABLE, args->vma, addr);
+ if (!dpage) {
+ /* Unlock and free pages already allocated. */
+ while (i > 0) {
+ struct page *fpage;
+
+ fpage = migrate_pfn_to_page(dst[--i]);
+ unlock_page(fpage);
+ __free_page(fpage);
+ }
+ /* Clear remaining dst entries to avoid
+ * migrate_vma_pages/finalize() using
+ * uninitialized values.
+ */
+ while (i < (1 << order)) {
+ dst[i] = 0;
+ i++;
+ }
+ return VM_FAULT_OOM;
+ }
lock_page(dpage);
dst[i] = migrate_pfn(page_to_pfn(dpage));
dst_page = pfn_to_page(page_to_pfn(dpage));
@@ -1111,9 +1130,6 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror,
unsigned long *src_pfns;
unsigned long *dst_pfns;
- src_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL);
- dst_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL);
-
start = cmd->addr;
end = start + size;
if (end < start)
@@ -1123,6 +1139,9 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror,
if (!mmget_not_zero(mm))
return -EINVAL;
+ src_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL);
+ dst_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL);
+
cmd->cpages = 0;
mmap_read_lock(mm);
for (addr = start; addr < end; addr = next) {
@@ -1148,7 +1167,11 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror,
goto out;
pr_debug("Migrating from device mem to sys mem\n");
- dmirror_devmem_fault_alloc_and_copy(&args, dmirror);
+ if (dmirror_devmem_fault_alloc_and_copy(&args, dmirror)) {
+ migrate_vma_finalize(&args);
+ ret = -ENOMEM;
+ goto out;
+ }
migrate_vma_pages(&args);
cmd->cpages += dmirror_successful_migrated_pages(&args);
@@ -1253,8 +1276,8 @@ out:
mmap_read_unlock(mm);
mmput(mm);
free_mem:
- kfree(src_pfns);
- kfree(dst_pfns);
+ kvfree(src_pfns);
+ kvfree(dst_pfns);
return ret;
}
@@ -1679,12 +1702,20 @@ static vm_fault_t dmirror_devmem_fault(struct vm_fault *vmf)
if (order)
args.flags |= MIGRATE_VMA_SELECT_COMPOUND;
- if (migrate_vma_setup(&args))
- return VM_FAULT_SIGBUS;
+ /*
+ * In practice migrate_vma_setup() should never fail unless the
+ * test is wrong as it just tests some static VMA properties.
+ */
+ if (migrate_vma_setup(&args)) {
+ ret = VM_FAULT_SIGBUS;
+ goto err;
+ }
ret = dmirror_devmem_fault_alloc_and_copy(&args, dmirror);
- if (ret)
+ if (ret) {
+ migrate_vma_finalize(&args);
goto err;
+ }
migrate_vma_pages(&args);
/*
* No device finalize step is needed since
diff --git a/lib/test_meminit.c b/lib/test_meminit.c
index e106a0c0601a..68c3b9da090e 100644
--- a/lib/test_meminit.c
+++ b/lib/test_meminit.c
@@ -386,7 +386,7 @@ static int __init test_kmemcache(int *total_failures)
ctor = flags & 1;
rcu = flags & 2;
zero = flags & 4;
- if (ctor & zero)
+ if (ctor && zero)
continue;
num_tests += do_kmem_cache_size(size, ctor, rcu, zero,
&failures);
diff --git a/lib/test_vmalloc.c b/lib/test_vmalloc.c
index 876c72c18a0c..b23f85e8f8ca 100644
--- a/lib/test_vmalloc.c
+++ b/lib/test_vmalloc.c
@@ -55,6 +55,7 @@ __param(int, run_test_mask, 7,
"\t\tid: 512, name: kvfree_rcu_2_arg_vmalloc_test\n"
"\t\tid: 1024, name: vm_map_ram_test\n"
"\t\tid: 2048, name: no_block_alloc_test\n"
+ "\t\tid: 4096, name: vrealloc_test\n"
/* Add a new test case description here. */
);
@@ -421,6 +422,66 @@ cleanup:
return nr_allocated != map_nr_pages;
}
+static int vrealloc_test(void)
+{
+ void *ptr, *tmp;
+ int i;
+
+ for (i = 0; i < test_loop_count; i++) {
+ int err = -1;
+
+ ptr = vrealloc(NULL, PAGE_SIZE, GFP_KERNEL);
+ if (!ptr)
+ return -1;
+
+ *((__u8 *)ptr) = 'a';
+
+ /* Grow: beyond allocated pages, triggers full realloc. */
+ tmp = vrealloc(ptr, 4 * PAGE_SIZE, GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ ptr = tmp;
+
+ if (*((__u8 *)ptr) != 'a')
+ goto error;
+
+ /* Shrink: crosses page boundary, frees tail pages. */
+ tmp = vrealloc(ptr, PAGE_SIZE, GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ ptr = tmp;
+
+ if (*((__u8 *)ptr) != 'a')
+ goto error;
+
+ /* Shrink: within same page, no page freeing. */
+ tmp = vrealloc(ptr, PAGE_SIZE / 2, GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ ptr = tmp;
+
+ if (*((__u8 *)ptr) != 'a')
+ goto error;
+
+ /* Grow: within allocated page, in-place, no realloc. */
+ tmp = vrealloc(ptr, PAGE_SIZE, GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ ptr = tmp;
+
+ if (*((__u8 *)ptr) != 'a')
+ goto error;
+
+ err = 0;
+error:
+ vfree(ptr);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
struct test_case_desc {
const char *test_name;
int (*test_func)(void);
@@ -440,6 +501,7 @@ static struct test_case_desc test_case_array[] = {
{ "kvfree_rcu_2_arg_vmalloc_test", kvfree_rcu_2_arg_vmalloc_test, },
{ "vm_map_ram_test", vm_map_ram_test, },
{ "no_block_alloc_test", no_block_alloc_test, true },
+ { "vrealloc_test", vrealloc_test, },
/* Add a new test case here. */
};