Received: by 2002:a05:6a10:22f:0:0:0:0 with SMTP id 15csp745734pxk; Wed, 23 Sep 2020 15:15:45 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwwWDLNZ5cxv4ApnQLnoYgqfStiWLojQJzaV+fFXQ2Xe9BdjM1xxB9dHk8zYvBXrF3dQa7B X-Received: by 2002:a17:906:f157:: with SMTP id gw23mr1669417ejb.325.1600899344904; Wed, 23 Sep 2020 15:15:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1600899344; cv=none; d=google.com; s=arc-20160816; b=j4anjn4Rv4/pHdrd7LdnU6++3rZxlqy9jL+gMry4wi6pJDZkF1yZFHweSUm8ixfkJE rEpy+0rKEtHU/DDcvN5ghWWWeNdS2Ap7j55L+8Zr6JX0BPPtsvvx6Ljnq9DGhTfGSayD 1ISsSRccjeQiarniXxrOix00rZLVnGnfZCYOfJ2fyd2gpw6fiXo1gTrfHRme8+cjx3Q6 tBgzr/P7CDzzxEwtUACWmN3wHveCevq/VPS73SVxqHVUemO0oTsT7yCC9eggDyYK+9Fu JUmpqFhnU0m/0WDLgVJYxAxYyBWNGR1bVS8flSTW9SjLEeaZZDkPmdolShk21cGStOrW ghXQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :ironport-sdr:ironport-sdr; bh=8wEovpNYNU+gNDG769/cLI2OD4V5/xqI2H+MI0VBvCE=; b=R1cFnKVOyIZCEAbdD9XLGxEr3d4yd0UvMVbfL6FknD3rxSDwerMIizUlCt1z5m4/rn x8l2g2DUu9afvKoIlmagZRoHFGUJlfYN203O8SVOv/DXyOEyxaj+wLASfYOVd1H96+jV PdsnRI+5/01AWzyk29dQ/akySM2ThnhrjJQ2kczD1YHdeEYjs1ryxBJ4MB5G1sZnYXP2 B/6V3/pBo/FUyfTT1lzpSLV7uV1J41+/xOqgz5018q1qpWZxAwP1ebS6sCJsTgPhDJ15 NdNdSIY4tTqZrAgH9yGoV5Tarf/EntdKEkYtJpg9rKTFc3sfNz+sPgtKfBYJXFX4Zi4R s5gw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id q26si750228ejz.749.2020.09.23.15.15.21; Wed, 23 Sep 2020 15:15:44 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726764AbgIWWOL (ORCPT + 99 others); Wed, 23 Sep 2020 18:14:11 -0400 Received: from mga11.intel.com ([192.55.52.93]:60899 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726720AbgIWWOI (ORCPT ); Wed, 23 Sep 2020 18:14:08 -0400 IronPort-SDR: 0co9eYTswt8X/tDM+w2E48lVzuuULjUblCdI+C2ms3L+xxfRvvn5MewU567R6RY0Hv7B33EnKV 44fBL5pfuRvw== X-IronPort-AV: E=McAfee;i="6000,8403,9753"; a="158382292" X-IronPort-AV: E=Sophos;i="5.77,295,1596524400"; d="scan'208";a="158382292" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2020 15:14:08 -0700 IronPort-SDR: wxEdB7Xmb7OXR+GL+bRbx6l0oESpRxYM6nMuQHUqHv/AnAUQ1WePj8uhtmn168SeujbWkz3id1 gDonET1q92EQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.77,295,1596524400"; d="scan'208";a="335651280" Received: from sjchrist-coffee.jf.intel.com ([10.54.74.160]) by fmsmga004.fm.intel.com with ESMTP; 23 Sep 2020 15:14:07 -0700 From: Sean Christopherson To: Paolo Bonzini Cc: Sean Christopherson , Vitaly Kuznetsov , Wanpeng Li , Jim Mattson , Joerg Roedel , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Peter Shier , Ben Gardon Subject: [PATCH v3 2/2] KVM: x86/MMU: Recursively zap nested TDP SPs when zapping last/only parent Date: Wed, 23 Sep 2020 15:14:06 -0700 Message-Id: <20200923221406.16297-3-sean.j.christopherson@intel.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200923221406.16297-1-sean.j.christopherson@intel.com> References: <20200923221406.16297-1-sean.j.christopherson@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Ben Gardon Recursively zap all to-be-orphaned children, unsynced or otherwise, when zapping a shadow page for a nested TDP MMU. KVM currently only zaps the unsynced child pages, but not the synced ones. This can create problems over time when running many nested guests because it leaves unlinked pages which will not be freed until the page quota is hit. With the default page quota of 20 shadow pages per 1000 guest pages, this looks like a memory leak and can degrade MMU performance. In a recent benchmark, substantial performance degradation was observed: An L1 guest was booted with 64G memory. 2G nested Windows guests were booted, 10 at a time for 20 iterations. (200 total boots) Windows was used in this benchmark because they touch all of their memory on startup. By the end of the benchmark, the nested guests were taking ~10% longer to boot. With this patch there is no degradation in boot time. Without this patch the benchmark ends with hundreds of thousands of stale EPT02 pages cluttering up rmaps and the page hash map. As a result, VM shutdown is also much slower: deleting memslot 0 was observed to take over a minute. With this patch it takes just a few miliseconds. Cc: Peter Shier Signed-off-by: Ben Gardon Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/mmu.c | 30 +++++++++++++++++++++++------- arch/x86/kvm/mmu/paging_tmpl.h | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index a91e8601594d..e993d5cd4bc8 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2615,8 +2615,9 @@ static void validate_direct_spte(struct kvm_vcpu *vcpu, u64 *sptep, } } -static void mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp, - u64 *spte) +/* Returns the number of zapped non-leaf child shadow pages. */ +static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp, + u64 *spte, struct list_head *invalid_list) { u64 pte; struct kvm_mmu_page *child; @@ -2630,19 +2631,34 @@ static void mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp, } else { child = to_shadow_page(pte & PT64_BASE_ADDR_MASK); drop_parent_pte(child, spte); + + /* + * Recursively zap nested TDP SPs, parentless SPs are + * unlikely to be used again in the near future. This + * avoids retaining a large number of stale nested SPs. + */ + if (tdp_enabled && invalid_list && + child->role.guest_mode && !child->parent_ptes.val) + return kvm_mmu_prepare_zap_page(kvm, child, + invalid_list); } } else if (is_mmio_spte(pte)) { mmu_spte_clear_no_track(spte); } + return 0; } -static void kvm_mmu_page_unlink_children(struct kvm *kvm, - struct kvm_mmu_page *sp) +static int kvm_mmu_page_unlink_children(struct kvm *kvm, + struct kvm_mmu_page *sp, + struct list_head *invalid_list) { + int zapped = 0; unsigned i; for (i = 0; i < PT64_ENT_PER_PAGE; ++i) - mmu_page_zap_pte(kvm, sp, sp->spt + i); + zapped += mmu_page_zap_pte(kvm, sp, sp->spt + i, invalid_list); + + return zapped; } static void kvm_mmu_unlink_parents(struct kvm *kvm, struct kvm_mmu_page *sp) @@ -2688,7 +2704,7 @@ static bool __kvm_mmu_prepare_zap_page(struct kvm *kvm, trace_kvm_mmu_prepare_zap_page(sp); ++kvm->stat.mmu_shadow_zapped; *nr_zapped = mmu_zap_unsync_children(kvm, sp, invalid_list); - kvm_mmu_page_unlink_children(kvm, sp); + *nr_zapped += kvm_mmu_page_unlink_children(kvm, sp, invalid_list); kvm_mmu_unlink_parents(kvm, sp); /* Zapping children means active_mmu_pages has become unstable. */ @@ -5396,7 +5412,7 @@ static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, u32 base_role = vcpu->arch.mmu->mmu_role.base.word; entry = *spte; - mmu_page_zap_pte(vcpu->kvm, sp, spte); + mmu_page_zap_pte(vcpu->kvm, sp, spte, NULL); if (gentry && !((sp->role.word ^ base_role) & ~role_ign.word) && rmap_can_add(vcpu)) diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 3bb624a3dda9..e1066226b8f0 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -929,7 +929,7 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva, hpa_t root_hpa) pte_gpa = FNAME(get_level1_sp_gpa)(sp); pte_gpa += (sptep - sp->spt) * sizeof(pt_element_t); - mmu_page_zap_pte(vcpu->kvm, sp, sptep); + mmu_page_zap_pte(vcpu->kvm, sp, sptep, NULL); if (is_shadow_present_pte(old_spte)) kvm_flush_remote_tlbs_with_address(vcpu->kvm, sp->gfn, KVM_PAGES_PER_HPAGE(sp->role.level)); -- 2.28.0