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

Upstream: no

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: hanliyang <hanliyang@hygon.cn>
This commit is contained in:
hanliyang 2023-09-08 20:09:00 -04:00
parent d0eb7cacd6
commit 079cc16170
5 changed files with 186 additions and 8 deletions

View File

@ -154,4 +154,14 @@ config KVM_PROVE_MMU
config KVM_EXTERNAL_WRITE_TRACKING
bool
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

View File

@ -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)
{
}

View File

@ -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
/*

View File

@ -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 @@ again:
__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,7 +260,25 @@ 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);
@ -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 @@ out:
*/
sev_install_hooks();
/*
* 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)
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.
*/
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:
*

View File

@ -2301,6 +2301,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)