Commit 1e361b51 authored by hanliyang's avatar hanliyang
Browse files

KVM: SVM: Add support for different CSV guests to reuse the same ASID

hygon inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I98WUF


CVE: NA

---------------------------

If user want to reuse one ASID for many CSV guests, he should provide a
label (i.e. userid) and the length of the label when launch CSV guest.
The reference count of the ASID will be increased if user launch a CSV
guest with the label correspond to the ASID. When a CSV guest which
launch with a label is destroyed, the reference count of the ASID
correspond to the label will be decreased, and the ASID is freed only if
the reference count becomes zero.

The codes for reuse ASID is not compatible with CONFIG_CGROUP_MISC, we
introduce CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID that depends on
!CGROUP_MISC, the code take effect only when
CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID=y.

Signed-off-by: default avatarhanliyang <hanliyang@hygon.cn>
parent 10bfbd53
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -163,4 +163,14 @@ config ARCH_VCPU_STAT

	  If unsure, say N.

config KVM_SUPPORTS_CSV_REUSE_ASID
	def_bool y
	bool "Reuse the same ASID for different HYGON CSV guests"
	depends on KVM_AMD_SEV && CPU_SUP_HYGON && HYGON_CSV
	depends on !CGROUP_MISC
	help
	  Provide support for reuse the same ASID for difference HYGON
	  CSV guests, this allow the user to create more CSV guests on
	  HYGON CPUs with limited ASIDs.

endif # VIRTUALIZATION
+27 −0
Original line number Diff line number Diff line
@@ -1058,6 +1058,33 @@ static int csv_control_post_system_reset(struct kvm *kvm)
	return 0;
}

#ifdef CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID

struct csv_asid_userid *csv_asid_userid_array;

int csv_alloc_asid_userid_array(unsigned int nr_asids)
{
	int ret = 0;

	csv_asid_userid_array = kcalloc(nr_asids, sizeof(struct csv_asid_userid),
					GFP_KERNEL_ACCOUNT);
	if (!csv_asid_userid_array)
		ret = -ENOMEM;

	if (ret)
		pr_warn("Fail to allocate array, reuse ASID is unavailable\n");

	return ret;
}

void csv_free_asid_userid_array(void)
{
	kfree(csv_asid_userid_array);
	csv_asid_userid_array = NULL;
}

#endif	/* CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID */

void csv_exit(void)
{
}
+22 −0
Original line number Diff line number Diff line
@@ -32,6 +32,28 @@ struct csv_ringbuf_infos {
	int num;
};

#ifdef CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID

#define ASID_USERID_LENGTH 20

struct csv_asid_userid {
	int refcnt; // reference count of the ASID
	u32 userid_len;
	char userid[ASID_USERID_LENGTH];
};

extern struct csv_asid_userid *csv_asid_userid_array;

int csv_alloc_asid_userid_array(unsigned int nr_asids);
void csv_free_asid_userid_array(void);

#else

static inline int csv_alloc_asid_userid_array(unsigned int nr_asids) { return -ENOMEM; }
static inline void csv_free_asid_userid_array(void) { }

#endif	/* CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID */

#ifdef CONFIG_HYGON_CSV

/*
+122 −8
Original line number Diff line number Diff line
@@ -145,7 +145,11 @@ static void sev_misc_cg_uncharge(struct kvm_sev_info *sev)
	misc_cg_uncharge(type, sev->misc_cg, 1);
}

#ifdef CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID
static int sev_asid_new(struct kvm_sev_info *sev, const char *userid, u32 userid_len)
#else
static int sev_asid_new(struct kvm_sev_info *sev)
#endif
{
	/*
	 * SEV-enabled guests must use asid from min_sev_asid to max_sev_asid.
@@ -180,6 +184,34 @@ static int sev_asid_new(struct kvm_sev_info *sev)

	mutex_lock(&sev_bitmap_lock);

#ifdef CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID
	/* For Hygon CPU, check whether the userid exists */
	if (is_x86_vendor_hygon() && userid && userid_len &&
	    !WARN_ON_ONCE(!csv_asid_userid_array)) {
		int i = !min_sev_asid ? 1 : min_sev_asid;

		for (; i <= max_sev_asid; i++) {
			/* skip ASIDs without correspond userid */
			if (!csv_asid_userid_array[i].userid_len)
				continue;

			/* skip if length of userid is different */
			if (csv_asid_userid_array[i].userid_len != userid_len)
				continue;

			if (!memcmp(csv_asid_userid_array[i].userid,
				    userid, userid_len)) {
				pr_debug("Found reusable asid %d\n", i);
				/* Increase reference count if userid exists */
				csv_asid_userid_array[i].refcnt++;

				mutex_unlock(&sev_bitmap_lock);
				return i;
			}
		}
	}
#endif

again:
	asid = find_next_zero_bit(sev_asid_bitmap, max_asid + 1, min_asid);
	if (asid > max_asid) {
@@ -194,6 +226,16 @@ static int sev_asid_new(struct kvm_sev_info *sev)

	__set_bit(asid, sev_asid_bitmap);

#ifdef CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID
	/* For Hygon CPU, initialize the new userid */
	if (is_x86_vendor_hygon() && userid && userid_len &&
	    !WARN_ON_ONCE(!csv_asid_userid_array)) {
		memcpy(csv_asid_userid_array[asid].userid, userid, userid_len);
		csv_asid_userid_array[asid].userid_len = userid_len;
		csv_asid_userid_array[asid].refcnt = 1;
	}
#endif

	mutex_unlock(&sev_bitmap_lock);

	return asid;
@@ -218,8 +260,26 @@ static void sev_asid_free(struct kvm_sev_info *sev)

	mutex_lock(&sev_bitmap_lock);

#ifdef CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID
	/* For Hygon CPU, decrease the reference count if userid exist */
	if (!is_x86_vendor_hygon() || !csv_asid_userid_array ||
	    !csv_asid_userid_array[sev->asid].userid_len) {
		__set_bit(sev->asid, sev_reclaim_asid_bitmap);
	} else {
		/* If reach here, reference count should large than 0. */
		WARN_ON(csv_asid_userid_array[sev->asid].refcnt <= 0);

		if (--csv_asid_userid_array[sev->asid].refcnt == 0) {
			__set_bit(sev->asid, sev_reclaim_asid_bitmap);

			memset(&csv_asid_userid_array[sev->asid], 0,
			       sizeof(struct csv_asid_userid));
		}
	}
#else
	__set_bit(sev->asid, sev_reclaim_asid_bitmap);
#endif

	for_each_possible_cpu(cpu) {
		sd = per_cpu_ptr(&svm_data, cpu);
		sd->sev_vmcbs[sev->asid] = NULL;
@@ -274,7 +334,46 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)

	sev->active = true;
	sev->es_active = argp->id == KVM_SEV_ES_INIT;

#ifdef CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID
	/* Try reuse ASID iff userid array is available for HYGON CSV guests */
	if (is_x86_vendor_hygon() && csv_asid_userid_array) {
		struct kvm_csv_init params;
		void *csv_blob = NULL;

		memset(&params, 0, sizeof(params));

		if (argp->data &&
		    copy_from_user(&params,
				(void __user *)(uintptr_t)argp->data, sizeof(params)))
			return -EFAULT;

		if (params.userid_addr) {
			if (params.len >= ASID_USERID_LENGTH) {
				pr_err("Invalid length of userid %d > %d\n",
				       params.len, ASID_USERID_LENGTH);
				return -EINVAL;
			}

			csv_blob = psp_copy_user_blob(params.userid_addr, params.len);
			if (IS_ERR(csv_blob)) {
				pr_err("Copy userid failed, %llx (%u)\n",
				       params.userid_addr, params.len);
				return PTR_ERR(csv_blob);
			}
		}

		asid = sev_asid_new(sev, (const char *)csv_blob, params.len);

		/* The buffer @csv_blob is no longer used, free it. */
		kfree(csv_blob);
	} else {
		asid = sev_asid_new(sev, NULL, 0);
	}
#else
	asid = sev_asid_new(sev);
#endif

	if (asid < 0)
		goto e_no_asid;
	sev->asid = asid;
@@ -2348,13 +2447,19 @@ void __init sev_hardware_setup(void)
		 */
		sev_install_hooks();

		if (sev_enabled) {
			/*
			 * Allocate a memory pool to speed up live migration of
			 * the CSV/CSV2 guests. If the allocation fails, no
			 * acceleration is performed at live migration.
			 */
		if (sev_enabled)
			csv_alloc_trans_mempool();
			/*
			 * Allocate a buffer to support reuse ASID, reuse ASID
			 * will not work if the allocation fails.
			 */
			csv_alloc_asid_userid_array(nr_asids);
		}
	}
#endif

@@ -2366,9 +2471,11 @@ void sev_hardware_unsetup(void)
	if (!sev_enabled)
		return;

	/* Free the memory pool that allocated in sev_hardware_setup(). */
	if (is_x86_vendor_hygon())
	/* Free the memory that allocated in sev_hardware_setup(). */
	if (is_x86_vendor_hygon()) {
		csv_free_trans_mempool();
		csv_free_asid_userid_array();
	}

	/* No need to take sev_bitmap_lock, all VMs have been destroyed. */
	sev_flush_asids(1, max_sev_asid);
@@ -2727,6 +2834,13 @@ void pre_sev_run(struct vcpu_svm *svm, int cpu)
	/* Assign the asid allocated with this SEV guest */
	svm->asid = asid;

#ifdef CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID
	/* If ASID is shared with other guests, then flush TLB before VMRUN */
	if (is_x86_vendor_hygon() && csv_asid_userid_array &&
	    csv_asid_userid_array[asid].userid_len)
		svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ASID;
#endif

	/*
	 * Flush guest TLB:
	 *
+5 −0
Original line number Diff line number Diff line
@@ -2334,6 +2334,11 @@ struct kvm_csv_receive_update_vmsa {
	__u32 trans_len;
};

struct kvm_csv_init {
	__u64 userid_addr;
	__u32 len;
};

/* ioctls for control vm during system reset, currently only for CSV */
#define KVM_CONTROL_PRE_SYSTEM_RESET	 _IO(KVMIO, 0xe8)
#define KVM_CONTROL_POST_SYSTEM_RESET	 _IO(KVMIO, 0xe9)