Skip to content

Commit 0cd1a2b

Browse files
committed
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
1 parent 7f36718 commit 0cd1a2b

File tree

7 files changed

+98
-64
lines changed

7 files changed

+98
-64
lines changed

README.adoc

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2877,8 +2877,52 @@ You should then look up if there is a branch that supports that kernel. Staying
28772877

28782878
=== Pseudo filesystems
28792879

2880+
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.
2881+
2882+
Some notable examples include:
2883+
2884+
* procfs, often mounted at: `/proc`
2885+
* sysfs, often mounted at: `/sys`
2886+
* devtmpfs, often mounted at: `/dev`
2887+
* debugfs, often mounted at: `/sys/kernel/debug/`
2888+
2889+
What each pseudo-file does for each related system call does is defined by its <<file-operations>>.
2890+
2891+
Bibliography:
2892+
2893+
* https://superuser.com/questions/1198292/what-is-a-pseudo-file-system-in-linux
2894+
* https://en.wikipedia.org/wiki/Synthetic_file_system
2895+
2896+
==== File operations
2897+
2898+
In guest:
2899+
2900+
....
2901+
/fops.sh
2902+
echo $?
2903+
....
2904+
2905+
Outcome: the test passes:
2906+
2907+
....
2908+
0
2909+
....
2910+
2911+
Sources:
2912+
2913+
* link:kernel_module/fops.c[]
2914+
* link:rootfs_overlay/fops.sh[]
2915+
2916+
File operations is the main method of userland driver communication.
2917+
2918+
`struct file_operations` determines what the kernel will do on filesystem system calls of <<pseudo-filesystems>>.
2919+
2920+
No, there no official documentation: http://stackoverflow.com/questions/15213932/what-are-the-struct-file-operations-arguments
2921+
28802922
==== Character device
28812923

2924+
In guest:
2925+
28822926
....
28832927
/character_device.sh
28842928
echo $?
@@ -7579,7 +7623,15 @@ Should:
75797623
* make a network request
75807624
* shutdown gracefully
75817625

7582-
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.
7626+
We are slowly automating testable guest tests with:
7627+
7628+
....
7629+
./run -F '/test_all.sh;/poweroff.out' | grep lkmc_test
7630+
....
7631+
7632+
which outputs to stdout `lkmc_test_pass` or `lkmc_test_fail` to stdout, which we can grep from host to automate testing.
7633+
7634+
Source: link:rootfs_overlay/test_all.sh[].
75837635

75847636
===== Host testing
75857637

configure

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,11 @@ fi
109109
# In particular:
110110
# - `shallow = true` on the submodule has no effect for the non default educational branches of our submodules
111111
# - QEMU's submodules point to commits that are neither under branches nor tags, and so `--shallow-submodules` fails
112-
#
113112
git submodule update --depth 1 $gitjobs --init -- $submodules
114113
if "$qemu"; then
115114
cd qemu
116115
git submodule update --init --recursive
117116
fi
118117
) &
118+
# https://unix.stackexchange.com/questions/65532/why-does-set-e-not-work-inside-subshells-with-parenthesis-followed-by-an-or
119119
wait $! || git submodule update --init --recursive -- $submodules

kernel_module/README.adoc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
. Pseudo filesystems
2222
.. link:anonymous_inode.c[]
2323
.. link:debugfs.c[]
24-
.. link:fops.c[]
2524
.. link:ioctl.c[]
2625
.. link:mmap.c[]
2726
.. link:poll.c[]

kernel_module/debugfs.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ static int myinit(void)
5959
}
6060

6161
/* Created on the toplevel of the debugfs mount,
62-
* and with explicit fops instead of a fixed integer value. */
62+
* and with explicit fops instead of a fixed integer value.
63+
*/
6364
toplevel_file = debugfs_create_file(
6465
"lkmc_debugfs_file", S_IWUSR, NULL, NULL, &fops);
6566
if (!toplevel_file) {

kernel_module/fops.c

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,3 @@
1-
/*
2-
Basic fops example, with a fixed size static data buffer.
3-
4-
Usage:
5-
6-
/fops.sh
7-
8-
The buffer can be written and read from. If data overflows, data is thrown away.
9-
10-
No, there ain't no official docs:
11-
http://stackoverflow.com/questions/15213932/what-are-the-struct-file-operations-arguments
12-
13-
fops define what the kernel will do on filesystem system calls on all of
14-
/dev, /proc, /sys, and consistute the main method of userland communication
15-
in drivers (syscalls being the other one).
16-
17-
Here we use debugfs.
18-
*/
19-
201
#include <linux/debugfs.h>
212
#include <linux/errno.h> /* EFAULT */
223
#include <linux/fs.h> /* file_operations */
@@ -39,7 +20,7 @@ static int open(struct inode *inode, struct file *filp)
3920
* We must increment this by the ammount of bytes read.
4021
* Then when userland reads the same file descriptor again,
4122
* we start from that point instead.
42-
* */
23+
*/
4324
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
4425
{
4526
ssize_t ret;
@@ -65,7 +46,8 @@ static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off
6546
/* Similar to read, but with one notable difference:
6647
* we must return ENOSPC if the user tries to write more
6748
* than the size of our buffer. Otherwise, Bash > just
68-
* keeps trying to write to it infinitely. */
49+
* keeps trying to write to it infinitely.
50+
*/
6951
static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
7052
{
7153
ssize_t ret;
@@ -92,10 +74,9 @@ static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff
9274
return ret;
9375
}
9476

95-
/*
96-
Called on the last close:
97-
http://stackoverflow.com/questions/11393674/why-is-the-close-function-is-called-release-in-struct-file-operations-in-the-l
98-
*/
77+
/* Called on the last close:
78+
* http://stackoverflow.com/questions/11393674/why-is-the-close-function-is-called-release-in-struct-file-operations-in-the-l
79+
*/
9980
static int release(struct inode *inode, struct file *filp)
10081
{
10182
pr_info("release\n");

rootfs_overlay/fops.sh

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,30 @@
11
#!/bin/sh
2+
set -e
23

3-
set -x
4+
# Setup
5+
f=/sys/kernel/debug/lkmc_fops
46
insmod /fops.ko
5-
cd /sys/kernel/debug/lkmc_fops
67

7-
## Basic read.
8-
cat f
9-
# => abcd
10-
# dmesg => open
11-
# dmesg => read
12-
# dmesg => len = [0-9]+
13-
# dmesg => close
8+
# read
9+
[ "$(cat "$f")" = abcd ]
1410

15-
## Basic write
11+
# write
12+
printf 01 > "$f"
13+
[ "$(cat "$f")" = 01cd ]
1614

17-
printf '01' >f
18-
# dmesg => open
19-
# dmesg => write
20-
# dmesg => len = 1
21-
# dmesg => buf = a
22-
# dmesg => close
15+
# ENOSPC
16+
printf abcd > "$f"
17+
set +e
18+
printf 12345 > "$f"
19+
exit_status="$?"
20+
set -e
21+
[ "$exit_status" -eq 8 ]
22+
[ "$(cat "$f")" = abcd ]
2323

24-
cat f
25-
# => 01cd
26-
# dmesg => open
27-
# dmesg => read
28-
# dmesg => len = [0-9]+
29-
# dmesg => close
24+
# seek
25+
printf 1234 > "$f"
26+
printf z | dd bs=1 of="$f" seek=2
27+
[ "$(cat "$f")" = 12z4 ]
3028

31-
## ENOSPC
32-
printf '1234' >f
33-
printf '12345' >f
34-
echo "$?"
35-
# => 8
36-
cat f
37-
# => 1234
38-
39-
## seek
40-
printf '1234' >f
41-
printf 'z' | dd bs=1 of=f seek=2
42-
cat f
43-
# => 12z4
29+
# Teardown
30+
rmmod fops

rootfs_overlay/test_all.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
(
3+
set -ex
4+
/character_device.sh
5+
/character_device_create.sh
6+
/fops.sh
7+
)
8+
if [ "$?" -eq 0 ]; then
9+
echo lkmc_test_pass
10+
exit 0
11+
else
12+
echo lkmc_test_fail
13+
exit 1
14+
fi

0 commit comments

Comments
 (0)