From 084e3faf5afe7141ff0efea7fc1aee25272ccb47 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Sun, 1 Jul 2018 16:46:15 +0100 Subject: [PATCH] seq_file: move doc to README --- README.adoc | 77 ++++++++++++++++++- kernel_module/README.adoc | 2 - kernel_module/anonymous_inode.c | 3 + kernel_module/seq_file.c | 26 +------ ...q_file_single.c => seq_file_single_open.c} | 13 +--- rootfs_overlay/seq_file.sh | 33 ++------ rootfs_overlay/seq_file_single.sh | 12 --- rootfs_overlay/seq_file_single_open.sh | 7 ++ rootfs_overlay/test_all.sh | 6 +- 9 files changed, 97 insertions(+), 82 deletions(-) rename kernel_module/{seq_file_single.c => seq_file_single_open.c} (77%) delete mode 100755 rootfs_overlay/seq_file_single.sh create mode 100755 rootfs_overlay/seq_file_single_open.sh diff --git a/README.adoc b/README.adoc index da67e6f..99511dd 100644 --- a/README.adoc +++ b/README.adoc @@ -2976,7 +2976,7 @@ This example shows how sysfs is more restricted, as it does not take an arbitrar So you basically can only do `open`, `close`, `read`, `write`, and `lseek` on sysfs files. -It is similar to a `seq_file` file operation, except that write is also implemented. +It is similar to a <> file operation, except that write is also implemented. TODO: what are those `kobject` structs? Make a more complex example that shows what they can do. @@ -3024,6 +3024,76 @@ File operations is the main method of userland driver communication. No, there no official documentation: http://stackoverflow.com/questions/15213932/what-are-the-struct-file-operations-arguments +==== seq_file + +In guest: + +.... +/seq_file.sh +echo $? +.... + +Outcome: the test passes: + +.... +0 +.... + +Sources: + +* link:kernel_module/seq_file.c[] +* link:rootfs_overlay/seq_file.sh[] + +Writing trivial read <> is repetitive and error prone. + +The `seq_file` API makes the process much easier for those trivial cases. + +In this example we create a debugfs file that behaves just like a file that contains: + +.... +0 +1 +2 +.... + +However, we only store a single integer in memory and calculate the file on the fly in an iterator fashion. + +`seq_file` does not provide `write`: https://stackoverflow.com/questions/30710517/how-to-implement-a-writable-proc-file-by-using-seq-file-in-a-driver-module + +Bibliography: + +* link:https://github.com/torvalds/linux/blob/v4.17/Documentation/filesystems/seq_file.txt[Documentation/filesystems/seq_file.txt] +* https://stackoverflow.com/questions/25399112/how-to-use-a-seq-file-in-linux-modules + +===== seq_file single_open + +In guest: + +.... +/seq_file.sh +echo $? +.... + +Outcome: the test passes: + +.... +0 +.... + +Sources: + +* link:kernel_module/seq_file_single_open.c[] +* link:rootfs_overlay/seq_file_single_open.sh[] + +If you have the entire read output upfront, `single_open` is an even more convenient version of <>. + +This example produces a debugfs file that behaves like a file that contains: + +.... +ab +cd +.... + ==== Character devices In guest: @@ -3123,10 +3193,9 @@ Sources: * link:kernel_module/user/anonymous_inode.c[] * link:rootfs_overlay/anonymous_inode.sh[] -This example: +This example gets an anonymous inode via `ioctl` from a debugfs entry by using `anon_inode_getfd`. -* gets an anonymous inode via `ioctl` from a debugfs entry `anon_inode_getfd` from a debugfs file -* read jiffies from that inode +Reads to that inode return the sequence: `1`, `10`, `100`, ... `10000000`, `1`, `100`, ... Anonymous inodes allow getting multiple file descriptors from a single filesystem entry, which reduces namespace pollution compared to creating multiple device files. diff --git a/kernel_module/README.adoc b/kernel_module/README.adoc index d4e08b5..c7c6a6f 100644 --- a/kernel_module/README.adoc +++ b/kernel_module/README.adoc @@ -22,8 +22,6 @@ .. link:ioctl.c[] .. link:mmap.c[] .. link:poll.c[] -.. link:seq_file.c[] -.. link:seq_file_inode.c[] . Asynchronous .. link:irq.c[] .. link:kthread.c[] diff --git a/kernel_module/anonymous_inode.c b/kernel_module/anonymous_inode.c index 40fa6e6..b8b1e9c 100644 --- a/kernel_module/anonymous_inode.c +++ b/kernel_module/anonymous_inode.c @@ -24,6 +24,9 @@ static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off ret = -EFAULT; } myval <<= 4; + if (myval == 0) { + myval = 1; + } return ret; } diff --git a/kernel_module/seq_file.c b/kernel_module/seq_file.c index 1e124ad..208b434 100644 --- a/kernel_module/seq_file.c +++ b/kernel_module/seq_file.c @@ -1,28 +1,4 @@ -/* -Adapted from: Documentation/filesystems/seq_file.txt -but we limit the count to the max module parameter. - -Writting trivial read fops is repetitive and error prone. - -The seq_file API makes the process much easier for those trivial cases. - -This example is behaves just like a file that contains: - - 0 - 1 - 2 - -However, we only store a single integer in memory -and calculate the file on the fly in an iterator fashion. - -There is not write version, as writes are more complex: -https://stackoverflow.com/questions/30710517/how-to-implement-a-writable-proc-file-by-using-seq-file-in-a-driver-module - -Bibliography: - -- Documentation/filesystems/seq_file.txt -- https://stackoverflow.com/questions/25399112/how-to-use-a-seq-file-in-linux-modules -*/ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#seq_file */ #include #include /* EFAULT */ diff --git a/kernel_module/seq_file_single.c b/kernel_module/seq_file_single_open.c similarity index 77% rename from kernel_module/seq_file_single.c rename to kernel_module/seq_file_single_open.c index a6c1708..d759d2b 100644 --- a/kernel_module/seq_file_single.c +++ b/kernel_module/seq_file_single_open.c @@ -1,12 +1,4 @@ -/* -If you have the entire read output upfront, single_open -is an even more convenient version of seq_file. - -This example behaves like a file that contains: - - ab - cd -*/ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#seq_file-single_open */ #include #include /* EFAULT */ @@ -40,8 +32,7 @@ static const struct file_operations fops = { static int myinit(void) { - debugfs_file = debugfs_create_file( - "lkmc_seq_file_single", S_IRUSR, NULL, NULL, &fops); + debugfs_file = debugfs_create_file("lkmc_seq_file_single_open", S_IRUSR, NULL, NULL, &fops); return 0; } diff --git a/rootfs_overlay/seq_file.sh b/rootfs_overlay/seq_file.sh index f29e506..88b4dc4 100755 --- a/rootfs_overlay/seq_file.sh +++ b/rootfs_overlay/seq_file.sh @@ -1,29 +1,10 @@ #!/bin/sh - -set -ex - +set -e +f=/sys/kernel/debug/lkmc_seq_file insmod /seq_file.ko -cd /sys/kernel/debug - -cat 'lkmc_seq_file' -# => 0 -# => 1 -# => 2 - -cat 'lkmc_seq_file' -# => 0 -# => 1 -# => 2 - -dd if='lkmc_seq_file' bs=1 count=2 skip=0 status=none -# => 0 -dd if='lkmc_seq_file' bs=1 count=4 skip=0 status=none -# => 0 -# => 1 -dd if='lkmc_seq_file' bs=1 count=2 skip=2 status=none -# => 1 -dd if='lkmc_seq_file' bs=4 count=1 skip=0 status=none -# => 0 -# => 1 - +[ "$(cat "$f")" = "$(printf '0\n1\n2\n')" ] +[ "$(cat "$f")" = "$(printf '0\n1\n2\n')" ] +[ "$(dd if="$f" bs=1 count=2 skip=0 status=none)" = "$(printf '0\n')" ] +[ "$(dd if="$f" bs=1 count=2 skip=2 status=none)" = "$(printf '1\n')" ] +[ "$(dd if="$f" bs=4 count=1 skip=0 status=none)" = "$(printf '0\n1\n')" ] rmmod seq_file diff --git a/rootfs_overlay/seq_file_single.sh b/rootfs_overlay/seq_file_single.sh deleted file mode 100755 index 795ef58..0000000 --- a/rootfs_overlay/seq_file_single.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -set -ex -insmod /seq_file_single.ko -cd /sys/kernel/debug -cat 'lkmc_seq_file_single' -# => ab -# => cd -dd if='lkmc_seq_file_single' bs=1 count=3 skip=1 -# => b -# => c - -rmmod seq_file_single diff --git a/rootfs_overlay/seq_file_single_open.sh b/rootfs_overlay/seq_file_single_open.sh new file mode 100755 index 0000000..f504a57 --- /dev/null +++ b/rootfs_overlay/seq_file_single_open.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e +f=/sys/kernel/debug/lkmc_seq_file_single_open +insmod /seq_file_single_open.ko +[ "$(cat "$f")" = "$(printf 'ab\ncd\n')" ] +[ "$(dd if="$f" bs=1 count=3 skip=1)" = "$(printf "b\nc\n")" ] +rmmod seq_file_single_open diff --git a/rootfs_overlay/test_all.sh b/rootfs_overlay/test_all.sh index 3402198..1b5c133 100755 --- a/rootfs_overlay/test_all.sh +++ b/rootfs_overlay/test_all.sh @@ -6,8 +6,10 @@ for test in \ /debugfs.sh \ /fops.sh \ /procfs.sh \ - /sysfs.sh -do + /seq_file.sh \ + /seq_file_single_open.sh \ + /sysfs.sh \ +; do if ! "$test"; then echo "lkmc_test_fail: ${test}" exit 1