Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions arch/x86/kvm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
27 changes: 27 additions & 0 deletions arch/x86/kvm/svm/csv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,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)
{
}
Expand Down
22 changes: 22 additions & 0 deletions arch/x86/kvm/svm/csv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

/*
Expand Down
130 changes: 122 additions & 8 deletions arch/x86/kvm/svm/sev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -173,6 +177,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) {
Expand All @@ -187,6 +219,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;
Expand All @@ -211,7 +253,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);
Expand Down Expand Up @@ -267,7 +327,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;
Expand Down Expand Up @@ -2345,13 +2444,19 @@ void __init sev_hardware_setup(void)
*/
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

Expand All @@ -2363,9 +2468,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);
Expand Down Expand Up @@ -2721,6 +2828,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:
*
Expand Down
5 changes: 5 additions & 0 deletions include/uapi/linux/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down