The output of the test executable should be grouped together with the regular KUnit output and also be available in debugfs. Install a custom miscdevice as stdout and stderr which forwards the written data to the KUnit log.
Signed-off-by: Thomas Weißschuh <[email protected]> --- lib/kunit/kunit-uapi.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/lib/kunit/kunit-uapi.c b/lib/kunit/kunit-uapi.c index 485b79fd193d..7f0309a827a5 100644 --- a/lib/kunit/kunit-uapi.c +++ b/lib/kunit/kunit-uapi.c @@ -11,6 +11,7 @@ #include <linux/file.h> #include <linux/fs.h> #include <linux/fs_struct.h> +#include <linux/miscdevice.h> #include <linux/pid.h> #include <linux/pipe_fs_i.h> #include <linux/sched/task.h> @@ -22,6 +23,8 @@ #include <kunit/test.h> #include <kunit/uapi.h> +#define KUNIT_LOG_DEVICE "kunit-log" + enum { KSFT_PASS = 0, KSFT_FAIL = 1, @@ -94,10 +97,48 @@ static int kunit_uapi_get_cwd(struct vfsmount *mnt) return take_fd(fd); } +static int kunit_uapi_open_standard_streams(void) +{ + struct vfsmount *devtmpfs __free(kern_unmount) = kunit_uapi_mount_fs("devtmpfs"); + if (IS_ERR(devtmpfs)) + return PTR_ERR(devtmpfs); + + CLASS(get_unused_fd, stdin_fd)(O_RDONLY); + if (stdin_fd < 0) + return stdin_fd; + + CLASS(get_unused_fd, stdout_fd)(O_WRONLY); + if (stdout_fd < 0) + return stdout_fd; + + CLASS(get_unused_fd, stderr_fd)(O_WRONLY); + if (stderr_fd < 0) + return stderr_fd; + + struct file *logfile __free(fput) = file_open_root_mnt(devtmpfs, KUNIT_LOG_DEVICE, + O_RDWR, 0); + if (IS_ERR(logfile)) + return PTR_ERR(logfile); + + fd_install(stdin_fd, no_free_ptr(logfile)); + fd_install(stdout_fd, fget(stdin_fd)); + fd_install(stderr_fd, fget(stdin_fd)); + + take_fd(stdin_fd); + take_fd(stdout_fd); + take_fd(stderr_fd); + + return 0; +} + static int kunit_uapi_usermodehelper_init(struct subprocess_info *info, struct cred *new) { struct kunit_uapi_usermodehelper_ctx *ctx = info->data; - int dirfd; + int ret, dirfd; + + ret = kunit_uapi_open_standard_streams(); + if (ret) + return ret; dirfd = kunit_uapi_get_cwd(ctx->mnt); if (dirfd < 0) @@ -188,6 +229,118 @@ void kunit_uapi_run_kselftest(struct kunit *test, const struct kunit_uapi_blob * } EXPORT_SYMBOL_GPL(kunit_uapi_run_kselftest); +struct kunit_uapi_log_private { + struct mutex mutex; + struct seq_buf buf; + char data[4096]; +}; + +static int kunit_uapi_log_open(struct inode *ino, struct file *file) +{ + struct kunit_uapi_log_private *priv; + + priv = kmalloc_obj(*priv); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->mutex); + seq_buf_init(&priv->buf, priv->data, sizeof(priv->data)); + + file->private_data = priv; + + return 0; +} + +static void kunit_uapi_log_str(struct kunit *test, const char *str, size_t len) +{ + kunit_log(KERN_INFO, test, KUNIT_SUBSUBTEST_INDENT "%.*s", (int)len, str); +} + +static void kunit_uapi_print_buf_to_log(struct kunit *test, struct seq_buf *s) +{ + const char *start, *lf; + + if (s->size == 0 || s->len == 0) + return; + + start = seq_buf_str(s); + while ((lf = strchr(start, '\n'))) { + kunit_uapi_log_str(test, start, lf - start + 1); + start = ++lf; + } + + /* Remove printed data from buffer */ + memmove(s->buffer, start, start - s->buffer); + s->len -= start - s->buffer; +} + +static ssize_t kunit_uapi_log_write(struct file *file, const char __user *ubuf, size_t count, + loff_t *off) +{ + struct kunit_uapi_log_private *priv = file->private_data; + struct seq_buf *buf = &priv->buf; + struct kunit *test; + + test = kunit_get_current_test(); + if (!test) + return -ENODEV; + + guard(mutex)(&priv->mutex); + + if (seq_buf_has_overflowed(buf)) + return -E2BIG; + + if (buf->size < buf->len + count) { + seq_buf_set_overflow(buf); + kunit_warn(test, "KUnit UAPI line buffer has overflowed\n"); + return -E2BIG; + } + + if (copy_from_user(buf->buffer + buf->len, ubuf, count)) + return -EFAULT; + + buf->len += count; + + kunit_uapi_print_buf_to_log(test, &priv->buf); + + return count; +} + +static int kunit_uapi_log_release(struct inode *ino, struct file *file) +{ + struct kunit_uapi_log_private *priv = file->private_data; + struct kunit *test; + + mutex_destroy(&priv->mutex); + + test = kunit_get_current_test(); + if (!test) { + kfree(priv); + return -ENODEV; + } + + /* Flush last partial line */ + kunit_uapi_log_str(test, priv->buf.buffer, priv->buf.len); + kunit_uapi_log_str(test, "\n", 1); + + kfree(priv); + return 0; +} + +static const struct file_operations kunit_uapi_log_fops = { + .owner = THIS_MODULE, + .open = kunit_uapi_log_open, + .release = kunit_uapi_log_release, + .write = kunit_uapi_log_write, +}; + +static struct miscdevice kunit_uapi_log = { + .minor = MISC_DYNAMIC_MINOR, + .name = KUNIT_LOG_DEVICE, + .fops = &kunit_uapi_log_fops, +}; +module_misc_device(kunit_uapi_log); + MODULE_DESCRIPTION("KUnit UAPI testing framework"); MODULE_AUTHOR("Thomas Weißschuh <[email protected]"); MODULE_LICENSE("GPL"); -- 2.53.0

