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. 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. 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. 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..dc7ca65 --- /dev/null +++ b/06_memory/kmemtime/kmemtime.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 + +/** + * @brief Kernel module to measure time spent for different type of memory allocators + */ + +#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) { + 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 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/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 | 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 |