diff options
Diffstat (limited to 'arch/powerpc/kvm/book3s_hv.c')
| -rw-r--r-- | arch/powerpc/kvm/book3s_hv.c | 136 | 
1 files changed, 94 insertions, 42 deletions
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index c6af1d62383..83e929e66f9 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -56,7 +56,7 @@  /* #define EXIT_DEBUG_INT */  static void kvmppc_end_cede(struct kvm_vcpu *vcpu); -static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu); +static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu);  void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)  { @@ -268,24 +268,45 @@ static unsigned long do_h_register_vpa(struct kvm_vcpu *vcpu,  	return err;  } -static void kvmppc_update_vpa(struct kvm *kvm, struct kvmppc_vpa *vpap) +static void kvmppc_update_vpa(struct kvm_vcpu *vcpu, struct kvmppc_vpa *vpap)  { +	struct kvm *kvm = vcpu->kvm;  	void *va;  	unsigned long nb; +	unsigned long gpa; -	vpap->update_pending = 0; -	va = NULL; -	if (vpap->next_gpa) { -		va = kvmppc_pin_guest_page(kvm, vpap->next_gpa, &nb); -		if (nb < vpap->len) { -			/* -			 * If it's now too short, it must be that userspace -			 * has changed the mappings underlying guest memory, -			 * so unregister the region. -			 */ +	/* +	 * We need to pin the page pointed to by vpap->next_gpa, +	 * but we can't call kvmppc_pin_guest_page under the lock +	 * as it does get_user_pages() and down_read().  So we +	 * have to drop the lock, pin the page, then get the lock +	 * again and check that a new area didn't get registered +	 * in the meantime. +	 */ +	for (;;) { +		gpa = vpap->next_gpa; +		spin_unlock(&vcpu->arch.vpa_update_lock); +		va = NULL; +		nb = 0; +		if (gpa) +			va = kvmppc_pin_guest_page(kvm, vpap->next_gpa, &nb); +		spin_lock(&vcpu->arch.vpa_update_lock); +		if (gpa == vpap->next_gpa) +			break; +		/* sigh... unpin that one and try again */ +		if (va)  			kvmppc_unpin_guest_page(kvm, va); -			va = NULL; -		} +	} + +	vpap->update_pending = 0; +	if (va && nb < vpap->len) { +		/* +		 * If it's now too short, it must be that userspace +		 * has changed the mappings underlying guest memory, +		 * so unregister the region. +		 */ +		kvmppc_unpin_guest_page(kvm, va); +		va = NULL;  	}  	if (vpap->pinned_addr)  		kvmppc_unpin_guest_page(kvm, vpap->pinned_addr); @@ -296,20 +317,18 @@ static void kvmppc_update_vpa(struct kvm *kvm, struct kvmppc_vpa *vpap)  static void kvmppc_update_vpas(struct kvm_vcpu *vcpu)  { -	struct kvm *kvm = vcpu->kvm; -  	spin_lock(&vcpu->arch.vpa_update_lock);  	if (vcpu->arch.vpa.update_pending) { -		kvmppc_update_vpa(kvm, &vcpu->arch.vpa); +		kvmppc_update_vpa(vcpu, &vcpu->arch.vpa);  		init_vpa(vcpu, vcpu->arch.vpa.pinned_addr);  	}  	if (vcpu->arch.dtl.update_pending) { -		kvmppc_update_vpa(kvm, &vcpu->arch.dtl); +		kvmppc_update_vpa(vcpu, &vcpu->arch.dtl);  		vcpu->arch.dtl_ptr = vcpu->arch.dtl.pinned_addr;  		vcpu->arch.dtl_index = 0;  	}  	if (vcpu->arch.slb_shadow.update_pending) -		kvmppc_update_vpa(kvm, &vcpu->arch.slb_shadow); +		kvmppc_update_vpa(vcpu, &vcpu->arch.slb_shadow);  	spin_unlock(&vcpu->arch.vpa_update_lock);  } @@ -800,12 +819,39 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc)  	struct kvm_vcpu *vcpu, *vcpu0, *vnext;  	long ret;  	u64 now; -	int ptid, i; +	int ptid, i, need_vpa_update;  	/* don't start if any threads have a signal pending */ -	list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) +	need_vpa_update = 0; +	list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) {  		if (signal_pending(vcpu->arch.run_task))  			return 0; +		need_vpa_update |= vcpu->arch.vpa.update_pending | +			vcpu->arch.slb_shadow.update_pending | +			vcpu->arch.dtl.update_pending; +	} + +	/* +	 * Initialize *vc, in particular vc->vcore_state, so we can +	 * drop the vcore lock if necessary. +	 */ +	vc->n_woken = 0; +	vc->nap_count = 0; +	vc->entry_exit_count = 0; +	vc->vcore_state = VCORE_RUNNING; +	vc->in_guest = 0; +	vc->napping_threads = 0; + +	/* +	 * Updating any of the vpas requires calling kvmppc_pin_guest_page, +	 * which can't be called with any spinlocks held. +	 */ +	if (need_vpa_update) { +		spin_unlock(&vc->lock); +		list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) +			kvmppc_update_vpas(vcpu); +		spin_lock(&vc->lock); +	}  	/*  	 * Make sure we are running on thread 0, and that @@ -838,20 +884,10 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc)  		if (vcpu->arch.ceded)  			vcpu->arch.ptid = ptid++; -	vc->n_woken = 0; -	vc->nap_count = 0; -	vc->entry_exit_count = 0; -	vc->vcore_state = VCORE_RUNNING;  	vc->stolen_tb += mftb() - vc->preempt_tb; -	vc->in_guest = 0;  	vc->pcpu = smp_processor_id(); -	vc->napping_threads = 0;  	list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) {  		kvmppc_start_thread(vcpu); -		if (vcpu->arch.vpa.update_pending || -		    vcpu->arch.slb_shadow.update_pending || -		    vcpu->arch.dtl.update_pending) -			kvmppc_update_vpas(vcpu);  		kvmppc_create_dtl_entry(vcpu, vc);  	}  	/* Grab any remaining hw threads so they can't go into the kernel */ @@ -1068,11 +1104,15 @@ int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu)  		return -EINTR;  	} -	/* On the first time here, set up VRMA or RMA */ +	atomic_inc(&vcpu->kvm->arch.vcpus_running); +	/* Order vcpus_running vs. rma_setup_done, see kvmppc_alloc_reset_hpt */ +	smp_mb(); + +	/* On the first time here, set up HTAB and VRMA or RMA */  	if (!vcpu->kvm->arch.rma_setup_done) { -		r = kvmppc_hv_setup_rma(vcpu); +		r = kvmppc_hv_setup_htab_rma(vcpu);  		if (r) -			return r; +			goto out;  	}  	flush_fp_to_thread(current); @@ -1090,6 +1130,9 @@ int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu)  			kvmppc_core_prepare_to_enter(vcpu);  		}  	} while (r == RESUME_GUEST); + + out: +	atomic_dec(&vcpu->kvm->arch.vcpus_running);  	return r;  } @@ -1305,7 +1348,7 @@ void kvmppc_core_commit_memory_region(struct kvm *kvm,  {  } -static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu) +static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)  {  	int err = 0;  	struct kvm *kvm = vcpu->kvm; @@ -1324,6 +1367,15 @@ static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu)  	if (kvm->arch.rma_setup_done)  		goto out;	/* another vcpu beat us to it */ +	/* Allocate hashed page table (if not done already) and reset it */ +	if (!kvm->arch.hpt_virt) { +		err = kvmppc_alloc_hpt(kvm, NULL); +		if (err) { +			pr_err("KVM: Couldn't alloc HPT\n"); +			goto out; +		} +	} +  	/* Look up the memslot for guest physical address 0 */  	memslot = gfn_to_memslot(kvm, 0); @@ -1435,13 +1487,14 @@ static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu)  int kvmppc_core_init_vm(struct kvm *kvm)  { -	long r; -	unsigned long lpcr; +	unsigned long lpcr, lpid; -	/* Allocate hashed page table */ -	r = kvmppc_alloc_hpt(kvm); -	if (r) -		return r; +	/* Allocate the guest's logical partition ID */ + +	lpid = kvmppc_alloc_lpid(); +	if (lpid < 0) +		return -ENOMEM; +	kvm->arch.lpid = lpid;  	INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables); @@ -1451,7 +1504,6 @@ int kvmppc_core_init_vm(struct kvm *kvm)  	if (cpu_has_feature(CPU_FTR_ARCH_201)) {  		/* PPC970; HID4 is effectively the LPCR */ -		unsigned long lpid = kvm->arch.lpid;  		kvm->arch.host_lpid = 0;  		kvm->arch.host_lpcr = lpcr = mfspr(SPRN_HID4);  		lpcr &= ~((3 << HID4_LPID1_SH) | (0xful << HID4_LPID5_SH));  |