* [PATCH v10 09/18] kunit: test: add support for test abort
From: Brendan Higgins @ 2019-07-16 9:42 UTC (permalink / raw)
To: frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
jpoimboe-H+wXaHxf7aLQT0dZR+AlfA, keescook-hpIqsD4AKlfQT0dZR+AlfA,
kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
mcgrof-DgEjT+Ai2ygdnm+yROfE0A, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
robh-DgEjT+Ai2ygdnm+yROfE0A, sboyd-DgEjT+Ai2ygdnm+yROfE0A,
shuah-DgEjT+Ai2ygdnm+yROfE0A, tytso-3s7WtUTddSA,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A
Cc: pmladek-IBi9RG/b67k, linux-doc-u79uwXL29TY76Z2rM5mHXA,
amir73il-Re5JQEeQqe8AvxtiuMwx3w, Brendan Higgins,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
Alexander.Levin-0li6OtcxBFHby3iVrkZq2A,
linux-kselftest-u79uwXL29TY76Z2rM5mHXA,
linux-nvdimm-hn68Rpc1hR1g9hUCZPvPmw,
khilman-rdvid1DuHRBWk0Htik3J/w, knut.omang-QHcLZuEGTsvQT0dZR+AlfA,
wfg-VuQAYsv1563Yd54FQh9/CA, joel-U3u1mxZcP9KHXe+LvDLADg,
rientjes-hpIqsD4AKlfQT0dZR+AlfA, jdike-OPE4K8JWMJJBDgjK7y7TUQ,
dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kbuild-u79uwXL29TY76Z2rM5mHXA, Tim.Bird-7U/KSKJipcs,
linux-um-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
rostedt-nx8X9YLhiw1AfugRpC6u6w, julia.lawall-L2FTfq7BK8M,
kunit-dev-/JYPxA39Uh5TLH3MbocFFw, richard-/L3Ra7n9ekc,
rdunlap-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
mpe-Gsx/Oe8HsFggBc27wqDAHg, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20190716094302.180360-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Add support for aborting/bailing out of test cases, which is needed for
implementing assertions.
An assertion is like an expectation, but bails out of the test case
early if the assertion is not met. The idea with assertions is that you
use them to state all the preconditions for your test. Logically
speaking, these are the premises of the test case, so if a premise isn't
true, there is no point in continuing the test case because there are no
conclusions that can be drawn without the premises. Whereas, the
expectation is the thing you are trying to prove.
Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Reviewed-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Reviewed-by: Logan Gunthorpe <logang-OTvnGxWRz7hWk0Htik3J/w@public.gmane.org>
---
include/kunit/test.h | 16 ++++
include/kunit/try-catch.h | 69 +++++++++++++++
kunit/Makefile | 3 +-
kunit/test.c | 176 ++++++++++++++++++++++++++++++++++----
kunit/try-catch.c | 95 ++++++++++++++++++++
5 files changed, 343 insertions(+), 16 deletions(-)
create mode 100644 include/kunit/try-catch.h
create mode 100644 kunit/try-catch.c
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 4cc94f09389d7..1d35f542a30c9 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -13,6 +13,7 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <kunit/kunit-stream.h>
+#include <kunit/try-catch.h>
struct kunit_resource;
@@ -167,6 +168,7 @@ struct kunit {
/* private: internal use only. */
const char *name; /* Read only after initialization! */
+ struct kunit_try_catch try_catch;
/*
* success starts as true, and may only be set to false during a test
* case; thus, it is safe to update this across multiple threads using
@@ -176,6 +178,11 @@ struct kunit {
*/
bool success; /* Read only after test_case finishes! */
spinlock_t lock; /* Gaurds all mutable test state. */
+ /*
+ * death_test may be both set and unset from multiple threads in a test
+ * case.
+ */
+ bool death_test; /* Protected by lock. */
/*
* Because resources is a list that may be updated multiple times (with
* new resources) from any thread associated with a test case, we must
@@ -184,10 +191,19 @@ struct kunit {
struct list_head resources; /* Protected by lock. */
};
+static inline void kunit_set_death_test(struct kunit *test, bool death_test)
+{
+ spin_lock(&test->lock);
+ test->death_test = death_test;
+ spin_unlock(&test->lock);
+}
+
void kunit_init_test(struct kunit *test, const char *name);
void kunit_fail(struct kunit *test, struct kunit_stream *stream);
+void kunit_abort(struct kunit *test);
+
int kunit_run_tests(struct kunit_suite *suite);
/**
diff --git a/include/kunit/try-catch.h b/include/kunit/try-catch.h
new file mode 100644
index 0000000000000..8a414a9af0b64
--- /dev/null
+++ b/include/kunit/try-catch.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * An API to allow a function, that may fail, to be executed, and recover in a
+ * controlled manner.
+ *
+ * Copyright (C) 2019, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
+ */
+
+#ifndef _KUNIT_TRY_CATCH_H
+#define _KUNIT_TRY_CATCH_H
+
+#include <linux/types.h>
+
+typedef void (*kunit_try_catch_func_t)(void *);
+
+struct kunit;
+
+/*
+ * struct kunit_try_catch - provides a generic way to run code which might fail.
+ * @context: used to pass user data to the try and catch functions.
+ *
+ * kunit_try_catch provides a generic, architecture independent way to execute
+ * an arbitrary function of type kunit_try_catch_func_t which may bail out by
+ * calling kunit_try_catch_throw(). If kunit_try_catch_throw() is called, @try
+ * is stopped at the site of invocation and @catch is catch is called.
+ *
+ * struct kunit_try_catch provides a generic interface for the functionality
+ * needed to implement kunit->abort() which in turn is needed for implementing
+ * assertions. Assertions allow stating a precondition for a test simplifying
+ * how test cases are written and presented.
+ *
+ * Assertions are like expectations, except they abort (call
+ * kunit_try_catch_throw()) when the specified condition is not met. This is
+ * useful when you look at a test case as a logical statement about some piece
+ * of code, where assertions are the premises for the test case, and the
+ * conclusion is a set of predicates, rather expectations, that must all be
+ * true. If your premises are violated, it does not makes sense to continue.
+ */
+struct kunit_try_catch {
+ /* private: internal use only. */
+ struct kunit *test;
+ struct completion *try_completion;
+ int try_result;
+ kunit_try_catch_func_t try;
+ kunit_try_catch_func_t catch;
+ void *context;
+};
+
+void kunit_try_catch_init(struct kunit_try_catch *try_catch,
+ struct kunit *test,
+ kunit_try_catch_func_t try,
+ kunit_try_catch_func_t catch);
+
+void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context);
+
+void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch);
+
+static inline int kunit_try_catch_get_result(struct kunit_try_catch *try_catch)
+{
+ return try_catch->try_result;
+}
+
+/*
+ * Exposed for testing only.
+ */
+void kunit_generic_try_catch_init(struct kunit_try_catch *try_catch);
+
+#endif /* _KUNIT_TRY_CATCH_H */
diff --git a/kunit/Makefile b/kunit/Makefile
index 60a9ea6cb4697..1f7680cfa11ad 100644
--- a/kunit/Makefile
+++ b/kunit/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_KUNIT) += test.o \
string-stream.o \
- kunit-stream.o
+ kunit-stream.o \
+ try-catch.o
obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o
diff --git a/kunit/test.c b/kunit/test.c
index 1f94a9224b03e..12db50b221781 100644
--- a/kunit/test.c
+++ b/kunit/test.c
@@ -7,13 +7,26 @@
*/
#include <linux/kernel.h>
+#include <linux/sched/debug.h>
#include <kunit/test.h>
+#include <kunit/try-catch.h>
static void kunit_set_failure(struct kunit *test)
{
WRITE_ONCE(test->success, false);
}
+static bool kunit_get_death_test(struct kunit *test)
+{
+ bool death_test;
+
+ spin_lock(&test->lock);
+ death_test = test->death_test;
+ spin_unlock(&test->lock);
+
+ return death_test;
+}
+
static int kunit_vprintk_emit(int level, const char *fmt, va_list args)
{
return vprintk_emit(0, level, NULL, 0, fmt, args);
@@ -126,42 +139,175 @@ void kunit_fail(struct kunit *test, struct kunit_stream *stream)
kunit_stream_commit(stream);
}
+void __noreturn kunit_abort(struct kunit *test)
+{
+ kunit_set_death_test(test, true);
+
+ kunit_try_catch_throw(&test->try_catch);
+
+ /*
+ * Throw could not abort from test.
+ *
+ * XXX: we should never reach this line! As kunit_try_catch_throw is
+ * marked __noreturn.
+ */
+ WARN_ONCE(true, "Throw could not abort from test!\n");
+}
+
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;
+ test->death_test = false;
}
/*
- * Performs all logic to run a test case.
+ * Initializes and runs test case. Does not clean up or do post validations.
*/
-static void kunit_run_case(struct kunit_suite *suite,
- struct kunit_case *test_case)
+static void kunit_run_case_internal(struct kunit *test,
+ struct kunit_suite *suite,
+ struct kunit_case *test_case)
{
- struct kunit test;
-
- kunit_init_test(&test, test_case->name);
-
if (suite->init) {
int ret;
- ret = suite->init(&test);
+ ret = suite->init(test);
if (ret) {
- kunit_err(&test, "failed to initialize: %d\n", ret);
- kunit_set_failure(&test);
- test_case->success = test.success;
+ kunit_err(test, "failed to initialize: %d\n", ret);
+ kunit_set_failure(test);
return;
}
}
- test_case->run_case(&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!
+ */
+static void kunit_run_case_cleanup(struct kunit *test,
+ struct kunit_suite *suite)
+{
if (suite->exit)
- suite->exit(&test);
+ suite->exit(test);
+
+ kunit_case_internal_cleanup(test);
+}
+
+/*
+ * Handles an unexpected crash in a test case.
+ */
+static void kunit_handle_test_crash(struct kunit *test,
+ struct kunit_suite *suite,
+ struct kunit_case *test_case)
+{
+ kunit_err(test, "kunit test case crashed!");
+ /*
+ * TODO(brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org): This prints the stack trace up
+ * through this frame, not up to the frame that caused the crash.
+ */
+ show_stack(NULL, NULL);
+
+ kunit_case_internal_cleanup(test);
+}
+
+struct kunit_try_catch_context {
+ struct kunit *test;
+ struct kunit_suite *suite;
+ struct kunit_case *test_case;
+};
+
+static void kunit_try_run_case(void *data)
+{
+ struct kunit_try_catch_context *ctx = data;
+ struct kunit *test = ctx->test;
+ struct kunit_suite *suite = ctx->suite;
+ struct kunit_case *test_case = ctx->test_case;
+
+ /*
+ * kunit_run_case_internal may encounter a fatal error; if it does,
+ * abort will be called, this thread will exit, and finally the parent
+ * thread will resume control and handle any necessary clean up.
+ */
+ kunit_run_case_internal(test, suite, test_case);
+ /* This line may never be reached. */
+ kunit_run_case_cleanup(test, suite);
+}
+
+static void kunit_catch_run_case(void *data)
+{
+ struct kunit_try_catch_context *ctx = data;
+ struct kunit *test = ctx->test;
+ struct kunit_suite *suite = ctx->suite;
+ struct kunit_case *test_case = ctx->test_case;
+ int try_exit_code = kunit_try_catch_get_result(&test->try_catch);
+
+ if (try_exit_code) {
+ kunit_set_failure(test);
+ /*
+ * Test case could not finish, we have no idea what state it is
+ * in, so don't do clean up.
+ */
+ if (try_exit_code == -ETIMEDOUT)
+ kunit_err(test, "test case timed out\n");
+ /*
+ * Unknown internal error occurred preventing test case from
+ * running, so there is nothing to clean up.
+ */
+ else
+ kunit_err(test, "internal error occurred preventing test case from running: %d\n",
+ try_exit_code);
+ return;
+ }
+
+ if (kunit_get_death_test(test)) {
+ /*
+ * EXPECTED DEATH: kunit_run_case_internal encountered
+ * anticipated fatal error. Everything should be in a safe
+ * state.
+ */
+ kunit_run_case_cleanup(test, suite);
+ } else {
+ /*
+ * UNEXPECTED DEATH: kunit_run_case_internal encountered an
+ * unanticipated fatal error. We have no idea what the state of
+ * the test case is in.
+ */
+ kunit_handle_test_crash(test, suite, test_case);
+ kunit_set_failure(test);
+ }
+}
+
+/*
+ * Performs all logic to run a test case. It also catches most errors that
+ * occurs 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_try_catch_context context;
+ struct kunit_try_catch *try_catch;
+ struct kunit test;
+
+ kunit_init_test(&test, test_case->name);
+ try_catch = &test.try_catch;
- kunit_cleanup(&test);
+ kunit_try_catch_init(try_catch,
+ &test,
+ kunit_try_run_case,
+ kunit_catch_run_case);
+ context.test = &test;
+ context.suite = suite;
+ context.test_case = test_case;
+ kunit_try_catch_run(try_catch, &context);
test_case->success = test.success;
}
@@ -174,7 +320,7 @@ int kunit_run_tests(struct kunit_suite *suite)
kunit_print_subtest_start(suite);
for (test_case = suite->test_cases; test_case->run_case; test_case++) {
- kunit_run_case(suite, test_case);
+ kunit_run_case_catch_errors(suite, test_case);
kunit_print_test_case_ok_not_ok(test_case, test_case_count++);
}
diff --git a/kunit/try-catch.c b/kunit/try-catch.c
new file mode 100644
index 0000000000000..de580f074387b
--- /dev/null
+++ b/kunit/try-catch.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * An API to allow a function, that may fail, to be executed, and recover in a
+ * controlled manner.
+ *
+ * Copyright (C) 2019, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
+ */
+
+#include <kunit/try-catch.h>
+#include <kunit/test.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+
+void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch)
+{
+ try_catch->try_result = -EFAULT;
+ complete_and_exit(try_catch->try_completion, -EFAULT);
+}
+
+static int kunit_generic_run_threadfn_adapter(void *data)
+{
+ struct kunit_try_catch *try_catch = data;
+
+ try_catch->try(try_catch->context);
+
+ complete_and_exit(try_catch->try_completion, 0);
+}
+
+void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context)
+{
+ DECLARE_COMPLETION_ONSTACK(try_completion);
+ struct kunit *test = try_catch->test;
+ struct task_struct *task_struct;
+ int exit_code, status;
+
+ try_catch->context = context;
+ try_catch->try_completion = &try_completion;
+ try_catch->try_result = 0;
+ task_struct = kthread_run(kunit_generic_run_threadfn_adapter,
+ try_catch,
+ "kunit_try_catch_thread");
+ if (IS_ERR(task_struct)) {
+ try_catch->catch(try_catch->context);
+ return;
+ }
+
+ /*
+ * TODO(brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org): We should probably have some type of
+ * variable timeout here. The only question is what that timeout value
+ * should be.
+ *
+ * The intention has always been, at some point, to be able to label
+ * tests with some type of size bucket (unit/small, integration/medium,
+ * large/system/end-to-end, etc), where each size bucket would get a
+ * default timeout value kind of like what Bazel does:
+ * https://docs.bazel.build/versions/master/be/common-definitions.html#test.size
+ * There is still some debate to be had on exactly how we do this. (For
+ * one, we probably want to have some sort of test runner level
+ * timeout.)
+ *
+ * For more background on this topic, see:
+ * https://mike-bland.com/2011/11/01/small-medium-large.html
+ */
+ status = wait_for_completion_timeout(&try_completion,
+ 300 * MSEC_PER_SEC); /* 5 min */
+ if (status < 0) {
+ kunit_err(test, "try timed out\n");
+ try_catch->try_result = -ETIMEDOUT;
+ }
+
+ exit_code = try_catch->try_result;
+
+ if (!exit_code)
+ return;
+
+ if (exit_code == -EFAULT)
+ try_catch->try_result = 0;
+ else if (exit_code == -EINTR)
+ kunit_err(test, "wake_up_process() was never called\n");
+ else if (exit_code)
+ kunit_err(test, "Unknown error: %d\n", exit_code);
+
+ try_catch->catch(try_catch->context);
+}
+
+void kunit_try_catch_init(struct kunit_try_catch *try_catch,
+ struct kunit *test,
+ kunit_try_catch_func_t try,
+ kunit_try_catch_func_t catch)
+{
+ try_catch->test = test;
+ try_catch->try = try;
+ try_catch->catch = catch;
+}
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v10 10/18] kunit: test: add tests for kunit test abort
From: Brendan Higgins @ 2019-07-16 9:42 UTC (permalink / raw)
To: frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
jpoimboe-H+wXaHxf7aLQT0dZR+AlfA, keescook-hpIqsD4AKlfQT0dZR+AlfA,
kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
mcgrof-DgEjT+Ai2ygdnm+yROfE0A, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
robh-DgEjT+Ai2ygdnm+yROfE0A, sboyd-DgEjT+Ai2ygdnm+yROfE0A,
shuah-DgEjT+Ai2ygdnm+yROfE0A, tytso-3s7WtUTddSA,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A
Cc: pmladek-IBi9RG/b67k, linux-doc-u79uwXL29TY76Z2rM5mHXA,
amir73il-Re5JQEeQqe8AvxtiuMwx3w, Brendan Higgins,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
Alexander.Levin-0li6OtcxBFHby3iVrkZq2A,
linux-kselftest-u79uwXL29TY76Z2rM5mHXA,
linux-nvdimm-hn68Rpc1hR1g9hUCZPvPmw,
khilman-rdvid1DuHRBWk0Htik3J/w, knut.omang-QHcLZuEGTsvQT0dZR+AlfA,
wfg-VuQAYsv1563Yd54FQh9/CA, joel-U3u1mxZcP9KHXe+LvDLADg,
rientjes-hpIqsD4AKlfQT0dZR+AlfA, jdike-OPE4K8JWMJJBDgjK7y7TUQ,
dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kbuild-u79uwXL29TY76Z2rM5mHXA, Tim.Bird-7U/KSKJipcs,
linux-um-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
rostedt-nx8X9YLhiw1AfugRpC6u6w, julia.lawall-L2FTfq7BK8M,
kunit-dev-/JYPxA39Uh5TLH3MbocFFw, richard-/L3Ra7n9ekc,
rdunlap-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
mpe-Gsx/Oe8HsFggBc27wqDAHg, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20190716094302.180360-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Add KUnit tests for the KUnit test abort mechanism (see preceding
commit). Add tests both for general try catch mechanism as well as
non-architecture specific mechanism.
Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Reviewed-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Reviewed-by: Logan Gunthorpe <logang-OTvnGxWRz7hWk0Htik3J/w@public.gmane.org>
---
kunit/Makefile | 3 +-
kunit/test-test.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 103 insertions(+), 1 deletion(-)
create mode 100644 kunit/test-test.c
diff --git a/kunit/Makefile b/kunit/Makefile
index 1f7680cfa11ad..533355867abd2 100644
--- a/kunit/Makefile
+++ b/kunit/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_KUNIT) += test.o \
kunit-stream.o \
try-catch.o
-obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o
+obj-$(CONFIG_KUNIT_TEST) += test-test.o \
+ string-stream-test.o
obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += example-test.o
diff --git a/kunit/test-test.c b/kunit/test-test.c
new file mode 100644
index 0000000000000..88f4cdf03db2a
--- /dev/null
+++ b/kunit/test-test.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for core test infrastructure.
+ *
+ * Copyright (C) 2019, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
+ */
+#include <kunit/test.h>
+
+struct kunit_try_catch_test_context {
+ struct kunit_try_catch *try_catch;
+ bool function_called;
+};
+
+void kunit_test_successful_try(void *data)
+{
+ struct kunit *test = data;
+ struct kunit_try_catch_test_context *ctx = test->priv;
+
+ ctx->function_called = true;
+}
+
+void kunit_test_no_catch(void *data)
+{
+ struct kunit *test = data;
+
+ KUNIT_FAIL(test, "Catch should not be called\n");
+}
+
+static void kunit_test_try_catch_successful_try_no_catch(struct kunit *test)
+{
+ struct kunit_try_catch_test_context *ctx = test->priv;
+ struct kunit_try_catch *try_catch = ctx->try_catch;
+
+ kunit_try_catch_init(try_catch,
+ test,
+ kunit_test_successful_try,
+ kunit_test_no_catch);
+ kunit_try_catch_run(try_catch, test);
+
+ KUNIT_EXPECT_TRUE(test, ctx->function_called);
+}
+
+void kunit_test_unsuccessful_try(void *data)
+{
+ struct kunit *test = data;
+ struct kunit_try_catch_test_context *ctx = test->priv;
+ struct kunit_try_catch *try_catch = ctx->try_catch;
+
+ kunit_try_catch_throw(try_catch);
+ KUNIT_FAIL(test, "This line should never be reached\n");
+}
+
+void kunit_test_catch(void *data)
+{
+ struct kunit *test = data;
+ struct kunit_try_catch_test_context *ctx = test->priv;
+
+ ctx->function_called = true;
+}
+
+static void kunit_test_try_catch_unsuccessful_try_does_catch(struct kunit *test)
+{
+ struct kunit_try_catch_test_context *ctx = test->priv;
+ struct kunit_try_catch *try_catch = ctx->try_catch;
+
+ kunit_try_catch_init(try_catch,
+ test,
+ kunit_test_unsuccessful_try,
+ kunit_test_catch);
+ kunit_try_catch_run(try_catch, test);
+
+ KUNIT_EXPECT_TRUE(test, ctx->function_called);
+}
+
+static int kunit_try_catch_test_init(struct kunit *test)
+{
+ struct kunit_try_catch_test_context *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ test->priv = ctx;
+
+ ctx->try_catch = kunit_kmalloc(test,
+ sizeof(*ctx->try_catch),
+ GFP_KERNEL);
+
+ return 0;
+}
+
+static struct kunit_case kunit_try_catch_test_cases[] = {
+ KUNIT_CASE(kunit_test_try_catch_successful_try_no_catch),
+ KUNIT_CASE(kunit_test_try_catch_unsuccessful_try_does_catch),
+ {}
+};
+
+static struct kunit_suite kunit_try_catch_test_suite = {
+ .name = "kunit-try-catch-test",
+ .init = kunit_try_catch_test_init,
+ .test_cases = kunit_try_catch_test_cases,
+};
+kunit_test_suite(kunit_try_catch_test_suite);
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v10 11/18] kunit: test: add the concept of assertions
From: Brendan Higgins @ 2019-07-16 9:42 UTC (permalink / raw)
To: frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
jpoimboe-H+wXaHxf7aLQT0dZR+AlfA, keescook-hpIqsD4AKlfQT0dZR+AlfA,
kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
mcgrof-DgEjT+Ai2ygdnm+yROfE0A, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
robh-DgEjT+Ai2ygdnm+yROfE0A, sboyd-DgEjT+Ai2ygdnm+yROfE0A,
shuah-DgEjT+Ai2ygdnm+yROfE0A, tytso-3s7WtUTddSA,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A
Cc: pmladek-IBi9RG/b67k, linux-doc-u79uwXL29TY76Z2rM5mHXA,
amir73il-Re5JQEeQqe8AvxtiuMwx3w, Brendan Higgins,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
Alexander.Levin-0li6OtcxBFHby3iVrkZq2A,
linux-kselftest-u79uwXL29TY76Z2rM5mHXA,
linux-nvdimm-hn68Rpc1hR1g9hUCZPvPmw,
khilman-rdvid1DuHRBWk0Htik3J/w, knut.omang-QHcLZuEGTsvQT0dZR+AlfA,
wfg-VuQAYsv1563Yd54FQh9/CA, joel-U3u1mxZcP9KHXe+LvDLADg,
rientjes-hpIqsD4AKlfQT0dZR+AlfA, jdike-OPE4K8JWMJJBDgjK7y7TUQ,
dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kbuild-u79uwXL29TY76Z2rM5mHXA, Tim.Bird-7U/KSKJipcs,
linux-um-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
rostedt-nx8X9YLhiw1AfugRpC6u6w, julia.lawall-L2FTfq7BK8M,
kunit-dev-/JYPxA39Uh5TLH3MbocFFw, richard-/L3Ra7n9ekc,
rdunlap-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
mpe-Gsx/Oe8HsFggBc27wqDAHg, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20190716094302.180360-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Add support for assertions which are like expectations except the test
terminates if the assertion is not satisfied.
The idea with assertions is that you use them to state all the
preconditions for your test. Logically speaking, these are the premises
of the test case, so if a premise isn't true, there is no point in
continuing the test case because there are no conclusions that can be
drawn without the premises. Whereas, the expectation is the thing you
are trying to prove. It is not used universally in x-unit style test
frameworks, but I really like it as a convention. You could still
express the idea of a premise using the above idiom, but I think
KUNIT_ASSERT_* states the intended idea perfectly.
Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Reviewed-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Reviewed-by: Logan Gunthorpe <logang-OTvnGxWRz7hWk0Htik3J/w@public.gmane.org>
---
include/kunit/test.h | 499 ++++++++++++++++++++++++++++++++++++-
kunit/string-stream-test.c | 12 +-
kunit/test-test.c | 2 +
kunit/test.c | 66 +++++
4 files changed, 570 insertions(+), 9 deletions(-)
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 1d35f542a30c9..06f849430ce4b 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -86,9 +86,10 @@ struct kunit;
* @name: the name of the test case.
*
* A test case is a function with the signature, ``void (*)(struct kunit *)``
- * that makes expectations (see KUNIT_EXPECT_TRUE()) about code under test. Each
- * test case is associated with a &struct kunit_suite and will be run after the
- * suite's init function and followed by the suite's exit function.
+ * that makes expectations and assertions (see KUNIT_EXPECT_TRUE() and
+ * KUNIT_ASSERT_TRUE()) about code under test. Each test case is associated with
+ * a &struct kunit_suite and will be run after the suite's init function and
+ * followed by the suite's exit function.
*
* A test case should be static and should only be created with the KUNIT_CASE()
* macro; additionally, every array of test cases should be terminated with an
@@ -863,4 +864,496 @@ do { \
KUNIT_EXPECT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \
} while (0)
+static inline struct kunit_stream *kunit_assert_start(struct kunit *test,
+ const char *file,
+ const char *line)
+{
+ struct kunit_stream *stream = alloc_kunit_stream(test, KERN_ERR);
+
+ kunit_stream_add(stream, "ASSERTION FAILED at %s:%s\n\t", file, line);
+
+ return stream;
+}
+
+static inline void kunit_assert_end(struct kunit *test,
+ bool success,
+ struct kunit_stream *stream)
+{
+ if (!success) {
+ kunit_fail(test, stream);
+ kunit_abort(test);
+ } else {
+ kunit_stream_clear(stream);
+ }
+}
+
+#define KUNIT_ASSERT_START(test) \
+ kunit_assert_start(test, __FILE__, __stringify(__LINE__))
+
+#define KUNIT_ASSERT_END(test, success, stream) \
+ kunit_assert_end(test, success, stream)
+
+#define KUNIT_ASSERT(test, success, message) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ \
+ kunit_stream_add(__stream, message); \
+ KUNIT_ASSERT_END(test, success, __stream); \
+} while (0)
+
+#define KUNIT_ASSERT_MSG(test, success, message, fmt, ...) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ \
+ kunit_stream_add(__stream, message); \
+ kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \
+ KUNIT_ASSERT_END(test, success, __stream); \
+} while (0)
+
+#define KUNIT_ASSERT_FAILURE(test, fmt, ...) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ \
+ kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \
+ KUNIT_ASSERT_END(test, false, __stream); \
+} while (0)
+
+/**
+ * KUNIT_ASSERT_TRUE() - Sets an assertion that @condition is true.
+ * @test: The test context object.
+ * @condition: an arbitrary boolean expression. The test fails and aborts when
+ * this does not evaluate to true.
+ *
+ * This and assertions of the form `KUNIT_ASSERT_*` will cause the test case to
+ * fail *and immediately abort* when the specified condition is not met. Unlike
+ * an expectation failure, it will prevent the test case from continuing to run;
+ * this is otherwise known as an *assertion failure*.
+ */
+#define KUNIT_ASSERT_TRUE(test, condition) \
+ KUNIT_ASSERT(test, (condition), \
+ "Asserted " #condition " is true, but is false\n")
+
+#define KUNIT_ASSERT_TRUE_MSG(test, condition, fmt, ...) \
+ KUNIT_ASSERT_MSG(test, (condition), \
+ "Asserted " #condition " is true, but is false\n",\
+ fmt, ##__VA_ARGS__)
+
+/**
+ * KUNIT_ASSERT_FALSE() - Sets an assertion that @condition is false.
+ * @test: The test context object.
+ * @condition: an arbitrary boolean expression.
+ *
+ * Sets an assertion that the value that @condition evaluates to is false. This
+ * is the same as KUNIT_EXPECT_FALSE(), except it causes an assertion failure
+ * (see KUNIT_ASSERT_TRUE()) when the assertion is not met.
+ */
+#define KUNIT_ASSERT_FALSE(test, condition) \
+ KUNIT_ASSERT(test, !(condition), \
+ "Asserted " #condition " is false, but is true\n")
+
+#define KUNIT_ASSERT_FALSE_MSG(test, condition, fmt, ...) \
+ KUNIT_ASSERT_MSG(test, !(condition), \
+ "Asserted " #condition " is false, but is true\n",\
+ fmt, ##__VA_ARGS__)
+
+void kunit_assert_binary_msg(struct kunit *test,
+ long long left, const char *left_name,
+ long long right, const char *right_name,
+ bool compare_result,
+ const char *compare_name,
+ const char *file,
+ const char *line,
+ const char *fmt, ...);
+
+static inline void kunit_assert_binary(struct kunit *test,
+ long long left, const char *left_name,
+ long long right, const char *right_name,
+ bool compare_result,
+ const char *compare_name,
+ const char *file,
+ const char *line)
+{
+ kunit_assert_binary_msg(test,
+ left, left_name,
+ right, right_name,
+ compare_result,
+ compare_name,
+ file,
+ line,
+ NULL);
+}
+
+void kunit_assert_ptr_binary_msg(struct kunit *test,
+ void *left, const char *left_name,
+ void *right, const char *right_name,
+ bool compare_result,
+ const char *compare_name,
+ const char *file,
+ const char *line,
+ const char *fmt, ...);
+
+static inline void kunit_assert_ptr_binary(struct kunit *test,
+ void *left, const char *left_name,
+ void *right, const char *right_name,
+ bool compare_result,
+ const char *compare_name,
+ const char *file,
+ const char *line)
+{
+ kunit_assert_ptr_binary_msg(test,
+ left, left_name,
+ right, right_name,
+ compare_result,
+ compare_name,
+ file,
+ line,
+ NULL);
+}
+
+/*
+ * A factory macro for defining the expectations for the basic comparisons
+ * defined for the built in types.
+ *
+ * Unfortunately, there is no common type that all types can be promoted to for
+ * which all the binary operators behave the same way as for the actual types
+ * (for example, there is no type that long long and unsigned long long can
+ * both be cast to where the comparison result is preserved for all values). So
+ * the best we can do is do the comparison in the original types and then coerce
+ * everything to long long for printing; this way, the comparison behaves
+ * correctly and the printed out value usually makes sense without
+ * interpretation, but can always be interpretted to figure out the actual
+ * value.
+ */
+#define KUNIT_ASSERT_BINARY(test, left, condition, right) do { \
+ typeof(left) __left = (left); \
+ typeof(right) __right = (right); \
+ __kunit_typecheck(__left, __right); \
+ kunit_assert_binary(test, \
+ (long long) __left, #left, \
+ (long long) __right, #right, \
+ __left condition __right, #condition, \
+ __FILE__, __stringify(__LINE__)); \
+} while (0)
+
+#define KUNIT_ASSERT_BINARY_MSG(test, left, condition, right, fmt, ...) do { \
+ typeof(left) __left = (left); \
+ typeof(right) __right = (right); \
+ __kunit_typecheck(__left, __right); \
+ kunit_assert_binary_msg(test, \
+ (long long) __left, #left, \
+ (long long) __right, #right, \
+ __left condition __right, #condition, \
+ __FILE__, __stringify(__LINE__), \
+ fmt, ##__VA_ARGS__); \
+} while (0)
+
+/*
+ * Just like KUNIT_EXPECT_BINARY, but for comparing pointer types.
+ */
+#define KUNIT_ASSERT_PTR_BINARY(test, left, condition, right) do { \
+ typeof(left) __left = (left); \
+ typeof(right) __right = (right); \
+ __kunit_typecheck(__left, __right); \
+ kunit_assert_ptr_binary(test, \
+ (void *) __left, #left, \
+ (void *) __right, #right, \
+ __left condition __right, #condition, \
+ __FILE__, __stringify(__LINE__)); \
+} while (0)
+
+#define KUNIT_ASSERT_PTR_BINARY_MSG(test, left, condition, right, fmt, ...) \
+do { \
+ typeof(left) __left = (left); \
+ typeof(right) __right = (right); \
+ __kunit_typecheck(__left, __right); \
+ kunit_assert_ptr_binary_msg(test, \
+ (void *) __left, #left, \
+ (void *) __right, #right, \
+ __left condition __right, #condition, \
+ __FILE__, __stringify(__LINE__), \
+ fmt, ##__VA_ARGS__); \
+} while (0)
+
+/**
+ * KUNIT_ASSERT_EQ() - Sets an assertion that @left and @right are equal.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a primitive C type.
+ * @right: an arbitrary expression that evaluates to a primitive C type.
+ *
+ * Sets an assertion that the values that @left and @right evaluate to are
+ * equal. This is the same as KUNIT_EXPECT_EQ(), except it causes an assertion
+ * failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met.
+ */
+#define KUNIT_ASSERT_EQ(test, left, right) \
+ KUNIT_ASSERT_BINARY(test, left, ==, right)
+
+#define KUNIT_ASSERT_EQ_MSG(test, left, right, fmt, ...) \
+ KUNIT_ASSERT_BINARY_MSG(test, \
+ left, \
+ ==, \
+ right, \
+ fmt, \
+ ##__VA_ARGS__)
+
+/**
+ * KUNIT_ASSERT_PTR_EQ() - Asserts that pointers @left and @right are equal.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a pointer.
+ * @right: an arbitrary expression that evaluates to a pointer.
+ *
+ * Sets an assertion that the values that @left and @right evaluate to are
+ * equal. This is the same as KUNIT_EXPECT_EQ(), except it causes an assertion
+ * failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met.
+ */
+#define KUNIT_ASSERT_PTR_EQ(test, left, right) \
+ KUNIT_ASSERT_PTR_BINARY(test, left, ==, right)
+
+#define KUNIT_ASSERT_PTR_EQ_MSG(test, left, right, fmt, ...) \
+ KUNIT_ASSERT_PTR_BINARY_MSG(test, \
+ left, \
+ ==, \
+ right, \
+ fmt, \
+ ##__VA_ARGS__)
+
+/**
+ * KUNIT_ASSERT_NE() - An assertion that @left and @right are not equal.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a primitive C type.
+ * @right: an arbitrary expression that evaluates to a primitive C type.
+ *
+ * Sets an assertion that the values that @left and @right evaluate to are not
+ * equal. This is the same as KUNIT_EXPECT_NE(), except it causes an assertion
+ * failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met.
+ */
+#define KUNIT_ASSERT_NE(test, left, right) \
+ KUNIT_ASSERT_BINARY(test, left, !=, right)
+
+#define KUNIT_ASSERT_NE_MSG(test, left, right, fmt, ...) \
+ KUNIT_ASSERT_BINARY_MSG(test, \
+ left, \
+ !=, \
+ right, \
+ fmt, \
+ ##__VA_ARGS__)
+
+/**
+ * KUNIT_ASSERT_PTR_NE() - Asserts that pointers @left and @right are not equal.
+ * KUNIT_ASSERT_PTR_EQ() - Asserts that pointers @left and @right are equal.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a pointer.
+ * @right: an arbitrary expression that evaluates to a pointer.
+ *
+ * Sets an assertion that the values that @left and @right evaluate to are not
+ * equal. This is the same as KUNIT_EXPECT_NE(), except it causes an assertion
+ * failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met.
+ */
+#define KUNIT_ASSERT_PTR_NE(test, left, right) \
+ KUNIT_ASSERT_PTR_BINARY(test, left, !=, right)
+
+#define KUNIT_ASSERT_PTR_NE_MSG(test, left, right, fmt, ...) \
+ KUNIT_ASSERT_PTR_BINARY_MSG(test, \
+ left, \
+ !=, \
+ right, \
+ fmt, \
+ ##__VA_ARGS__)
+/**
+ * KUNIT_ASSERT_LT() - An assertion that @left is less than @right.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a primitive C type.
+ * @right: an arbitrary expression that evaluates to a primitive C type.
+ *
+ * Sets an assertion that the value that @left evaluates to is less than the
+ * value that @right evaluates to. This is the same as KUNIT_EXPECT_LT(), except
+ * it causes an assertion failure (see KUNIT_ASSERT_TRUE()) when the assertion
+ * is not met.
+ */
+#define KUNIT_ASSERT_LT(test, left, right) \
+ KUNIT_ASSERT_BINARY(test, left, <, right)
+
+#define KUNIT_ASSERT_LT_MSG(test, left, right, fmt, ...) \
+ KUNIT_ASSERT_BINARY_MSG(test, \
+ left, \
+ <, \
+ right, \
+ fmt, \
+ ##__VA_ARGS__)
+/**
+ * KUNIT_ASSERT_LE() - An assertion that @left is less than or equal to @right.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a primitive C type.
+ * @right: an arbitrary expression that evaluates to a primitive C type.
+ *
+ * Sets an assertion that the value that @left evaluates to is less than or
+ * equal to the value that @right evaluates to. This is the same as
+ * KUNIT_EXPECT_LE(), except it causes an assertion failure (see
+ * KUNIT_ASSERT_TRUE()) when the assertion is not met.
+ */
+#define KUNIT_ASSERT_LE(test, left, right) \
+ KUNIT_ASSERT_BINARY(test, left, <=, right)
+
+#define KUNIT_ASSERT_LE_MSG(test, left, right, fmt, ...) \
+ KUNIT_ASSERT_BINARY_MSG(test, \
+ left, \
+ <=, \
+ right, \
+ fmt, \
+ ##__VA_ARGS__)
+/**
+ * KUNIT_ASSERT_GT() - An assertion that @left is greater than @right.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a primitive C type.
+ * @right: an arbitrary expression that evaluates to a primitive C type.
+ *
+ * Sets an assertion that the value that @left evaluates to is greater than the
+ * value that @right evaluates to. This is the same as KUNIT_EXPECT_GT(), except
+ * it causes an assertion failure (see KUNIT_ASSERT_TRUE()) when the assertion
+ * is not met.
+ */
+#define KUNIT_ASSERT_GT(test, left, right) \
+ KUNIT_ASSERT_BINARY(test, left, >, right)
+
+#define KUNIT_ASSERT_GT_MSG(test, left, right, fmt, ...) \
+ KUNIT_ASSERT_BINARY_MSG(test, \
+ left, \
+ >, \
+ right, \
+ fmt, \
+ ##__VA_ARGS__)
+
+/**
+ * KUNIT_ASSERT_GE() - Assertion that @left is greater than or equal to @right.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a primitive C type.
+ * @right: an arbitrary expression that evaluates to a primitive C type.
+ *
+ * Sets an assertion that the value that @left evaluates to is greater than the
+ * value that @right evaluates to. This is the same as KUNIT_EXPECT_GE(), except
+ * it causes an assertion failure (see KUNIT_ASSERT_TRUE()) when the assertion
+ * is not met.
+ */
+#define KUNIT_ASSERT_GE(test, left, right) \
+ KUNIT_ASSERT_BINARY(test, left, >=, right)
+
+#define KUNIT_ASSERT_GE_MSG(test, left, right, fmt, ...) \
+ KUNIT_ASSERT_BINARY_MSG(test, \
+ left, \
+ >=, \
+ right, \
+ fmt, \
+ ##__VA_ARGS__)
+
+/**
+ * KUNIT_ASSERT_STREQ() - An assertion that strings @left and @right are equal.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a null terminated string.
+ * @right: an arbitrary expression that evaluates to a null terminated string.
+ *
+ * Sets an assertion that the values that @left and @right evaluate to are
+ * equal. This is the same as KUNIT_EXPECT_STREQ(), except it causes an
+ * assertion failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met.
+ */
+#define KUNIT_ASSERT_STREQ(test, left, right) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ typeof(left) __left = (left); \
+ typeof(right) __right = (right); \
+ \
+ kunit_stream_add(__stream, "Asserted " #left " == " #right ", but\n"); \
+ kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \
+ kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \
+ \
+ KUNIT_ASSERT_END(test, !strcmp(left, right), __stream); \
+} while (0)
+
+#define KUNIT_ASSERT_STREQ_MSG(test, left, right, fmt, ...) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ typeof(left) __left = (left); \
+ typeof(right) __right = (right); \
+ \
+ kunit_stream_add(__stream, "Asserted " #left " == " #right ", but\n"); \
+ kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \
+ kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \
+ kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \
+ \
+ KUNIT_ASSERT_END(test, !strcmp(left, right), __stream); \
+} while (0)
+
+/**
+ * KUNIT_ASSERT_STRNEQ() - Expects that strings @left and @right are not equal.
+ * @test: The test context object.
+ * @left: an arbitrary expression that evaluates to a null terminated string.
+ * @right: an arbitrary expression that evaluates to a null terminated string.
+ *
+ * Sets an expectation that the values that @left and @right evaluate to are
+ * not equal. This is semantically equivalent to
+ * KUNIT_ASSERT_TRUE(@test, strcmp((@left), (@right))). See KUNIT_ASSERT_TRUE()
+ * for more information.
+ */
+#define KUNIT_ASSERT_STRNEQ(test, left, right) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ typeof(left) __left = (left); \
+ typeof(right) __right = (right); \
+ \
+ kunit_stream_add(__stream, "Asserted " #left " == " #right ", but\n"); \
+ kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \
+ kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \
+ \
+ KUNIT_ASSERT_END(test, strcmp(left, right), __stream); \
+} while (0)
+
+#define KUNIT_ASSERT_STRNEQ_MSG(test, left, right, fmt, ...) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ typeof(left) __left = (left); \
+ typeof(right) __right = (right); \
+ \
+ kunit_stream_add(__stream, "Asserted " #left " == " #right ", but\n"); \
+ kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \
+ kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \
+ kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \
+ \
+ KUNIT_ASSERT_END(test, strcmp(left, right), __stream); \
+} while (0)
+
+/**
+ * KUNIT_ASSERT_NOT_ERR_OR_NULL() - Assertion that @ptr is not null and not err.
+ * @test: The test context object.
+ * @ptr: an arbitrary pointer.
+ *
+ * Sets an assertion that the value that @ptr evaluates to is not null and not
+ * an errno stored in a pointer. This is the same as
+ * KUNIT_EXPECT_NOT_ERR_OR_NULL(), except it causes an assertion failure (see
+ * KUNIT_ASSERT_TRUE()) when the assertion is not met.
+ */
+#define KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ typeof(ptr) __ptr = (ptr); \
+ \
+ if (!__ptr) \
+ kunit_stream_add(__stream, \
+ "Asserted " #ptr " is not null, but is\n"); \
+ if (IS_ERR(__ptr)) \
+ kunit_stream_add(__stream, \
+ "Asserted " #ptr " is not error, but is: %ld\n",\
+ PTR_ERR(__ptr)); \
+ \
+ KUNIT_ASSERT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \
+} while (0)
+
+#define KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, ptr, fmt, ...) do { \
+ struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \
+ typeof(ptr) __ptr = (ptr); \
+ \
+ if (!__ptr) { \
+ kunit_stream_add(__stream, \
+ "Asserted " #ptr " is not null, but is\n"); \
+ kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \
+ } \
+ if (IS_ERR(__ptr)) { \
+ kunit_stream_add(__stream, \
+ "Asserted " #ptr " is not error, but is: %ld\n",\
+ PTR_ERR(__ptr)); \
+ \
+ kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \
+ } \
+ KUNIT_ASSERT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \
+} while (0)
+
#endif /* _KUNIT_TEST_H */
diff --git a/kunit/string-stream-test.c b/kunit/string-stream-test.c
index b5641b078b8f6..5f27d576d2daf 100644
--- a/kunit/string-stream-test.c
+++ b/kunit/string-stream-test.c
@@ -34,7 +34,7 @@ static void string_stream_test_get_string(struct kunit *test)
string_stream_add(stream, " %s", "bar");
output = string_stream_get_string(stream);
- KUNIT_EXPECT_STREQ(test, output, "Foo bar");
+ KUNIT_ASSERT_STREQ(test, output, "Foo bar");
kfree(output);
}
@@ -48,16 +48,16 @@ static void string_stream_test_add_and_clear(struct kunit *test)
string_stream_add(stream, "A");
output = string_stream_get_string(stream);
- KUNIT_EXPECT_STREQ(test, output, "AAAAAAAAAA");
- KUNIT_EXPECT_EQ(test, stream->length, (size_t)10);
- KUNIT_EXPECT_FALSE(test, string_stream_is_empty(stream));
+ KUNIT_ASSERT_STREQ(test, output, "AAAAAAAAAA");
+ KUNIT_ASSERT_EQ(test, stream->length, (size_t)10);
+ KUNIT_ASSERT_FALSE(test, string_stream_is_empty(stream));
kfree(output);
string_stream_clear(stream);
output = string_stream_get_string(stream);
- KUNIT_EXPECT_STREQ(test, output, "");
- KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream));
+ KUNIT_ASSERT_STREQ(test, output, "");
+ KUNIT_ASSERT_TRUE(test, string_stream_is_empty(stream));
}
static struct kunit_case string_stream_test_cases[] = {
diff --git a/kunit/test-test.c b/kunit/test-test.c
index 88f4cdf03db2a..058f3fb37458a 100644
--- a/kunit/test-test.c
+++ b/kunit/test-test.c
@@ -78,11 +78,13 @@ static int kunit_try_catch_test_init(struct kunit *test)
struct kunit_try_catch_test_context *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
test->priv = ctx;
ctx->try_catch = kunit_kmalloc(test,
sizeof(*ctx->try_catch),
GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->try_catch);
return 0;
}
diff --git a/kunit/test.c b/kunit/test.c
index 12db50b221781..2723f7bc04682 100644
--- a/kunit/test.c
+++ b/kunit/test.c
@@ -499,3 +499,69 @@ void kunit_expect_ptr_binary_msg(struct kunit *test,
kunit_expect_end(test, compare_result, stream);
}
+
+void kunit_assert_binary_msg(struct kunit *test,
+ long long left, const char *left_name,
+ long long right, const char *right_name,
+ bool compare_result,
+ const char *compare_name,
+ const char *file,
+ const char *line,
+ const char *fmt, ...)
+{
+ struct kunit_stream *stream = kunit_assert_start(test, file, line);
+ struct va_format vaf;
+ va_list args;
+
+ kunit_stream_add(stream,
+ "Asserted %s %s %s, but\n",
+ left_name, compare_name, right_name);
+ kunit_stream_add(stream, "\t\t%s == %lld\n", left_name, left);
+ kunit_stream_add(stream, "\t\t%s == %lld\n", right_name, right);
+
+ if (fmt) {
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ kunit_stream_add(stream, "\n%pV", &vaf);
+
+ va_end(args);
+ }
+
+ kunit_assert_end(test, compare_result, stream);
+}
+
+void kunit_assert_ptr_binary_msg(struct kunit *test,
+ void *left, const char *left_name,
+ void *right, const char *right_name,
+ bool compare_result,
+ const char *compare_name,
+ const char *file,
+ const char *line,
+ const char *fmt, ...)
+{
+ struct kunit_stream *stream = kunit_assert_start(test, file, line);
+ struct va_format vaf;
+ va_list args;
+
+ kunit_stream_add(stream,
+ "Asserted %s %s %s, but\n",
+ left_name, compare_name, right_name);
+ kunit_stream_add(stream, "\t\t%s == %pK\n", left_name, left);
+ kunit_stream_add(stream, "\t\t%s == %pK", right_name, right);
+
+ if (fmt) {
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ kunit_stream_add(stream, "\n%pV", &vaf);
+
+ va_end(args);
+ }
+
+ kunit_assert_end(test, compare_result, stream);
+}
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v10 12/18] kunit: test: add tests for KUnit managed resources
From: Brendan Higgins @ 2019-07-16 9:42 UTC (permalink / raw)
To: frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
jpoimboe-H+wXaHxf7aLQT0dZR+AlfA, keescook-hpIqsD4AKlfQT0dZR+AlfA,
kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
mcgrof-DgEjT+Ai2ygdnm+yROfE0A, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
robh-DgEjT+Ai2ygdnm+yROfE0A, sboyd-DgEjT+Ai2ygdnm+yROfE0A,
shuah-DgEjT+Ai2ygdnm+yROfE0A, tytso-3s7WtUTddSA,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A
Cc: pmladek-IBi9RG/b67k, linux-doc-u79uwXL29TY76Z2rM5mHXA,
amir73il-Re5JQEeQqe8AvxtiuMwx3w, Brendan Higgins,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
Alexander.Levin-0li6OtcxBFHby3iVrkZq2A,
linux-kselftest-u79uwXL29TY76Z2rM5mHXA,
linux-nvdimm-hn68Rpc1hR1g9hUCZPvPmw,
khilman-rdvid1DuHRBWk0Htik3J/w, knut.omang-QHcLZuEGTsvQT0dZR+AlfA,
wfg-VuQAYsv1563Yd54FQh9/CA, joel-U3u1mxZcP9KHXe+LvDLADg,
rientjes-hpIqsD4AKlfQT0dZR+AlfA, jdike-OPE4K8JWMJJBDgjK7y7TUQ,
dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kbuild-u79uwXL29TY76Z2rM5mHXA, Tim.Bird-7U/KSKJipcs,
Avinash Kondareddy, linux-um-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
rostedt-nx8X9YLhiw1AfugRpC6u6w, julia.lawall-L2FTfq7BK8M,
kunit-dev-/JYPxA39Uh5TLH3MbocFFw, richard-/L3Ra7n9ekc,
rdunlap-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
mpe-Gsx/Oe8HsFggBc27wqDAHg, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20190716094302.180360-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
From: Avinash Kondareddy <akndr41-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Add unit tests for KUnit managed resources. KUnit managed resources
(struct kunit_resource) are resources that are automatically cleaned up
at the end of a KUnit test, similar to the concept of devm_* managed
resources.
Signed-off-by: Avinash Kondareddy <akndr41-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Reviewed-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Reviewed-by: Logan Gunthorpe <logang-OTvnGxWRz7hWk0Htik3J/w@public.gmane.org>
---
kunit/test-test.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 225 insertions(+)
diff --git a/kunit/test-test.c b/kunit/test-test.c
index 058f3fb37458a..725f1486376fa 100644
--- a/kunit/test-test.c
+++ b/kunit/test-test.c
@@ -101,3 +101,228 @@ static struct kunit_suite kunit_try_catch_test_suite = {
.test_cases = kunit_try_catch_test_cases,
};
kunit_test_suite(kunit_try_catch_test_suite);
+
+/*
+ * Context for testing test managed resources
+ * is_resource_initialized is used to test arbitrary resources
+ */
+struct kunit_test_resource_context {
+ struct kunit test;
+ bool is_resource_initialized;
+ int allocate_order[2];
+ int free_order[2];
+};
+
+static int fake_resource_init(struct kunit_resource *res, void *context)
+{
+ struct kunit_test_resource_context *ctx = context;
+
+ res->allocation = &ctx->is_resource_initialized;
+ ctx->is_resource_initialized = true;
+ return 0;
+}
+
+static void fake_resource_free(struct kunit_resource *res)
+{
+ bool *is_resource_initialized = res->allocation;
+
+ *is_resource_initialized = false;
+}
+
+static void kunit_resource_test_init_resources(struct kunit *test)
+{
+ struct kunit_test_resource_context *ctx = test->priv;
+
+ kunit_init_test(&ctx->test, "testing_test_init_test");
+
+ KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources));
+}
+
+static void kunit_resource_test_alloc_resource(struct kunit *test)
+{
+ struct kunit_test_resource_context *ctx = test->priv;
+ struct kunit_resource *res;
+ kunit_resource_free_t free = fake_resource_free;
+
+ res = kunit_alloc_and_get_resource(&ctx->test,
+ fake_resource_init,
+ fake_resource_free,
+ GFP_KERNEL,
+ ctx);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res);
+ KUNIT_EXPECT_PTR_EQ(test,
+ &ctx->is_resource_initialized,
+ (bool *) res->allocation);
+ KUNIT_EXPECT_TRUE(test, list_is_last(&res->node, &ctx->test.resources));
+ KUNIT_EXPECT_PTR_EQ(test, free, res->free);
+}
+
+static void kunit_resource_test_free_resource(struct kunit *test)
+{
+ struct kunit_test_resource_context *ctx = test->priv;
+ struct kunit_resource *res = kunit_alloc_and_get_resource(
+ &ctx->test,
+ fake_resource_init,
+ fake_resource_free,
+ GFP_KERNEL,
+ ctx);
+
+ kunit_free_resource(&ctx->test, res);
+
+ KUNIT_EXPECT_FALSE(test, ctx->is_resource_initialized);
+ KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources));
+}
+
+static void kunit_resource_test_cleanup_resources(struct kunit *test)
+{
+ int i;
+ struct kunit_test_resource_context *ctx = test->priv;
+ struct kunit_resource *resources[5];
+
+ for (i = 0; i < ARRAY_SIZE(resources); i++) {
+ resources[i] = kunit_alloc_and_get_resource(&ctx->test,
+ fake_resource_init,
+ fake_resource_free,
+ GFP_KERNEL,
+ ctx);
+ }
+
+ kunit_cleanup(&ctx->test);
+
+ KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources));
+}
+
+static void kunit_resource_test_mark_order(int order_array[],
+ size_t order_size,
+ int key)
+{
+ int i;
+
+ for (i = 0; i < order_size && order_array[i]; i++)
+ ;
+
+ order_array[i] = key;
+}
+
+#define KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, order_field, key) \
+ kunit_resource_test_mark_order(ctx->order_field, \
+ ARRAY_SIZE(ctx->order_field), \
+ key)
+
+static int fake_resource_2_init(struct kunit_resource *res, void *context)
+{
+ struct kunit_test_resource_context *ctx = context;
+
+ KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, allocate_order, 2);
+
+ res->allocation = ctx;
+
+ return 0;
+}
+
+static void fake_resource_2_free(struct kunit_resource *res)
+{
+ struct kunit_test_resource_context *ctx = res->allocation;
+
+ KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, free_order, 2);
+}
+
+static int fake_resource_1_init(struct kunit_resource *res, void *context)
+{
+ struct kunit_test_resource_context *ctx = context;
+
+ kunit_alloc_and_get_resource(&ctx->test,
+ fake_resource_2_init,
+ fake_resource_2_free,
+ GFP_KERNEL,
+ ctx);
+
+ KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, allocate_order, 1);
+
+ res->allocation = ctx;
+
+ return 0;
+}
+
+static void fake_resource_1_free(struct kunit_resource *res)
+{
+ struct kunit_test_resource_context *ctx = res->allocation;
+
+ KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, free_order, 1);
+}
+
+/*
+ * TODO(brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org): replace the arrays that keep track of the
+ * order of allocation and freeing with strict mocks using the IN_SEQUENCE macro
+ * to assert allocation and freeing order when the feature becomes available.
+ */
+static void kunit_resource_test_proper_free_ordering(struct kunit *test)
+{
+ struct kunit_test_resource_context *ctx = test->priv;
+
+ /* fake_resource_1 allocates a fake_resource_2 in its init. */
+ kunit_alloc_and_get_resource(&ctx->test,
+ fake_resource_1_init,
+ fake_resource_1_free,
+ GFP_KERNEL,
+ ctx);
+
+ /*
+ * Since fake_resource_2_init calls KUNIT_RESOURCE_TEST_MARK_ORDER
+ * before returning to fake_resource_1_init, it should be the first to
+ * put its key in the allocate_order array.
+ */
+ KUNIT_EXPECT_EQ(test, ctx->allocate_order[0], 2);
+ KUNIT_EXPECT_EQ(test, ctx->allocate_order[1], 1);
+
+ kunit_cleanup(&ctx->test);
+
+ /*
+ * Because fake_resource_2 finishes allocation before fake_resource_1,
+ * fake_resource_1 should be freed first since it could depend on
+ * fake_resource_2.
+ */
+ KUNIT_EXPECT_EQ(test, ctx->free_order[0], 1);
+ KUNIT_EXPECT_EQ(test, ctx->free_order[1], 2);
+}
+
+static int kunit_resource_test_init(struct kunit *test)
+{
+ struct kunit_test_resource_context *ctx =
+ kzalloc(sizeof(*ctx), GFP_KERNEL);
+
+ if (!ctx)
+ return -ENOMEM;
+
+ test->priv = ctx;
+
+ kunit_init_test(&ctx->test, "test_test_context");
+
+ return 0;
+}
+
+static void kunit_resource_test_exit(struct kunit *test)
+{
+ struct kunit_test_resource_context *ctx = test->priv;
+
+ kunit_cleanup(&ctx->test);
+ kfree(ctx);
+}
+
+static struct kunit_case kunit_resource_test_cases[] = {
+ KUNIT_CASE(kunit_resource_test_init_resources),
+ KUNIT_CASE(kunit_resource_test_alloc_resource),
+ KUNIT_CASE(kunit_resource_test_free_resource),
+ KUNIT_CASE(kunit_resource_test_cleanup_resources),
+ KUNIT_CASE(kunit_resource_test_proper_free_ordering),
+ {}
+};
+
+static struct kunit_suite kunit_resource_test_suite = {
+ .name = "kunit-resource-test",
+ .init = kunit_resource_test_init,
+ .exit = kunit_resource_test_exit,
+ .test_cases = kunit_resource_test_cases,
+};
+kunit_test_suite(kunit_resource_test_suite);
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v10 13/18] kunit: tool: add Python wrappers for running KUnit tests
From: Brendan Higgins @ 2019-07-16 9:42 UTC (permalink / raw)
To: frowand.list, gregkh, jpoimboe, keescook, kieran.bingham, mcgrof,
peterz, robh, sboyd, shuah, tytso, yamada.masahiro
Cc: pmladek, linux-doc, amir73il, Brendan Higgins, dri-devel,
Alexander.Levin, linux-kselftest, linux-nvdimm, khilman,
knut.omang, Felix Guo, wfg, joel, rientjes, jdike, dan.carpenter,
devicetree, linux-kbuild, Tim.Bird, linux-um, rostedt,
julia.lawall, kunit-dev, richard, rdunlap, linux-kernel, mpe,
linux-fsdevel, logang
In-Reply-To: <20190716094302.180360-1-brendanhiggins@google.com>
From: Felix Guo <felixguoxiuping@gmail.com>
The ultimate goal is to create minimal isolated test binaries; in the
meantime we are using UML to provide the infrastructure to run tests, so
define an abstract way to configure and run tests that allow us to
change the context in which tests are built without affecting the user.
This also makes pretty and dynamic error reporting, and a lot of other
nice features easier.
kunit_config.py:
- parse .config and Kconfig files.
kunit_kernel.py: provides helper functions to:
- configure the kernel using kunitconfig.
- build the kernel with the appropriate configuration.
- provide function to invoke the kernel and stream the output back.
kunit_parser.py: parses raw logs returned out by kunit_kernel and
displays them in a user friendly way.
test_data/*: samples of test data for testing kunit.py, kunit_config.py,
etc.
Signed-off-by: Felix Guo <felixguoxiuping@gmail.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
tools/testing/kunit/.gitignore | 3 +
tools/testing/kunit/kunit.py | 116 +++++++
tools/testing/kunit/kunit_config.py | 66 ++++
tools/testing/kunit/kunit_kernel.py | 148 +++++++++
tools/testing/kunit/kunit_parser.py | 290 ++++++++++++++++++
tools/testing/kunit/kunit_tool_test.py | 206 +++++++++++++
.../test_is_test_passed-all_passed.log | 32 ++
.../test_data/test_is_test_passed-crash.log | 69 +++++
.../test_data/test_is_test_passed-failure.log | 36 +++
.../test_is_test_passed-no_tests_run.log | 75 +++++
.../test_output_isolated_correctly.log | 106 +++++++
.../test_data/test_read_from_file.kconfig | 17 +
12 files changed, 1164 insertions(+)
create mode 100644 tools/testing/kunit/.gitignore
create mode 100755 tools/testing/kunit/kunit.py
create mode 100644 tools/testing/kunit/kunit_config.py
create mode 100644 tools/testing/kunit/kunit_kernel.py
create mode 100644 tools/testing/kunit/kunit_parser.py
create mode 100755 tools/testing/kunit/kunit_tool_test.py
create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-all_passed.log
create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-crash.log
create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-failure.log
create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-no_tests_run.log
create mode 100644 tools/testing/kunit/test_data/test_output_isolated_correctly.log
create mode 100644 tools/testing/kunit/test_data/test_read_from_file.kconfig
diff --git a/tools/testing/kunit/.gitignore b/tools/testing/kunit/.gitignore
new file mode 100644
index 0000000000000..c791ff59a37a9
--- /dev/null
+++ b/tools/testing/kunit/.gitignore
@@ -0,0 +1,3 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
\ No newline at end of file
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
new file mode 100755
index 0000000000000..da11bd62a4b82
--- /dev/null
+++ b/tools/testing/kunit/kunit.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# A thin wrapper on top of the KUnit Kernel
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import argparse
+import sys
+import os
+import time
+
+from collections import namedtuple
+from enum import Enum, auto
+
+import kunit_config
+import kunit_kernel
+import kunit_parser
+
+KunitResult = namedtuple('KunitResult', ['status','result'])
+
+KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 'build_dir'])
+
+class KunitStatus(Enum):
+ SUCCESS = auto()
+ CONFIG_FAILURE = auto()
+ BUILD_FAILURE = auto()
+ TEST_FAILURE = auto()
+
+def run_tests(linux: kunit_kernel.LinuxSourceTree,
+ request: KunitRequest) -> KunitResult:
+ config_start = time.time()
+ success = linux.build_reconfig(request.build_dir)
+ config_end = time.time()
+ if not success:
+ return KunitResult(KunitStatus.CONFIG_FAILURE, 'could not configure kernel')
+
+ kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
+
+ build_start = time.time()
+ success = linux.build_um_kernel(request.jobs, request.build_dir)
+ build_end = time.time()
+ if not success:
+ return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel')
+
+ kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
+ test_start = time.time()
+
+ test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS,
+ [],
+ 'Tests not Parsed.')
+ if request.raw_output:
+ kunit_parser.raw_output(
+ linux.run_kernel(timeout=request.timeout))
+ else:
+ kunit_output = linux.run_kernel(timeout=request.timeout)
+ test_result = kunit_parser.parse_run_tests(kunit_output)
+ test_end = time.time()
+
+ kunit_parser.print_with_timestamp((
+ 'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' +
+ 'building, %.3fs running\n') % (
+ test_end - config_start,
+ config_end - config_start,
+ build_end - build_start,
+ test_end - test_start))
+
+ if test_result.status != kunit_parser.TestStatus.SUCCESS:
+ return KunitResult(KunitStatus.TEST_FAILURE, test_result)
+ else:
+ return KunitResult(KunitStatus.SUCCESS, test_result)
+
+def main(argv, linux):
+ parser = argparse.ArgumentParser(
+ description='Helps writing and running KUnit tests.')
+ subparser = parser.add_subparsers(dest='subcommand')
+
+ run_parser = subparser.add_parser('run', help='Runs KUnit tests.')
+ run_parser.add_argument('--raw_output', help='don\'t format output from kernel',
+ action='store_true')
+
+ run_parser.add_argument('--timeout',
+ help='maximum number of seconds to allow for all tests '
+ 'to run. This does not include time taken to build the '
+ 'tests.',
+ type=int,
+ default=300,
+ metavar='timeout')
+
+ run_parser.add_argument('--jobs',
+ help='As in the make command, "Specifies the number of '
+ 'jobs (commands) to run simultaneously."',
+ type=int, default=8, metavar='jobs')
+
+ run_parser.add_argument('--build_dir',
+ help='As in the make command, it specifies the build '
+ 'directory.',
+ type=str, default=None, metavar='build_dir')
+
+ cli_args = parser.parse_args(argv)
+
+ if cli_args.subcommand == 'run':
+ request = KunitRequest(cli_args.raw_output,
+ cli_args.timeout,
+ cli_args.jobs,
+ cli_args.build_dir)
+ result = run_tests(linux, request)
+ if result.status != KunitStatus.SUCCESS:
+ sys.exit(1)
+ else:
+ parser.print_help()
+
+if __name__ == '__main__':
+ main(sys.argv[1:], kunit_kernel.LinuxSourceTree())
diff --git a/tools/testing/kunit/kunit_config.py b/tools/testing/kunit/kunit_config.py
new file mode 100644
index 0000000000000..ebf3942b23f51
--- /dev/null
+++ b/tools/testing/kunit/kunit_config.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Builds a .config from a kunitconfig.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import collections
+import re
+
+CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_\w+ is not set$'
+CONFIG_PATTERN = r'^CONFIG_\w+=\S+$'
+
+KconfigEntryBase = collections.namedtuple('KconfigEntry', ['raw_entry'])
+
+
+class KconfigEntry(KconfigEntryBase):
+
+ def __str__(self) -> str:
+ return self.raw_entry
+
+
+class KconfigParseError(Exception):
+ """Error parsing Kconfig defconfig or .config."""
+
+
+class Kconfig(object):
+ """Represents defconfig or .config specified using the Kconfig language."""
+
+ def __init__(self):
+ self._entries = []
+
+ def entries(self):
+ return set(self._entries)
+
+ def add_entry(self, entry: KconfigEntry) -> None:
+ self._entries.append(entry)
+
+ def is_subset_of(self, other: 'Kconfig') -> bool:
+ return self.entries().issubset(other.entries())
+
+ def write_to_file(self, path: str) -> None:
+ with open(path, 'w') as f:
+ for entry in self.entries():
+ f.write(str(entry) + '\n')
+
+ def parse_from_string(self, blob: str) -> None:
+ """Parses a string containing KconfigEntrys and populates this Kconfig."""
+ self._entries = []
+ is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN)
+ config_matcher = re.compile(CONFIG_PATTERN)
+ for line in blob.split('\n'):
+ line = line.strip()
+ if not line:
+ continue
+ elif config_matcher.match(line) or is_not_set_matcher.match(line):
+ self._entries.append(KconfigEntry(line))
+ elif line[0] == '#':
+ continue
+ else:
+ raise KconfigParseError('Failed to parse: ' + line)
+
+ def read_from_file(self, path: str) -> None:
+ with open(path, 'r') as f:
+ self.parse_from_string(f.read())
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
new file mode 100644
index 0000000000000..07c0abf2f47df
--- /dev/null
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Runs UML kernel, collects output, and handles errors.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+
+import logging
+import subprocess
+import os
+
+import kunit_config
+
+KCONFIG_PATH = '.config'
+
+class ConfigError(Exception):
+ """Represents an error trying to configure the Linux kernel."""
+
+
+class BuildError(Exception):
+ """Represents an error trying to build the Linux kernel."""
+
+
+class LinuxSourceTreeOperations(object):
+ """An abstraction over command line operations performed on a source tree."""
+
+ def make_mrproper(self):
+ try:
+ subprocess.check_output(['make', 'mrproper'])
+ except OSError as e:
+ raise ConfigError('Could not call make command: ' + e)
+ except subprocess.CalledProcessError as e:
+ raise ConfigError(e.output)
+
+ def make_olddefconfig(self, build_dir):
+ command = ['make', 'ARCH=um', 'olddefconfig']
+ if build_dir:
+ command += ['O=' + build_dir]
+ try:
+ subprocess.check_output(command)
+ except OSError as e:
+ raise ConfigError('Could not call make command: ' + e)
+ except subprocess.CalledProcessError as e:
+ raise ConfigError(e.output)
+
+ def make(self, jobs, build_dir):
+ command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
+ if build_dir:
+ command += ['O=' + build_dir]
+ try:
+ subprocess.check_output(command)
+ except OSError as e:
+ raise BuildError('Could not call execute make: ' + e)
+ except subprocess.CalledProcessError as e:
+ raise BuildError(e.output)
+
+ def linux_bin(self, params, timeout, build_dir):
+ """Runs the Linux UML binary. Must be named 'linux'."""
+ linux_bin = './linux'
+ if build_dir:
+ linux_bin = os.path.join(build_dir, 'linux')
+ process = subprocess.Popen(
+ [linux_bin] + params,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ process.wait(timeout=timeout)
+ return process
+
+
+def get_kconfig_path(build_dir):
+ kconfig_path = KCONFIG_PATH
+ if build_dir:
+ kconfig_path = os.path.join(build_dir, KCONFIG_PATH)
+ return kconfig_path
+
+class LinuxSourceTree(object):
+ """Represents a Linux kernel source tree with KUnit tests."""
+
+ def __init__(self):
+ self._kconfig = kunit_config.Kconfig()
+ self._kconfig.read_from_file('kunitconfig')
+ self._ops = LinuxSourceTreeOperations()
+
+ def clean(self):
+ try:
+ self._ops.make_mrproper()
+ except ConfigError as e:
+ logging.error(e)
+ return False
+ return True
+
+ def build_config(self, build_dir):
+ kconfig_path = get_kconfig_path(build_dir)
+ if build_dir and not os.path.exists(build_dir):
+ os.mkdir(build_dir)
+ self._kconfig.write_to_file(kconfig_path)
+ try:
+ self._ops.make_olddefconfig(build_dir)
+ except ConfigError as e:
+ logging.error(e)
+ return False
+ validated_kconfig = kunit_config.Kconfig()
+ validated_kconfig.read_from_file(kconfig_path)
+ if not self._kconfig.is_subset_of(validated_kconfig):
+ logging.error('Provided Kconfig is not contained in validated .config!')
+ return False
+ return True
+
+ def build_reconfig(self, build_dir):
+ """Creates a new .config if it is not a subset of the kunitconfig."""
+ kconfig_path = get_kconfig_path(build_dir)
+ if os.path.exists(kconfig_path):
+ existing_kconfig = kunit_config.Kconfig()
+ existing_kconfig.read_from_file(kconfig_path)
+ if not self._kconfig.is_subset_of(existing_kconfig):
+ print('Regenerating .config ...')
+ os.remove(kconfig_path)
+ return self.build_config(build_dir)
+ else:
+ return True
+ else:
+ print('Generating .config ...')
+ return self.build_config(build_dir)
+
+ def build_um_kernel(self, jobs, build_dir):
+ try:
+ self._ops.make_olddefconfig(build_dir)
+ self._ops.make(jobs, build_dir)
+ except (ConfigError, BuildError) as e:
+ logging.error(e)
+ return False
+ used_kconfig = kunit_config.Kconfig()
+ used_kconfig.read_from_file(get_kconfig_path(build_dir))
+ if not self._kconfig.is_subset_of(used_kconfig):
+ logging.error('Provided Kconfig is not contained in final config!')
+ return False
+ return True
+
+ def run_kernel(self, args=[], timeout=None, build_dir=None):
+ args.extend(['mem=256M'])
+ process = self._ops.linux_bin(args, timeout, build_dir)
+ with open('test.log', 'w') as f:
+ for line in process.stdout:
+ f.write(line.rstrip().decode('ascii') + '\n')
+ yield line.rstrip().decode('ascii')
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
new file mode 100644
index 0000000000000..f27f3d675c3bb
--- /dev/null
+++ b/tools/testing/kunit/kunit_parser.py
@@ -0,0 +1,290 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Parses test results from a kernel dmesg log.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import re
+
+from collections import namedtuple
+from datetime import datetime
+from enum import Enum, auto
+from functools import reduce
+from typing import List
+
+TestResult = namedtuple('TestResult', ['status','suites','log'])
+
+class TestSuite(object):
+ def __init__(self):
+ self.status = None
+ self.name = None
+ self.cases = []
+
+ def __str__(self):
+ return 'TestSuite(' + self.status + ',' + self.name + ',' + str(self.cases) + ')'
+
+ def __repr__(self):
+ return str(self)
+
+class TestCase(object):
+ def __init__(self):
+ self.status = None
+ self.name = ''
+ self.log = []
+
+ def __str__(self):
+ return 'TestCase(' + self.status + ',' + self.name + ',' + str(self.log) + ')'
+
+ def __repr__(self):
+ return str(self)
+
+class TestStatus(Enum):
+ SUCCESS = auto()
+ FAILURE = auto()
+ TEST_CRASHED = auto()
+ NO_TESTS = auto()
+
+kunit_start_re = re.compile(r'^TAP version [0-9]+$')
+kunit_end_re = re.compile('List of all partitions:')
+
+def isolate_kunit_output(kernel_output):
+ started = False
+ for line in kernel_output:
+ if kunit_start_re.match(line):
+ started = True
+ yield line
+ elif kunit_end_re.match(line):
+ break
+ elif started:
+ yield line
+
+def raw_output(kernel_output):
+ for line in kernel_output:
+ print(line)
+
+DIVIDER = '=' * 60
+
+RESET = '\033[0;0m'
+
+def red(text):
+ return '\033[1;31m' + text + RESET
+
+def yellow(text):
+ return '\033[1;33m' + text + RESET
+
+def green(text):
+ return '\033[1;32m' + text + RESET
+
+def print_with_timestamp(message):
+ print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message))
+
+def format_suite_divider(message):
+ return '======== ' + message + ' ========'
+
+def print_suite_divider(message):
+ print_with_timestamp(DIVIDER)
+ print_with_timestamp(format_suite_divider(message))
+
+def print_log(log):
+ for m in log:
+ print_with_timestamp(m)
+
+TAP_ENTRIES = re.compile(r'^(TAP|\t?ok|\t?not ok|\t?[0-9]+\.\.[0-9]+|\t?#).*$')
+
+def consume_non_diagnositic(lines: List[str]) -> None:
+ while not TAP_ENTRIES.match(lines[0]):
+ lines.pop(0)
+
+def save_non_diagnositic(lines: List[str], test_case: TestCase) -> None:
+ while not TAP_ENTRIES.match(lines[0]):
+ test_case.log.append(lines[0])
+ lines.pop(0)
+
+OkNotOkResult = namedtuple('OkNotOkResult', ['is_ok','description', 'text'])
+
+OK_NOT_OK_SUBTEST = re.compile(r'^\t(ok|not ok) [0-9]+ - (.*)$')
+
+OK_NOT_OK_MODULE = re.compile(r'^(ok|not ok) [0-9]+ - (.*)$')
+
+def parse_ok_not_ok_test_case(lines: List[str], test_case: TestCase) -> bool:
+ save_non_diagnositic(lines, test_case)
+ line = lines[0]
+ match = OK_NOT_OK_SUBTEST.match(line)
+ if match:
+ test_case.log.append(lines.pop(0))
+ test_case.name = match.group(2)
+ if test_case.status == TestStatus.TEST_CRASHED:
+ return True
+ if match.group(1) == 'ok':
+ test_case.status = TestStatus.SUCCESS
+ else:
+ test_case.status = TestStatus.FAILURE
+ return True
+ else:
+ return False
+
+SUBTEST_DIAGNOSTIC = re.compile(r'^\t# .*?: (.*)$')
+DIAGNOSTIC_CRASH_MESSAGE = 'kunit test case crashed!'
+
+def parse_diagnostic(lines: List[str], test_case: TestCase) -> bool:
+ save_non_diagnositic(lines, test_case)
+ line = lines[0]
+ match = SUBTEST_DIAGNOSTIC.match(line)
+ if match:
+ test_case.log.append(lines.pop(0))
+ if match.group(1) == DIAGNOSTIC_CRASH_MESSAGE:
+ test_case.status = TestStatus.TEST_CRASHED
+ return True
+ else:
+ return False
+
+def parse_test_case(lines: List[str]) -> TestCase:
+ test_case = TestCase()
+ save_non_diagnositic(lines, test_case)
+ while parse_diagnostic(lines, test_case):
+ pass
+ if parse_ok_not_ok_test_case(lines, test_case):
+ return test_case
+ else:
+ return None
+
+SUBTEST_HEADER = re.compile(r'^\t# Subtest: (.*)$')
+
+def parse_subtest_header(lines: List[str]) -> str:
+ consume_non_diagnositic(lines)
+ match = SUBTEST_HEADER.match(lines[0])
+ if match:
+ lines.pop(0)
+ return match.group(1)
+ else:
+ return None
+
+SUBTEST_PLAN = re.compile(r'\t[0-9]+\.\.([0-9]+)')
+
+def parse_subtest_plan(lines: List[str]) -> int:
+ consume_non_diagnositic(lines)
+ match = SUBTEST_PLAN.match(lines[0])
+ if match:
+ lines.pop(0)
+ return match.group(1)
+ else:
+ return None
+
+def max_status(left: TestStatus, right: TestStatus) -> TestStatus:
+ if left == TestStatus.TEST_CRASHED or right == TestStatus.TEST_CRASHED:
+ return TestStatus.TEST_CRASHED
+ elif left == TestStatus.FAILURE or right == TestStatus.FAILURE:
+ return TestStatus.FAILURE
+ elif left != TestStatus.SUCCESS:
+ return left
+ elif right != TestStatus.SUCCESS:
+ return right
+ else:
+ return TestStatus.SUCCESS
+
+def parse_ok_not_ok_test_suite(lines: List[str], test_suite: TestSuite) -> bool:
+ consume_non_diagnositic(lines)
+ line = lines[0]
+ match = OK_NOT_OK_MODULE.match(line)
+ if match:
+ lines.pop(0)
+ if match.group(1) == 'ok':
+ test_suite.status = TestStatus.SUCCESS
+ else:
+ test_suite.status = TestStatus.FAILURE
+ return True
+ else:
+ return False
+
+def bubble_up_errors(to_status, status_container_list) -> TestStatus:
+ status_list = map(to_status, status_container_list)
+ return reduce(max_status, status_list, TestStatus.SUCCESS)
+
+def bubble_up_test_case_errors(test_suite: TestSuite) -> TestStatus:
+ max_test_case_status = bubble_up_errors(lambda x: x.status, test_suite.cases)
+ return max_status(max_test_case_status, test_suite.status)
+
+def parse_test_suite(lines: List[str]) -> TestSuite:
+ if not lines:
+ return None
+ consume_non_diagnositic(lines)
+ test_suite = TestSuite()
+ test_suite.status = TestStatus.SUCCESS
+ name = parse_subtest_header(lines)
+ if not name:
+ return None
+ test_suite.name = name
+ test_case_num = parse_subtest_plan(lines)
+ if not test_case_num:
+ return None
+ test_case = parse_test_case(lines)
+ while test_case:
+ test_suite.cases.append(test_case)
+ test_case = parse_test_case(lines)
+ if parse_ok_not_ok_test_suite(lines, test_suite):
+ test_suite.status = bubble_up_test_case_errors(test_suite)
+ return test_suite
+ else:
+ print('failed to parse end of suite' + lines[0])
+ return None
+
+TAP_HEADER = re.compile(r'^TAP version 14$')
+
+def parse_tap_header(lines: List[str]) -> bool:
+ consume_non_diagnositic(lines)
+ if TAP_HEADER.match(lines[0]):
+ lines.pop(0)
+ return True
+ else:
+ return False
+
+def bubble_up_suite_errors(test_suite_list: List[TestSuite]) -> TestStatus:
+ return bubble_up_errors(lambda x: x.status, test_suite_list)
+
+def parse_test_result(lines: List[str]) -> TestResult:
+ if not lines:
+ return TestResult(TestStatus.NO_TESTS, [], lines)
+ consume_non_diagnositic(lines)
+ if not parse_tap_header(lines):
+ return None
+ test_suites = []
+ test_suite = parse_test_suite(lines)
+ while test_suite:
+ test_suites.append(test_suite)
+ test_suite = parse_test_suite(lines)
+ return TestResult(bubble_up_suite_errors(test_suites), test_suites, lines)
+
+def parse_run_tests(kernel_output) -> TestResult:
+ total_tests = 0
+ failed_tests = 0
+ crashed_tests = 0
+ test_result = parse_test_result(list(isolate_kunit_output(kernel_output)))
+ for test_suite in test_result.suites:
+ if test_suite.status == TestStatus.SUCCESS:
+ print_suite_divider(green('[PASSED] ') + test_suite.name)
+ elif test_suite.status == TestStatus.TEST_CRASHED:
+ print_suite_divider(red('[CRASHED] ' + test_suite.name))
+ else:
+ print_suite_divider(red('[FAILED] ') + test_suite.name)
+ for test_case in test_suite.cases:
+ total_tests += 1
+ if test_case.status == TestStatus.SUCCESS:
+ print_with_timestamp(green('[PASSED] ') + test_case.name)
+ elif test_case.status == TestStatus.TEST_CRASHED:
+ crashed_tests += 1
+ print_with_timestamp(red('[CRASHED] ' + test_case.name))
+ print_log(map(yellow, test_case.log))
+ print_with_timestamp('')
+ else:
+ failed_tests += 1
+ print_with_timestamp(red('[FAILED] ') + test_case.name)
+ print_log(map(yellow, test_case.log))
+ print_with_timestamp('')
+ print_with_timestamp(DIVIDER)
+ fmt = green if test_result.status == TestStatus.SUCCESS else red
+ print_with_timestamp(
+ fmt('Testing complete. %d tests run. %d failed. %d crashed.' %
+ (total_tests, failed_tests, crashed_tests)))
+ return test_result
diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py
new file mode 100755
index 0000000000000..4a12baa0cd4e0
--- /dev/null
+++ b/tools/testing/kunit/kunit_tool_test.py
@@ -0,0 +1,206 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# A collection of tests for tools/testing/kunit/kunit.py
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import unittest
+from unittest import mock
+
+import tempfile, shutil # Handling test_tmpdir
+
+import os
+
+import kunit_config
+import kunit_parser
+import kunit_kernel
+import kunit
+
+test_tmpdir = ''
+
+def setUpModule():
+ global test_tmpdir
+ test_tmpdir = tempfile.mkdtemp()
+
+def tearDownModule():
+ shutil.rmtree(test_tmpdir)
+
+def get_absolute_path(path):
+ return os.path.join(os.path.dirname(__file__), path)
+
+class KconfigTest(unittest.TestCase):
+
+ def test_is_subset_of(self):
+ kconfig0 = kunit_config.Kconfig()
+ self.assertTrue(kconfig0.is_subset_of(kconfig0))
+
+ kconfig1 = kunit_config.Kconfig()
+ kconfig1.add_entry(kunit_config.KconfigEntry('CONFIG_TEST=y'))
+ self.assertTrue(kconfig1.is_subset_of(kconfig1))
+ self.assertTrue(kconfig0.is_subset_of(kconfig1))
+ self.assertFalse(kconfig1.is_subset_of(kconfig0))
+
+ def test_read_from_file(self):
+ kconfig = kunit_config.Kconfig()
+ kconfig_path = get_absolute_path(
+ 'test_data/test_read_from_file.kconfig')
+
+ kconfig.read_from_file(kconfig_path)
+
+ expected_kconfig = kunit_config.Kconfig()
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('CONFIG_UML=y'))
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('CONFIG_MMU=y'))
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('CONFIG_TEST=y'))
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('CONFIG_EXAMPLE_TEST=y'))
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('# CONFIG_MK8 is not set'))
+
+ self.assertEqual(kconfig.entries(), expected_kconfig.entries())
+
+ def test_write_to_file(self):
+ kconfig_path = os.path.join(test_tmpdir, '.config')
+
+ expected_kconfig = kunit_config.Kconfig()
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('CONFIG_UML=y'))
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('CONFIG_MMU=y'))
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('CONFIG_TEST=y'))
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('CONFIG_EXAMPLE_TEST=y'))
+ expected_kconfig.add_entry(
+ kunit_config.KconfigEntry('# CONFIG_MK8 is not set'))
+
+ expected_kconfig.write_to_file(kconfig_path)
+
+ actual_kconfig = kunit_config.Kconfig()
+ actual_kconfig.read_from_file(kconfig_path)
+
+ self.assertEqual(actual_kconfig.entries(),
+ expected_kconfig.entries())
+
+class KUnitParserTest(unittest.TestCase):
+
+ def assertContains(self, needle, haystack):
+ for line in haystack:
+ if needle in line:
+ return
+ raise AssertionError('"' +
+ str(needle) + '" not found in "' + str(haystack) + '"!')
+
+ def test_output_isolated_correctly(self):
+ log_path = get_absolute_path(
+ 'test_data/test_output_isolated_correctly.log')
+ file = open(log_path)
+ result = kunit_parser.isolate_kunit_output(file.readlines())
+ self.assertContains('TAP version 14\n', result)
+ self.assertContains(' # Subtest: example', result)
+ self.assertContains(' 1..2', result)
+ self.assertContains(' ok 1 - example_simple_test', result)
+ self.assertContains(' ok 2 - example_mock_test', result)
+ self.assertContains('ok 1 - example', result)
+ file.close()
+
+ def test_parse_successful_test_log(self):
+ all_passed_log = get_absolute_path(
+ 'test_data/test_is_test_passed-all_passed.log')
+ file = open(all_passed_log)
+ result = kunit_parser.parse_run_tests(file.readlines())
+ self.assertEqual(
+ kunit_parser.TestStatus.SUCCESS,
+ result.status)
+ file.close()
+
+ def test_parse_failed_test_log(self):
+ failed_log = get_absolute_path(
+ 'test_data/test_is_test_passed-failure.log')
+ file = open(failed_log)
+ result = kunit_parser.parse_run_tests(file.readlines())
+ self.assertEqual(
+ kunit_parser.TestStatus.FAILURE,
+ result.status)
+ file.close()
+
+ def test_no_tests(self):
+ empty_log = get_absolute_path(
+ 'test_data/test_is_test_passed-no_tests_run.log')
+ file = open(empty_log)
+ result = kunit_parser.parse_run_tests(
+ kunit_parser.isolate_kunit_output(file.readlines()))
+ self.assertEqual(0, len(result.suites))
+ self.assertEqual(
+ kunit_parser.TestStatus.NO_TESTS,
+ result.status)
+ file.close()
+
+ def test_crashed_test(self):
+ crashed_log = get_absolute_path(
+ 'test_data/test_is_test_passed-crash.log')
+ file = open(crashed_log)
+ result = kunit_parser.parse_run_tests(file.readlines())
+ self.assertEqual(
+ kunit_parser.TestStatus.TEST_CRASHED,
+ result.status)
+ file.close()
+
+class StrContains(str):
+ def __eq__(self, other):
+ return self in other
+
+class KUnitMainTest(unittest.TestCase):
+ def setUp(self):
+ path = get_absolute_path('test_data/test_is_test_passed-all_passed.log')
+ file = open(path)
+ all_passed_log = file.readlines()
+ self.print_patch = mock.patch('builtins.print')
+ self.print_mock = self.print_patch.start()
+ self.linux_source_mock = mock.Mock()
+ self.linux_source_mock.build_reconfig = mock.Mock(return_value=True)
+ self.linux_source_mock.build_um_kernel = mock.Mock(return_value=True)
+ self.linux_source_mock.run_kernel = mock.Mock(return_value=all_passed_log)
+
+ def tearDown(self):
+ self.print_patch.stop()
+ pass
+
+ def test_run_passes_args_pass(self):
+ kunit.main(['run'], self.linux_source_mock)
+ assert self.linux_source_mock.build_reconfig.call_count == 1
+ assert self.linux_source_mock.run_kernel.call_count == 1
+ self.print_mock.assert_any_call(StrContains('Testing complete.'))
+
+ def test_run_passes_args_fail(self):
+ self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
+ with self.assertRaises(SystemExit) as e:
+ kunit.main(['run'], self.linux_source_mock)
+ assert type(e.exception) == SystemExit
+ assert e.exception.code == 1
+ assert self.linux_source_mock.build_reconfig.call_count == 1
+ assert self.linux_source_mock.run_kernel.call_count == 1
+ self.print_mock.assert_any_call(StrContains(' 0 tests run'))
+
+ def test_run_raw_output(self):
+ self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
+ kunit.main(['run', '--raw_output'], self.linux_source_mock)
+ assert self.linux_source_mock.build_reconfig.call_count == 1
+ assert self.linux_source_mock.run_kernel.call_count == 1
+ for kall in self.print_mock.call_args_list:
+ assert kall != mock.call(StrContains('Testing complete.'))
+ assert kall != mock.call(StrContains(' 0 tests run'))
+
+ def test_run_timeout(self):
+ timeout = 3453
+ kunit.main(['run', '--timeout', str(timeout)], self.linux_source_mock)
+ assert self.linux_source_mock.build_reconfig.call_count == 1
+ self.linux_source_mock.run_kernel.assert_called_once_with(timeout=timeout)
+ self.print_mock.assert_any_call(StrContains('Testing complete.'))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-all_passed.log b/tools/testing/kunit/test_data/test_is_test_passed-all_passed.log
new file mode 100644
index 0000000000000..62ebc0288355c
--- /dev/null
+++ b/tools/testing/kunit/test_data/test_is_test_passed-all_passed.log
@@ -0,0 +1,32 @@
+TAP version 14
+ # Subtest: sysctl_test
+ 1..8
+ # sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed
+ ok 1 - sysctl_test_dointvec_null_tbl_data
+ # sysctl_test_dointvec_table_maxlen_unset: sysctl_test_dointvec_table_maxlen_unset passed
+ ok 2 - sysctl_test_dointvec_table_maxlen_unset
+ # sysctl_test_dointvec_table_len_is_zero: sysctl_test_dointvec_table_len_is_zero passed
+ ok 3 - sysctl_test_dointvec_table_len_is_zero
+ # sysctl_test_dointvec_table_read_but_position_set: sysctl_test_dointvec_table_read_but_position_set passed
+ ok 4 - sysctl_test_dointvec_table_read_but_position_set
+ # sysctl_test_dointvec_happy_single_positive: sysctl_test_dointvec_happy_single_positive passed
+ ok 5 - sysctl_test_dointvec_happy_single_positive
+ # sysctl_test_dointvec_happy_single_negative: sysctl_test_dointvec_happy_single_negative passed
+ ok 6 - sysctl_test_dointvec_happy_single_negative
+ # sysctl_test_dointvec_single_less_int_min: sysctl_test_dointvec_single_less_int_min passed
+ ok 7 - sysctl_test_dointvec_single_less_int_min
+ # sysctl_test_dointvec_single_greater_int_max: sysctl_test_dointvec_single_greater_int_max passed
+ ok 8 - sysctl_test_dointvec_single_greater_int_max
+kunit sysctl_test: all tests passed
+ok 1 - sysctl_test
+ # Subtest: example
+ 1..2
+init_suite
+ # example_simple_test: initializing
+ # example_simple_test: example_simple_test passed
+ ok 1 - example_simple_test
+ # example_mock_test: initializing
+ # example_mock_test: example_mock_test passed
+ ok 2 - example_mock_test
+kunit example: all tests passed
+ok 2 - example
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-crash.log b/tools/testing/kunit/test_data/test_is_test_passed-crash.log
new file mode 100644
index 0000000000000..0b249870c8be4
--- /dev/null
+++ b/tools/testing/kunit/test_data/test_is_test_passed-crash.log
@@ -0,0 +1,69 @@
+printk: console [tty0] enabled
+printk: console [mc-1] enabled
+TAP version 14
+ # Subtest: sysctl_test
+ 1..8
+ # sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed
+ ok 1 - sysctl_test_dointvec_null_tbl_data
+ # sysctl_test_dointvec_table_maxlen_unset: sysctl_test_dointvec_table_maxlen_unset passed
+ ok 2 - sysctl_test_dointvec_table_maxlen_unset
+ # sysctl_test_dointvec_table_len_is_zero: sysctl_test_dointvec_table_len_is_zero passed
+ ok 3 - sysctl_test_dointvec_table_len_is_zero
+ # sysctl_test_dointvec_table_read_but_position_set: sysctl_test_dointvec_table_read_but_position_set passed
+ ok 4 - sysctl_test_dointvec_table_read_but_position_set
+ # sysctl_test_dointvec_happy_single_positive: sysctl_test_dointvec_happy_single_positive passed
+ ok 5 - sysctl_test_dointvec_happy_single_positive
+ # sysctl_test_dointvec_happy_single_negative: sysctl_test_dointvec_happy_single_negative passed
+ ok 6 - sysctl_test_dointvec_happy_single_negative
+ # sysctl_test_dointvec_single_less_int_min: sysctl_test_dointvec_single_less_int_min passed
+ ok 7 - sysctl_test_dointvec_single_less_int_min
+ # sysctl_test_dointvec_single_greater_int_max: sysctl_test_dointvec_single_greater_int_max passed
+ ok 8 - sysctl_test_dointvec_single_greater_int_max
+kunit sysctl_test: all tests passed
+ok 1 - sysctl_test
+ # Subtest: example
+ 1..2
+init_suite
+ # example_simple_test: initializing
+Stack:
+ 6016f7db 6f81bd30 6f81bdd0 60021450
+ 6024b0e8 60021440 60018bbe 16f81bdc0
+ 00000001 6f81bd30 6f81bd20 6f81bdd0
+Call Trace:
+ [<6016f7db>] ? kunit_try_run_case+0xab/0xf0
+ [<60021450>] ? set_signals+0x0/0x60
+ [<60021440>] ? get_signals+0x0/0x10
+ [<60018bbe>] ? kunit_um_run_try_catch+0x5e/0xc0
+ [<60021450>] ? set_signals+0x0/0x60
+ [<60021440>] ? get_signals+0x0/0x10
+ [<60018bb3>] ? kunit_um_run_try_catch+0x53/0xc0
+ [<6016f321>] ? kunit_run_case_catch_errors+0x121/0x1a0
+ [<60018b60>] ? kunit_um_run_try_catch+0x0/0xc0
+ [<600189e0>] ? kunit_um_throw+0x0/0x180
+ [<6016f730>] ? kunit_try_run_case+0x0/0xf0
+ [<6016f600>] ? kunit_catch_run_case+0x0/0x130
+ [<6016edd0>] ? kunit_vprintk+0x0/0x30
+ [<6016ece0>] ? kunit_fail+0x0/0x40
+ [<6016eca0>] ? kunit_abort+0x0/0x40
+ [<6016ed20>] ? kunit_printk_emit+0x0/0xb0
+ [<6016f200>] ? kunit_run_case_catch_errors+0x0/0x1a0
+ [<6016f46e>] ? kunit_run_tests+0xce/0x260
+ [<6005b390>] ? unregister_console+0x0/0x190
+ [<60175b70>] ? suite_kunit_initexample_test_suite+0x0/0x20
+ [<60001cbb>] ? do_one_initcall+0x0/0x197
+ [<60001d47>] ? do_one_initcall+0x8c/0x197
+ [<6005cd20>] ? irq_to_desc+0x0/0x30
+ [<60002005>] ? kernel_init_freeable+0x1b3/0x272
+ [<6005c5ec>] ? printk+0x0/0x9b
+ [<601c0086>] ? kernel_init+0x26/0x160
+ [<60014442>] ? new_thread_handler+0x82/0xc0
+
+ # example_simple_test: kunit test case crashed!
+ # example_simple_test: example_simple_test failed
+ not ok 1 - example_simple_test
+ # example_mock_test: initializing
+ # example_mock_test: example_mock_test passed
+ ok 2 - example_mock_test
+kunit example: one or more tests failed
+not ok 2 - example
+List of all partitions:
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-failure.log b/tools/testing/kunit/test_data/test_is_test_passed-failure.log
new file mode 100644
index 0000000000000..2ed870b6de72f
--- /dev/null
+++ b/tools/testing/kunit/test_data/test_is_test_passed-failure.log
@@ -0,0 +1,36 @@
+TAP version 14
+ # Subtest: sysctl_test
+ 1..8
+ # sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed
+ ok 1 - sysctl_test_dointvec_null_tbl_data
+ # sysctl_test_dointvec_table_maxlen_unset: sysctl_test_dointvec_table_maxlen_unset passed
+ ok 2 - sysctl_test_dointvec_table_maxlen_unset
+ # sysctl_test_dointvec_table_len_is_zero: sysctl_test_dointvec_table_len_is_zero passed
+ ok 3 - sysctl_test_dointvec_table_len_is_zero
+ # sysctl_test_dointvec_table_read_but_position_set: sysctl_test_dointvec_table_read_but_position_set passed
+ ok 4 - sysctl_test_dointvec_table_read_but_position_set
+ # sysctl_test_dointvec_happy_single_positive: sysctl_test_dointvec_happy_single_positive passed
+ ok 5 - sysctl_test_dointvec_happy_single_positive
+ # sysctl_test_dointvec_happy_single_negative: sysctl_test_dointvec_happy_single_negative passed
+ ok 6 - sysctl_test_dointvec_happy_single_negative
+ # sysctl_test_dointvec_single_less_int_min: sysctl_test_dointvec_single_less_int_min passed
+ ok 7 - sysctl_test_dointvec_single_less_int_min
+ # sysctl_test_dointvec_single_greater_int_max: sysctl_test_dointvec_single_greater_int_max passed
+ ok 8 - sysctl_test_dointvec_single_greater_int_max
+kunit sysctl_test: all tests passed
+ok 1 - sysctl_test
+ # Subtest: example
+ 1..2
+init_suite
+ # example_simple_test: initializing
+ # example_simple_test: EXPECTATION FAILED at kunit/example-test.c:30
+ Expected 1 + 1 == 3, but
+ 1 + 1 == 2
+ 3 == 3
+ # example_simple_test: example_simple_test failed
+ not ok 1 - example_simple_test
+ # example_mock_test: initializing
+ # example_mock_test: example_mock_test passed
+ ok 2 - example_mock_test
+kunit example: one or more tests failed
+not ok 2 - example
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-no_tests_run.log b/tools/testing/kunit/test_data/test_is_test_passed-no_tests_run.log
new file mode 100644
index 0000000000000..ba69f5c94b75f
--- /dev/null
+++ b/tools/testing/kunit/test_data/test_is_test_passed-no_tests_run.log
@@ -0,0 +1,75 @@
+Core dump limits :
+ soft - 0
+ hard - NONE
+Checking environment variables for a tempdir...none found
+Checking if /dev/shm is on tmpfs...OK
+Checking PROT_EXEC mmap in /dev/shm...OK
+Adding 24743936 bytes to physical memory to account for exec-shield gap
+Linux version 4.12.0-rc3-00010-g7319eb35f493-dirty (brendanhiggins@mactruck.svl.corp.google.com) (gcc version 7.3.0 (Debian 7.3.0-5) ) #29 Thu Mar 15 14:57:19 PDT 2018
+Built 1 zonelists in Zone order, mobility grouping on. Total pages: 14038
+Kernel command line: root=98:0
+PID hash table entries: 256 (order: -1, 2048 bytes)
+Dentry cache hash table entries: 8192 (order: 4, 65536 bytes)
+Inode-cache hash table entries: 4096 (order: 3, 32768 bytes)
+Memory: 27868K/56932K available (1681K kernel code, 480K rwdata, 400K rodata, 89K init, 205K bss, 29064K reserved, 0K cma-reserved)
+SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
+NR_IRQS:15
+clocksource: timer: mask: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
+Calibrating delay loop... 7384.26 BogoMIPS (lpj=36921344)
+pid_max: default: 32768 minimum: 301
+Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
+Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes)
+Checking that host ptys support output SIGIO...Yes
+Checking that host ptys support SIGIO on close...No, enabling workaround
+Using 2.6 host AIO
+clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
+futex hash table entries: 256 (order: 0, 6144 bytes)
+clocksource: Switched to clocksource timer
+console [stderr0] disabled
+mconsole (version 2) initialized on /usr/local/google/home/brendanhiggins/.uml/6Ijecl/mconsole
+Checking host MADV_REMOVE support...OK
+workingset: timestamp_bits=62 max_order=13 bucket_order=0
+Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
+io scheduler noop registered
+io scheduler deadline registered
+io scheduler cfq registered (default)
+io scheduler mq-deadline registered
+io scheduler kyber registered
+Initialized stdio console driver
+Using a channel type which is configured out of UML
+setup_one_line failed for device 1 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 2 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 3 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 4 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 5 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 6 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 7 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 8 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 9 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 10 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 11 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 12 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 13 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 14 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 15 : Configuration failed
+Console initialized on /dev/tty0
+console [tty0] enabled
+console [mc-1] enabled
+List of all partitions:
+No filesystem could mount root, tried:
+
+Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(98,0)
diff --git a/tools/testing/kunit/test_data/test_output_isolated_correctly.log b/tools/testing/kunit/test_data/test_output_isolated_correctly.log
new file mode 100644
index 0000000000000..94a6b3aeaa922
--- /dev/null
+++ b/tools/testing/kunit/test_data/test_output_isolated_correctly.log
@@ -0,0 +1,106 @@
+Linux version 5.1.0-rc7-00061-g04652f1cb4aa0 (brendanhiggins@mactruck.svl.corp.google.com) (gcc version 7.3.0 (Debian 7.3.0-18)) #163 Wed May 8 16:18:20 PDT 2019
+Built 1 zonelists, mobility grouping on. Total pages: 69906
+Kernel command line: mem=256M root=98:0
+Dentry cache hash table entries: 65536 (order: 7, 524288 bytes)
+Inode-cache hash table entries: 32768 (order: 6, 262144 bytes)
+Memory: 254468K/283500K available (1734K kernel code, 489K rwdata, 396K rodata, 85K init, 216K bss, 29032K reserved, 0K cma-reserved)
+SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
+NR_IRQS: 15
+clocksource: timer: mask: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
+------------[ cut here ]------------
+WARNING: CPU: 0 PID: 0 at kernel/time/clockevents.c:458 clockevents_register_device+0x143/0x160
+posix-timer cpumask == cpu_all_mask, using cpu_possible_mask instead
+CPU: 0 PID: 0 Comm: swapper Not tainted 5.1.0-rc7-00061-g04652f1cb4aa0 #163
+Stack:
+ 6005cc00 60233e18 60233e60 60233e18
+ 60233e60 00000009 00000000 6002a1b4
+ 1ca00000000 60071c23 60233e78 100000000000062
+Call Trace:
+ [<600214c5>] ? os_is_signal_stack+0x15/0x30
+ [<6005c5ec>] ? printk+0x0/0x9b
+ [<6001597e>] ? show_stack+0xbe/0x1c0
+ [<6005cc00>] ? __printk_safe_exit+0x0/0x40
+ [<6002a1b4>] ? __warn+0x144/0x170
+ [<60071c23>] ? clockevents_register_device+0x143/0x160
+ [<60021440>] ? get_signals+0x0/0x10
+ [<6005c5ec>] ? printk+0x0/0x9b
+ [<6002a27b>] ? warn_slowpath_fmt+0x9b/0xb0
+ [<6005c5ec>] ? printk+0x0/0x9b
+ [<6002a1e0>] ? warn_slowpath_fmt+0x0/0xb0
+ [<6005c5ec>] ? printk+0x0/0x9b
+ [<60021440>] ? get_signals+0x0/0x10
+ [<600213f0>] ? block_signals+0x0/0x20
+ [<60071c23>] ? clockevents_register_device+0x143/0x160
+ [<60021440>] ? get_signals+0x0/0x10
+ [<600213f0>] ? block_signals+0x0/0x20
+ [<6005c5ec>] ? printk+0x0/0x9b
+ [<60001bc8>] ? start_kernel+0x477/0x56a
+ [<600036f1>] ? start_kernel_proc+0x46/0x4d
+ [<60014442>] ? new_thread_handler+0x82/0xc0
+
+random: get_random_bytes called from print_oops_end_marker+0x4c/0x60 with crng_init=0
+---[ end trace c83434852b3702d3 ]---
+Calibrating delay loop... 6958.28 BogoMIPS (lpj=34791424)
+pid_max: default: 32768 minimum: 301
+Mount-cache hash table entries: 1024 (order: 1, 8192 bytes)
+Mountpoint-cache hash table entries: 1024 (order: 1, 8192 bytes)
+*** VALIDATE proc ***
+Checking that host ptys support output SIGIO...Yes
+Checking that host ptys support SIGIO on close...No, enabling workaround
+clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
+futex hash table entries: 256 (order: 0, 6144 bytes)
+clocksource: Switched to clocksource timer
+printk: console [stderr0] disabled
+mconsole (version 2) initialized on /usr/local/google/home/brendanhiggins/.uml/VZ2qMm/mconsole
+Checking host MADV_REMOVE support...OK
+workingset: timestamp_bits=62 max_order=16 bucket_order=0
+Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
+io scheduler mq-deadline registered
+io scheduler kyber registered
+Initialized stdio console driver
+Using a channel type which is configured out of UML
+setup_one_line failed for device 1 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 2 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 3 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 4 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 5 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 6 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 7 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 8 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 9 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 10 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 11 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 12 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 13 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 14 : Configuration failed
+Using a channel type which is configured out of UML
+setup_one_line failed for device 15 : Configuration failed
+Console initialized on /dev/tty0
+printk: console [tty0] enabled
+printk: console [mc-1] enabled
+TAP version 14
+ # Subtest: example
+ 1..2
+init_suite
+ # example_simple_test: initializing
+ # example_simple_test: example_simple_test passed
+ ok 1 - example_simple_test
+ # example_mock_test: initializing
+ # example_mock_test: example_mock_test passed
+ ok 2 - example_mock_test
+kunit example: all tests passed
+ok 1 - example
+List of all partitions:
diff --git a/tools/testing/kunit/test_data/test_read_from_file.kconfig b/tools/testing/kunit/test_data/test_read_from_file.kconfig
new file mode 100644
index 0000000000000..d2a4928ac773b
--- /dev/null
+++ b/tools/testing/kunit/test_data/test_read_from_file.kconfig
@@ -0,0 +1,17 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# User Mode Linux/x86 4.12.0-rc3 Kernel Configuration
+#
+CONFIG_UML=y
+CONFIG_MMU=y
+
+#
+# UML-specific options
+#
+
+#
+# Host processor type and features
+#
+# CONFIG_MK8 is not set
+CONFIG_TEST=y
+CONFIG_EXAMPLE_TEST=y
--
2.22.0.510.g264f2c817a-goog
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related
* [PATCH v10 14/18] kunit: defconfig: add defconfigs for building KUnit tests
From: Brendan Higgins @ 2019-07-16 9:42 UTC (permalink / raw)
To: frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
jpoimboe-H+wXaHxf7aLQT0dZR+AlfA, keescook-hpIqsD4AKlfQT0dZR+AlfA,
kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
mcgrof-DgEjT+Ai2ygdnm+yROfE0A, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
robh-DgEjT+Ai2ygdnm+yROfE0A, sboyd-DgEjT+Ai2ygdnm+yROfE0A,
shuah-DgEjT+Ai2ygdnm+yROfE0A, tytso-3s7WtUTddSA,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A
Cc: pmladek-IBi9RG/b67k, linux-doc-u79uwXL29TY76Z2rM5mHXA,
amir73il-Re5JQEeQqe8AvxtiuMwx3w, Brendan Higgins,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
Alexander.Levin-0li6OtcxBFHby3iVrkZq2A,
linux-kselftest-u79uwXL29TY76Z2rM5mHXA,
linux-nvdimm-hn68Rpc1hR1g9hUCZPvPmw,
khilman-rdvid1DuHRBWk0Htik3J/w, knut.omang-QHcLZuEGTsvQT0dZR+AlfA,
wfg-VuQAYsv1563Yd54FQh9/CA, joel-U3u1mxZcP9KHXe+LvDLADg,
rientjes-hpIqsD4AKlfQT0dZR+AlfA, jdike-OPE4K8JWMJJBDgjK7y7TUQ,
dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kbuild-u79uwXL29TY76Z2rM5mHXA, Tim.Bird-7U/KSKJipcs,
linux-um-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
rostedt-nx8X9YLhiw1AfugRpC6u6w, julia.lawall-L2FTfq7BK8M,
kunit-dev-/JYPxA39Uh5TLH3MbocFFw, richard-/L3Ra7n9ekc,
rdunlap-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
mpe-Gsx/Oe8HsFggBc27wqDAHg, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20190716094302.180360-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Add defconfig for UML and a fragment that can be used to configure other
architectures for building KUnit tests. Add option to kunit_tool to use
a defconfig to create the kunitconfig.
Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Reviewed-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Reviewed-by: Logan Gunthorpe <logang-OTvnGxWRz7hWk0Htik3J/w@public.gmane.org>
---
arch/um/configs/kunit_defconfig | 8 ++++++++
tools/testing/kunit/configs/all_tests.config | 8 ++++++++
tools/testing/kunit/kunit.py | 18 ++++++++++++++++--
tools/testing/kunit/kunit_kernel.py | 3 ++-
4 files changed, 34 insertions(+), 3 deletions(-)
create mode 100644 arch/um/configs/kunit_defconfig
create mode 100644 tools/testing/kunit/configs/all_tests.config
diff --git a/arch/um/configs/kunit_defconfig b/arch/um/configs/kunit_defconfig
new file mode 100644
index 0000000000000..bfe49689038f1
--- /dev/null
+++ b/arch/um/configs/kunit_defconfig
@@ -0,0 +1,8 @@
+CONFIG_OF=y
+CONFIG_OF_UNITTEST=y
+CONFIG_OF_OVERLAY=y
+CONFIG_I2C=y
+CONFIG_I2C_MUX=y
+CONFIG_KUNIT=y
+CONFIG_KUNIT_TEST=y
+CONFIG_KUNIT_EXAMPLE_TEST=y
diff --git a/tools/testing/kunit/configs/all_tests.config b/tools/testing/kunit/configs/all_tests.config
new file mode 100644
index 0000000000000..bfe49689038f1
--- /dev/null
+++ b/tools/testing/kunit/configs/all_tests.config
@@ -0,0 +1,8 @@
+CONFIG_OF=y
+CONFIG_OF_UNITTEST=y
+CONFIG_OF_OVERLAY=y
+CONFIG_I2C=y
+CONFIG_I2C_MUX=y
+CONFIG_KUNIT=y
+CONFIG_KUNIT_TEST=y
+CONFIG_KUNIT_EXAMPLE_TEST=y
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
index da11bd62a4b82..3e51dc4febfdc 100755
--- a/tools/testing/kunit/kunit.py
+++ b/tools/testing/kunit/kunit.py
@@ -11,6 +11,7 @@ import argparse
import sys
import os
import time
+import shutil
from collections import namedtuple
from enum import Enum, auto
@@ -21,7 +22,7 @@ import kunit_parser
KunitResult = namedtuple('KunitResult', ['status','result'])
-KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 'build_dir'])
+KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 'build_dir', 'defconfig'])
class KunitStatus(Enum):
SUCCESS = auto()
@@ -29,8 +30,16 @@ class KunitStatus(Enum):
BUILD_FAILURE = auto()
TEST_FAILURE = auto()
+def create_default_kunitconfig():
+ if not os.path.exists(kunit_kernel.KUNITCONFIG_PATH):
+ shutil.copyfile('arch/um/configs/kunit_defconfig',
+ kunit_kernel.KUNITCONFIG_PATH)
+
def run_tests(linux: kunit_kernel.LinuxSourceTree,
request: KunitRequest) -> KunitResult:
+ if request.defconfig:
+ create_default_kunitconfig()
+
config_start = time.time()
success = linux.build_reconfig(request.build_dir)
config_end = time.time()
@@ -99,13 +108,18 @@ def main(argv, linux):
'directory.',
type=str, default=None, metavar='build_dir')
+ run_parser.add_argument('--defconfig',
+ help='Uses a default kunitconfig.',
+ action='store_true')
+
cli_args = parser.parse_args(argv)
if cli_args.subcommand == 'run':
request = KunitRequest(cli_args.raw_output,
cli_args.timeout,
cli_args.jobs,
- cli_args.build_dir)
+ cli_args.build_dir,
+ cli_args.defconfig)
result = run_tests(linux, request)
if result.status != KunitStatus.SUCCESS:
sys.exit(1)
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
index 07c0abf2f47df..bf38768353313 100644
--- a/tools/testing/kunit/kunit_kernel.py
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -14,6 +14,7 @@ import os
import kunit_config
KCONFIG_PATH = '.config'
+KUNITCONFIG_PATH = 'kunitconfig'
class ConfigError(Exception):
"""Represents an error trying to configure the Linux kernel."""
@@ -81,7 +82,7 @@ class LinuxSourceTree(object):
def __init__(self):
self._kconfig = kunit_config.Kconfig()
- self._kconfig.read_from_file('kunitconfig')
+ self._kconfig.read_from_file(KUNITCONFIG_PATH)
self._ops = LinuxSourceTreeOperations()
def clean(self):
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v10 15/18] Documentation: kunit: add documentation for KUnit
From: Brendan Higgins @ 2019-07-16 9:42 UTC (permalink / raw)
To: frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
jpoimboe-H+wXaHxf7aLQT0dZR+AlfA, keescook-hpIqsD4AKlfQT0dZR+AlfA,
kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
mcgrof-DgEjT+Ai2ygdnm+yROfE0A, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
robh-DgEjT+Ai2ygdnm+yROfE0A, sboyd-DgEjT+Ai2ygdnm+yROfE0A,
shuah-DgEjT+Ai2ygdnm+yROfE0A, tytso-3s7WtUTddSA,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A
Cc: pmladek-IBi9RG/b67k, linux-doc-u79uwXL29TY76Z2rM5mHXA,
amir73il-Re5JQEeQqe8AvxtiuMwx3w, Brendan Higgins,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
Alexander.Levin-0li6OtcxBFHby3iVrkZq2A,
linux-kselftest-u79uwXL29TY76Z2rM5mHXA, Jonathan Corbet,
linux-nvdimm-hn68Rpc1hR1g9hUCZPvPmw,
khilman-rdvid1DuHRBWk0Htik3J/w, knut.omang-QHcLZuEGTsvQT0dZR+AlfA,
Felix Guo, wfg-VuQAYsv1563Yd54FQh9/CA,
joel-U3u1mxZcP9KHXe+LvDLADg, rientjes-hpIqsD4AKlfQT0dZR+AlfA,
jdike-OPE4K8JWMJJBDgjK7y7TUQ,
dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kbuild-u79uwXL29TY76Z2rM5mHXA, Tim.Bird-7U/KSKJipcs,
linux-um-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
rostedt-nx8X9YLhiw1AfugRpC6u6w, julia.lawall-L2FTfq7BK8M,
kunit-dev-/JYPxA39Uh5TLH3MbocFFw, richard-/L3Ra7n9ekc,
rdunlap-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
mpe-Gsx/Oe8HsFggBc27wqDAHg, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20190716094302.180360-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Add documentation for KUnit, the Linux kernel unit testing framework.
- Add intro and usage guide for KUnit
- Add API reference
Signed-off-by: Felix Guo <felixguoxiuping-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Cc: Jonathan Corbet <corbet-T1hC0tSOHrs@public.gmane.org>
Reviewed-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Reviewed-by: Logan Gunthorpe <logang-OTvnGxWRz7hWk0Htik3J/w@public.gmane.org>
---
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kunit/api/index.rst | 16 +
Documentation/dev-tools/kunit/api/test.rst | 14 +
Documentation/dev-tools/kunit/faq.rst | 62 +++
Documentation/dev-tools/kunit/index.rst | 79 +++
Documentation/dev-tools/kunit/start.rst | 180 ++++++
Documentation/dev-tools/kunit/usage.rst | 575 ++++++++++++++++++++
7 files changed, 927 insertions(+)
create mode 100644 Documentation/dev-tools/kunit/api/index.rst
create mode 100644 Documentation/dev-tools/kunit/api/test.rst
create mode 100644 Documentation/dev-tools/kunit/faq.rst
create mode 100644 Documentation/dev-tools/kunit/index.rst
create mode 100644 Documentation/dev-tools/kunit/start.rst
create mode 100644 Documentation/dev-tools/kunit/usage.rst
diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index b0522a4dd1073..09dee10d25928 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -24,6 +24,7 @@ whole; patches welcome!
gdb-kernel-debugging
kgdb
kselftest
+ kunit/index
.. only:: subproject and html
diff --git a/Documentation/dev-tools/kunit/api/index.rst b/Documentation/dev-tools/kunit/api/index.rst
new file mode 100644
index 0000000000000..9b9bffe5d41a0
--- /dev/null
+++ b/Documentation/dev-tools/kunit/api/index.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============
+API Reference
+=============
+.. toctree::
+
+ test
+
+This section documents the KUnit kernel testing API. It is divided into the
+following sections:
+
+================================= ==============================================
+:doc:`test` documents all of the standard testing API
+ excluding mocking or mocking related features.
+================================= ==============================================
diff --git a/Documentation/dev-tools/kunit/api/test.rst b/Documentation/dev-tools/kunit/api/test.rst
new file mode 100644
index 0000000000000..d0ce19b1e1185
--- /dev/null
+++ b/Documentation/dev-tools/kunit/api/test.rst
@@ -0,0 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========
+Test API
+========
+
+This file documents all of the standard testing API excluding mocking or mocking
+related features.
+
+.. kernel-doc:: include/kunit/test.h
+ :internal:
+
+.. kernel-doc:: include/kunit/kunit-stream.h
+ :internal:
diff --git a/Documentation/dev-tools/kunit/faq.rst b/Documentation/dev-tools/kunit/faq.rst
new file mode 100644
index 0000000000000..bf2095112d899
--- /dev/null
+++ b/Documentation/dev-tools/kunit/faq.rst
@@ -0,0 +1,62 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
+Frequently Asked Questions
+==========================
+
+How is this different from Autotest, kselftest, etc?
+====================================================
+KUnit is a unit testing framework. Autotest, kselftest (and some others) are
+not.
+
+A `unit test <https://martinfowler.com/bliki/UnitTest.html>`_ is supposed to
+test a single unit of code in isolation, hence the name. A unit test should be
+the finest granularity of testing and as such should allow all possible code
+paths to be tested in the code under test; this is only possible if the code
+under test is very small and does not have any external dependencies outside of
+the test's control like hardware.
+
+There are no testing frameworks currently available for the kernel that do not
+require installing the kernel on a test machine or in a VM and all require
+tests to be written in userspace and run on the kernel under test; this is true
+for Autotest, kselftest, and some others, disqualifying any of them from being
+considered unit testing frameworks.
+
+Does KUnit support running on architectures other than UML?
+===========================================================
+
+Yes, well, mostly.
+
+For the most part, the KUnit core framework (what you use to write the tests)
+can compile to any architecture; it compiles like just another part of the
+kernel and runs when the kernel boots. However, there is some infrastructure,
+like the KUnit Wrapper (``tools/testing/kunit/kunit.py``) that does not support
+other architectures.
+
+In short, this means that, yes, you can run KUnit on other architectures, but
+it might require more work than using KUnit on UML.
+
+For more information, see :ref:`kunit-on-non-uml`.
+
+What is the difference between a unit test and these other kinds of tests?
+==========================================================================
+Most existing tests for the Linux kernel would be categorized as an integration
+test, or an end-to-end test.
+
+- A unit test is supposed to test a single unit of code in isolation, hence the
+ name. A unit test should be the finest granularity of testing and as such
+ should allow all possible code paths to be tested in the code under test; this
+ is only possible if the code under test is very small and does not have any
+ external dependencies outside of the test's control like hardware.
+- An integration test tests the interaction between a minimal set of components,
+ usually just two or three. For example, someone might write an integration
+ test to test the interaction between a driver and a piece of hardware, or to
+ test the interaction between the userspace libraries the kernel provides and
+ the kernel itself; however, one of these tests would probably not test the
+ entire kernel along with hardware interactions and interactions with the
+ userspace.
+- An end-to-end test usually tests the entire system from the perspective of the
+ code under test. For example, someone might write an end-to-end test for the
+ kernel by installing a production configuration of the kernel on production
+ hardware with a production userspace and then trying to exercise some behavior
+ that depends on interactions between the hardware, the kernel, and userspace.
diff --git a/Documentation/dev-tools/kunit/index.rst b/Documentation/dev-tools/kunit/index.rst
new file mode 100644
index 0000000000000..a317ab45bfe2d
--- /dev/null
+++ b/Documentation/dev-tools/kunit/index.rst
@@ -0,0 +1,79 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================
+KUnit - Unit Testing for the Linux Kernel
+=========================================
+
+.. toctree::
+ :maxdepth: 2
+
+ start
+ usage
+ api/index
+ faq
+
+What is KUnit?
+==============
+
+KUnit is a lightweight unit testing and mocking framework for the Linux kernel.
+These tests are able to be run locally on a developer's workstation without a VM
+or special hardware.
+
+KUnit is heavily inspired by JUnit, Python's unittest.mock, and
+Googletest/Googlemock for C++. KUnit provides facilities for defining unit test
+cases, grouping related test cases into test suites, providing common
+infrastructure for running tests, and much more.
+
+Get started now: :doc:`start`
+
+Why KUnit?
+==========
+
+A unit test is supposed to test a single unit of code in isolation, hence the
+name. A unit test should be the finest granularity of testing and as such should
+allow all possible code paths to be tested in the code under test; this is only
+possible if the code under test is very small and does not have any external
+dependencies outside of the test's control like hardware.
+
+Outside of KUnit, there are no testing frameworks currently
+available for the kernel that do not require installing the kernel on a test
+machine or in a VM and all require tests to be written in userspace running on
+the kernel; this is true for Autotest, and kselftest, disqualifying
+any of them from being considered unit testing frameworks.
+
+KUnit addresses the problem of being able to run tests without needing a virtual
+machine or actual hardware with User Mode Linux. User Mode Linux is a Linux
+architecture, like ARM or x86; however, unlike other architectures it compiles
+to a standalone program that can be run like any other program directly inside
+of a host operating system; to be clear, it does not require any virtualization
+support; it is just a regular program.
+
+KUnit is fast. Excluding build time, from invocation to completion KUnit can run
+several dozen tests in only 10 to 20 seconds; this might not sound like a big
+deal to some people, but having such fast and easy to run tests fundamentally
+changes the way you go about testing and even writing code in the first place.
+Linus himself said in his `git talk at Google
+<https://gist.github.com/lorn/1272686/revisions#diff-53c65572127855f1b003db4064a94573R874>`_:
+
+ "... a lot of people seem to think that performance is about doing the
+ same thing, just doing it faster, and that is not true. That is not what
+ performance is all about. If you can do something really fast, really
+ well, people will start using it differently."
+
+In this context Linus was talking about branching and merging,
+but this point also applies to testing. If your tests are slow, unreliable, are
+difficult to write, and require a special setup or special hardware to run,
+then you wait a lot longer to write tests, and you wait a lot longer to run
+tests; this means that tests are likely to break, unlikely to test a lot of
+things, and are unlikely to be rerun once they pass. If your tests are really
+fast, you run them all the time, every time you make a change, and every time
+someone sends you some code. Why trust that someone ran all their tests
+correctly on every change when you can just run them yourself in less time than
+it takes to read his / her test log?
+
+How do I use it?
+================
+
+* :doc:`start` - for new users of KUnit
+* :doc:`usage` - for a more detailed explanation of KUnit features
+* :doc:`api/index` - for the list of KUnit APIs used for testing
diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst
new file mode 100644
index 0000000000000..6dc229e46bb34
--- /dev/null
+++ b/Documentation/dev-tools/kunit/start.rst
@@ -0,0 +1,180 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+Getting Started
+===============
+
+Installing dependencies
+=======================
+KUnit has the same dependencies as the Linux kernel. As long as you can build
+the kernel, you can run KUnit.
+
+KUnit Wrapper
+=============
+Included with KUnit is a simple Python wrapper that helps format the output to
+easily use and read KUnit output. It handles building and running the kernel, as
+well as formatting the output.
+
+The wrapper can be run with:
+
+.. code-block:: bash
+
+ ./tools/testing/kunit/kunit.py run
+
+Creating a kunitconfig
+======================
+The Python script is a thin wrapper around Kbuild as such, it needs to be
+configured with a ``kunitconfig`` file. This file essentially contains the
+regular Kernel config, with the specific test targets as well.
+
+.. code-block:: bash
+
+ git clone -b master https://kunit.googlesource.com/kunitconfig $PATH_TO_KUNITCONFIG_REPO
+ cd $PATH_TO_LINUX_REPO
+ ln -s $PATH_TO_KUNIT_CONFIG_REPO/kunitconfig kunitconfig
+
+You may want to add kunitconfig to your local gitignore.
+
+Verifying KUnit Works
+---------------------
+
+To make sure that everything is set up correctly, simply invoke the Python
+wrapper from your kernel repo:
+
+.. code-block:: bash
+
+ ./tools/testing/kunit/kunit.py
+
+.. note::
+ You may want to run ``make mrproper`` first.
+
+If everything worked correctly, you should see the following:
+
+.. code-block:: bash
+
+ Generating .config ...
+ Building KUnit Kernel ...
+ Starting KUnit Kernel ...
+
+followed by a list of tests that are run. All of them should be passing.
+
+.. note::
+ Because it is building a lot of sources for the first time, the ``Building
+ kunit kernel`` step may take a while.
+
+Writing your first test
+=======================
+
+In your kernel repo let's add some code that we can test. Create a file
+``drivers/misc/example.h`` with the contents:
+
+.. code-block:: c
+
+ int misc_example_add(int left, int right);
+
+create a file ``drivers/misc/example.c``:
+
+.. code-block:: c
+
+ #include <linux/errno.h>
+
+ #include "example.h"
+
+ int misc_example_add(int left, int right)
+ {
+ return left + right;
+ }
+
+Now add the following lines to ``drivers/misc/Kconfig``:
+
+.. code-block:: kconfig
+
+ config MISC_EXAMPLE
+ bool "My example"
+
+and the following lines to ``drivers/misc/Makefile``:
+
+.. code-block:: make
+
+ obj-$(CONFIG_MISC_EXAMPLE) += example.o
+
+Now we are ready to write the test. The test will be in
+``drivers/misc/example-test.c``:
+
+.. code-block:: c
+
+ #include <kunit/test.h>
+ #include "example.h"
+
+ /* Define the test cases. */
+
+ static void misc_example_add_test_basic(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, 1, misc_example_add(1, 0));
+ KUNIT_EXPECT_EQ(test, 2, misc_example_add(1, 1));
+ KUNIT_EXPECT_EQ(test, 0, misc_example_add(-1, 1));
+ KUNIT_EXPECT_EQ(test, INT_MAX, misc_example_add(0, INT_MAX));
+ KUNIT_EXPECT_EQ(test, -1, misc_example_add(INT_MAX, INT_MIN));
+ }
+
+ static void misc_example_test_failure(struct kunit *test)
+ {
+ KUNIT_FAIL(test, "This test never passes.");
+ }
+
+ static struct kunit_case misc_example_test_cases[] = {
+ KUNIT_CASE(misc_example_add_test_basic),
+ KUNIT_CASE(misc_example_test_failure),
+ {}
+ };
+
+ static struct kunit_suite misc_example_test_suite = {
+ .name = "misc-example",
+ .test_cases = misc_example_test_cases,
+ };
+ kunit_test_suite(misc_example_test_suite);
+
+Now add the following to ``drivers/misc/Kconfig``:
+
+.. code-block:: kconfig
+
+ config MISC_EXAMPLE_TEST
+ bool "Test for my example"
+ depends on MISC_EXAMPLE && KUNIT
+
+and the following to ``drivers/misc/Makefile``:
+
+.. code-block:: make
+
+ obj-$(CONFIG_MISC_EXAMPLE_TEST) += example-test.o
+
+Now add it to your ``kunitconfig``:
+
+.. code-block:: none
+
+ CONFIG_MISC_EXAMPLE=y
+ CONFIG_MISC_EXAMPLE_TEST=y
+
+Now you can run the test:
+
+.. code-block:: bash
+
+ ./tools/testing/kunit/kunit.py
+
+You should see the following failure:
+
+.. code-block:: none
+
+ ...
+ [16:08:57] [PASSED] misc-example:misc_example_add_test_basic
+ [16:08:57] [FAILED] misc-example:misc_example_test_failure
+ [16:08:57] EXPECTATION FAILED at drivers/misc/example-test.c:17
+ [16:08:57] This test never passes.
+ ...
+
+Congrats! You just wrote your first KUnit test!
+
+Next Steps
+==========
+* Check out the :doc:`usage` page for a more
+ in-depth explanation of KUnit.
diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
new file mode 100644
index 0000000000000..049886006dbce
--- /dev/null
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -0,0 +1,575 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+Using KUnit
+===========
+
+The purpose of this document is to describe what KUnit is, how it works, how it
+is intended to be used, and all the concepts and terminology that are needed to
+understand it. This guide assumes a working knowledge of the Linux kernel and
+some basic knowledge of testing.
+
+For a high level introduction to KUnit, including setting up KUnit for your
+project, see :doc:`start`.
+
+Organization of this document
+=============================
+
+This document is organized into two main sections: Testing and Isolating
+Behavior. The first covers what a unit test is and how to use KUnit to write
+them. The second covers how to use KUnit to isolate code and make it possible
+to unit test code that was otherwise un-unit-testable.
+
+Testing
+=======
+
+What is KUnit?
+--------------
+
+"K" is short for "kernel" so "KUnit" is the "(Linux) Kernel Unit Testing
+Framework." KUnit is intended first and foremost for writing unit tests; it is
+general enough that it can be used to write integration tests; however, this is
+a secondary goal. KUnit has no ambition of being the only testing framework for
+the kernel; for example, it does not intend to be an end-to-end testing
+framework.
+
+What is Unit Testing?
+---------------------
+
+A `unit test <https://martinfowler.com/bliki/UnitTest.html>`_ is a test that
+tests code at the smallest possible scope, a *unit* of code. In the C
+programming language that's a function.
+
+Unit tests should be written for all the publicly exposed functions in a
+compilation unit; so that is all the functions that are exported in either a
+*class* (defined below) or all functions which are **not** static.
+
+Writing Tests
+-------------
+
+Test Cases
+~~~~~~~~~~
+
+The fundamental unit in KUnit is the test case. A test case is a function with
+the signature ``void (*)(struct kunit *test)``. It calls a function to be tested
+and then sets *expectations* for what should happen. For example:
+
+.. code-block:: c
+
+ void example_test_success(struct kunit *test)
+ {
+ }
+
+ void example_test_failure(struct kunit *test)
+ {
+ KUNIT_FAIL(test, "This test never passes.");
+ }
+
+In the above example ``example_test_success`` always passes because it does
+nothing; no expectations are set, so all expectations pass. On the other hand
+``example_test_failure`` always fails because it calls ``KUNIT_FAIL``, which is
+a special expectation that logs a message and causes the test case to fail.
+
+Expectations
+~~~~~~~~~~~~
+An *expectation* is a way to specify that you expect a piece of code to do
+something in a test. An expectation is called like a function. A test is made
+by setting expectations about the behavior of a piece of code under test; when
+one or more of the expectations fail, the test case fails and information about
+the failure is logged. For example:
+
+.. code-block:: c
+
+ void add_test_basic(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, 1, add(1, 0));
+ KUNIT_EXPECT_EQ(test, 2, add(1, 1));
+ }
+
+In the above example ``add_test_basic`` makes a number of assertions about the
+behavior of a function called ``add``; the first parameter is always of type
+``struct kunit *``, which contains information about the current test context;
+the second parameter, in this case, is what the value is expected to be; the
+last value is what the value actually is. If ``add`` passes all of these
+expectations, the test case, ``add_test_basic`` will pass; if any one of these
+expectations fail, the test case will fail.
+
+It is important to understand that a test case *fails* when any expectation is
+violated; however, the test will continue running, potentially trying other
+expectations until the test case ends or is otherwise terminated. This is as
+opposed to *assertions* which are discussed later.
+
+To learn about more expectations supported by KUnit, see :doc:`api/test`.
+
+.. note::
+ A single test case should be pretty short, pretty easy to understand,
+ focused on a single behavior.
+
+For example, if we wanted to properly test the add function above, we would
+create additional tests cases which would each test a different property that an
+add function should have like this:
+
+.. code-block:: c
+
+ void add_test_basic(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, 1, add(1, 0));
+ KUNIT_EXPECT_EQ(test, 2, add(1, 1));
+ }
+
+ void add_test_negative(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, 0, add(-1, 1));
+ }
+
+ void add_test_max(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX));
+ KUNIT_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN));
+ }
+
+ void add_test_overflow(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, INT_MIN, add(INT_MAX, 1));
+ }
+
+Notice how it is immediately obvious what all the properties that we are testing
+for are.
+
+Assertions
+~~~~~~~~~~
+
+KUnit also has the concept of an *assertion*. An assertion is just like an
+expectation except the assertion immediately terminates the test case if it is
+not satisfied.
+
+For example:
+
+.. code-block:: c
+
+ static void mock_test_do_expect_default_return(struct kunit *test)
+ {
+ struct mock_test_context *ctx = test->priv;
+ struct mock *mock = ctx->mock;
+ int param0 = 5, param1 = -5;
+ const char *two_param_types[] = {"int", "int"};
+ const void *two_params[] = {¶m0, ¶m1};
+ const void *ret;
+
+ ret = mock->do_expect(mock,
+ "test_printk", test_printk,
+ two_param_types, two_params,
+ ARRAY_SIZE(two_params));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ret);
+ KUNIT_EXPECT_EQ(test, -4, *((int *) ret));
+ }
+
+In this example, the method under test should return a pointer to a value, so
+if the pointer returned by the method is null or an errno, we don't want to
+bother continuing the test since the following expectation could crash the test
+case. `ASSERT_NOT_ERR_OR_NULL(...)` allows us to bail out of the test case if
+the appropriate conditions have not been satisfied to complete the test.
+
+Test Suites
+~~~~~~~~~~~
+
+Now obviously one unit test isn't very helpful; the power comes from having
+many test cases covering all of your behaviors. Consequently it is common to
+have many *similar* tests; in order to reduce duplication in these closely
+related tests most unit testing frameworks provide the concept of a *test
+suite*, in KUnit we call it a *test suite*; all it is is just a collection of
+test cases for a unit of code with a set up function that gets invoked before
+every test cases and then a tear down function that gets invoked after every
+test case completes.
+
+Example:
+
+.. code-block:: c
+
+ static struct kunit_case example_test_cases[] = {
+ KUNIT_CASE(example_test_foo),
+ KUNIT_CASE(example_test_bar),
+ KUNIT_CASE(example_test_baz),
+ {}
+ };
+
+ static struct kunit_suite example_test_suite = {
+ .name = "example",
+ .init = example_test_init,
+ .exit = example_test_exit,
+ .test_cases = example_test_cases,
+ };
+ kunit_test_suite(example_test_suite);
+
+In the above example the test suite, ``example_test_suite``, would run the test
+cases ``example_test_foo``, ``example_test_bar``, and ``example_test_baz``,
+each would have ``example_test_init`` called immediately before it and would
+have ``example_test_exit`` called immediately after it.
+``kunit_test_suite(example_test_suite)`` registers the test suite with the
+KUnit test framework.
+
+.. note::
+ A test case will only be run if it is associated with a test suite.
+
+For a more information on these types of things see the :doc:`api/test`.
+
+Isolating Behavior
+==================
+
+The most important aspect of unit testing that other forms of testing do not
+provide is the ability to limit the amount of code under test to a single unit.
+In practice, this is only possible by being able to control what code gets run
+when the unit under test calls a function and this is usually accomplished
+through some sort of indirection where a function is exposed as part of an API
+such that the definition of that function can be changed without affecting the
+rest of the code base. In the kernel this primarily comes from two constructs,
+classes, structs that contain function pointers that are provided by the
+implementer, and architecture specific functions which have definitions selected
+at compile time.
+
+Classes
+-------
+
+Classes are not a construct that is built into the C programming language;
+however, it is an easily derived concept. Accordingly, pretty much every project
+that does not use a standardized object oriented library (like GNOME's GObject)
+has their own slightly different way of doing object oriented programming; the
+Linux kernel is no exception.
+
+The central concept in kernel object oriented programming is the class. In the
+kernel, a *class* is a struct that contains function pointers. This creates a
+contract between *implementers* and *users* since it forces them to use the
+same function signature without having to call the function directly. In order
+for it to truly be a class, the function pointers must specify that a pointer
+to the class, known as a *class handle*, be one of the parameters; this makes
+it possible for the member functions (also known as *methods*) to have access
+to member variables (more commonly known as *fields*) allowing the same
+implementation to have multiple *instances*.
+
+Typically a class can be *overridden* by *child classes* by embedding the
+*parent class* in the child class. Then when a method provided by the child
+class is called, the child implementation knows that the pointer passed to it is
+of a parent contained within the child; because of this, the child can compute
+the pointer to itself because the pointer to the parent is always a fixed offset
+from the pointer to the child; this offset is the offset of the parent contained
+in the child struct. For example:
+
+.. code-block:: c
+
+ struct shape {
+ int (*area)(struct shape *this);
+ };
+
+ struct rectangle {
+ struct shape parent;
+ int length;
+ int width;
+ };
+
+ int rectangle_area(struct shape *this)
+ {
+ struct rectangle *self = container_of(this, struct shape, parent);
+
+ return self->length * self->width;
+ };
+
+ void rectangle_new(struct rectangle *self, int length, int width)
+ {
+ self->parent.area = rectangle_area;
+ self->length = length;
+ self->width = width;
+ }
+
+In this example (as in most kernel code) the operation of computing the pointer
+to the child from the pointer to the parent is done by ``container_of``.
+
+Faking Classes
+~~~~~~~~~~~~~~
+
+In order to unit test a piece of code that calls a method in a class, the
+behavior of the method must be controllable, otherwise the test ceases to be a
+unit test and becomes an integration test.
+
+A fake just provides an implementation of a piece of code that is different than
+what runs in a production instance, but behaves identically from the standpoint
+of the callers; this is usually done to replace a dependency that is hard to
+deal with, or is slow.
+
+A good example for this might be implementing a fake EEPROM that just stores the
+"contents" in an internal buffer. For example, let's assume we have a class that
+represents an EEPROM:
+
+.. code-block:: c
+
+ struct eeprom {
+ ssize_t (*read)(struct eeprom *this, size_t offset, char *buffer, size_t count);
+ ssize_t (*write)(struct eeprom *this, size_t offset, const char *buffer, size_t count);
+ };
+
+And we want to test some code that buffers writes to the EEPROM:
+
+.. code-block:: c
+
+ struct eeprom_buffer {
+ ssize_t (*write)(struct eeprom_buffer *this, const char *buffer, size_t count);
+ int flush(struct eeprom_buffer *this);
+ size_t flush_count; /* Flushes when buffer exceeds flush_count. */
+ };
+
+ struct eeprom_buffer *new_eeprom_buffer(struct eeprom *eeprom);
+ void destroy_eeprom_buffer(struct eeprom *eeprom);
+
+We can easily test this code by *faking out* the underlying EEPROM:
+
+.. code-block:: c
+
+ struct fake_eeprom {
+ struct eeprom parent;
+ char contents[FAKE_EEPROM_CONTENTS_SIZE];
+ };
+
+ ssize_t fake_eeprom_read(struct eeprom *parent, size_t offset, char *buffer, size_t count)
+ {
+ struct fake_eeprom *this = container_of(parent, struct fake_eeprom, parent);
+
+ count = min(count, FAKE_EEPROM_CONTENTS_SIZE - offset);
+ memcpy(buffer, this->contents + offset, count);
+
+ return count;
+ }
+
+ ssize_t fake_eeprom_write(struct eeprom *this, size_t offset, const char *buffer, size_t count)
+ {
+ struct fake_eeprom *this = container_of(parent, struct fake_eeprom, parent);
+
+ count = min(count, FAKE_EEPROM_CONTENTS_SIZE - offset);
+ memcpy(this->contents + offset, buffer, count);
+
+ return count;
+ }
+
+ void fake_eeprom_init(struct fake_eeprom *this)
+ {
+ this->parent.read = fake_eeprom_read;
+ this->parent.write = fake_eeprom_write;
+ memset(this->contents, 0, FAKE_EEPROM_CONTENTS_SIZE);
+ }
+
+We can now use it to test ``struct eeprom_buffer``:
+
+.. code-block:: c
+
+ struct eeprom_buffer_test {
+ struct fake_eeprom *fake_eeprom;
+ struct eeprom_buffer *eeprom_buffer;
+ };
+
+ static void eeprom_buffer_test_does_not_write_until_flush(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx = test->priv;
+ struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer;
+ struct fake_eeprom *fake_eeprom = ctx->fake_eeprom;
+ char buffer[] = {0xff};
+
+ eeprom_buffer->flush_count = SIZE_MAX;
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0);
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0);
+
+ eeprom_buffer->flush(eeprom_buffer);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff);
+ }
+
+ static void eeprom_buffer_test_flushes_after_flush_count_met(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx = test->priv;
+ struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer;
+ struct fake_eeprom *fake_eeprom = ctx->fake_eeprom;
+ char buffer[] = {0xff};
+
+ eeprom_buffer->flush_count = 2;
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0);
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff);
+ }
+
+ static void eeprom_buffer_test_flushes_increments_of_flush_count(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx = test->priv;
+ struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer;
+ struct fake_eeprom *fake_eeprom = ctx->fake_eeprom;
+ char buffer[] = {0xff, 0xff};
+
+ eeprom_buffer->flush_count = 2;
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0);
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 2);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff);
+ /* Should have only flushed the first two bytes. */
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[2], 0);
+ }
+
+ static int eeprom_buffer_test_init(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ctx->fake_eeprom = kunit_kzalloc(test, sizeof(*ctx->fake_eeprom), GFP_KERNEL);
+ ASSERT_NOT_ERR_OR_NULL(test, ctx->fake_eeprom);
+
+ ctx->eeprom_buffer = new_eeprom_buffer(&ctx->fake_eeprom->parent);
+ ASSERT_NOT_ERR_OR_NULL(test, ctx->eeprom_buffer);
+
+ test->priv = ctx;
+
+ return 0;
+ }
+
+ static void eeprom_buffer_test_exit(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx = test->priv;
+
+ destroy_eeprom_buffer(ctx->eeprom_buffer);
+ }
+
+.. _kunit-on-non-uml:
+
+KUnit on non-UML architectures
+==============================
+
+By default KUnit uses UML as a way to provide dependencies for code under test.
+Under most circumstances KUnit's usage of UML should be treated as an
+implementation detail of how KUnit works under the hood. Nevertheless, there
+are instances where being able to run architecture specific code, or test
+against real hardware is desirable. For these reasons KUnit supports running on
+other architectures.
+
+Running existing KUnit tests on non-UML architectures
+-----------------------------------------------------
+
+There are some special considerations when running existing KUnit tests on
+non-UML architectures:
+
+* Hardware may not be deterministic, so a test that always passes or fails
+ when run under UML may not always do so on real hardware.
+* Hardware and VM environments may not be hermetic. KUnit tries its best to
+ provide a hermetic environment to run tests; however, it cannot manage state
+ that it doesn't know about outside of the kernel. Consequently, tests that
+ may be hermetic on UML may not be hermetic on other architectures.
+* Some features and tooling may not be supported outside of UML.
+* Hardware and VMs are slower than UML.
+
+None of these are reasons not to run your KUnit tests on real hardware; they are
+only things to be aware of when doing so.
+
+The biggest impediment will likely be that certain KUnit features and
+infrastructure may not support your target environment. For example, at this
+time the KUnit Wrapper (``tools/testing/kunit/kunit.py``) does not work outside
+of UML. Unfortunately, there is no way around this. Using UML (or even just a
+particular architecture) allows us to make a lot of assumptions that make it
+possible to do things which might otherwise be impossible.
+
+Nevertheless, all core KUnit framework features are fully supported on all
+architectures, and using them is straightforward: all you need to do is to take
+your kunitconfig, your Kconfig options for the tests you would like to run, and
+merge them into whatever config your are using for your platform. That's it!
+
+For example, let's say you have the following kunitconfig:
+
+.. code-block:: none
+
+ CONFIG_KUNIT=y
+ CONFIG_KUNIT_EXAMPLE_TEST=y
+
+If you wanted to run this test on an x86 VM, you might add the following config
+options to your ``.config``:
+
+.. code-block:: none
+
+ CONFIG_KUNIT=y
+ CONFIG_KUNIT_EXAMPLE_TEST=y
+ CONFIG_SERIAL_8250=y
+ CONFIG_SERIAL_8250_CONSOLE=y
+
+All these new options do is enable support for a common serial console needed
+for logging.
+
+Next, you could build a kernel with these tests as follows:
+
+
+.. code-block:: bash
+
+ make ARCH=x86 olddefconfig
+ make ARCH=x86
+
+Once you have built a kernel, you could run it on QEMU as follows:
+
+.. code-block:: bash
+
+ qemu-system-x86_64 -enable-kvm \
+ -m 1024 \
+ -kernel arch/x86_64/boot/bzImage \
+ -append 'console=ttyS0' \
+ --nographic
+
+Interspersed in the kernel logs you might see the following:
+
+.. code-block:: none
+
+ TAP version 14
+ # Subtest: example
+ 1..1
+ # example_simple_test: initializing
+ ok 1 - example_simple_test
+ ok 1 - example
+
+Congratulations, you just ran a KUnit test on the x86 architecture!
+
+Writing new tests for other architectures
+-----------------------------------------
+
+The first thing you must do is ask yourself whether it is necessary to write a
+KUnit test for a specific architecture, and then whether it is necessary to
+write that test for a particular piece of hardware. In general, writing a test
+that depends on having access to a particular piece of hardware or software (not
+included in the Linux source repo) should be avoided at all costs.
+
+Even if you only ever plan on running your KUnit test on your hardware
+configuration, other people may want to run your tests and may not have access
+to your hardware. If you write your test to run on UML, then anyone can run your
+tests without knowing anything about your particular setup, and you can still
+run your tests on your hardware setup just by compiling for your architecture.
+
+.. important::
+ Always prefer tests that run on UML to tests that only run under a particular
+ architecture, and always prefer tests that run under QEMU or another easy
+ (and monitarily free) to obtain software environment to a specific piece of
+ hardware.
+
+Nevertheless, there are still valid reasons to write an architecture or hardware
+specific test: for example, you might want to test some code that really belongs
+in ``arch/some-arch/*``. Even so, try your best to write the test so that it
+does not depend on physical hardware: if some of your test cases don't need the
+hardware, only require the hardware for tests that actually need it.
+
+Now that you have narrowed down exactly what bits are hardware specific, the
+actual procedure for writing and running the tests is pretty much the same as
+writing normal KUnit tests. One special caveat is that you have to reset
+hardware state in between test cases; if this is not possible, you may only be
+able to run one test case per invocation.
+
+.. TODO(brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org): Add an actual example of an architecture
+ dependent KUnit test.
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v10 16/18] MAINTAINERS: add entry for KUnit the unit testing framework
From: Brendan Higgins @ 2019-07-16 9:43 UTC (permalink / raw)
To: frowand.list, gregkh, jpoimboe, keescook, kieran.bingham, mcgrof,
peterz, robh, sboyd, shuah, tytso, yamada.masahiro
Cc: devicetree, dri-devel, kunit-dev, linux-doc, linux-fsdevel,
linux-kbuild, linux-kernel, linux-kselftest, linux-nvdimm,
linux-um, Alexander.Levin, Tim.Bird, amir73il, dan.carpenter,
daniel, jdike, joel, julia.lawall, khilman, knut.omang, logang,
mpe, pmladek, rdunlap, richard, rientjes, rostedt, wfg,
Brendan Higgins
In-Reply-To: <20190716094302.180360-1-brendanhiggins@google.com>
Add myself as maintainer of KUnit, the Linux kernel's unit testing
framework.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
MAINTAINERS | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 677ef41cb012c..48d04d180a988 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8599,6 +8599,17 @@ S: Maintained
F: tools/testing/selftests/
F: Documentation/dev-tools/kselftest*
+KERNEL UNIT TESTING FRAMEWORK (KUnit)
+M: Brendan Higgins <brendanhiggins@google.com>
+L: linux-kselftest@vger.kernel.org
+L: kunit-dev@googlegroups.com
+W: https://google.github.io/kunit-docs/third_party/kernel/docs/
+S: Maintained
+F: Documentation/dev-tools/kunit/
+F: include/kunit/
+F: kunit/
+F: tools/testing/kunit/
+
KERNEL USERMODE HELPER
M: Luis Chamberlain <mcgrof@kernel.org>
L: linux-kernel@vger.kernel.org
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v10 17/18] kernel/sysctl-test: Add null pointer test for sysctl.c:proc_dointvec()
From: Brendan Higgins @ 2019-07-16 9:43 UTC (permalink / raw)
To: frowand.list, gregkh, jpoimboe, keescook, kieran.bingham, mcgrof,
peterz, robh, sboyd, shuah, tytso, yamada.masahiro
Cc: pmladek, linux-doc, amir73il, Brendan Higgins, dri-devel,
Alexander.Levin, linux-kselftest, linux-nvdimm, khilman,
knut.omang, wfg, joel, rientjes, Iurii Zaikin, jdike,
dan.carpenter, devicetree, linux-kbuild, Tim.Bird, linux-um,
rostedt, julia.lawall, kunit-dev, richard, rdunlap, linux-kernel,
mpe, linux-fsdevel, logang
In-Reply-To: <20190716094302.180360-1-brendanhiggins@google.com>
From: Iurii Zaikin <yzaikin@google.com>
KUnit tests for initialized data behavior of proc_dointvec that is
explicitly checked in the code. Includes basic parsing tests including
int min/max overflow.
Signed-off-by: Iurii Zaikin <yzaikin@google.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Acked-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/Makefile | 2 +
kernel/sysctl-test.c | 392 +++++++++++++++++++++++++++++++++++++++++++
lib/Kconfig.debug | 11 ++
3 files changed, 405 insertions(+)
create mode 100644 kernel/sysctl-test.c
diff --git a/kernel/Makefile b/kernel/Makefile
index a8d923b5481ba..50fd511cd0ee0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -114,6 +114,8 @@ obj-$(CONFIG_HAS_IOMEM) += iomem.o
obj-$(CONFIG_ZONE_DEVICE) += memremap.o
obj-$(CONFIG_RSEQ) += rseq.o
+obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
+
obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o
KASAN_SANITIZE_stackleak.o := n
KCOV_INSTRUMENT_stackleak.o := n
diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
new file mode 100644
index 0000000000000..2a63241a8453b
--- /dev/null
+++ b/kernel/sysctl-test.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test of proc sysctl.
+ */
+
+#include <kunit/test.h>
+#include <linux/sysctl.h>
+
+#define KUNIT_PROC_READ 0
+#define KUNIT_PROC_WRITE 1
+
+static int i_zero;
+static int i_one_hundred = 100;
+
+/*
+ * Test that proc_dointvec will not try to use a NULL .data field even when the
+ * length is non-zero.
+ */
+static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
+{
+ struct ctl_table null_data_table = {
+ .procname = "foo",
+ /*
+ * Here we are testing that proc_dointvec behaves correctly when
+ * we give it a NULL .data field. Normally this would point to a
+ * piece of memory where the value would be stored.
+ */
+ .data = NULL,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ /*
+ * proc_dointvec expects a buffer in user space, so we allocate one. We
+ * also need to cast it to __user so sparse doesn't get mad.
+ */
+ void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
+ GFP_USER);
+ size_t len;
+ loff_t pos;
+
+ /*
+ * We don't care what the starting length is since proc_dointvec should
+ * not try to read because .data is NULL.
+ */
+ len = 1234;
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
+ KUNIT_PROC_READ, buffer, &len,
+ &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+
+ /*
+ * See above.
+ */
+ len = 1234;
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
+ KUNIT_PROC_WRITE, buffer, &len,
+ &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+}
+
+/*
+ * Similar to the previous test, we create a struct ctrl_table that has a .data
+ * field that proc_dointvec cannot do anything with; however, this time it is
+ * because we tell proc_dointvec that the size is 0.
+ */
+static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table data_maxlen_unset_table = {
+ .procname = "foo",
+ .data = &data,
+ /*
+ * So .data is no longer NULL, but we tell proc_dointvec its
+ * length is 0, so it still shouldn't try to use it.
+ */
+ .maxlen = 0,
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
+ GFP_USER);
+ size_t len;
+ loff_t pos;
+
+ /*
+ * As before, we don't care what buffer length is because proc_dointvec
+ * cannot do anything because its internal .data buffer has zero length.
+ */
+ len = 1234;
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
+ KUNIT_PROC_READ, buffer, &len,
+ &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+
+ /*
+ * See previous comment.
+ */
+ len = 1234;
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
+ KUNIT_PROC_WRITE, buffer, &len,
+ &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+}
+
+/*
+ * Here we provide a valid struct ctl_table, but we try to read and write from
+ * it using a buffer of zero length, so it should still fail in a similar way as
+ * before.
+ */
+static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
+ GFP_USER);
+ /*
+ * However, now our read/write buffer has zero length.
+ */
+ size_t len = 0;
+ loff_t pos;
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
+ &len, &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer,
+ &len, &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+}
+
+/*
+ * Test that proc_dointvec refuses to read when the file position is non-zero.
+ */
+static void sysctl_test_api_dointvec_table_read_but_position_set(
+ struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
+ GFP_USER);
+ /*
+ * We don't care about our buffer length because we start off with a
+ * non-zero file position.
+ */
+ size_t len = 1234;
+ /*
+ * proc_dointvec should refuse to read into the buffer since the file
+ * pos is non-zero.
+ */
+ loff_t pos = 1;
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
+ &len, &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+}
+
+/*
+ * Test that we can read a two digit number in a sufficiently size buffer.
+ * Nothing fancy.
+ */
+static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ size_t len = 4;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ /* Store 13 in the data field. */
+ *((int *)table.data) = 13;
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
+ user_buffer, &len, &pos));
+ KUNIT_ASSERT_EQ(test, (size_t)3, len);
+ buffer[len] = '\0';
+ /* And we read 13 back out. */
+ KUNIT_EXPECT_STREQ(test, "13\n", buffer);
+}
+
+/*
+ * Same as previous test, just now with negative numbers.
+ */
+static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ size_t len = 5;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ *((int *)table.data) = -16;
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
+ user_buffer, &len, &pos));
+ KUNIT_ASSERT_EQ(test, (size_t)4, len);
+ buffer[len] = '\0';
+ KUNIT_EXPECT_STREQ(test, "-16\n", (char *)buffer);
+}
+
+/*
+ * Test that a simple positive write works.
+ */
+static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ char input[] = "9";
+ size_t len = sizeof(input) - 1;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+
+ memcpy(buffer, input, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
+ KUNIT_EXPECT_EQ(test, sizeof(input) - 1, (size_t)pos);
+ KUNIT_EXPECT_EQ(test, 9, *((int *)table.data));
+}
+
+/*
+ * Same as previous test, but now with negative numbers.
+ */
+static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ char input[] = "-9";
+ size_t len = sizeof(input) - 1;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+
+ memcpy(buffer, input, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
+ KUNIT_EXPECT_EQ(test, sizeof(input) - 1, (size_t)pos);
+ KUNIT_EXPECT_EQ(test, -9, *((int *)table.data));
+}
+
+/*
+ * Test that writing a value smaller than the minimum possible value is not
+ * allowed.
+ */
+static void sysctl_test_api_dointvec_write_single_less_int_min(
+ struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ unsigned long abs_of_less_than_min = (unsigned long)INT_MAX
+ - (INT_MAX + INT_MIN) + 1;
+
+ /*
+ * We use this rigmarole to create a string that contains a value one
+ * less than the minimum accepted value.
+ */
+ KUNIT_ASSERT_LT(test,
+ (size_t)snprintf(buffer, max_len, "-%lu",
+ abs_of_less_than_min),
+ max_len);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, max_len, len);
+ KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
+}
+
+/*
+ * Test that writing the maximum possible value works.
+ */
+static void sysctl_test_api_dointvec_write_single_greater_int_max(
+ struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ unsigned long greater_than_max = (unsigned long)INT_MAX + 1;
+
+ KUNIT_ASSERT_GT(test, greater_than_max, (unsigned long)INT_MAX);
+ KUNIT_ASSERT_LT(test, (size_t)snprintf(buffer, max_len, "%lu",
+ greater_than_max),
+ max_len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_ASSERT_EQ(test, max_len, len);
+ KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
+}
+
+static struct kunit_case sysctl_test_cases[] = {
+ KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
+ KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
+ KUNIT_CASE(sysctl_test_api_dointvec_table_len_is_zero),
+ KUNIT_CASE(sysctl_test_api_dointvec_table_read_but_position_set),
+ KUNIT_CASE(sysctl_test_dointvec_read_happy_single_positive),
+ KUNIT_CASE(sysctl_test_dointvec_read_happy_single_negative),
+ KUNIT_CASE(sysctl_test_dointvec_write_happy_single_positive),
+ KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
+ KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
+ KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
+ {}
+};
+
+static struct kunit_suite sysctl_test_suite = {
+ .name = "sysctl_test",
+ .test_cases = sysctl_test_cases,
+};
+
+kunit_test_suite(sysctl_test_suite);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index cbdfae3798965..6f8007800a76f 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1939,6 +1939,17 @@ config TEST_SYSCTL
If unsure, say N.
+config SYSCTL_KUNIT_TEST
+ bool "KUnit test for sysctl"
+ depends on KUNIT
+ help
+ This builds the proc sysctl unit test, which runs on boot.
+ Tests the API contract and implementation correctness of sysctl.
+ For more information on KUnit and unit tests in general please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
config TEST_UDELAY
tristate "udelay test driver"
help
--
2.22.0.510.g264f2c817a-goog
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related
* [PATCH v10 18/18] MAINTAINERS: add proc sysctl KUnit test to PROC SYSCTL section
From: Brendan Higgins @ 2019-07-16 9:43 UTC (permalink / raw)
To: frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
jpoimboe-H+wXaHxf7aLQT0dZR+AlfA, keescook-hpIqsD4AKlfQT0dZR+AlfA,
kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
mcgrof-DgEjT+Ai2ygdnm+yROfE0A, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
robh-DgEjT+Ai2ygdnm+yROfE0A, sboyd-DgEjT+Ai2ygdnm+yROfE0A,
shuah-DgEjT+Ai2ygdnm+yROfE0A, tytso-3s7WtUTddSA,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A
Cc: pmladek-IBi9RG/b67k, linux-doc-u79uwXL29TY76Z2rM5mHXA,
amir73il-Re5JQEeQqe8AvxtiuMwx3w, Brendan Higgins,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
Alexander.Levin-0li6OtcxBFHby3iVrkZq2A,
linux-kselftest-u79uwXL29TY76Z2rM5mHXA,
linux-nvdimm-hn68Rpc1hR1g9hUCZPvPmw,
khilman-rdvid1DuHRBWk0Htik3J/w, knut.omang-QHcLZuEGTsvQT0dZR+AlfA,
wfg-VuQAYsv1563Yd54FQh9/CA, joel-U3u1mxZcP9KHXe+LvDLADg,
rientjes-hpIqsD4AKlfQT0dZR+AlfA, Iurii Zaikin,
jdike-OPE4K8JWMJJBDgjK7y7TUQ,
dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kbuild-u79uwXL29TY76Z2rM5mHXA, Tim.Bird-7U/KSKJipcs,
linux-um-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
rostedt-nx8X9YLhiw1AfugRpC6u6w, julia.lawall-L2FTfq7BK8M,
kunit-dev-/JYPxA39Uh5TLH3MbocFFw, richard-/L3Ra7n9ekc,
rdunlap-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
mpe-Gsx/Oe8HsFggBc27wqDAHg, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20190716094302.180360-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Add entry for the new proc sysctl KUnit test to the PROC SYSCTL section,
and add Iurii as a maintainer.
Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Cc: Iurii Zaikin <yzaikin-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Reviewed-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Reviewed-by: Logan Gunthorpe <logang-OTvnGxWRz7hWk0Htik3J/w@public.gmane.org>
Acked-by: Luis Chamberlain <mcgrof-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 48d04d180a988..f8204c75114da 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12721,12 +12721,14 @@ F: Documentation/filesystems/proc.txt
PROC SYSCTL
M: Luis Chamberlain <mcgrof-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
M: Kees Cook <keescook-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
+M: Iurii Zaikin <yzaikin-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
L: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
L: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
S: Maintained
F: fs/proc/proc_sysctl.c
F: include/linux/sysctl.h
F: kernel/sysctl.c
+F: kernel/sysctl-test.c
F: tools/testing/selftests/sysctl/
PS3 NETWORK SUPPORT
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* Re: [PATCHv2 1/3] iio: imu: st_lsm6sdx: move register definitions to sensor_settings struct
From: Lorenzo Bianconi @ 2019-07-16 9:43 UTC (permalink / raw)
To: Martin Kepplinger
Cc: lorenzo.bianconi83, jic23, knaack.h, lars, pmeerw, linux-iio,
devicetree
In-Reply-To: <20190716093325.7683-1-martin.kepplinger@puri.sm>
[-- Attachment #1: Type: text/plain, Size: 5590 bytes --]
> Move some register definitions to the per-device array of struct
> st_lsm6dsx_sensor_settings in order to simplify adding new sensor
> devices to the driver.
>
> Also, remove completely unused register definitions.
>
> Signed-off-by: Martin Kepplinger <martin.kepplinger@puri.sm>
> ---
>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
>
> This is already based on Lorenzo's recent changes:
> https://lore.kernel.org/linux-iio/853f216a-7814-cb79-180b-078ac5e8a359@puri.sm/T/#u
> https://lore.kernel.org/linux-iio/501b0db9-63cb-905c-c09b-682eb73f1ff3@puri.sm/T/#u
>
> revision history:
> v2: improve variable names, thanks Lorenzo
>
> thanks
> martin
>
>
>
> drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 6 ++++
> drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 31 ++++++++++++++------
> 2 files changed, 28 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> index ab1c66615d67..3c47f5d27d30 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> @@ -198,6 +198,9 @@ struct st_lsm6dsx_ext_dev_settings {
> /**
> * struct st_lsm6dsx_settings - ST IMU sensor settings
> * @wai: Sensor WhoAmI default value.
> + * @int1_addr: Control Register address for INT1
> + * @int2_addr: Control Register address for INT2
> + * @reset_addr: register address for reset/reboot
> * @max_fifo_size: Sensor max fifo length in FIFO words.
> * @id: List of hw id/device name supported by the driver configuration.
> * @odr_table: Hw sensors odr table (Hz + val).
> @@ -210,6 +213,9 @@ struct st_lsm6dsx_ext_dev_settings {
> */
> struct st_lsm6dsx_settings {
> u8 wai;
> + u8 int1_addr;
> + u8 int2_addr;
> + u8 reset_addr;
> u16 max_fifo_size;
> struct {
> enum st_lsm6dsx_hw_id hw_id;
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> index 9aa109428a52..e0d2149625cc 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> @@ -49,17 +49,12 @@
>
> #include "st_lsm6dsx.h"
>
> -#define ST_LSM6DSX_REG_INT1_ADDR 0x0d
> -#define ST_LSM6DSX_REG_INT2_ADDR 0x0e
> #define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3)
> #define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
> -#define ST_LSM6DSX_REG_RESET_ADDR 0x12
> #define ST_LSM6DSX_REG_RESET_MASK BIT(0)
> #define ST_LSM6DSX_REG_BOOT_MASK BIT(7)
> #define ST_LSM6DSX_REG_BDU_ADDR 0x12
> #define ST_LSM6DSX_REG_BDU_MASK BIT(6)
> -#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13
> -#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK BIT(5)
>
> #define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR 0x28
> #define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR 0x2a
> @@ -72,6 +67,9 @@
> static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> {
> .wai = 0x69,
> + .int1_addr = 0x0d,
> + .int2_addr = 0x0e,
> + .reset_addr = 0x12,
> .max_fifo_size = 1365,
> .id = {
> {
> @@ -170,6 +168,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> },
> {
> .wai = 0x69,
> + .int1_addr = 0x0d,
> + .int2_addr = 0x0e,
> + .reset_addr = 0x12,
> .max_fifo_size = 682,
> .id = {
> {
> @@ -268,6 +269,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> },
> {
> .wai = 0x6a,
> + .int1_addr = 0x0d,
> + .int2_addr = 0x0e,
> + .reset_addr = 0x12,
> .max_fifo_size = 682,
> .id = {
> {
> @@ -375,6 +379,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> },
> {
> .wai = 0x6c,
> + .int1_addr = 0x0d,
> + .int2_addr = 0x0e,
> + .reset_addr = 0x12,
> .max_fifo_size = 512,
> .id = {
> {
> @@ -494,6 +501,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> },
> {
> .wai = 0x6b,
> + .int1_addr = 0x0d,
> + .int2_addr = 0x0e,
> + .reset_addr = 0x12,
> .max_fifo_size = 512,
> .id = {
> {
> @@ -584,6 +594,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> },
> {
> .wai = 0x6b,
> + .int1_addr = 0x0d,
> + .int2_addr = 0x0e,
> + .reset_addr = 0x12,
> .max_fifo_size = 512,
> .id = {
> {
> @@ -1117,10 +1130,10 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
>
> switch (drdy_pin) {
> case 1:
> - *drdy_reg = ST_LSM6DSX_REG_INT1_ADDR;
> + *drdy_reg = hw->settings->int1_addr;
> break;
> case 2:
> - *drdy_reg = ST_LSM6DSX_REG_INT2_ADDR;
> + *drdy_reg = hw->settings->int2_addr;
> break;
> default:
> dev_err(hw->dev, "unsupported data ready pin\n");
> @@ -1220,7 +1233,7 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
> int err;
>
> /* device sw reset */
> - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
> + err = regmap_update_bits(hw->regmap, hw->settings->reset_addr,
> ST_LSM6DSX_REG_RESET_MASK,
> FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1));
> if (err < 0)
> @@ -1229,7 +1242,7 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
> msleep(50);
>
> /* reload trimming parameter */
> - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
> + err = regmap_update_bits(hw->regmap, hw->settings->reset_addr,
> ST_LSM6DSX_REG_BOOT_MASK,
> FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1));
> if (err < 0)
> --
> 2.20.1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCHv2 2/3] iio: imu: st_lsm6dsx: add support for accel/gyro unit of lsm9sd1
From: Lorenzo Bianconi @ 2019-07-16 9:48 UTC (permalink / raw)
To: Martin Kepplinger
Cc: lorenzo.bianconi83, jic23, knaack.h, lars, pmeerw, linux-iio,
devicetree, linux-kernel
In-Reply-To: <20190716093325.7683-2-martin.kepplinger@puri.sm>
[-- Attachment #1: Type: text/plain, Size: 9611 bytes --]
> The LSM9DS1's accelerometer / gyroscope unit and it's magnetometer (separately
> supported in iio/magnetometer/st_magn*) are located on a separate i2c addresses
> on the bus.
>
> For the datasheet, see https://www.st.com/resource/en/datasheet/lsm9ds1.pdf
>
> Treat it just like the LSM6* devices and, despite it's name, hook it up
> to the st_lsm6dsx driver, using it's basic functionality.
I think LSM9DS1 relies on LSM6DS0 for acc and gyro part so I guess we can use
this name here, what do you think?
>
> Signed-off-by: Martin Kepplinger <martin.kepplinger@puri.sm>
> ---
>
>
> This is already based on Lorenzo's recent changes:
> https://lore.kernel.org/linux-iio/853f216a-7814-cb79-180b-078ac5e8a359@puri.sm/T/#u
> https://lore.kernel.org/linux-iio/501b0db9-63cb-905c-c09b-682eb73f1ff3@puri.sm/T/#u
>
> revision history:
> v2: overall further simplification
>
> thanks
> martin
>
>
>
> drivers/iio/imu/st_lsm6dsx/Kconfig | 1 +
> drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 2 +
> drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 94 +++++++++++++++++++-
> drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 5 ++
> drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 5 ++
> 5 files changed, 104 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig
> index 2d8b2e1edfce..4a57bfb3c12e 100644
> --- a/drivers/iio/imu/st_lsm6dsx/Kconfig
> +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig
> @@ -11,6 +11,7 @@ config IIO_ST_LSM6DSX
> Say yes here to build support for STMicroelectronics LSM6DSx imu
> sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
> ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr, lsm6ds3tr-c
> + and the accelerometer/gyroscope of lsm9ds1.
>
> To compile this driver as a module, choose M here: the module
> will be called st_lsm6dsx.
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> index 3c47f5d27d30..9a30cc717de2 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> @@ -23,6 +23,7 @@
> #define ST_LSM6DSOX_DEV_NAME "lsm6dsox"
> #define ST_LSM6DSR_DEV_NAME "lsm6dsr"
> #define ST_LSM6DS3TRC_DEV_NAME "lsm6ds3tr-c"
> +#define ST_LSM9DS1_DEV_NAME "lsm9ds1"
>
> enum st_lsm6dsx_hw_id {
> ST_LSM6DS3_ID,
> @@ -35,6 +36,7 @@ enum st_lsm6dsx_hw_id {
> ST_LSM6DSOX_ID,
> ST_LSM6DSR_ID,
> ST_LSM6DS3TRC_ID,
> + ST_LSM9DS1_ID,
> ST_LSM6DSX_MAX_ID,
> };
>
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> index e0d2149625cc..2f3d2bf25646 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> @@ -10,6 +10,8 @@
> * +-125/+-245/+-500/+-1000/+-2000 dps
> * LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer
> * allowing dynamic batching of sensor data.
> + * LSM9DSx series is similar but includes an additional magnetometer, handled
> + * by a different driver.
> *
> * Supported sensors:
> * - LSM6DS3:
> @@ -30,6 +32,13 @@
> * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
> * - FIFO size: 3KB
> *
> + * - LSM9DS1:
> + * - Accelerometer supported ODR [Hz]: 10, 50, 119, 238, 476, 952
> + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
> + * - Gyroscope supported ODR [Hz]: 15, 60, 119, 238, 476, 952
> + * - Gyroscope supported full-scale [dps]: +-245/+-500/+-2000
> + * - FIFO size: 32
> + *
> * Copyright 2016 STMicroelectronics Inc.
> *
> * Lorenzo Bianconi <lorenzo.bianconi@st.com>
> @@ -64,7 +73,72 @@
> #define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR 0x24
> #define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR 0x26
>
> +#define ST_LSM9DSX_REG_GYRO_OUT_X_L_ADDR 0x18
> +#define ST_LSM9DSX_REG_GYRO_OUT_Y_L_ADDR 0x1a
> +#define ST_LSM9DSX_REG_GYRO_OUT_Z_L_ADDR 0x1c
> +
> static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> + {
> + .wai = 0x68,
> + .int1_addr = 0x0c,
> + .int2_addr = 0x0d,
> + .reset_addr = 0x22,
> + .max_fifo_size = 32,
> + .id = {
> + {
> + .hw_id = ST_LSM9DS1_ID,
> + .name = ST_LSM9DS1_DEV_NAME,
> + },
> + },
> + .odr_table = {
> + [ST_LSM6DSX_ID_ACC] = {
> + .reg = {
> + .addr = 0x20,
> + .mask = GENMASK(7, 5),
> + },
> + .odr_avl[0] = { 10, 0x01 },
> + .odr_avl[1] = { 50, 0x02 },
> + .odr_avl[2] = { 119, 0x03 },
> + .odr_avl[3] = { 238, 0x04 },
> + .odr_avl[4] = { 476, 0x05 },
> + .odr_avl[5] = { 952, 0x06 },
> + },
> + [ST_LSM6DSX_ID_GYRO] = {
> + .reg = {
> + .addr = 0x10,
> + .mask = GENMASK(7, 5),
> + },
> + .odr_avl[0] = { 15, 0x01 },
> + .odr_avl[1] = { 60, 0x02 },
> + .odr_avl[2] = { 119, 0x03 },
> + .odr_avl[3] = { 238, 0x04 },
> + .odr_avl[4] = { 476, 0x05 },
> + .odr_avl[5] = { 952, 0x06 },
> + },
> + },
> + .fs_table = {
> + [ST_LSM6DSX_ID_ACC] = {
> + .reg = {
> + .addr = 0x20,
> + .mask = GENMASK(4, 3),
> + },
> + .fs_avl[0] = { 599, 0x0 },
> + .fs_avl[1] = { 1197, 0x2 },
> + .fs_avl[2] = { 2394, 0x3 },
> + .fs_avl[3] = { 4788, 0x1 },
> + },
> + [ST_LSM6DSX_ID_GYRO] = {
> + .reg = {
> + .addr = 0x10,
> + .mask = GENMASK(4, 3),
> + },
> + .fs_avl[0] = { IIO_DEGREE_TO_RAD(245), 0x0 },
> + .fs_avl[1] = { IIO_DEGREE_TO_RAD(500), 0x1 },
> + .fs_avl[2] = { IIO_DEGREE_TO_RAD(0), 0x2 },
> + .fs_avl[3] = { IIO_DEGREE_TO_RAD(2000), 0x3 },
> + },
> + },
> + },
> {
> .wai = 0x69,
> .int1_addr = 0x0d,
> @@ -733,6 +807,16 @@ static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
> IIO_CHAN_SOFT_TIMESTAMP(3),
> };
>
> +static const struct iio_chan_spec st_lsm9dsx_gyro_channels[] = {
> + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM9DSX_REG_GYRO_OUT_X_L_ADDR,
> + IIO_MOD_X, 0),
I guess you can use 0x18, 0x1a and 0x1c here and remove the definitions
> + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM9DSX_REG_GYRO_OUT_Y_L_ADDR,
> + IIO_MOD_Y, 1),
> + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM9DSX_REG_GYRO_OUT_Z_L_ADDR,
> + IIO_MOD_Z, 2),
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable)
> {
> const struct st_lsm6dsx_shub_settings *hub_settings;
> @@ -1278,7 +1362,7 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
>
> static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
> enum st_lsm6dsx_sensor_id id,
> - const char *name)
> + const char *name, int hw_id)
> {
> struct st_lsm6dsx_sensor *sensor;
> struct iio_dev *iio_dev;
> @@ -1308,7 +1392,11 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
> name);
> break;
> case ST_LSM6DSX_ID_GYRO:
> - iio_dev->channels = st_lsm6dsx_gyro_channels;
> + if (hw_id == ST_LSM9DS1_ID)
> + iio_dev->channels = st_lsm9dsx_gyro_channels;
> + else
> + iio_dev->channels = st_lsm6dsx_gyro_channels;
> +
> iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels);
> iio_dev->info = &st_lsm6dsx_gyro_info;
>
> @@ -1354,7 +1442,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
> return err;
>
> for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) {
> - hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
> + hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name, hw_id);
> if (!hw->iio_devs[i])
> return -ENOMEM;
> }
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
> index 28581eb0532c..c36a057c36ee 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
> @@ -79,6 +79,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
> .compatible = "st,lsm6ds3tr-c",
> .data = (void *)ST_LSM6DS3TRC_ID,
> },
> + {
> + .compatible = "st,lsm9ds1",
> + .data = (void *)ST_LSM9DS1_ID,
> + },
> {},
> };
> MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
> @@ -94,6 +98,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
> { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
> { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
> { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID },
> + { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID },
> {},
> };
> MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
> index 0371e8b94a3e..138e3b985865 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
> @@ -79,6 +79,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
> .compatible = "st,lsm6ds3tr-c",
> .data = (void *)ST_LSM6DS3TRC_ID,
> },
> + {
> + .compatible = "st,lsm9ds1",
> + .data = (void *)ST_LSM9DS1_ID,
> + },
> {},
> };
> MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
> @@ -94,6 +98,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
> { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
> { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
> { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID },
> + { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID },
> {},
> };
> MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
> --
> 2.20.1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v1 01/50] clk: samsung: add new IDs for Exynos5420 clocks
From: Chanwoo Choi @ 2019-07-16 9:49 UTC (permalink / raw)
To: Lukasz Luba, devicetree, linux-kernel, linux-arm-kernel,
linux-samsung-soc, linux-clk
Cc: mturquette, sboyd, b.zolnierkie, krzk, kgene, mark.rutland,
robh+dt, kyungmin.park, a.hajda, m.szyprowski, s.nawrocki,
myungjoo.ham
In-Reply-To: <20190715124417.4787-2-l.luba@partner.samsung.com>
Hi,
Usually, when developing the clock controller driver,
define the same sequence between the definition sequence in dt-bibing
and clock driver. As I replied, if you squash patches, it is easy.
For example,
This series add clock id as following.
569 MUX(CLK_MOUT_ACLK400_ISP, "mout_aclk400_isp", mout_group1_p,
570 SRC_TOP0, 0, 2),
571 MUX(CLK_MOUT_ACLK400_MSCL, "mout_aclk400_mscl", mout_group1_p,
572 SRC_TOP0, 4, 2),
573 MUX(CLK_MOUT_ACLK400_WCORE, "mout_aclk400_wcore", mout_group1_p,
574 SRC_TOP0, 16, 2),
575 MUX(CLK_MOUT_ACLK100_NOC, "mout_aclk100_noc", mout_group1_p,
576 SRC_TOP0, 20, 2),
In case of this code, you can define the IDs as following sequentially:
#define CLK_MOUT_ACLK400_ISP ...
#define CLK_MOUT_ACLK400_MSCL ...
#define CLK_MOUT_ACLK400_WCORE ...
#define CLK_MOUT_ACLK100_NOC ...
But, this series define the ID as following:
#define CLK_MOUT_ACLK400_WCORE 662
#define CLK_MOUT_SCLK_DPLL 663
#define CLK_MOUT_ACLK100_NOC 664
#define CLK_MOUT_ACLK200_FSYS2 665
#define CLK_MOUT_PCLK200_FSYS 666
#define CLK_MOUT_ACLK200_FSYS 667
#define CLK_MOUT_ACLK400_ISP 668
#define CLK_MOUT_ACLK400_MSCL 669
#define CLK_MOUT_SCLK_MPLL 700
#define CLK_MOUT_ACLK266 701
#define CLK_MOUT_UART0 702
#define CLK_MOUT_UART1 703
#define CLK_MOUT_UART2 704
#define CLK_MOUT_UART3 7
On 19. 7. 15. 오후 9:43, Lukasz Luba wrote:
> There is a need of new IDs which will be used for modeling proper hierarchy
> in the Exynos54xx SoCs. Previous implementation rely on bootloader
> settings, which are not configuring properly some clocks.
> These IDs provide interface to set proper parents.
>
> Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
> ---
> include/dt-bindings/clock/exynos5420.h | 27 +++++++++++++++++++++++++-
> 1 file changed, 26 insertions(+), 1 deletion(-)
>
> diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h
> index 02d5ac469a3d..c37a28eeaf7e 100644
> --- a/include/dt-bindings/clock/exynos5420.h
> +++ b/include/dt-bindings/clock/exynos5420.h
> @@ -230,6 +230,30 @@
> #define CLK_MOUT_USER_MAU_EPLL 659
> #define CLK_MOUT_SCLK_SPLL 660
> #define CLK_MOUT_MX_MSPLL_CCORE_PHY 661
> +#define CLK_MOUT_ACLK400_WCORE 662
> +#define CLK_MOUT_SCLK_DPLL 663
> +#define CLK_MOUT_ACLK100_NOC 664
> +#define CLK_MOUT_ACLK200_FSYS2 665
> +#define CLK_MOUT_PCLK200_FSYS 666
> +#define CLK_MOUT_ACLK200_FSYS 667
> +#define CLK_MOUT_ACLK400_ISP 668
> +#define CLK_MOUT_ACLK400_MSCL 669
> +#define CLK_MOUT_SCLK_MPLL 700
> +#define CLK_MOUT_ACLK266 701
> +#define CLK_MOUT_UART0 702
> +#define CLK_MOUT_UART1 703
> +#define CLK_MOUT_UART2 704
> +#define CLK_MOUT_UART3 705
> +#define CLK_MOUT_SCLK_CPLL 706
> +#define CLK_MOUT_PWM 707
> +#define CLK_MOUT_ACLK266_G2D 708
> +#define CLK_MOUT_SW_ACLK400_WCORE 709
> +#define CLK_MOUT_SW_ACLK400_MSCL 710
> +#define CLK_MOUT_SW_ACLK400_ISP 711
> +#define CLK_MOUT_SW_ACLK266_ISP 712
> +#define CLK_MOUT_USER_ACLK266_ISP 713
> +#define CLK_MOUT_ACLK266_ISP 714
> +#define CLK_MOUT_MMC0 715
>
> /* divider clocks */
> #define CLK_DOUT_PIXEL 768
> @@ -264,8 +288,9 @@
> #define CLK_FF_DOUT_SPLL2 797
> #define CLK_DOUT_PCLK_DREX0 798
> #define CLK_DOUT_PCLK_DREX1 799
> +#define CLK_DOUT_ACLK266_ISP 800
>
> /* must be greater than maximal clock id */
> -#define CLK_NR_CLKS 800
> +#define CLK_NR_CLKS 801
>
> #endif /* _DT_BINDINGS_CLOCK_EXYNOS_5420_H */
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
^ permalink raw reply
* Re: [PATCH v1 10/50] clk: samsung: change ACLK100_NOC clocks definitions Exynos5x
From: Chanwoo Choi @ 2019-07-16 10:01 UTC (permalink / raw)
To: Lukasz Luba, devicetree, linux-kernel, linux-arm-kernel,
linux-samsung-soc, linux-clk
Cc: mturquette, sboyd, b.zolnierkie, krzk, kgene, mark.rutland,
robh+dt, kyungmin.park, a.hajda, m.szyprowski, s.nawrocki,
myungjoo.ham
In-Reply-To: <20190715124417.4787-11-l.luba@partner.samsung.com>
Hi,
I checked the range of ratio as following: Looks good to me.
But, you better to make it to fix the ratio range without adding ID.
Please merge the code related to adding IDs into one patch.
- Exynos5422 TRM ACLK_100_NOC_RATIO [23:20]
- Exynos5420 TRM ACLK_100_NOC_RATIO [22:20]
On 19. 7. 15. 오후 9:43, Lukasz Luba wrote:
> The ACLK100_NOC has different topology in Exynos5420 and 5422/5800. In
> Exynos5420 this clock divider has 3 bits while in 5422/5800 has 4 bits.
> The patch adds needed dividers in the exynos5800_div_clks, updates
> exynos5x_div_clks and exynos5420_div_clks properly. It also adds IDs to
> manage clocks from DT.
>
> Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
> ---
> drivers/clk/samsung/clk-exynos5420.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
> index 6164d0ca75e0..c13f34d3d9a1 100644
> --- a/drivers/clk/samsung/clk-exynos5420.c
> +++ b/drivers/clk/samsung/clk-exynos5420.c
> @@ -471,7 +471,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = {
> SRC_TOP0, 4, 3),
> MUX(CLK_MOUT_ACLK400_WCORE, "mout_aclk400_wcore", mout_group2_5800_p,
> SRC_TOP0, 16, 3),
> - MUX(0, "mout_aclk100_noc", mout_group1_5800_p, SRC_TOP0, 20, 2),
> + MUX(CLK_MOUT_ACLK100_NOC, "mout_aclk100_noc", mout_group1_5800_p,
> + SRC_TOP0, 20, 2),
Please squash this code to the patch which add the IDs.
Actually, it is not related to fix the rate range of clock.
>
> MUX(0, "mout_aclk333_432_gscl", mout_group6_5800_p, SRC_TOP1, 0, 2),
> MUX(0, "mout_aclk333_432_isp", mout_group6_5800_p, SRC_TOP1, 4, 2),
> @@ -534,6 +535,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = {
> static const struct samsung_div_clock exynos5800_div_clks[] __initconst = {
> DIV(CLK_DOUT_ACLK400_WCORE, "dout_aclk400_wcore",
> "mout_aclk400_wcore", DIV_TOP0, 16, 3),
> + DIV(CLK_DOUT_ACLK100_NOC, "dout_aclk100_noc", "mout_aclk100_noc",
> + DIV_TOP0, 20, 4),
> DIV(CLK_DOUT_ACLK266_ISP, "dout_aclk266_isp", "mout_aclk266_isp",
> DIV_TOP8, 12, 3),
> DIV(0, "dout_aclk550_cam", "mout_aclk550_cam",
> @@ -569,7 +572,8 @@ static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = {
> SRC_TOP0, 4, 2),
> MUX(CLK_MOUT_ACLK400_WCORE, "mout_aclk400_wcore", mout_group1_p,
> SRC_TOP0, 16, 2),
> - MUX(0, "mout_aclk100_noc", mout_group1_p, SRC_TOP0, 20, 2),
> + MUX(CLK_MOUT_ACLK100_NOC, "mout_aclk100_noc", mout_group1_p,
> + SRC_TOP0, 20, 2),
ditto.
Please squash this code to the patch which add the IDs.
Actually, it is not related to fix the rate range of clock.
>
> MUX(0, "mout_aclk333_432_gscl", mout_group4_p, SRC_TOP1, 0, 2),
> MUX(0, "mout_aclk333_432_isp", mout_group4_p,
> @@ -600,6 +604,8 @@ static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = {
> static const struct samsung_div_clock exynos5420_div_clks[] __initconst = {
> DIV(CLK_DOUT_ACLK400_WCORE, "dout_aclk400_wcore",
> "mout_aclk400_wcore", DIV_TOP0, 16, 3),
> + DIV(CLK_DOUT_ACLK100_NOC, "dout_aclk100_noc", "mout_aclk100_noc",
> + DIV_TOP0, 20, 3),
> };
>
> static const struct samsung_gate_clock exynos5420_gate_clks[] __initconst = {
> @@ -798,8 +804,6 @@ static const struct samsung_div_clock exynos5x_div_clks[] __initconst = {
> DIV_TOP0, 8, 3),
> DIV(CLK_DOUT_ACLK200_FSYS2, "dout_aclk200_fsys2", "mout_aclk200_fsys2",
> DIV_TOP0, 12, 3),
> - DIV(CLK_DOUT_ACLK100_NOC, "dout_aclk100_noc", "mout_aclk100_noc",
> - DIV_TOP0, 20, 3),
> DIV(CLK_DOUT_PCLK200_FSYS, "dout_pclk200_fsys", "mout_pclk200_fsys",
> DIV_TOP0, 24, 3),
> DIV(CLK_DOUT_ACLK200_FSYS, "dout_aclk200_fsys", "mout_aclk200_fsys",
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
^ permalink raw reply
* Re: [PATCH v2 1/4] opp: core: add regulators enable and disable
From: Viresh Kumar @ 2019-07-16 10:05 UTC (permalink / raw)
To: Kamil Konieczny
Cc: Bartlomiej Zolnierkiewicz, Marek Szyprowski, Chanwoo Choi,
Krzysztof Kozlowski, Kukjin Kim, Kyungmin Park, Mark Rutland,
MyungJoo Ham, Nishanth Menon, Rob Herring, Stephen Boyd,
Viresh Kumar, devicetree, linux-arm-kernel, linux-kernel,
linux-pm, linux-samsung-soc
In-Reply-To: <20190715120416.3561-2-k.konieczny@partner.samsung.com>
On 15-07-19, 14:04, Kamil Konieczny wrote:
> Add enable regulators to dev_pm_opp_set_regulators() and disable
> regulators to dev_pm_opp_put_regulators(). This prepares for
> converting exynos-bus devfreq driver to use dev_pm_opp_set_rate().
>
> Signed-off-by: Kamil Konieczny <k.konieczny@partner.samsung.com>
> --
> Changes in v2:
>
> - move regulator enable and disable into loop
>
> ---
> drivers/opp/core.c | 18 +++++++++++++++---
> 1 file changed, 15 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/opp/core.c b/drivers/opp/core.c
> index 0e7703fe733f..069c5cf8827e 100644
> --- a/drivers/opp/core.c
> +++ b/drivers/opp/core.c
> @@ -1570,6 +1570,10 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
> goto free_regulators;
> }
>
> + ret = regulator_enable(reg);
> + if (ret < 0)
> + goto disable;
The name of this label is logically incorrect because we won't disable
the regulator from there but put it. Over that, I would rather prefer
to remove the label and add regulator_put() here itself.
> +
> opp_table->regulators[i] = reg;
> }
>
> @@ -1582,9 +1586,15 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
>
> return opp_table;
>
> +disable:
> + regulator_put(reg);
> + --i;
> +
> free_regulators:
> - while (i != 0)
> - regulator_put(opp_table->regulators[--i]);
> + for (; i >= 0; --i) {
> + regulator_disable(opp_table->regulators[i]);
> + regulator_put(opp_table->regulators[i]);
This is incorrect as this will now try to put/disable the regulator
which we failed to acquire. As --i happens only after the loop has run
once. You can rather do:
while (i--) {
regulator_disable(opp_table->regulators[i]);
regulator_put(opp_table->regulators[i]);
}
> + }
>
> kfree(opp_table->regulators);
> opp_table->regulators = NULL;
> @@ -1610,8 +1620,10 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table)
> /* Make sure there are no concurrent readers while updating opp_table */
> WARN_ON(!list_empty(&opp_table->opp_list));
>
> - for (i = opp_table->regulator_count - 1; i >= 0; i--)
> + for (i = opp_table->regulator_count - 1; i >= 0; i--) {
> + regulator_disable(opp_table->regulators[i]);
> regulator_put(opp_table->regulators[i]);
> + }
>
> _free_set_opp_data(opp_table);
>
> --
> 2.22.0
--
viresh
^ permalink raw reply
* Re: [PATCH v2 2/4] devfreq: exynos-bus: convert to use dev_pm_opp_set_rate()
From: Bartlomiej Zolnierkiewicz @ 2019-07-16 10:13 UTC (permalink / raw)
To: Chanwoo Choi
Cc: Kamil Konieczny, Marek Szyprowski, Krzysztof Kozlowski,
Kukjin Kim, Kyungmin Park, Mark Rutland, MyungJoo Ham,
Nishanth Menon, Rob Herring, Stephen Boyd, Viresh Kumar,
devicetree, linux-arm-kernel, linux-kernel, linux-pm,
linux-samsung-soc
In-Reply-To: <7f7cf551-005a-c647-d571-77eb5426478a@samsung.com>
Hi Chanwoo,
On 7/16/19 5:56 AM, Chanwoo Choi wrote:
> Hi Kamil,
>
> Looks good to me. But, this patch has some issue.
> I added the detailed reviews.
>
> I recommend that you make the separate patches as following
> in order to clarify the role of which apply the dev_pm_opp_* function.
>
> First patch,
> Need to consolidate the following two function into one function.
> because the original exynos-bus.c has the problem that the regulator
> of parent devfreq device have to be enabled before enabling the clock.
> This issue did not happen because bootloader enables the bus-related
> regulators before kernel booting.
> - exynos_bus_parse_of()
> - exynos_bus_parent_parse_of()
> > Second patch,
> Apply dev_pm_opp_set_regulators() and dev_pm_opp_set_rate()
>
>
> On 19. 7. 15. 오후 9:04, Kamil Konieczny wrote:
>> Reuse opp core code for setting bus clock and voltage. As a side
>> effect this allow useage of coupled regulators feature (required
>> for boards using Exynos5422/5800 SoCs) because dev_pm_opp_set_rate()
>> uses regulator_set_voltage_triplet() for setting regulator voltage
>> while the old code used regulator_set_voltage_tol() with fixed
>> tolerance. This patch also removes no longer needed parsing of DT
>> property "exynos,voltage-tolerance" (no Exynos devfreq DT node uses
>> it).
>>
>> Signed-off-by: Kamil Konieczny <k.konieczny@partner.samsung.com>
>> ---
>> drivers/devfreq/exynos-bus.c | 172 ++++++++++++++---------------------
>> 1 file changed, 66 insertions(+), 106 deletions(-)
>>
>> diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
>> index 486cc5b422f1..7fc4f76bd848 100644
>> --- a/drivers/devfreq/exynos-bus.c
>> +++ b/drivers/devfreq/exynos-bus.c
>> @@ -25,7 +25,6 @@
>> #include <linux/slab.h>
>>
>> #define DEFAULT_SATURATION_RATIO 40
>> -#define DEFAULT_VOLTAGE_TOLERANCE 2
>>
>> struct exynos_bus {
>> struct device *dev;
>> @@ -37,9 +36,9 @@ struct exynos_bus {
>>
>> unsigned long curr_freq;
>>
>> - struct regulator *regulator;
>> + struct opp_table *opp_table;
>> +
>> struct clk *clk;
>> - unsigned int voltage_tolerance;
>> unsigned int ratio;
>> };
>>
>> @@ -99,56 +98,25 @@ static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
>> {
>> struct exynos_bus *bus = dev_get_drvdata(dev);
>> struct dev_pm_opp *new_opp;
>> - unsigned long old_freq, new_freq, new_volt, tol;
>> int ret = 0;
>> -
>> - /* Get new opp-bus instance according to new bus clock */
>> + /*
>> + * New frequency for bus may not be exactly matched to opp, adjust
>> + * *freq to correct value.
>> + */
>
> You better to change this comment with following styles
> to keep the consistency:
>
> /* Get correct frequency for bus ... */
>
>> new_opp = devfreq_recommended_opp(dev, freq, flags);
>> if (IS_ERR(new_opp)) {
>> dev_err(dev, "failed to get recommended opp instance\n");
>> return PTR_ERR(new_opp);
>> }
>>
>> - new_freq = dev_pm_opp_get_freq(new_opp);
>> - new_volt = dev_pm_opp_get_voltage(new_opp);
>> dev_pm_opp_put(new_opp);
>>
>> - old_freq = bus->curr_freq;
>> -
>> - if (old_freq == new_freq)
>> - return 0;
>> - tol = new_volt * bus->voltage_tolerance / 100;
>> -
>> /* Change voltage and frequency according to new OPP level */
>> mutex_lock(&bus->lock);
>> + ret = dev_pm_opp_set_rate(dev, *freq);
>> + if (!ret)
>> + bus->curr_freq = *freq;
>
> Have to print the error log if ret has minus error value.
dev_pm_opp_set_rate() should print the error message on all
errors so wouldn't printing the error log also here be superfluous?
[ Please also note that the other user of dev_pm_opp_set_rate()
(cpufreq-dt cpufreq driver) doesn't do this. ]
> Modify it as following:
>
> if (ret < 0) {
> dev_err(dev, "failed to set bus rate\n");
> goto err:
> }
> bus->curr_freq = *freq;
>
> err:
> mutex_unlock(&bus->lock);
>
> return ret;
>
>>
>> - if (old_freq < new_freq) {
>> - ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
>> - if (ret < 0) {
>> - dev_err(bus->dev, "failed to set voltage\n");
>> - goto out;
>> - }
>> - }
>> -
>> - ret = clk_set_rate(bus->clk, new_freq);
>> - if (ret < 0) {
>> - dev_err(dev, "failed to change clock of bus\n");
>> - clk_set_rate(bus->clk, old_freq);
>> - goto out;
>> - }
>> -
>> - if (old_freq > new_freq) {
>> - ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
>> - if (ret < 0) {
>> - dev_err(bus->dev, "failed to set voltage\n");
>> - goto out;
>> - }
>> - }
>> - bus->curr_freq = new_freq;
>> -
>> - dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
>> - old_freq, new_freq, clk_get_rate(bus->clk));
>> -out:
>> mutex_unlock(&bus->lock);
>>
>> return ret;
>> @@ -194,10 +162,11 @@ static void exynos_bus_exit(struct device *dev)
>> if (ret < 0)
>> dev_warn(dev, "failed to disable the devfreq-event devices\n");
>>
>> - if (bus->regulator)
>> - regulator_disable(bus->regulator);
>> + if (bus->opp_table)
>> + dev_pm_opp_put_regulators(bus->opp_table);
>
> Have to disable regulator after disabling the clock
> to prevent the h/w fault.
>
> I think that you should call them with following sequence:
>
> clk_disable_unprepare(bus->clk);
> if (bus->opp_table)
> dev_pm_opp_put_regulators(bus->opp_table);
> dev_pm_opp_of_remove_table(dev);
>
>>
>> dev_pm_opp_of_remove_table(dev);
>> +
>> clk_disable_unprepare(bus->clk);
>> }
>>
>> @@ -209,39 +178,26 @@ static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
>> {
>> struct exynos_bus *bus = dev_get_drvdata(dev);
>> struct dev_pm_opp *new_opp;
>> - unsigned long old_freq, new_freq;
>> - int ret = 0;
>> + int ret;
>>
>> - /* Get new opp-bus instance according to new bus clock */
>> + /*
>> + * New frequency for bus may not be exactly matched to opp, adjust
>> + * *freq to correct value.
>> + */
>
> You better to change this comment with following styles
> to keep the consistency:
>
> /* Get correct frequency for bus ... */
>
>> new_opp = devfreq_recommended_opp(dev, freq, flags);
>> if (IS_ERR(new_opp)) {
>> dev_err(dev, "failed to get recommended opp instance\n");
>> return PTR_ERR(new_opp);
>> }
>>
>> - new_freq = dev_pm_opp_get_freq(new_opp);
>> dev_pm_opp_put(new_opp);
>>
>> - old_freq = bus->curr_freq;
>> -
>> - if (old_freq == new_freq)
>> - return 0;
>> -
>> /* Change the frequency according to new OPP level */
>> mutex_lock(&bus->lock);
>> + ret = dev_pm_opp_set_rate(dev, *freq);
>> + if (!ret)
>> + bus->curr_freq = *freq;
>
> ditto. Have to print the error log, check above comment.
>
>>
>> - ret = clk_set_rate(bus->clk, new_freq);
>> - if (ret < 0) {
>> - dev_err(dev, "failed to set the clock of bus\n");
>> - goto out;
>> - }
>> -
>> - *freq = new_freq;
>> - bus->curr_freq = new_freq;
>> -
>> - dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
>> - old_freq, new_freq, clk_get_rate(bus->clk));
>> -out:
>> mutex_unlock(&bus->lock);
>>
>> return ret;
>> @@ -259,20 +215,7 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>> struct exynos_bus *bus)
>> {
>> struct device *dev = bus->dev;
>> - int i, ret, count, size;
>> -
>> - /* Get the regulator to provide each bus with the power */
>> - bus->regulator = devm_regulator_get(dev, "vdd");
>> - if (IS_ERR(bus->regulator)) {
>> - dev_err(dev, "failed to get VDD regulator\n");
>> - return PTR_ERR(bus->regulator);
>> - }
>> -
>> - ret = regulator_enable(bus->regulator);
>> - if (ret < 0) {
>> - dev_err(dev, "failed to enable VDD regulator\n");
>> - return ret;
>> - }
>> + int i, count, size;
>>
>> /*
>> * Get the devfreq-event devices to get the current utilization of
>> @@ -281,24 +224,20 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>> count = devfreq_event_get_edev_count(dev);
>> if (count < 0) {
>> dev_err(dev, "failed to get the count of devfreq-event dev\n");
>> - ret = count;
>> - goto err_regulator;
>> + return count;
>> }
>> +
>> bus->edev_count = count;
>>
>> size = sizeof(*bus->edev) * count;
>> bus->edev = devm_kzalloc(dev, size, GFP_KERNEL);
>> - if (!bus->edev) {
>> - ret = -ENOMEM;
>> - goto err_regulator;
>> - }
>> + if (!bus->edev)
>> + return -ENOMEM;
>>
>> for (i = 0; i < count; i++) {
>> bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i);
>> - if (IS_ERR(bus->edev[i])) {
>> - ret = -EPROBE_DEFER;
>> - goto err_regulator;
>> - }
>> + if (IS_ERR(bus->edev[i]))
>> + return -EPROBE_DEFER;
>> }
>>
>> /*
>> @@ -314,22 +253,15 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>> if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
>> bus->ratio = DEFAULT_SATURATION_RATIO;
>>
>> - if (of_property_read_u32(np, "exynos,voltage-tolerance",
>> - &bus->voltage_tolerance))
>> - bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE;
>> -
>> return 0;
>> -
>> -err_regulator:
>> - regulator_disable(bus->regulator);
>> -
>> - return ret;
>> }
>>
>> static int exynos_bus_parse_of(struct device_node *np,
>> - struct exynos_bus *bus)
>> + struct exynos_bus *bus, bool passive)
>> {
>> struct device *dev = bus->dev;
>> + struct opp_table *opp_table;
>> + const char *vdd = "vdd";
>> struct dev_pm_opp *opp;
>> unsigned long rate;
>> int ret;
>> @@ -347,11 +279,22 @@ static int exynos_bus_parse_of(struct device_node *np,
>> return ret;
>> }
>>
>> + if (!passive) {
>> + opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1);
>> + if (IS_ERR(opp_table)) {
>> + ret = PTR_ERR(opp_table);
>> + dev_err(dev, "failed to set regulators %d\n", ret);
>> + goto err_clk;/
>> + }
>> +
>> + bus->opp_table = opp_table;
>> + }
>
> This driver has exynos_bus_parent_parse_of() function for parent devfreq device.
> dev_pm_opp_set_regulators() have to be called in exynos_bus_parent_parse_of()
> because the regulator is only used by parent devfreq device.
exynos_bus_parse_of() is called for all devfreq devices (including
parent) and (as you've noticed) the regulator should be enabled before
enabling clock (which is done in exynos_bus_parse_of()) so adding
extra argument to exynos_bus_parse_of() (like it is done currently in
the patch) makes it possible to do the setup correctly without the need
of merging both functions into one huge function (which would be more
difficult to follow than two simpler functions IMHO). Is that approach
acceptable or do you prefer one big function?
>> +
>> /* Get the freq and voltage from OPP table to scale the bus freq */
>> ret = dev_pm_opp_of_add_table(dev);
>> if (ret < 0) {
>> dev_err(dev, "failed to get OPP table\n");
>> - goto err_clk;
>> + goto err_regulator;
>> }
>>
>> rate = clk_get_rate(bus->clk);
>> @@ -362,6 +305,7 @@ static int exynos_bus_parse_of(struct device_node *np,
>> ret = PTR_ERR(opp);
>> goto err_opp;
>> }
>> +
>> bus->curr_freq = dev_pm_opp_get_freq(opp);
>> dev_pm_opp_put(opp);
>>
>> @@ -369,6 +313,13 @@ static int exynos_bus_parse_of(struct device_node *np,
>>
>> err_opp:
>> dev_pm_opp_of_remove_table(dev);
>> +
>> +err_regulator:
>> + if (bus->opp_table) {
>> + dev_pm_opp_put_regulators(bus->opp_table);
>> + bus->opp_table = NULL;
>> + }
>
> As I mentioned above, it it wrong to call dev_pm_opp_put_regulators()
> after removing the opp_table by dev_pm_opp_of_remove_table().
>
>> +
>> err_clk:
>> clk_disable_unprepare(bus->clk);
>>
>> @@ -386,6 +337,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
>> struct exynos_bus *bus;
>> int ret, max_state;
>> unsigned long min_freq, max_freq;
>> + bool passive = false;
>>
>> if (!np) {
>> dev_err(dev, "failed to find devicetree node\n");
>> @@ -395,12 +347,18 @@ static int exynos_bus_probe(struct platform_device *pdev)
>> bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
>> if (!bus)
>> return -ENOMEM;
>> +
>> mutex_init(&bus->lock);
>> bus->dev = &pdev->dev;
>> platform_set_drvdata(pdev, bus);
>> + node = of_parse_phandle(dev->of_node, "devfreq", 0);
>> + if (node) {
>> + of_node_put(node);
>> + passive = true;
>> + }
>>
>> /* Parse the device-tree to get the resource information */
>> - ret = exynos_bus_parse_of(np, bus);
>> + ret = exynos_bus_parse_of(np, bus, passive);
>> if (ret < 0)
>> return ret;
>>
>> @@ -410,13 +368,10 @@ static int exynos_bus_probe(struct platform_device *pdev)
>> goto err;
>> }
>>
>> - node = of_parse_phandle(dev->of_node, "devfreq", 0);
>> - if (node) {
>> - of_node_put(node);
>> + if (passive)
>> goto passive;
>> - } else {
>> - ret = exynos_bus_parent_parse_of(np, bus);
>> - }
>> +
>> + ret = exynos_bus_parent_parse_of(np, bus);
>>
>
> Remove unneeded blank line.
>
>> if (ret < 0)
>> goto err;
>> @@ -509,6 +464,11 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>
>> err:
>> dev_pm_opp_of_remove_table(dev);
>> + if (bus->opp_table) {
>> + dev_pm_opp_put_regulators(bus->opp_table);
>> + bus->opp_table = NULL;
>> + }
>> +
>
> ditto.
> Have to disable regulator after disabling the clock
> to prevent the h/w fault.
>
> I think that you should call them with following sequence:
>
> clk_disable_unprepare(bus->clk);
> if (bus->opp_table)
> dev_pm_opp_put_regulators(bus->opp_table);
> dev_pm_opp_of_remove_table(dev);
>
>> clk_disable_unprepare(bus->clk);
>>
>> return ret;
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
^ permalink raw reply
* Re: [PATCH] ARM: dts: at91: Avoid colliding 'display' node and property names
From: Alexandre Belloni @ 2019-07-16 10:15 UTC (permalink / raw)
To: Rob Herring
Cc: devicetree, Ludovic Desroches,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <CAL_JsqK0piWGQBeqcOceF=fSX4vSW7_vyv0qAAxz-bg25qEVow@mail.gmail.com>
On 15/07/2019 15:45:24-0600, Rob Herring wrote:
> On Mon, May 20, 2019 at 8:58 AM Alexandre Belloni
> <alexandre.belloni@bootlin.com> wrote:
> >
> > On 16/05/2019 17:56:14-0500, Rob Herring wrote:
> > > While properties and child nodes with the same name are valid DT, the
> > > practice is not encouraged.
> >
> > I don't see anything mentioning that in the devicetree specification. I
> > think this is something you should add if you don't want that to happen
> > again.
>
> I suppose, but I prefer tools to enforce it.
>
> >
> > > Furthermore, the collision is problematic for
> > > YAML encoded DT. Let's just avoid the issue and rename the nodes.
> > >
> >
> > Or maybe you should fix the tool ;)
>
> You mean the YAML and JSON specifications because the problem is it is
> not valid YAML? (I think YAML allowed it at one time, but it is
> deprecated) The only way to fix it in the tool would be to define some
> way to handle the collision like renaming properties and then undoing
> that.
>
> > Do you plan to enforce it at some point? How close are you?
>
> Soon as this patch is merged. There's a switch in parsing tools to
> disallow the collision, so it will be an error instead of a warning.
>
> > > Cc: Nicolas Ferre <nicolas.ferre@microchip.com>
> > > Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
> > > Cc: Ludovic Desroches <ludovic.desroches@microchip.com>
> > > Cc: linux-arm-kernel@lists.infradead.org
> > > Signed-off-by: Rob Herring <robh@kernel.org>
> >
> > Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
>
> Is someone going to apply this?
>
I'll apply it for the next cycle, sorry about that.
--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* [v5,1/2] rtc/fsl: add FTM alarm driver as the wakeup source
From: Biwen Li @ 2019-07-16 10:16 UTC (permalink / raw)
To: a.zummo, alexandre.belloni, leoyang.li, robh+dt
Cc: linux-rtc, linux-kernel, xiaobo.xie, jiafei.pan, ran.wang_1,
mark.rutland, devicetree, Biwen Li
For the paltforms including LS1012A, LS1021A, LS1028A, LS1043A,
LS1046A, LS1088A, LS208xA that has the FlexTimer
module, implementing alarm functions within RTC subsystem
to wakeup the system when system going to sleep (work with RCPM driver).
Signed-off-by: Biwen Li <biwen.li@nxp.com>
---
Change in v5:
- replace devm_rtc_device_register with devm_rtc_allocate_device
and rtc_register_device
Change in v4:
- clean code
- correct requesting irq
- register as a regular RTC driver
- change return value of ftm_rtc_set_alarm from -EINVAL to -ERANGE
- replace pr_err with dev_err
- sort alphabetically
- auto select RCPM driver
- correct UTC time in ftm_rtc_read_time
Change in v3:
- add some comments about clock source and errata
- adjust format
- replace endian with big_endian of struct ftm_rtc
- remove compatible "fsl,ftm-alarm"
Change in v2:
- remove code about setting rcpm
drivers/rtc/Kconfig | 15 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-fsl-ftm-alarm.c | 336 ++++++++++++++++++++++++++++++++
3 files changed, 352 insertions(+)
create mode 100644 drivers/rtc/rtc-fsl-ftm-alarm.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5b9b2eec1435..ac6728ba7895 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1322,6 +1322,21 @@ config RTC_DRV_IMXDI
This driver can also be built as a module, if so, the module
will be called "rtc-imxdi".
+config RTC_DRV_FSL_FTM_ALARM
+ tristate "Freescale FlexTimer alarm timer"
+ depends on ARCH_LAYERSCAPE
+ select FSL_RCPM
+ default y
+ help
+ For the FlexTimer in LS1012A, LS1021A, LS1028A, LS1043A, LS1046A,
+ LS1088A, LS208xA, we can use FTM as the wakeup source.
+
+ Say y here to enable FTM alarm support. The FTM alarm provides
+ alarm functions for wakeup system from deep sleep.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-fsl-ftm-alarm".
+
config RTC_DRV_MESON
tristate "Amlogic Meson RTC"
depends on (ARM && ARCH_MESON) || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 84deeddc8f14..5b1fa1d3a7e8 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
+obj-$(CONFIG_RTC_DRV_FSL_FTM_ALARM) += rtc-fsl-ftm-alarm.o
obj-$(CONFIG_RTC_DRV_FTRTC010) += rtc-ftrtc010.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o
diff --git a/drivers/rtc/rtc-fsl-ftm-alarm.c b/drivers/rtc/rtc-fsl-ftm-alarm.c
new file mode 100644
index 000000000000..4f7259c2d6a3
--- /dev/null
+++ b/drivers/rtc/rtc-fsl-ftm-alarm.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Freescale FlexTimer Module (FTM) alarm device driver.
+ *
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ * Copyright 2019 NXP
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/fsl/ftm.h>
+#include <linux/rtc.h>
+#include <linux/time.h>
+
+#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_MASK_SHIFT)
+
+/*
+ * Select Fixed frequency clock (32KHz) as clock source
+ * of FlexTimer Module
+ */
+#define FTM_SC_CLKS_FIXED_FREQ 0x02
+#define FIXED_FREQ_CLK 32000
+
+/* Select 128 (2^7) as divider factor */
+#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
+
+/* Maximum counter value in FlexTimer's CNT registers */
+#define MAX_COUNT_VAL 0xffff
+
+struct ftm_rtc {
+ struct rtc_device *rtc_dev;
+ void __iomem *base;
+ bool big_endian;
+ u32 alarm_freq;
+};
+
+static inline u32 rtc_readl(struct ftm_rtc *dev, u32 reg)
+{
+ if (dev->big_endian)
+ return ioread32be(dev->base + reg);
+ else
+ return ioread32(dev->base + reg);
+}
+
+static inline void rtc_writel(struct ftm_rtc *dev, u32 reg, u32 val)
+{
+ if (dev->big_endian)
+ iowrite32be(val, dev->base + reg);
+ else
+ iowrite32(val, dev->base + reg);
+}
+
+static inline void ftm_counter_enable(struct ftm_rtc *rtc)
+{
+ u32 val;
+
+ /* select and enable counter clock source */
+ val = rtc_readl(rtc, FTM_SC);
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
+ val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
+ rtc_writel(rtc, FTM_SC, val);
+}
+
+static inline void ftm_counter_disable(struct ftm_rtc *rtc)
+{
+ u32 val;
+
+ /* disable counter clock source */
+ val = rtc_readl(rtc, FTM_SC);
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
+ rtc_writel(rtc, FTM_SC, val);
+}
+
+static inline void ftm_irq_acknowledge(struct ftm_rtc *rtc)
+{
+ unsigned int timeout = 100;
+
+ /*
+ *Fix errata A-007728 for flextimer
+ * If the FTM counter reaches the FTM_MOD value between
+ * the reading of the TOF bit and the writing of 0 to
+ * the TOF bit, the process of clearing the TOF bit
+ * does not work as expected when FTMx_CONF[NUMTOF] != 0
+ * and the current TOF count is less than FTMx_CONF[NUMTOF].
+ * If the above condition is met, the TOF bit remains set.
+ * If the TOF interrupt is enabled (FTMx_SC[TOIE] = 1),the
+ * TOF interrupt also remains asserted.
+ *
+ * Above is the errata discription
+ *
+ * In one word: software clearing TOF bit not works when
+ * FTMx_CONF[NUMTOF] was seted as nonzero and FTM counter
+ * reaches the FTM_MOD value.
+ *
+ * The workaround is clearing TOF bit until it works
+ * (FTM counter doesn't always reache the FTM_MOD anyway),
+ * which may cost some cycles.
+ */
+ while ((FTM_SC_TOF & rtc_readl(rtc, FTM_SC)) && timeout--)
+ rtc_writel(rtc, FTM_SC, rtc_readl(rtc, FTM_SC) & (~FTM_SC_TOF));
+}
+
+static inline void ftm_irq_enable(struct ftm_rtc *rtc)
+{
+ u32 val;
+
+ val = rtc_readl(rtc, FTM_SC);
+ val |= FTM_SC_TOIE;
+ rtc_writel(rtc, FTM_SC, val);
+}
+
+static inline void ftm_irq_disable(struct ftm_rtc *rtc)
+{
+ u32 val;
+
+ val = rtc_readl(rtc, FTM_SC);
+ val &= ~FTM_SC_TOIE;
+ rtc_writel(rtc, FTM_SC, val);
+}
+
+static inline void ftm_reset_counter(struct ftm_rtc *rtc)
+{
+ /*
+ * The CNT register contains the FTM counter value.
+ * Reset clears the CNT register. Writing any value to COUNT
+ * updates the counter with its initial value, CNTIN.
+ */
+ rtc_writel(rtc, FTM_CNT, 0x00);
+}
+
+static void ftm_clean_alarm(struct ftm_rtc *rtc)
+{
+ ftm_counter_disable(rtc);
+
+ rtc_writel(rtc, FTM_CNTIN, 0x00);
+ rtc_writel(rtc, FTM_MOD, ~0U);
+
+ ftm_reset_counter(rtc);
+}
+
+static irqreturn_t ftm_rtc_alarm_interrupt(int irq, void *dev)
+{
+ struct ftm_rtc *rtc = dev;
+
+ ftm_irq_acknowledge(rtc);
+ ftm_irq_disable(rtc);
+ ftm_clean_alarm(rtc);
+
+ return IRQ_HANDLED;
+}
+
+static int ftm_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct ftm_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ ftm_irq_enable(rtc);
+ else
+ ftm_irq_disable(rtc);
+
+ return 0;
+}
+
+/*
+ * Note:
+ * The function is not really getting time from the RTC
+ * since FlexTimer is not a RTC device, but we need to
+ * get time to setup alarm, so we are using system time
+ * for now.
+ */
+static int ftm_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct timespec64 ts64;
+
+ ktime_get_real_ts64(&ts64);
+ rtc_time_to_tm(ts64.tv_sec, tm);
+
+ return 0;
+}
+
+static int ftm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ return 0;
+}
+
+/*
+ * 1. Select fixed frequency clock (32KHz) as clock source;
+ * 2. Select 128 (2^7) as divider factor;
+ * So clock is 250 Hz (32KHz/128).
+ *
+ * 3. FlexTimer's CNT register is a 32bit register,
+ * but the register's 16 bit as counter value,it's other 16 bit
+ * is reserved.So minimum counter value is 0x0,maximum counter
+ * value is 0xffff.
+ * So max alarm value is 262 (65536 / 250) seconds
+ */
+static int ftm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rtc_time tm;
+ unsigned long now, alm_time, cycle;
+ struct ftm_rtc *rtc = dev_get_drvdata(dev);
+
+ ftm_rtc_read_time(dev, &tm);
+ rtc_tm_to_time(&tm, &now);
+ rtc_tm_to_time(&alm->time, &alm_time);
+
+ ftm_clean_alarm(rtc);
+ cycle = (alm_time - now) * rtc->alarm_freq;
+ if (cycle > MAX_COUNT_VAL) {
+ pr_err("Out of alarm range {0~262} seconds.\n");
+ return -ERANGE;
+ }
+
+ ftm_irq_disable(rtc);
+
+ /*
+ * The counter increments until the value of MOD is reached,
+ * at which point the counter is reloaded with the value of CNTIN.
+ * The TOF (the overflow flag) bit is set when the FTM counter
+ * changes from MOD to CNTIN. So we should using the cycle - 1.
+ */
+ rtc_writel(rtc, FTM_MOD, cycle - 1);
+
+ ftm_counter_enable(rtc);
+ ftm_irq_enable(rtc);
+
+ return 0;
+
+}
+
+static const struct rtc_class_ops ftm_rtc_ops = {
+ .read_time = ftm_rtc_read_time,
+ .read_alarm = ftm_rtc_read_alarm,
+ .set_alarm = ftm_rtc_set_alarm,
+ .alarm_irq_enable = ftm_rtc_alarm_irq_enable,
+};
+
+static int ftm_rtc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *r;
+ int irq;
+ int ret;
+ struct ftm_rtc *rtc;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (unlikely(!rtc)) {
+ dev_err(&pdev->dev, "cannot alloc memery for rtc\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "cannot get resource for rtc\n");
+ return -ENODEV;
+ }
+
+ rtc->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(rtc->base)) {
+ dev_err(&pdev->dev, "cannot ioremap resource for rtc\n");
+ return PTR_ERR(rtc->base);
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "unable to get IRQ from DT, %d\n", irq);
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, ftm_rtc_alarm_interrupt,
+ IRQF_NO_SUSPEND, dev_name(&pdev->dev), rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ rtc->big_endian = of_property_read_bool(np, "big-endian");
+ rtc->alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
+ rtc->rtc_dev->ops = &ftm_rtc_ops;
+
+ device_init_wakeup(&pdev->dev, true);
+
+ ret = rtc_register_device(rtc->rtc_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't register rtc device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ftm_rtc_match[] = {
+ { .compatible = "fsl,ls1012a-ftm-alarm", },
+ { .compatible = "fsl,ls1021a-ftm-alarm", },
+ { .compatible = "fsl,ls1043a-ftm-alarm", },
+ { .compatible = "fsl,ls1046a-ftm-alarm", },
+ { .compatible = "fsl,ls1088a-ftm-alarm", },
+ { .compatible = "fsl,ls208xa-ftm-alarm", },
+ { .compatible = "fsl,ls1028a-ftm-alarm", },
+ { },
+};
+
+static struct platform_driver ftm_rtc_driver = {
+ .probe = ftm_rtc_probe,
+ .driver = {
+ .name = "ftm-alarm",
+ .of_match_table = ftm_rtc_match,
+ },
+};
+
+static int __init ftm_alarm_init(void)
+{
+ return platform_driver_register(&ftm_rtc_driver);
+}
+
+device_initcall(ftm_alarm_init);
+
+MODULE_DESCRIPTION("NXP/Freescale FlexTimer alarm driver");
+MODULE_AUTHOR("Biwen Li <biwen.li@nxp.com>");
+MODULE_LICENSE("GPL");
--
2.17.1
^ permalink raw reply related
* [v5,2/2] Documentation: dt: binding: rtc: add binding for ftm alarm driver
From: Biwen Li @ 2019-07-16 10:16 UTC (permalink / raw)
To: a.zummo, alexandre.belloni, leoyang.li, robh+dt
Cc: linux-rtc, linux-kernel, xiaobo.xie, jiafei.pan, ran.wang_1,
mark.rutland, devicetree, Biwen Li
In-Reply-To: <20190716101655.47418-1-biwen.li@nxp.com>
The patch adds binding for ftm alarm driver
Signed-off-by: Biwen Li <biwen.li@nxp.com>
---
Change in v5:
- None
Change in v4:
- add note about dts and kernel options
- add aliases in example
Change in v3:
- remove reg-names property
- correct cells number
Change in v2:
- replace ls1043a with ls1088a as example
- add rcpm node and fsl,rcpm-wakeup property
.../bindings/rtc/rtc-fsl-ftm-alarm.txt | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/rtc-fsl-ftm-alarm.txt
diff --git a/Documentation/devicetree/bindings/rtc/rtc-fsl-ftm-alarm.txt b/Documentation/devicetree/bindings/rtc/rtc-fsl-ftm-alarm.txt
new file mode 100644
index 000000000000..fb018065406c
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rtc-fsl-ftm-alarm.txt
@@ -0,0 +1,49 @@
+Freescale FlexTimer Module (FTM) Alarm
+
+Note:
+- The driver depends on RCPM driver
+ to wake up system in sleep.
+- Need stop using RTC_HCTOSYS or use the DT aliases
+ to ensure the driver is not used as the primary RTC.
+ (Select DT aliases defaultly)
+
+Required properties:
+- compatible : Should be "fsl,<chip>-ftm-alarm", the
+ supported chips include
+ "fsl,ls1012a-ftm-alarm"
+ "fsl,ls1021a-ftm-alarm"
+ "fsl,ls1028a-ftm-alarm"
+ "fsl,ls1043a-ftm-alarm"
+ "fsl,ls1046a-ftm-alarm"
+ "fsl,ls1088a-ftm-alarm"
+ "fsl,ls208xa-ftm-alarm"
+- reg : Specifies base physical address and size of the register sets for the
+ FlexTimer Module and base physical address of IP Powerdown Exception Control
+ Register.
+- interrupts : Should be the FlexTimer Module interrupt.
+- fsl,rcpm-wakeup property and rcpm node : Please refer
+ Documentation/devicetree/bindings/soc/fsl/rcpm.txt
+
+Optional properties:
+- big-endian: If the host controller is big-endian mode, specify this property.
+ The default endian mode is little-endian.
+
+Example:
+aliases {
+ ...
+ rtc1 = ftm_alarm0; /* Use flextimer alarm driver as /dev/rtc1 */
+ ...
+};
+
+rcpm: rcpm@1e34040 {
+ compatible = "fsl,ls1088a-rcpm", "fsl,qoriq-rcpm-2.1+";
+ reg = <0x0 0x1e34040 0x0 0x18>;
+ fsl,#rcpm-wakeup-cells = <6>;
+};
+
+ftm_alarm0: timer@2800000 {
+ compatible = "fsl,ls1088a-ftm-alarm";
+ reg = <0x0 0x2800000 0x0 0x10000>;
+ fsl,rcpm-wakeup = <&rcpm 0x0 0x0 0x0 0x0 0x4000 0x0>;
+ interrupts = <0 44 4>;
+};
--
2.17.1
^ permalink raw reply related
* Re: [PATCH v1 08/50] clk: samsung: change aclk266_isp clocks definitions Exynos5420
From: Chanwoo Choi @ 2019-07-16 10:22 UTC (permalink / raw)
To: Lukasz Luba, devicetree, linux-kernel, linux-arm-kernel,
linux-samsung-soc, linux-clk
Cc: mturquette, sboyd, b.zolnierkie, krzk, kgene, mark.rutland,
robh+dt, kyungmin.park, a.hajda, m.szyprowski, s.nawrocki,
myungjoo.ham
In-Reply-To: <20190715124417.4787-9-l.luba@partner.samsung.com>
Hi,
On 19. 7. 15. 오후 9:43, Lukasz Luba wrote:
> The ACLK266_ISP has different topology in Exynos5420 and 5422/5800. In
> Exynos5420 this clock does not have dedicated MUX which chooses PLL instead
> it takes the clock from 2-level mux from ACLK266. In Exynos5422 there is a
> dedicated clock tree and the PLL can be chosen. The patch adds needed
> MUXes in the exynos5800_mux_cloks, updates exynos5x_mux_clks and
> exynos5420_mux_clks properly. It also adds IDs to mange these clocks from
> DT.
>
> Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
> ---
> drivers/clk/samsung/clk-exynos5420.c | 17 +++++++++++++++--
> 1 file changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
> index 9d54856dbcda..5f251279b4c8 100644
> --- a/drivers/clk/samsung/clk-exynos5420.c
> +++ b/drivers/clk/samsung/clk-exynos5420.c
> @@ -432,6 +432,10 @@ PNAME(mout_group16_5800_p) = { "dout_osc_div", "mout_mau_epll_clk" };
> PNAME(mout_mx_mspll_ccore_phy_p) = { "sclk_bpll", "mout_sclk_dpll",
> "mout_sclk_mpll", "ff_dout_spll2",
> "mout_sclk_spll", "mout_sclk_epll"};
> +PNAME(mout_group17_5800_p) = { "dout_aclk266_isp", "dout_sclk_sw" };
> +PNAME(mout_group18_5800_p) = { "dout_osc_div", "mout_sw_aclk266_isp" };
> +PNAME(mout_group19_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll",
> + "mout_sclk_mpll", "mout_sclk_ipll"};
>
> /* fixed rate clocks generated outside the soc */
> static struct samsung_fixed_rate_clock
> @@ -494,6 +498,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = {
> MUX(CLK_SCLK_BPLL, "sclk_bpll", mout_bpll_p, SRC_TOP7, 24, 1),
> MUX(0, "mout_epll2", mout_epll2_5800_p, SRC_TOP7, 28, 1),
>
> + MUX(CLK_MOUT_ACLK266_ISP, "mout_aclk266_isp", mout_group19_5800_p,
> + SRC_TOP8, 12, 2),
> MUX(0, "mout_aclk550_cam", mout_group3_5800_p, SRC_TOP8, 16, 3),
> MUX(0, "mout_aclkfl1_550_cam", mout_group3_5800_p, SRC_TOP8, 20, 3),
> MUX(0, "mout_aclk432_cam", mout_group6_5800_p, SRC_TOP8, 24, 2),
> @@ -501,6 +507,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = {
>
> MUX_F(CLK_MOUT_USER_MAU_EPLL, "mout_user_mau_epll", mout_group16_5800_p,
> SRC_TOP9, 8, 1, CLK_SET_RATE_PARENT, 0),
> + MUX(CLK_MOUT_USER_ACLK266_ISP, "mout_user_aclk266_isp",
> + mout_group18_5800_p, SRC_TOP9, 12, 1),
> MUX(0, "mout_user_aclk550_cam", mout_group15_5800_p,
> SRC_TOP9, 16, 1),
> MUX(0, "mout_user_aclkfl1_550_cam", mout_group13_5800_p,
> @@ -510,6 +518,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = {
> MUX(0, "mout_user_aclk432_scaler", mout_group9_5800_p,
> SRC_TOP9, 28, 1),
>
> + MUX(CLK_MOUT_SW_ACLK266_ISP, "mout_sw_aclk266_isp", mout_group17_5800_p,
> + SRC_TOP13, 12, 1),
> MUX(0, "mout_sw_aclk550_cam", mout_group14_5800_p, SRC_TOP13, 16, 1),
> MUX(0, "mout_sw_aclkfl1_550_cam", mout_group12_5800_p,
> SRC_TOP13, 20, 1),
> @@ -524,6 +534,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = {
> static const struct samsung_div_clock exynos5800_div_clks[] __initconst = {
> DIV(CLK_DOUT_ACLK400_WCORE, "dout_aclk400_wcore",
> "mout_aclk400_wcore", DIV_TOP0, 16, 3),
> + DIV(CLK_DOUT_ACLK266_ISP, "dout_aclk266_isp", "mout_aclk266_isp",
> + DIV_TOP8, 12, 3),
> DIV(0, "dout_aclk550_cam", "mout_aclk550_cam",
> DIV_TOP8, 16, 3),
> DIV(0, "dout_aclkfl1_550_cam", "mout_aclkfl1_550_cam",
> @@ -574,6 +586,9 @@ static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = {
> MUX(0, "mout_aclk300_disp1", mout_group1_p, SRC_TOP2, 24, 2),
> MUX(0, "mout_aclk300_gscl", mout_group1_p, SRC_TOP2, 28, 2),
>
> + MUX(CLK_MOUT_USER_ACLK266_ISP, "mout_user_aclk266_isp",
> + mout_user_aclk266_isp_p, SRC_TOP4, 16, 1),
> +
> MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore",
> mout_group5_5800_p, SRC_TOP7, 16, 2),
> MUX_F(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2,
> @@ -641,8 +656,6 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = {
> SRC_TOP4, 8, 1),
> MUX(0, "mout_user_aclk333_432_isp0", mout_user_aclk333_432_isp0_p,
> SRC_TOP4, 12, 1),
> - MUX(0, "mout_user_aclk266_isp", mout_user_aclk266_isp_p,
> - SRC_TOP4, 16, 1),
> MUX(0, "mout_user_aclk266", mout_user_aclk266_p, SRC_TOP4, 20, 1),
> MUX(0, "mout_user_aclk166", mout_user_aclk166_p, SRC_TOP4, 24, 1),
> MUX(CLK_MOUT_USER_ACLK333, "mout_user_aclk333", mout_user_aclk333_p,
>
I checked it on Exynos5420/5422 TRM.
Acked-by: Chanwoo Choi <cw00.choi@samsung.com>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
^ permalink raw reply
* Re: [PATCH v2 3/4] ARM: dts: exynos: add initial data for coupled regulators for Exynos5422/5800
From: Bartlomiej Zolnierkiewicz @ 2019-07-16 10:30 UTC (permalink / raw)
To: Krzysztof Kozlowski, Kamil Konieczny
Cc: Marek Szyprowski, Chanwoo Choi, Kukjin Kim, Kyungmin Park,
Mark Rutland, MyungJoo Ham, Nishanth Menon, Rob Herring,
Stephen Boyd, Viresh Kumar, devicetree, linux-arm-kernel,
linux-kernel, linux-pm, linux-samsung-soc@vger.kernel.org
In-Reply-To: <CAJKOXPd6a5aLf1CEhx9m7khPQOwruSuA22efkJb41BsaWXjM3A@mail.gmail.com>
On 7/16/19 11:22 AM, Krzysztof Kozlowski wrote:
> On Mon, 15 Jul 2019 at 14:04, Kamil Konieczny
> <k.konieczny@partner.samsung.com> wrote:
>>
>> Declare Exynos5422/5800 voltage ranges for opp points for big cpu core and
>> bus wcore and couple their voltage supllies as vdd_arm and vdd_int should
>> be in 300mV range.
>>
>> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
>> Signed-off-by: Kamil Konieczny <k.konieczny@partner.samsung.com>
>
> This one was previously from Marek, now it is from you. Any changes here?
Hmmm, it seems that "From:" tag somehow got lost in v2 compared to v1?
Also the note about adding patch description (which was the only update to
original Marek's patch IIRC) should be added, something like:
...
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
[k.konieczny: add missing patch description]
Signed-off-by: Kamil Konieczny <k.konieczny@partner.samsung.com>
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
^ permalink raw reply
* Re: [PATCH v2 2/4] devfreq: exynos-bus: convert to use dev_pm_opp_set_rate()
From: Chanwoo Choi @ 2019-07-16 10:33 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz
Cc: Kamil Konieczny, Marek Szyprowski, Krzysztof Kozlowski,
Kukjin Kim, Kyungmin Park, Mark Rutland, MyungJoo Ham,
Nishanth Menon, Rob Herring, Stephen Boyd, Viresh Kumar,
devicetree, linux-arm-kernel, linux-kernel, linux-pm,
linux-samsung-soc
In-Reply-To: <3d1687b7-4825-ad82-2706-a712c30e530b@samsung.com>
Hi Bartlomiej,
On 19. 7. 16. 오후 7:13, Bartlomiej Zolnierkiewicz wrote:
>
> Hi Chanwoo,
>
> On 7/16/19 5:56 AM, Chanwoo Choi wrote:
>> Hi Kamil,
>>
>> Looks good to me. But, this patch has some issue.
>> I added the detailed reviews.
>>
>> I recommend that you make the separate patches as following
>> in order to clarify the role of which apply the dev_pm_opp_* function.
>>
>> First patch,
>> Need to consolidate the following two function into one function.
>> because the original exynos-bus.c has the problem that the regulator
>> of parent devfreq device have to be enabled before enabling the clock.
>> This issue did not happen because bootloader enables the bus-related
>> regulators before kernel booting.
>> - exynos_bus_parse_of()
>> - exynos_bus_parent_parse_of()
>>> Second patch,
>> Apply dev_pm_opp_set_regulators() and dev_pm_opp_set_rate()
>>
>>
>> On 19. 7. 15. 오후 9:04, Kamil Konieczny wrote:
>>> Reuse opp core code for setting bus clock and voltage. As a side
>>> effect this allow useage of coupled regulators feature (required
>>> for boards using Exynos5422/5800 SoCs) because dev_pm_opp_set_rate()
>>> uses regulator_set_voltage_triplet() for setting regulator voltage
>>> while the old code used regulator_set_voltage_tol() with fixed
>>> tolerance. This patch also removes no longer needed parsing of DT
>>> property "exynos,voltage-tolerance" (no Exynos devfreq DT node uses
>>> it).
>>>
>>> Signed-off-by: Kamil Konieczny <k.konieczny@partner.samsung.com>
>>> ---
>>> drivers/devfreq/exynos-bus.c | 172 ++++++++++++++---------------------
>>> 1 file changed, 66 insertions(+), 106 deletions(-)
>>>
>>> diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
>>> index 486cc5b422f1..7fc4f76bd848 100644
>>> --- a/drivers/devfreq/exynos-bus.c
>>> +++ b/drivers/devfreq/exynos-bus.c
>>> @@ -25,7 +25,6 @@
>>> #include <linux/slab.h>
>>>
>>> #define DEFAULT_SATURATION_RATIO 40
>>> -#define DEFAULT_VOLTAGE_TOLERANCE 2
>>>
>>> struct exynos_bus {
>>> struct device *dev;
>>> @@ -37,9 +36,9 @@ struct exynos_bus {
>>>
>>> unsigned long curr_freq;
>>>
>>> - struct regulator *regulator;
>>> + struct opp_table *opp_table;
>>> +
>>> struct clk *clk;
>>> - unsigned int voltage_tolerance;
>>> unsigned int ratio;
>>> };
>>>
>>> @@ -99,56 +98,25 @@ static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
>>> {
>>> struct exynos_bus *bus = dev_get_drvdata(dev);
>>> struct dev_pm_opp *new_opp;
>>> - unsigned long old_freq, new_freq, new_volt, tol;
>>> int ret = 0;
>>> -
>>> - /* Get new opp-bus instance according to new bus clock */
>>> + /*
>>> + * New frequency for bus may not be exactly matched to opp, adjust
>>> + * *freq to correct value.
>>> + */
>>
>> You better to change this comment with following styles
>> to keep the consistency:
>>
>> /* Get correct frequency for bus ... */
>>
>>> new_opp = devfreq_recommended_opp(dev, freq, flags);
>>> if (IS_ERR(new_opp)) {
>>> dev_err(dev, "failed to get recommended opp instance\n");
>>> return PTR_ERR(new_opp);
>>> }
>>>
>>> - new_freq = dev_pm_opp_get_freq(new_opp);
>>> - new_volt = dev_pm_opp_get_voltage(new_opp);
>>> dev_pm_opp_put(new_opp);
>>>
>>> - old_freq = bus->curr_freq;
>>> -
>>> - if (old_freq == new_freq)
>>> - return 0;
>>> - tol = new_volt * bus->voltage_tolerance / 100;
>>> -
>>> /* Change voltage and frequency according to new OPP level */
>>> mutex_lock(&bus->lock);
>>> + ret = dev_pm_opp_set_rate(dev, *freq);
>>> + if (!ret)
>>> + bus->curr_freq = *freq;
>>
>> Have to print the error log if ret has minus error value.
>
> dev_pm_opp_set_rate() should print the error message on all
> errors so wouldn't printing the error log also here be superfluous?
>
> [ Please also note that the other user of dev_pm_opp_set_rate()
> (cpufreq-dt cpufreq driver) doesn't do this. ]
OK. Thanks for the explanation.
>
>> Modify it as following:
>>
>> if (ret < 0) {
>> dev_err(dev, "failed to set bus rate\n");
>> goto err:
>> }
>> bus->curr_freq = *freq;
>>
>> err:
>> mutex_unlock(&bus->lock);
>>
>> return ret;
>>
>>>
>>> - if (old_freq < new_freq) {
>>> - ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
>>> - if (ret < 0) {
>>> - dev_err(bus->dev, "failed to set voltage\n");
>>> - goto out;
>>> - }
>>> - }
>>> -
>>> - ret = clk_set_rate(bus->clk, new_freq);
>>> - if (ret < 0) {
>>> - dev_err(dev, "failed to change clock of bus\n");
>>> - clk_set_rate(bus->clk, old_freq);
>>> - goto out;
>>> - }
>>> -
>>> - if (old_freq > new_freq) {
>>> - ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
>>> - if (ret < 0) {
>>> - dev_err(bus->dev, "failed to set voltage\n");
>>> - goto out;
>>> - }
>>> - }
>>> - bus->curr_freq = new_freq;
>>> -
>>> - dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
>>> - old_freq, new_freq, clk_get_rate(bus->clk));
>>> -out:
>>> mutex_unlock(&bus->lock);
>>>
>>> return ret;
>>> @@ -194,10 +162,11 @@ static void exynos_bus_exit(struct device *dev)
>>> if (ret < 0)
>>> dev_warn(dev, "failed to disable the devfreq-event devices\n");
>>>
>>> - if (bus->regulator)
>>> - regulator_disable(bus->regulator);
>>> + if (bus->opp_table)
>>> + dev_pm_opp_put_regulators(bus->opp_table);
>>
>> Have to disable regulator after disabling the clock
>> to prevent the h/w fault.
>>
>> I think that you should call them with following sequence:
>>
>> clk_disable_unprepare(bus->clk);
>> if (bus->opp_table)
>> dev_pm_opp_put_regulators(bus->opp_table);
>> dev_pm_opp_of_remove_table(dev);
>>
>>>
>>> dev_pm_opp_of_remove_table(dev);
>>> +
>>> clk_disable_unprepare(bus->clk);
>>> }
>>>
>>> @@ -209,39 +178,26 @@ static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
>>> {
>>> struct exynos_bus *bus = dev_get_drvdata(dev);
>>> struct dev_pm_opp *new_opp;
>>> - unsigned long old_freq, new_freq;
>>> - int ret = 0;
>>> + int ret;
>>>
>>> - /* Get new opp-bus instance according to new bus clock */
>>> + /*
>>> + * New frequency for bus may not be exactly matched to opp, adjust
>>> + * *freq to correct value.
>>> + */
>>
>> You better to change this comment with following styles
>> to keep the consistency:
>>
>> /* Get correct frequency for bus ... */
>>
>>> new_opp = devfreq_recommended_opp(dev, freq, flags);
>>> if (IS_ERR(new_opp)) {
>>> dev_err(dev, "failed to get recommended opp instance\n");
>>> return PTR_ERR(new_opp);
>>> }
>>>
>>> - new_freq = dev_pm_opp_get_freq(new_opp);
>>> dev_pm_opp_put(new_opp);
>>>
>>> - old_freq = bus->curr_freq;
>>> -
>>> - if (old_freq == new_freq)
>>> - return 0;
>>> -
>>> /* Change the frequency according to new OPP level */
>>> mutex_lock(&bus->lock);
>>> + ret = dev_pm_opp_set_rate(dev, *freq);
>>> + if (!ret)
>>> + bus->curr_freq = *freq;
>>
>> ditto. Have to print the error log, check above comment.
>>
>>>
>>> - ret = clk_set_rate(bus->clk, new_freq);
>>> - if (ret < 0) {
>>> - dev_err(dev, "failed to set the clock of bus\n");
>>> - goto out;
>>> - }
>>> -
>>> - *freq = new_freq;
>>> - bus->curr_freq = new_freq;
>>> -
>>> - dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
>>> - old_freq, new_freq, clk_get_rate(bus->clk));
>>> -out:
>>> mutex_unlock(&bus->lock);
>>>
>>> return ret;
>>> @@ -259,20 +215,7 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>>> struct exynos_bus *bus)
>>> {
>>> struct device *dev = bus->dev;
>>> - int i, ret, count, size;
>>> -
>>> - /* Get the regulator to provide each bus with the power */
>>> - bus->regulator = devm_regulator_get(dev, "vdd");
>>> - if (IS_ERR(bus->regulator)) {
>>> - dev_err(dev, "failed to get VDD regulator\n");
>>> - return PTR_ERR(bus->regulator);
>>> - }
>>> -
>>> - ret = regulator_enable(bus->regulator);
>>> - if (ret < 0) {
>>> - dev_err(dev, "failed to enable VDD regulator\n");
>>> - return ret;
>>> - }
>>> + int i, count, size;
>>>
>>> /*
>>> * Get the devfreq-event devices to get the current utilization of
>>> @@ -281,24 +224,20 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>>> count = devfreq_event_get_edev_count(dev);
>>> if (count < 0) {
>>> dev_err(dev, "failed to get the count of devfreq-event dev\n");
>>> - ret = count;
>>> - goto err_regulator;
>>> + return count;
>>> }
>>> +
>>> bus->edev_count = count;
>>>
>>> size = sizeof(*bus->edev) * count;
>>> bus->edev = devm_kzalloc(dev, size, GFP_KERNEL);
>>> - if (!bus->edev) {
>>> - ret = -ENOMEM;
>>> - goto err_regulator;
>>> - }
>>> + if (!bus->edev)
>>> + return -ENOMEM;
>>>
>>> for (i = 0; i < count; i++) {
>>> bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i);
>>> - if (IS_ERR(bus->edev[i])) {
>>> - ret = -EPROBE_DEFER;
>>> - goto err_regulator;
>>> - }
>>> + if (IS_ERR(bus->edev[i]))
>>> + return -EPROBE_DEFER;
>>> }
>>>
>>> /*
>>> @@ -314,22 +253,15 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>>> if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
>>> bus->ratio = DEFAULT_SATURATION_RATIO;
>>>
>>> - if (of_property_read_u32(np, "exynos,voltage-tolerance",
>>> - &bus->voltage_tolerance))
>>> - bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE;
>>> -
>>> return 0;
>>> -
>>> -err_regulator:
>>> - regulator_disable(bus->regulator);
>>> -
>>> - return ret;
>>> }
>>>
>>> static int exynos_bus_parse_of(struct device_node *np,
>>> - struct exynos_bus *bus)
>>> + struct exynos_bus *bus, bool passive)
>>> {
>>> struct device *dev = bus->dev;
>>> + struct opp_table *opp_table;
>>> + const char *vdd = "vdd";
>>> struct dev_pm_opp *opp;
>>> unsigned long rate;
>>> int ret;
>>> @@ -347,11 +279,22 @@ static int exynos_bus_parse_of(struct device_node *np,
>>> return ret;
>>> }
>>>
>>> + if (!passive) {
>>> + opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1);
>>> + if (IS_ERR(opp_table)) {
>>> + ret = PTR_ERR(opp_table);
>>> + dev_err(dev, "failed to set regulators %d\n", ret);
>>> + goto err_clk;/
>>> + }
>>> +
>>> + bus->opp_table = opp_table;
>>> + }
>>
>> This driver has exynos_bus_parent_parse_of() function for parent devfreq device.
>> dev_pm_opp_set_regulators() have to be called in exynos_bus_parent_parse_of()
>> because the regulator is only used by parent devfreq device.
>
> exynos_bus_parse_of() is called for all devfreq devices (including
> parent) and (as you've noticed) the regulator should be enabled before
> enabling clock (which is done in exynos_bus_parse_of()) so adding
> extra argument to exynos_bus_parse_of() (like it is done currently in
> the patch)
I think that this patch has still the problem about call sequence
between clock and regulator as following:
273 ret = clk_prepare_enable(bus->clk);
274 if (ret < 0) {
275 dev_err(dev, "failed to get enable clock\n");
276 return ret;
277 }
278
279 if (!passive) {
280 opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1);
281 if (IS_ERR(opp_table)) {
282 ret = PTR_ERR(opp_table);
283 dev_err(dev, "failed to set regulators %d\n", ret);
284 goto err_clk;
285 }
286
287 bus->opp_table = opp_table;
288 }
makes it possible to do the setup correctly without the need
> of merging both functions into one huge function (which would be more
> difficult to follow than two simpler functions IMHO). Is that approach
> acceptable or do you prefer one big function?
Actually, I don't force to make one function for both
exynos_bus_parse_of() and exynos_bus_parent_parse_of().
If we just keep this code, dev_pm_opp_set_regulators()
should be handled in exynos_bus_parent_parse_of()
because only parent devfreq device controls the regulator.
In order to keep the two functions, maybe have to change
the call the sequence between exynos_bus_parse_of() and
exynos_bus_parent_parse_of().
Once again, I don't force any fixed method. I want to fix them
with correct way.
>
>>> +
>>> /* Get the freq and voltage from OPP table to scale the bus freq */
>>> ret = dev_pm_opp_of_add_table(dev);
>>> if (ret < 0) {
>>> dev_err(dev, "failed to get OPP table\n");
>>> - goto err_clk;
>>> + goto err_regulator;
>>> }
>>>
>>> rate = clk_get_rate(bus->clk);
>>> @@ -362,6 +305,7 @@ static int exynos_bus_parse_of(struct device_node *np,
>>> ret = PTR_ERR(opp);
>>> goto err_opp;
>>> }
>>> +
>>> bus->curr_freq = dev_pm_opp_get_freq(opp);
>>> dev_pm_opp_put(opp);
>>>
>>> @@ -369,6 +313,13 @@ static int exynos_bus_parse_of(struct device_node *np,
>>>
>>> err_opp:
>>> dev_pm_opp_of_remove_table(dev);
>>> +
>>> +err_regulator:
>>> + if (bus->opp_table) {
>>> + dev_pm_opp_put_regulators(bus->opp_table);
>>> + bus->opp_table = NULL;
>>> + }
>>
>> As I mentioned above, it it wrong to call dev_pm_opp_put_regulators()
>> after removing the opp_table by dev_pm_opp_of_remove_table().
>>
>>> +
>>> err_clk:
>>> clk_disable_unprepare(bus->clk);
>>>
>>> @@ -386,6 +337,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>> struct exynos_bus *bus;
>>> int ret, max_state;
>>> unsigned long min_freq, max_freq;
>>> + bool passive = false;
>>>
>>> if (!np) {
>>> dev_err(dev, "failed to find devicetree node\n");
>>> @@ -395,12 +347,18 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>> bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
>>> if (!bus)
>>> return -ENOMEM;
>>> +
>>> mutex_init(&bus->lock);
>>> bus->dev = &pdev->dev;
>>> platform_set_drvdata(pdev, bus);
>>> + node = of_parse_phandle(dev->of_node, "devfreq", 0);
>>> + if (node) {
>>> + of_node_put(node);
>>> + passive = true;
>>> + }
>>>
>>> /* Parse the device-tree to get the resource information */
>>> - ret = exynos_bus_parse_of(np, bus);
>>> + ret = exynos_bus_parse_of(np, bus, passive);
>>> if (ret < 0)
>>> return ret;
>>>
>>> @@ -410,13 +368,10 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>> goto err;
>>> }
>>>
>>> - node = of_parse_phandle(dev->of_node, "devfreq", 0);
>>> - if (node) {
>>> - of_node_put(node);
>>> + if (passive)
>>> goto passive;
>>> - } else {
>>> - ret = exynos_bus_parent_parse_of(np, bus);
>>> - }
>>> +
>>> + ret = exynos_bus_parent_parse_of(np, bus);
>>>
>>
>> Remove unneeded blank line.
>>
>>> if (ret < 0)
>>> goto err;
>>> @@ -509,6 +464,11 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>>
>>> err:
>>> dev_pm_opp_of_remove_table(dev);
>>> + if (bus->opp_table) {
>>> + dev_pm_opp_put_regulators(bus->opp_table);
>>> + bus->opp_table = NULL;
>>> + }
>>> +
>>
>> ditto.
>> Have to disable regulator after disabling the clock
>> to prevent the h/w fault.
>>
>> I think that you should call them with following sequence:
>>
>> clk_disable_unprepare(bus->clk);
>> if (bus->opp_table)
>> dev_pm_opp_put_regulators(bus->opp_table);
>> dev_pm_opp_of_remove_table(dev);
>>
>>> clk_disable_unprepare(bus->clk);
>>>
>>> return ret;
>
> Best regards,
> --
> Bartlomiej Zolnierkiewicz
> Samsung R&D Institute Poland
> Samsung Electronics
>
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
^ permalink raw reply
* Re: [PATCH 11/13] arm64: dts: qcom: qcs404: Add CPR and populate OPP table
From: Viresh Kumar @ 2019-07-16 10:34 UTC (permalink / raw)
To: Niklas Cassel
Cc: Andy Gross, linux-arm-msm, jorge.ramirez-ortiz, sboyd, vireshk,
bjorn.andersson, ulf.hansson, Rob Herring, Mark Rutland,
devicetree, linux-kernel
In-Reply-To: <20190715132405.GA5040@centauri>
On 15-07-19, 15:24, Niklas Cassel wrote:
> This was actually my initial thought when talking to you 6+ months ago.
> However, the problem was that, from the CPR drivers' perspective, it
> only sees the CPR OPP table.
>
>
> So this is the order things are called,
> from qcom-cpufreq-nvmem.c perspective:
>
> 1) dev_pm_opp_set_supported_hw()
>
> 2) dev_pm_opp_attach_genpd() ->
> which results in
> int cpr_pd_attach_dev(struct generic_pm_domain *domain,
> struct device *dev)
> being called.
> This callback is inside the CPR driver, and here we have the
> CPU's (genpd virtual) struct device, and this is where we would like to
> know the opp-hz.
> The problem here is that:
> [ 3.114979] cpr_pd_attach_dev: dev_pm_opp_get_opp_count for dev: genpd:0:cpu0: -19
> [ 3.119610] cpr_pd_attach_dev: dev_pm_opp_get_opp_count for dev: cpu0: 0
> [ 3.126489] cpr_pd_attach_dev: dev_pm_opp_get_opp_count for dev: cpr@b018000: 3
>
> While we have the CPR OPP table in the attach callback, we don't
> have the CPU OPP table, neither in the CPU struct device or the genpd virtual
> struct device.
If you can find CPU's physical number from the virtual device, then
you can do get_cpu_device(X) and then life will be easy ?
> Since we have called dev_pm_opp_attach_genpd(.., .., &virt_devs) which
> attaches an OPP table to the CPU, I would have expected one of them to
> be >= 0.
> Especially since dev_name(virt_devs[0]) == genpd:0:cpu0
>
> I guess it should still be possible to parse the required-opps manually here,
> by iterating the OF nodes, however, we won't be able to use the CPU's struct
> opp_table (which is the nice representation of the OF nodes).
>
> Any suggestions?
--
viresh
^ permalink raw reply
* Re: [RESEND PATCH next v2 0/6] ARM: keystone: update dt and enable cpts support
From: grygorii @ 2019-07-16 10:44 UTC (permalink / raw)
To: santosh.shilimkar, Santosh Shilimkar
Cc: devicetree, Murali Karicheri, Sekhar Nori, linux-kernel,
linux-arm-kernel
In-Reply-To: <2ef8b34e-7a6e-b3e4-90e0-c4e7f16c2e99@oracle.com>
On 06/07/2019 05:18, santosh.shilimkar@oracle.com wrote:
> On 7/5/19 8:12 AM, Grygorii Strashko wrote:
>> Hi Santosh,
>>
>> This series is set of platform changes required to enable NETCP CPTS reference
>> clock selection and final patch to enable CPTS for Keystone 66AK2E/L/HK SoCs.
>>
>> Those patches were posted already [1] together with driver's changes, so this
>> is re-send of DT/platform specific changes only, as driver's changes have
>> been merged already.
>>
>> Patches 1-5: CPTS DT nodes update for TI Keystone 2 66AK2HK/E/L SoCs.
>> Patch 6: enables CPTS for TI Keystone 2 66AK2HK/E/L SoCs.
>>
>> [1] https://patchwork.kernel.org/cover/10980037/
>>
>> Grygorii Strashko (6):
>> ARM: dts: keystone-clocks: add input fixed clocks
>> ARM: dts: k2e-clocks: add input ext. fixed clocks tsipclka/b
>> ARM: dts: k2e-netcp: add cpts refclk_mux node
>> ARM: dts: k2hk-netcp: add cpts refclk_mux node
>> ARM: dts: k2l-netcp: add cpts refclk_mux node
>> ARM: configs: keystone: enable cpts
>>
> Will add these for 5.4 queue. Thanks !!
Thank you
--
Best regards,
grygorii
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 2/2] leds: Add control of the voltage/current regulator to the LED core
From: Daniel Thompson @ 2019-07-16 10:50 UTC (permalink / raw)
To: Jean-Jacques Hiblot
Cc: jacek.anaszewski, pavel, robh+dt, mark.rutland, dmurphy,
linux-leds, linux-kernel, devicetree
In-Reply-To: <20190715155657.22976-3-jjhiblot@ti.com>
On Mon, Jul 15, 2019 at 05:56:57PM +0200, Jean-Jacques Hiblot wrote:
> A LED is usually powered by a voltage/current regulator. Let the LED core
This is almost certainly nitpicking but since there's enough other
review comments that you will have to respin anyway ;-)
Is an LED really "usually powered by a voltage/current regulator"? Some
LEDs have a software controlled power supply but I'm not sure it is
the usual case.
Likewise its a little confusing to be talking about LEDs with an
external current regulator since, although that is possible, it is also
one the main features provided by LED driver chips.
Daniel.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox