From f272f1d226212b81f1ce9b31e5e0188efeaa2a02 Mon Sep 17 00:00:00 2001 From: Ivan Stepanenko Date: Sun, 21 Nov 2021 19:36:39 +0200 Subject: [PATCH 01/12] TASK1: Add rock-paper-scissors game The game logic is defined in "rock_paper_scissors" module. Additional task printing md5 checksum of computer's choice before human's move is added. $ make clean all && ./rock-paper-scissors /bin/rm -rf *.o a.out rock-paper-scissors gcc -c rock_paper_scissors.c -I. gcc -c main.c -I. gcc rock_paper_scissors.o main.o -o rock-paper-scissors -I. -lcrypto Random choice: s md5: 03c7c0ace395d80182db07ae2c30f034 Please choose: rock (r) - paper (p) - scissors (s) r You choose rock, I choose scissors You win: rock beats scissors Signed-off-by: Ivan Stepanenko --- 01_git/scissors/Makefile | 18 +++ 01_git/scissors/main.c | 7 ++ 01_git/scissors/rock_paper_scissors.c | 161 ++++++++++++++++++++++++++ 01_git/scissors/rock_paper_scissors.h | 23 ++++ 4 files changed, 209 insertions(+) create mode 100644 01_git/scissors/Makefile create mode 100644 01_git/scissors/main.c create mode 100644 01_git/scissors/rock_paper_scissors.c create mode 100644 01_git/scissors/rock_paper_scissors.h diff --git a/01_git/scissors/Makefile b/01_git/scissors/Makefile new file mode 100644 index 0000000..341ed08 --- /dev/null +++ b/01_git/scissors/Makefile @@ -0,0 +1,18 @@ +CC=gcc +CFLAGS=-I. +CRYPTOLAGS=-lcrypto +OUT_NAME=rock-paper-scissors + +all: $(OUT_NAME) + +$(OUT_NAME): rock_paper_scissors.o main.o + $(CC) rock_paper_scissors.o main.o -o $(OUT_NAME) $(CFLAGS) $(CRYPTOLAGS) + +main.o: main.c + $(CC) -c main.c $(CFLAGS) + +rock_paper_scissors.o: rock_paper_scissors.c + $(CC) -c rock_paper_scissors.c $(CFLAGS) + +clean: + /bin/rm -rf *.o a.out $(OUT_NAME) diff --git a/01_git/scissors/main.c b/01_git/scissors/main.c new file mode 100644 index 0000000..6e584a4 --- /dev/null +++ b/01_git/scissors/main.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + RPS_run_game(); + return 0; +} diff --git a/01_git/scissors/rock_paper_scissors.c b/01_git/scissors/rock_paper_scissors.c new file mode 100644 index 0000000..20b3040 --- /dev/null +++ b/01_git/scissors/rock_paper_scissors.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include + +#include "rock_paper_scissors.h" + +static RPS_CHOICES_E get_human_choice(void); +static RPS_CHOICES_E get_random_choice(void); +static void RPS_show_game_result(RPS_CHOICES_E comp_choice, RPS_CHOICES_E human_choice); +static void RPS_print_md5(char value); + +static RPS_CHOICE_ENTRY_S choices_arr[] = {{RPS_CHOICE_ROCK, "rock", 'r'}, + {RPS_CHOICE_PAPER, "paper", 'p'}, + {RPS_CHOICE_SCISSORS, "scissors", 's'}, + {RPS_CHOICE_UNKNOWN, "unknown", 'u'}}; + +static RPS_CHOICES_E RPS_get_random_choice(void) +{ + unsigned char buff; + RPS_CHOICES_E choice = RPS_CHOICE_UNKNOWN; + + int fd = open("/dev/urandom", O_RDONLY); + + if (!fd) + { + printf("Error opening /dev/urandom\n"); + exit(1); + } + + read(fd, &buff, sizeof buff); + close(fd); + choice = buff % RPS_N_VALUABLE_CHOICES; + + return choice; +} + + +static RPS_CHOICES_E RPS_get_human_choice(void) +{ + RPS_CHOICES_E choice = RPS_CHOICE_UNKNOWN; + char read_value = '?'; + + printf("Please choose: rock (r) - paper (p) - scissors (s)\n"); + scanf(" %c", &read_value); + + switch (read_value) + { + case 'r': + { + choice = RPS_CHOICE_ROCK; + break; + } + + case 'p': + { + choice = RPS_CHOICE_PAPER; + break; + } + case 's': + { + choice = RPS_CHOICE_SCISSORS; + break; + } + default: + { + choice = RPS_CHOICE_UNKNOWN; + break; + } + } + + return choice; +} + + +static void RPS_print_md5(char value) +{ + MD5_CTX ctx; + unsigned char md5digest[MD5_DIGEST_LENGTH]; + + MD5_Init(&ctx); + MD5_Update(&ctx, (const void *)&value, sizeof value); + MD5_Final(md5digest, &ctx); + + printf("Random choice: %c md5: ", value); + + for ( int i=0;i < MD5_DIGEST_LENGTH; i++ ) { + printf("%02x", md5digest[i]); + }; + printf("\n"); +} + + +static void RPS_show_game_result(RPS_CHOICES_E comp_choice, RPS_CHOICES_E human_choice) +{ + RPS_CHOICE_ENTRY_S *p_human_entry; + RPS_CHOICE_ENTRY_S *p_comp_entry; + int game_result; + + p_comp_entry = &choices_arr[comp_choice]; + p_human_entry = &choices_arr[human_choice]; + + printf("You choose %s, I choose %s\n", p_human_entry->long_name, p_comp_entry->long_name); + + game_result = comp_choice - human_choice; + + switch (game_result) + { + case 0: + { + printf("Draw: %s and %s\n", p_human_entry->long_name, p_comp_entry->long_name); + break; + } + + case 1: + { + printf("I win: %s beats %s\n", p_comp_entry->long_name, p_human_entry->long_name); + break; + } + case 2: + { + printf("You win: %s beats %s\n", p_human_entry->long_name, p_comp_entry->long_name); + break; + } + case -1: + { + printf("You win: %s beats %s\n", p_human_entry->long_name, p_comp_entry->long_name); + break; + } + case -2: + { + printf("I win: %s beats %s\n", p_comp_entry->long_name, p_human_entry->long_name); + break; + } + default: + { + printf("Error game result: %i\n", game_result); + exit(1); + break; + } + } +} + + +void RPS_run_game(void) +{ + RPS_CHOICES_E human_choice = RPS_CHOICE_UNKNOWN; + RPS_CHOICES_E comp_choice = RPS_CHOICE_UNKNOWN; + + comp_choice = RPS_get_random_choice(); + // Optional task: add printing md5 checksum of computer's choice before human's move. + RPS_print_md5(choices_arr[comp_choice].short_name); + + for ( ; human_choice == RPS_CHOICE_UNKNOWN; ) + { + human_choice = RPS_get_human_choice(); + } + + RPS_show_game_result(comp_choice, human_choice); +} diff --git a/01_git/scissors/rock_paper_scissors.h b/01_git/scissors/rock_paper_scissors.h new file mode 100644 index 0000000..f4cd09b --- /dev/null +++ b/01_git/scissors/rock_paper_scissors.h @@ -0,0 +1,23 @@ +#ifndef __ROCK_PAPER_SCISSORS_H_ +#define __ROCK_PAPER_SCISSORS_H_ + +#define RPS_N_VALUABLE_CHOICES 3 + +typedef enum RPS_CHOICES { + RPS_CHOICE_ROCK, + RPS_CHOICE_PAPER, + RPS_CHOICE_SCISSORS, + RPS_CHOICE_UNKNOWN +} RPS_CHOICES_E; + + +typedef struct RPS_CHOICE_ENTRY { + RPS_CHOICES_E id; + char long_name[16]; + char short_name; +} RPS_CHOICE_ENTRY_S; + + +void RPS_run_game(void); + +#endif // __ROCK_PAPER_SCISSORS_H_ From 60eef68462091a3658cb0dea8b66810c42073ffe Mon Sep 17 00:00:00 2001 From: Ivan Stepanenko Date: Sun, 21 Nov 2021 22:17:32 +0200 Subject: [PATCH 02/12] TASK02 Add hot plugged hardware detector script It uses optional script arugment to define polling folder with default value "/dev". The script is polling that folder and uses "diff" utility to detect changes in the output. Here is the script run with current target folder and new 1, 2 files playing role of new devices. $ ./hwdetect.sh . Polling folder: . > ./1 2021-11-21 22:25:33.907655100 +0200 > ./2 2021-11-21 22:25:40.335717754 +0200 Signed-off-by: Ivan Stepanenko --- 02_bash/hwdetect/hwdetect.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100755 02_bash/hwdetect/hwdetect.sh diff --git a/02_bash/hwdetect/hwdetect.sh b/02_bash/hwdetect/hwdetect.sh new file mode 100755 index 0000000..5325c95 --- /dev/null +++ b/02_bash/hwdetect/hwdetect.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +FOLDER=${1:-/dev} +echo "Polling folder: $FOLDER" + +PREV=$(stat --terse --format="%n %z" ${FOLDER}/*|sort -n) + +while true +do + NEW=$(stat --terse --format="%n %z" ${FOLDER}/*|sort -n) + DELTA=$(diff <(echo "$PREV") <(echo "$NEW") | grep '^>') + + if [[ "${#DELTA}" != 0 ]] + then + echo "$DELTA" + fi + PREV=$NEW + sleep 1 +done From cc496d023ba156c5f7c46ca035f55f69c055d7f6 Mon Sep 17 00:00:00 2001 From: Serhii Perederii Date: Fri, 19 Nov 2021 19:01:51 +0200 Subject: [PATCH 03/12] TASK3: Add module task Add description for module task Signed-off-by: Serhii Perederii --- 03_module/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 03_module/README.md diff --git a/03_module/README.md b/03_module/README.md new file mode 100644 index 0000000..739d55a --- /dev/null +++ b/03_module/README.md @@ -0,0 +1,15 @@ +## module home work: create a simple loadable module with parameters + +Create a loadadle kernel module which should accept two integer parameters and provide: + - A sum of parameters upon driver load + - A substration of parameters upon driver unload + +Info about module parameters can be found at: https://devarea.com/linux-kernel-development-kernel-module-parameters/#.YZfWcpFByV4 + +Task should be performed using buildroot+qemu approach + +The task results should contain: +- The module code +- The Makefile +- Dump of the kernel logs from the target system + From 0f9262264ad0dfcc1ab2bc8c015a6ddfdc56a96f Mon Sep 17 00:00:00 2001 From: Oleksandr Posukhov Date: Tue, 23 Nov 2021 20:10:08 +0200 Subject: [PATCH 04/12] Task04: Add task for Lection 03 Add home task for lection Basic Data structures Signed-off-by: Oleksandr Posukhov --- 04_basic_struct/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 04_basic_struct/README.md diff --git a/04_basic_struct/README.md b/04_basic_struct/README.md new file mode 100644 index 0000000..445d6dc --- /dev/null +++ b/04_basic_struct/README.md @@ -0,0 +1,7 @@ +## Basic structure homework +Implement object with name “MyObject” which is parent of kernel_kobj. +Object should include linked_list structure. +This object should contain sysfs attribute with name “list”. +On read form attribute “list” it should show content of the objects linked list. +On write to attribute “list” it should add new string to the objects linked list. +!! Do not forget properly free all the resources during rmmod. From bb6586bc235cac1b56b20bd9946af290cd10b263 Mon Sep 17 00:00:00 2001 From: Ivan Stepanenko Date: Tue, 30 Nov 2021 10:14:22 +0200 Subject: [PATCH 05/12] TASK04: Implement kobject "wimbledon" with sysfs attribute "list" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On read from the “list” attr. it shows content of the linked list. On write to the “list” attr. it adds new string to the linked list. Since the list items separator is not specified - content is shown as it was inserted including line endings. This gives flexibility of using "echo" with "-n" argument and without. Signed-off-by: Ivan Stepanenko --- 04_basic_struct/Makefile | 9 +++ 04_basic_struct/dump.txt | 37 +++++++++++ 04_basic_struct/wimbledon.c | 118 ++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 04_basic_struct/Makefile create mode 100644 04_basic_struct/dump.txt create mode 100644 04_basic_struct/wimbledon.c diff --git a/04_basic_struct/Makefile b/04_basic_struct/Makefile new file mode 100644 index 0000000..14231e1 --- /dev/null +++ b/04_basic_struct/Makefile @@ -0,0 +1,9 @@ +KERNELDIR ?= ../output/build/linux-5.10.7 + +obj-m := wimbledon.o + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean diff --git a/04_basic_struct/dump.txt b/04_basic_struct/dump.txt new file mode 100644 index 0000000..29f5ecb --- /dev/null +++ b/04_basic_struct/dump.txt @@ -0,0 +1,37 @@ +# insmod /tmp/wimbledon.ko +wimbledon: loading out-of-tree module taints kernel. +# echo "1" > /sys/kernel/wimbledon/list +wimbledon: adding msg size: 2 to the list: '1 +' +# echo -n "2" > /sys/kernel/wimbledon/list +wimbledon: adding msg size: 1 to the list: '2' +# echo -n "33" > /sys/kernel/wimbledon/list +wimbledon: adding msg size: 2 to the list: '33' +# echo "44" > /sys/kernel/wimbledon/list +wimbledon: adding msg size: 3 to the list: '44 +' +# echo -n "55" > /sys/kernel/wimbledon/list +wimbledon: adding msg size: 2 to the list: '55' +# cat /sys/kernel/wimbledon/list +wimbledon: msg #0 size: 2 '1 +' +wimbledon: msg #1 size: 1 '2' +wimbledon: msg #2 size: 2 '33' +wimbledon: msg #3 size: 3 '44 +' +wimbledon: msg #4 size: 2 '55' +1 +23344 +55# + +# rmmod wimbledon.ko +wimbledon: freeing msg size: 2 from the list: '55' +wimbledon: freeing msg size: 3 from the list: '44 +' +wimbledon: freeing msg size: 2 from the list: '33' +wimbledon: freeing msg size: 1 from the list: '2' +wimbledon: freeing msg size: 2 from the list: '1 +' +wimbledon: list is empty: 1 +wimbledon: Module exited + diff --git a/04_basic_struct/wimbledon.c b/04_basic_struct/wimbledon.c new file mode 100644 index 0000000..c05e758 --- /dev/null +++ b/04_basic_struct/wimbledon.c @@ -0,0 +1,118 @@ +/* + * Name of this module is English wiki random article choice. + * To get away from "hello", "foo", "bar", "baz"... names. + */ +#define DEBUG +#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME +#include +#include +#include +#include +#include +#include +#include + + +struct msg_item { + struct list_head list; + char *msg; + size_t msg_len; +}; +static LIST_HEAD(msg_list_head); + +static ssize_t wimbledon_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff); +static ssize_t wimbledon_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t count); +static int wimbledon_init(void); +static void wimbledon_exit(void); + +static struct kobj_attribute list_attribute = + __ATTR(list, 0644, wimbledon_show, wimbledon_store); +static struct kobject *wimbledon_kobj; + + +static ssize_t wimbledon_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) +{ + struct msg_item *pos = NULL; + size_t item_number = 0; + size_t count = 0; + char *p_buff = buff; + + list_for_each_entry_reverse(pos, &msg_list_head, list) { + pr_info("msg #%lu size: %lu '%.*s'\n", + item_number, pos->msg_len, (int)pos->msg_len, (char *)pos->msg); + + memcpy(p_buff, (char *)pos->msg, pos->msg_len); + p_buff += pos->msg_len; + count += pos->msg_len; + item_number += 1; + } + return count; +} + + +static ssize_t wimbledon_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t count) +{ + char *p_msg = NULL; + struct msg_item *p_item = NULL; + + p_msg = kmalloc(count, GFP_KERNEL); + if (!p_msg) { + pr_err("could not allocate msg cnt: %lu\n", count); + return -ENOMEM; + } + p_item = kmalloc(sizeof(struct msg_item), GFP_KERNEL); + if (!p_item) { + pr_err("could not allocate msg_item\n"); + return -ENOMEM; + } + memcpy(p_msg, buff, count); + p_item->msg = p_msg; + p_item->msg_len = count; + + pr_info("adding msg size: %lu to the list: '%.*s'\n", + p_item->msg_len, (int)p_item->msg_len, (char *)p_item->msg); + list_add(&p_item->list, &msg_list_head); + return count; +} + + +static int wimbledon_init(void) +{ + int res = 0; + wimbledon_kobj = kobject_create_and_add("wimbledon", kernel_kobj); + if (!wimbledon_kobj) + return -ENOMEM; + res = sysfs_create_file(wimbledon_kobj, &list_attribute.attr); + if (res) + kobject_put(wimbledon_kobj); + + return res; +} + + +static void wimbledon_exit(void) +{ + struct list_head *pos = NULL; + struct list_head *n = NULL; + struct msg_item *p_item = NULL; + + list_for_each_safe(pos, n, &msg_list_head) { + p_item = list_entry(pos, struct msg_item, list); + pr_info("freeing msg size: %lu from the list: '%.*s'\n", + p_item->msg_len, (int)p_item->msg_len, (char *)p_item->msg); + list_del(pos); + kfree(p_item->msg); + kfree(p_item); + } + pr_info("list is empty: %d\n", list_empty_careful(&msg_list_head)); + kobject_put(wimbledon_kobj); + pr_info("Module exited\n"); +} + +module_init(wimbledon_init); +module_exit(wimbledon_exit); + +MODULE_AUTHOR("Ivan Stepanenko "); +MODULE_DESCRIPTION("Wimbledon module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); From 120488e365fb867a5e38b363dcac2ab2018f7381 Mon Sep 17 00:00:00 2001 From: Ivan Stepanenko Date: Mon, 29 Nov 2021 23:00:47 +0200 Subject: [PATCH 06/12] TASK03: Add simple loadable module with parameters The module accepts two integer parameters n1 and n2 and provides: - A sum of parameters upon the module load - A substration of parameters upon the module unload Signed-off-by: Ivan Stepanenko --- 03_module/Makefile | 9 +++++++++ 03_module/dump.txt | 11 +++++++++++ 03_module/hello.c | 26 ++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 03_module/Makefile create mode 100644 03_module/dump.txt create mode 100644 03_module/hello.c diff --git a/03_module/Makefile b/03_module/Makefile new file mode 100644 index 0000000..8dbe20a --- /dev/null +++ b/03_module/Makefile @@ -0,0 +1,9 @@ +KERNELDIR ?= ../output/build/linux-5.10.7 + +obj-m := hello.o + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean diff --git a/03_module/dump.txt b/03_module/dump.txt new file mode 100644 index 0000000..1eef35d --- /dev/null +++ b/03_module/dump.txt @@ -0,0 +1,11 @@ +# insmod /tmp/hello.ko n1=42 n2=-10 +hello add n1: 42 and n2: -10 result: 32 +# cd /sys/module/hello/parameters/ +# ls +n1 n2 +# cat n1 +42 +# cat n2 +-10 +# rmmod hello.ko +Bye substract n1: 42 and n2: -10 result: 52 diff --git a/03_module/hello.c b/03_module/hello.c new file mode 100644 index 0000000..7ecdbc1 --- /dev/null +++ b/03_module/hello.c @@ -0,0 +1,26 @@ +#include +#include +#include + + +static int n1=0; +module_param(n1, int, 0660); + +static int n2=0; +module_param(n2, int, 0660); + +int init_module(void) +{ + printk(KERN_INFO "hello add n1: %d and n2: %d result: %d\n", n1, n2, n1+n2); + return 0; +} + +void cleanup_module(void) +{ + printk(KERN_INFO "Bye substract n1: %d and n2: %d result: %d\n", n1, n2, n1-n2); +} + +MODULE_AUTHOR("Ivan Stepanenko "); +MODULE_DESCRIPTION("Hello world module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); From f8199a0d2bbccb2bfeed70f994188e89441df457 Mon Sep 17 00:00:00 2001 From: Yevgen Kovalyov Date: Wed, 1 Dec 2021 00:13:52 +0200 Subject: [PATCH 07/12] Task05: Add tasks for lesson 5 Time Management Add descriptions of tasks for lesson 5. Signed-off-by: Yevgen Kovalyov --- 05_timers/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 05_timers/README.md diff --git a/05_timers/README.md b/05_timers/README.md new file mode 100644 index 0000000..55b41f2 --- /dev/null +++ b/05_timers/README.md @@ -0,0 +1,10 @@ +## Homework: Linux Kernel Time Management + +1. Implement program which return absolute time in user space. +Use clock_gettime() from time.h. Try different clock id. +Find the difference. Show possible clock resolution provided by clock_getres(). + +2. Implement kernel module with API in sysfs, which returns relative +time in maximum possible resolution passed since previous read of it. +Implement kernel module with API in sysfs which returns absolute time +of previous reading with maximum resolution like ‘400.123567’ seconds. From d7b7182bb7beb929ebd471d3a07a47b12316f4c5 Mon Sep 17 00:00:00 2001 From: Yevgen Kovalyov Date: Thu, 2 Dec 2021 19:36:43 +0200 Subject: [PATCH 08/12] Task06: Add tasks for lesson 6 Memory Management --- 06_memory/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 06_memory/README.md diff --git a/06_memory/README.md b/06_memory/README.md new file mode 100644 index 0000000..4c22613 --- /dev/null +++ b/06_memory/README.md @@ -0,0 +1,22 @@ +# Memory management + +## Homework +1. Create user-space C or C++ program which tries to allocate buffers + with sizes 2^x for x in range from 0 to maximium possible value + using functions: + **malloc, calloc, alloca, (optional for C++) new **. + Measure time of each allocation/freeing. + 2^x means x power of 2 in this task. +Pull request should contains program source code and program output +in text format. + +2. Create kernel module and test allocation/freeing time for functions: + **kmalloc, kzmalloc, vmalloc, get_free_pages, + (optional and only for drivers integrated to kernel)alloc_bootmem**. + Measure the time of each allocation/freeing except alloc_bootmem. + The results should be presented in text file table with followed columns: + Buffer size, allocation time, freeing time. + Size unit is 1 byte, time unit is 1 ns. + +Pull request should contains source code of developed driver, Makefile +and program output from system log in text format. From 8d48fb8dbe60c6ac2a5674e7532c191590005ac4 Mon Sep 17 00:00:00 2001 From: Ivan Stepanenko Date: Mon, 13 Dec 2021 12:31:06 +0200 Subject: [PATCH 09/12] TASK05: Implement module that shows abs&relat time since last read First read of the "last_abs_time" attribute returns -1 next reads return epoch time of the previous read cat /sys/kernel/time_attr_presenter/last_abs_time -1 cat /sys/kernel/time_attr_presenter/last_abs_time 1639337982.183260522 First read of the "last_relative_time" attribute returns -1 next reads return time passed since previous read cat /sys/kernel/time_attr_presenter/last_relative_time -1 cat /sys/kernel/time_attr_presenter/last_relative_time 0.636000000 Signed-off-by: Ivan Stepanenko --- 05_timers/kernel_module/Makefile | 9 ++ 05_timers/kernel_module/dump.txt | 25 ++++ 05_timers/kernel_module/time_attr_presenter.c | 112 ++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 05_timers/kernel_module/Makefile create mode 100644 05_timers/kernel_module/dump.txt create mode 100644 05_timers/kernel_module/time_attr_presenter.c diff --git a/05_timers/kernel_module/Makefile b/05_timers/kernel_module/Makefile new file mode 100644 index 0000000..a7fac31 --- /dev/null +++ b/05_timers/kernel_module/Makefile @@ -0,0 +1,9 @@ +KERNELDIR ?= ../output/build/linux-5.10.7 + +obj-m := time_attr_presenter.o + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean diff --git a/05_timers/kernel_module/dump.txt b/05_timers/kernel_module/dump.txt new file mode 100644 index 0000000..bb7d3f0 --- /dev/null +++ b/05_timers/kernel_module/dump.txt @@ -0,0 +1,25 @@ +# insmod /tmp/time_attr_presenter.ko +# cat /sys/kernel/time_attr_presenter/last_abs_time +-1 +# cat /sys/kernel/time_attr_presenter/last_abs_time +1639337982.183260522 +# cat /sys/kernel/time_attr_presenter/last_abs_time +1639337984.166915503 +# cat /sys/kernel/time_attr_presenter/last_relative_time +-1 +# cat /sys/kernel/time_attr_presenter/last_relative_time +0.636000000 +# cat /sys/kernel/time_attr_presenter/last_relative_time +1.812000000 +# cat /sys/kernel/time_attr_presenter/last_relative_time +0.752000000 +# rmmod time_attr_presenter.ko +time_attr_presenter: Module exited +# ls /sys/kernel/time_attr_presenter +ls: /sys/kernel/time_attr_presenter: No such file or directory + +# unix timestamps are matched with real time +date -d @1639337984.166915503 +неділя, 12 грудня 2021 21:39:44 +0200 +date -d @1639337982.183260522 +неділя, 12 грудня 2021 21:39:42 +0200 diff --git a/05_timers/kernel_module/time_attr_presenter.c b/05_timers/kernel_module/time_attr_presenter.c new file mode 100644 index 0000000..abc0510 --- /dev/null +++ b/05_timers/kernel_module/time_attr_presenter.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 1 Implement kernel module with API in sysfs, + * which returns relative time in maximum possible + * resolution passed since previous read of it. + * 2 Implement kernel module with API in sysfs + * which returns absolute time of previous reading + * with maximum resolution like ‘400.123567’ seconds. + */ + + +#define DEBUG +#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static ssize_t time_attr_presenter_last_abs_time_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff); +static ssize_t time_attr_presenter_last_relative_time_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff); +static ssize_t time_attr_presenter_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t count); +static int time_attr_presenter_init(void); +static void time_attr_presenter_exit(void); + + +static struct kobject *time_attr_presenter_kobj; +static u64 last_read_jiffies; +static struct timespec64 last_read_time; + +static struct kobj_attribute param_abs_attribute = + __ATTR(last_abs_time, 0644, time_attr_presenter_last_abs_time_show, time_attr_presenter_store); +static struct kobj_attribute param_relative_attribute = + __ATTR(last_relative_time, 0644, time_attr_presenter_last_relative_time_show, time_attr_presenter_store); + + +static ssize_t time_attr_presenter_last_abs_time_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) +{ + if (last_read_time.tv_sec == 0 && last_read_time.tv_nsec == 0) + sprintf(buff, "-1\n"); + else + sprintf(buff, "%lli.%li\n", last_read_time.tv_sec, last_read_time.tv_nsec); + + ktime_get_real_ts64(&last_read_time); + + return strlen(buff); +} + +static ssize_t time_attr_presenter_last_relative_time_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) +{ + u64 delta = 0; + struct timespec64 ts; + + if (last_read_jiffies == 0) { + sprintf(buff, "-1\n"); + } else { + delta = get_jiffies_64() - last_read_jiffies; + jiffies_to_timespec64(delta, &ts); + sprintf(buff, "%lli.%li\n", ts.tv_sec, ts.tv_nsec); + } + last_read_jiffies = get_jiffies_64(); + + return strlen(buff); +} + + +static ssize_t time_attr_presenter_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t count) +{ + return count; +} + + +static int time_attr_presenter_init(void) +{ + int res = 0; + + time_attr_presenter_kobj = kobject_create_and_add("time_attr_presenter", kernel_kobj); + if (!time_attr_presenter_kobj) + return -ENOMEM; + res = sysfs_create_file(time_attr_presenter_kobj, ¶m_relative_attribute.attr); + if (res) + kobject_put(time_attr_presenter_kobj); + res = sysfs_create_file(time_attr_presenter_kobj, ¶m_abs_attribute.attr); + if (res) + kobject_put(time_attr_presenter_kobj); + + last_read_jiffies = 0; + last_read_time.tv_sec = 0; + last_read_time.tv_nsec = 0; + + return res; +} + + +static void time_attr_presenter_exit(void) +{ + kobject_put(time_attr_presenter_kobj); + pr_info("Module exited\n"); +} + +module_init(time_attr_presenter_init); +module_exit(time_attr_presenter_exit); + +MODULE_AUTHOR("Ivan Stepanenko "); +MODULE_DESCRIPTION("Time attribute presenter module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); From 15324e0f9d572d73fe935825ee86fb2c8c5129f5 Mon Sep 17 00:00:00 2001 From: Ivan Stepanenko Date: Mon, 13 Dec 2021 12:40:09 +0200 Subject: [PATCH 10/12] TASK05: Implement program which return abs time in user space. Useed clock_gettime() from time.h and different clock ids. Provided dump files with results. Signed-off-by: Ivan Stepanenko --- 05_timers/user_space/Makefile | 11 +++++ 05_timers/user_space/dump1.txt | 41 +++++++++++++++ 05_timers/user_space/user_space_timers.c | 63 ++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 05_timers/user_space/Makefile create mode 100644 05_timers/user_space/dump1.txt create mode 100644 05_timers/user_space/user_space_timers.c diff --git a/05_timers/user_space/Makefile b/05_timers/user_space/Makefile new file mode 100644 index 0000000..4142923 --- /dev/null +++ b/05_timers/user_space/Makefile @@ -0,0 +1,11 @@ +CC=gcc +CFLAGS=-I. -std=c99 -D_POSIX_C_SOURCE=199309L +OUT_NAME=user-space-timers + +all: $(OUT_NAME) + +$(OUT_NAME): user_space_timers.c + $(CC) user_space_timers.c -o $(OUT_NAME) $(CFLAGS) + +clean: + /bin/rm -rf *.o a.out $(OUT_NAME) diff --git a/05_timers/user_space/dump1.txt b/05_timers/user_space/dump1.txt new file mode 100644 index 0000000..fa0ef03 --- /dev/null +++ b/05_timers/user_space/dump1.txt @@ -0,0 +1,41 @@ +ivan@ivan-VirtualBox:~/buildroot/buildroot-2021.02.7/05-timers$ gcc -std=c99 -D_POSIX_C_SOURCE=199309L main.c && ./a.out ; echo $? +getting clock resolution... +---------------------------------------------------------------- +|clock_id |sec |nsec | +---------------------------------------------------------------- +|CLOCK_REALTIME |0 |1 | +|CLOCK_MONOTONIC |0 |1 | +|CLOCK_PROCESS_CPUTIM|0 |1 | +|CLOCK_THREAD_CPUTIME|0 |1 | +|CLOCK_BOOTTIME |0 |1 | + +getting actual clock values... +---------------------------------------------------------------- +|clock_id |sec |nsec | +---------------------------------------------------------------- +|CLOCK_REALTIME |1638701883 |825167534 | +|CLOCK_MONOTONIC |6539 |56162158 | +|CLOCK_PROCESS_CPUTIM|0 |625613 | +|CLOCK_THREAD_CPUTIME|0 |626629 | +|CLOCK_BOOTTIME |6539 |56164193 | + +Conclusion: +*1 I have started with clock resolutions for different clock_id. +In my case all of them have 1nsec resolution which is expected for the host system. + +*2 Then I have contnued with actual clock values. +CLOCK_REALTIME is matched with localtime + +ivan@ivan-VirtualBox:~/buildroot/buildroot-2021.02.7/05-timers$ date -d @1638701883.825167534 +неділя, 5 грудня 2021 12:58:03 +0200 + +CLOCK_MONOTONIC is coresponding with the system uptime (take into account time taken for this report writing). +6539/60=108 minutes +van@ivan-VirtualBox:~$ w + 13:08:06 up 1:59, 1 user, load average: 0,05, 0,07, 0,08 + +The difference is explained by the time spent on this report writing. + +CLOCK_PROCESS_CPUTIM and CLOCK_THREAD_CPUTIME are short and very close. In this task I do not expect another result. +CLOCK_BOOTTIME is mathched with CLOCK_MONOTONIC here, but also includes suspended time which I do not have for the moment. +have not managed to get difference between CLOCK_BOOTTIME and CLOCK_MONOTONIC diff --git a/05_timers/user_space/user_space_timers.c b/05_timers/user_space/user_space_timers.c new file mode 100644 index 0000000..7f23a39 --- /dev/null +++ b/05_timers/user_space/user_space_timers.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Implement program which return absolute time in user space. + * Use clock_gettime() from time.h. Try different clock id. Find the difference. + * Show possible clock resolution provided by clock_getres(). + */ + +#include +#include +#include +#include +#include + +int main(void) +{ + struct timespec real_ts, mono_ts, pcpu_ts, tcpu_ts, boot_ts; + struct timespec real_tp, mono_tp, pcpu_tp, tcpu_tp, boot_tp; + + printf("getting clock resolution...\n"); + if (clock_getres(CLOCK_REALTIME, &real_ts)) + perror(strerror(errno)); + if (clock_getres(CLOCK_MONOTONIC, &mono_ts)) + perror(strerror(errno)); + if (clock_getres(CLOCK_PROCESS_CPUTIME_ID, &pcpu_ts)) + perror(strerror(errno)); + if (clock_getres(CLOCK_THREAD_CPUTIME_ID, &tcpu_ts)) + perror(strerror(errno)); + if (clock_getres(CLOCK_BOOTTIME, &boot_ts)) + perror(strerror(errno)); + + printf("----------------------------------------------------------------\n"); + printf("|%-20.20s|%-20.20s|%-20.20s|\n", "clock_id", "sec", "nsec"); + printf("----------------------------------------------------------------\n"); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_REALTIME", real_ts.tv_sec, real_ts.tv_nsec); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_MONOTONIC", mono_ts.tv_sec, mono_ts.tv_nsec); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_PROCESS_CPUTIME_ID", pcpu_ts.tv_sec, pcpu_ts.tv_nsec); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_THREAD_CPUTIME_ID", tcpu_ts.tv_sec, tcpu_ts.tv_nsec); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_BOOTTIME", boot_ts.tv_sec, boot_ts.tv_nsec); + printf("\n"); + + printf("getting actual clock values...\n"); + if (clock_gettime(CLOCK_REALTIME, &real_tp)) + perror(strerror(errno)); + if (clock_gettime(CLOCK_MONOTONIC, &mono_tp)) + perror(strerror(errno)); + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pcpu_tp)) + perror(strerror(errno)); + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tcpu_tp)) + perror(strerror(errno)); + if (clock_gettime(CLOCK_BOOTTIME, &boot_tp)) + perror(strerror(errno)); + + printf("----------------------------------------------------------------\n"); + printf("|%-20.20s|%-20.20s|%-20.20s|\n", "clock_id", "sec", "nsec"); + printf("----------------------------------------------------------------\n"); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_REALTIME", real_tp.tv_sec, real_tp.tv_nsec); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_MONOTONIC", mono_tp.tv_sec, mono_tp.tv_nsec); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_PROCESS_CPUTIME_ID", pcpu_tp.tv_sec, pcpu_tp.tv_nsec); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_THREAD_CPUTIME_ID", tcpu_tp.tv_sec, tcpu_tp.tv_nsec); + printf("|%-20.20s|%-20li|%-20li|\n", "CLOCK_BOOTTIME", boot_tp.tv_sec, boot_tp.tv_nsec); + printf("\n"); + + return 0; +} From 488587afc39438d3a521bc4736924565133570d1 Mon Sep 17 00:00:00 2001 From: Oleksandr Posukhov Date: Wed, 8 Dec 2021 16:50:33 +0200 Subject: [PATCH 11/12] Task07: Add procfs task for lesson 7 ProcFS and platform bootup Add task for implementation of procFS API Signed-off-by: Oleksandr Posukhov --- 07_procfs/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 07_procfs/README.md diff --git a/07_procfs/README.md b/07_procfs/README.md new file mode 100644 index 0000000..7fa3528 --- /dev/null +++ b/07_procfs/README.md @@ -0,0 +1,10 @@ +## Lesson 07 - ProcFS interface and orange pi bootup + +* Update your existing sysfs kernel module with procfs API: +* Create folder in procfs file system; +* Create entry that returns module author name; +* Create entry that returns amount of "store" callback calls; +* Create entry that returns amount of "show" callback calls. +* Build image for orange pi zero +* Attach console output from your development board + From 64ac4f0b0d9edaad31a9a5f6c6d8f2041c5dcb30 Mon Sep 17 00:00:00 2001 From: Ivan Stepanenko Date: Sun, 19 Dec 2021 16:19:03 +0200 Subject: [PATCH 12/12] TASK07: Update kenel module with procfs API Existing "wimbledone" module already has sysfs interface "/sys/kernel/wimbledon/list". By reading and wrinting this file "show" and "store" callbacks are called and counted. With this update the module creates a folder "wimbeldon" and child files: * "/proc/wimbeldon/nshow" Returns amount of show callbacks (cat /sys/kernel/wimbledon/list) * "/proc/wimbeldon/nstore" Returns amount of store callbacks (echo >> /sys/kernel/wimbledon/list) * "/proc/wimbeldon/auth_name" Returns module author name Output from the orange board is attached. Signed-off-by: Ivan Stepanenko --- 07_procfs/Makefile | 9 ++ 07_procfs/report.txt | 45 ++++++++ 07_procfs/wimbledon.c | 254 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 308 insertions(+) create mode 100644 07_procfs/Makefile create mode 100644 07_procfs/report.txt create mode 100644 07_procfs/wimbledon.c diff --git a/07_procfs/Makefile b/07_procfs/Makefile new file mode 100644 index 0000000..d8a8ac9 --- /dev/null +++ b/07_procfs/Makefile @@ -0,0 +1,9 @@ +KERNELDIR ?= ../output/build/linux-5.10.10 + +obj-m := wimbledon.o + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean diff --git a/07_procfs/report.txt b/07_procfs/report.txt new file mode 100644 index 0000000..991775b --- /dev/null +++ b/07_procfs/report.txt @@ -0,0 +1,45 @@ +$ insmod wimbledon.ko +$ echo "a1" >> /sys/kernel/wimbledon/list +$ cat /proc/wimbeldon/nshow +0 +$ cat /proc/wimbeldon/nstore +1 +$ cat /proc/wimbeldon/auth_name +Ivan Stepanenko +$ cat /proc/wimbeldon/nshow +0 +$ cat /sys/kernel/wimbledon/list +a1 +$ cat /proc/wimbeldon/nshow +1 +$ echo "b2" >> /sys/kernel/wimbledon/list +$ cat /proc/wimbeldon/nstore +2 +$ cat /sys/kernel/wimbledon/list +a1 +b2 +$ cat /proc/wimbeldon/nshow +2 +$ uname -a +Linux orange 5.10.10 #1 SMP Mon Dec 13 18:03:39 EET 2021 armv7l GNU/Linux +$ rmmod wimbledon.ko + +$ dmesg +[ 3395.465083] wimbledon: adding msg size: 3 to the list: 'a1 +[ 3395.465083] ' +[ 3437.850684] wimbledon: msg #0 size: 3 'a1 +[ 3437.850684] ' +[ 3455.577961] wimbledon: adding msg size: 3 to the list: 'b2 +[ 3455.577961] ' +[ 3468.681388] wimbledon: msg #0 size: 3 'a1 +[ 3468.681388] ' +[ 3468.686988] wimbledon: msg #1 size: 3 'b2 +[ 3468.686988] ' +[ 3498.512991] wimbledon: freeing msg size: 3 from the list: 'b2 +[ 3498.512991] ' +[ 3498.520373] wimbledon: freeing msg size: 3 from the list: 'a1 +[ 3498.520373] ' +[ 3498.527719] wimbledon: list is empty: 1 +[ 3498.531576] wimbledon: removing procfs entries +[ 3498.536034] wimbledon: Module exited +$ diff --git a/07_procfs/wimbledon.c b/07_procfs/wimbledon.c new file mode 100644 index 0000000..e1b1325 --- /dev/null +++ b/07_procfs/wimbledon.c @@ -0,0 +1,254 @@ +#define DEBUG +#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUTHOR "Ivan Stepanenko " + +#define PROCFS_BUFF_SIZE 64 +#define PROCFS_DIR "wimbeldon" +#define PROCFS_FN_NSHOW "nshow" +#define PROCFS_FN_NSTORE "nstore" +#define PROCFS_FN_AUTH_NAME "auth_name" + +struct msg_item { + struct list_head list; + char *msg; + size_t msg_len; +}; +static LIST_HEAD(msg_list_head); + +static unsigned int show_cb_counter = 0; +static unsigned int store_cb_counter = 0; + + +static ssize_t wimbledon_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) +{ + struct msg_item *pos = NULL; + size_t item_number = 0; + size_t count = 0; + char *p_buff = buff; + + show_cb_counter += 1; + + list_for_each_entry_reverse(pos, &msg_list_head, list) { + pr_info("msg #%u size: %u '%.*s'\n", item_number, pos->msg_len, (int)pos->msg_len, (char *)pos->msg); + + memcpy(p_buff, (char *)pos->msg, pos->msg_len); + p_buff += pos->msg_len; + count += pos->msg_len; + item_number += 1; + } + + return count; +} + + +static ssize_t wimbledon_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t count) +{ + char *p_msg = NULL; + struct msg_item *p_item = NULL; + + store_cb_counter += 1; + + p_msg = kmalloc(count, GFP_KERNEL); + if (!p_msg) { + pr_err("could not allocate msg cnt: %u\n", count); + return -ENOMEM; + } + p_item = kmalloc(sizeof(struct msg_item), GFP_KERNEL); + if (!p_item) { + pr_err("could not allocate msg_item\n"); + return -ENOMEM; + } + memcpy(p_msg, buff, count); + p_item->msg = p_msg; + p_item->msg_len = count; + + pr_info("adding msg size: %u to the list: '%.*s'\n", p_item->msg_len, (int)p_item->msg_len, (char *)p_item->msg); + list_add(&p_item->list, &msg_list_head); + return count; +} + +/* procfs operations */ + +static struct proc_dir_entry *procfs_root_entry, *procfs_nshow_entry, *procfs_nstore_entry, *procfs_auth_name_entry; + + +static ssize_t procfs_op_nshow_read(struct file *file, char __user *p_ubuf, size_t count, loff_t *ppos) +{ + char msg_buf[PROCFS_BUFF_SIZE]; + int len = 0; + + if(*ppos > 0) + return 0; + + len = snprintf(NULL, 0, "%u\n", show_cb_counter); + if (len < 0 || len >= sizeof(msg_buf)) { + pr_err("msg len: %i doesn't fit buffer: %i\n", len, sizeof(msg_buf)); + return -ENOMEM; + } + + snprintf(msg_buf, len+1, "%u\n", show_cb_counter); + + if (copy_to_user(p_ubuf, msg_buf, len)) { + pr_err("can't copy msg size: %i to user space\n", len); + return -EFAULT; + } + + *ppos = len; + + return len; +} + + +static ssize_t procfs_op_nstore_read(struct file *file, char __user *p_ubuf, size_t count, loff_t *ppos) +{ + char msg_buf[PROCFS_BUFF_SIZE]; + int len = 0; + + if(*ppos > 0) + return 0; + + len = snprintf(NULL, 0, "%u\n", store_cb_counter); + if (len < 0 || len >= sizeof(msg_buf)) { + pr_err("msg len: %i doesn't fit buffer: %i\n", len, sizeof(msg_buf)); + return -ENOMEM; + } + snprintf(msg_buf, len+1, "%u\n", store_cb_counter); + + if (copy_to_user(p_ubuf, msg_buf, len)) { + pr_err("can't copy msg size: %i to user space\n", len); + return -EFAULT; + } + + *ppos = len; + + return len; +} + + +static ssize_t procfs_op_auth_name_read(struct file *file, char __user *p_ubuf, size_t count, loff_t *ppos) +{ + char msg_buf[sizeof(AUTHOR)+1]; + int len = 0; + + if(*ppos > 0) + return 0; + + len = snprintf(NULL, 0, "%s\n", AUTHOR); + if (len < 0 || len >= sizeof(msg_buf)) { + pr_err("msg len: %i doesn't fit buffer: %i\n", len, sizeof(msg_buf)); + return -ENOMEM; + } + + snprintf(msg_buf, len+1, "%s\n", AUTHOR); + + if (copy_to_user(p_ubuf, msg_buf, len)) { + pr_err("can't copy msg size: %i to user space\n", len); + return -EFAULT; + } + + *ppos = len; + + return len; +} + + +static struct proc_ops procfs_nshow_ops = +{ + .proc_read = procfs_op_nshow_read +}; + +static struct proc_ops procfs_nstore_ops = +{ + .proc_read= procfs_op_nstore_read +}; + +static struct proc_ops procfs_auth_name_ops = +{ + .proc_read= procfs_op_auth_name_read +}; + +/* end procsfs operations */ + +static struct kobj_attribute list_attribute = + __ATTR(list, 0644, wimbledon_show, wimbledon_store); + +static struct kobject *wimbledon_kobj; + +static int wimbledon_init(void) +{ + int res = 0; + wimbledon_kobj = kobject_create_and_add("wimbledon", kernel_kobj); + if (!wimbledon_kobj) + return -ENOMEM; + res = sysfs_create_file(wimbledon_kobj, &list_attribute.attr); + if (res) + kobject_put(wimbledon_kobj); + + procfs_root_entry = proc_mkdir(PROCFS_DIR, NULL); + if (procfs_root_entry == NULL) { + pr_err("error creating procfs: %s entry\n", PROCFS_DIR); + return -ENOMEM; + } + procfs_nshow_entry = proc_create(PROCFS_FN_NSHOW, 0444, procfs_root_entry, &procfs_nshow_ops); + if (procfs_nshow_entry == NULL) { + pr_err("error creating procfs: %s entry\n", PROCFS_FN_NSHOW); + return -ENOMEM; + } + procfs_nstore_entry = proc_create(PROCFS_FN_NSTORE, 0444, procfs_root_entry, &procfs_nstore_ops); + if (procfs_nstore_entry == NULL) { + pr_err("error creating procfs: %s entry\n", PROCFS_FN_NSTORE); + return -ENOMEM; + } + procfs_auth_name_entry = proc_create(PROCFS_FN_AUTH_NAME, 0444, procfs_root_entry, &procfs_auth_name_ops); + if (procfs_nstore_entry == NULL) { + pr_err("error creating procfs: %s entry\n", PROCFS_FN_AUTH_NAME); + return -ENOMEM; + } + + return res; +} + +static void wimbledon_exit(void) +{ + struct list_head *pos = NULL; + struct list_head *n = NULL; + struct msg_item *p_item = NULL; + + list_for_each_safe(pos, n, &msg_list_head) { + p_item = list_entry(pos, struct msg_item, list); + pr_info("freeing msg size: %u from the list: '%.*s'\n", p_item->msg_len, (int)p_item->msg_len, (char *)p_item->msg); + list_del(pos); + kfree(p_item->msg); + kfree(p_item); + } + pr_info("list is empty: %d\n", list_empty_careful(&msg_list_head)); + kobject_put(wimbledon_kobj); + + pr_info("removing procfs entries\n"); + proc_remove(procfs_auth_name_entry); + proc_remove(procfs_nstore_entry); + proc_remove(procfs_nshow_entry); + proc_remove(procfs_root_entry); + + pr_info("Module exited\n"); +} + +module_init(wimbledon_init); +module_exit(wimbledon_exit); + +MODULE_AUTHOR(AUTHOR); +MODULE_DESCRIPTION("Shows num of store/show callbacks, stores data in the list items."); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1");