From patchwork Fri Feb 17 04:12:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yu Zhao X-Patchwork-Id: 13144324 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 9FEE9C636D4 for ; Fri, 17 Feb 2023 04:14:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Cc:To:From:Subject:References: Mime-Version:Message-Id:In-Reply-To:Date:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=9R7NJGJrIphpdGeEFRDADID8AzYArMdqiOlNnimJrCE=; b=rA2gMtsmQzFV2lbgSEFuY0/Tjm McjBd+iIVsg6oMgaxPfVaVXnDMI9gKQAVj0u0pXxZrxNOXH2t+o6F6Zk6oPoP8Dh6wueoo56yyUqA BLs7pt56STQKt7myqQiWicyaXoUMC00QwoAfk7n085yyJMwzG/oC/+CwBGBdmedBg34Iv9JuqWBAc nYmsyKQuyt49/FROpzKs+P+26DQl8eHZGZ+OT+OqQAJeV8oIm+VGyPr/PVMibIWmAVzBv1fxdMFmF 68TLjM8VPJddOITSMaS2D4QY+BnEMvvZTLTeZhH4P0Q1iutol3fM+jQKdJMoBjMD1blmd9M0+zYVc 2WPJL2Zw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1pSs7b-00CaIn-P4; Fri, 17 Feb 2023 04:13:19 +0000 Received: from mail-yw1-x114a.google.com ([2607:f8b0:4864:20::114a]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1pSs6z-00CZu5-L0 for linux-arm-kernel@lists.infradead.org; Fri, 17 Feb 2023 04:12:43 +0000 Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-52a8f97c8ccso44809177b3.6 for ; Thu, 16 Feb 2023 20:12:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:references:mime-version:message-id:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=zOMC30Dt5jTWJdMwqPugeizmTtOBtAcC7CRKjtqAf8M=; b=bm43kjVqsx1c7qHOsFFVJ0+0xrXUHmX/V9lp20jfJ1X7N4zZd4d3vyNBymGX/me9gK nTebRJhW2oIy2yjncVC9wMVFjstG9iWFcB6e1y3NE5fXCSioxKMMujXyRRWA09QDaWuI 5NHp8SGOOrBADfd/tJIWc3jugNV6pCNLo8yDtwEDkvM75+pJzBnfO9BcBe5GRXiC6M6C BsxIBG/O68HSI1OC0bY7F+eEOjy5Z383u8SWnJZPB3cOWNgoqbleGxYTNZYvv1mrxews ibrqxX3GI/du8KUrofan0Xz4K1l4wKn3rqZtCcgiQArjBpayUlYXpRK7dgISyESZns/1 kB0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:references:mime-version:message-id:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=zOMC30Dt5jTWJdMwqPugeizmTtOBtAcC7CRKjtqAf8M=; b=1wf7K4kXVjNJD/zd8ISSf+816b31teJNlKnCJDheDB1yVYKERJPohUChQboKy1cmNG TDGNVxKjVFAMTTyB1O/9p4OcUoWVrDWX6JrKRsnJl6Phhsmqkf3Hjn6AcMIscHVMRENJ uU4wj3EykEvAKtACG+0gp1jn8oTSJYFIhWB/fd4WCTC430hnLF8LLK5VoRRrxA/xvCHQ XARueNVwlJpJznEbBIDARPSScdl80Gj54oqz3nzVR5ZFICXNTN5pjcndMwTrkbSIH/rf Plvmgn+ecPVQ4YoX9Takz3p1hicOiODL70qGN7HPqtuWLL6q7kaFa+3FvchxVAAYFRxI xkug== X-Gm-Message-State: AO0yUKV5ATHIMPvjPdZpDM775D5tWCK7Lydm/jmPPr33CL0yLTvQ5Vuf DAXQSt9+q2i9kJziV1QsNzG/HvXaJQQ= X-Google-Smtp-Source: AK7set9YiUb7IHS5x9zvQDFrhuacJgLt4KpkKYJiB428hgjDG7WBXSnP1azODlrvhkhiovQ/6TgBQWA2SqA= X-Received: from yuzhao.bld.corp.google.com ([2620:15c:183:200:6fb3:61e:d31f:1ad3]) (user=yuzhao job=sendgmr) by 2002:a5b:786:0:b0:92c:23ba:7adb with SMTP id b6-20020a5b0786000000b0092c23ba7adbmr836225ybq.545.1676607158852; Thu, 16 Feb 2023 20:12:38 -0800 (PST) Date: Thu, 16 Feb 2023 21:12:27 -0700 In-Reply-To: <20230217041230.2417228-1-yuzhao@google.com> Message-Id: <20230217041230.2417228-3-yuzhao@google.com> Mime-Version: 1.0 References: <20230217041230.2417228-1-yuzhao@google.com> X-Mailer: git-send-email 2.39.2.637.g21b0678d19-goog Subject: [PATCH mm-unstable v1 2/5] kvm/x86: add kvm_arch_test_clear_young() From: Yu Zhao To: Andrew Morton , Paolo Bonzini Cc: Jonathan Corbet , Michael Larabel , kvmarm@lists.linux.dev, kvm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linuxppc-dev@lists.ozlabs.org, x86@kernel.org, linux-mm@google.com, Yu Zhao X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230216_201241_715369_E8D94DB1 X-CRM114-Status: GOOD ( 19.21 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org This patch adds kvm_arch_test_clear_young() for the vast majority of VMs that are not nested and run on hardware that sets the accessed bit in TDP MMU page tables. It relies on two techniques, RCU and cmpxchg, to safely test and clear the accessed bit without taking the MMU lock. The former protects KVM page tables from being freed while the latter clears the accessed bit atomically against both the hardware and other software page table walkers. Signed-off-by: Yu Zhao --- arch/x86/include/asm/kvm_host.h | 27 ++++++++++++++++++++++ arch/x86/kvm/mmu/spte.h | 12 ---------- arch/x86/kvm/mmu/tdp_mmu.c | 41 +++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 6aaae18f1854..d2995c9e8f07 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1367,6 +1367,12 @@ struct kvm_arch { * the MMU lock in read mode + the tdp_mmu_pages_lock or * the MMU lock in write mode * + * kvm_arch_test_clear_young() is a special case. It relies on two + * techniques, RCU and cmpxchg, to safely test and clear the accessed + * bit without taking the MMU lock. The former protects KVM page tables + * from being freed while the latter clears the accessed bit atomically + * against both the hardware and other software page table walkers. + * * Roots will remain in the list until their tdp_mmu_root_count * drops to zero, at which point the thread that decremented the * count to zero should removed the root from the list and clean @@ -2171,4 +2177,25 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages); KVM_X86_QUIRK_FIX_HYPERCALL_INSN | \ KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS) +extern u64 __read_mostly shadow_accessed_mask; + +/* + * Returns true if A/D bits are supported in hardware and are enabled by KVM. + * When enabled, KVM uses A/D bits for all non-nested MMUs. Because L1 can + * disable A/D bits in EPTP12, SP and SPTE variants are needed to handle the + * scenario where KVM is using A/D bits for L1, but not L2. + */ +static inline bool kvm_ad_enabled(void) +{ + return shadow_accessed_mask; +} + +/* see the comments on the generic kvm_arch_has_test_clear_young() */ +#define kvm_arch_has_test_clear_young kvm_arch_has_test_clear_young +static inline bool kvm_arch_has_test_clear_young(void) +{ + return IS_ENABLED(CONFIG_KVM) && IS_ENABLED(CONFIG_X86_64) && + (!IS_REACHABLE(CONFIG_KVM) || (kvm_ad_enabled() && tdp_enabled)); +} + #endif /* _ASM_X86_KVM_HOST_H */ diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 6f54dc9409c9..0dc7fed1f3fd 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -153,7 +153,6 @@ extern u64 __read_mostly shadow_mmu_writable_mask; extern u64 __read_mostly shadow_nx_mask; extern u64 __read_mostly shadow_x_mask; /* mutual exclusive with nx_mask */ extern u64 __read_mostly shadow_user_mask; -extern u64 __read_mostly shadow_accessed_mask; extern u64 __read_mostly shadow_dirty_mask; extern u64 __read_mostly shadow_mmio_value; extern u64 __read_mostly shadow_mmio_mask; @@ -247,17 +246,6 @@ static inline bool is_shadow_present_pte(u64 pte) return !!(pte & SPTE_MMU_PRESENT_MASK); } -/* - * Returns true if A/D bits are supported in hardware and are enabled by KVM. - * When enabled, KVM uses A/D bits for all non-nested MMUs. Because L1 can - * disable A/D bits in EPTP12, SP and SPTE variants are needed to handle the - * scenario where KVM is using A/D bits for L1, but not L2. - */ -static inline bool kvm_ad_enabled(void) -{ - return !!shadow_accessed_mask; -} - static inline bool sp_ad_disabled(struct kvm_mmu_page *sp) { return sp->role.ad_disabled; diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index d6df38d371a0..9028e09f1aab 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -1309,6 +1309,47 @@ bool kvm_tdp_mmu_age_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) return kvm_tdp_mmu_handle_gfn(kvm, range, age_gfn_range); } +bool kvm_arch_test_clear_young(struct kvm *kvm, struct kvm_gfn_range *range, + gfn_t lsb_gfn, unsigned long *bitmap) +{ + struct kvm_mmu_page *root; + + if (WARN_ON_ONCE(!kvm_arch_has_test_clear_young())) + return false; + + if (kvm_memslots_have_rmaps(kvm)) + return false; + + /* see the comments on kvm_arch->tdp_mmu_roots */ + rcu_read_lock(); + + list_for_each_entry_rcu(root, &kvm->arch.tdp_mmu_roots, link) { + struct tdp_iter iter; + + if (kvm_mmu_page_as_id(root) != range->slot->as_id) + continue; + + tdp_root_for_each_leaf_pte(iter, root, range->start, range->end) { + u64 *sptep = rcu_dereference(iter.sptep); + u64 new_spte = iter.old_spte & ~shadow_accessed_mask; + + VM_WARN_ON_ONCE(!page_count(virt_to_page(sptep))); + VM_WARN_ON_ONCE(iter.gfn < range->start || iter.gfn >= range->end); + + if (new_spte == iter.old_spte) + continue; + + /* see the comments on the generic kvm_arch_has_test_clear_young() */ + if (__test_and_change_bit(lsb_gfn - iter.gfn, bitmap)) + cmpxchg64(sptep, iter.old_spte, new_spte); + } + } + + rcu_read_unlock(); + + return true; +} + static bool test_age_gfn(struct kvm *kvm, struct tdp_iter *iter, struct kvm_gfn_range *range) {