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.
This commit is contained in:
@@ -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 {
|
||||
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 */
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user