[v8,23/28] virt: gunyah: Add resource tickets

Message ID 20221219225850.2397345-24-quic_eberman@quicinc.com
State New
Headers
Series Drivers for gunyah hypervisor |

Commit Message

Elliot Berman Dec. 19, 2022, 10:58 p.m. UTC
  Some VM functions need to acquire Gunyah resources. For instance, Gunyah
vCPUs are exposed to the host as a resource. The Gunyah vCPU function
will register a resource ticket and be able to interact with the
hypervisor once the resource ticket is filled.

Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
---
 drivers/virt/gunyah/vm_mgr.c  | 93 ++++++++++++++++++++++++++++++++++-
 drivers/virt/gunyah/vm_mgr.h  |  5 ++
 include/linux/gunyah.h        |  1 +
 include/linux/gunyah_vm_mgr.h | 14 ++++++
 4 files changed, 112 insertions(+), 1 deletion(-)
  

Patch

diff --git a/drivers/virt/gunyah/vm_mgr.c b/drivers/virt/gunyah/vm_mgr.c
index a5bda4d6613b..3328b84fa681 100644
--- a/drivers/virt/gunyah/vm_mgr.c
+++ b/drivers/virt/gunyah/vm_mgr.c
@@ -115,6 +115,56 @@  static long gh_vm_add_function(struct gunyah_vm *ghvm, struct gunyah_vm_function
 	return r;
 }
 
+int ghvm_add_resource_ticket(struct gunyah_vm *ghvm, struct gunyah_vm_resource_ticket *ticket)
+{
+	struct gunyah_vm_resource_ticket *iter;
+	struct gunyah_resource *ghrsc;
+	int ret = 0;
+
+	mutex_lock(&ghvm->resources_lock);
+	list_for_each_entry(iter, &ghvm->resource_tickets, list) {
+		if (iter->resource_type == ticket->resource_type && iter->label == ticket->label) {
+			ret = -EEXIST;
+			goto out;
+		}
+	}
+
+	if (!try_module_get(ticket->owner)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	list_add(&ticket->list, &ghvm->resource_tickets);
+	INIT_LIST_HEAD(&ticket->resources);
+
+	list_for_each_entry(ghrsc, &ghvm->resources, list) {
+		if (ghrsc->type == ticket->resource_type && ghrsc->rm_label == ticket->label) {
+			if (!ticket->populate(ticket, ghrsc))
+				list_move(&ghrsc->list, &ticket->resources);
+		}
+	}
+out:
+	mutex_unlock(&ghvm->resources_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ghvm_add_resource_ticket);
+
+void ghvm_remove_resource_ticket(struct gunyah_vm *ghvm, struct gunyah_vm_resource_ticket *ticket)
+{
+	struct gunyah_resource *ghrsc;
+
+	mutex_lock(&ghvm->resources_lock);
+	list_for_each_entry(ghrsc, &ticket->resources, list) {
+		ticket->unpopulate(ticket, ghrsc);
+		list_move(&ghrsc->list, &ghvm->resources);
+	}
+
+	module_put(ticket->owner);
+	list_del(&ticket->list);
+	mutex_unlock(&ghvm->resources_lock);
+}
+EXPORT_SYMBOL_GPL(ghvm_remove_resource_ticket);
+
 static void ghvm_put(struct kref *kref)
 {
 	struct gunyah_vm *ghvm = container_of(kref, struct gunyah_vm, kref);
@@ -156,17 +206,41 @@  static __must_check struct gunyah_vm *gunyah_vm_alloc(struct gh_rm_rpc *rm)
 	INIT_LIST_HEAD(&ghvm->memory_mappings);
 	init_rwsem(&ghvm->status_lock);
 	kref_init(&ghvm->kref);
+	mutex_init(&ghvm->resources_lock);
+	INIT_LIST_HEAD(&ghvm->resources);
+	INIT_LIST_HEAD(&ghvm->resource_tickets);
 	INIT_LIST_HEAD(&ghvm->functions);
 
 	return ghvm;
 }
 
+static void gh_vm_add_resource(struct gunyah_vm *ghvm, struct gunyah_resource *ghrsc)
+{
+	struct gunyah_vm_resource_ticket *ticket;
+
+	mutex_lock(&ghvm->resources_lock);
+	list_for_each_entry(ticket, &ghvm->resource_tickets, list) {
+		if (ghrsc->type == ticket->resource_type && ghrsc->rm_label == ticket->label) {
+			if (!ticket->populate(ticket, ghrsc)) {
+				list_add(&ghrsc->list, &ticket->resources);
+				goto found;
+			}
+		}
+	}
+	list_add(&ghrsc->list, &ghvm->resources);
+found:
+	mutex_unlock(&ghvm->resources_lock);
+}
+
 static int gh_vm_start(struct gunyah_vm *ghvm)
 {
 	struct gunyah_vm_memory_mapping *mapping;
 	u64 dtb_offset;
 	u32 mem_handle;
-	int ret;
+	ssize_t num_hyp_resources;
+	struct gunyah_resource *ghrsc;
+	struct gh_rm_hyp_resource *resources;
+	int ret, i;
 
 	down_write(&ghvm->status_lock);
 	if (ghvm->vm_status != GH_RM_VM_STATUS_NO_STATE) {
@@ -198,6 +272,23 @@  static int gh_vm_start(struct gunyah_vm *ghvm)
 		goto err;
 	}
 
+	num_hyp_resources = gh_rm_get_hyp_resources(ghvm->rm, ghvm->vmid, &resources);
+	if (num_hyp_resources < 0) {
+		pr_warn("Failed to get hypervisor resources for VM: %ld\n", num_hyp_resources);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	for (i = 0; i < num_hyp_resources; i++) {
+		ghrsc = gh_rm_alloc_resource(ghvm->rm, &resources[i]);
+		if (!ghrsc) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		gh_vm_add_resource(ghvm, ghrsc);
+	}
+
 	ret = gh_rm_vm_start(ghvm->rm, ghvm->vmid);
 	if (ret) {
 		pr_warn("Failed to start VM: %d\n", ret);
diff --git a/drivers/virt/gunyah/vm_mgr.h b/drivers/virt/gunyah/vm_mgr.h
index 2c4fd240792a..8a71f97960bc 100644
--- a/drivers/virt/gunyah/vm_mgr.h
+++ b/drivers/virt/gunyah/vm_mgr.h
@@ -7,6 +7,7 @@ 
 #define _GH_PRIV_VM_MGR_H
 
 #include <linux/gunyah_rsc_mgr.h>
+#include <linux/gunyah_vm_mgr.h>
 #include <linux/list.h>
 #include <linux/kref.h>
 #include <linux/miscdevice.h>
@@ -53,6 +54,10 @@  struct gunyah_vm {
 	struct mutex mm_lock;
 	struct list_head memory_mappings;
 
+	struct mutex resources_lock;
+	struct list_head resources;
+	struct list_head resource_tickets;
+
 	struct list_head functions;
 };
 
diff --git a/include/linux/gunyah.h b/include/linux/gunyah.h
index 8cb6af88c75d..af69a3479025 100644
--- a/include/linux/gunyah.h
+++ b/include/linux/gunyah.h
@@ -28,6 +28,7 @@  struct gunyah_resource {
 	int irq;
 
 	/* To help allocator in resource manager */
+	struct list_head list;
 	u32 rm_label;
 };
 
diff --git a/include/linux/gunyah_vm_mgr.h b/include/linux/gunyah_vm_mgr.h
index 4bb06f100ae5..3e6e6e0ab7f0 100644
--- a/include/linux/gunyah_vm_mgr.h
+++ b/include/linux/gunyah_vm_mgr.h
@@ -65,4 +65,18 @@  void gunyah_vm_function_unregister(struct gunyah_vm_function_driver *f);
 	}								\
 	module_exit(_name##_mod_exit)
 
+struct gunyah_vm_resource_ticket {
+	struct list_head list;
+	struct list_head resources;
+	enum gunyah_resource_type resource_type;
+	u32 label;
+
+	struct module *owner;
+	int (*populate)(struct gunyah_vm_resource_ticket *ticket, struct gunyah_resource *ghrsc);
+	void (*unpopulate)(struct gunyah_vm_resource_ticket *ticket, struct gunyah_resource *ghrsc);
+};
+
+int ghvm_add_resource_ticket(struct gunyah_vm *ghvm, struct gunyah_vm_resource_ticket *ticket);
+void ghvm_remove_resource_ticket(struct gunyah_vm *ghvm, struct gunyah_vm_resource_ticket *ticket);
+
 #endif