Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions 04_basic_struct/Makefile
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

13 changes: 13 additions & 0 deletions 04_basic_struct/build_log.txt
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'
41 changes: 41 additions & 0 deletions 04_basic_struct/console_log.txt
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
176 changes: 176 additions & 0 deletions 04_basic_struct/sysfs_list.c
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;
Comment thread
militant-daos marked this conversation as resolved.
Outdated
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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not necessary please avoid pre-increment

Copy link
Copy Markdown
Author

@militant-daos militant-daos Dec 29, 2021

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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:

no space before the postfix increment & decrement unary operators:
++ --
no space after the prefix increment & decrement unary operators:
++ --

}

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);