From ba6acd8c0f4019c92ccd550590fd9dfadf9e69de Mon Sep 17 00:00:00 2001 From: Oleksandr Posukhov Date: Tue, 23 Nov 2021 20:10:08 +0200 Subject: [PATCH 1/6] 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 e260cf649de16aad3755fecf2fe586c60b420e5f Mon Sep 17 00:00:00 2001 From: Yevgen Kovalyov Date: Wed, 1 Dec 2021 00:13:52 +0200 Subject: [PATCH 2/6] 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 a8bc3010b8552272a6d76d1e62754e0e778b7342 Mon Sep 17 00:00:00 2001 From: Yevgen Kovalyov Date: Thu, 2 Dec 2021 19:36:43 +0200 Subject: [PATCH 3/6] 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 d754dcfe7866d97cf483a44f454573804e7462af Mon Sep 17 00:00:00 2001 From: d-serj Date: Wed, 8 Dec 2021 09:53:31 -0800 Subject: [PATCH 4/6] TASK6: Add kernel module for dynamic memory tests Signed-off-by: d-serj --- 06_memory/kmemtime/Makefile | 11 ++ 06_memory/kmemtime/kmemtime.c | 188 ++++++++++++++++++++++++++++++++ 06_memory/kmemtime/kmemtime.mod | 2 + 06_memory/kmemtime/results.txt | 47 ++++++++ 4 files changed, 248 insertions(+) create mode 100644 06_memory/kmemtime/Makefile create mode 100644 06_memory/kmemtime/kmemtime.c create mode 100644 06_memory/kmemtime/kmemtime.mod create mode 100644 06_memory/kmemtime/results.txt diff --git a/06_memory/kmemtime/Makefile b/06_memory/kmemtime/Makefile new file mode 100644 index 0000000..d6b022a --- /dev/null +++ b/06_memory/kmemtime/Makefile @@ -0,0 +1,11 @@ +KERNELDIR ?= ../../../buildroot/output/build/linux-5.15/ #WARNING relative path + +obj-m := kmemtime.o +CFLAGS_kmemtime.o := -DDEBUG -std=gnu11 -Wno-declaration-after-statement + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean + diff --git a/06_memory/kmemtime/kmemtime.c b/06_memory/kmemtime/kmemtime.c new file mode 100644 index 0000000..72ae6fb --- /dev/null +++ b/06_memory/kmemtime/kmemtime.c @@ -0,0 +1,188 @@ +/** + * @file kmemtime.c + * @brief + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Max n for 2^n to allocate the buffer */ +#define KMEMTIME_TESTS_MAX_N 10 + +enum kmem_type +{ + e_malloc, + e_calloc, + e_get_free_pages, + e_alloca, +}; + +struct kmemtime +{ + enum kmem_type type; + const char *alloc_name; + const char *free_name; + int buff_size[KMEMTIME_TESTS_MAX_N]; + u64 time_alloc[KMEMTIME_TESTS_MAX_N]; + u64 time_free[KMEMTIME_TESTS_MAX_N]; +}; + +static struct kmemtime kmemtime_test[] = +{ + { .type = e_malloc, .alloc_name = "kmalloc ", .free_name = "kfree " }, + { .type = e_calloc, .alloc_name = "vmalloc ", .free_name = "vfree " }, + { .type = e_get_free_pages, .alloc_name = "get_free_pages", .free_name = "free_pages" }, + { .type = e_alloca, .alloc_name = "kzalloc ", .free_name = "kfree " }, +}; + +static char *memtime_print_result(const struct kmemtime *result) +{ + const char *size_str = "Buffer size, bytes |"; + int buff_pos = 0; + char *table; + + /* Estimate buffer size */ + const int header_len = snprintf(NULL, 0, "%s %s, ns | %s, ns |\n", + size_str, result->alloc_name, result->free_name); + + /* Table columns width for data printing. -2 for '|' and space characters */ + const int buff_size_width = strlen(size_str) - 2; + const int alloc_time_width = strlen(result->alloc_name) + strlen(", ns | ") - 2; + const int free_time_width = strlen(result->free_name) + strlen(", ns | ") - 2; + const int row_len = snprintf(NULL, 0, "%*d |%*llu |%*llu |\n", + buff_size_width, result->buff_size[0], + alloc_time_width, result->time_alloc[0], + free_time_width, result->time_free[0]); + + /* +1 for '\0' */ + const size_t table_len = header_len + row_len * KMEMTIME_TESTS_MAX_N + 1; + table = kmalloc(table_len, GFP_KERNEL); + if (table == NULL) + return NULL; + + table[0] = '\0'; + + buff_pos = snprintf(table, table_len, "%s %s, ns | %s, ns |\n", + size_str, result->alloc_name, result->free_name); + + for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) + { + const int sn_res = snprintf(&table[buff_pos], table_len - buff_pos, "%*d |%*llu |%*llu |\n", + buff_size_width, result->buff_size[i], + alloc_time_width, result->time_alloc[i], + free_time_width, result->time_free[i]); + + buff_pos += sn_res; + } + + return table; +} + +static void memtime_get_result(struct kmemtime *result) +{ + int *test_ptr; + int buff_size; + unsigned long page_addr; + u64 alloc_start; + u64 alloc_end; + u64 free_end; + + for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) + { + buff_size = int_pow(2, i); + result->buff_size[i] = buff_size; + + switch (result->type) + { + case e_malloc: + alloc_start = ktime_get_ns(); + test_ptr = kmalloc(buff_size, GFP_KERNEL); + BUG_ON(test_ptr == NULL); + alloc_end = ktime_get_ns(); + kfree(test_ptr); + free_end = ktime_get_ns(); + break; + + case e_alloca: + alloc_start = ktime_get_ns(); + test_ptr = kzalloc(buff_size, GFP_KERNEL); + BUG_ON(test_ptr == NULL); + alloc_end = ktime_get_ns(); + kfree(test_ptr); + free_end = ktime_get_ns(); + break; + + case e_calloc: + alloc_start = ktime_get_ns(); + test_ptr = vmalloc(buff_size); + BUG_ON(test_ptr == NULL); + alloc_end = ktime_get_ns(); + vfree(test_ptr); + free_end = ktime_get_ns(); + break; + + case e_get_free_pages: + /* Overwrite buff size for __get_free_pages */ + result->buff_size[i] = int_pow(2, i) * PAGE_SIZE; + alloc_start = ktime_get_ns(); + page_addr = __get_free_pages(GFP_KERNEL, i); + alloc_end = ktime_get_ns(); + free_pages(page_addr, i); + free_end = ktime_get_ns(); + break; + + default: + BUG(); + break; + } + + result->time_alloc[i] = alloc_end - alloc_start; + result->time_free[i] = free_end - alloc_end; + pr_debug("time %lld nanoseconds for %s\n", result->time_alloc[i], result->alloc_name); + pr_debug("Free time %lld nanoseconds for %s\n", result->time_free[i], result->free_name); + } +} + +int kmemtime_init(void) +{ + char *res; + + for (int i = 0; i < ARRAY_SIZE(kmemtime_test); ++i) + { + memtime_get_result(&kmemtime_test[i]); + res = memtime_print_result(&kmemtime_test[i]); + if (res == NULL) + { + BUG(); + return -ENOMEM; + } + + pr_info("%s\n", res); + kfree(res); + } + + return 0; +} + +void kmemtime_exit(void) +{ + +} + +module_init(kmemtime_init); +module_exit(kmemtime_exit); + +MODULE_DESCRIPTION("kmemtime kernel module"); +MODULE_AUTHOR("Sergey D."); +MODULE_LICENSE("GPL"); diff --git a/06_memory/kmemtime/kmemtime.mod b/06_memory/kmemtime/kmemtime.mod new file mode 100644 index 0000000..04bde59 --- /dev/null +++ b/06_memory/kmemtime/kmemtime.mod @@ -0,0 +1,2 @@ +/home/serj/kernel_procamp/gl_kernel_procamp_2021/06_memory/kmemtime/kmemtime.o + diff --git a/06_memory/kmemtime/results.txt b/06_memory/kmemtime/results.txt new file mode 100644 index 0000000..8201d8d --- /dev/null +++ b/06_memory/kmemtime/results.txt @@ -0,0 +1,47 @@ +Buffer size, bytes | kmalloc , ns | kfree , ns | + 1 | 15824 | 6420 | + 2 | 564 | 288 | + 4 | 242 | 213 | + 8 | 235 | 191 | + 16 | 270 | 258 | + 32 | 231 | 199 | + 64 | 231 | 196 | + 128 | 615 | 257 | + 256 | 751 | 569 | + 512 | 601 | 251 | + +Buffer size, bytes | vmalloc , ns | vfree , ns | + 1 | 41907 | 13412 | + 2 | 5914 | 2487 | + 4 | 2933 | 2030 | + 8 | 2656375 | 19768 | + 16 | 12440 | 3048 | + 32 | 2856 | 1675 | + 64 | 2144 | 1430 | + 128 | 1857 | 1668 | + 256 | 1794 | 1336 | + 512 | 1660 | 1361 | + +Buffer size, bytes | get_free_pages, ns | free_pages, ns | + 4096 | 12029 | 59528 | + 8192 | 5866 | 3687 | + 16384 | 960 | 680 | + 32768 | 1649 | 617 | + 65536 | 10293 | 17811 | + 131072 | 1827 | 887 | + 262144 | 847 | 786 | + 524288 | 12730 | 1472 | + 1048576 | 6729 | 1938 | + 2097152 | 31621 | 5070 | + +Buffer size, bytes | kzalloc , ns | kfree , ns | + 1 | 25354 | 13805 | + 2 | 432 | 234 | + 4 | 245 | 174 | + 8 | 214 | 175 | + 16 | 5389 | 597 | + 32 | 3468 | 1812 | + 64 | 846 | 562 | + 128 | 1819 | 415 | + 256 | 1266 | 226 | + 512 | 1562 | 431 | From 188c678e2a194bb62f1456f7537e0616ff0a62fb Mon Sep 17 00:00:00 2001 From: d-serj Date: Thu, 9 Dec 2021 09:30:49 -0800 Subject: [PATCH 5/6] TASK6: Add user space program to test dynamic memory allocations/deallocations Signed-off-by: d-serj --- 06_memory/kmemtime/kmemtime.c | 12 ++- 06_memory/kmemtime/kmemtime.mod | 2 - 06_memory/memtime/Makefile | 14 +++ 06_memory/memtime/memtime.c | 153 ++++++++++++++++++++++++++++++++ 06_memory/memtime/program.log | 70 +++++++++++++++ 5 files changed, 242 insertions(+), 9 deletions(-) delete mode 100644 06_memory/kmemtime/kmemtime.mod create mode 100644 06_memory/memtime/Makefile create mode 100644 06_memory/memtime/memtime.c create mode 100644 06_memory/memtime/program.log diff --git a/06_memory/kmemtime/kmemtime.c b/06_memory/kmemtime/kmemtime.c index 72ae6fb..1d8c801 100644 --- a/06_memory/kmemtime/kmemtime.c +++ b/06_memory/kmemtime/kmemtime.c @@ -1,6 +1,6 @@ /** * @file kmemtime.c - * @brief + * @brief Kernel module to measure time spent for different type of memory allocators * */ @@ -40,10 +40,10 @@ struct kmemtime static struct kmemtime kmemtime_test[] = { - { .type = e_malloc, .alloc_name = "kmalloc ", .free_name = "kfree " }, - { .type = e_calloc, .alloc_name = "vmalloc ", .free_name = "vfree " }, + { .type = e_malloc, .alloc_name = "kmalloc ", .free_name = "kfree " }, + { .type = e_calloc, .alloc_name = "vmalloc ", .free_name = "vfree " }, { .type = e_get_free_pages, .alloc_name = "get_free_pages", .free_name = "free_pages" }, - { .type = e_alloca, .alloc_name = "kzalloc ", .free_name = "kfree " }, + { .type = e_alloca, .alloc_name = "kzalloc ", .free_name = "kfree " }, }; static char *memtime_print_result(const struct kmemtime *result) @@ -78,12 +78,10 @@ static char *memtime_print_result(const struct kmemtime *result) for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) { - const int sn_res = snprintf(&table[buff_pos], table_len - buff_pos, "%*d |%*llu |%*llu |\n", + buff_pos += snprintf(&table[buff_pos], table_len - buff_pos, "%*d |%*llu |%*llu |\n", buff_size_width, result->buff_size[i], alloc_time_width, result->time_alloc[i], free_time_width, result->time_free[i]); - - buff_pos += sn_res; } return table; diff --git a/06_memory/kmemtime/kmemtime.mod b/06_memory/kmemtime/kmemtime.mod deleted file mode 100644 index 04bde59..0000000 --- a/06_memory/kmemtime/kmemtime.mod +++ /dev/null @@ -1,2 +0,0 @@ -/home/serj/kernel_procamp/gl_kernel_procamp_2021/06_memory/kmemtime/kmemtime.o - diff --git a/06_memory/memtime/Makefile b/06_memory/memtime/Makefile new file mode 100644 index 0000000..bb4f8ca --- /dev/null +++ b/06_memory/memtime/Makefile @@ -0,0 +1,14 @@ +CC=gcc +CC_FLAGS=-std=gnu11 -Wall + +CC_SOURCES=memtime.c +TARGET_NAME=memtime + +$(TARGET_NAME): + $(CC) $(CC_FLAGS) -o $@ $(CC_SOURCES) -lm + +all: clean $(TARGET_NAME) + +clean: + -$(RM) -rf *.o $(TARGET_NAME) + diff --git a/06_memory/memtime/memtime.c b/06_memory/memtime/memtime.c new file mode 100644 index 0000000..8092942 --- /dev/null +++ b/06_memory/memtime/memtime.c @@ -0,0 +1,153 @@ +/** + * @file memtime.c + * @brief User space program to measure time spent for different type of memory allocators + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Max n for 2^n to allocate the buffer */ +#define KMEMTIME_TESTS_MAX_N 23 + +enum kmem_type +{ + e_malloc, + e_calloc, + e_alloca, +}; + +struct kmemtime +{ + enum kmem_type type; + const char *alloc_name; + const char *free_name; + long buff_size[KMEMTIME_TESTS_MAX_N]; + long time_alloc[KMEMTIME_TESTS_MAX_N]; + long time_free[KMEMTIME_TESTS_MAX_N]; +}; + +static struct kmemtime memtime_test[] = +{ + { .type = e_malloc, .alloc_name = "malloc", .free_name = "free" }, + { .type = e_calloc, .alloc_name = "calloc", .free_name = "free" }, + { .type = e_alloca, .alloc_name = "alloca", .free_name = "- " }, +}; + +static void *memtime_get_mem(enum kmem_type type, long buff_size, long *time) +{ + struct timespec alloc_start; + struct timespec alloc_end; + void *mem_ptr = NULL; + + switch (type) + { + case e_malloc: + clock_gettime(CLOCK_MONOTONIC, &alloc_start); + mem_ptr = malloc(buff_size); + clock_gettime(CLOCK_MONOTONIC, &alloc_end); + break; + + case e_alloca: + clock_gettime(CLOCK_MONOTONIC, &alloc_start); + mem_ptr = alloca(buff_size); + clock_gettime(CLOCK_MONOTONIC, &alloc_end); + break; + + case e_calloc: + clock_gettime(CLOCK_MONOTONIC, &alloc_start); + mem_ptr = calloc(buff_size, sizeof(char)); + clock_gettime(CLOCK_MONOTONIC, &alloc_end); + break; + + default: + assert(0); + break; + } + + *time = (mem_ptr != NULL) ? (alloc_end.tv_nsec - alloc_start.tv_nsec) : -1; + + return mem_ptr; +} + +static void memtime_free(enum kmem_type type, void *mem_ptr, long *time) +{ + struct timespec free_start; + struct timespec free_end; + + if (mem_ptr == NULL) + { + *time = -1; + return; + } + + switch (type) + { + case e_malloc: + clock_gettime(CLOCK_MONOTONIC, &free_start); + free(mem_ptr); + clock_gettime(CLOCK_MONOTONIC, &free_end); + *time = free_end.tv_nsec - free_start.tv_nsec; + break; + + case e_alloca: + // Freed automatically + *time = 0; + break; + + case e_calloc: + clock_gettime(CLOCK_MONOTONIC, &free_start); + free(mem_ptr); + clock_gettime(CLOCK_MONOTONIC, &free_end); + *time = free_end.tv_nsec - free_start.tv_nsec; + break; + + default: + assert(0); + break; + } +} + +static void memtime_get_result(struct kmemtime *result) +{ + int *test_ptr; + int buff_size; + + for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) + { + buff_size = pow(2, i); + result->buff_size[i] = buff_size; + + test_ptr = memtime_get_mem(result->type, buff_size, &result->time_alloc[i]); + memtime_free(result->type, test_ptr, &result->time_free[i]); + } +} + +int main(void) +{ + const int test_struct_size = sizeof(memtime_test) / sizeof(memtime_test[0]); + + for (int i = 0; i < test_struct_size; ++i) + { + memtime_get_result(&memtime_test[i]); + } + + printf("Buffer size, bytes | Function name | Allocation time, ns | Deallocation time, ns |\n"); + + for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) + { + for (int j = 0; j < test_struct_size; ++j) + { + printf("%18ld | %13s | %19ld | %21ld |\n", + memtime_test[j].buff_size[i], + memtime_test[j].alloc_name, + memtime_test[j].time_alloc[i], memtime_test[j].time_free[i]); + } + } + + return 0; +} diff --git a/06_memory/memtime/program.log b/06_memory/memtime/program.log new file mode 100644 index 0000000..b9dd91b --- /dev/null +++ b/06_memory/memtime/program.log @@ -0,0 +1,70 @@ +Buffer size, bytes | Function name | Allocation time, ns | Deallocation time, ns | + 1 | malloc | 37360 | 104 | + 1 | calloc | 142 | 77 | + 1 | alloca | 37 | 0 | + 2 | malloc | 62 | 40 | + 2 | calloc | 59 | 16 | + 2 | alloca | 23 | 0 | + 4 | malloc | 26 | 23 | + 4 | calloc | 24 | 15 | + 4 | alloca | 17 | 0 | + 8 | malloc | 15 | 17 | + 8 | calloc | 22 | 16 | + 8 | alloca | 17 | 0 | + 16 | malloc | 28 | 17 | + 16 | calloc | 41 | 16 | + 16 | alloca | 17 | 0 | + 32 | malloc | 76 | 17 | + 32 | calloc | 56 | 18 | + 32 | alloca | 16 | 0 | + 64 | malloc | 33 | 16 | + 64 | calloc | 46 | 16 | + 64 | alloca | 17 | 0 | + 128 | malloc | 28 | 16 | + 128 | calloc | 127 | 18 | + 128 | alloca | 17 | 0 | + 256 | malloc | 21 | 17 | + 256 | calloc | 36 | 17 | + 256 | alloca | 16 | 0 | + 512 | malloc | 29 | 16 | + 512 | calloc | 49 | 18 | + 512 | alloca | 17 | 0 | + 1024 | malloc | 54 | 17 | + 1024 | calloc | 56 | 17 | + 1024 | alloca | 17 | 0 | + 2048 | malloc | 1070 | 201 | + 2048 | calloc | 53 | 46 | + 2048 | alloca | 16 | 0 | + 4096 | malloc | 52 | 77 | + 4096 | calloc | 77 | 17 | + 4096 | alloca | 104 | 0 | + 8192 | malloc | 862 | 20 | + 8192 | calloc | 702 | 20 | + 8192 | alloca | 1012 | 0 | + 16384 | malloc | 901 | 19 | + 16384 | calloc | 1238 | 20 | + 16384 | alloca | 1490 | 0 | + 32768 | malloc | 853 | 20 | + 32768 | calloc | 3311 | 29 | + 32768 | alloca | 3344 | 0 | + 65536 | malloc | 923 | 19 | + 65536 | calloc | 7384 | 23 | + 65536 | alloca | 6410 | 0 | + 131072 | malloc | 846 | 19 | + 131072 | calloc | 17247 | 24 | + 131072 | alloca | 13430 | 0 | + 262144 | malloc | 2180 | 3959 | + 262144 | calloc | 34001 | 23 | + 262144 | alloca | 37046 | 0 | + 524288 | malloc | 1908 | 2204 | + 524288 | calloc | 11971 | 26 | + 524288 | alloca | 64922 | 0 | + 1048576 | malloc | 1979 | 2151 | + 1048576 | calloc | 116355 | 93 | + 1048576 | alloca | 131491 | 0 | + 2097152 | malloc | 6668 | 1599 | + 2097152 | calloc | 265247 | 358 | + 2097152 | alloca | 412731 | 0 | + 4194304 | malloc | 982 | 1385 | + 4194304 | calloc | 380459 | 43 | + 4194304 | alloca | 746262 | 0 | From 73647e55f14cfd6ad88cadb70060b522d0425828 Mon Sep 17 00:00:00 2001 From: d-serj Date: Fri, 28 Jan 2022 13:45:35 -0800 Subject: [PATCH 6/6] TASK6: update codestyle according to checkpatch.pl Signed-off-by: d-serj --- 06_memory/kmemtime/kmemtime.c | 267 +++++++++++++++++----------------- 1 file changed, 130 insertions(+), 137 deletions(-) diff --git a/06_memory/kmemtime/kmemtime.c b/06_memory/kmemtime/kmemtime.c index 1d8c801..dc7ca65 100644 --- a/06_memory/kmemtime/kmemtime.c +++ b/06_memory/kmemtime/kmemtime.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 + /** - * @file kmemtime.c * @brief Kernel module to measure time spent for different type of memory allocators - * */ #include @@ -20,157 +20,150 @@ /* Max n for 2^n to allocate the buffer */ #define KMEMTIME_TESTS_MAX_N 10 -enum kmem_type -{ - e_malloc, - e_calloc, - e_get_free_pages, - e_alloca, +enum kmem_type { + e_malloc, + e_calloc, + e_get_free_pages, + e_alloca, }; -struct kmemtime -{ - enum kmem_type type; - const char *alloc_name; - const char *free_name; - int buff_size[KMEMTIME_TESTS_MAX_N]; - u64 time_alloc[KMEMTIME_TESTS_MAX_N]; - u64 time_free[KMEMTIME_TESTS_MAX_N]; +struct kmemtime { + enum kmem_type type; + const char *alloc_name; + const char *free_name; + int buff_size[KMEMTIME_TESTS_MAX_N]; + u64 time_alloc[KMEMTIME_TESTS_MAX_N]; + u64 time_free[KMEMTIME_TESTS_MAX_N]; }; -static struct kmemtime kmemtime_test[] = -{ - { .type = e_malloc, .alloc_name = "kmalloc ", .free_name = "kfree " }, - { .type = e_calloc, .alloc_name = "vmalloc ", .free_name = "vfree " }, - { .type = e_get_free_pages, .alloc_name = "get_free_pages", .free_name = "free_pages" }, - { .type = e_alloca, .alloc_name = "kzalloc ", .free_name = "kfree " }, +static struct kmemtime kmemtime_test[] = { + { .type = e_malloc, .alloc_name = "kmalloc ", .free_name = "kfree " }, + { .type = e_calloc, .alloc_name = "vmalloc ", .free_name = "vfree " }, + { .type = e_get_free_pages, .alloc_name = "get_free_pages", .free_name = "free_pages" }, + { .type = e_alloca, .alloc_name = "kzalloc ", .free_name = "kfree " }, }; static char *memtime_print_result(const struct kmemtime *result) { - const char *size_str = "Buffer size, bytes |"; - int buff_pos = 0; - char *table; - - /* Estimate buffer size */ - const int header_len = snprintf(NULL, 0, "%s %s, ns | %s, ns |\n", - size_str, result->alloc_name, result->free_name); - - /* Table columns width for data printing. -2 for '|' and space characters */ - const int buff_size_width = strlen(size_str) - 2; - const int alloc_time_width = strlen(result->alloc_name) + strlen(", ns | ") - 2; - const int free_time_width = strlen(result->free_name) + strlen(", ns | ") - 2; - const int row_len = snprintf(NULL, 0, "%*d |%*llu |%*llu |\n", - buff_size_width, result->buff_size[0], - alloc_time_width, result->time_alloc[0], - free_time_width, result->time_free[0]); - - /* +1 for '\0' */ - const size_t table_len = header_len + row_len * KMEMTIME_TESTS_MAX_N + 1; - table = kmalloc(table_len, GFP_KERNEL); - if (table == NULL) - return NULL; - - table[0] = '\0'; - - buff_pos = snprintf(table, table_len, "%s %s, ns | %s, ns |\n", - size_str, result->alloc_name, result->free_name); - - for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) - { - buff_pos += snprintf(&table[buff_pos], table_len - buff_pos, "%*d |%*llu |%*llu |\n", - buff_size_width, result->buff_size[i], - alloc_time_width, result->time_alloc[i], - free_time_width, result->time_free[i]); - } - - return table; + const char *size_str = "Buffer size, bytes |"; + int buff_pos = 0; + char *table; + + /* Estimate buffer size */ + const int header_len = snprintf(NULL, 0, "%s %s, ns | %s, ns |\n", + size_str, result->alloc_name, result->free_name); + + /* Table columns width for data printing. -2 for '|' and space characters */ + const int buff_size_width = strlen(size_str) - 2; + const int alloc_time_width = strlen(result->alloc_name) + strlen(", ns | ") - 2; + const int free_time_width = strlen(result->free_name) + strlen(", ns | ") - 2; + const int row_len = snprintf(NULL, 0, "%*d |%*llu |%*llu |\n", + buff_size_width, result->buff_size[0], + alloc_time_width, result->time_alloc[0], + free_time_width, result->time_free[0]); + + /* +1 for '\0' */ + const size_t table_len = header_len + row_len * KMEMTIME_TESTS_MAX_N + 1; + + table = kmalloc(table_len, GFP_KERNEL); + if (table == NULL) + return NULL; + + table[0] = '\0'; + + buff_pos = snprintf(table, table_len, "%s %s, ns | %s, ns |\n", + size_str, result->alloc_name, result->free_name); + + for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) { + buff_pos += snprintf(&table[buff_pos], table_len - buff_pos, "%*d |%*llu |%*llu |\n", + buff_size_width, result->buff_size[i], + alloc_time_width, result->time_alloc[i], + free_time_width, result->time_free[i]); + } + + return table; } static void memtime_get_result(struct kmemtime *result) { - int *test_ptr; - int buff_size; - unsigned long page_addr; - u64 alloc_start; - u64 alloc_end; - u64 free_end; - - for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) - { - buff_size = int_pow(2, i); - result->buff_size[i] = buff_size; - - switch (result->type) - { - case e_malloc: - alloc_start = ktime_get_ns(); - test_ptr = kmalloc(buff_size, GFP_KERNEL); - BUG_ON(test_ptr == NULL); - alloc_end = ktime_get_ns(); - kfree(test_ptr); - free_end = ktime_get_ns(); - break; - - case e_alloca: - alloc_start = ktime_get_ns(); - test_ptr = kzalloc(buff_size, GFP_KERNEL); - BUG_ON(test_ptr == NULL); - alloc_end = ktime_get_ns(); - kfree(test_ptr); - free_end = ktime_get_ns(); - break; - - case e_calloc: - alloc_start = ktime_get_ns(); - test_ptr = vmalloc(buff_size); - BUG_ON(test_ptr == NULL); - alloc_end = ktime_get_ns(); - vfree(test_ptr); - free_end = ktime_get_ns(); - break; - - case e_get_free_pages: - /* Overwrite buff size for __get_free_pages */ - result->buff_size[i] = int_pow(2, i) * PAGE_SIZE; - alloc_start = ktime_get_ns(); - page_addr = __get_free_pages(GFP_KERNEL, i); - alloc_end = ktime_get_ns(); - free_pages(page_addr, i); - free_end = ktime_get_ns(); - break; - - default: - BUG(); - break; - } - - result->time_alloc[i] = alloc_end - alloc_start; - result->time_free[i] = free_end - alloc_end; - pr_debug("time %lld nanoseconds for %s\n", result->time_alloc[i], result->alloc_name); - pr_debug("Free time %lld nanoseconds for %s\n", result->time_free[i], result->free_name); - } + int *test_ptr; + int buff_size; + unsigned long page_addr; + u64 alloc_start; + u64 alloc_end; + u64 free_end; + + for (int i = 0; i < KMEMTIME_TESTS_MAX_N; ++i) { + buff_size = int_pow(2, i); + result->buff_size[i] = buff_size; + + switch (result->type) { + case e_malloc: + alloc_start = ktime_get_ns(); + test_ptr = kmalloc(buff_size, GFP_KERNEL); + BUG_ON(test_ptr == NULL); + alloc_end = ktime_get_ns(); + kfree(test_ptr); + free_end = ktime_get_ns(); + break; + + case e_alloca: + alloc_start = ktime_get_ns(); + test_ptr = kzalloc(buff_size, GFP_KERNEL); + BUG_ON(test_ptr == NULL); + alloc_end = ktime_get_ns(); + kfree(test_ptr); + free_end = ktime_get_ns(); + break; + + case e_calloc: + alloc_start = ktime_get_ns(); + test_ptr = vmalloc(buff_size); + BUG_ON(test_ptr == NULL); + alloc_end = ktime_get_ns(); + vfree(test_ptr); + free_end = ktime_get_ns(); + break; + + case e_get_free_pages: + /* Overwrite buff size for __get_free_pages */ + result->buff_size[i] = int_pow(2, i) * PAGE_SIZE; + alloc_start = ktime_get_ns(); + page_addr = __get_free_pages(GFP_KERNEL, i); + alloc_end = ktime_get_ns(); + free_pages(page_addr, i); + free_end = ktime_get_ns(); + break; + + default: + BUG(); + break; + } + + result->time_alloc[i] = alloc_end - alloc_start; + result->time_free[i] = free_end - alloc_end; + pr_debug("time %lld nanoseconds for %s\n", result->time_alloc[i], result->alloc_name); + pr_debug("Free time %lld nanoseconds for %s\n", result->time_free[i], result->free_name); + } } int kmemtime_init(void) { - char *res; - - for (int i = 0; i < ARRAY_SIZE(kmemtime_test); ++i) - { - memtime_get_result(&kmemtime_test[i]); - res = memtime_print_result(&kmemtime_test[i]); - if (res == NULL) - { - BUG(); - return -ENOMEM; - } - - pr_info("%s\n", res); - kfree(res); - } - - return 0; + char *res; + + for (int i = 0; i < ARRAY_SIZE(kmemtime_test); ++i) { + memtime_get_result(&kmemtime_test[i]); + res = memtime_print_result(&kmemtime_test[i]); + if (res == NULL) { + BUG(); + return -ENOMEM; + } + + pr_info("%s\n", res); + kfree(res); + } + + return 0; } void kmemtime_exit(void)