From b1ffdcf2bcc9a8e1975423160a5a0fdc61048179 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Mon, 23 Feb 2026 18:35:45 +0000 Subject: [PATCH] vm: region: improve locking rules and semantics; implement region killing the rules around acquiring locks have been strictly defined and implemented, and general lock usage has been improved, to fix and prevent several different issues. a vm-region is now destroyed in two separate steps: 1. it is "killed": all mappings are unmapped and deleted, the region is removed from its parent, and the region and all of its sub-regions are marked as "dead", preventing any further actions from being performed with the region. 2. it is "destroyed": the vm-region object is de-allocated when the last reference/handle is closed. the references that this region holds to any sub-regions are also released, meaning these regions may also be de-allocated too. --- include/kernel/vm-region.h | 30 ++++++++++++++++++++++++++++-- syscall/vm-region.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/include/kernel/vm-region.h b/include/kernel/vm-region.h index ef0c374..c4585b7 100644 --- a/include/kernel/vm-region.h +++ b/include/kernel/vm-region.h @@ -11,6 +11,11 @@ struct vm_region; struct vm_object; +enum vm_region_status { + VM_REGION_DEAD = 0, + VM_REGION_ONLINE, +}; + enum vm_region_entry_type { VM_REGION_ENTRY_NONE = 0, VM_REGION_ENTRY_REGION, @@ -18,9 +23,16 @@ enum vm_region_entry_type { }; struct vm_region_entry { - struct btree_node e_node; + union { + struct btree_node e_node; + /* this entry is only used to queue vm-region objects for + * recursive cleanup */ + struct queue_entry e_entry; + }; struct vm_region_entry *e_parent; enum vm_region_entry_type e_type; + /* absolute address of this entry */ + virt_addr_t e_address; /* offset in bytes of this entry within its immediate parent. */ off_t e_offset; /* size of the entry in bytes */ @@ -31,7 +43,7 @@ struct vm_region_mapping { struct vm_region_entry m_entry; struct vm_object *m_object; - /* used to link to vm_object->vo_mappings */ + /* used to link to vm_object->vo_mappings */ struct queue_entry m_object_entry; vm_prot_t m_prot; @@ -41,6 +53,7 @@ struct vm_region_mapping { struct vm_region { struct object vr_base; + enum vm_region_status vr_status; struct vm_region_entry vr_entry; char vr_name[VM_REGION_NAME_MAX]; @@ -81,6 +94,19 @@ extern kern_status_t vm_region_create( vm_prot_t prot, struct vm_region **out); +/* recursively kills a given region and all of its sub-regions. + * when a region is killed, all of its mappings are unmapped, and any further + * operations on the region are denied. however, all handles and references to + * the region (any any sub-region) remain valid, and no kernel memory is + * de-allocated. + * the memory used by the vm-region object itself is de-allocated when the last + * handle/reference to the object is released. + * this function should be called with `region` locked. + */ +extern kern_status_t vm_region_kill( + struct vm_region *region, + unsigned long *lock_flags); + /* map a vm-object into a vm-region. * [region_offset,length] must fall within exactly one region, and cannot span * multiple sibling regions. diff --git a/syscall/vm-region.c b/syscall/vm-region.c index 6b9fd17..80c9831 100644 --- a/syscall/vm-region.c +++ b/syscall/vm-region.c @@ -63,6 +63,7 @@ kern_status_t sys_vm_region_create( object_ref(obj); task_unlock_irqrestore(self, flags); + vm_region_lock_irqsave(parent_region, &flags); struct vm_region *child = NULL; status = vm_region_create( @@ -73,6 +74,7 @@ kern_status_t sys_vm_region_create( region_len, prot, &child); + vm_region_unlock_irqrestore(parent_region, flags); object_unref(obj); if (status != KERN_OK) { @@ -92,6 +94,39 @@ kern_status_t sys_vm_region_create( return KERN_OK; } +kern_status_t sys_vm_region_kill(kern_handle_t region_handle) +{ + struct task *self = current_task(); + + unsigned long flags; + task_lock_irqsave(self, &flags); + + struct object *obj = NULL; + handle_flags_t handle_flags = 0; + kern_status_t status + = task_resolve_handle(self, region_handle, &obj, &handle_flags); + if (status != KERN_OK) { + task_unlock_irqrestore(self, flags); + return status; + } + + struct vm_region *region = vm_region_cast(obj); + if (!region) { + task_unlock_irqrestore(self, flags); + return KERN_INVALID_ARGUMENT; + } + + object_ref(obj); + task_unlock_irqrestore(self, flags); + + vm_region_lock_irqsave(region, &flags); + status = vm_region_kill(region, &flags); + vm_region_unlock_irqrestore(region, flags); + object_unref(obj); + + return status; +} + kern_status_t sys_vm_region_read( kern_handle_t region_handle, void *dst,