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
This commit is contained in:
Ciro Santilli
2018-06-29 08:40:15 +01:00
parent 7f3671894f
commit 0cd1a2b602
7 changed files with 98 additions and 64 deletions

View File

@@ -2877,8 +2877,52 @@ You should then look up if there is a branch that supports that kernel. Staying
=== Pseudo filesystems === 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 <<file-operations>>.
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 <<pseudo-filesystems>>.
No, there no official documentation: http://stackoverflow.com/questions/15213932/what-are-the-struct-file-operations-arguments
==== Character device ==== Character device
In guest:
.... ....
/character_device.sh /character_device.sh
echo $? echo $?
@@ -7579,7 +7623,15 @@ Should:
* make a network request * make a network request
* shutdown gracefully * 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 ===== Host testing

2
configure vendored
View File

@@ -109,11 +109,11 @@ fi
# In particular: # In particular:
# - `shallow = true` on the submodule has no effect for the non default educational branches of our submodules # - `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 # - 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 git submodule update --depth 1 $gitjobs --init -- $submodules
if "$qemu"; then if "$qemu"; then
cd qemu cd qemu
git submodule update --init --recursive git submodule update --init --recursive
fi 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 wait $! || git submodule update --init --recursive -- $submodules

View File

@@ -21,7 +21,6 @@
. Pseudo filesystems . Pseudo filesystems
.. link:anonymous_inode.c[] .. link:anonymous_inode.c[]
.. link:debugfs.c[] .. link:debugfs.c[]
.. link:fops.c[]
.. link:ioctl.c[] .. link:ioctl.c[]
.. link:mmap.c[] .. link:mmap.c[]
.. link:poll.c[] .. link:poll.c[]

View File

@@ -59,7 +59,8 @@ static int myinit(void)
} }
/* Created on the toplevel of the debugfs mount, /* 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( toplevel_file = debugfs_create_file(
"lkmc_debugfs_file", S_IWUSR, NULL, NULL, &fops); "lkmc_debugfs_file", S_IWUSR, NULL, NULL, &fops);
if (!toplevel_file) { if (!toplevel_file) {

View File

@@ -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 <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/errno.h> /* EFAULT */ #include <linux/errno.h> /* EFAULT */
#include <linux/fs.h> /* file_operations */ #include <linux/fs.h> /* 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. * We must increment this by the ammount of bytes read.
* Then when userland reads the same file descriptor again, * Then when userland reads the same file descriptor again,
* we start from that point instead. * we start from that point instead.
* */ */
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off) static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{ {
ssize_t ret; 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: /* Similar to read, but with one notable difference:
* we must return ENOSPC if the user tries to write more * we must return ENOSPC if the user tries to write more
* than the size of our buffer. Otherwise, Bash > just * 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) static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{ {
ssize_t ret; ssize_t ret;
@@ -92,10 +74,9 @@ static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff
return ret; return ret;
} }
/* /* Called on the last close:
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
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) static int release(struct inode *inode, struct file *filp)
{ {
pr_info("release\n"); pr_info("release\n");

View File

@@ -1,43 +1,30 @@
#!/bin/sh #!/bin/sh
set -e
set -x # Setup
f=/sys/kernel/debug/lkmc_fops
insmod /fops.ko insmod /fops.ko
cd /sys/kernel/debug/lkmc_fops
## Basic read. # read
cat f [ "$(cat "$f")" = abcd ]
# => abcd
# dmesg => open
# dmesg => read
# dmesg => len = [0-9]+
# dmesg => close
## Basic write # write
printf 01 > "$f"
[ "$(cat "$f")" = 01cd ]
printf '01' >f # ENOSPC
# dmesg => open printf abcd > "$f"
# dmesg => write set +e
# dmesg => len = 1 printf 12345 > "$f"
# dmesg => buf = a exit_status="$?"
# dmesg => close set -e
[ "$exit_status" -eq 8 ]
[ "$(cat "$f")" = abcd ]
cat f # seek
# => 01cd printf 1234 > "$f"
# dmesg => open printf z | dd bs=1 of="$f" seek=2
# dmesg => read [ "$(cat "$f")" = 12z4 ]
# dmesg => len = [0-9]+
# dmesg => close
## ENOSPC # Teardown
printf '1234' >f rmmod fops
printf '12345' >f
echo "$?"
# => 8
cat f
# => 1234
## seek
printf '1234' >f
printf 'z' | dd bs=1 of=f seek=2
cat f
# => 12z4

14
rootfs_overlay/test_all.sh Executable file
View File

@@ -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