Skip to content

Commit

Permalink
lib/rhashtable: guarantee initial hashtable allocation
Browse files Browse the repository at this point in the history
rhashtable_init() may fail due to -ENOMEM, thus making the entire api
unusable.  This patch removes this scenario, however unlikely.  In order
to guarantee memory allocation, this patch always ends up doing
GFP_KERNEL|__GFP_NOFAIL for both the tbl as well as
alloc_bucket_spinlocks().

Upon the first table allocation failure, we shrink the size to the
smallest value that makes sense and retry with __GFP_NOFAIL semantics.
With the defaults, this means that from 64 buckets, we retry with only 4.
Any later issues regarding performance due to collisions or larger table
resizing (when more memory becomes available) is the least of our
problems.

Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Davidlohr Bueso <[email protected]>
Signed-off-by: Manfred Spraul <[email protected]>
Acked-by: Herbert Xu <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Michael Kerrisk <[email protected]>
Cc: Michal Hocko <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Davidlohr Bueso authored and torvalds committed Aug 22, 2018
1 parent 93f976b commit 2d22ecf
Showing 1 changed file with 11 additions and 3 deletions.
14 changes: 11 additions & 3 deletions lib/rhashtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,11 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,

size = nbuckets;

if (tbl == NULL && gfp != GFP_KERNEL) {
if (tbl == NULL && (gfp & ~__GFP_NOFAIL) != GFP_KERNEL) {
tbl = nested_bucket_table_alloc(ht, nbuckets, gfp);
nbuckets = 0;
}

if (tbl == NULL)
return NULL;

Expand Down Expand Up @@ -1057,9 +1058,16 @@ int rhashtable_init(struct rhashtable *ht,
}
}

/*
* This is api initialization and thus we need to guarantee the
* initial rhashtable allocation. Upon failure, retry with the
* smallest possible size with __GFP_NOFAIL semantics.
*/
tbl = bucket_table_alloc(ht, size, GFP_KERNEL);
if (tbl == NULL)
return -ENOMEM;
if (unlikely(tbl == NULL)) {
size = max_t(u16, ht->p.min_size, HASH_MIN_SIZE);
tbl = bucket_table_alloc(ht, size, GFP_KERNEL | __GFP_NOFAIL);
}

atomic_set(&ht->nelems, 0);

Expand Down

0 comments on commit 2d22ecf

Please sign in to comment.