From f82c058eb676b9b94a4064dd9cde2c5978d9cdcc Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Fri, 14 Jul 2017 10:31:18 +0100 Subject: [PATCH] Saner seq_file example adapted from kernel doc --- kernel_module/seq_file.c | 96 +++++++++++++++++++++++-------- kernel_module/seq_file_single.c | 19 ++++-- rootfs_overlay/seq_file.sh | 18 ++++-- rootfs_overlay/seq_file_single.sh | 6 +- 4 files changed, 104 insertions(+), 35 deletions(-) diff --git a/kernel_module/seq_file.c b/kernel_module/seq_file.c index fea041b..4e79dc8 100644 --- a/kernel_module/seq_file.c +++ b/kernel_module/seq_file.c @@ -1,12 +1,27 @@ /* -Adapted from: http://allskyee.blogspot.co.uk/2011/12/proc-seqfile-write.html +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. -There is not write version however, as writes are more complex: +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 */ #include /* copy_from_user, copy_to_user */ @@ -16,43 +31,70 @@ https://stackoverflow.com/questions/30710517/how-to-implement-a-writable-proc-fi #include #include /* pr_info */ #include /* seq_read, seq_lseek, single_release */ +#include #include /* S_IRUSR */ MODULE_LICENSE("GPL"); +static int max = 2; +module_param(max, int, S_IRUSR | S_IWUSR); + static struct dentry *debugfs_file; -static void * next(struct seq_file *s, void *v, loff_t *pos) +/* Called at the beginning of every read. + * + * The return value is passsed to the first show. + * It normally represents the current position of the iterator. + * It could be any struct, but we use just a single integer here. + * + * NULL return means stop should be called next, and so the read will be empty.. + * This happens for example for an ftell that goes beyond the file size. + */ +static void *start(struct seq_file *s, loff_t *pos) { - pr_info("next\n"); - (*(unsigned long *)v)++; - (*pos)++; - return *pos < 3 ? v : NULL; -} - -static void * start(struct seq_file *s, loff_t *pos) -{ - static unsigned long counter = 0; + loff_t *spos; pr_info("start pos = %llx\n", (unsigned long long)*pos); - if (*pos == 0) { - return &counter; - } else { - *pos = 0; - return NULL; - } + spos = kmalloc(sizeof(loff_t), GFP_KERNEL); + if (!spos || *pos >= max) + return NULL; + *spos = *pos; + return spos; } -static int show(struct seq_file *s, void *v) +/* The return value is passed to next show. + * If NULL, stop is called next instead of show, and read ends. + * + * Can get called multiple times, until enough data is returned for the read. + */ +static void *next(struct seq_file *s, void *v, loff_t *pos) { - pr_info("show\n"); - seq_printf(s, "%lx\n", *(unsigned long *)v); - return 0; + loff_t *spos; + + spos = v; + pr_info("next pos = %llx\n", (unsigned long long)*pos); + if (*pos >= max) + return NULL; + *pos = ++*spos; + return spos; } +/* Called at the end of every read. */ static void stop(struct seq_file *s, void *v) { pr_info("stop\n"); + kfree(v); +} + +/* Return 0 means success, SEQ_SKIP ignores previous prints, negative for error. */ +static int show(struct seq_file *s, void *v) +{ + loff_t *spos; + + spos = v; + pr_info("show pos = %llx\n", (unsigned long long)*spos); + seq_printf(s, "%llx\n", (long long unsigned)*spos); + return 0; } static struct seq_operations my_seq_ops = { @@ -64,6 +106,7 @@ static struct seq_operations my_seq_ops = { static int open(struct inode *inode, struct file *file) { + pr_info("open\n"); return seq_open(file, &my_seq_ops); } @@ -77,8 +120,13 @@ static struct file_operations fops = { static int myinit(void) { - debugfs_file = debugfs_create_file("lkmc_seq_file", S_IRUSR | S_IWUSR, NULL, NULL, &fops); - return 0; + debugfs_file = debugfs_create_file( + "lkmc_seq_file", S_IRUSR | S_IWUSR, NULL, NULL, &fops); + if (debugfs_file) { + return 0; + } else { + return -EINVAL; + } } static void myexit(void) diff --git a/kernel_module/seq_file_single.c b/kernel_module/seq_file_single.c index d70ba6d..d31c455 100644 --- a/kernel_module/seq_file_single.c +++ b/kernel_module/seq_file_single.c @@ -1,5 +1,11 @@ /* -For single reads, single_open is an even more convenient version of seq_file. +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 */ #include /* copy_from_user, copy_to_user */ @@ -17,7 +23,7 @@ static struct dentry *debugfs_file; static int show(struct seq_file *m, void *v) { - seq_printf(m, "abcd\n"); + seq_printf(m, "ab\ncd\n"); return 0; } @@ -36,8 +42,13 @@ static const struct file_operations fops = { static int myinit(void) { - debugfs_file = debugfs_create_file("lkmc_seq_file", S_IRUSR | S_IWUSR, NULL, NULL, &fops); - return 0; + debugfs_file = debugfs_create_file( + "lkmc_seq_file_single", S_IRUSR | S_IWUSR, NULL, NULL, &fops); + if (debugfs_file) { + return 0; + } else { + return -EINVAL; + } } static void myexit(void) diff --git a/rootfs_overlay/seq_file.sh b/rootfs_overlay/seq_file.sh index c08b3e2..12be441 100755 --- a/rootfs_overlay/seq_file.sh +++ b/rootfs_overlay/seq_file.sh @@ -11,9 +11,17 @@ cat 'lkmc_seq_file' # => 2 cat 'lkmc_seq_file' -# => 3 -# => 4 -# => 5 +# => 0 +# => 1 +# => 2 -# TODO understand, why does this print nothing? -dd if='lkmc_seq_file' bs=1 count=2 skip=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 diff --git a/rootfs_overlay/seq_file_single.sh b/rootfs_overlay/seq_file_single.sh index 9a93533..48f4d00 100755 --- a/rootfs_overlay/seq_file_single.sh +++ b/rootfs_overlay/seq_file_single.sh @@ -3,6 +3,8 @@ set -ex insmod /seq_file_single.ko cd /sys/kernel/debug cat 'lkmc_seq_file_single' -# => abcd -dd if='lkmc_seq_file_single' bs=1 skip=2 +# => ab # => cd +dd if='lkmc_seq_file_single' bs=1 count=3 skip=1 +# => b +# => c