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 <rmoar@google.com> 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 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 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 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 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 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 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 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 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 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