gpu: nvgpu: Fix buddy_allocator mutex, logical bugs

Bugs in current version are listed below:
1. Function alloc() or alloc_pte() allocate memory for len=0.
2. Function alloc() or alloc_pte() don't unlock nvgpu_allocator if
pte_size is invalid.
3. Function alloc_pte() and alloc_fixed() set alloc_made flag
unconditionally.
4. Function release_carveout() tries to acquire nvgpu_allocator lock
twice causing unresponsive state.
5. If buddy allocation fails in balloc_do_alloc() or
balloc_do_alloc_fixed() function, previously allocated buddies are not
merged. This causes seg fault in ops->fini().
6. With gva_space enabled and base=0, buddy_allocator updated base not
checked for pde alignment.
7. In balloc_do_alloc_fixed(), align_order computed using __fls()
results in one order higher than requested.
8. Initializing buddy allocator with size=0, initializes very large
memory and will trigger seg fault with the changes in this patch.
Setting size=1G so that further execution is successful.

This patch fixes above listed bugs and updates following:
1. With gva_space enabled, BALLOC_PTE_SIZE_ANY is considered as
BALLOC_PTE_SIZE_SMALL which allows alloc() to be used.
2. GPU_BALLOC_MAX_ORDER changed to 63U. Condition added to check that
max_order is never greater than GPU_BALLOC_MAX_ORDER.
3. BUG() changed to nvgpu_do_assert().

JIRA NVGPU-3005

Change-Id: I20c28e20aa3404976d67f7884b4f8cbd5c908ba7
Signed-off-by: Vedashree Vidwans <vvidwans@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/2075646
Reviewed-by: svc-misra-checker <svc-misra-checker@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Alex Waterman <alexw@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Vedashree Vidwans
2019-03-18 09:48:34 -07:00
committed by mobile promotions
parent 3437bbee09
commit e05e655dd4
2 changed files with 61 additions and 34 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@@ -78,6 +78,9 @@ static u32 nvgpu_balloc_page_size_to_pte_size(struct nvgpu_buddy_allocator *a,
return BALLOC_PTE_SIZE_BIG; return BALLOC_PTE_SIZE_BIG;
} else if (page_size == SZ_4K) { } else if (page_size == SZ_4K) {
return BALLOC_PTE_SIZE_SMALL; return BALLOC_PTE_SIZE_SMALL;
} else if (page_size == BALLOC_PTE_SIZE_ANY) {
/* With gva_space enabled, only 2 types of PTE sizes allowed */
return BALLOC_PTE_SIZE_SMALL;
} else { } else {
return BALLOC_PTE_SIZE_INVALID; return BALLOC_PTE_SIZE_INVALID;
} }
@@ -93,17 +96,15 @@ static void balloc_compute_max_order(struct nvgpu_buddy_allocator *a)
{ {
u64 true_max_order = ilog2(a->blks); u64 true_max_order = ilog2(a->blks);
if (a->max_order == 0U) { if (true_max_order > GPU_BALLOC_MAX_ORDER) {
a->max_order = true_max_order; alloc_dbg(balloc_owner(a),
return; "Oops: Can't manage more than 1 Exabyte memory");
nvgpu_do_assert();
} }
if (a->max_order > true_max_order) { if ((a->max_order == 0ULL) || (a->max_order > true_max_order)) {
a->max_order = true_max_order; a->max_order = true_max_order;
} }
if (a->max_order > GPU_BALLOC_MAX_ORDER) {
a->max_order = GPU_BALLOC_MAX_ORDER;
}
} }
/* /*
@@ -152,7 +153,7 @@ static void balloc_buddy_list_do_add(struct nvgpu_buddy_allocator *a,
alloc_dbg(balloc_owner(a), alloc_dbg(balloc_owner(a),
"Oops: adding added buddy (%llu:0x%llx)", "Oops: adding added buddy (%llu:0x%llx)",
b->order, b->start); b->order, b->start);
BUG(); nvgpu_do_assert();
} }
/* /*
@@ -177,7 +178,7 @@ static void balloc_buddy_list_do_rem(struct nvgpu_buddy_allocator *a,
alloc_dbg(balloc_owner(a), alloc_dbg(balloc_owner(a),
"Oops: removing removed buddy (%llu:0x%llx)", "Oops: removing removed buddy (%llu:0x%llx)",
b->order, b->start); b->order, b->start);
BUG(); nvgpu_do_assert();
} }
nvgpu_list_del(&b->buddy_entry); nvgpu_list_del(&b->buddy_entry);
@@ -333,19 +334,19 @@ static void nvgpu_buddy_allocator_destroy(struct nvgpu_allocator *na)
nvgpu_info(na->g, nvgpu_info(na->g,
"Excess buddies!!! (%d: %llu)", "Excess buddies!!! (%d: %llu)",
i, a->buddy_list_len[i]); i, a->buddy_list_len[i]);
BUG(); nvgpu_do_assert();
} }
if (a->buddy_list_split[i] != 0U) { if (a->buddy_list_split[i] != 0U) {
nvgpu_info(na->g, nvgpu_info(na->g,
"Excess split nodes!!! (%d: %llu)", "Excess split nodes!!! (%d: %llu)",
i, a->buddy_list_split[i]); i, a->buddy_list_split[i]);
BUG(); nvgpu_do_assert();
} }
if (a->buddy_list_alloced[i] != 0U) { if (a->buddy_list_alloced[i] != 0U) {
nvgpu_info(na->g, nvgpu_info(na->g,
"Excess alloced nodes!!! (%d: %llu)", "Excess alloced nodes!!! (%d: %llu)",
i, a->buddy_list_alloced[i]); i, a->buddy_list_alloced[i]);
BUG(); nvgpu_do_assert();
} }
} }
@@ -565,6 +566,7 @@ static u64 balloc_do_alloc(struct nvgpu_buddy_allocator *a,
while (bud->order != order) { while (bud->order != order) {
if (balloc_split_buddy(a, bud, pte_size) != 0) { if (balloc_split_buddy(a, bud, pte_size) != 0) {
balloc_coalesce(a, bud);
return 0; /* No mem... */ return 0; /* No mem... */
} }
bud = bud->left; bud = bud->left;
@@ -762,11 +764,11 @@ static u64 balloc_do_alloc_fixed(struct nvgpu_buddy_allocator *a,
shifted_base = balloc_base_shift(a, base); shifted_base = balloc_base_shift(a, base);
if (shifted_base == 0U) { if (shifted_base == 0U) {
align_order = __fls(len >> a->blk_shift); align_order = __ffs(len >> a->blk_shift);
} else { } else {
align_order = min_t(u64, align_order = min_t(u64,
__ffs(shifted_base >> a->blk_shift), __ffs(shifted_base >> a->blk_shift),
__fls(len >> a->blk_shift)); __ffs(len >> a->blk_shift));
} }
if (align_order > a->max_order) { if (align_order > a->max_order) {
@@ -822,7 +824,11 @@ err_and_cleanup:
balloc_buddy_list_do_rem(a, bud); balloc_buddy_list_do_rem(a, bud);
(void) balloc_free_buddy(a, bud->start); (void) balloc_free_buddy(a, bud->start);
nvgpu_kmem_cache_free(a->buddy_cache, bud); balloc_blist_add(a, bud);
/*
* Attemp to defrag the allocation.
*/
balloc_coalesce(a, bud);
} }
return 0; return 0;
@@ -862,6 +868,11 @@ static u64 nvgpu_buddy_balloc_pte(struct nvgpu_allocator *na, u64 len,
u32 pte_size; u32 pte_size;
struct nvgpu_buddy_allocator *a = na->priv; struct nvgpu_buddy_allocator *a = na->priv;
if (len == 0ULL) {
alloc_dbg(balloc_owner(a), "Alloc fail");
return 0;
}
alloc_lock(na); alloc_lock(na);
order = balloc_get_order(a, len); order = balloc_get_order(a, len);
@@ -874,7 +885,8 @@ static u64 nvgpu_buddy_balloc_pte(struct nvgpu_allocator *na, u64 len,
pte_size = nvgpu_balloc_page_size_to_pte_size(a, page_size); pte_size = nvgpu_balloc_page_size_to_pte_size(a, page_size);
if (pte_size == BALLOC_PTE_SIZE_INVALID) { if (pte_size == BALLOC_PTE_SIZE_INVALID) {
return 0ULL; alloc_unlock(na);
return 0;
} }
addr = balloc_do_alloc(a, order, pte_size); addr = balloc_do_alloc(a, order, pte_size);
@@ -888,12 +900,11 @@ static u64 nvgpu_buddy_balloc_pte(struct nvgpu_allocator *na, u64 len,
pte_size == BALLOC_PTE_SIZE_BIG ? "big" : pte_size == BALLOC_PTE_SIZE_BIG ? "big" :
pte_size == BALLOC_PTE_SIZE_SMALL ? "small" : pte_size == BALLOC_PTE_SIZE_SMALL ? "small" :
"NA/any"); "NA/any");
a->alloc_made = true;
} else { } else {
alloc_dbg(balloc_owner(a), "Alloc failed: no mem!"); alloc_dbg(balloc_owner(a), "Alloc failed: no mem!");
} }
a->alloc_made = true;
alloc_unlock(na); alloc_unlock(na);
return addr; return addr;
@@ -989,27 +1000,25 @@ static u64 nvgpu_balloc_fixed_buddy(struct nvgpu_allocator *na,
alloc_lock(na); alloc_lock(na);
alloc = nvgpu_balloc_fixed_buddy_locked(na, base, len, page_size); alloc = nvgpu_balloc_fixed_buddy_locked(na, base, len, page_size);
a->alloc_made = true;
if (alloc != 0ULL) {
a->alloc_made = true;
}
alloc_unlock(na); alloc_unlock(na);
return alloc; return alloc;
} }
/* /*
* Free the passed allocation. * @a must be locked.
*/ */
static void nvgpu_buddy_bfree(struct nvgpu_allocator *na, u64 addr) static void nvgpu_buddy_bfree_locked(struct nvgpu_allocator *na, u64 addr)
{ {
struct nvgpu_buddy *bud; struct nvgpu_buddy *bud;
struct nvgpu_fixed_alloc *falloc; struct nvgpu_fixed_alloc *falloc;
struct nvgpu_buddy_allocator *a = na->priv; struct nvgpu_buddy_allocator *a = na->priv;
if (addr == 0ULL) {
return;
}
alloc_lock(na);
/* /*
* First see if this is a fixed alloc. If not fall back to a regular * First see if this is a fixed alloc. If not fall back to a regular
* buddy. * buddy.
@@ -1034,9 +1043,21 @@ static void nvgpu_buddy_bfree(struct nvgpu_allocator *na, u64 addr)
balloc_coalesce(a, bud); balloc_coalesce(a, bud);
done: done:
alloc_unlock(na);
alloc_dbg(balloc_owner(a), "Free 0x%llx", addr); alloc_dbg(balloc_owner(a), "Free 0x%llx", addr);
return; }
/*
* Free the passed allocation.
*/
static void nvgpu_buddy_bfree(struct nvgpu_allocator *na, u64 addr)
{
if (addr == 0ULL) {
return;
}
alloc_lock(na);
nvgpu_buddy_bfree_locked(na, addr);
alloc_unlock(na);
} }
static bool nvgpu_buddy_reserve_is_possible(struct nvgpu_buddy_allocator *a, static bool nvgpu_buddy_reserve_is_possible(struct nvgpu_buddy_allocator *a,
@@ -1115,7 +1136,7 @@ static void nvgpu_buddy_release_co(struct nvgpu_allocator *na,
alloc_lock(na); alloc_lock(na);
nvgpu_list_del(&co->co_entry); nvgpu_list_del(&co->co_entry);
nvgpu_free(na, co->base); nvgpu_buddy_bfree_locked(na, co->base);
alloc_unlock(na); alloc_unlock(na);
} }
@@ -1309,6 +1330,12 @@ int nvgpu_buddy_allocator_init(struct gk20a *g, struct nvgpu_allocator *na,
return -EINVAL; return -EINVAL;
} }
/* Needs to be fixed, return -EINVAL*/
if (size == 0U) {
/* Setting to fixed size 1G to avoid further issues */
size = 0x40000000;
}
/* If this is to manage a GVA space we need a VM. */ /* If this is to manage a GVA space we need a VM. */
if (is_gva_space && vm == NULL) { if (is_gva_space && vm == NULL) {
return -EINVAL; return -EINVAL;
@@ -1351,9 +1378,9 @@ int nvgpu_buddy_allocator_init(struct gk20a *g, struct nvgpu_allocator *na,
* requirement is not necessary. * requirement is not necessary.
*/ */
if (is_gva_space) { if (is_gva_space) {
base_big_page = base & base_big_page = a->base &
((U64(vm->big_page_size) << U64(10)) - U64(1)); ((U64(vm->big_page_size) << U64(10)) - U64(1));
size_big_page = size & size_big_page = a->length &
((U64(vm->big_page_size) << U64(10)) - U64(1)); ((U64(vm->big_page_size) << U64(10)) - U64(1));
if (vm->big_pages && if (vm->big_pages &&
(base_big_page != 0ULL || size_big_page != 0ULL)) { (base_big_page != 0ULL || size_big_page != 0ULL)) {

View File

@@ -235,7 +235,7 @@ int nvgpu_lockless_allocator_init(struct gk20a *g, struct nvgpu_allocator *na,
const char *name, u64 base, u64 length, const char *name, u64 base, u64 length,
u64 blk_size, u64 flags); u64 blk_size, u64 flags);
#define GPU_BALLOC_MAX_ORDER 31U #define GPU_BALLOC_MAX_ORDER 63U
/* /*
* Allocator APIs. * Allocator APIs.