Received: by 2002:a25:ab43:0:0:0:0:0 with SMTP id u61csp2256995ybi; Mon, 17 Jun 2019 01:27:26 -0700 (PDT) X-Google-Smtp-Source: APXvYqx3YgdB8ZFVjNcFE44pOIPPxdVm4UU0no+ihMe6tEekJCUdLtAXreWOkRJcboJYWUaPgycV X-Received: by 2002:a17:90a:bb8b:: with SMTP id v11mr18230920pjr.64.1560760046494; Mon, 17 Jun 2019 01:27:26 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1560760046; cv=none; d=google.com; s=arc-20160816; b=JRXYyEsoVtpx66MmjGm841OeFjlC2bMUXb4XUI3QtX34JuiFHIe2SSnUT4q+xSpPJa JSVOTvUmcGJdM912f0ueYkF4crpk4wA1+RqV9/qHWQWzIzz3WFiublY08/9csXPwR5NQ TDboFMwGU/XJm3gro14IyMqQXwSkbiJ+OkByC32XWRO+3XaSxFqaqAjOP/e7PLUKb35p wzstQ4jsRBR1lqjdagUfLaGESkWwWQKTQ5cG9wylvnoQwHG9DqFNW7yeSf+nhqxIYjYS 58MyJkOxuKXfUa14FK3WoiWyEdYAXCsQYTI37arfKbdI09KgikqR9HLIYiIhMt14Jy2c dEhA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:from:subject:references :mime-version:message-id:in-reply-to:date:dkim-signature; bh=upKJS7mvDrOGdiR22YXOIMfT67Z7sYkgnOz7jdyIx/M=; b=xowR3rtD5FOgDbkGRU0LovrQmoWzd3dJIMGpiWiOjs/Z02CArRDoqQS6zyT6A4Hwf3 /7V/x6lF0DOQIcGmB+DjMXiyQEISs28/0hlq+X1TFSvTd/bDnv2toL+JdLXqF8CqlhHu KsGf1gDQ7eG8TcwjO4YF1vDDf34rFS+dfM2pzt9fPUmnCcJQXwJyLgephlSrvdn+PoH8 kRKdcXFRioN+NMS+6fB4N9z7vveCiT84BZ+cEef3Rd+iMNA6JOpja8us1qUB5Ou62PvU mCl/TC4+pbP5/cvaC8bFeivg5aiE+Kvbzz5y1vc+qW08T6VJVuVRlDPdpDYrMUs1oZyi lzOA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=uqINFloR; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k16si9204224pls.326.2019.06.17.01.27.11; Mon, 17 Jun 2019 01:27:26 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=uqINFloR; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727676AbfFQI0s (ORCPT + 99 others); Mon, 17 Jun 2019 04:26:48 -0400 Received: from mail-qt1-f202.google.com ([209.85.160.202]:47800 "EHLO mail-qt1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727669AbfFQI0r (ORCPT ); Mon, 17 Jun 2019 04:26:47 -0400 Received: by mail-qt1-f202.google.com with SMTP id s9so8584125qtn.14 for ; Mon, 17 Jun 2019 01:26:45 -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=upKJS7mvDrOGdiR22YXOIMfT67Z7sYkgnOz7jdyIx/M=; b=uqINFloRHQ2K581xNH7a9nPKCRCRUPS98r4jMuKoChYHg//G+tbmShkygusTVklc99 JeBXfGf7/ushhngLyS8wr8Vw8WE/zJ4yol0wn8iKaCHK+Fmb66J1RNxBAhD7tuMWlMSY HPOLZ2Icw4klAi3rMu1GmovCjypxUvt/fYPcb/PhfGYEV7D9/nFapF6U5GmkPEHtgi1t qb0iW5FnZ6Qp0lLW+DjAJcCFTy/q98vCy1BjDV3VCHHvm5Q27T5AMS7xMcyK8xTUkNyW /q+A9OdotlOyBCWgpPNnceCfXWnUXJ4yXGP6YIZot55IUMklFGnB0c2lqW+Bl74n+OO4 C67w== 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=upKJS7mvDrOGdiR22YXOIMfT67Z7sYkgnOz7jdyIx/M=; b=IlMnhLlhD/lTMqf1sUUsiHsnZ9H0mQh+egXjgKPPhGcUOyHS2IJaC3eUS1vqvJwjgC JNViLmfKhVZZDGFfMqHBlaOoyR9RqhB2vwHQg6LHRRR+9zIKSjsrTL7DPuMTYA2B+oje gz1ZK6rSJZvUk+gzWtgYYr9v8X3ty3kEU+uFMXg0HFl9+z7gpJN7S/jPopiCX94zLRUd YA9WpnTNnR1NiirQSwI/mzQJrF+n7hTIELI+mAidzyd7tCB3JD7vGEX7UFCIHeniq7xA chh54b3iEEe9Wg4WMgKYKQPSBPq7d6OukdUg5zaesSiMuAMVcCuscsAJNWoxSPawfxUD BheA== X-Gm-Message-State: APjAAAUp/xAvqWr0d4H0HDeU4YLtG4l1lefRJGjft6dEDX/6Ma+IG6VQ aGZeInXGrpRj76rmRyXL8JbiIqkxKzEBZUwtSPa3Bw== X-Received: by 2002:a0c:d40d:: with SMTP id t13mr20296372qvh.175.1560760005243; Mon, 17 Jun 2019 01:26:45 -0700 (PDT) Date: Mon, 17 Jun 2019 01:25:57 -0700 In-Reply-To: <20190617082613.109131-1-brendanhiggins@google.com> Message-Id: <20190617082613.109131-3-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190617082613.109131-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH v5 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 Cc: devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, kunit-dev@googlegroups.com, linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, linux-um@lists.infradead.org, Alexander.Levin@microsoft.com, Tim.Bird@sony.com, amir73il@gmail.com, dan.carpenter@oracle.com, daniel@ffwll.ch, jdike@addtoit.com, joel@jms.id.au, julia.lawall@lip6.fr, khilman@baylibre.com, knut.omang@oracle.com, logang@deltatee.com, mpe@ellerman.id.au, pmladek@suse.com, rdunlap@infradead.org, richard@nod.at, rientjes@google.com, rostedt@goodmis.org, wfg@linux.intel.com, Brendan Higgins Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 --- Changes Since Last Revision: - Found and fixed bug in resource deallocation logic. Bug was discovered as a result of making a change suggested by Stephen Boyd. This does not substantially change how any of the code works conceptually. --- include/kunit/test.h | 110 +++++++++++++++++++++++++++++++++++++++++++ kunit/test.c | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) diff --git a/include/kunit/test.h b/include/kunit/test.h index 8476b3d371cb9..27bd95b6b5523 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; @@ -103,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 kunit_init_test(struct kunit *test, const char *name); @@ -123,6 +188,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 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. + */ +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 d05d254f1521f..53838f5394303 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -142,6 +142,7 @@ 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; } @@ -172,6 +173,8 @@ static void kunit_run_case(struct kunit_module *module, if (module->exit) module->exit(&test); + kunit_cleanup(&test); + test_case->success = kunit_get_success(&test); } @@ -192,6 +195,98 @@ 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, + ¶ms); + + if (res) + return res->allocation; + + return NULL; +} + +void kunit_cleanup(struct kunit *test) +{ + struct kunit_resource *resource, *resource_safe; + unsigned long flags; + + spin_lock_irqsave(&test->lock, flags); + /* + * 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_irqrestore(&test->lock, flags); +} + void kunit_printk(const char *level, const struct kunit *test, const char *fmt, ...) -- 2.22.0.410.gd8fdbe21b5-goog