--- /dev/null
+#include <linux/rosebush.h>
+#include <kunit/test.h>
+
+static void iter_rbh(struct kunit *test, struct rbh *rbh, u32 hash, void *p)
+{
+ RBH_ITER(iter, rbh, hash);
+ void *q;
+
+ rcu_read_lock();
+ q = rbh_next(&iter);
+ KUNIT_EXPECT_PTR_EQ_MSG(test, p, q,
+ "rbh_next hash:%u returned %px, expected %px", hash, q, p);
+ q = rbh_next(&iter);
+ KUNIT_EXPECT_PTR_EQ_MSG(test, NULL, q,
+ "rbh_next hash:%u returned %px, expected NULL", hash, q);
+ rcu_read_unlock();
+}
+
+static void check_empty_rbh(struct kunit *test, struct rbh *rbh)
+{
+ iter_rbh(test, rbh, 0, NULL);
+ iter_rbh(test, rbh, 1, NULL);
+ iter_rbh(test, rbh, 17, NULL);
+ iter_rbh(test, rbh, 42, NULL);
+}
+
+static void test_insert(struct kunit *test, struct rbh *rbh, u32 hash)
+{
+ void *p = (void *)((hash << 1) | 1UL);
+ int err;
+
+ err = rbh_insert(rbh, hash, p);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ iter_rbh(test, rbh, hash, p);
+}
+
+static void test_reserve(struct kunit *test, struct rbh *rbh, u32 hash)
+{
+ int err;
+
+ err = rbh_reserve(rbh, hash);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ iter_rbh(test, rbh, hash, NULL);
+}
+
+static void test_use(struct kunit *test, struct rbh *rbh, u32 hash)
+{
+ void *p = (void *)((hash << 1) | 1UL);
+ int err;
+
+ err = rbh_use(rbh, hash, p);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ iter_rbh(test, rbh, hash, p);
+}
+
+static void test_remove(struct kunit *test, struct rbh *rbh, u32 hash)
+{
+ void *p = (void *)((hash << 1) | 1UL);
+ int err;
+
+ err = rbh_remove(rbh, hash, p);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ iter_rbh(test, rbh, hash, NULL);
+}
+
+static DEFINE_ROSEBUSH(rosebush);
+
+/*
+ * Conduct a number of tests on a rosebush that has never been used.
+ * They should all return NULL or an errno. We're looking for crashes
+ * here.
+ */
+static void empty(struct kunit *test)
+{
+ int err;
+
+ check_empty_rbh(test, &rosebush);
+ err = rbh_remove(&rosebush, 0, test);
+ KUNIT_EXPECT_EQ(test, err, -ENOENT);
+ err = rbh_use(&rosebush, 0, test);
+ KUNIT_EXPECT_EQ(test, err, -ENOENT);
+ KUNIT_EXPECT_EQ(test, rosebush.rbh_table, 0);
+}
+
+static void first(struct kunit *test)
+{
+ int err;
+
+ test_insert(test, &rosebush, 5);
+ check_empty_rbh(test, &rosebush);
+ test_remove(test, &rosebush, 5);
+ check_empty_rbh(test, &rosebush);
+
+ err = rbh_remove(&rosebush, 5, NULL);
+ KUNIT_EXPECT_EQ(test, err, -ENOENT);
+ test_reserve(test, &rosebush, 5);
+ err = rbh_remove(&rosebush, 5, test);
+ KUNIT_EXPECT_EQ(test, err, -ENOENT);
+ err = rbh_remove(&rosebush, 5, NULL);
+ KUNIT_EXPECT_EQ(test, err, 0);
+ err = rbh_remove(&rosebush, 5, NULL);
+ KUNIT_EXPECT_EQ(test, err, -ENOENT);
+
+ test_reserve(test, &rosebush, 5);
+ test_use(test, &rosebush, 5);
+ err = rbh_remove(&rosebush, 5, NULL);
+ KUNIT_EXPECT_EQ(test, err, -ENOENT);
+ test_remove(test, &rosebush, 5);
+}
+
+static void grow(struct kunit *test)
+{
+ int i;
+
+ for (i = 3; i < 3333; i += 2)
+ test_insert(test, &rosebush, i);
+
+ rbh_destroy(&rosebush);
+}
+
+static struct kunit_case rosebush_cases[] __refdata = {
+ KUNIT_CASE(empty),
+ KUNIT_CASE(first),
+ KUNIT_CASE(grow),
+ {}
+};
+
+static struct kunit_suite rosebush_suite = {
+ .name = "rosebush",
+ .test_cases = rosebush_cases,
+};
+
+kunit_test_suite(rosebush_suite);
+
+MODULE_AUTHOR("Matthew Wilcox (Oracle) <willy@infradead.org>");
+MODULE_LICENSE("GPL");