/* Size of log associated with test. */
 #define KUNIT_LOG_SIZE 512
 
+/* Maximum size of parameter description string. */
+#define KUNIT_PARAM_DESC_SIZE 128
+
 /*
  * TAP specifies subtest stream indentation of 4 spaces, 8 spaces for a
  * sub-subtest.  See the "Subtests" section in
  *
  * @run_case: the function representing the actual test case.
  * @name:     the name of the test case.
+ * @generate_params: the generator function for parameterized tests.
  *
  * A test case is a function with the signature,
  * ``void (*)(struct kunit *)``
 struct kunit_case {
        void (*run_case)(struct kunit *test);
        const char *name;
+       const void* (*generate_params)(const void *prev, char *desc);
 
        /* private: internal use only. */
        bool success;
  */
 #define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name }
 
+/**
+ * KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case
+ *
+ * @test_name: a reference to a test case function.
+ * @gen_params: a reference to a parameter generator function.
+ *
+ * The generator function::
+ *
+ *     const void* gen_params(const void *prev, char *desc)
+ *
+ * is used to lazily generate a series of arbitrarily typed values that fit into
+ * a void*. The argument @prev is the previously returned value, which should be
+ * used to derive the next value; @prev is set to NULL on the initial generator
+ * call. When no more values are available, the generator must return NULL.
+ * Optionally write a string into @desc (size of KUNIT_PARAM_DESC_SIZE)
+ * describing the parameter.
+ */
+#define KUNIT_CASE_PARAM(test_name, gen_params)                        \
+               { .run_case = test_name, .name = #test_name,    \
+                 .generate_params = gen_params }
+
 /**
  * struct kunit_suite - describes a related collection of &struct kunit_case
  *
        const char *name; /* Read only after initialization! */
        char *log; /* Points at case log after initialization */
        struct kunit_try_catch try_catch;
+       /* param_value is the current parameter value for a test case. */
+       const void *param_value;
+       /* param_index stores the index of the parameter in parameterized tests. */
+       int param_index;
        /*
         * success starts as true, and may only be set to false during a
         * test case; thus, it is safe to update this across multiple
                                                fmt,                           \
                                                ##__VA_ARGS__)
 
+/**
+ * KUNIT_ARRAY_PARAM() - Define test parameter generator from an array.
+ * @name:  prefix for the test parameter generator function.
+ * @array: array of test parameters.
+ * @get_desc: function to convert param to description; NULL to use default
+ *
+ * Define function @name_gen_params which uses @array to generate parameters.
+ */
+#define KUNIT_ARRAY_PARAM(name, array, get_desc)                                               \
+       static const void *name##_gen_params(const void *prev, char *desc)                      \
+       {                                                                                       \
+               typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);      \
+               if (__next - (array) < ARRAY_SIZE((array))) {                                   \
+                       void (*__get_desc)(typeof(__next), char *) = get_desc;                  \
+                       if (__get_desc)                                                         \
+                               __get_desc(__next, desc);                                       \
+                       return __next;                                                          \
+               }                                                                               \
+               return NULL;                                                                    \
+       }
+
 #endif /* _KUNIT_TEST_H */
 
  * occur in a test case and reports them as failures.
  */
 static void kunit_run_case_catch_errors(struct kunit_suite *suite,
-                                       struct kunit_case *test_case)
+                                       struct kunit_case *test_case,
+                                       struct kunit *test)
 {
        struct kunit_try_catch_context context;
        struct kunit_try_catch *try_catch;
-       struct kunit test;
 
-       kunit_init_test(&test, test_case->name, test_case->log);
-       try_catch = &test.try_catch;
+       kunit_init_test(test, test_case->name, test_case->log);
+       try_catch = &test->try_catch;
 
        kunit_try_catch_init(try_catch,
-                            &test,
+                            test,
                             kunit_try_run_case,
                             kunit_catch_run_case);
-       context.test = &test;
+       context.test = test;
        context.suite = suite;
        context.test_case = test_case;
        kunit_try_catch_run(try_catch, &context);
 
-       test_case->success = test.success;
-
-       kunit_print_ok_not_ok(&test, true, test_case->success,
-                             kunit_test_case_num(suite, test_case),
-                             test_case->name);
+       test_case->success = test->success;
 }
 
 int kunit_run_tests(struct kunit_suite *suite)
 {
+       char param_desc[KUNIT_PARAM_DESC_SIZE];
        struct kunit_case *test_case;
 
        kunit_print_subtest_start(suite);
 
-       kunit_suite_for_each_test_case(suite, test_case)
-               kunit_run_case_catch_errors(suite, test_case);
+       kunit_suite_for_each_test_case(suite, test_case) {
+               struct kunit test = { .param_value = NULL, .param_index = 0 };
+               bool test_success = true;
+
+               if (test_case->generate_params) {
+                       /* Get initial param. */
+                       param_desc[0] = '\0';
+                       test.param_value = test_case->generate_params(NULL, param_desc);
+               }
+
+               do {
+                       kunit_run_case_catch_errors(suite, test_case, &test);
+                       test_success &= test_case->success;
+
+                       if (test_case->generate_params) {
+                               if (param_desc[0] == '\0') {
+                                       snprintf(param_desc, sizeof(param_desc),
+                                                "param-%d", test.param_index);
+                               }
+
+                               kunit_log(KERN_INFO, &test,
+                                         KUNIT_SUBTEST_INDENT
+                                         "# %s: %s %d - %s",
+                                         test_case->name,
+                                         kunit_status_to_string(test.success),
+                                         test.param_index + 1, param_desc);
+
+                               /* Get next param. */
+                               param_desc[0] = '\0';
+                               test.param_value = test_case->generate_params(test.param_value, param_desc);
+                               test.param_index++;
+                       }
+               } while (test.param_value);
+
+               kunit_print_ok_not_ok(&test, true, test_success,
+                                     kunit_test_case_num(suite, test_case),
+                                     test_case->name);
+       }
 
        kunit_print_subtest_end(suite);