@@ -175,12 +175,18 @@ int gzvm_arch_create_vm(unsigned long vm_type)
return ret ? ret : res.a1;
}
-int gzvm_arch_destroy_vm(u16 vm_id)
+int gzvm_arch_destroy_vm(u16 vm_id, u64 destroy_page_gran)
{
struct arm_smccc_res res;
+ int ret;
+
+ do {
+ ret = gzvm_hypcall_wrapper(MT_HVC_GZVM_DESTROY_VM, vm_id,
+ destroy_page_gran, 0, 0,
+ 0, 0, 0, &res);
+ } while (ret == -EAGAIN);
- return gzvm_hypcall_wrapper(MT_HVC_GZVM_DESTROY_VM, vm_id, 0, 0, 0, 0,
- 0, 0, &res);
+ return ret;
}
int gzvm_arch_memregion_purpose(struct gzvm *gzvm,
@@ -241,6 +247,21 @@ int gzvm_arch_query_hyp_batch_pages(struct gzvm_enable_cap *cap,
return ret;
}
+int gzvm_arch_query_destroy_batch_pages(struct gzvm_enable_cap *cap,
+ void __user *argp)
+{
+ struct arm_smccc_res res = {0};
+ int ret;
+
+ ret = gzvm_arch_enable_cap(cap, &res);
+ // destroy page batch size should be power of 2
+ if (ret || ((res.a1 & (res.a1 - 1)) != 0))
+ return -EINVAL;
+
+ cap->args[0] = res.a1;
+ return ret;
+}
+
/**
* gzvm_vm_ioctl_get_pvmfw_size() - Get pvmfw size from hypervisor, return
* in x1, and return to userspace in args
@@ -49,6 +49,33 @@ static ssize_t demand_paging_batch_pages_store(struct kobject *kobj,
return count;
}
+static ssize_t destroy_batch_pages_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%u\n", gzvm_drv.destroy_batch_pages);
+}
+
+static ssize_t destroy_batch_pages_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ u32 temp;
+
+ ret = kstrtoint(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ // destroy page batch size should be power of 2
+ if ((temp & (temp - 1)) != 0)
+ return -EINVAL;
+
+ gzvm_drv.destroy_batch_pages = temp;
+
+ return count;
+}
+
/* /sys/kernel/gzvm/demand_paging_batch_pages */
static struct kobj_attribute demand_paging_batch_pages_attr = {
.attr = {
@@ -59,6 +86,16 @@ static struct kobj_attribute demand_paging_batch_pages_attr = {
.store = demand_paging_batch_pages_store,
};
+/* /sys/kernel/gzvm/destroy_batch_pages */
+static struct kobj_attribute destroy_batch_pages_attr = {
+ .attr = {
+ .name = "destroy_batch_pages",
+ .mode = 0660,
+ },
+ .show = destroy_batch_pages_show,
+ .store = destroy_batch_pages_store,
+};
+
static int gzvm_drv_sysfs_init(void)
{
int ret = 0;
@@ -73,6 +110,11 @@ static int gzvm_drv_sysfs_init(void)
if (ret)
pr_debug("failed to create demand_batch_pages in /sys/kernel/gzvm\n");
+ ret = sysfs_create_file(gzvm_drv.sysfs_root_dir,
+ &destroy_batch_pages_attr.attr);
+ if (ret)
+ pr_debug("failed to create destroy_batch_pages in /sys/kernel/gzvm\n");
+
return ret;
}
@@ -124,6 +166,8 @@ int gzvm_err_to_errno(unsigned long err)
return -EOPNOTSUPP;
case ERR_FAULT:
return -EFAULT;
+ case ERR_BUSY:
+ return -EAGAIN;
default:
break;
}
@@ -220,6 +264,20 @@ static int gzvm_query_hyp_batch_pages(void)
return ret;
}
+static int gzvm_query_destroy_batch_pages(void)
+{
+ int ret;
+ struct gzvm_enable_cap cap = {0};
+
+ gzvm_drv.destroy_batch_pages = GZVM_DRV_DESTROY_PAGING_BATCH_PAGES;
+ cap.cap = GZVM_CAP_QUERY_DESTROY_BATCH_PAGES;
+
+ ret = gzvm_arch_query_destroy_batch_pages(&cap, NULL);
+ if (!ret)
+ gzvm_drv.destroy_batch_pages = cap.args[0];
+ return ret;
+}
+
static int gzvm_drv_probe(struct platform_device *pdev)
{
int ret;
@@ -257,6 +315,10 @@ static int gzvm_drv_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = gzvm_query_destroy_batch_pages();
+ if (ret)
+ return ret;
+
return 0;
}
@@ -360,7 +360,7 @@ static void gzvm_destroy_vm(struct gzvm *gzvm)
gzvm_vm_irqfd_release(gzvm);
gzvm_destroy_vcpus(gzvm);
- gzvm_arch_destroy_vm(gzvm->vm_id);
+ gzvm_arch_destroy_vm(gzvm->vm_id, gzvm->gzvm_drv->destroy_batch_pages);
mutex_lock(&gzvm_list_lock);
list_del(&gzvm->vm_list);
@@ -30,6 +30,7 @@ struct gzvm_driver {
struct kobject *sysfs_root_dir;
u32 demand_paging_batch_pages;
+ u32 destroy_batch_pages;
struct dentry *gzvm_debugfs_dir;
};
@@ -53,6 +54,7 @@ struct gzvm_driver {
#define ERR_INVALID_ARGS (-8)
#define ERR_NOT_SUPPORTED (-24)
#define ERR_NOT_IMPLEMENTED (-27)
+#define ERR_BUSY (-33)
#define ERR_FAULT (-40)
#define GZVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1
@@ -68,6 +70,7 @@ struct gzvm_driver {
#define GZVM_BLOCK_BASED_DEMAND_PAGE_SIZE (PMD_SIZE) /* 2MB */
#define GZVM_DRV_DEMAND_PAGING_BATCH_PAGES \
(GZVM_BLOCK_BASED_DEMAND_PAGE_SIZE / PAGE_SIZE)
+#define GZVM_DRV_DESTROY_PAGING_BATCH_PAGES (128)
#define GZVM_MAX_DEBUGFS_DIR_NAME_SIZE 20
#define GZVM_MAX_DEBUGFS_VALUE_SIZE 20
@@ -225,12 +228,14 @@ int gzvm_arch_probe(struct gzvm_version drv_version,
struct gzvm_version *hyp_version);
int gzvm_arch_query_hyp_batch_pages(struct gzvm_enable_cap *cap,
void __user *argp);
+int gzvm_arch_query_destroy_batch_pages(struct gzvm_enable_cap *cap,
+ void __user *argp);
int gzvm_arch_set_memregion(u16 vm_id, size_t buf_size,
phys_addr_t region);
int gzvm_arch_check_extension(struct gzvm *gzvm, __u64 cap, void __user *argp);
int gzvm_arch_create_vm(unsigned long vm_type);
-int gzvm_arch_destroy_vm(u16 vm_id);
+int gzvm_arch_destroy_vm(u16 vm_id, u64 destroy_page_gran);
int gzvm_arch_map_guest(u16 vm_id, int memslot_id, u64 pfn, u64 gfn,
u64 nr_pages);
int gzvm_arch_map_guest_block(u16 vm_id, int memslot_id, u64 gfn, u64 nr_pages);
@@ -23,6 +23,7 @@
#define GZVM_CAP_ENABLE_DEMAND_PAGING 0x9202
#define GZVM_CAP_ENABLE_IDLE 0x9203
#define GZVM_CAP_QUERY_HYP_BATCH_PAGES 0x9204
+#define GZVM_CAP_QUERY_DESTROY_BATCH_PAGES 0x9205
/* sub-commands put in args[0] for GZVM_CAP_PROTECTED_VM */
#define GZVM_CAP_PVM_SET_PVMFW_GPA 0