diff --git a/.gitignore b/.gitignore index e69de29..47cf365 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +test/** diff --git a/shared/.vscode-ctags b/shared/.vscode-ctags new file mode 100644 index 0000000..b35e51f --- /dev/null +++ b/shared/.vscode-ctags @@ -0,0 +1,38 @@ +!_TAG_EXTRA_DESCRIPTION anonymous /Include tags for non-named objects like lambda/ +!_TAG_EXTRA_DESCRIPTION fileScope /Include tags of file scope/ +!_TAG_EXTRA_DESCRIPTION pseudo /Include pseudo tags/ +!_TAG_EXTRA_DESCRIPTION subparser /Include tags generated by subparsers/ +!_TAG_FIELD_DESCRIPTION epoch /the last modified time of the input file (only for F\/file kind tag)/ +!_TAG_FIELD_DESCRIPTION file /File-restricted scoping/ +!_TAG_FIELD_DESCRIPTION input /input file/ +!_TAG_FIELD_DESCRIPTION name /tag name/ +!_TAG_FIELD_DESCRIPTION pattern /pattern/ +!_TAG_FIELD_DESCRIPTION typeref /Type and name of a variable or typedef/ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_KIND_DESCRIPTION!C d,macro /macro definitions/ +!_TAG_KIND_DESCRIPTION!C e,enumerator /enumerators (values inside an enumeration)/ +!_TAG_KIND_DESCRIPTION!C f,function /function definitions/ +!_TAG_KIND_DESCRIPTION!C g,enum /enumeration names/ +!_TAG_KIND_DESCRIPTION!C h,header /included header files/ +!_TAG_KIND_DESCRIPTION!C m,member /struct, and union members/ +!_TAG_KIND_DESCRIPTION!C s,struct /structure names/ +!_TAG_KIND_DESCRIPTION!C t,typedef /typedefs/ +!_TAG_KIND_DESCRIPTION!C u,union /union names/ +!_TAG_KIND_DESCRIPTION!C v,variable /variable definitions/ +!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ +!_TAG_OUTPUT_FILESEP slash /slash or backslash/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_OUTPUT_VERSION 0.0 /current.age/ +!_TAG_PARSER_VERSION!C 1.1 /current.age/ +!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/ +!_TAG_PROC_CWD /home/rubberhead/Git/00-UOE/unnamed_ba_thesis/src/aarch64-linux-flush-dcache/ // +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 6.1.0 /v6.1.0/ +!_TAG_ROLE_DESCRIPTION!C!function foreigndecl /declared in foreign languages/ +!_TAG_ROLE_DESCRIPTION!C!header local /local header/ +!_TAG_ROLE_DESCRIPTION!C!header system /system header/ +!_TAG_ROLE_DESCRIPTION!C!macro undef /undefined/ +!_TAG_ROLE_DESCRIPTION!C!struct foreigndecl /declared in foreign languages/ diff --git a/shared/.vscode/c_cpp_properties.json b/shared/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..3690212 --- /dev/null +++ b/shared/.vscode/c_cpp_properties.json @@ -0,0 +1,44 @@ +{ + "configurations": [ + { + "name": "Linux-clang-arm64", + "includePath": [ + "${workspaceFolder}/**", + "/home/rubberhead/Git/linux/include", + "/home/rubberhead/Git/linux/include/uapi", + "/home/rubberhead/Git/linux/include/generated", + "/home/rubberhead/Git/linux/arch/arm64/include", + "/home/rubberhead/Git/linux/arch/arm64/include/uapi", + "/home/rubberhead/Git/linux/arch/arm64/include/generated", + "/usr/lib/gcc/aarch64-linux-gnu/13.2.0/include" + ], + "defines": [ + "__GNUC__", + "__KERNEL__" + ], + "compilerPath": "/usr/bin/clang", + "compilerArgs": [ + "-nostdinc", + "-std=gnu11" + ], + "cStandard": "gnu11", + "cppStandard": "${default}", + "browse": { + "path": [ + "${workspaceFolder}", + "/home/rubberhead/Git/linux/include", + "/home/rubberhead/Git/linux/include/uapi", + "/home/rubberhead/Git/linux/include/generated", + "/home/rubberhead/Git/linux/arch/arm64/include", + "/home/rubberhead/Git/linux/arch/arm64/include/uapi", + "/home/rubberhead/Git/linux/arch/arm64/include/generated", + "/usr/lib/gcc/aarch64-linux-gnu/13.2.0/include" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "intelliSenseMode": "linux-clang-arm64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/shared/.vscode/settings.json b/shared/.vscode/settings.json new file mode 100644 index 0000000..e29650f --- /dev/null +++ b/shared/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "C_Cpp.errorSquiggles": "enabled", + "files.associations": { + "*.h": "c", + }, + "editor.insertSpaces": false, + "editor.indentSize": "tabSize", + "editor.tabSize": 8, + "clangd.arguments": ["--compile-commands-dir=/home/rubberhead/Git/linux"] +} \ No newline at end of file diff --git a/shared/Makefile b/shared/Makefile new file mode 100644 index 0000000..c6c824c --- /dev/null +++ b/shared/Makefile @@ -0,0 +1,9 @@ +obj-m += my_shmem.o +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean \ No newline at end of file diff --git a/src/aarch64-linux-flush-dcache/mod_entry_1.c b/shared/my_shmem.c similarity index 89% rename from src/aarch64-linux-flush-dcache/mod_entry_1.c rename to shared/my_shmem.c index fddf251..4fe37e1 100644 --- a/src/aarch64-linux-flush-dcache/mod_entry_1.c +++ b/shared/my_shmem.c @@ -1,6 +1,5 @@ -#include "asm-generic/cacheflush.h" -#include "asm/cacheflush.h" -#include "asm/page-def.h" +// [TODO] Clean up headers +#include #include "linux/atomic/atomic-long.h" #include "linux/device.h" #include "linux/device/class.h" @@ -17,9 +16,10 @@ #include #include #include +#include MODULE_AUTHOR("Zk."); -MODULE_DESCRIPTION("4.2.1: mmap for point of coherency"); +MODULE_DESCRIPTION("4.2.W1: mmap for point of coherency"); MODULE_LICENSE("GPL"); struct my_shmem_page { @@ -45,11 +45,17 @@ const char* DEV_NAME = "my_shmem"; static void my_shmem_vmops_close(struct vm_area_struct *vma) { // [TODO] Flush dcache at close. - // `dcache_clean_poc` writebacks D-cache region s.t. PoC. Period. + // `dcache_clean_poc` writebacks D-cache region till PoC. Period. // This should? work on all ARM64 CPUs w/ no-alias VIPT dcache. // The addresses are VAs (obv., as opposed to PAs). - // dcache_clean_poc(unsigned long start, unsigned long end) + pr_info("[%s] Closing vma: [0x%lx - 0x%lx].\n", + __func__, vma->vm_start, vma->vm_end); + // It might, however, be better to just call a asm-generic function + // e.g., `flush_dcache_page`... Though I don't think arm64 supports this. + // Not sure why. dcache_clean_poc(vma->vm_start, vma->vm_end); + // flush_cache_range(vma, vma->vm_start, vma->vm_end); + pr_info("[%s] Flushed dcache.\n", __func__); } static vm_fault_t my_shmem_vmops_fault(struct vm_fault *vmf) @@ -73,7 +79,7 @@ static vm_fault_t my_shmem_vmops_fault(struct vm_fault *vmf) nr_pages_from_vm_start--; } // Found correct page entry, remap - get_page(page_entry->page); + get_page(page_entry->page); // [FIXME] Incorrect refcount keeping. vmf->page = page_entry->page; _dbg_phys = page_to_phys(page_entry->page); @@ -146,6 +152,7 @@ ok_ret_allocated: static const struct vm_operations_struct my_shmem_vmops = { + .close = my_shmem_vmops_close, .fault = my_shmem_vmops_fault, }; @@ -204,7 +211,12 @@ static int __init my_shmem_init(void) goto err_ret_cdev_reg_failed; /* Create device class */ + #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) class = class_create(DEV_NAME); + #else + // Re: commit dcfbb67e48a2becfce7990386e985b9c45098ee5 (I think?) + class = class_create(THIS_MODULE, DEV_NAME); + #endif if (IS_ERR(class)) { unregister_chrdev(major, DEV_NAME); goto err_ret_class_crea_failed; diff --git a/shared/userspace/.vscode/c_cpp_properties.json b/shared/userspace/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..d9a0f74 --- /dev/null +++ b/shared/userspace/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "C-clang-arm64", + "includePath": [ + "${workspaceFolder}/**", + "/usr/include/" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "clang-arm64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/shared/userspace/.vscode/settings.json b/shared/userspace/.vscode/settings.json new file mode 100644 index 0000000..3b218d1 --- /dev/null +++ b/shared/userspace/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "C_Cpp.errorSquiggles": "enabled", + "files.associations": { + "*.h": "c", + }, + "editor.insertSpaces": false, + "editor.indentSize": "tabSize", + "editor.tabSize": 4, +} \ No newline at end of file diff --git a/shared/userspace/userspace.c b/shared/userspace/userspace.c new file mode 100644 index 0000000..986f6b6 --- /dev/null +++ b/shared/userspace/userspace.c @@ -0,0 +1,11 @@ +/** + * @file userspace.c + * @author Zhengyi Chen (you@domain.com) + * @brief + * @version 0.1 + * @date 2024-01-16 + * + * @copyright Copyright (c) 2024 + * + */ + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..d94e501 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +linux-debian-src/ diff --git a/src/aarch64-linux-flush-dcache/Makefile b/src/aarch64-linux-flush-dcache/Makefile new file mode 100644 index 0000000..c6c824c --- /dev/null +++ b/src/aarch64-linux-flush-dcache/Makefile @@ -0,0 +1,9 @@ +obj-m += my_shmem.o +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean \ No newline at end of file diff --git a/src/aarch64-linux-flush-dcache/mod_entry.c b/src/aarch64-linux-flush-dcache/mod_entry.c deleted file mode 100644 index ded20ca..0000000 --- a/src/aarch64-linux-flush-dcache/mod_entry.c +++ /dev/null @@ -1,185 +0,0 @@ -#include "asm-generic/errno-base.h" -#include "asm/current.h" -#include "asm/page-def.h" -#include "linux/export.h" -#include "linux/fs.h" -#include "linux/gfp_types.h" -#include "linux/rcupdate.h" -#include -#include -#include -#include -#include -#include -#include -#include - -/* PID: The task for which page cache is to be flushed. */ -static int param_pid; -module_param_named(pid, param_pid, int, 0644); - -/* addr: The virtual memory address used to index into the page to be flushed. */ -static unsigned long param_vmaddr; -module_param_named(addr, param_vmaddr, ulong, 0644); - -static void **page_addrs; -static void *page_addr = NULL; - -/* Design: - * 2 userspace threads (each affine to a different CPU to prevent non-aliasing - * caches from incoherence prevention). - * 1. Load kernel module, init chardev which provide page mapping. - * 2. Run userspace program, wherein each thread mmaps the chardev for the same - * mapping. - * Otherwise -- one thread mmaps the chardev, but the kernel itself also - * modifies. - * 3. On write: kernel module examines the content of in-kernel mapping vs. - * userspace mapping, which have same PA but obv. diff VA. They should be - * dissimilar by VIPT. - * Then, the kernel flushes dcache for the userspace to read. - * Finally, it reads again to see whether two mappings agree. - */ - -static int __init flush_dcache_init(void) -{ - struct task_struct *tsk; - struct mm_struct *mm_of_tsk; - struct page *page_of_addr; - - // Get task_struct from PID, then get its mm_struct - rcu_read_lock(); - tsk = find_task_by_pid_ns(param_pid, &init_pid_ns); - if (!tsk) { - rcu_read_unlock(); - goto ret_err_no_tsk; - } - mm_of_tsk = get_task_mm(tsk); - rcu_read_unlock(); - - // No mm_struct -- may be kthread? - if (!mm_of_tsk) - goto ret_warn_kthread; - - /* Get page from mm_struct -- - * We need to pin the page i.e., have it marked as FOLL_PIN. - * Ref. Documentation/core-api/pin_user_pages.rst: - * Because we are writing to the data represented by the page -- we are - * flushing cache to dirty page, after all -- we need FOLL_PIN instead - * of *get-API. This effectively? prevents page from being evicted in - * the short term. - */ - mmap_read_lock(mm_of_tsk); - long pin_pages_retval = pin_user_pages_remote( - mm_of_tsk, param_vmaddr, 1, - FOLL_WRITE, &page_of_addr, NULL - ); // We know mmap is locked, stop asking. - if (pin_pages_retval != 1) { - mmap_read_unlock(mm_of_tsk); - goto ret_err_no_page; - } - - /* Begin test -- - * It may complicate things if this got preempted, so no preemption. - */ - unsigned long _eflags; - local_irq_save(_eflags); - // [TODO] Alter main memory content from kernel. - pr_info("Before flush: cache line/memory diff: "); -#if ARCH == arm64 - // dcache_clean_poc() -#endif - // dcache_clean_ - pr_info("After flush: ..."); - mmap_read_unlock(mm_of_tsk); - local_irq_restore(_eflags); - - unpin_user_pages(&page_of_addr, 1); - return 0; - -ret_warn_kthread: - pr_warn("[%s] Cannot find `tsk->mm` for PID %d. This may be a kthread.\n" - "Messing with `active_mm` may be unsafe. Exiting...", - __func__, param_pid); - return 0; -ret_err_no_tsk: - pr_err("[%s] Cannot find `task_struct` for PID %d.", - __func__, param_pid); - return -EINVAL; -ret_err_no_page: - pr_err("[%s] Cannot pin requested pages. [TODO]", __func__); - return pin_pages_retval == 0 ? -EINVAL : (int)pin_pages_retval; -} - -static void __exit flush_dcache_exit(void) -{ - free_page((ulong) page_addr); - pr_info("[%s] See ya~", __func__); -} - -int _flush_dcache_init_devfs(void) -{ - -} - -int _flush_dcache_alloc_pages(int page_nr) -{ - page_addr = alloc_page(GFP_USER); - if (!page_addr) - goto ret_err_alloc_failed; - return 0; - -ret_err_alloc_failed: - pr_err("[%s] Failed to allocate virtual memory page.", __func__); - return -ENOMEM; -} - -static int flush_dcache_fops_open(struct inode *inode, struct file *filp) -{ - filp->f_mode |= FMODE_CAN_ODIRECT; - return generic_file_open(inode, filp); -} - -static int flush_dcache_fops_mmap(struct file *filp, struct vm_area_struct *vma) -{ - /* Check if vma has mm backing -- e.g., non-kthread */ - if (!vma->vm_mm) - goto ret_err_no_mm; - - /* Check vma size */ - ulong vma_size = - PAGE_ALIGN(max(PAGE_SIZE, vma->vm_end - vma->vm_start)); - if (vma_size != PAGE_SIZE) - goto ret_err_wrong_size; - - /* Insert page */ - struct page *page = vmalloc_to_page(page_addr); - vm_insert_page(vma, vma->vm_start, page); - vm_flags_set(vma, VM_IO | VM_DONTCOPY | VM_DONTEXPAND); - - pr_info("[%s] mmapped vma: 0x%lx-0x%lx", - __func__, vma->vm_start, vma->vm_end); - return 0; -ret_err_no_mm: - pr_err("[%s] vm_area_struct has null vm_mm -- kthread or crashed?", - __func__); - return -EINVAL; -ret_err_wrong_size: - pr_err("[%s] Requested %ld pages -- please request only 1!", - __func__, vma_size); - return -EINVAL; -} - -const struct file_operations flush_dcache_fops = { - .owner = THIS_MODULE, - .open = flush_dcache_fops_open, - .mmap = flush_dcache_fops_mmap -}; - -// const struct vm_operations_struct flush_dcache_vmops = { -// .mremap = NULL, -// }; - -module_init(flush_dcache_init); -module_exit(flush_dcache_exit); - -MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/src/aarch64-linux-flush-dcache/my_shmem.c b/src/aarch64-linux-flush-dcache/my_shmem.c new file mode 100644 index 0000000..4b2ba5e --- /dev/null +++ b/src/aarch64-linux-flush-dcache/my_shmem.c @@ -0,0 +1,286 @@ +// [TODO] Clean up headers +#include +#include "asm/page-def.h" +#include "linux/atomic/atomic-long.h" +#include "linux/device.h" +#include "linux/device/class.h" +#include "linux/mutex.h" +#include "linux/pfn_t.h" +#include "linux/pid.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Zk."); +MODULE_DESCRIPTION("4.2.W1: mmap for point of coherency"); +MODULE_LICENSE("GPL"); + +struct my_shmem_page { + struct page *page; + struct list_head list; +}; +static DEFINE_MUTEX(my_shmem_pages_mtx); + +/* [!] READ/WRITE UNDER LOCK */ +static LIST_HEAD(my_shmem_pages); + +/* [!] READ/WRITE UNDER LOCK */ +static size_t my_shmem_page_count = 0; + +static int major; +static struct class* class; +static struct device* dev; +const char* DEV_NAME = "my_shmem"; + +/* Virtual Memory Area Operations... + * ============================================================================ + */ +static void my_shmem_vmops_close(struct vm_area_struct *vma) +{ + // [TODO] Flush dcache at close. + // `dcache_clean_poc` writebacks D-cache region till PoC. Period. + // This should? work on all ARM64 CPUs w/ no-alias VIPT dcache. + // The addresses are VAs (obv., as opposed to PAs). + pr_info("[%s] Closing vma: [0x%lx - 0x%lx].\n", + __func__, vma->vm_start, vma->vm_end); + // Or dcache_clean_poc(vma->vm_start, vma->vm_end)... + // which I'm not sure if it's correct, we'll see. + for (ulong addr = PAGE_ALIGN(vma->vm_start); + addr <= PAGE_ALIGN(vma->vm_end); + addr += PAGE_SIZE) + { + // For each page intersected by this vma: + struct page *pg = vmalloc_to_page((void *) addr); + BUG_ON(pg == NULL); // [>_<] Hope this works... + pr_info("[%s] Flushing page 0x%llx.\n", + __func__, page_to_pfn_t(pg).val); + flush_dcache_page(pg); + } + pr_info("[%s] Flushed dcache.\n", __func__); +} + +static vm_fault_t my_shmem_vmops_fault(struct vm_fault *vmf) +{ + ulong nr_pages_from_vm_start = + (vmf->address - vmf->vma->vm_start) >> PAGE_SHIFT; + const pgoff_t _dbg_offset_from_page = vmf->pgoff; + phys_addr_t _dbg_phys; + + mutex_lock(&my_shmem_pages_mtx); + if (nr_pages_from_vm_start < my_shmem_page_count) { + /* Offset in range, return existing page */ + pr_info("[%s] Found remappable page nr: %lu, offset: %lu.\n", + __func__, nr_pages_from_vm_start, _dbg_offset_from_page); + + // We won't delete elements from list here! + struct my_shmem_page *page_entry; + list_for_each_entry(page_entry, &my_shmem_pages, list) { + if (!nr_pages_from_vm_start) + break; + nr_pages_from_vm_start--; + } + // Found correct page entry, remap + get_page(page_entry->page); // [FIXME] Incorrect refcount keeping. + vmf->page = page_entry->page; + _dbg_phys = page_to_phys(page_entry->page); + + mutex_unlock(&my_shmem_pages_mtx); + goto ok_ret_remapped; + } + + /* Otherwise, allocate the new page(s) */ + for (int i = 0; i <= nr_pages_from_vm_start - my_shmem_page_count; i++) + { + // Allocate page handle in kernel + struct my_shmem_page *new_page = kzalloc( + sizeof(struct my_shmem_page), GFP_KERNEL); + if (!new_page) { + mutex_unlock(&my_shmem_pages_mtx); + goto err_ret_no_kmem; + } + + // Allocate page in virtual memory + // [!] We specifically WANT to allocate cachable memory to + // create cache incoherence btwn procs. Synchronization + // (e.g., on arm64) is done via calling fsync for now. + void *addr = vmalloc_user(PAGE_SIZE); + if (!addr) { + mutex_unlock(&my_shmem_pages_mtx); + goto err_ret_no_vmem; + } + new_page->page = vmalloc_to_page(addr); + + // List maintenance: add and incr. size + list_add(&new_page->list, &my_shmem_pages); + my_shmem_page_count++; + + // Fill in allocated page entry + get_page(new_page->page); + vmf->page = new_page->page; + _dbg_phys = page_to_phys(new_page->page); + } + mutex_unlock(&my_shmem_pages_mtx); + goto ok_ret_allocated; + +err_ret_no_kmem: + pr_err("[%s] Cannot allocate `struct my_shmem_page` in kernel memory.\n", + __func__); + return VM_FAULT_OOM; +err_ret_no_vmem: + pr_err("[%s] Cannot allocate requested page for virtual memory.\n", + __func__); + return VM_FAULT_OOM; +ok_ret_remapped: + if (vmf->vma->vm_mm) { + rcu_read_lock(); + struct task_struct *fault_owner = vmf->vma->vm_mm->owner; + pr_info("[%s] Remapped phys: 0x%llx -> virt@PID(%d): 0x%lx.\n", + __func__, _dbg_phys, fault_owner->pid, vmf->address); + rcu_read_unlock(); + } + return 0; +ok_ret_allocated: + if (vmf->vma->vm_mm){ + rcu_read_lock(); + struct task_struct *fault_owner = vmf->vma->vm_mm->owner; + pr_info("[%s] Allocated phys: 0x%llx -> virt@PID(%d): 0x%lx.\n", + __func__, _dbg_phys, fault_owner->pid, vmf->address); + rcu_read_unlock(); + } + return 0; +} + + + +static const struct vm_operations_struct my_shmem_vmops = { + .close = my_shmem_vmops_close, + .fault = my_shmem_vmops_fault, +}; + +/* File Operations... + * ============================================================================ + */ +// static int my_shmem_fops_open(struct inode *inode, struct file *filp); + +static const struct file_operations my_shmem_fops; + +static int my_shmem_fops_mmap(struct file *filp, struct vm_area_struct *vma) +{ + vma->vm_ops = &my_shmem_vmops; + + pr_info("[%s] Device file '%s' mmapped for vma: [0x%lx - 0x%lx].\n", + __func__, file_dentry(filp)->d_name.name, vma->vm_start, vma->vm_end); + return 0; +} + +static int my_shmem_fops_open(struct inode *inode, struct file *filp) +{ + filp->f_op = &my_shmem_fops; + + pr_info("[%s] Device file '%s' opened.\n", + __func__, file_dentry(filp)->d_name.name); + return 0; +} + +static int my_shmem_fops_release(struct inode *inode, struct file *filp) +{ + pr_info("[%s] Device file '%s' released.\n", + __func__, file_dentry(filp)->d_name.name); + + /* Garbage collection requires knowing who references which page... + * Ideally this is stored in filp->private_data but ah well, oversight. + */ + return 0; +} + +static const struct file_operations my_shmem_fops = { + .owner = THIS_MODULE, + .open = my_shmem_fops_open, + .mmap = my_shmem_fops_mmap, + .release = my_shmem_fops_release, +}; + +/* Module init & exit... + * ============================================================================ + */ +static int __init my_shmem_init(void) +{ + /* Register cdev */ + major = register_chrdev( + 0, DEV_NAME, &my_shmem_fops); + if (major < 0) + goto err_ret_cdev_reg_failed; + + /* Create device class */ + #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) + class = class_create(DEV_NAME); + #else + // Re: commit dcfbb67e48a2becfce7990386e985b9c45098ee5 (I think?) + class = class_create(THIS_MODULE, DEV_NAME); + #endif + if (IS_ERR(class)) { + unregister_chrdev(major, DEV_NAME); + goto err_ret_class_crea_failed; + } + + /* Create one device */ + dev_t dev_nr = MKDEV(major, 0); + dev = device_create( + class, NULL, dev_nr, NULL, DEV_NAME); + if (IS_ERR(dev)) { + class_destroy(class); + unregister_chrdev(major, DEV_NAME); + goto err_ret_dev_crea_failed; + } + + pr_info("[%s] Device `%s` built successfully.\n", + __func__, dev->init_name); + return 0; + +err_ret_cdev_reg_failed: + pr_err("[%s] Cannot register character dev -- error code %d.\n", + __func__, major); + return major; +err_ret_class_crea_failed: + pr_err("[%s] Cannot create device class -- error code %ld.\n", + __func__, PTR_ERR(class)); + return (int) PTR_ERR(class); +err_ret_dev_crea_failed: + pr_err("[%s] Cannot create device -- error code %ld.\n", + __func__, PTR_ERR(dev)); + return (int) PTR_ERR(dev); +} + +static void __exit my_shmem_exit(void) +{ + /* Destroy device */ + device_destroy(class, dev->devt); + class_destroy(class); + unregister_chrdev(major, DEV_NAME); + pr_info("[%s] Device destroyed.\n", __func__); + + /* Free all pages -- I'm not compacting in runtime!!! */ + struct my_shmem_page *page_entry, *tmp; + mutex_lock(&my_shmem_pages_mtx); + list_for_each_entry_safe(page_entry, tmp, &my_shmem_pages, list) { + vfree(page_to_virt(page_entry->page)); + put_page(page_entry->page); + + my_shmem_page_count--; + list_del(&page_entry->list); + kfree(page_entry); + } + mutex_unlock(&my_shmem_pages_mtx); +} + +module_init(my_shmem_init); +module_exit(my_shmem_exit); \ No newline at end of file