aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavidlohr Bueso <dave@stgolabs.net>2018-08-22 01:01:48 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-08-22 13:52:52 -0400
commit2d22ecf6db1c390974476758681ba4229018e774 (patch)
treea3e555c29ee83f0cff4bfded9355802bc9df0850
parent93f976b5190df32793908d49165f78e67fcb66cf (diff)
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.com Signed-off-by: Davidlohr Bueso <dbueso@suse.de> Signed-off-by: Manfred Spraul <manfred@colorfullife.com> Acked-by: Herbert 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: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--lib/rhashtable.c14
1 files changed, 11 insertions, 3 deletions
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index 238c8daa9852..310e29b51507 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -178,10 +178,11 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
178 178
179 size = nbuckets; 179 size = nbuckets;
180 180
181 if (tbl == NULL && gfp != GFP_KERNEL) { 181 if (tbl == NULL && (gfp & ~__GFP_NOFAIL) != GFP_KERNEL) {
182 tbl = nested_bucket_table_alloc(ht, nbuckets, gfp); 182 tbl = nested_bucket_table_alloc(ht, nbuckets, gfp);
183 nbuckets = 0; 183 nbuckets = 0;
184 } 184 }
185
185 if (tbl == NULL) 186 if (tbl == NULL)
186 return NULL; 187 return NULL;
187 188
@@ -1057,9 +1058,16 @@ int rhashtable_init(struct rhashtable *ht,
1057 } 1058 }
1058 } 1059 }
1059 1060
1061 /*
1062 * This is api initialization and thus we need to guarantee the
1063 * initial rhashtable allocation. Upon failure, retry with the
1064 * smallest possible size with __GFP_NOFAIL semantics.
1065 */
1060 tbl = bucket_table_alloc(ht, size, GFP_KERNEL); 1066 tbl = bucket_table_alloc(ht, size, GFP_KERNEL);
1061 if (tbl == NULL) 1067 if (unlikely(tbl == NULL)) {
1062 return -ENOMEM; 1068 size = max_t(u16, ht->p.min_size, HASH_MIN_SIZE);
1069 tbl = bucket_table_alloc(ht, size, GFP_KERNEL | __GFP_NOFAIL);
1070 }
1063 1071
1064 atomic_set(&ht->nelems, 0); 1072 atomic_set(&ht->nelems, 0);
1065 1073