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_region;
|
||||||
struct vm_object;
|
struct vm_object;
|
||||||
|
|
||||||
|
enum vm_region_status {
|
||||||
|
VM_REGION_DEAD = 0,
|
||||||
|
VM_REGION_ONLINE,
|
||||||
|
};
|
||||||
|
|
||||||
enum vm_region_entry_type {
|
enum vm_region_entry_type {
|
||||||
VM_REGION_ENTRY_NONE = 0,
|
VM_REGION_ENTRY_NONE = 0,
|
||||||
VM_REGION_ENTRY_REGION,
|
VM_REGION_ENTRY_REGION,
|
||||||
@@ -18,9 +23,16 @@ enum vm_region_entry_type {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct vm_region_entry {
|
struct vm_region_entry {
|
||||||
|
union {
|
||||||
struct btree_node e_node;
|
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;
|
struct vm_region_entry *e_parent;
|
||||||
enum vm_region_entry_type e_type;
|
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. */
|
/* offset in bytes of this entry within its immediate parent. */
|
||||||
off_t e_offset;
|
off_t e_offset;
|
||||||
/* size of the entry in bytes */
|
/* size of the entry in bytes */
|
||||||
@@ -41,6 +53,7 @@ struct vm_region_mapping {
|
|||||||
|
|
||||||
struct vm_region {
|
struct vm_region {
|
||||||
struct object vr_base;
|
struct object vr_base;
|
||||||
|
enum vm_region_status vr_status;
|
||||||
struct vm_region_entry vr_entry;
|
struct vm_region_entry vr_entry;
|
||||||
|
|
||||||
char vr_name[VM_REGION_NAME_MAX];
|
char vr_name[VM_REGION_NAME_MAX];
|
||||||
@@ -81,6 +94,19 @@ extern kern_status_t vm_region_create(
|
|||||||
vm_prot_t prot,
|
vm_prot_t prot,
|
||||||
struct vm_region **out);
|
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.
|
/* map a vm-object into a vm-region.
|
||||||
* [region_offset,length] must fall within exactly one region, and cannot span
|
* [region_offset,length] must fall within exactly one region, and cannot span
|
||||||
* multiple sibling regions.
|
* multiple sibling regions.
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ kern_status_t sys_vm_region_create(
|
|||||||
|
|
||||||
object_ref(obj);
|
object_ref(obj);
|
||||||
task_unlock_irqrestore(self, flags);
|
task_unlock_irqrestore(self, flags);
|
||||||
|
vm_region_lock_irqsave(parent_region, &flags);
|
||||||
|
|
||||||
struct vm_region *child = NULL;
|
struct vm_region *child = NULL;
|
||||||
status = vm_region_create(
|
status = vm_region_create(
|
||||||
@@ -73,6 +74,7 @@ kern_status_t sys_vm_region_create(
|
|||||||
region_len,
|
region_len,
|
||||||
prot,
|
prot,
|
||||||
&child);
|
&child);
|
||||||
|
vm_region_unlock_irqrestore(parent_region, flags);
|
||||||
object_unref(obj);
|
object_unref(obj);
|
||||||
|
|
||||||
if (status != KERN_OK) {
|
if (status != KERN_OK) {
|
||||||
@@ -92,6 +94,39 @@ kern_status_t sys_vm_region_create(
|
|||||||
return KERN_OK;
|
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_status_t sys_vm_region_read(
|
||||||
kern_handle_t region_handle,
|
kern_handle_t region_handle,
|
||||||
void *dst,
|
void *dst,
|
||||||
|
|||||||
Reference in New Issue
Block a user