From patchwork Wed Jul 17 01:55:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 11046917 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 050BA13BD for ; Wed, 17 Jul 2019 01:56:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E667028702 for ; Wed, 17 Jul 2019 01:56:01 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D7D1828708; Wed, 17 Jul 2019 01:56:01 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3B05328720 for ; Wed, 17 Jul 2019 01:56:01 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 5B147212BF55C; Tue, 16 Jul 2019 18:58:29 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::54a; helo=mail-pg1-x54a.google.com; envelope-from=3loauxq4kdnc4k7g63gab99bgl9hh9e7.5hfebgnq-go6bffeblml.tu.hk9@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-pg1-x54a.google.com (mail-pg1-x54a.google.com [IPv6:2607:f8b0:4864:20::54a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 86CF0212BF553 for ; Tue, 16 Jul 2019 18:58:27 -0700 (PDT) Received: by mail-pg1-x54a.google.com with SMTP id h5so13723157pgq.23 for ; Tue, 16 Jul 2019 18:55:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=o0gC2EZck0lzXt9O6xQ/kQy7+j1XmoVzEMRvT3kmyD8=; b=gGwlL/Q8lFHppJlChD/7Gpk6INebYO0xwgl3+RmlR4k9Jf5okuUYdcc2MxRbc4uVm+ jEkK7J0Hjal5+poWNch49oMYg2NNaK7YFV5v6vGiG/sfUL6fRrqUaGv9mHD9aZxvi2Mw blKlnR6buESd450ogDBJHKyad5+Gk1oowbaL2s4iRWVmhskBR/3yzHxQKMki+e060Zgr WI3SctAGmj+YNVYcYUgnF1scttR4t1nJM0AeOavEubcZ1O7zKgfDZRLdrDynLoeq3Fn4 ui/qIU8rgMboDU4ldQIa8MZ6VkIZyBQcGVSzhvkktg7Tq+BA5iYl/puv3fswsOnoFfGo FLpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=o0gC2EZck0lzXt9O6xQ/kQy7+j1XmoVzEMRvT3kmyD8=; b=mLpeTS8rK/lrNll2jkav5owrGzDLMniZfLH5EsuOB8DzQaqOK+dClXYQtVazV1MORP DlVo0X9k7+/Unud104yPwN96cJYNgTOYk95B17hhnUniacqYSjZJBhEQqr8oWtFOT8T7 m+V7xPQE2iCBESPwjIcBQR5S2HLr4qxBETA2hEZydKCZgcMvIp2dak+E2R2FNFTN5eaY czhmK8DTF/0mkA108TkdrJ1I8H6WK4MALaiQGoYLbFcuUyHSC1qseRgo29GaBviJZvhr hwB/3y52CVOb9yFgGChPuYpX8p/1uGibQgvJ+9cjoPBYbM/mHNqt7yGHBIw8mMxITK5Q wmXg== X-Gm-Message-State: APjAAAWK73MCzc5tUqK8FIxnYaEWyJkPtSQ63xSQUT+Mz3n14aBLW17F ehQpLBRfp4j2EdMw8W4kbwZahfrQVP2dwTXisrGmTA== X-Google-Smtp-Source: APXvYqzzk9t0qR2ers20EPrOzletizg/t01hJblTw0Tnrtc6lcyP+77G+oNfjDMTVDyf0xt9d05ng+N6zNJyK/nW6Hb/0Q== X-Received: by 2002:a63:d30f:: with SMTP id b15mr37289960pgg.341.1563328558322; Tue, 16 Jul 2019 18:55:58 -0700 (PDT) Date: Tue, 16 Jul 2019 18:55:27 -0700 In-Reply-To: <20190717015543.152251-1-brendanhiggins@google.com> Message-Id: <20190717015543.152251-3-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190717015543.152251-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.22.0.510.g264f2c817a-goog Subject: [PATCH v11 02/18] kunit: test: add test resource management API From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, jpoimboe@redhat.com, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, peterz@infradead.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org, tytso@mit.edu, yamada.masahiro@socionext.com X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, rdunlap@infradead.org, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP 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 Reviewed-by: Greg Kroah-Hartman Reviewed-by: Logan Gunthorpe --- include/kunit/test.h | 143 +++++++++++++++++++++++++++++++++++++++++++ kunit/test.c | 92 ++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) diff --git a/include/kunit/test.h b/include/kunit/test.h index e0b34acb9ee4e..d0bf112910caf 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -10,6 +10,70 @@ #define _KUNIT_TEST_H #include +#include + +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, ¶ms); + * if (res) + * return res->allocation; + * + * return NULL; + * } + */ +struct kunit_resource { + void *allocation; + kunit_resource_free_t free; + + /* private: internal use only. */ + struct list_head node; +}; struct kunit; @@ -109,6 +173,13 @@ struct kunit { * have terminated. */ bool success; /* Read only after test_case finishes! */ + spinlock_t lock; /* Gaurds all mutable test state. */ + /* + * Because resources is a list that may be updated multiple times (with + * new resources) from any thread associated with a test case, we must + * protect it with some type of lock. + */ + struct list_head resources; /* Protected by lock. */ }; void kunit_init_test(struct kunit *test, const char *name); @@ -141,6 +212,78 @@ int kunit_run_tests(struct kunit_suite *suite); } \ late_initcall(kunit_suite_init##suite) +/* + * Like kunit_alloc_resource() below, but returns the struct kunit_resource + * object that contains the allocation. This is mostly for testing purposes. + */ +struct kunit_resource *kunit_alloc_and_get_resource(struct kunit *test, + kunit_resource_init_t init, + kunit_resource_free_t free, + gfp_t internal_gfp, + void *context); + +/** + * 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. + * @internal_gfp: gfp to use for internal allocations, if unsure, use GFP_KERNEL + * @context: for the user to pass in arbitrary data to the init function. + * + * 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. + * + * NOTE: KUnit needs to allocate memory for each kunit_resource object. You must + * specify an @internal_gfp that is compatible with the use context of your + * resource. + */ +static inline void *kunit_alloc_resource(struct kunit *test, + kunit_resource_init_t init, + kunit_resource_free_t free, + gfp_t internal_gfp, + void *context) +{ + struct kunit_resource *res; + + res = kunit_alloc_and_get_resource(test, init, free, internal_gfp, + context); + + if (res) + return res->allocation; + + return NULL; +} + +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 d302cff0f1dc7..4c178a817f2fe 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -122,6 +122,8 @@ static void kunit_print_test_case_ok_not_ok(struct kunit_case *test_case, void kunit_init_test(struct kunit *test, const char *name) { + spin_lock_init(&test->lock); + INIT_LIST_HEAD(&test->resources); test->name = name; test->success = true; } @@ -153,6 +155,8 @@ static void kunit_run_case(struct kunit_suite *suite, if (suite->exit) suite->exit(&test); + kunit_cleanup(&test); + test_case->success = test.success; } @@ -173,6 +177,94 @@ int kunit_run_tests(struct kunit_suite *suite) return 0; } +struct kunit_resource *kunit_alloc_and_get_resource(struct kunit *test, + kunit_resource_init_t init, + kunit_resource_free_t free, + gfp_t internal_gfp, + void *context) +{ + struct kunit_resource *res; + int ret; + + res = kzalloc(sizeof(*res), internal_gfp); + if (!res) + return NULL; + + ret = init(res, context); + if (ret) + return NULL; + + res->free = free; + spin_lock(&test->lock); + list_add_tail(&res->node, &test->resources); + spin_unlock(&test->lock); + + return res; +} + +void kunit_free_resource(struct kunit *test, struct kunit_resource *res) +{ + lockdep_assert_held(&test->lock); + + 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 = { + .size = size, + .gfp = gfp + }; + + return kunit_alloc_resource(test, + kunit_kmalloc_init, + kunit_kmalloc_free, + gfp, + ¶ms); +} + +void kunit_cleanup(struct kunit *test) +{ + struct kunit_resource *resource, *resource_safe; + + spin_lock(&test->lock); + /* + * test->resources is a stack - each allocation must be freed in the + * reverse order from which it was added since one resource may depend + * on another for its entire lifetime. + */ + list_for_each_entry_safe_reverse(resource, + resource_safe, + &test->resources, + node) { + kunit_free_resource(test, resource); + } + spin_unlock(&test->lock); +} + void kunit_printk(const char *level, const struct kunit *test, const char *fmt, ...)