Create a common API for test managed resources like memory and test
objects. A lot of times a test will want to set up infrastructure to be
used in test cases; this could be anything from just wanting to allocate
some memory to setting up a driver stack; this defines facilities for
creating "test resources" which are managed by the test infrastructure
and are automatically cleaned up at the conclusion of the test.

Signed-off-by: Brendan Higgins <brendanhigg...@google.com>
---
 include/kunit/test.h | 109 +++++++++++++++++++++++++++++++++++++++++++
 kunit/test.c         |  95 +++++++++++++++++++++++++++++++++++++
 2 files changed, 204 insertions(+)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index ffe66bb355d63..583840e24ffda 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -12,6 +12,69 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 
+struct kunit_resource;
+
+typedef int (*kunit_resource_init_t)(struct kunit_resource *, void *);
+typedef void (*kunit_resource_free_t)(struct kunit_resource *);
+
+/**
+ * struct kunit_resource - represents a *test managed resource*
+ * @allocation: for the user to store arbitrary data.
+ * @free: a user supplied function to free the resource. Populated by
+ * kunit_alloc_resource().
+ *
+ * Represents a *test managed resource*, a resource which will automatically be
+ * cleaned up at the end of a test case.
+ *
+ * Example:
+ *
+ * .. code-block:: c
+ *
+ *     struct kunit_kmalloc_params {
+ *             size_t size;
+ *             gfp_t gfp;
+ *     };
+ *
+ *     static int kunit_kmalloc_init(struct kunit_resource *res, void *context)
+ *     {
+ *             struct kunit_kmalloc_params *params = context;
+ *             res->allocation = kmalloc(params->size, params->gfp);
+ *
+ *             if (!res->allocation)
+ *                     return -ENOMEM;
+ *
+ *             return 0;
+ *     }
+ *
+ *     static void kunit_kmalloc_free(struct kunit_resource *res)
+ *     {
+ *             kfree(res->allocation);
+ *     }
+ *
+ *     void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp)
+ *     {
+ *             struct kunit_kmalloc_params params;
+ *             struct kunit_resource *res;
+ *
+ *             params.size = size;
+ *             params.gfp = gfp;
+ *
+ *             res = kunit_alloc_resource(test, kunit_kmalloc_init,
+ *                     kunit_kmalloc_free, &params);
+ *             if (res)
+ *                     return res->allocation;
+ *             else
+ *                     return NULL;
+ *     }
+ */
+struct kunit_resource {
+       void *allocation;
+       kunit_resource_free_t free;
+
+       /* private: internal use only. */
+       struct list_head node;
+};
+
 struct kunit;
 
 /**
@@ -104,6 +167,7 @@ struct kunit {
        const char *name; /* Read only after initialization! */
        spinlock_t lock; /* Gaurds all mutable test state. */
        bool success; /* Protected by lock. */
+       struct list_head resources; /* Protected by lock. */
        void (*vprintk)(const struct kunit *test,
                        const char *level,
                        struct va_format *vaf);
@@ -127,6 +191,51 @@ int kunit_run_tests(struct kunit_module *module);
                } \
                late_initcall(module_kunit_init##module)
 
+/**
+ * kunit_alloc_resource() - Allocates a *test managed resource*.
+ * @test: The test context object.
+ * @init: a user supplied function to initialize the resource.
+ * @free: a user supplied function to free the resource.
+ * @context: for the user to pass in arbitrary data.
+ *
+ * Allocates a *test managed resource*, a resource which will automatically be
+ * cleaned up at the end of a test case. See &struct kunit_resource for an
+ * example.
+ */
+struct kunit_resource *kunit_alloc_resource(struct kunit *test,
+                                           kunit_resource_init_t init,
+                                           kunit_resource_free_t free,
+                                           void *context);
+
+void kunit_free_resource(struct kunit *test, struct kunit_resource *res);
+
+/**
+ * kunit_kmalloc() - Like kmalloc() except the allocation is *test managed*.
+ * @test: The test context object.
+ * @size: The size in bytes of the desired memory.
+ * @gfp: flags passed to underlying kmalloc().
+ *
+ * Just like `kmalloc(...)`, except the allocation is managed by the test case
+ * and is automatically cleaned up after the test case concludes. See &struct
+ * kunit_resource for more information.
+ */
+void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp);
+
+/**
+ * kunit_kzalloc() - Just like kunit_kmalloc(), but zeroes the allocation.
+ * @test: The test context object.
+ * @size: The size in bytes of the desired memory.
+ * @gfp: flags passed to underlying kmalloc().
+ *
+ * See kzalloc() and kunit_kmalloc() for more information.
+ */
+static inline void *kunit_kzalloc(struct kunit *test, size_t size, gfp_t gfp)
+{
+       return kunit_kmalloc(test, size, gfp | __GFP_ZERO);
+}
+
+void kunit_cleanup(struct kunit *test);
+
 void __printf(3, 4) kunit_printk(const char *level,
                                 const struct kunit *test,
                                 const char *fmt, ...);
diff --git a/kunit/test.c b/kunit/test.c
index 26d3d6d260e6c..fb1a786e4c94f 100644
--- a/kunit/test.c
+++ b/kunit/test.c
@@ -66,6 +66,7 @@ static void kunit_vprintk(const struct kunit *test,
 int kunit_init_test(struct kunit *test, const char *name)
 {
        spin_lock_init(&test->lock);
+       INIT_LIST_HEAD(&test->resources);
        test->name = name;
        test->vprintk = kunit_vprintk;
 
@@ -93,6 +94,11 @@ static void kunit_run_case_internal(struct kunit *test,
        test_case->run_case(test);
 }
 
+static void kunit_case_internal_cleanup(struct kunit *test)
+{
+       kunit_cleanup(test);
+}
+
 /*
  * Performs post validations and cleanup after a test case was run.
  * XXX: Should ONLY BE CALLED AFTER kunit_run_case_internal!
@@ -103,6 +109,8 @@ static void kunit_run_case_cleanup(struct kunit *test,
 {
        if (module->exit)
                module->exit(test);
+
+       kunit_case_internal_cleanup(test);
 }
 
 /*
@@ -150,6 +158,93 @@ int kunit_run_tests(struct kunit_module *module)
        return 0;
 }
 
+struct kunit_resource *kunit_alloc_resource(struct kunit *test,
+                                           kunit_resource_init_t init,
+                                           kunit_resource_free_t free,
+                                           void *context)
+{
+       struct kunit_resource *res;
+       unsigned long flags;
+       int ret;
+
+       res = kzalloc(sizeof(*res), GFP_KERNEL);
+       if (!res)
+               return NULL;
+
+       ret = init(res, context);
+       if (ret)
+               return NULL;
+
+       res->free = free;
+       spin_lock_irqsave(&test->lock, flags);
+       list_add_tail(&res->node, &test->resources);
+       spin_unlock_irqrestore(&test->lock, flags);
+
+       return res;
+}
+
+void kunit_free_resource(struct kunit *test, struct kunit_resource *res)
+{
+       res->free(res);
+       list_del(&res->node);
+       kfree(res);
+}
+
+struct kunit_kmalloc_params {
+       size_t size;
+       gfp_t gfp;
+};
+
+static int kunit_kmalloc_init(struct kunit_resource *res, void *context)
+{
+       struct kunit_kmalloc_params *params = context;
+
+       res->allocation = kmalloc(params->size, params->gfp);
+       if (!res->allocation)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void kunit_kmalloc_free(struct kunit_resource *res)
+{
+       kfree(res->allocation);
+}
+
+void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp)
+{
+       struct kunit_kmalloc_params params;
+       struct kunit_resource *res;
+
+       params.size = size;
+       params.gfp = gfp;
+
+       res = kunit_alloc_resource(test,
+                                  kunit_kmalloc_init,
+                                  kunit_kmalloc_free,
+                                  &params);
+
+       if (res)
+               return res->allocation;
+       else
+               return NULL;
+}
+
+void kunit_cleanup(struct kunit *test)
+{
+       struct kunit_resource *resource, *resource_safe;
+       unsigned long flags;
+
+       spin_lock_irqsave(&test->lock, flags);
+       list_for_each_entry_safe(resource,
+                                resource_safe,
+                                &test->resources,
+                                node) {
+               kunit_free_resource(test, resource);
+       }
+       spin_unlock_irqrestore(&test->lock, flags);
+}
+
 void kunit_printk(const char *level,
                  const struct kunit *test,
                  const char *fmt, ...)
-- 
2.20.0.rc0.387.gc7a69e6b6c-goog

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to