Blame view

lib/test_rhashtable.c 20 KB
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
1
2
3
  /*
   * Resizable, Scalable, Concurrent Hash Table
   *
1aa661f5c   Thomas Graf   rhashtable-test: ...
4
   * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
5
6
   * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
   *
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
7
8
9
10
11
12
13
14
15
16
17
18
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  
  /**************************************************************************
   * Self Test
   **************************************************************************/
  
  #include <linux/init.h>
  #include <linux/jhash.h>
  #include <linux/kernel.h>
f4a3e90ba   Phil Sutter   rhashtable-test: ...
19
  #include <linux/kthread.h>
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
20
21
22
  #include <linux/module.h>
  #include <linux/rcupdate.h>
  #include <linux/rhashtable.h>
f4a3e90ba   Phil Sutter   rhashtable-test: ...
23
  #include <linux/semaphore.h>
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
24
  #include <linux/slab.h>
685a015e4   Thomas Graf   rhashtable: Allow...
25
  #include <linux/sched.h>
cdd4de372   Florian Westphal   test_rhashtable: ...
26
  #include <linux/random.h>
f4a3e90ba   Phil Sutter   rhashtable-test: ...
27
  #include <linux/vmalloc.h>
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
28

1aa661f5c   Thomas Graf   rhashtable-test: ...
29
  #define MAX_ENTRIES	1000000
67b7cbf42   Thomas Graf   rhashtable-test: ...
30
  #define TEST_INSERT_FAIL INT_MAX
1aa661f5c   Thomas Graf   rhashtable-test: ...
31

f651616e7   Florian Westphal   test_rhashtable: ...
32
33
34
  static int parm_entries = 50000;
  module_param(parm_entries, int, 0);
  MODULE_PARM_DESC(parm_entries, "Number of entries to add (default: 50000)");
1aa661f5c   Thomas Graf   rhashtable-test: ...
35
36
37
38
  
  static int runs = 4;
  module_param(runs, int, 0);
  MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
95e435afe   Phil Sutter   rhashtable-test: ...
39
  static int max_size = 0;
1aa661f5c   Thomas Graf   rhashtable-test: ...
40
  module_param(max_size, int, 0);
3b3bf80b9   Phil Sutter   rhashtable-test: ...
41
  MODULE_PARM_DESC(max_size, "Maximum table size (default: calculated)");
1aa661f5c   Thomas Graf   rhashtable-test: ...
42
43
44
45
46
47
48
49
  
  static bool shrinking = false;
  module_param(shrinking, bool, 0);
  MODULE_PARM_DESC(shrinking, "Enable automatic shrinking (default: off)");
  
  static int size = 8;
  module_param(size, int, 0);
  MODULE_PARM_DESC(size, "Initial size hint of table (default: 8)");
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
50

f4a3e90ba   Phil Sutter   rhashtable-test: ...
51
52
53
  static int tcount = 10;
  module_param(tcount, int, 0);
  MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
d662e037f   Phil Sutter   rhashtable-test: ...
54
55
56
  static bool enomem_retry = false;
  module_param(enomem_retry, bool, 0);
  MODULE_PARM_DESC(enomem_retry, "Retry insert even if -ENOMEM was returned (default: off)");
e859afe1e   Phil Sutter   lib: test_rhashta...
57
58
59
60
  struct test_obj_val {
  	int	id;
  	int	tid;
  };
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
61
  struct test_obj {
e859afe1e   Phil Sutter   lib: test_rhashta...
62
  	struct test_obj_val	value;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
63
64
  	struct rhash_head	node;
  };
cdd4de372   Florian Westphal   test_rhashtable: ...
65
66
67
68
  struct test_obj_rhl {
  	struct test_obj_val	value;
  	struct rhlist_head	list_node;
  };
f4a3e90ba   Phil Sutter   rhashtable-test: ...
69
  struct thread_data {
f651616e7   Florian Westphal   test_rhashtable: ...
70
  	unsigned int entries;
f4a3e90ba   Phil Sutter   rhashtable-test: ...
71
72
73
74
  	int id;
  	struct task_struct *task;
  	struct test_obj *objs;
  };
499ac3b60   Paul Blakey   test_rhashtable: ...
75
76
77
  static u32 my_hashfn(const void *data, u32 len, u32 seed)
  {
  	const struct test_obj_rhl *obj = data;
9f9a70773   NeilBrown   rhashtable: remov...
78
  	return (obj->value.id % 10);
499ac3b60   Paul Blakey   test_rhashtable: ...
79
80
81
82
83
84
85
86
87
  }
  
  static int my_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
  {
  	const struct test_obj_rhl *test_obj = obj;
  	const struct test_obj_val *val = arg->key;
  
  	return test_obj->value.id - val->id;
  }
1aa661f5c   Thomas Graf   rhashtable-test: ...
88
  static struct rhashtable_params test_rht_params = {
b182aa6e9   Herbert Xu   test_rhashtable: ...
89
90
  	.head_offset = offsetof(struct test_obj, node),
  	.key_offset = offsetof(struct test_obj, value),
e859afe1e   Phil Sutter   lib: test_rhashta...
91
  	.key_len = sizeof(struct test_obj_val),
b182aa6e9   Herbert Xu   test_rhashtable: ...
92
  	.hashfn = jhash,
b182aa6e9   Herbert Xu   test_rhashtable: ...
93
  };
499ac3b60   Paul Blakey   test_rhashtable: ...
94
95
96
97
98
99
100
101
102
103
  static struct rhashtable_params test_rht_params_dup = {
  	.head_offset = offsetof(struct test_obj_rhl, list_node),
  	.key_offset = offsetof(struct test_obj_rhl, value),
  	.key_len = sizeof(struct test_obj_val),
  	.hashfn = jhash,
  	.obj_hashfn = my_hashfn,
  	.obj_cmpfn = my_cmpfn,
  	.nelem_hint = 128,
  	.automatic_shrinking = false,
  };
f4a3e90ba   Phil Sutter   rhashtable-test: ...
104
105
  static struct semaphore prestart_sem;
  static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0);
7e936bd73   Florian Westphal   test_rhashtable: ...
106
  static int insert_retry(struct rhashtable *ht, struct test_obj *obj,
9e9089e5a   Phil Sutter   rhashtable-test: ...
107
108
                          const struct rhashtable_params params)
  {
d662e037f   Phil Sutter   rhashtable-test: ...
109
  	int err, retries = -1, enomem_retries = 0;
9e9089e5a   Phil Sutter   rhashtable-test: ...
110
111
112
113
  
  	do {
  		retries++;
  		cond_resched();
7e936bd73   Florian Westphal   test_rhashtable: ...
114
  		err = rhashtable_insert_fast(ht, &obj->node, params);
d662e037f   Phil Sutter   rhashtable-test: ...
115
116
117
118
  		if (err == -ENOMEM && enomem_retry) {
  			enomem_retries++;
  			err = -EBUSY;
  		}
9e9089e5a   Phil Sutter   rhashtable-test: ...
119
  	} while (err == -EBUSY);
d662e037f   Phil Sutter   rhashtable-test: ...
120
121
122
123
  	if (enomem_retries)
  		pr_info(" %u insertions retried after -ENOMEM
  ",
  			enomem_retries);
9e9089e5a   Phil Sutter   rhashtable-test: ...
124
125
  	return err ? : retries;
  }
f651616e7   Florian Westphal   test_rhashtable: ...
126
127
  static int __init test_rht_lookup(struct rhashtable *ht, struct test_obj *array,
  				  unsigned int entries)
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
128
129
  {
  	unsigned int i;
f651616e7   Florian Westphal   test_rhashtable: ...
130
  	for (i = 0; i < entries; i++) {
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
131
132
  		struct test_obj *obj;
  		bool expected = !(i % 2);
e859afe1e   Phil Sutter   lib: test_rhashta...
133
134
135
  		struct test_obj_val key = {
  			.id = i,
  		};
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
136

e859afe1e   Phil Sutter   lib: test_rhashta...
137
  		if (array[i / 2].value.id == TEST_INSERT_FAIL)
67b7cbf42   Thomas Graf   rhashtable-test: ...
138
  			expected = false;
b182aa6e9   Herbert Xu   test_rhashtable: ...
139
  		obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
140
141
  
  		if (expected && !obj) {
e859afe1e   Phil Sutter   lib: test_rhashta...
142
143
  			pr_warn("Test failed: Could not find key %u
  ", key.id);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
144
145
146
147
  			return -ENOENT;
  		} else if (!expected && obj) {
  			pr_warn("Test failed: Unexpected entry found for key %u
  ",
e859afe1e   Phil Sutter   lib: test_rhashta...
148
  				key.id);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
149
150
  			return -EEXIST;
  		} else if (expected && obj) {
e859afe1e   Phil Sutter   lib: test_rhashta...
151
  			if (obj->value.id != i) {
c2c8a9016   Thomas Graf   rhashtable-test: ...
152
153
  				pr_warn("Test failed: Lookup value mismatch %u!=%u
  ",
e859afe1e   Phil Sutter   lib: test_rhashta...
154
  					obj->value.id, i);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
155
156
157
  				return -EINVAL;
  			}
  		}
685a015e4   Thomas Graf   rhashtable: Allow...
158
159
  
  		cond_resched_rcu();
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
160
161
162
163
  	}
  
  	return 0;
  }
f651616e7   Florian Westphal   test_rhashtable: ...
164
  static void test_bucket_stats(struct rhashtable *ht, unsigned int entries)
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
165
  {
246b23a76   Thomas Graf   rhashtable-test: ...
166
167
  	unsigned int err, total = 0, chain_len = 0;
  	struct rhashtable_iter hti;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
168
  	struct rhash_head *pos;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
169

8f6fd83c6   Bob Copeland   rhashtable: accep...
170
  	err = rhashtable_walk_init(ht, &hti, GFP_KERNEL);
246b23a76   Thomas Graf   rhashtable-test: ...
171
172
173
174
  	if (err) {
  		pr_warn("Test failed: allocation error");
  		return;
  	}
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
175

97a6ec4ac   Tom Herbert   rhashtable: Chang...
176
  	rhashtable_walk_start(&hti);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
177

246b23a76   Thomas Graf   rhashtable-test: ...
178
179
180
181
182
183
184
185
186
187
188
  	while ((pos = rhashtable_walk_next(&hti))) {
  		if (PTR_ERR(pos) == -EAGAIN) {
  			pr_info("Info: encountered resize
  ");
  			chain_len++;
  			continue;
  		} else if (IS_ERR(pos)) {
  			pr_warn("Test failed: rhashtable_walk_next() error: %ld
  ",
  				PTR_ERR(pos));
  			break;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
189
  		}
246b23a76   Thomas Graf   rhashtable-test: ...
190
  		total++;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
191
  	}
246b23a76   Thomas Graf   rhashtable-test: ...
192
193
194
195
196
197
  	rhashtable_walk_stop(&hti);
  	rhashtable_walk_exit(&hti);
  
  	pr_info("  Traversal complete: counted=%u, nelems=%u, entries=%d, table-jumps=%u
  ",
  		total, atomic_read(&ht->nelems), entries, chain_len);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
198

1aa661f5c   Thomas Graf   rhashtable-test: ...
199
  	if (total != atomic_read(&ht->nelems) || total != entries)
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
200
201
  		pr_warn("Test failed: Total count mismatch ^^^");
  }
f651616e7   Florian Westphal   test_rhashtable: ...
202
203
  static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array,
  				  unsigned int entries)
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
204
  {
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
205
  	struct test_obj *obj;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
206
  	int err;
9e9089e5a   Phil Sutter   rhashtable-test: ...
207
  	unsigned int i, insert_retries = 0;
1aa661f5c   Thomas Graf   rhashtable-test: ...
208
  	s64 start, end;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
209
210
211
  
  	/*
  	 * Insertion Test:
1aa661f5c   Thomas Graf   rhashtable-test: ...
212
  	 * Insert entries into table with all keys even numbers
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
213
  	 */
1aa661f5c   Thomas Graf   rhashtable-test: ...
214
215
216
217
  	pr_info("  Adding %d keys
  ", entries);
  	start = ktime_get_ns();
  	for (i = 0; i < entries; i++) {
fcc570207   Thomas Graf   rhashtable-test: ...
218
  		struct test_obj *obj = &array[i];
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
219

e859afe1e   Phil Sutter   lib: test_rhashta...
220
  		obj->value.id = i * 2;
7e936bd73   Florian Westphal   test_rhashtable: ...
221
  		err = insert_retry(ht, obj, test_rht_params);
9e9089e5a   Phil Sutter   rhashtable-test: ...
222
223
224
  		if (err > 0)
  			insert_retries += err;
  		else if (err)
fcc570207   Thomas Graf   rhashtable-test: ...
225
  			return err;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
226
  	}
9e9089e5a   Phil Sutter   rhashtable-test: ...
227
228
229
230
  	if (insert_retries)
  		pr_info("  %u insertions retried due to memory pressure
  ",
  			insert_retries);
67b7cbf42   Thomas Graf   rhashtable-test: ...
231

f651616e7   Florian Westphal   test_rhashtable: ...
232
  	test_bucket_stats(ht, entries);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
233
  	rcu_read_lock();
f651616e7   Florian Westphal   test_rhashtable: ...
234
  	test_rht_lookup(ht, array, entries);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
235
  	rcu_read_unlock();
f651616e7   Florian Westphal   test_rhashtable: ...
236
  	test_bucket_stats(ht, entries);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
237

1aa661f5c   Thomas Graf   rhashtable-test: ...
238
239
240
  	pr_info("  Deleting %d keys
  ", entries);
  	for (i = 0; i < entries; i++) {
783692558   Phil Sutter   lib: test_rhashta...
241
242
243
  		struct test_obj_val key = {
  			.id = i * 2,
  		};
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
244

e859afe1e   Phil Sutter   lib: test_rhashta...
245
  		if (array[i].value.id != TEST_INSERT_FAIL) {
67b7cbf42   Thomas Graf   rhashtable-test: ...
246
247
  			obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
  			BUG_ON(!obj);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
248

67b7cbf42   Thomas Graf   rhashtable-test: ...
249
250
  			rhashtable_remove_fast(ht, &obj->node, test_rht_params);
  		}
685a015e4   Thomas Graf   rhashtable: Allow...
251
252
  
  		cond_resched();
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
253
  	}
1aa661f5c   Thomas Graf   rhashtable-test: ...
254
255
256
257
258
  	end = ktime_get_ns();
  	pr_info("  Duration of test: %lld ns
  ", end - start);
  
  	return end - start;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
259
  }
b7f5e5c7f   Daniel Borkmann   rhashtable: don't...
260
  static struct rhashtable ht;
411d788a2   Florian Westphal   test_rhashtable: ...
261
  static struct rhltable rhlt;
cdd4de372   Florian Westphal   test_rhashtable: ...
262
263
264
265
266
267
268
269
270
271
  
  static int __init test_rhltable(unsigned int entries)
  {
  	struct test_obj_rhl *rhl_test_objects;
  	unsigned long *obj_in_table;
  	unsigned int i, j, k;
  	int ret, err;
  
  	if (entries == 0)
  		entries = 1;
fad953ce0   Kees Cook   treewide: Use arr...
272
273
  	rhl_test_objects = vzalloc(array_size(entries,
  					      sizeof(*rhl_test_objects)));
cdd4de372   Florian Westphal   test_rhashtable: ...
274
275
276
277
  	if (!rhl_test_objects)
  		return -ENOMEM;
  
  	ret = -ENOMEM;
fad953ce0   Kees Cook   treewide: Use arr...
278
279
  	obj_in_table = vzalloc(array_size(sizeof(unsigned long),
  					  BITS_TO_LONGS(entries)));
cdd4de372   Florian Westphal   test_rhashtable: ...
280
281
  	if (!obj_in_table)
  		goto out_free;
cdd4de372   Florian Westphal   test_rhashtable: ...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  	err = rhltable_init(&rhlt, &test_rht_params);
  	if (WARN_ON(err))
  		goto out_free;
  
  	k = prandom_u32();
  	ret = 0;
  	for (i = 0; i < entries; i++) {
  		rhl_test_objects[i].value.id = k;
  		err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
  				      test_rht_params);
  		if (WARN(err, "error %d on element %d
  ", err, i))
  			break;
  		if (err == 0)
  			set_bit(i, obj_in_table);
  	}
  
  	if (err)
  		ret = err;
  
  	pr_info("test %d add/delete pairs into rhlist
  ", entries);
  	for (i = 0; i < entries; i++) {
  		struct rhlist_head *h, *pos;
  		struct test_obj_rhl *obj;
  		struct test_obj_val key = {
  			.id = k,
  		};
  		bool found;
  
  		rcu_read_lock();
  		h = rhltable_lookup(&rhlt, &key, test_rht_params);
  		if (WARN(!h, "key not found during iteration %d of %d", i, entries)) {
  			rcu_read_unlock();
  			break;
  		}
  
  		if (i) {
  			j = i - 1;
  			rhl_for_each_entry_rcu(obj, pos, h, list_node) {
  				if (WARN(pos == &rhl_test_objects[j].list_node, "old element found, should be gone"))
  					break;
  			}
  		}
  
  		cond_resched_rcu();
  
  		found = false;
  
  		rhl_for_each_entry_rcu(obj, pos, h, list_node) {
  			if (pos == &rhl_test_objects[i].list_node) {
  				found = true;
  				break;
  			}
  		}
  
  		rcu_read_unlock();
  
  		if (WARN(!found, "element %d not found", i))
  			break;
  
  		err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  		WARN(err, "rhltable_remove: err %d for iteration %d
  ", err, i);
  		if (err == 0)
  			clear_bit(i, obj_in_table);
  	}
  
  	if (ret == 0 && err)
  		ret = err;
  
  	for (i = 0; i < entries; i++) {
  		WARN(test_bit(i, obj_in_table), "elem %d allegedly still present", i);
  
  		err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
  				      test_rht_params);
  		if (WARN(err, "error %d on element %d
  ", err, i))
  			break;
  		if (err == 0)
  			set_bit(i, obj_in_table);
  	}
  
  	pr_info("test %d random rhlist add/delete operations
  ", entries);
  	for (j = 0; j < entries; j++) {
  		u32 i = prandom_u32_max(entries);
  		u32 prand = prandom_u32();
  
  		cond_resched();
  
  		if (prand == 0)
  			prand = prandom_u32();
  
  		if (prand & 1) {
  			prand >>= 1;
  			continue;
  		}
  
  		err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  		if (test_bit(i, obj_in_table)) {
  			clear_bit(i, obj_in_table);
  			if (WARN(err, "cannot remove element at slot %d", i))
  				continue;
  		} else {
  			if (WARN(err != -ENOENT, "removed non-existant element %d, error %d not %d",
  			     i, err, -ENOENT))
  				continue;
  		}
  
  		if (prand & 1) {
  			prand >>= 1;
  			continue;
  		}
  
  		err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  		if (err == 0) {
  			if (WARN(test_and_set_bit(i, obj_in_table), "succeeded to insert same object %d", i))
  				continue;
  		} else {
  			if (WARN(!test_bit(i, obj_in_table), "failed to insert object %d", i))
  				continue;
  		}
  
  		if (prand & 1) {
  			prand >>= 1;
  			continue;
  		}
  
  		i = prandom_u32_max(entries);
  		if (test_bit(i, obj_in_table)) {
  			err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  			WARN(err, "cannot remove element at slot %d", i);
  			if (err == 0)
  				clear_bit(i, obj_in_table);
  		} else {
  			err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  			WARN(err, "failed to insert object %d", i);
  			if (err == 0)
  				set_bit(i, obj_in_table);
  		}
  	}
  
  	for (i = 0; i < entries; i++) {
  		cond_resched();
  		err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  		if (test_bit(i, obj_in_table)) {
  			if (WARN(err, "cannot remove element at slot %d", i))
  				continue;
  		} else {
  			if (WARN(err != -ENOENT, "removed non-existant element, error %d not %d",
  				 err, -ENOENT))
  			continue;
  		}
  	}
  
  	rhltable_destroy(&rhlt);
  out_free:
  	vfree(rhl_test_objects);
  	vfree(obj_in_table);
  	return ret;
  }
b7f5e5c7f   Daniel Borkmann   rhashtable: don't...
444

a6359bd8d   Florian Westphal   test_rhashtable: ...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
  static int __init test_rhashtable_max(struct test_obj *array,
  				      unsigned int entries)
  {
  	unsigned int i, insert_retries = 0;
  	int err;
  
  	test_rht_params.max_size = roundup_pow_of_two(entries / 8);
  	err = rhashtable_init(&ht, &test_rht_params);
  	if (err)
  		return err;
  
  	for (i = 0; i < ht.max_elems; i++) {
  		struct test_obj *obj = &array[i];
  
  		obj->value.id = i * 2;
  		err = insert_retry(&ht, obj, test_rht_params);
  		if (err > 0)
  			insert_retries += err;
  		else if (err)
  			return err;
  	}
  
  	err = insert_retry(&ht, &array[ht.max_elems], test_rht_params);
  	if (err == -E2BIG) {
  		err = 0;
  	} else {
  		pr_info("insert element %u should have failed with %d, got %d
  ",
  				ht.max_elems, -E2BIG, err);
  		if (err == 0)
  			err = -1;
  	}
  
  	rhashtable_destroy(&ht);
  
  	return err;
  }
499ac3b60   Paul Blakey   test_rhashtable: ...
482
483
484
485
486
487
488
489
  static unsigned int __init print_ht(struct rhltable *rhlt)
  {
  	struct rhashtable *ht;
  	const struct bucket_table *tbl;
  	char buff[512] = "";
  	unsigned int i, cnt = 0;
  
  	ht = &rhlt->ht;
cbab90129   NeilBrown   rhashtable: silen...
490
491
  	/* Take the mutex to avoid RCU warning */
  	mutex_lock(&ht->mutex);
499ac3b60   Paul Blakey   test_rhashtable: ...
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
  	tbl = rht_dereference(ht->tbl, ht);
  	for (i = 0; i < tbl->size; i++) {
  		struct rhash_head *pos, *next;
  		struct test_obj_rhl *p;
  
  		pos = rht_dereference(tbl->buckets[i], ht);
  		next = !rht_is_a_nulls(pos) ? rht_dereference(pos->next, ht) : NULL;
  
  		if (!rht_is_a_nulls(pos)) {
  			sprintf(buff, "%s
  bucket[%d] -> ", buff, i);
  		}
  
  		while (!rht_is_a_nulls(pos)) {
  			struct rhlist_head *list = container_of(pos, struct rhlist_head, rhead);
  			sprintf(buff, "%s[[", buff);
  			do {
  				pos = &list->rhead;
  				list = rht_dereference(list->next, ht);
  				p = rht_obj(ht, pos);
  
  				sprintf(buff, "%s val %d (tid=%d)%s", buff, p->value.id, p->value.tid,
  					list? ", " : " ");
  				cnt++;
  			} while (list);
  
  			pos = next,
  			next = !rht_is_a_nulls(pos) ?
  				rht_dereference(pos->next, ht) : NULL;
  
  			sprintf(buff, "%s]]%s", buff, !rht_is_a_nulls(pos) ? " -> " : "");
  		}
  	}
  	printk(KERN_ERR "
  ---- ht: ----%s
  -------------
  ", buff);
cbab90129   NeilBrown   rhashtable: silen...
529
  	mutex_unlock(&ht->mutex);
499ac3b60   Paul Blakey   test_rhashtable: ...
530
531
532
533
534
535
536
  
  	return cnt;
  }
  
  static int __init test_insert_dup(struct test_obj_rhl *rhl_test_objects,
  				  int cnt, bool slow)
  {
81733c642   Bart Van Assche   lib/test_rhashtab...
537
  	struct rhltable *rhlt;
499ac3b60   Paul Blakey   test_rhashtable: ...
538
539
540
  	unsigned int i, ret;
  	const char *key;
  	int err = 0;
81733c642   Bart Van Assche   lib/test_rhashtab...
541
542
543
544
545
546
547
  	rhlt = kmalloc(sizeof(*rhlt), GFP_KERNEL);
  	if (WARN_ON(!rhlt))
  		return -EINVAL;
  
  	err = rhltable_init(rhlt, &test_rht_params_dup);
  	if (WARN_ON(err)) {
  		kfree(rhlt);
499ac3b60   Paul Blakey   test_rhashtable: ...
548
  		return err;
81733c642   Bart Van Assche   lib/test_rhashtab...
549
  	}
499ac3b60   Paul Blakey   test_rhashtable: ...
550
551
552
  
  	for (i = 0; i < cnt; i++) {
  		rhl_test_objects[i].value.tid = i;
81733c642   Bart Van Assche   lib/test_rhashtab...
553
  		key = rht_obj(&rhlt->ht, &rhl_test_objects[i].list_node.rhead);
499ac3b60   Paul Blakey   test_rhashtable: ...
554
555
556
  		key += test_rht_params_dup.key_offset;
  
  		if (slow) {
81733c642   Bart Van Assche   lib/test_rhashtab...
557
  			err = PTR_ERR(rhashtable_insert_slow(&rhlt->ht, key,
499ac3b60   Paul Blakey   test_rhashtable: ...
558
559
560
561
  							     &rhl_test_objects[i].list_node.rhead));
  			if (err == -EAGAIN)
  				err = 0;
  		} else
81733c642   Bart Van Assche   lib/test_rhashtab...
562
  			err = rhltable_insert(rhlt,
499ac3b60   Paul Blakey   test_rhashtable: ...
563
564
565
566
567
568
  					      &rhl_test_objects[i].list_node,
  					      test_rht_params_dup);
  		if (WARN(err, "error %d on element %d/%d (%s)
  ", err, i, cnt, slow? "slow" : "fast"))
  			goto skip_print;
  	}
81733c642   Bart Van Assche   lib/test_rhashtab...
569
  	ret = print_ht(rhlt);
499ac3b60   Paul Blakey   test_rhashtable: ...
570
571
572
573
  	WARN(ret != cnt, "missing rhltable elements (%d != %d, %s)
  ", ret, cnt, slow? "slow" : "fast");
  
  skip_print:
81733c642   Bart Van Assche   lib/test_rhashtab...
574
575
  	rhltable_destroy(rhlt);
  	kfree(rhlt);
499ac3b60   Paul Blakey   test_rhashtable: ...
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
  
  	return 0;
  }
  
  static int __init test_insert_duplicates_run(void)
  {
  	struct test_obj_rhl rhl_test_objects[3] = {};
  
  	pr_info("test inserting duplicates
  ");
  
  	/* two different values that map to same bucket */
  	rhl_test_objects[0].value.id = 1;
  	rhl_test_objects[1].value.id = 21;
  
  	/* and another duplicate with same as [0] value
  	 * which will be second on the bucket list */
  	rhl_test_objects[2].value.id = rhl_test_objects[0].value.id;
  
  	test_insert_dup(rhl_test_objects, 2, false);
  	test_insert_dup(rhl_test_objects, 3, false);
  	test_insert_dup(rhl_test_objects, 2, true);
  	test_insert_dup(rhl_test_objects, 3, true);
  
  	return 0;
  }
f4a3e90ba   Phil Sutter   rhashtable-test: ...
602
603
  static int thread_lookup_test(struct thread_data *tdata)
  {
f651616e7   Florian Westphal   test_rhashtable: ...
604
  	unsigned int entries = tdata->entries;
f4a3e90ba   Phil Sutter   rhashtable-test: ...
605
606
607
608
  	int i, err = 0;
  
  	for (i = 0; i < entries; i++) {
  		struct test_obj *obj;
e859afe1e   Phil Sutter   lib: test_rhashta...
609
610
611
612
  		struct test_obj_val key = {
  			.id = i,
  			.tid = tdata->id,
  		};
f4a3e90ba   Phil Sutter   rhashtable-test: ...
613
614
  
  		obj = rhashtable_lookup_fast(&ht, &key, test_rht_params);
e859afe1e   Phil Sutter   lib: test_rhashta...
615
616
617
  		if (obj && (tdata->objs[i].value.id == TEST_INSERT_FAIL)) {
  			pr_err("  found unexpected object %d-%d
  ", key.tid, key.id);
f4a3e90ba   Phil Sutter   rhashtable-test: ...
618
  			err++;
e859afe1e   Phil Sutter   lib: test_rhashta...
619
620
621
  		} else if (!obj && (tdata->objs[i].value.id != TEST_INSERT_FAIL)) {
  			pr_err("  object %d-%d not found!
  ", key.tid, key.id);
f4a3e90ba   Phil Sutter   rhashtable-test: ...
622
  			err++;
e859afe1e   Phil Sutter   lib: test_rhashta...
623
624
625
626
  		} else if (obj && memcmp(&obj->value, &key, sizeof(key))) {
  			pr_err("  wrong object returned (got %d-%d, expected %d-%d)
  ",
  			       obj->value.tid, obj->value.id, key.tid, key.id);
f4a3e90ba   Phil Sutter   rhashtable-test: ...
627
628
  			err++;
  		}
cd5b318da   Phil Sutter   rhashtable-test: ...
629
630
  
  		cond_resched();
f4a3e90ba   Phil Sutter   rhashtable-test: ...
631
632
633
634
635
636
  	}
  	return err;
  }
  
  static int threadfunc(void *data)
  {
9e9089e5a   Phil Sutter   rhashtable-test: ...
637
  	int i, step, err = 0, insert_retries = 0;
f4a3e90ba   Phil Sutter   rhashtable-test: ...
638
639
640
641
642
643
  	struct thread_data *tdata = data;
  
  	up(&prestart_sem);
  	if (down_interruptible(&startup_sem))
  		pr_err("  thread[%d]: down_interruptible failed
  ", tdata->id);
f651616e7   Florian Westphal   test_rhashtable: ...
644
  	for (i = 0; i < tdata->entries; i++) {
e859afe1e   Phil Sutter   lib: test_rhashta...
645
646
  		tdata->objs[i].value.id = i;
  		tdata->objs[i].value.tid = tdata->id;
7e936bd73   Florian Westphal   test_rhashtable: ...
647
  		err = insert_retry(&ht, &tdata->objs[i], test_rht_params);
9e9089e5a   Phil Sutter   rhashtable-test: ...
648
649
  		if (err > 0) {
  			insert_retries += err;
f4a3e90ba   Phil Sutter   rhashtable-test: ...
650
651
652
653
654
655
656
  		} else if (err) {
  			pr_err("  thread[%d]: rhashtable_insert_fast failed
  ",
  			       tdata->id);
  			goto out;
  		}
  	}
9e9089e5a   Phil Sutter   rhashtable-test: ...
657
658
659
660
  	if (insert_retries)
  		pr_info("  thread[%d]: %u insertions retried due to memory pressure
  ",
  			tdata->id, insert_retries);
f4a3e90ba   Phil Sutter   rhashtable-test: ...
661
662
663
664
665
666
667
668
669
670
  
  	err = thread_lookup_test(tdata);
  	if (err) {
  		pr_err("  thread[%d]: rhashtable_lookup_test failed
  ",
  		       tdata->id);
  		goto out;
  	}
  
  	for (step = 10; step > 0; step--) {
f651616e7   Florian Westphal   test_rhashtable: ...
671
  		for (i = 0; i < tdata->entries; i += step) {
e859afe1e   Phil Sutter   lib: test_rhashta...
672
  			if (tdata->objs[i].value.id == TEST_INSERT_FAIL)
f4a3e90ba   Phil Sutter   rhashtable-test: ...
673
674
675
676
677
678
679
680
681
  				continue;
  			err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
  			                             test_rht_params);
  			if (err) {
  				pr_err("  thread[%d]: rhashtable_remove_fast failed
  ",
  				       tdata->id);
  				goto out;
  			}
e859afe1e   Phil Sutter   lib: test_rhashta...
682
  			tdata->objs[i].value.id = TEST_INSERT_FAIL;
cd5b318da   Phil Sutter   rhashtable-test: ...
683
684
  
  			cond_resched();
f4a3e90ba   Phil Sutter   rhashtable-test: ...
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
  		}
  		err = thread_lookup_test(tdata);
  		if (err) {
  			pr_err("  thread[%d]: rhashtable_lookup_test (2) failed
  ",
  			       tdata->id);
  			goto out;
  		}
  	}
  out:
  	while (!kthread_should_stop()) {
  		set_current_state(TASK_INTERRUPTIBLE);
  		schedule();
  	}
  	return err;
  }
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
701
702
  static int __init test_rht_init(void)
  {
f651616e7   Florian Westphal   test_rhashtable: ...
703
  	unsigned int entries;
f4a3e90ba   Phil Sutter   rhashtable-test: ...
704
  	int i, err, started_threads = 0, failed_threads = 0;
1aa661f5c   Thomas Graf   rhashtable-test: ...
705
  	u64 total_time = 0;
f4a3e90ba   Phil Sutter   rhashtable-test: ...
706
707
  	struct thread_data *tdata;
  	struct test_obj *objs;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
708

f651616e7   Florian Westphal   test_rhashtable: ...
709
710
711
712
  	if (parm_entries < 0)
  		parm_entries = 1;
  
  	entries = min(parm_entries, MAX_ENTRIES);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
713

1aa661f5c   Thomas Graf   rhashtable-test: ...
714
  	test_rht_params.automatic_shrinking = shrinking;
95e435afe   Phil Sutter   rhashtable-test: ...
715
  	test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries);
1aa661f5c   Thomas Graf   rhashtable-test: ...
716
  	test_rht_params.nelem_hint = size;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
717

fad953ce0   Kees Cook   treewide: Use arr...
718
719
  	objs = vzalloc(array_size(sizeof(struct test_obj),
  				  test_rht_params.max_size + 1));
7e936bd73   Florian Westphal   test_rhashtable: ...
720
721
  	if (!objs)
  		return -ENOMEM;
1aa661f5c   Thomas Graf   rhashtable-test: ...
722
723
724
  	pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d
  ",
  		size, max_size, shrinking);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
725

1aa661f5c   Thomas Graf   rhashtable-test: ...
726
727
  	for (i = 0; i < runs; i++) {
  		s64 time;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
728

1aa661f5c   Thomas Graf   rhashtable-test: ...
729
730
  		pr_info("Test %02d:
  ", i);
7e936bd73   Florian Westphal   test_rhashtable: ...
731
  		memset(objs, 0, test_rht_params.max_size * sizeof(struct test_obj));
1aa661f5c   Thomas Graf   rhashtable-test: ...
732
733
734
735
736
737
738
  		err = rhashtable_init(&ht, &test_rht_params);
  		if (err < 0) {
  			pr_warn("Test failed: Unable to initialize hashtable: %d
  ",
  				err);
  			continue;
  		}
f651616e7   Florian Westphal   test_rhashtable: ...
739
  		time = test_rhashtable(&ht, objs, entries);
1aa661f5c   Thomas Graf   rhashtable-test: ...
740
741
  		rhashtable_destroy(&ht);
  		if (time < 0) {
7e936bd73   Florian Westphal   test_rhashtable: ...
742
  			vfree(objs);
1aa661f5c   Thomas Graf   rhashtable-test: ...
743
744
745
746
747
748
749
  			pr_warn("Test failed: return code %lld
  ", time);
  			return -EINVAL;
  		}
  
  		total_time += time;
  	}
a6359bd8d   Florian Westphal   test_rhashtable: ...
750
751
752
753
  	pr_info("test if its possible to exceed max_size %d: %s
  ",
  			test_rht_params.max_size, test_rhashtable_max(objs, entries) == 0 ?
  			"no, ok" : "YES, failed");
7e936bd73   Florian Westphal   test_rhashtable: ...
754
  	vfree(objs);
a6359bd8d   Florian Westphal   test_rhashtable: ...
755

6decd63ac   Thomas Graf   rhashtable-test: ...
756
757
758
  	do_div(total_time, runs);
  	pr_info("Average test time: %llu
  ", total_time);
1aa661f5c   Thomas Graf   rhashtable-test: ...
759

499ac3b60   Paul Blakey   test_rhashtable: ...
760
  	test_insert_duplicates_run();
f4a3e90ba   Phil Sutter   rhashtable-test: ...
761
762
763
764
765
766
767
  	if (!tcount)
  		return 0;
  
  	pr_info("Testing concurrent rhashtable access from %d threads
  ",
  	        tcount);
  	sema_init(&prestart_sem, 1 - tcount);
fad953ce0   Kees Cook   treewide: Use arr...
768
  	tdata = vzalloc(array_size(tcount, sizeof(struct thread_data)));
f4a3e90ba   Phil Sutter   rhashtable-test: ...
769
770
  	if (!tdata)
  		return -ENOMEM;
fad953ce0   Kees Cook   treewide: Use arr...
771
  	objs  = vzalloc(array3_size(sizeof(struct test_obj), tcount, entries));
f4a3e90ba   Phil Sutter   rhashtable-test: ...
772
773
774
775
  	if (!objs) {
  		vfree(tdata);
  		return -ENOMEM;
  	}
95e435afe   Phil Sutter   rhashtable-test: ...
776
777
  	test_rht_params.max_size = max_size ? :
  	                           roundup_pow_of_two(tcount * entries);
f4a3e90ba   Phil Sutter   rhashtable-test: ...
778
779
780
781
782
783
784
785
786
787
788
  	err = rhashtable_init(&ht, &test_rht_params);
  	if (err < 0) {
  		pr_warn("Test failed: Unable to initialize hashtable: %d
  ",
  			err);
  		vfree(tdata);
  		vfree(objs);
  		return -EINVAL;
  	}
  	for (i = 0; i < tcount; i++) {
  		tdata[i].id = i;
f651616e7   Florian Westphal   test_rhashtable: ...
789
  		tdata[i].entries = entries;
f4a3e90ba   Phil Sutter   rhashtable-test: ...
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
  		tdata[i].objs = objs + i * entries;
  		tdata[i].task = kthread_run(threadfunc, &tdata[i],
  		                            "rhashtable_thrad[%d]", i);
  		if (IS_ERR(tdata[i].task))
  			pr_err(" kthread_run failed for thread %d
  ", i);
  		else
  			started_threads++;
  	}
  	if (down_interruptible(&prestart_sem))
  		pr_err("  down interruptible failed
  ");
  	for (i = 0; i < tcount; i++)
  		up(&startup_sem);
  	for (i = 0; i < tcount; i++) {
  		if (IS_ERR(tdata[i].task))
  			continue;
  		if ((err = kthread_stop(tdata[i].task))) {
  			pr_warn("Test failed: thread %d returned: %d
  ",
  			        i, err);
  			failed_threads++;
  		}
  	}
f4a3e90ba   Phil Sutter   rhashtable-test: ...
814
815
816
  	rhashtable_destroy(&ht);
  	vfree(tdata);
  	vfree(objs);
cdd4de372   Florian Westphal   test_rhashtable: ...
817
818
819
820
821
822
823
824
825
  
  	/*
  	 * rhltable_remove is very expensive, default values can cause test
  	 * to run for 2 minutes or more,  use a smaller number instead.
  	 */
  	err = test_rhltable(entries / 16);
  	pr_info("Started %d threads, %d failed, rhltable test returns %d
  ",
  	        started_threads, failed_threads, err);
1aa661f5c   Thomas Graf   rhashtable-test: ...
826
  	return 0;
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
827
  }
6dd0c1655   Daniel Borkmann   rhashtable: allow...
828
829
830
  static void __exit test_rht_exit(void)
  {
  }
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
831
  module_init(test_rht_init);
6dd0c1655   Daniel Borkmann   rhashtable: allow...
832
  module_exit(test_rht_exit);
9d6dbe1bb   Geert Uytterhoeven   rhashtable: Make ...
833
834
  
  MODULE_LICENSE("GPL v2");