From patchwork Fri Mar 29 03:44:56 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Honig X-Patchwork-Id: 2364051 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 01CE83FD8C for ; Fri, 29 Mar 2013 03:45:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754750Ab3C2DpD (ORCPT ); Thu, 28 Mar 2013 23:45:03 -0400 Received: from mail-fa0-f74.google.com ([209.85.161.74]:54564 "EHLO mail-fa0-f74.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753800Ab3C2DpB (ORCPT ); Thu, 28 Mar 2013 23:45:01 -0400 Received: by mail-fa0-f74.google.com with SMTP id p1so10082fap.5 for ; Thu, 28 Mar 2013 20:44:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:date:from:x-x-sender:to:cc:subject:message-id:user-agent :mime-version:content-type; bh=Bw9cYPdCGwy5O7XcjItFLEoKLM8uTS8vEhA86unmg60=; b=PHHMql/Pm2YGMsBTtOhFq8qB6B8/j+GZnP3xa+MC+H1rgQORTIxoJ+UTlqxl8LyOi3 rOdEeZ6FinpMKldWyzVl+w/3tvsOzyojzF1JBm6KCgmjKD520eMsgWAClEvnqILBqLCx yAGcyTqI00qGeZJAIb5/ff3clUF97KOKCuii8zMhDp7QsNiSIQPEPpqyjTMlp79ZHG+O kwKAjzzC8hEB+TcWgczR4JRASSt78Kolb2Jt3PLasueFRMlxjvxmg4m5b/y2t0GCY4VG FR2A64dftza7PR83sd2kQnAuh6l1H6y60DeqErLJtRz7GLZRFT/74glBVbh6ISi/NYJM 3yZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:date:from:x-x-sender:to:cc:subject:message-id:user-agent :mime-version:content-type:x-gm-message-state; bh=Bw9cYPdCGwy5O7XcjItFLEoKLM8uTS8vEhA86unmg60=; b=ZdfNtCoNgf/bQGZOmJlciOSWrtKWNzAXOejbqPd/uxCUOnfGIFxwg6RRhTfObvEtwj 9/8GBCXimtcuV+5FtFzJD9H4lplWYKEAl2mA8MZMbdml2gH3CUrLj3+TlNwf7gF0aoBP lWaPkiJdo264SZJ+LPwsCznUCD6kym6TA87n7gs3TBFF0/wBDnwD/VUI4FrVWQsTlQrP U22ILKhax6MI1pWnY1nDOTZD8Th2uCFTCn/P2WSKyf0SjYcAR+yu/edQuTd7T/xUwsKj 8VQMyjmfPwAQRs8eQ//yiSKcKjwt3UBARiO1c9onWAk03xMvKvvY6BT6ILKyEnQ9xt3s Qgaw== X-Received: by 10.14.215.69 with SMTP id d45mr1642075eep.6.1364528699458; Thu, 28 Mar 2013 20:44:59 -0700 (PDT) Received: from corp2gmr1-1.eem.corp.google.com (corp2gmr1-1.eem.corp.google.com [172.25.138.99]) by gmr-mx.google.com with ESMTPS id 6si300521eej.0.2013.03.28.20.44.59 (version=TLSv1.1 cipher=AES128-SHA bits=128/128); Thu, 28 Mar 2013 20:44:59 -0700 (PDT) Received: from dhcp-192-168-61-161.mtv.corp.google.com (unknown [172.19.242.222]) by corp2gmr1-1.eem.corp.google.com (Postfix) with ESMTPS id 8D2091CA15E; Thu, 28 Mar 2013 20:44:58 -0700 (PDT) Date: Thu, 28 Mar 2013 20:44:56 -0700 (PDT) From: Andrew Honig X-X-Sender: ahonig@ahonig-virtual-machine To: kvm@vger.kernel.org cc: ahonig@google.com Subject: [PATCH] KVM: Allow cross page reads and writes from cached translations Message-ID: User-Agent: Alpine 2.02 (DEB 1266 2009-07-14) MIME-Version: 1.0 X-Gm-Message-State: ALoCoQkha6Y+lpMh83G2StX9xNgo9P1eEJDzW319swKNHX5NhunEP+zx0S4vytmOh6rQbHl9NBzjxoKzTwhyH/hnkLTTRRhJPduR4Lh7nZQUxsbzZf1jjrA4vgLdLkajY1zVDogkfn89sj1j2ysn3yIv2uFhOMBdDSxXmEBVXA769vTsBj4FLCh6U0IONFRCq3DnOV3XxWTD Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This patch adds support for kvm_gfn_to_hva_cache_init functions for reads and writes that will cross a page. If the range falls within the same memslot, then this will be a fast operation. If the range is split between two memslots, then the slower kvm_read_guest and kvm_write_guest are used. Tested: Test against kvm_clock unit tests. Signed-off-by: Andrew Honig --- arch/x86/kvm/lapic.c | 2 +- arch/x86/kvm/x86.c | 12 +++++------- include/linux/kvm_host.h | 2 +- include/linux/kvm_types.h | 2 ++ virt/kvm/kvm_main.c | 43 +++++++++++++++++++++++++++++++++---------- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 02b51dd..f77df1c 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1857,7 +1857,7 @@ int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data) if (!pv_eoi_enabled(vcpu)) return 0; return kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.pv_eoi.data, - addr); + addr, sizeof(u8)); } void kvm_lapic_init(void) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f19ac0a..0fd5ad8 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1823,7 +1823,7 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) return 0; } - if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa)) + if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa, 4)) return 1; vcpu->arch.apf.send_user_only = !(data & KVM_ASYNC_PF_SEND_ALWAYS); @@ -1952,12 +1952,9 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) gpa_offset = data & ~(PAGE_MASK | 1); - /* Check that the address is 32-byte aligned. */ - if (gpa_offset & (sizeof(struct pvclock_vcpu_time_info) - 1)) - break; - if (kvm_gfn_to_hva_cache_init(vcpu->kvm, - &vcpu->arch.pv_time, data & ~1ULL)) + &vcpu->arch.pv_time, data & ~1ULL, + sizeof(struct pvclock_vcpu_time_info))) vcpu->arch.pv_time_enabled = false; else vcpu->arch.pv_time_enabled = true; @@ -1977,7 +1974,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.st.stime, - data & KVM_STEAL_VALID_BITS)) + data & KVM_STEAL_VALID_BITS, + sizeof(struct kvm_steal_time))) return 1; vcpu->arch.st.msr_val = data; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index cad77fe..c139582 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -518,7 +518,7 @@ int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, void *data, unsigned long len); int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, - gpa_t gpa); + gpa_t gpa, unsigned long len); int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len); int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len); struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h index fa7cc72..bd420d6 100644 --- a/include/linux/kvm_types.h +++ b/include/linux/kvm_types.h @@ -71,6 +71,8 @@ struct gfn_to_hva_cache { u64 generation; gpa_t gpa; unsigned long hva; + unsigned long len; + int slow; struct kvm_memory_slot *memslot; }; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index adc68fe..dcd3e57 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1541,21 +1541,38 @@ int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, } int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, - gpa_t gpa) + gpa_t gpa, unsigned long len) { struct kvm_memslots *slots = kvm_memslots(kvm); int offset = offset_in_page(gpa); - gfn_t gfn = gpa >> PAGE_SHIFT; + gfn_t start_gfn = gpa >> PAGE_SHIFT; + gfn_t end_gfn = (gpa + len - 1) >> PAGE_SHIFT; + gfn_t nr_pages_needed = end_gfn - start_gfn + 1; + gfn_t nr_pages_avail; ghc->gpa = gpa; + ghc->slow = 0; ghc->generation = slots->generation; - ghc->memslot = gfn_to_memslot(kvm, gfn); - ghc->hva = gfn_to_hva_many(ghc->memslot, gfn, NULL); - if (!kvm_is_error_hva(ghc->hva)) + ghc->len = len; + ghc->memslot = gfn_to_memslot(kvm, start_gfn); + ghc->hva = gfn_to_hva_many(ghc->memslot, start_gfn, &nr_pages_avail); + if (!kvm_is_error_hva(ghc->hva) && nr_pages_avail >= nr_pages_needed) { ghc->hva += offset; - else - return -EFAULT; - + } else { + ghc->slow = 1; + /* + * If the requested region crosses two memslots, we still + * verify that the entire region is valid here. + */ + while (start_gfn <= end_gfn) { + ghc->memslot = gfn_to_memslot(kvm, start_gfn); + ghc->hva = gfn_to_hva_many(ghc->memslot, start_gfn, + &nr_pages_avail); + if (kvm_is_error_hva(ghc->hva)) + return -EFAULT; + start_gfn += nr_pages_avail; + } + } return 0; } EXPORT_SYMBOL_GPL(kvm_gfn_to_hva_cache_init); @@ -1567,7 +1584,10 @@ int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, int r; if (slots->generation != ghc->generation) - kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa); + kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa, ghc->len); + + if (unlikely(ghc->slow)) + return kvm_write_guest(kvm, ghc->gpa, data, len); if (kvm_is_error_hva(ghc->hva)) return -EFAULT; @@ -1588,7 +1608,10 @@ int kvm_read_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, int r; if (slots->generation != ghc->generation) - kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa); + kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa, ghc->len); + + if (unlikely(ghc->slow)) + return kvm_read_guest(kvm, ghc->gpa, data, len); if (kvm_is_error_hva(ghc->hva)) return -EFAULT;