All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/3] kvm/e500: Map (vcpu,as,id) to core's shadow id.
@ 2009-04-27  6:58 Liu Yu
  0 siblings, 0 replies; only message in thread
From: Liu Yu @ 2009-04-27  6:58 UTC (permalink / raw)
  To: kvm-ppc

The shadow id mappings is based on Hollis's idea.

We can benifit a lot from this trick:

1. Support AS=1 in guest.
So that OSes other than Linux can be expected to run in the guest.

2. Minimize the frequency of TLB flushes.

Signed-off-by: Liu Yu <yu.liu@freescale.com>
---
 arch/powerpc/include/asm/kvm_e500.h |    3 +
 arch/powerpc/include/asm/kvm_ppc.h  |    1 +
 arch/powerpc/kernel/asm-offsets.c   |    1 +
 arch/powerpc/kvm/booke.h            |    4 +
 arch/powerpc/kvm/booke_interrupts.S |   11 +++
 arch/powerpc/kvm/e500_emulate.c     |   10 ++-
 arch/powerpc/kvm/e500_tlb.c         |  146 +++++++++++++++++++++++++++++++++--
 arch/powerpc/kvm/e500_tlb.h         |    2 +
 8 files changed, 167 insertions(+), 11 deletions(-)

diff --git a/arch/powerpc/include/asm/kvm_e500.h b/arch/powerpc/include/asm/kvm_e500.h
index ffa111b..e5ca811 100644
--- a/arch/powerpc/include/asm/kvm_e500.h
+++ b/arch/powerpc/include/asm/kvm_e500.h
@@ -43,6 +43,9 @@ struct kvmppc_vcpu_e500 {
 	/* Pages which are referenced in the shadow TLB. */
 	struct kvmppc_e500_shadow_ref *shadow_refs[E500_TLB_NUM];
 
+	/* MMU id mapping */
+	void *id_mapping;
+
 	unsigned int guest_tlb_size[E500_TLB_NUM];
 	unsigned int shadow_tlb_size[E500_TLB_NUM];
 	unsigned int guest_tlb_nv[E500_TLB_NUM];
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index 2c6ee34..40823c4 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -57,6 +57,7 @@ extern void kvmppc_emulate_dec(struct kvm_vcpu *vcpu);
 extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr,
                            unsigned int gtlb_idx);
 extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode);
+extern void kvmppc_mmu_as_switch(struct kvm_vcpu *vcpu, unsigned int as);
 extern void kvmppc_mmu_switch_pid(struct kvm_vcpu *vcpu, u32 pid);
 extern void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu);
 extern int kvmppc_mmu_dtlb_index(struct kvm_vcpu *vcpu, gva_t eaddr);
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 42fe4da..89d28df 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -379,6 +379,7 @@ int main(void)
 	DEFINE(VCPU_LAST_INST, offsetof(struct kvm_vcpu, arch.last_inst));
 	DEFINE(VCPU_FAULT_DEAR, offsetof(struct kvm_vcpu, arch.fault_dear));
 	DEFINE(VCPU_FAULT_ESR, offsetof(struct kvm_vcpu, arch.fault_esr));
+	DEFINE(VCPU_SWAP_PID, offsetof(struct kvm_vcpu, arch.swap_pid));
 #endif
 #ifdef CONFIG_44x
 	DEFINE(PGD_T_LOG2, PGD_T_LOG2);
diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
index d59bcca..96e6cc0 100644
--- a/arch/powerpc/kvm/booke.h
+++ b/arch/powerpc/kvm/booke.h
@@ -57,6 +57,10 @@ static inline void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr)
 	if ((new_msr & MSR_PR) != (vcpu->arch.msr & MSR_PR))
 		kvmppc_mmu_priv_switch(vcpu, new_msr & MSR_PR);
 
+	if ((new_msr & (MSR_IS | MSR_DS)) !+			(vcpu->arch.msr & (MSR_IS | MSR_DS)))
+		kvmppc_mmu_as_switch(vcpu, new_msr & (MSR_IS | MSR_DS));
+
 	vcpu->arch.msr = new_msr;
 
 	if (vcpu->arch.msr & MSR_WE) {
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
index d0c6f84..12383fe 100644
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -192,6 +192,12 @@ _GLOBAL(kvmppc_resume_host)
 	lwz	r3, VCPU_HOST_PID(r4)
 	mtspr	SPRN_PID, r3
 
+#ifdef CONFIG_E500
+	/* we cheat and know Linux doesn't use PID1 which is always 0 */
+	lis	r3, 0
+	mtspr	SPRN_PID1, r3
+#endif
+
 	/* Restore host IVPR before re-enabling interrupts. We cheat and know
 	 * that Linux IVPR is always 0xc0000000. */
 	lis	r3, 0xc000
@@ -350,6 +356,11 @@ lightweight_exit:
 	lwz	r3, VCPU_SHADOW_PID(r4)
 	mtspr	SPRN_PID, r3
 
+#ifdef CONFIG_E500
+	lwz	r3, VCPU_SWAP_PID(r4)
+	mtspr	SPRN_PID1, r3
+#endif
+
 #ifdef CONFIG_44x
 	iccci	0, 0 /* XXX hack */
 #endif
diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c
index 3f76041..8d85655 100644
--- a/arch/powerpc/kvm/e500_emulate.c
+++ b/arch/powerpc/kvm/e500_emulate.c
@@ -76,10 +76,14 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
 	int emulated = EMULATE_DONE;
 
 	switch (sprn) {
-	case SPRN_PID:
-		vcpu_e500->pid[0] = vcpu->arch.shadow_pid -			vcpu->arch.pid = vcpu->arch.gpr[rs];
+	case SPRN_PID: {
+		unsigned int as = !!(vcpu->arch.msr & (MSR_IS | MSR_DS));
+
+		vcpu_e500->pid[0] = vcpu->arch.pid = vcpu->arch.gpr[rs];
+		vcpu->arch.shadow_pid = kvmppc_e500_get_sid(vcpu_e500, as,
+						    get_cur_pid(vcpu));
 		break;
+	}
 	case SPRN_PID1:
 		vcpu_e500->pid[1] = vcpu->arch.gpr[rs]; break;
 	case SPRN_PID2:
diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c
index 847dfec..7b614a9 100644
--- a/arch/powerpc/kvm/e500_tlb.c
+++ b/arch/powerpc/kvm/e500_tlb.c
@@ -27,6 +27,101 @@
 
 static unsigned int tlb1_entry_num;
 
+struct id_mapping {
+	unsigned char id[2][256];
+};
+
+struct shadow_id {
+	void *ref;
+};
+
+static DEFINE_PER_CPU(struct shadow_id[256], host_sid);
+
+static inline int e500_id_create_mapping(unsigned char *entry)
+{
+	unsigned long sid;
+
+	preempt_disable();
+	sid = (unsigned long)++(__get_cpu_var(host_sid)[0].ref);
+	if (sid < 256) {
+		*entry = (unsigned char)sid;
+		__get_cpu_var(host_sid)[sid].ref = entry;
+		return sid;
+	}
+	preempt_enable();
+
+	return -1;
+}
+
+static inline void e500_id_destroy_all(void)
+{
+	preempt_disable();
+	memset(__get_cpu_var(host_sid), 0, sizeof(__get_cpu_var(host_sid)));
+	preempt_enable();
+}
+
+static inline int e500_id_find_mapping(unsigned char *entry)
+{
+	if (*entry && __get_cpu_var(host_sid)[*entry].ref = entry)
+		return *entry;
+	return -1;
+}
+
+static int kvmppc_e500_alloc_idm(struct kvmppc_vcpu_e500 *vcpu_e500)
+{
+	vcpu_e500->id_mapping +	    (struct id_mapping *)kzalloc(sizeof(struct id_mapping), GFP_KERNEL);
+	if (vcpu_e500->id_mapping != NULL)
+		return 0;
+	return -1;
+}
+
+static void kvmppc_e500_free_idm(struct kvmppc_vcpu_e500 *vcpu_e500)
+{
+	kfree(vcpu_e500->id_mapping);
+	vcpu_e500->id_mapping = NULL;
+	return;
+}
+
+static inline void kvmppc_e500_reset_idm(struct kvmppc_vcpu_e500 *vcpu_e500)
+{
+	memset(vcpu_e500->id_mapping, 0, sizeof(struct id_mapping));
+}
+
+static void inline kvmppc_e500_update_spid(struct kvmppc_vcpu_e500 *vcpu_e500)
+{
+	unsigned int as = !!(vcpu_e500->vcpu.arch.msr & (MSR_IS | MSR_DS));
+
+	vcpu_e500->vcpu.arch.shadow_pid = kvmppc_e500_get_sid(vcpu_e500, as,
+			get_cur_pid(&vcpu_e500->vcpu));
+	vcpu_e500->vcpu.arch.swap_pid = kvmppc_e500_get_sid(vcpu_e500, as, 0);
+}
+
+/*
+ * Map guest (vcpu,as,id) to individual shadow id.
+ */
+unsigned int kvmppc_e500_get_sid(struct kvmppc_vcpu_e500 *vcpu_e500,
+				  int as, int gid)
+{
+	struct id_mapping *idm = vcpu_e500->id_mapping;
+	int sid;
+
+	sid = e500_id_find_mapping(&idm->id[as][gid]);
+
+	while (sid <= 0) {
+		/* None mapping yet */
+		sid = e500_id_create_mapping(&idm->id[as][gid]);
+		if(sid <= 0) {
+			BUG_ON(sid = 0);
+			_tlbil_all();
+			e500_id_destroy_all();
+			kvmppc_e500_update_spid(vcpu_e500);
+		}
+	}
+
+	return sid;
+}
+
 void kvmppc_dump_tlbs(struct kvm_vcpu *vcpu)
 {
 	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
@@ -131,11 +226,14 @@ static inline void write_host_tlbe(struct kvmppc_vcpu_e500 *vcpu_e500,
 
 void kvmppc_e500_tlb_load(struct kvm_vcpu *vcpu, int cpu)
 {
+	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
+
+	/* Shadow PID may be expired */
+	kvmppc_e500_update_spid(vcpu_e500);
 }
 
 void kvmppc_e500_tlb_put(struct kvm_vcpu *vcpu)
 {
-	_tlbil_all();
 }
 
 /* Search the guest TLB for a matching entry. */
@@ -245,10 +343,14 @@ static inline void kvmppc_e500_setup_stlbe(struct kvmppc_vcpu_e500 *vcpu_e500,
 		u64 gvaddr, struct tlbe *stlbe)
 {
 	hpa_t hpaddr = page_to_phys(ref->page);
+	unsigned int stid;
+
+	stid = kvmppc_e500_get_sid(vcpu_e500, get_tlb_ts(gtlbe),
+				get_tlb_tid(gtlbe));
 
 	/* Force TS=1 IPROT=0 TSIZE=4KB for all guest mappings. */
 	stlbe->mas1 = MAS1_TSIZE(BOOKE_PAGESZ_4K)
-		| MAS1_TID(get_tlb_tid(gtlbe)) | MAS1_TS | MAS1_VALID;
+		| MAS1_TID(stid) | MAS1_TS | MAS1_VALID;
 	stlbe->mas2 = (gvaddr & MAS2_EPN)
 		| e500_shadow_mas2_attrib(gtlbe->mas2,
 				vcpu_e500->vcpu.arch.msr & MSR_PR);
@@ -315,11 +417,29 @@ static int kvmppc_e500_tlb1_map(struct kvmppc_vcpu_e500 *vcpu_e500,
  * proper permission bits. */
 void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode)
 {
+	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
+
 	if (usermode) {
-		_tlbil_all();
+		/* clear PID for guest kernel mapping */
+		vcpu->arch.swap_pid = 0;
+	} else {
+		/* set PID for guest kernel mapping
+		 * We assume:
+		 * 1. AS  = 0 when enter supervise mode
+		 * 2. TID = 0 for supervise code/data mappings */
+		vcpu->arch.swap_pid = kvmppc_e500_get_sid(vcpu_e500, 0, 0);
 	}
 }
 
+void kvmppc_mmu_as_switch(struct kvm_vcpu *vcpu, u32 new_as)
+{
+	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
+	u32 pid = get_cur_pid(vcpu);
+
+	vcpu->arch.shadow_pid = kvmppc_e500_get_sid(vcpu_e500, !!new_as, pid);
+	vcpu->arch.swap_pid = kvmppc_e500_get_sid(vcpu_e500, !!new_as, 0);
+}
+
 static inline int kvmppc_e500_gtlbe_invalidate(
 				struct kvmppc_vcpu_e500 *vcpu_e500,
 				int tlbsel, int esel)
@@ -345,7 +465,9 @@ int kvmppc_e500_emul_mt_mmucsr0(struct kvmppc_vcpu_e500 *vcpu_e500, ulong value)
 		for (esel = 0; esel < vcpu_e500->guest_tlb_size[1]; esel++)
 			kvmppc_e500_gtlbe_invalidate(vcpu_e500, 1, esel);
 
-	_tlbil_all();
+	/* Reset vcpu shadow id mapping */
+	kvmppc_e500_reset_idm(vcpu_e500);
+	kvmppc_e500_update_spid(vcpu_e500);
 
 	return EMULATE_DONE;
 }
@@ -376,7 +498,9 @@ int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *vcpu, int ra, int rb)
 			kvmppc_e500_gtlbe_invalidate(vcpu_e500, tlbsel, esel);
 	}
 
-	_tlbil_all();
+	/* Reset vcpu shadow id mapping */
+	kvmppc_e500_reset_idm(vcpu_e500);
+	kvmppc_e500_update_spid(vcpu_e500);
 
 	return EMULATE_DONE;
 }
@@ -555,9 +679,6 @@ void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
 	for (stlbsel = 0; stlbsel < 2; stlbsel++)
 		for (i = 0; i < vcpu_e500->guest_tlb_size[stlbsel]; i++)
 			kvmppc_e500_shadow_release(vcpu_e500, stlbsel, i);
-
-	/* discard all guest mapping */
-	_tlbil_all();
 }
 
 void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 eaddr, gpa_t gpaddr,
@@ -629,6 +750,9 @@ void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *vcpu_e500)
 	tlbe->mas2 = (0xe0004500 & 0xFFFFF000) | MAS2_I | MAS2_G;
 	tlbe->mas3 = (0xe0004500 & 0xFFFFF000) | E500_TLB_SUPER_PERM_MASK;
 	tlbe->mas7 = 0;
+
+	/* Setup shadow PID before start guest */
+	kvmppc_e500_update_spid(vcpu_e500);
 }
 
 int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500)
@@ -657,8 +781,13 @@ int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500)
 	if (vcpu_e500->shadow_refs[1] = NULL)
 		goto err_out_ref0;
 
+	if(kvmppc_e500_alloc_idm(vcpu_e500) < 0)
+		goto err_out_ref1;
+
 	return 0;
 
+err_out_ref1:
+	kfree(vcpu_e500->shadow_refs[1]);
 err_out_ref0:
 	kfree(vcpu_e500->shadow_refs[0]);
 err_out_guest1:
@@ -671,6 +800,7 @@ err_out:
 
 void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *vcpu_e500)
 {
+	kvmppc_e500_free_idm(vcpu_e500);
 	kfree(vcpu_e500->shadow_refs[1]);
 	kfree(vcpu_e500->shadow_refs[0]);
 	kfree(vcpu_e500->guest_tlb[1]);
diff --git a/arch/powerpc/kvm/e500_tlb.h b/arch/powerpc/kvm/e500_tlb.h
index 45b064b..5c6e56c 100644
--- a/arch/powerpc/kvm/e500_tlb.h
+++ b/arch/powerpc/kvm/e500_tlb.h
@@ -55,6 +55,8 @@ extern void kvmppc_e500_tlb_load(struct kvm_vcpu *, int);
 extern int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *);
 extern void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *);
 extern void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *);
+extern unsigned int kvmppc_e500_get_sid(struct kvmppc_vcpu_e500 *vcpu_e500,
+					int as, int gid);
 
 /* TLB helper functions */
 static inline unsigned int get_tlb_size(const struct tlbe *tlbe)
-- 
1.5.4


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2009-04-27  6:58 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-27  6:58 [PATCH 2/3] kvm/e500: Map (vcpu,as,id) to core's shadow id Liu Yu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.