TODO: test module
This commit is contained in:
parent
f9282a627f
commit
71b52acdb1
13 changed files with 453 additions and 192 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -0,0 +1 @@
|
||||||
|
test/**
|
||||||
38
shared/.vscode-ctags
Normal file
38
shared/.vscode-ctags
Normal file
|
|
@ -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/
|
||||||
44
shared/.vscode/c_cpp_properties.json
vendored
Normal file
44
shared/.vscode/c_cpp_properties.json
vendored
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
10
shared/.vscode/settings.json
vendored
Normal file
10
shared/.vscode/settings.json
vendored
Normal file
|
|
@ -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"]
|
||||||
|
}
|
||||||
9
shared/Makefile
Normal file
9
shared/Makefile
Normal file
|
|
@ -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
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#include "asm-generic/cacheflush.h"
|
// [TODO] Clean up headers
|
||||||
#include "asm/cacheflush.h"
|
#include <asm/cacheflush.h>
|
||||||
#include "asm/page-def.h"
|
|
||||||
#include "linux/atomic/atomic-long.h"
|
#include "linux/atomic/atomic-long.h"
|
||||||
#include "linux/device.h"
|
#include "linux/device.h"
|
||||||
#include "linux/device/class.h"
|
#include "linux/device/class.h"
|
||||||
|
|
@ -17,9 +16,10 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
MODULE_AUTHOR("Zk.");
|
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");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
struct my_shmem_page {
|
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)
|
static void my_shmem_vmops_close(struct vm_area_struct *vma)
|
||||||
{
|
{
|
||||||
// [TODO] Flush dcache at close.
|
// [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.
|
// This should? work on all ARM64 CPUs w/ no-alias VIPT dcache.
|
||||||
// The addresses are VAs (obv., as opposed to PAs).
|
// 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);
|
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)
|
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--;
|
nr_pages_from_vm_start--;
|
||||||
}
|
}
|
||||||
// Found correct page entry, remap
|
// Found correct page entry, remap
|
||||||
get_page(page_entry->page);
|
get_page(page_entry->page); // [FIXME] Incorrect refcount keeping.
|
||||||
vmf->page = page_entry->page;
|
vmf->page = page_entry->page;
|
||||||
_dbg_phys = page_to_phys(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 = {
|
static const struct vm_operations_struct my_shmem_vmops = {
|
||||||
|
.close = my_shmem_vmops_close,
|
||||||
.fault = my_shmem_vmops_fault,
|
.fault = my_shmem_vmops_fault,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -204,7 +211,12 @@ static int __init my_shmem_init(void)
|
||||||
goto err_ret_cdev_reg_failed;
|
goto err_ret_cdev_reg_failed;
|
||||||
|
|
||||||
/* Create device class */
|
/* Create device class */
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||||
class = class_create(DEV_NAME);
|
class = class_create(DEV_NAME);
|
||||||
|
#else
|
||||||
|
// Re: commit dcfbb67e48a2becfce7990386e985b9c45098ee5 (I think?)
|
||||||
|
class = class_create(THIS_MODULE, DEV_NAME);
|
||||||
|
#endif
|
||||||
if (IS_ERR(class)) {
|
if (IS_ERR(class)) {
|
||||||
unregister_chrdev(major, DEV_NAME);
|
unregister_chrdev(major, DEV_NAME);
|
||||||
goto err_ret_class_crea_failed;
|
goto err_ret_class_crea_failed;
|
||||||
16
shared/userspace/.vscode/c_cpp_properties.json
vendored
Normal file
16
shared/userspace/.vscode/c_cpp_properties.json
vendored
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
9
shared/userspace/.vscode/settings.json
vendored
Normal file
9
shared/userspace/.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"C_Cpp.errorSquiggles": "enabled",
|
||||||
|
"files.associations": {
|
||||||
|
"*.h": "c",
|
||||||
|
},
|
||||||
|
"editor.insertSpaces": false,
|
||||||
|
"editor.indentSize": "tabSize",
|
||||||
|
"editor.tabSize": 4,
|
||||||
|
}
|
||||||
11
shared/userspace/userspace.c
Normal file
11
shared/userspace/userspace.c
Normal file
|
|
@ -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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
1
src/.gitignore
vendored
Normal file
1
src/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
linux-debian-src/
|
||||||
9
src/aarch64-linux-flush-dcache/Makefile
Normal file
9
src/aarch64-linux-flush-dcache/Makefile
Normal file
|
|
@ -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
|
||||||
|
|
@ -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 <asm-generic/cacheflush.h>
|
|
||||||
#include <linux/irqflags.h>
|
|
||||||
#include <linux/mm_types.h>
|
|
||||||
#include <linux/mmap_lock.h>
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
|
|
||||||
/* 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");
|
|
||||||
286
src/aarch64-linux-flush-dcache/my_shmem.c
Normal file
286
src/aarch64-linux-flush-dcache/my_shmem.c
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
// [TODO] Clean up headers
|
||||||
|
#include <asm/cacheflush.h>
|
||||||
|
#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 <linux/rcupdate.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mm_types.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
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);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue