xref: /linux-6.15/lib/tests/hashtable_test.c (revision db6fe4d6)
1*db6fe4d6SKees Cook // SPDX-License-Identifier: GPL-2.0
2*db6fe4d6SKees Cook /*
3*db6fe4d6SKees Cook  * KUnit test for the Kernel Hashtable structures.
4*db6fe4d6SKees Cook  *
5*db6fe4d6SKees Cook  * Copyright (C) 2022, Google LLC.
6*db6fe4d6SKees Cook  * Author: Rae Moar <[email protected]>
7*db6fe4d6SKees Cook  */
8*db6fe4d6SKees Cook #include <kunit/test.h>
9*db6fe4d6SKees Cook 
10*db6fe4d6SKees Cook #include <linux/hashtable.h>
11*db6fe4d6SKees Cook 
12*db6fe4d6SKees Cook struct hashtable_test_entry {
13*db6fe4d6SKees Cook 	int key;
14*db6fe4d6SKees Cook 	int data;
15*db6fe4d6SKees Cook 	struct hlist_node node;
16*db6fe4d6SKees Cook 	int visited;
17*db6fe4d6SKees Cook };
18*db6fe4d6SKees Cook 
hashtable_test_hash_init(struct kunit * test)19*db6fe4d6SKees Cook static void hashtable_test_hash_init(struct kunit *test)
20*db6fe4d6SKees Cook {
21*db6fe4d6SKees Cook 	/* Test the different ways of initialising a hashtable. */
22*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash1, 2);
23*db6fe4d6SKees Cook 	DECLARE_HASHTABLE(hash2, 3);
24*db6fe4d6SKees Cook 
25*db6fe4d6SKees Cook 	/* When using DECLARE_HASHTABLE, must use hash_init to
26*db6fe4d6SKees Cook 	 * initialize the hashtable.
27*db6fe4d6SKees Cook 	 */
28*db6fe4d6SKees Cook 	hash_init(hash2);
29*db6fe4d6SKees Cook 
30*db6fe4d6SKees Cook 	KUNIT_EXPECT_TRUE(test, hash_empty(hash1));
31*db6fe4d6SKees Cook 	KUNIT_EXPECT_TRUE(test, hash_empty(hash2));
32*db6fe4d6SKees Cook }
33*db6fe4d6SKees Cook 
hashtable_test_hash_empty(struct kunit * test)34*db6fe4d6SKees Cook static void hashtable_test_hash_empty(struct kunit *test)
35*db6fe4d6SKees Cook {
36*db6fe4d6SKees Cook 	struct hashtable_test_entry a;
37*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash, 1);
38*db6fe4d6SKees Cook 
39*db6fe4d6SKees Cook 	KUNIT_EXPECT_TRUE(test, hash_empty(hash));
40*db6fe4d6SKees Cook 
41*db6fe4d6SKees Cook 	a.key = 1;
42*db6fe4d6SKees Cook 	a.data = 13;
43*db6fe4d6SKees Cook 	hash_add(hash, &a.node, a.key);
44*db6fe4d6SKees Cook 
45*db6fe4d6SKees Cook 	/* Hashtable should no longer be empty. */
46*db6fe4d6SKees Cook 	KUNIT_EXPECT_FALSE(test, hash_empty(hash));
47*db6fe4d6SKees Cook }
48*db6fe4d6SKees Cook 
hashtable_test_hash_hashed(struct kunit * test)49*db6fe4d6SKees Cook static void hashtable_test_hash_hashed(struct kunit *test)
50*db6fe4d6SKees Cook {
51*db6fe4d6SKees Cook 	struct hashtable_test_entry a, b;
52*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash, 4);
53*db6fe4d6SKees Cook 
54*db6fe4d6SKees Cook 	a.key = 1;
55*db6fe4d6SKees Cook 	a.data = 13;
56*db6fe4d6SKees Cook 	hash_add(hash, &a.node, a.key);
57*db6fe4d6SKees Cook 	b.key = 1;
58*db6fe4d6SKees Cook 	b.data = 2;
59*db6fe4d6SKees Cook 	hash_add(hash, &b.node, b.key);
60*db6fe4d6SKees Cook 
61*db6fe4d6SKees Cook 	KUNIT_EXPECT_TRUE(test, hash_hashed(&a.node));
62*db6fe4d6SKees Cook 	KUNIT_EXPECT_TRUE(test, hash_hashed(&b.node));
63*db6fe4d6SKees Cook }
64*db6fe4d6SKees Cook 
hashtable_test_hash_add(struct kunit * test)65*db6fe4d6SKees Cook static void hashtable_test_hash_add(struct kunit *test)
66*db6fe4d6SKees Cook {
67*db6fe4d6SKees Cook 	struct hashtable_test_entry a, b, *x;
68*db6fe4d6SKees Cook 	int bkt;
69*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash, 3);
70*db6fe4d6SKees Cook 
71*db6fe4d6SKees Cook 	a.key = 1;
72*db6fe4d6SKees Cook 	a.data = 13;
73*db6fe4d6SKees Cook 	a.visited = 0;
74*db6fe4d6SKees Cook 	hash_add(hash, &a.node, a.key);
75*db6fe4d6SKees Cook 	b.key = 2;
76*db6fe4d6SKees Cook 	b.data = 10;
77*db6fe4d6SKees Cook 	b.visited = 0;
78*db6fe4d6SKees Cook 	hash_add(hash, &b.node, b.key);
79*db6fe4d6SKees Cook 
80*db6fe4d6SKees Cook 	hash_for_each(hash, bkt, x, node) {
81*db6fe4d6SKees Cook 		x->visited++;
82*db6fe4d6SKees Cook 		if (x->key == a.key)
83*db6fe4d6SKees Cook 			KUNIT_EXPECT_EQ(test, x->data, 13);
84*db6fe4d6SKees Cook 		else if (x->key == b.key)
85*db6fe4d6SKees Cook 			KUNIT_EXPECT_EQ(test, x->data, 10);
86*db6fe4d6SKees Cook 		else
87*db6fe4d6SKees Cook 			KUNIT_FAIL(test, "Unexpected key in hashtable.");
88*db6fe4d6SKees Cook 	}
89*db6fe4d6SKees Cook 
90*db6fe4d6SKees Cook 	/* Both entries should have been visited exactly once. */
91*db6fe4d6SKees Cook 	KUNIT_EXPECT_EQ(test, a.visited, 1);
92*db6fe4d6SKees Cook 	KUNIT_EXPECT_EQ(test, b.visited, 1);
93*db6fe4d6SKees Cook }
94*db6fe4d6SKees Cook 
hashtable_test_hash_del(struct kunit * test)95*db6fe4d6SKees Cook static void hashtable_test_hash_del(struct kunit *test)
96*db6fe4d6SKees Cook {
97*db6fe4d6SKees Cook 	struct hashtable_test_entry a, b, *x;
98*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash, 6);
99*db6fe4d6SKees Cook 
100*db6fe4d6SKees Cook 	a.key = 1;
101*db6fe4d6SKees Cook 	a.data = 13;
102*db6fe4d6SKees Cook 	hash_add(hash, &a.node, a.key);
103*db6fe4d6SKees Cook 	b.key = 2;
104*db6fe4d6SKees Cook 	b.data = 10;
105*db6fe4d6SKees Cook 	b.visited = 0;
106*db6fe4d6SKees Cook 	hash_add(hash, &b.node, b.key);
107*db6fe4d6SKees Cook 
108*db6fe4d6SKees Cook 	hash_del(&b.node);
109*db6fe4d6SKees Cook 	hash_for_each_possible(hash, x, node, b.key) {
110*db6fe4d6SKees Cook 		x->visited++;
111*db6fe4d6SKees Cook 		KUNIT_EXPECT_NE(test, x->key, b.key);
112*db6fe4d6SKees Cook 	}
113*db6fe4d6SKees Cook 
114*db6fe4d6SKees Cook 	/* The deleted entry should not have been visited. */
115*db6fe4d6SKees Cook 	KUNIT_EXPECT_EQ(test, b.visited, 0);
116*db6fe4d6SKees Cook 
117*db6fe4d6SKees Cook 	hash_del(&a.node);
118*db6fe4d6SKees Cook 
119*db6fe4d6SKees Cook 	/* The hashtable should be empty. */
120*db6fe4d6SKees Cook 	KUNIT_EXPECT_TRUE(test, hash_empty(hash));
121*db6fe4d6SKees Cook }
122*db6fe4d6SKees Cook 
hashtable_test_hash_for_each(struct kunit * test)123*db6fe4d6SKees Cook static void hashtable_test_hash_for_each(struct kunit *test)
124*db6fe4d6SKees Cook {
125*db6fe4d6SKees Cook 	struct hashtable_test_entry entries[3];
126*db6fe4d6SKees Cook 	struct hashtable_test_entry *x;
127*db6fe4d6SKees Cook 	int bkt, i, j, count;
128*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash, 3);
129*db6fe4d6SKees Cook 
130*db6fe4d6SKees Cook 	/* Add three entries to the hashtable. */
131*db6fe4d6SKees Cook 	for (i = 0; i < 3; i++) {
132*db6fe4d6SKees Cook 		entries[i].key = i;
133*db6fe4d6SKees Cook 		entries[i].data = i + 10;
134*db6fe4d6SKees Cook 		entries[i].visited = 0;
135*db6fe4d6SKees Cook 		hash_add(hash, &entries[i].node, entries[i].key);
136*db6fe4d6SKees Cook 	}
137*db6fe4d6SKees Cook 
138*db6fe4d6SKees Cook 	count = 0;
139*db6fe4d6SKees Cook 	hash_for_each(hash, bkt, x, node) {
140*db6fe4d6SKees Cook 		x->visited += 1;
141*db6fe4d6SKees Cook 		KUNIT_ASSERT_GE_MSG(test, x->key, 0, "Unexpected key in hashtable.");
142*db6fe4d6SKees Cook 		KUNIT_ASSERT_LT_MSG(test, x->key, 3, "Unexpected key in hashtable.");
143*db6fe4d6SKees Cook 		count++;
144*db6fe4d6SKees Cook 	}
145*db6fe4d6SKees Cook 
146*db6fe4d6SKees Cook 	/* Should have visited each entry exactly once. */
147*db6fe4d6SKees Cook 	KUNIT_EXPECT_EQ(test, count, 3);
148*db6fe4d6SKees Cook 	for (j = 0; j < 3; j++)
149*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, entries[j].visited, 1);
150*db6fe4d6SKees Cook }
151*db6fe4d6SKees Cook 
hashtable_test_hash_for_each_safe(struct kunit * test)152*db6fe4d6SKees Cook static void hashtable_test_hash_for_each_safe(struct kunit *test)
153*db6fe4d6SKees Cook {
154*db6fe4d6SKees Cook 	struct hashtable_test_entry entries[3];
155*db6fe4d6SKees Cook 	struct hashtable_test_entry *x;
156*db6fe4d6SKees Cook 	struct hlist_node *tmp;
157*db6fe4d6SKees Cook 	int bkt, i, j, count;
158*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash, 3);
159*db6fe4d6SKees Cook 
160*db6fe4d6SKees Cook 	/* Add three entries to the hashtable. */
161*db6fe4d6SKees Cook 	for (i = 0; i < 3; i++) {
162*db6fe4d6SKees Cook 		entries[i].key = i;
163*db6fe4d6SKees Cook 		entries[i].data = i + 10;
164*db6fe4d6SKees Cook 		entries[i].visited = 0;
165*db6fe4d6SKees Cook 		hash_add(hash, &entries[i].node, entries[i].key);
166*db6fe4d6SKees Cook 	}
167*db6fe4d6SKees Cook 
168*db6fe4d6SKees Cook 	count = 0;
169*db6fe4d6SKees Cook 	hash_for_each_safe(hash, bkt, tmp, x, node) {
170*db6fe4d6SKees Cook 		x->visited += 1;
171*db6fe4d6SKees Cook 		KUNIT_ASSERT_GE_MSG(test, x->key, 0, "Unexpected key in hashtable.");
172*db6fe4d6SKees Cook 		KUNIT_ASSERT_LT_MSG(test, x->key, 3, "Unexpected key in hashtable.");
173*db6fe4d6SKees Cook 		count++;
174*db6fe4d6SKees Cook 
175*db6fe4d6SKees Cook 		/* Delete entry during loop. */
176*db6fe4d6SKees Cook 		hash_del(&x->node);
177*db6fe4d6SKees Cook 	}
178*db6fe4d6SKees Cook 
179*db6fe4d6SKees Cook 	/* Should have visited each entry exactly once. */
180*db6fe4d6SKees Cook 	KUNIT_EXPECT_EQ(test, count, 3);
181*db6fe4d6SKees Cook 	for (j = 0; j < 3; j++)
182*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, entries[j].visited, 1);
183*db6fe4d6SKees Cook }
184*db6fe4d6SKees Cook 
hashtable_test_hash_for_each_possible(struct kunit * test)185*db6fe4d6SKees Cook static void hashtable_test_hash_for_each_possible(struct kunit *test)
186*db6fe4d6SKees Cook {
187*db6fe4d6SKees Cook 	struct hashtable_test_entry entries[4];
188*db6fe4d6SKees Cook 	struct hashtable_test_entry *x, *y;
189*db6fe4d6SKees Cook 	int buckets[2];
190*db6fe4d6SKees Cook 	int bkt, i, j, count;
191*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash, 5);
192*db6fe4d6SKees Cook 
193*db6fe4d6SKees Cook 	/* Add three entries with key = 0 to the hashtable. */
194*db6fe4d6SKees Cook 	for (i = 0; i < 3; i++) {
195*db6fe4d6SKees Cook 		entries[i].key = 0;
196*db6fe4d6SKees Cook 		entries[i].data = i;
197*db6fe4d6SKees Cook 		entries[i].visited = 0;
198*db6fe4d6SKees Cook 		hash_add(hash, &entries[i].node, entries[i].key);
199*db6fe4d6SKees Cook 	}
200*db6fe4d6SKees Cook 
201*db6fe4d6SKees Cook 	/* Add an entry with key = 1. */
202*db6fe4d6SKees Cook 	entries[3].key = 1;
203*db6fe4d6SKees Cook 	entries[3].data = 3;
204*db6fe4d6SKees Cook 	entries[3].visited = 0;
205*db6fe4d6SKees Cook 	hash_add(hash, &entries[3].node, entries[3].key);
206*db6fe4d6SKees Cook 
207*db6fe4d6SKees Cook 	count = 0;
208*db6fe4d6SKees Cook 	hash_for_each_possible(hash, x, node, 0) {
209*db6fe4d6SKees Cook 		x->visited += 1;
210*db6fe4d6SKees Cook 		KUNIT_ASSERT_GE_MSG(test, x->data, 0, "Unexpected data in hashtable.");
211*db6fe4d6SKees Cook 		KUNIT_ASSERT_LT_MSG(test, x->data, 4, "Unexpected data in hashtable.");
212*db6fe4d6SKees Cook 		count++;
213*db6fe4d6SKees Cook 	}
214*db6fe4d6SKees Cook 
215*db6fe4d6SKees Cook 	/* Should have visited each entry with key = 0 exactly once. */
216*db6fe4d6SKees Cook 	for (j = 0; j < 3; j++)
217*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, entries[j].visited, 1);
218*db6fe4d6SKees Cook 
219*db6fe4d6SKees Cook 	/* Save the buckets for the different keys. */
220*db6fe4d6SKees Cook 	hash_for_each(hash, bkt, y, node) {
221*db6fe4d6SKees Cook 		KUNIT_ASSERT_GE_MSG(test, y->key, 0, "Unexpected key in hashtable.");
222*db6fe4d6SKees Cook 		KUNIT_ASSERT_LE_MSG(test, y->key, 1, "Unexpected key in hashtable.");
223*db6fe4d6SKees Cook 		buckets[y->key] = bkt;
224*db6fe4d6SKees Cook 	}
225*db6fe4d6SKees Cook 
226*db6fe4d6SKees Cook 	/* If entry with key = 1 is in the same bucket as the entries with
227*db6fe4d6SKees Cook 	 * key = 0, check it was visited. Otherwise ensure that only three
228*db6fe4d6SKees Cook 	 * entries were visited.
229*db6fe4d6SKees Cook 	 */
230*db6fe4d6SKees Cook 	if (buckets[0] == buckets[1]) {
231*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, count, 4);
232*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, entries[3].visited, 1);
233*db6fe4d6SKees Cook 	} else {
234*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, count, 3);
235*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, entries[3].visited, 0);
236*db6fe4d6SKees Cook 	}
237*db6fe4d6SKees Cook }
238*db6fe4d6SKees Cook 
hashtable_test_hash_for_each_possible_safe(struct kunit * test)239*db6fe4d6SKees Cook static void hashtable_test_hash_for_each_possible_safe(struct kunit *test)
240*db6fe4d6SKees Cook {
241*db6fe4d6SKees Cook 	struct hashtable_test_entry entries[4];
242*db6fe4d6SKees Cook 	struct hashtable_test_entry *x, *y;
243*db6fe4d6SKees Cook 	struct hlist_node *tmp;
244*db6fe4d6SKees Cook 	int buckets[2];
245*db6fe4d6SKees Cook 	int bkt, i, j, count;
246*db6fe4d6SKees Cook 	DEFINE_HASHTABLE(hash, 5);
247*db6fe4d6SKees Cook 
248*db6fe4d6SKees Cook 	/* Add three entries with key = 0 to the hashtable. */
249*db6fe4d6SKees Cook 	for (i = 0; i < 3; i++) {
250*db6fe4d6SKees Cook 		entries[i].key = 0;
251*db6fe4d6SKees Cook 		entries[i].data = i;
252*db6fe4d6SKees Cook 		entries[i].visited = 0;
253*db6fe4d6SKees Cook 		hash_add(hash, &entries[i].node, entries[i].key);
254*db6fe4d6SKees Cook 	}
255*db6fe4d6SKees Cook 
256*db6fe4d6SKees Cook 	/* Add an entry with key = 1. */
257*db6fe4d6SKees Cook 	entries[3].key = 1;
258*db6fe4d6SKees Cook 	entries[3].data = 3;
259*db6fe4d6SKees Cook 	entries[3].visited = 0;
260*db6fe4d6SKees Cook 	hash_add(hash, &entries[3].node, entries[3].key);
261*db6fe4d6SKees Cook 
262*db6fe4d6SKees Cook 	count = 0;
263*db6fe4d6SKees Cook 	hash_for_each_possible_safe(hash, x, tmp, node, 0) {
264*db6fe4d6SKees Cook 		x->visited += 1;
265*db6fe4d6SKees Cook 		KUNIT_ASSERT_GE_MSG(test, x->data, 0, "Unexpected data in hashtable.");
266*db6fe4d6SKees Cook 		KUNIT_ASSERT_LT_MSG(test, x->data, 4, "Unexpected data in hashtable.");
267*db6fe4d6SKees Cook 		count++;
268*db6fe4d6SKees Cook 
269*db6fe4d6SKees Cook 		/* Delete entry during loop. */
270*db6fe4d6SKees Cook 		hash_del(&x->node);
271*db6fe4d6SKees Cook 	}
272*db6fe4d6SKees Cook 
273*db6fe4d6SKees Cook 	/* Should have visited each entry with key = 0 exactly once. */
274*db6fe4d6SKees Cook 	for (j = 0; j < 3; j++)
275*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, entries[j].visited, 1);
276*db6fe4d6SKees Cook 
277*db6fe4d6SKees Cook 	/* Save the buckets for the different keys. */
278*db6fe4d6SKees Cook 	hash_for_each(hash, bkt, y, node) {
279*db6fe4d6SKees Cook 		KUNIT_ASSERT_GE_MSG(test, y->key, 0, "Unexpected key in hashtable.");
280*db6fe4d6SKees Cook 		KUNIT_ASSERT_LE_MSG(test, y->key, 1, "Unexpected key in hashtable.");
281*db6fe4d6SKees Cook 		buckets[y->key] = bkt;
282*db6fe4d6SKees Cook 	}
283*db6fe4d6SKees Cook 
284*db6fe4d6SKees Cook 	/* If entry with key = 1 is in the same bucket as the entries with
285*db6fe4d6SKees Cook 	 * key = 0, check it was visited. Otherwise ensure that only three
286*db6fe4d6SKees Cook 	 * entries were visited.
287*db6fe4d6SKees Cook 	 */
288*db6fe4d6SKees Cook 	if (buckets[0] == buckets[1]) {
289*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, count, 4);
290*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, entries[3].visited, 1);
291*db6fe4d6SKees Cook 	} else {
292*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, count, 3);
293*db6fe4d6SKees Cook 		KUNIT_EXPECT_EQ(test, entries[3].visited, 0);
294*db6fe4d6SKees Cook 	}
295*db6fe4d6SKees Cook }
296*db6fe4d6SKees Cook 
297*db6fe4d6SKees Cook static struct kunit_case hashtable_test_cases[] = {
298*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_init),
299*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_empty),
300*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_hashed),
301*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_add),
302*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_del),
303*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_for_each),
304*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_for_each_safe),
305*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_for_each_possible),
306*db6fe4d6SKees Cook 	KUNIT_CASE(hashtable_test_hash_for_each_possible_safe),
307*db6fe4d6SKees Cook 	{},
308*db6fe4d6SKees Cook };
309*db6fe4d6SKees Cook 
310*db6fe4d6SKees Cook static struct kunit_suite hashtable_test_module = {
311*db6fe4d6SKees Cook 	.name = "hashtable",
312*db6fe4d6SKees Cook 	.test_cases = hashtable_test_cases,
313*db6fe4d6SKees Cook };
314*db6fe4d6SKees Cook 
315*db6fe4d6SKees Cook kunit_test_suites(&hashtable_test_module);
316*db6fe4d6SKees Cook 
317*db6fe4d6SKees Cook MODULE_DESCRIPTION("KUnit test for the Kernel Hashtable structures");
318*db6fe4d6SKees Cook MODULE_LICENSE("GPL");
319