vm: controller: implement asynchronous DETACH page requests
This commit is contained in:
@@ -13,13 +13,11 @@ enum page_request_status {
|
||||
PAGE_REQUEST_PENDING = 0,
|
||||
PAGE_REQUEST_IN_PROGRESS,
|
||||
PAGE_REQUEST_COMPLETE,
|
||||
PAGE_REQUEST_ASYNC,
|
||||
};
|
||||
|
||||
struct vm_controller {
|
||||
struct object vc_base;
|
||||
/* tree of struct vm_objects bound to this controller, keyed with the
|
||||
* equeue_key_t specified when the object(s) were created. */
|
||||
struct btree vc_objects;
|
||||
/* tree of pending page requests */
|
||||
struct btree vc_requests;
|
||||
/* the equeue to send async page requests to */
|
||||
@@ -36,7 +34,7 @@ struct page_request {
|
||||
enum page_request_status req_status;
|
||||
kern_status_t req_result;
|
||||
spin_lock_t req_lock;
|
||||
struct vm_object *req_object;
|
||||
equeue_key_t req_object;
|
||||
struct thread *req_sender;
|
||||
struct btree_node req_node;
|
||||
off_t req_offset;
|
||||
|
||||
@@ -23,8 +23,14 @@ static struct object_type vm_controller_type = {
|
||||
.ob_header_offset = offsetof(struct vm_controller, vc_base),
|
||||
};
|
||||
|
||||
static struct vm_cache page_request_cache = {
|
||||
.c_name = "page-request",
|
||||
.c_obj_size = sizeof(struct page_request),
|
||||
};
|
||||
|
||||
kern_status_t vm_controller_type_init(void)
|
||||
{
|
||||
vm_cache_init(&page_request_cache);
|
||||
return object_type_register(&vm_controller_type);
|
||||
}
|
||||
|
||||
@@ -52,10 +58,17 @@ static struct page_request *get_next_request(struct vm_controller *ctrl)
|
||||
struct page_request *req
|
||||
= BTREE_CONTAINER(struct page_request, req_node, cur);
|
||||
spin_lock(&req->req_lock);
|
||||
if (req->req_status == PAGE_REQUEST_PENDING) {
|
||||
switch (req->req_status) {
|
||||
case PAGE_REQUEST_PENDING:
|
||||
req->req_status = PAGE_REQUEST_IN_PROGRESS;
|
||||
ctrl->vc_requests_waiting--;
|
||||
return req;
|
||||
case PAGE_REQUEST_ASYNC:
|
||||
btree_delete(&ctrl->vc_requests, &req->req_node);
|
||||
ctrl->vc_requests_waiting--;
|
||||
return req;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&req->req_lock);
|
||||
@@ -65,98 +78,6 @@ static struct page_request *get_next_request(struct vm_controller *ctrl)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kern_status_t vm_controller_recv(
|
||||
struct vm_controller *ctrl,
|
||||
equeue_packet_page_request_t *out)
|
||||
{
|
||||
struct page_request *req = NULL;
|
||||
|
||||
req = get_next_request(ctrl);
|
||||
if (!req) {
|
||||
return KERN_NO_ENTRY;
|
||||
}
|
||||
|
||||
if (ctrl->vc_requests_waiting == 0) {
|
||||
object_clear_signal(
|
||||
&ctrl->vc_base,
|
||||
VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED);
|
||||
}
|
||||
|
||||
out->req_vmo = req->req_object->vo_key;
|
||||
out->req_type = req->req_type;
|
||||
out->req_offset = req->req_offset;
|
||||
out->req_length = req->req_length;
|
||||
|
||||
spin_unlock(&req->req_lock);
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t vm_controller_recv_async(
|
||||
struct vm_controller *ctrl,
|
||||
struct equeue *eq,
|
||||
equeue_key_t key)
|
||||
{
|
||||
if (ctrl->vc_eq) {
|
||||
object_unref(&ctrl->vc_eq->eq_base);
|
||||
}
|
||||
|
||||
object_ref(&eq->eq_base);
|
||||
ctrl->vc_eq = eq;
|
||||
ctrl->vc_eq_key = key;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t vm_controller_create_object(
|
||||
struct vm_controller *ctrl,
|
||||
const char *name,
|
||||
size_t name_len,
|
||||
equeue_key_t key,
|
||||
size_t data_len,
|
||||
vm_prot_t prot,
|
||||
struct vm_object **out)
|
||||
{
|
||||
struct vm_object *vmo = get_object(&ctrl->vc_objects, key);
|
||||
if (vmo) {
|
||||
return KERN_NAME_EXISTS;
|
||||
}
|
||||
|
||||
vmo = vm_object_create(name, name_len, data_len, prot);
|
||||
if (!vmo) {
|
||||
return KERN_NO_MEMORY;
|
||||
}
|
||||
|
||||
object_ref(&ctrl->vc_base);
|
||||
object_ref(&vmo->vo_base);
|
||||
|
||||
vmo->vo_flags |= VMO_CONTROLLER;
|
||||
vmo->vo_ctrl = ctrl;
|
||||
vmo->vo_key = key;
|
||||
|
||||
put_object(&ctrl->vc_objects, vmo);
|
||||
|
||||
*out = vmo;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t vm_controller_detach_object(
|
||||
struct vm_controller *ctrl,
|
||||
struct vm_object *vmo)
|
||||
{
|
||||
if (vmo->vo_ctrl != ctrl) {
|
||||
return KERN_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
vmo->vo_ctrl = NULL;
|
||||
vmo->vo_key = 0;
|
||||
btree_delete(&ctrl->vc_objects, &vmo->vo_ctrl_node);
|
||||
|
||||
object_unref(&ctrl->vc_base);
|
||||
object_unref(&vmo->vo_base);
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
static kern_status_t try_enqueue(struct btree *tree, struct page_request *req)
|
||||
{
|
||||
if (!tree->b_root) {
|
||||
@@ -196,6 +117,118 @@ static kern_status_t try_enqueue(struct btree *tree, struct page_request *req)
|
||||
return true;
|
||||
}
|
||||
|
||||
static kern_status_t send_request_async(
|
||||
struct vm_controller *ctrl,
|
||||
struct page_request *req)
|
||||
{
|
||||
fill_random(&req->req_id, sizeof req->req_id);
|
||||
while (!try_enqueue(&ctrl->vc_requests, req)) {
|
||||
req->req_id++;
|
||||
}
|
||||
|
||||
ctrl->vc_requests_waiting++;
|
||||
object_assert_signal(
|
||||
&ctrl->vc_base,
|
||||
VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED);
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t vm_controller_recv(
|
||||
struct vm_controller *ctrl,
|
||||
equeue_packet_page_request_t *out)
|
||||
{
|
||||
struct page_request *req = NULL;
|
||||
|
||||
req = get_next_request(ctrl);
|
||||
if (!req) {
|
||||
return KERN_NO_ENTRY;
|
||||
}
|
||||
|
||||
if (ctrl->vc_requests_waiting == 0) {
|
||||
object_clear_signal(
|
||||
&ctrl->vc_base,
|
||||
VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED);
|
||||
}
|
||||
|
||||
out->req_vmo = req->req_object;
|
||||
out->req_type = req->req_type;
|
||||
out->req_offset = req->req_offset;
|
||||
out->req_length = req->req_length;
|
||||
|
||||
spin_unlock(&req->req_lock);
|
||||
|
||||
if (req->req_status == PAGE_REQUEST_ASYNC) {
|
||||
vm_cache_free(&page_request_cache, req);
|
||||
}
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t vm_controller_recv_async(
|
||||
struct vm_controller *ctrl,
|
||||
struct equeue *eq,
|
||||
equeue_key_t key)
|
||||
{
|
||||
if (ctrl->vc_eq) {
|
||||
object_unref(&ctrl->vc_eq->eq_base);
|
||||
}
|
||||
|
||||
object_ref(&eq->eq_base);
|
||||
ctrl->vc_eq = eq;
|
||||
ctrl->vc_eq_key = key;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t vm_controller_create_object(
|
||||
struct vm_controller *ctrl,
|
||||
const char *name,
|
||||
size_t name_len,
|
||||
equeue_key_t key,
|
||||
size_t data_len,
|
||||
vm_prot_t prot,
|
||||
struct vm_object **out)
|
||||
{
|
||||
struct vm_object *vmo
|
||||
= vm_object_create(name, name_len, data_len, prot);
|
||||
if (!vmo) {
|
||||
return KERN_NO_MEMORY;
|
||||
}
|
||||
|
||||
object_ref(&ctrl->vc_base);
|
||||
|
||||
vmo->vo_flags |= VMO_CONTROLLER;
|
||||
vmo->vo_ctrl = ctrl;
|
||||
vmo->vo_key = key;
|
||||
|
||||
*out = vmo;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t vm_controller_detach_object(
|
||||
struct vm_controller *ctrl,
|
||||
struct vm_object *vmo)
|
||||
{
|
||||
if (vmo->vo_ctrl != ctrl) {
|
||||
return KERN_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
struct page_request *req
|
||||
= vm_cache_alloc(&page_request_cache, VM_NORMAL);
|
||||
req->req_type = PAGE_REQUEST_DETACH;
|
||||
req->req_status = PAGE_REQUEST_ASYNC;
|
||||
req->req_object = vmo->vo_key;
|
||||
req->req_sender = current_thread();
|
||||
send_request_async(ctrl, req);
|
||||
|
||||
vmo->vo_ctrl = NULL;
|
||||
vmo->vo_key = 0;
|
||||
object_unref(&ctrl->vc_base);
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
static void wait_for_reply(
|
||||
struct vm_controller *ctrl,
|
||||
struct page_request *req,
|
||||
@@ -221,7 +254,7 @@ static void wait_for_reply(
|
||||
|
||||
static void fulfill_requests(
|
||||
struct vm_controller *ctrl,
|
||||
struct vm_object *obj,
|
||||
equeue_key_t object,
|
||||
off_t offset,
|
||||
size_t length,
|
||||
kern_status_t result)
|
||||
@@ -242,7 +275,7 @@ static void fulfill_requests(
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (req->req_object != obj) {
|
||||
if (req->req_object != object) {
|
||||
match = false;
|
||||
}
|
||||
|
||||
@@ -280,7 +313,7 @@ kern_status_t vm_controller_supply_pages(
|
||||
src_offset,
|
||||
count,
|
||||
NULL);
|
||||
fulfill_requests(ctrl, dst, dst_offset, count, status);
|
||||
fulfill_requests(ctrl, dst->vo_key, dst_offset, count, status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ static kern_status_t vm_object_cleanup(struct object *obj)
|
||||
cur = next;
|
||||
}
|
||||
|
||||
if (vmo->vo_ctrl) {
|
||||
unsigned long flags;
|
||||
vm_controller_lock_irqsave(vmo->vo_ctrl, &flags);
|
||||
vm_controller_detach_object(vmo->vo_ctrl, vmo);
|
||||
vm_controller_unlock_irqrestore(vmo->vo_ctrl, flags);
|
||||
}
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
@@ -366,12 +373,13 @@ static kern_status_t request_page(
|
||||
struct vm_controller *ctrl = vo->vo_ctrl;
|
||||
struct page_request req = {0};
|
||||
req.req_status = PAGE_REQUEST_PENDING;
|
||||
req.req_type = PAGE_REQUEST_READ;
|
||||
req.req_offset = offset;
|
||||
req.req_length = vm_page_order_to_bytes(VM_PAGE_4K);
|
||||
req.req_sender = current_thread();
|
||||
|
||||
object_ref(&vo->vo_base);
|
||||
req.req_object = vo;
|
||||
req.req_object = vo->vo_key;
|
||||
|
||||
vm_object_unlock_irqrestore(vo, *irq_flags);
|
||||
vm_controller_lock_irqsave(ctrl, irq_flags);
|
||||
|
||||
Reference in New Issue
Block a user