[DRAFT] mailbox: Add pingpong lat measure nshubin/qemu-mbox
authorNikita Shubin <nikita.shubin@maquefel.me>
Fri, 14 Jun 2024 07:28:26 +0000 (10:28 +0300)
committerNikita Shubin <nikita.shubin@maquefel.me>
Mon, 17 Jun 2024 10:32:04 +0000 (13:32 +0300)
Add pingpong latency measuring test for mailbox.

Only qemu-mailbox with tx_done interrupt supported.

Signed-off-by: Nikita Shubin <nikita.shubin@maquefel.me>
drivers/mailbox/Kconfig
drivers/mailbox/Makefile
drivers/mailbox/mailbox-pingpong.c [new file with mode: 0644]

index 020f32e85aa49d71125afdca7ca6c928157890b2..5d46a749455748a9a4873fbd86a2ede250bf9b09 100644 (file)
@@ -164,6 +164,14 @@ config MAILBOX_TEST
          Test client to help with testing new Controller driver
          implementations.
 
+config MAILBOX_PINGPONG
+       tristate "Mailbox Ping Pong Test Client"
+       depends on QEMU_MBOX
+       depends on HAS_IOMEM
+       help
+         Test client to help with testing new Controller driver
+         implementations.
+
 config POLARFIRE_SOC_MAILBOX
        tristate "PolarFire SoC (MPFS) Mailbox"
        depends on HAS_IOMEM
index d99cc632ff459b1c4dac4cfb3ca4454fea500d75..e065b7f1db15d5eb5a3c4a4073016311329e1cfe 100644 (file)
@@ -5,6 +5,8 @@ obj-$(CONFIG_MAILBOX)           += mailbox.o
 
 obj-$(CONFIG_MAILBOX_TEST)     += mailbox-test.o
 
+obj-$(CONFIG_MAILBOX_PINGPONG) += mailbox-pingpong.o
+
 obj-$(CONFIG_ARM_MHU)  += arm_mhu.o arm_mhu_db.o
 
 obj-$(CONFIG_ARM_MHU_V2)       += arm_mhuv2.o
diff --git a/drivers/mailbox/mailbox-pingpong.c b/drivers/mailbox/mailbox-pingpong.c
new file mode 100644 (file)
index 0000000..c15e884
--- /dev/null
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Nikita Shubin <nikita.shubin@maquefel.me>
+ * derived: drivers/dma/dmatest.c
+ */
+#define DEBUG
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/sched/signal.h>
+
+#include <acpi/qemu-mailbox.h>
+
+static unsigned int iterations = 5000;
+module_param(iterations, uint, 0644);
+MODULE_PARM_DESC(iterations,
+                "Iterations before stopping test (default: 5000)");
+
+static int timeout = 3000;
+module_param(timeout, int, 0644);
+MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
+                "Pass -1 for infinite timeout");
+
+/* TODO: pass as parameter */
+#define MAX_RX_TIMEOUT         (msecs_to_jiffies(3000))
+
+struct mbox_pp_params {
+       unsigned int    iterations;
+       int             timeout;
+};
+
+enum mbox_pp_error {
+       MBOX_PP_OK,
+       MBOX_PP_TX_DONE_ERR,
+       MBOX_PP_MBOX_SEND_ERR,
+};
+
+static struct mbox_pp_info {
+       /* Test parameters */
+       struct mbox_pp_params   params;
+
+       /* Internal state */
+       struct mbox_client      client;
+       struct mutex            lock;
+       struct mbox_chan        *chan;
+       struct task_struct      *task;
+       struct completion       tx_done;
+       unsigned int            iter;
+       ktime_t                 latency;
+       int                     last_error;
+       atomic_t                running;
+       bool                    did_init;
+       bool                    pending;
+} test_info = {
+       .lock = __MUTEX_INITIALIZER(test_info.lock),
+};
+
+static int mbox_pp_run_set(const char *val, const struct kernel_param *kp);
+static int mbox_pp_run_get(char *val, const struct kernel_param *kp);
+static const struct kernel_param_ops run_ops = {
+       .set = mbox_pp_run_set,
+       .get = mbox_pp_run_get,
+};
+static bool mbox_pp_run;
+module_param_cb(run, &run_ops, &mbox_pp_run, 0644);
+MODULE_PARM_DESC(run, "Run the test (default: false)");
+
+static DECLARE_WAIT_QUEUE_HEAD(thread_wait);
+static bool wait;
+
+static bool is_threaded_test_run(struct mbox_pp_info *info)
+{
+       return false;
+}
+
+static bool is_threaded_test_pending(struct mbox_pp_info *info)
+{
+       return false;
+}
+
+static void run_pending_test(struct mbox_pp_info *info)
+{
+       wake_up_process(info->task);
+}
+
+static void stop_threaded_test(struct mbox_pp_info *info)
+{
+       mbox_free_channel(info->chan);
+}
+
+static void start_threaded_tests(struct mbox_pp_info *info)
+{
+       /* we might be called early to set run=, defer running until all
+        * parameters have been evaluated
+        */
+       if (!info->did_init)
+               return;
+
+       run_pending_test(info);
+}
+
+static int mbox_pp_run_get(char *val, const struct kernel_param *kp)
+{
+       struct mbox_pp_info *info = &test_info;
+
+       mutex_lock(&info->lock);
+       if (is_threaded_test_run(info)) {
+               mbox_pp_run = true;
+       } else {
+               if (!is_threaded_test_pending(info))
+                       stop_threaded_test(info);
+               mbox_pp_run = false;
+       }
+       mutex_unlock(&info->lock);
+
+       return param_get_bool(val, kp);
+}
+
+static int mbox_pp_func(void *data)
+{
+       struct mbox_pp_info *info = data;
+       u32 message = 1;
+       u64 stop_at, now = 0;
+       int ret;
+
+       info->iter = 0;
+       info->latency = ktime_get();
+       info->last_error = 0;
+       atomic_set(&info->running, 1);
+
+       pr_debug("mbox_pp: started...\n");
+       if (info->params.timeout) {
+               now = ktime_get_real_fast_ns();
+               stop_at = now + info->params.timeout * NSEC_PER_MSEC;
+       } else
+               stop_at = -1;
+
+       pr_debug("mbox_pp: now %llu stop at %llu\n", now, stop_at);
+       while (ktime_get_real_fast_ns() < stop_at) {
+               ret = mbox_send_message(info->chan, &message);
+               if (ret < 0) {
+                       pr_err("mbox_pp: started failed on send_message: %d\n", ret);
+                       ret = -EIO;
+                       goto fail;
+               }
+
+               if (!wait_for_completion_timeout(&info->tx_done,
+                                                MAX_RX_TIMEOUT)) {
+                       pr_err("%s: send msg timeout\n", __func__);
+                       ret = -ETIMEDOUT;
+                       goto fail;
+               }
+
+               trace_printk("TX_DONE=%llu\n", ktime_get_raw_fast_ns());
+
+               reinit_completion(&info->tx_done);
+               if (info->last_error) {
+                       pr_info("mbox_pp: test finished with error: %d\n", info->last_error);
+                       ret = -EIO;
+                       goto fail;
+               }
+
+               info->iter++;
+               if (info->iter >= info->params.iterations) {
+                       info->last_error = MBOX_PP_OK;
+                       break;
+               }
+
+               if (!atomic_read(&info->running))
+                       break;
+       }
+
+       if (info->last_error)
+               pr_info("mbox_pp: test finished with error: %d\n", info->last_error);
+
+       info->latency = ktime_sub(ktime_get(), info->latency);
+       pr_info("mbox_pp: made %u iterations, lasted %llu usecs\n",
+               info->iter, ktime_to_us(info->latency));
+
+       if (info->iter)
+               info->latency = ns_to_ktime(ktime_divns(info->latency, info->iter));
+
+       pr_info("mbox_pp: latency %llu us (%llu ns)\n",
+               ktime_to_us(info->latency), ktime_to_ns(info->latency));
+
+       return 0;
+
+fail:
+       atomic_set(&info->running, 0);
+       return ret;
+}
+
+static void add_threaded_test(struct mbox_pp_info *info)
+{
+       struct mbox_pp_params *params = &info->params;
+
+       info->task = kthread_create(mbox_pp_func, info, "mbox_pp_thread");
+       if (IS_ERR(info->task)) {
+               pr_warn("Failed to create thread\n");
+               return;
+       }
+
+       params->iterations = iterations;
+       params->timeout = timeout;
+
+       info->pending = true;
+}
+
+static int mbox_pp_run_set(const char *val, const struct kernel_param *kp)
+{
+       struct mbox_pp_info *info = &test_info;
+       int ret;
+
+       mutex_lock(&info->lock);
+       ret = param_set_bool(val, kp);
+       if (ret) {
+               mutex_unlock(&info->lock);
+               return ret;
+       } else if (mbox_pp_run) {
+               add_threaded_test(info);
+               start_threaded_tests(info);
+       } else {
+               stop_threaded_test(info);
+       }
+
+       mutex_unlock(&info->lock);
+
+       return ret;
+}
+
+static void mbox_pp_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+       struct mbox_pp_info *info = &test_info;
+       if (unlikely(ret)) {
+               info->last_error = MBOX_PP_TX_DONE_ERR;
+               return;
+       }
+
+       complete(&info->tx_done);
+}
+
+static int __init mbox_pp_init(void)
+{
+       struct mbox_pp_info *info = &test_info;
+       struct mbox_pp_params *params = &info->params;
+       struct mbox_client *cl = &info->client;
+       struct mbox_chan *chan;
+
+       /* Request mailbox channel */
+       cl->tx_done = mbox_pp_tx_done;
+       cl->tx_block = false;
+       cl->tx_tout = test_info.params.timeout;
+       cl->knows_txdone = false;
+
+       /* find channel */
+       chan = qemu_mbox_request_channel(&info->client);
+       if (IS_ERR(chan))
+               pr_err("failed to acquire mailbox channel: %ld\n", PTR_ERR(chan));
+
+       info->chan = chan;
+       init_completion(&info->tx_done);
+
+       if (mbox_pp_run) {
+               mutex_lock(&info->lock);
+               add_threaded_test(info);
+               run_pending_test(info);
+               mutex_unlock(&info->lock);
+       }
+
+       if (params->iterations && wait)
+               wait_event(thread_wait, !is_threaded_test_run(info));
+
+       /* module parameters are stable, inittime tests are started,
+        * let userspace take over 'run' control
+        */
+       info->did_init = true;
+
+       return 0;
+}
+/* when compiled-in wait for drivers to load first */
+late_initcall(mbox_pp_init);
+
+static void __exit mbox_pp_exit(void)
+{
+       struct mbox_pp_info *info = &test_info;
+
+       mutex_lock(&info->lock);
+       stop_threaded_test(info);
+       mutex_unlock(&info->lock);
+}
+module_exit(mbox_pp_exit);
+
+MODULE_DESCRIPTION("Pingpong Mailbox Testing Facility");
+MODULE_AUTHOR("Nikita Shubin <nikita.shubin@maquefel.me>");
+MODULE_LICENSE("GPL v2");