-
Notifications
You must be signed in to change notification settings - Fork 33
TASK4: Implement sysfs attributes example #136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: Victor.Krasnoshchok
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| KDIR ?= ~/work/buildroot-2021.02.7/output/build/linux-5.10.7 | ||
| CHECKPATCH := $(KDIR)/scripts/checkpatch.pl | ||
|
|
||
| SRC := sysfs_list.c | ||
| OBJS := $(SRC:.c=.o) | ||
|
|
||
| obj-m += $(OBJS) | ||
|
|
||
| all: | ||
| $(CHECKPATCH) -f $(SRC) || exit 1 | ||
| $(MAKE) -C $(KDIR) M=$(PWD) modules | ||
|
|
||
| default: all | ||
|
|
||
| clean: | ||
| $(MAKE) -C $(KDIR) M=$(PWD) clean | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| victor@orion5491 ~/work/gl_kernel_procamp_2021/04_basic_struct task04 make | ||
| ~/work/buildroot-2021.02.7/output/build/linux-5.10.7/scripts/checkpatch.pl -f sysfs_list.c || exit 1 | ||
| total: 0 errors, 0 warnings, 176 lines checked | ||
|
|
||
| sysfs_list.c has no obvious style problems and is ready for submission. | ||
| make -C ~/work/buildroot-2021.02.7/output/build/linux-5.10.7 M=/home/victor/work/gl_kernel_procamp_2021/04_basic_struct modules | ||
| make[1]: Entering directory '/home/victor/work/buildroot-2021.02.7/output/build/linux-5.10.7' | ||
| CC [M] /home/victor/work/gl_kernel_procamp_2021/04_basic_struct/sysfs_list.o | ||
| MODPOST /home/victor/work/gl_kernel_procamp_2021/04_basic_struct/Module.symvers | ||
| WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. | ||
| CC [M] /home/victor/work/gl_kernel_procamp_2021/04_basic_struct/sysfs_list.mod.o | ||
| LD [M] /home/victor/work/gl_kernel_procamp_2021/04_basic_struct/sysfs_list.ko | ||
| make[1]: Leaving directory '/home/victor/work/buildroot-2021.02.7/output/build/linux-5.10.7' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| ... | ||
| OK | ||
| Starting dropbear sshd: OK | ||
|
|
||
| Welcome to Buildroot | ||
| buildroot login: root | ||
| # cd /home/user | ||
| # ls | ||
| sysfs_list.ko | ||
| # dmesg -c | ||
| Linux version 5.10.7 (victor@orion5491) (x86_64-buildroot-linux-uclibc-gcc.br_real (Buildroot 2021.02.7) 9.4.0, GNU ld (GNU Binutils) 2.35.2) #1 SMP Sun Nov 28 01:28:29 EET 2021 | ||
| Command line: rootwait root=/dev/vda console=tty1 console=ttyS0 | ||
| x86/fpu: x87 FPU will use FXSAVE | ||
| ... | ||
| # insmod sysfs_list.ko | ||
| sysfs_list: loading out-of-tree module taints kernel. | ||
| # echo "Foo!" > /sys/kernel/mod_sysfs/list | ||
| Node added: Foo! | ||
| # echo "Bar!" > /sys/kernel/mod_sysfs/list | ||
| Node added: Bar! | ||
| # echo "12345" > /sys/kernel/mod_sysfs/list | ||
| Node added: 12345 | ||
| # echo "%3@*&%#@" > /sys/kernel/mod_sysfs/list | ||
| Node added: %3@*&%#@ | ||
| # cat /sys/kernel/mod_sysfs/list | ||
| Foo! | ||
| Bar! | ||
| 12345 | ||
| %3@*&%#@ | ||
| # dmesg | ||
| sysfs_list: loading out-of-tree module taints kernel. | ||
| Node added: Foo! | ||
| Node added: Bar! | ||
| Node added: 12345 | ||
| Node added: %3@*&%#@ | ||
| # rmmod sysfs_list.ko | ||
| 4 nodes freed, the list is empty | ||
| # exit | ||
|
|
||
| Welcome to Buildroot | ||
| buildroot login: qemu-system-x86_64: terminating on signal 2 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| #include <linux/init.h> | ||
| #include <linux/kernel.h> | ||
| #include <linux/kobject.h> | ||
| #include <linux/list.h> | ||
| #include <linux/module.h> | ||
| #include <linux/slab.h> | ||
| #include <linux/string.h> | ||
| #include <linux/sysfs.h> | ||
| #include <linux/types.h> | ||
|
|
||
| MODULE_LICENSE("GPL"); | ||
| MODULE_AUTHOR("Victor Krasnoshchok <ct3da21164@protonmail.ch>"); | ||
| MODULE_DESCRIPTION("Interface to a linked list of strings through sysfs."); | ||
| MODULE_VERSION("0.1"); | ||
|
|
||
| /* ------------------------------------------ */ | ||
| /* List-related logic and data manipulation. */ | ||
|
|
||
| struct data_item { | ||
| struct list_head list_node; | ||
| char *payload; | ||
| size_t payload_len; | ||
| }; | ||
|
|
||
| static struct list_head root_node = { NULL, NULL }; | ||
|
|
||
| static ssize_t add_node(struct list_head *parent, const char *node_data, | ||
| size_t node_data_len) | ||
| { | ||
| struct data_item *new_item; | ||
| size_t payload_len; | ||
|
|
||
| if (parent == NULL || node_data == NULL) | ||
| return -EINVAL; | ||
|
|
||
| new_item = kmalloc(sizeof(struct data_item), GFP_KERNEL); | ||
| if (new_item == NULL) | ||
| return -ENOMEM; | ||
|
|
||
| payload_len = node_data_len + 1; /* Including \0 */ | ||
| new_item->payload = kzalloc(payload_len, GFP_KERNEL); | ||
| if (new_item->payload == NULL) { | ||
| pr_err("Failed to alloc. %lu bytes for payload.", payload_len); | ||
| kfree(new_item); | ||
| return -ENOMEM; | ||
| } | ||
|
|
||
| strncpy(new_item->payload, node_data, node_data_len); | ||
| new_item->payload_len = node_data_len; | ||
|
|
||
| list_add_tail(&new_item->list_node, parent); | ||
|
|
||
| /* No extra newline needed here - echo appends NLs by itself */ | ||
| pr_info("Node added: %s", new_item->payload); | ||
| return node_data_len; | ||
| } | ||
|
|
||
| static ssize_t serialize_list(struct list_head *list_root, char *output) | ||
| { | ||
| ssize_t total_len = 0; | ||
| char *wr_ptr = output; | ||
| struct list_head *curr_node; | ||
| struct data_item *curr_data; | ||
|
|
||
| if (output == NULL) | ||
| return -EINVAL; | ||
|
|
||
| list_for_each(curr_node, list_root) { | ||
| curr_data = list_entry(curr_node, struct data_item, list_node); | ||
| strncpy(wr_ptr, curr_data->payload, curr_data->payload_len); | ||
| wr_ptr += curr_data->payload_len; | ||
| total_len += curr_data->payload_len; | ||
| } | ||
|
|
||
| return total_len; | ||
| } | ||
|
|
||
| static void destroy_list(struct list_head *list_root) | ||
| { | ||
| struct data_item *curr_data_item, *placeholder; | ||
| size_t nodes_cnt = 0; | ||
|
|
||
| list_for_each_entry_safe(curr_data_item, placeholder, list_root, | ||
| list_node) { | ||
| kfree(curr_data_item->payload); | ||
| list_del(&curr_data_item->list_node); | ||
| kfree(curr_data_item); | ||
| ++nodes_cnt; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not necessary please avoid pre-increment
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, but I didn't get what's wrong with pre-increment itself. As far as I remember, pre-incrementing works slightly faster (takes something like 1 or 2 instructions less than post-incrementing) by avoiding the need for a temporary storage for the value being incremented. Here I use nodes_cnt variable just to track how many nodes I had in the list before freeing; it wasn't required in the assignment, I just added this log line for myself for debugging purposes, since I failed to find any API which returns list nodes count by a single call.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also I've just checked https://www.kernel.org/doc/html/v4.10/process/coding-style.html - there seems to be no recommendations regarding using or not using prefix increment operation. The only mentions of "increment" word are these:
|
||
| } | ||
|
|
||
| pr_info("%lu nodes freed, the list is%sempty\n", nodes_cnt, | ||
| (list_empty_careful(list_root) ? " " : " NOT ")); | ||
| } | ||
|
|
||
| /* ------------------------------------------ */ | ||
| /* Generic module context. */ | ||
|
|
||
| static ssize_t mod_sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
| char *buf) | ||
| { | ||
| (void)kobj; | ||
| (void)attr; | ||
|
|
||
| return serialize_list(&root_node, buf); | ||
| } | ||
|
|
||
| static ssize_t mod_sysfs_store(struct kobject *kobj, | ||
| struct kobj_attribute *attr, const char *buf, | ||
| size_t count) | ||
| { | ||
| (void)kobj; | ||
| (void)attr; | ||
|
|
||
| return add_node(&root_node, buf, count); | ||
| } | ||
|
|
||
| static struct module_context { | ||
| struct kobj_attribute mod_attr; | ||
| struct kobject *mod_kobj; | ||
| } mod_sysfs_ctx = { __ATTR(list, 0644, mod_sysfs_show, mod_sysfs_store), NULL }; | ||
|
|
||
| static int module_context_init(struct module_context *ctx) | ||
| { | ||
| int ret; | ||
|
|
||
| if (ctx == NULL) | ||
| return -EINVAL; | ||
|
|
||
| if (ctx->mod_kobj != NULL) { | ||
| pr_err("Context has been already initialized."); | ||
| return -EEXIST; | ||
| } | ||
|
|
||
| ctx->mod_kobj = kobject_create_and_add("mod_sysfs", kernel_kobj); | ||
| if (ctx->mod_kobj == NULL) | ||
| return -ENOMEM; | ||
|
|
||
| ret = sysfs_create_file(ctx->mod_kobj, &ctx->mod_attr.attr); | ||
| if (ret) { | ||
| kobject_put(ctx->mod_kobj); | ||
| ctx->mod_kobj = NULL; | ||
| } | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static void module_context_cleanup(struct module_context *ctx) | ||
| { | ||
| if (ctx->mod_kobj != NULL) | ||
| kobject_put(ctx->mod_kobj); | ||
| } | ||
|
|
||
| static int __init mod_sysfs_init(void) | ||
| { | ||
| int ret = module_context_init(&mod_sysfs_ctx); | ||
|
|
||
| if (ret != 0) { | ||
| pr_err("Ctx init. failed: %d", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| INIT_LIST_HEAD(&root_node); | ||
| return 0; | ||
| } | ||
|
|
||
| static void __exit mod_sysfs_exit(void) | ||
| { | ||
| destroy_list(&root_node); | ||
| module_context_cleanup(&mod_sysfs_ctx); | ||
| } | ||
|
|
||
| module_init(mod_sysfs_init); | ||
| module_exit(mod_sysfs_exit); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.