Commit 2d22ecf6 authored by Davidlohr Bueso's avatar Davidlohr Bueso Committed by Linus Torvalds

lib/rhashtable: guarantee initial hashtable allocation

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/20180712185241.4017-9-manfred@colorfullife.comSigned-off-by: default avatarDavidlohr Bueso <dbueso@suse.de>
Signed-off-by: default avatarManfred Spraul <manfred@colorfullife.com>
Acked-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Michal Hocko <mhocko@suse.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 93f976b5
...@@ -178,10 +178,11 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, ...@@ -178,10 +178,11 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
size = nbuckets; size = nbuckets;
if (tbl == NULL && gfp != GFP_KERNEL) { if (tbl == NULL && (gfp & ~__GFP_NOFAIL) != GFP_KERNEL) {
tbl = nested_bucket_table_alloc(ht, nbuckets, gfp); tbl = nested_bucket_table_alloc(ht, nbuckets, gfp);
nbuckets = 0; nbuckets = 0;
} }
if (tbl == NULL) if (tbl == NULL)
return NULL; return NULL;
...@@ -1057,9 +1058,16 @@ int rhashtable_init(struct rhashtable *ht, ...@@ -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); tbl = bucket_table_alloc(ht, size, GFP_KERNEL);
if (tbl == NULL) if (unlikely(tbl == NULL)) {
return -ENOMEM; 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); atomic_set(&ht->nelems, 0);
......
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