From 0cd1a2b6022447a0e735e341d8a5f7c229be844d Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Fri, 29 Jun 2018 08:40:15 +0100 Subject: [PATCH] kernel module: fix fops... and move its documentation into README Sometimes I wonder if anyone has ever run this tutorial, otherwise how can such basic bugs persist for so long? test_all.sh: crete --- README.adoc | 54 +++++++++++++++++++++++++++++++++++- configure | 2 +- kernel_module/README.adoc | 1 - kernel_module/debugfs.c | 3 +- kernel_module/fops.c | 31 ++++----------------- rootfs_overlay/fops.sh | 57 +++++++++++++++----------------------- rootfs_overlay/test_all.sh | 14 ++++++++++ 7 files changed, 98 insertions(+), 64 deletions(-) create mode 100755 rootfs_overlay/test_all.sh diff --git a/README.adoc b/README.adoc index 2725478..06cbe02 100644 --- a/README.adoc +++ b/README.adoc @@ -2877,8 +2877,52 @@ You should then look up if there is a branch that supports that kernel. Staying === Pseudo filesystems +Pseudo filesystems are filesystems that don't represent actual files in a hard disk, but rather allow us to do special operations on filesystem-related system calls. + +Some notable examples include: + +* procfs, often mounted at: `/proc` +* sysfs, often mounted at: `/sys` +* devtmpfs, often mounted at: `/dev` +* debugfs, often mounted at: `/sys/kernel/debug/` + +What each pseudo-file does for each related system call does is defined by its <>. + +Bibliography: + +* https://superuser.com/questions/1198292/what-is-a-pseudo-file-system-in-linux +* https://en.wikipedia.org/wiki/Synthetic_file_system + +==== File operations + +In guest: + +.... +/fops.sh +echo $? +.... + +Outcome: the test passes: + +.... +0 +.... + +Sources: + +* link:kernel_module/fops.c[] +* link:rootfs_overlay/fops.sh[] + +File operations is the main method of userland driver communication. + +`struct file_operations` determines what the kernel will do on filesystem system calls of <>. + +No, there no official documentation: http://stackoverflow.com/questions/15213932/what-are-the-struct-file-operations-arguments + ==== Character device +In guest: + .... /character_device.sh echo $? @@ -7579,7 +7623,15 @@ Should: * make a network request * shutdown gracefully -TODO automate all of this with a `/test-all.sh` script in guest which outputs to stdout `LKMC_TEST_PASS` or `LKMC_TEST_FAIL` and grep that from host. +We are slowly automating testable guest tests with: + +.... +./run -F '/test_all.sh;/poweroff.out' | grep lkmc_test +.... + +which outputs to stdout `lkmc_test_pass` or `lkmc_test_fail` to stdout, which we can grep from host to automate testing. + +Source: link:rootfs_overlay/test_all.sh[]. ===== Host testing diff --git a/configure b/configure index b21c0be..6d4b6b3 100755 --- a/configure +++ b/configure @@ -109,11 +109,11 @@ fi # In particular: # - `shallow = true` on the submodule has no effect for the non default educational branches of our submodules # - QEMU's submodules point to commits that are neither under branches nor tags, and so `--shallow-submodules` fails - # git submodule update --depth 1 $gitjobs --init -- $submodules if "$qemu"; then cd qemu git submodule update --init --recursive fi ) & +# https://unix.stackexchange.com/questions/65532/why-does-set-e-not-work-inside-subshells-with-parenthesis-followed-by-an-or wait $! || git submodule update --init --recursive -- $submodules diff --git a/kernel_module/README.adoc b/kernel_module/README.adoc index 389cff1..f6ca0b7 100644 --- a/kernel_module/README.adoc +++ b/kernel_module/README.adoc @@ -21,7 +21,6 @@ . Pseudo filesystems .. link:anonymous_inode.c[] .. link:debugfs.c[] -.. link:fops.c[] .. link:ioctl.c[] .. link:mmap.c[] .. link:poll.c[] diff --git a/kernel_module/debugfs.c b/kernel_module/debugfs.c index 5e8f405..91c967a 100644 --- a/kernel_module/debugfs.c +++ b/kernel_module/debugfs.c @@ -59,7 +59,8 @@ static int myinit(void) } /* Created on the toplevel of the debugfs mount, - * and with explicit fops instead of a fixed integer value. */ + * and with explicit fops instead of a fixed integer value. + */ toplevel_file = debugfs_create_file( "lkmc_debugfs_file", S_IWUSR, NULL, NULL, &fops); if (!toplevel_file) { diff --git a/kernel_module/fops.c b/kernel_module/fops.c index 23b4112..5ab3fd2 100644 --- a/kernel_module/fops.c +++ b/kernel_module/fops.c @@ -1,22 +1,3 @@ -/* -Basic fops example, with a fixed size static data buffer. - -Usage: - - /fops.sh - -The buffer can be written and read from. If data overflows, data is thrown away. - -No, there ain't no official docs: -http://stackoverflow.com/questions/15213932/what-are-the-struct-file-operations-arguments - -fops define what the kernel will do on filesystem system calls on all of -/dev, /proc, /sys, and consistute the main method of userland communication -in drivers (syscalls being the other one). - -Here we use debugfs. -*/ - #include #include /* EFAULT */ #include /* file_operations */ @@ -39,7 +20,7 @@ static int open(struct inode *inode, struct file *filp) * We must increment this by the ammount of bytes read. * Then when userland reads the same file descriptor again, * we start from that point instead. - * */ + */ static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off) { ssize_t ret; @@ -65,7 +46,8 @@ static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off /* Similar to read, but with one notable difference: * we must return ENOSPC if the user tries to write more * than the size of our buffer. Otherwise, Bash > just - * keeps trying to write to it infinitely. */ + * keeps trying to write to it infinitely. + */ static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off) { ssize_t ret; @@ -92,10 +74,9 @@ static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff return ret; } -/* -Called on the last close: -http://stackoverflow.com/questions/11393674/why-is-the-close-function-is-called-release-in-struct-file-operations-in-the-l -*/ +/* Called on the last close: + * http://stackoverflow.com/questions/11393674/why-is-the-close-function-is-called-release-in-struct-file-operations-in-the-l + */ static int release(struct inode *inode, struct file *filp) { pr_info("release\n"); diff --git a/rootfs_overlay/fops.sh b/rootfs_overlay/fops.sh index 3651f4a..1c8f5b0 100755 --- a/rootfs_overlay/fops.sh +++ b/rootfs_overlay/fops.sh @@ -1,43 +1,30 @@ #!/bin/sh +set -e -set -x +# Setup +f=/sys/kernel/debug/lkmc_fops insmod /fops.ko -cd /sys/kernel/debug/lkmc_fops -## Basic read. -cat f -# => abcd -# dmesg => open -# dmesg => read -# dmesg => len = [0-9]+ -# dmesg => close +# read +[ "$(cat "$f")" = abcd ] -## Basic write +# write +printf 01 > "$f" +[ "$(cat "$f")" = 01cd ] -printf '01' >f -# dmesg => open -# dmesg => write -# dmesg => len = 1 -# dmesg => buf = a -# dmesg => close +# ENOSPC +printf abcd > "$f" +set +e +printf 12345 > "$f" +exit_status="$?" +set -e +[ "$exit_status" -eq 8 ] +[ "$(cat "$f")" = abcd ] -cat f -# => 01cd -# dmesg => open -# dmesg => read -# dmesg => len = [0-9]+ -# dmesg => close +# seek +printf 1234 > "$f" +printf z | dd bs=1 of="$f" seek=2 +[ "$(cat "$f")" = 12z4 ] -## ENOSPC -printf '1234' >f -printf '12345' >f -echo "$?" -# => 8 -cat f -# => 1234 - -## seek -printf '1234' >f -printf 'z' | dd bs=1 of=f seek=2 -cat f -# => 12z4 +# Teardown +rmmod fops diff --git a/rootfs_overlay/test_all.sh b/rootfs_overlay/test_all.sh new file mode 100755 index 0000000..ee6cc88 --- /dev/null +++ b/rootfs_overlay/test_all.sh @@ -0,0 +1,14 @@ +#!/bin/sh +( + set -ex + /character_device.sh + /character_device_create.sh + /fops.sh +) +if [ "$?" -eq 0 ]; then + echo lkmc_test_pass + exit 0 +else + echo lkmc_test_fail + exit 1 +fi