From patchwork Thu Sep 21 20:14:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Isaku Yamahata X-Patchwork-Id: 13394561 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DBA30E7D0AA for ; Thu, 21 Sep 2023 20:41:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232037AbjIUUlP (ORCPT ); Thu, 21 Sep 2023 16:41:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46982 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232226AbjIUUlE (ORCPT ); Thu, 21 Sep 2023 16:41:04 -0400 Received: from mgamail.intel.com (mgamail.intel.com [134.134.136.24]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A9A775B422; Thu, 21 Sep 2023 13:14:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695327287; x=1726863287; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=LZO5yjdBhkI2eq5MmnoYAkLMHFYrLWe3TrIyqhKkZPU=; b=VIaZlZxCI1JQuQnDmTFFVbkdoT8u44sVkbHmvQJ8KgQBTIfAcrRsWts+ sv0b8WMqv/QmWZWSeID/QEL1b3TdATpnP6ccQERE5oUeV5xNyRuykX2VQ NHbEYCbr3nnyy4Xo/yYkDoNx/wHUHgEE8NQyIJ6uXRS4ch4ptscYql9te ADPmcTXxHJYtsXr7/OwLheGVdLFezRhxdiznRgpqFH+mKhuRaLtKNaFjF IRiYz2mV7oxrZywnz3O4MGqlWFXzRf2GPb0Df0nUiJV5up98zVbsfq1uQ nfNYDgIshJJ3/9GQgjG6KC6UioQB6efvScUXlt2ke8TTiySpPrW5jVMjj Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="383401590" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="383401590" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:46 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="696897783" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="696897783" Received: from ls.sc.intel.com (HELO localhost) ([172.25.112.31]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:45 -0700 From: isaku.yamahata@intel.com To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: isaku.yamahata@intel.com, isaku.yamahata@gmail.com, Michael Roth , Paolo Bonzini , Sean Christopherson , erdemaktas@google.com, Sagi Shahar , David Matlack , Kai Huang , Zhi Wang , chen.bo@intel.com, linux-coco@lists.linux.dev, Chao Peng , Ackerley Tng , Vishal Annapurve , Yuan Yao , Jarkko Sakkinen , Xu Yilun , Quentin Perret , wei.w.wang@intel.com, Fuad Tabba Subject: [RFC PATCH v2 1/6] KVM: gmem: Truncate pages on punch hole Date: Thu, 21 Sep 2023 13:14:34 -0700 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Isaku Yamahata Although kvm_gmem_punch_hole() keeps all pages in mapping on punching hole, it's common expectation that pages are truncated. Truncate pages on punching hole. As page contents can be encrypted, avoid zeroing partial folio by refusing partial punch hole. Signed-off-by: Isaku Yamahata --- virt/kvm/guest_mem.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/virt/kvm/guest_mem.c b/virt/kvm/guest_mem.c index a819367434e9..01fb4ca861d0 100644 --- a/virt/kvm/guest_mem.c +++ b/virt/kvm/guest_mem.c @@ -130,22 +130,32 @@ static void kvm_gmem_invalidate_end(struct kvm_gmem *gmem, pgoff_t start, static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len) { struct list_head *gmem_list = &inode->i_mapping->private_list; + struct address_space *mapping = inode->i_mapping; pgoff_t start = offset >> PAGE_SHIFT; pgoff_t end = (offset + len) >> PAGE_SHIFT; struct kvm_gmem *gmem; + /* + * punch hole may result in zeroing partial area. As pages can be + * encrypted, prohibit zeroing partial area. + */ + if (offset & ~PAGE_MASK || len & ~PAGE_MASK) + return -EINVAL; + /* * Bindings must stable across invalidation to ensure the start+end * are balanced. */ - filemap_invalidate_lock(inode->i_mapping); + filemap_invalidate_lock(mapping); list_for_each_entry(gmem, gmem_list, entry) { kvm_gmem_invalidate_begin(gmem, start, end); kvm_gmem_invalidate_end(gmem, start, end); } - filemap_invalidate_unlock(inode->i_mapping); + truncate_inode_pages_range(mapping, offset, offset + len - 1); + + filemap_invalidate_unlock(mapping); return 0; } From patchwork Thu Sep 21 20:14:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Isaku Yamahata X-Patchwork-Id: 13394562 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7F9E6E7D0A2 for ; Thu, 21 Sep 2023 20:41:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231893AbjIUUlS (ORCPT ); Thu, 21 Sep 2023 16:41:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40250 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232245AbjIUUlF (ORCPT ); Thu, 21 Sep 2023 16:41:05 -0400 Received: from mgamail.intel.com (mgamail.intel.com [134.134.136.24]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1E793199B; Thu, 21 Sep 2023 13:14:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695327289; x=1726863289; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=j8I9xEhVY8G4DBx7Fus59ORWF9OXOGvs193SinT1foI=; b=LwjCAnJ137rH5XRwhcftBz4jCl1S0nkY7prTsUCfMFCTDsRuhLhxu3Ku DJ1uKoTsBixOBKYKhKe3vkT5bWzroUTahGx+95DH/Pw02++eqAdqelvTR 9Uu7uYG7z6O+HQ6UZFYp2pJXPx6Stw8oXaQ+l06W61Fz8uSatYEEkvXcr cgRsuFjxkVpMcL3Iz4k8DU0EkYUbQL18PMOD9i+bN3JemwMyGGUQzEvpt OSZNyHnUMe8Kf3smRMduiqvXdxAutPq9mDzVksCcQFxo9J3stsh/YmDD1 SJmRDTk1rbDExScqTuhP9FrB1t3kTUdjBOoaZG+5QpyZ4FS03MPxV2LZo Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="383401598" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="383401598" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:46 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="696897787" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="696897787" Received: from ls.sc.intel.com (HELO localhost) ([172.25.112.31]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:46 -0700 From: isaku.yamahata@intel.com To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: isaku.yamahata@intel.com, isaku.yamahata@gmail.com, Michael Roth , Paolo Bonzini , Sean Christopherson , erdemaktas@google.com, Sagi Shahar , David Matlack , Kai Huang , Zhi Wang , chen.bo@intel.com, linux-coco@lists.linux.dev, Chao Peng , Ackerley Tng , Vishal Annapurve , Yuan Yao , Jarkko Sakkinen , Xu Yilun , Quentin Perret , wei.w.wang@intel.com, Fuad Tabba Subject: [RFC PATCH v2 2/6] KVM: selftests: Add negative test cases for punch hole for guest_memfd() Date: Thu, 21 Sep 2023 13:14:35 -0700 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Isaku Yamahata Add test cases to check for punch hole of guest_memfd to reject unaligned offset or size. Signed-off-by: Isaku Yamahata --- .../testing/selftests/kvm/guest_memfd_test.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c index 75073645aaa1..d5b4bfcdc3fe 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -91,6 +91,37 @@ static void test_fallocate(int fd, size_t page_size, size_t total_size) TEST_ASSERT(!ret, "fallocate to restore punched hole should succeed"); } +/* Negative tests */ +static void test_fallocate_fail(int fd, size_t page_size, size_t total_size) +{ + struct { + off_t offset; + off_t len; + } cases[] = { + {0, 1}, + {0, page_size - 1}, + {0, page_size + 1}, + + {1, 1}, + {1, page_size - 1}, + {1, page_size}, + {1, page_size + 1}, + + {page_size, 1}, + {page_size, page_size - 1}, + {page_size, page_size + 1}, + }; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(cases); i++) { + ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, + cases[i].offset, cases[i].len); + TEST_ASSERT(ret == -1, + "fallocate(PUNCH_HOLE) with unaligned offset and/or size should fail"); + } +} + static void test_create_guest_memfd_invalid(struct kvm_vm *vm) { uint64_t valid_flags = 0; @@ -160,6 +191,7 @@ int main(int argc, char *argv[]) test_mmap(fd, page_size); test_file_size(fd, page_size, total_size); test_fallocate(fd, page_size, total_size); + test_fallocate_fail(fd, page_size, total_size); close(fd); } From patchwork Thu Sep 21 20:14:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Isaku Yamahata X-Patchwork-Id: 13394564 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EBF99E7D0AA for ; Thu, 21 Sep 2023 20:41:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232373AbjIUUls (ORCPT ); Thu, 21 Sep 2023 16:41:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46682 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230445AbjIUUl3 (ORCPT ); Thu, 21 Sep 2023 16:41:29 -0400 Received: from mgamail.intel.com (mgamail.intel.com [134.134.136.24]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id ACCAC2D62; Thu, 21 Sep 2023 13:15:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695327315; x=1726863315; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=RtfNaKFCK9P/yxwP8BTsIwYqXtE9p2zlPuJXOdTAut0=; b=e4DH9lqW+/cctTriOc12YlCVQdbn4hcdR/U7va03BA2QBGCLmG3E/2uS gJfsv8Xyi6tJrrG+3slupzU5HtRm10N/yNNTz6QBGEG02hQN9hhLklTsV SSDbJu+4yWfgeTSEPgcrL25mflHF6+6m4pWjEB1Bq4qHXhVMhvZRoc4Xk wK26qv2Zno4TXNYHN3nk4yI5WCr8Z1EXZcz2BkCnPe5fE9RwcHQBZNEch YTE+zZeDoDO/819MR4u+L6W4QWe1RO/rNUMPokyO9JUTYDQItmPlr9hq+ 1qtWjYX+9Gga359OC6ECKDKD8QNuVLjg/dICN4hv44X/bdk6GhHKDuP5s w==; X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="383401616" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="383401616" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:48 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="696897795" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="696897795" Received: from ls.sc.intel.com (HELO localhost) ([172.25.112.31]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:47 -0700 From: isaku.yamahata@intel.com To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: isaku.yamahata@intel.com, isaku.yamahata@gmail.com, Michael Roth , Paolo Bonzini , Sean Christopherson , erdemaktas@google.com, Sagi Shahar , David Matlack , Kai Huang , Zhi Wang , chen.bo@intel.com, linux-coco@lists.linux.dev, Chao Peng , Ackerley Tng , Vishal Annapurve , Yuan Yao , Jarkko Sakkinen , Xu Yilun , Quentin Perret , wei.w.wang@intel.com, Fuad Tabba Subject: [RFC PATCH v2 3/6] KVM: selftests: Add tests for punch hole on guest_memfd Date: Thu, 21 Sep 2023 13:14:36 -0700 Message-Id: <26822c313754e03b2c393e6fdefe495f117bbfff.1695327124.git.isaku.yamahata@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Isaku Yamahata Punch hole implies the region is zeroed out. Add tests if the punched region has zero. Oppertunistically Remove unused member, pattern, in guest_run_test(). Signed-off-by: Isaku Yamahata --- .../kvm/x86_64/private_mem_conversions_test.c | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/kvm/x86_64/private_mem_conversions_test.c b/tools/testing/selftests/kvm/x86_64/private_mem_conversions_test.c index 50541246d6fd..c05c725645af 100644 --- a/tools/testing/selftests/kvm/x86_64/private_mem_conversions_test.c +++ b/tools/testing/selftests/kvm/x86_64/private_mem_conversions_test.c @@ -85,9 +85,10 @@ static void guest_sync_private(uint64_t gpa, uint64_t size, uint8_t pattern) /* Arbitrary values, KVM doesn't care about the attribute flags. */ #define MAP_GPA_SHARED BIT(0) #define MAP_GPA_DO_FALLOCATE BIT(1) +#define MAP_GPA_FALLOCATE_ONLY BIT(2) static void guest_map_mem(uint64_t gpa, uint64_t size, bool map_shared, - bool do_fallocate) + bool do_fallocate, bool fallocate_only) { uint64_t flags = 0; @@ -95,17 +96,24 @@ static void guest_map_mem(uint64_t gpa, uint64_t size, bool map_shared, flags |= MAP_GPA_SHARED; if (do_fallocate) flags |= MAP_GPA_DO_FALLOCATE; + if (fallocate_only) + flags |= MAP_GPA_FALLOCATE_ONLY; kvm_hypercall_map_gpa_range(gpa, size, flags); } static void guest_map_shared(uint64_t gpa, uint64_t size, bool do_fallocate) { - guest_map_mem(gpa, size, true, do_fallocate); + guest_map_mem(gpa, size, true, do_fallocate, false); } static void guest_map_private(uint64_t gpa, uint64_t size, bool do_fallocate) { - guest_map_mem(gpa, size, false, do_fallocate); + guest_map_mem(gpa, size, false, do_fallocate, false); +} + +static void guest_punch_hole_private(uint64_t gpa, uint64_t size) +{ + guest_map_mem(gpa, size, true, true, true); } static void guest_run_test(uint64_t base_gpa, bool do_fallocate) @@ -113,7 +121,6 @@ static void guest_run_test(uint64_t base_gpa, bool do_fallocate) struct { uint64_t offset; uint64_t size; - uint8_t pattern; } stages[] = { GUEST_STAGE(0, PAGE_SIZE), GUEST_STAGE(0, SZ_2M), @@ -156,6 +163,10 @@ static void guest_run_test(uint64_t base_gpa, bool do_fallocate) if (size > PAGE_SIZE) { memset((void *)gpa, p2, PAGE_SIZE); + + /* Test if punch hole results in zeroing page. */ + guest_punch_hole_private(gpa, PAGE_SIZE); + memcmp_g(gpa, 0, PAGE_SIZE); goto skip; } @@ -229,6 +240,7 @@ static void handle_exit_hypercall(struct kvm_vcpu *vcpu) uint64_t size = run->hypercall.args[1] * PAGE_SIZE; bool map_shared = run->hypercall.args[2] & MAP_GPA_SHARED; bool do_fallocate = run->hypercall.args[2] & MAP_GPA_DO_FALLOCATE; + bool fallocate_only = run->hypercall.args[2] & MAP_GPA_FALLOCATE_ONLY; struct kvm_vm *vm = vcpu->vm; TEST_ASSERT(run->hypercall.nr == KVM_HC_MAP_GPA_RANGE, @@ -238,8 +250,10 @@ static void handle_exit_hypercall(struct kvm_vcpu *vcpu) if (do_fallocate) vm_guest_mem_fallocate(vm, gpa, size, map_shared); - vm_set_memory_attributes(vm, gpa, size, - map_shared ? 0 : KVM_MEMORY_ATTRIBUTE_PRIVATE); + if (!fallocate_only) + vm_set_memory_attributes(vm, gpa, size, + map_shared ? + 0 : KVM_MEMORY_ATTRIBUTE_PRIVATE); run->hypercall.ret = 0; } From patchwork Thu Sep 21 20:14:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Isaku Yamahata X-Patchwork-Id: 13394563 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4EA14E7D0A2 for ; Thu, 21 Sep 2023 20:41:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232340AbjIUUlr (ORCPT ); Thu, 21 Sep 2023 16:41:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46712 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230381AbjIUUl3 (ORCPT ); Thu, 21 Sep 2023 16:41:29 -0400 Received: from mgamail.intel.com (mgamail.intel.com [134.134.136.24]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 44045A4B8; Thu, 21 Sep 2023 13:15:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695327317; x=1726863317; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=LrS431o0cshlDoRy46oclShFvKK7oFAV7dBwoRHaY3I=; b=Wn1oLNbX95JqgAKw0V6G00XnJSaj6qw5sx/flPjuV+pbwHeluKqKPHbv li4Z/6TEhi+G35SW560WmpcPj9LdeCPy8okmx2ZQ41YvdCUlu45P0WQ03 3Qsch7dsXUVEiy8JcC8+dZ42gCtMZRE6CreRI3snpsdt9QEscRrOIzmsm yo6eAzCZ6fjJSIaXd3iUgvNpP7CjB7k+va6QcXuEt45acwhx47xiOkCtE 17hxKVbGaNKNuztBwpFf0RCtNW9vI/pvSbd8nHqFBHkoxC50hbHHB+yl2 goEfW3JeO7PJ+0vs0Z2Ra0yGis3dBmT6IWWn8l/wF7N8lZrHzvDSIAF0f Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="383401642" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="383401642" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:49 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="696897807" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="696897807" Received: from ls.sc.intel.com (HELO localhost) ([172.25.112.31]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:48 -0700 From: isaku.yamahata@intel.com To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: isaku.yamahata@intel.com, isaku.yamahata@gmail.com, Michael Roth , Paolo Bonzini , Sean Christopherson , erdemaktas@google.com, Sagi Shahar , David Matlack , Kai Huang , Zhi Wang , chen.bo@intel.com, linux-coco@lists.linux.dev, Chao Peng , Ackerley Tng , Vishal Annapurve , Yuan Yao , Jarkko Sakkinen , Xu Yilun , Quentin Perret , wei.w.wang@intel.com, Fuad Tabba Subject: [RFC PATCH v2 4/6] KVM: gmem: Add ioctl to inject memory failure on guest memfd Date: Thu, 21 Sep 2023 13:14:37 -0700 Message-Id: <363c4ac28af93aa96a52f897d2fe5c7ec013f746.1695327124.git.isaku.yamahata@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Isaku Yamahata To test error_remove_page() method of KVM gmem, add a new ioctl to inject memory failure based on offset of guest memfd. Signed-off-by: Isaku Yamahata --- include/uapi/linux/kvm.h | 6 ++++ virt/kvm/guest_mem.c | 68 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 65fc983af840..4160614bcc0f 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -2323,4 +2323,10 @@ struct kvm_create_guest_memfd { __u64 reserved[6]; }; +#define KVM_GUEST_MEMORY_FAILURE _IOWR(KVMIO, 0xd5, struct kvm_guest_memory_failure) + +struct kvm_guest_memory_failure { + __u64 offset; +}; + #endif /* __LINUX_KVM_H */ diff --git a/virt/kvm/guest_mem.c b/virt/kvm/guest_mem.c index 01fb4ca861d0..bc9dae50004b 100644 --- a/virt/kvm/guest_mem.c +++ b/virt/kvm/guest_mem.c @@ -291,10 +291,78 @@ static struct file *kvm_gmem_get_file(struct kvm_memory_slot *slot) return file; } +static int kvm_gmem_inject_failure(struct file *file, + struct kvm_guest_memory_failure *mf) +{ + struct inode *inode = file_inode(file); + struct address_space *mapping = inode->i_mapping; + pgoff_t index = mf->offset >> PAGE_SHIFT; + struct folio *folio; + unsigned long pfn; + int err = 0; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + filemap_invalidate_lock_shared(mapping); + + /* Don't allocate page. */ + folio = filemap_get_folio(mapping, index); + if (!folio) { + err = -ENOENT; + goto out; + } + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + goto out; + } + + pfn = folio_pfn(folio) + (index - folio_index(folio)); + folio_put(folio); + +out: + filemap_invalidate_unlock_shared(mapping); + if (err) + return err; + + /* + * Race with pfn: memory_failure() and unpoison_memory() gain invalidate + * lock as the error recovery logic tries to remove pages from + * mapping. + */ + if (!pfn_valid(pfn)) + return -ENXIO; + return memory_failure(pfn, MF_SW_SIMULATED); +} + +static long kvm_gmem_ioctl(struct file *file, unsigned int ioctl, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int r = -EINVAL; + + switch (ioctl) { + case KVM_GUEST_MEMORY_FAILURE: { + struct kvm_guest_memory_failure mf; + + r = -EFAULT; + if (copy_from_user(&mf, argp, sizeof(mf))) + break; + r = kvm_gmem_inject_failure(file, &mf); + break; + } + default: + break; + } + + return r; +} + static const struct file_operations kvm_gmem_fops = { .open = generic_file_open, .release = kvm_gmem_release, .fallocate = kvm_gmem_fallocate, + .unlocked_ioctl = kvm_gmem_ioctl, }; static int kvm_gmem_migrate_folio(struct address_space *mapping, From patchwork Thu Sep 21 20:14:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Isaku Yamahata X-Patchwork-Id: 13394565 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A3D59E7D0A2 for ; Thu, 21 Sep 2023 20:41:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231960AbjIUUlu (ORCPT ); Thu, 21 Sep 2023 16:41:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40082 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230197AbjIUUlf (ORCPT ); Thu, 21 Sep 2023 16:41:35 -0400 Received: from mgamail.intel.com (mgamail.intel.com [134.134.136.24]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8F1511731; Thu, 21 Sep 2023 13:15:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695327319; x=1726863319; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=xgBlKNZhDNJa7QFdNnudGsohvQxeiXqUZBEeWY2YObQ=; b=EHO0LIM/x1tFIuqEOXOraBxGoiC3wL3XeEvUQi76PSAv2AYAJCbQk7Qi D24pIX6CkkEAwsv4Pk0/OZQ679ETeH2lAh/Zatn1cJq6AAtZm/x/uTEqR Df9a5aGmrCb4/7S/4qOnWBA9TZ1cjmbQ+YaC/GvACvYTDhfC3VsiraNjX Gelt3u36fY6T5Id7Nhl3WgFRXUKVgqueiwQKfRmmBmu3QA4EXJXi2xMQ2 S4tKbVNuhArxpvYuVmsVbrJ03Vc+Ww0OgPHRsgbOM08lcRi74eWeE7nJc nA5j6q5HAbehud+d3DE7Rj9J9wMa6aEiAXAPOZO01pWGfgyPuj01xufDm g==; X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="383401651" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="383401651" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:50 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="696897810" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="696897810" Received: from ls.sc.intel.com (HELO localhost) ([172.25.112.31]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:49 -0700 From: isaku.yamahata@intel.com To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: isaku.yamahata@intel.com, isaku.yamahata@gmail.com, Michael Roth , Paolo Bonzini , Sean Christopherson , erdemaktas@google.com, Sagi Shahar , David Matlack , Kai Huang , Zhi Wang , chen.bo@intel.com, linux-coco@lists.linux.dev, Chao Peng , Ackerley Tng , Vishal Annapurve , Yuan Yao , Jarkko Sakkinen , Xu Yilun , Quentin Perret , wei.w.wang@intel.com, Fuad Tabba Subject: [RFC PATCH v2 5/6] KVM: selftests: Add test cases for KVM_GUEST_MEMORY_FAILURE Date: Thu, 21 Sep 2023 13:14:38 -0700 Message-Id: <71be0cea7cd1ed2d7d20403a803213a9e2b28c11.1695327124.git.isaku.yamahata@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Isaku Yamahata Add text cases for guest_memfd KVM_GUEST_MEMORY_FAILURE ioctl. + ioctl(KVM_GUEST_MEMORY_FAILURE) success with backing pages + ioctl(KVM_GUEST_MEMORY_FAILURE) fails with ENOENT without backing page + interaction with fallocate(PUNCH_HOLE) Signed-off-by: Isaku Yamahata --- .../testing/selftests/kvm/guest_memfd_test.c | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c index d5b4bfcdc3fe..f8b242c9319d 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -122,6 +122,53 @@ static void test_fallocate_fail(int fd, size_t page_size, size_t total_size) } } +static void test_memory_failure(int fd, size_t page_size, size_t total_size) +{ + struct kvm_guest_memory_failure mf; + int ret; + int i; + + /* Make whole file unallocated as known initial state */ + ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, + 0, total_size); + TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) at while file should succeed"); + + /* Because there is no backing page, fail to inject memory failure. */ + for (i = 0; i < total_size / page_size; i++) { + mf = (struct kvm_guest_memory_failure) { + .offset = i * page_size, + }; + + ret = ioctl(fd, KVM_GUEST_MEMORY_FAILURE, &mf); + if (ret == -1 && errno == EPERM) { + pr_info("KVM_GUEST_MEMORY_FAILURE requires CAP_SYS_ADMIN. Skipping.\n") + return; + } + TEST_ASSERT(ret == -1 && errno == ENOENT, + "ioctl(KVM_GUEST_MEMORY_FAILURE) should fail i %d", i); + } + + /* Allocate pages with index one and two. */ + ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, page_size, page_size * 2); + TEST_ASSERT(!ret, "fallocate beginning at page_size should succeed"); + + for (i = 0; i < total_size / page_size; i++) { + mf = (struct kvm_guest_memory_failure) { + .offset = i * page_size, + }; + + ret = ioctl(fd, KVM_GUEST_MEMORY_FAILURE, &mf); + + if (i == 1 || i == 2) { + TEST_ASSERT(!ret || (ret == -1 && errno == EBUSY), + "ioctl(KVM_GUEST_MEMORY_FAILURE) should succeed i %d", i); + } else { + TEST_ASSERT(ret == -1 && errno == ENOENT, + "ioctl(KVM_GUEST_MEMORY_FAILURE) should fail i %d", i); + } + } +} + static void test_create_guest_memfd_invalid(struct kvm_vm *vm) { uint64_t valid_flags = 0; @@ -192,6 +239,7 @@ int main(int argc, char *argv[]) test_file_size(fd, page_size, total_size); test_fallocate(fd, page_size, total_size); test_fallocate_fail(fd, page_size, total_size); + test_memory_failure(fd, page_size, total_size); close(fd); } From patchwork Thu Sep 21 20:14:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Isaku Yamahata X-Patchwork-Id: 13394566 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1C5E1E7D0AA for ; Thu, 21 Sep 2023 20:42:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232317AbjIUUmH (ORCPT ); Thu, 21 Sep 2023 16:42:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47094 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232160AbjIUUlm (ORCPT ); Thu, 21 Sep 2023 16:41:42 -0400 Received: from mgamail.intel.com (mgamail.intel.com [134.134.136.24]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 73AAA5B412; Thu, 21 Sep 2023 13:15:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695327336; x=1726863336; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=dm/BsRbcZi8npHyGQ8jehfHjGfIx8Vcs8CXWsZn7TdU=; b=B90vrY1MsQwWSD+pFWIn7ALW5fPoKigUbVyiHV0Rcp9TLm7H77gqOF1r tf/tX+3eo75igdLn0iJ+qYq6y0zcsJPoK8mFWfponFgl5mh5RfofichUn tryTi3rvCS9vmOxhz+aPP1wy8UsUPMO6awpzRMMULAkUixJO9jbIR8EXM PBdPq5X0QxHOZB2c0Uzd9dsm4HYVFjl44bN/YdI0SAUpXam8J26mCAI+Y VxzrsDe+uTKCL662U7TO3UaPt020NrPqNC0zTqXkkuYBvT8paKq9sqsHn iRrWaeM9uU2nuelomVXELavWueVxzWe3Ga7t4Dht2ti3vN/2pO9u+X2O3 A==; X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="383401660" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="383401660" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:50 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10840"; a="696897814" X-IronPort-AV: E=Sophos;i="6.03,166,1694761200"; d="scan'208";a="696897814" Received: from ls.sc.intel.com (HELO localhost) ([172.25.112.31]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Sep 2023 13:14:50 -0700 From: isaku.yamahata@intel.com To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: isaku.yamahata@intel.com, isaku.yamahata@gmail.com, Michael Roth , Paolo Bonzini , Sean Christopherson , erdemaktas@google.com, Sagi Shahar , David Matlack , Kai Huang , Zhi Wang , chen.bo@intel.com, linux-coco@lists.linux.dev, Chao Peng , Ackerley Tng , Vishal Annapurve , Yuan Yao , Jarkko Sakkinen , Xu Yilun , Quentin Perret , wei.w.wang@intel.com, Fuad Tabba Subject: [RFC PATCH v2 6/6] KVM: guest_memfd: selftest: Add test case for error_remove_page method Date: Thu, 21 Sep 2023 13:14:39 -0700 Message-Id: <7fddbf10494490251f2156fd600306991826165f.1695327124.git.isaku.yamahata@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Isaku Yamahata This test case implements fault injection into guest memory by madvise(MADV_HWPOISON) for shared(conventional) memory region and KVM_GUEST_MEMORY_FAILURE for private gmem region. Once page is poisoned, free the poisoned page and try to run vcpu again to see a new zero page is assigned. Signed-off-by: Isaku Yamahata --- tools/testing/selftests/kvm/Makefile | 1 + .../kvm/x86_64/private_mem_hwpoison_test.c | 367 ++++++++++++++++++ 2 files changed, 368 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86_64/private_mem_hwpoison_test.c diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index f7fdd8244547..a72d0946c233 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -82,6 +82,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/nested_exceptions_test TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test TEST_GEN_PROGS_x86_64 += x86_64/pmu_event_filter_test TEST_GEN_PROGS_x86_64 += x86_64/private_mem_conversions_test +TEST_GEN_PROGS_x86_64 += x86_64/private_mem_hwpoison_test TEST_GEN_PROGS_x86_64 += x86_64/private_mem_kvm_exits_test TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test diff --git a/tools/testing/selftests/kvm/x86_64/private_mem_hwpoison_test.c b/tools/testing/selftests/kvm/x86_64/private_mem_hwpoison_test.c new file mode 100644 index 000000000000..78242ee8c8db --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/private_mem_hwpoison_test.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022, Google LLC. + * Copyright (C) 2023, Intel Corp. + */ +#define _GNU_SOURCE /* for program_invocation_short_name */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define BASE_DATA_SLOT 10 +#define BASE_DATA_GPA ((uint64_t)(1ull << 32)) +#define PER_CPU_DATA_SIZE ((uint64_t)(SZ_2M)) + +enum ucall_syncs { + HWPOISON_SHARED, + HWPOISON_PRIVATE, +}; + +static void guest_sync_shared(uint64_t gpa) +{ + GUEST_SYNC2(HWPOISON_SHARED, gpa); +} + +static void guest_sync_private(uint64_t gpa) +{ + GUEST_SYNC2(HWPOISON_PRIVATE, gpa); +} + +/* Arbitrary values, KVM doesn't care about the attribute flags. */ +#define MAP_GPA_SHARED BIT(0) +#define MAP_GPA_DO_FALLOCATE BIT(1) +#define MAP_GPA_HWPOISON BIT(2) + +static void guest_map_mem(uint64_t gpa, uint64_t size, bool map_shared, + bool do_fallocate, bool hwpoison) +{ + uint64_t flags = 0; + + if (map_shared) + flags |= MAP_GPA_SHARED; + if (do_fallocate) + flags |= MAP_GPA_DO_FALLOCATE; + if (hwpoison) + flags |= MAP_GPA_HWPOISON; + kvm_hypercall_map_gpa_range(gpa, size, flags); +} + +static void guest_map_shared(uint64_t gpa, uint64_t size, bool do_fallocate, + bool hwpoison) +{ + guest_map_mem(gpa, size, true, do_fallocate, hwpoison); +} + +static void guest_map_private(uint64_t gpa, uint64_t size, bool do_fallocate, + bool hwpoison) +{ + guest_map_mem(gpa, size, false, do_fallocate, hwpoison); +} + +static void guest_run_test(uint64_t base_gpa, bool huge_page, + bool test_shared) +{ + uint64_t gpa = base_gpa + (huge_page ? 0 : PAGE_SIZE); + uint64_t size = huge_page ? SZ_2M : PAGE_SIZE; + const uint8_t init_p = 0xcc; + uint64_t r; + + /* Memory should be shared by default. */ + guest_map_shared(base_gpa, PER_CPU_DATA_SIZE, true, false); + memset((void *)base_gpa, 0, PER_CPU_DATA_SIZE); + + /* + * Set the test region to non-zero to differentiate it from the page + * newly assigned. + */ + memset((void *)gpa, init_p, size); + + /* Ask VMM to convert to private/shared the page and poison it. */ + if (test_shared) { + guest_map_shared(gpa, size, true, true); + guest_sync_shared(gpa); + } else { + guest_map_private(gpa, size, true, true); + guest_sync_private(gpa); + } + + /* Consume poisoned data. */ + r = READ_ONCE(*(uint64_t *)gpa); + /* Discard the poisoned page and assign a new page. */ + GUEST_ASSERT_EQ((uint8_t)r, 0); +} + +static void guest_code(uint64_t base_gpa, bool huge_page, bool test_shared) +{ + guest_run_test(base_gpa, huge_page, test_shared); + GUEST_DONE(); +} + +static void handle_exit_hypercall(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + uint64_t gpa = run->hypercall.args[0]; + uint64_t size = run->hypercall.args[1] * PAGE_SIZE; + bool map_shared = run->hypercall.args[2] & MAP_GPA_SHARED; + bool do_fallocate = run->hypercall.args[2] & MAP_GPA_DO_FALLOCATE; + struct kvm_vm *vm = vcpu->vm; + + TEST_ASSERT(run->hypercall.nr == KVM_HC_MAP_GPA_RANGE, + "Wanted MAP_GPA_RANGE (%u), got '%llu'", + KVM_HC_MAP_GPA_RANGE, run->hypercall.nr); + + if (do_fallocate) + vm_guest_mem_fallocate(vm, gpa, size, map_shared); + + vm_set_memory_attributes(vm, gpa, size, + map_shared ? 0 : KVM_MEMORY_ATTRIBUTE_PRIVATE); + run->hypercall.ret = 0; +} + +static void inject_memory_failure(int gmem_fd, uint64_t gpa) +{ + /* See vm_mem_add() in test_mem_failure() */ + uint64_t offset = gpa - BASE_DATA_GPA; + struct kvm_guest_memory_failure mf = { + .offset = offset, + }; + int ret; + + ret = ioctl(gmem_fd, KVM_GUEST_MEMORY_FAILURE, &mf); + __TEST_REQUIRE(!(ret == -1 && errno == EPERM), + "Injecting memory fault requires CAP_SYS_ADMIN"); + TEST_ASSERT(!ret || (ret == -1 && errno == EBUSY), + "ioctl(KVM_GUEST_MEMORY_FAILURE) should success"); +} + +static sigjmp_buf sigbuf; + +static void sigbus_handler(int sig, siginfo_t *info, void *data) +{ + TEST_ASSERT(sig == SIGBUS, "Unknown signal received %d\n", sig); + siglongjmp(sigbuf, 1); +} + +static bool run_vcpus; + +struct test_args { + struct kvm_vcpu *vcpu; + int gmem_fd; + bool huge_page; + bool test_shared; +}; + +static void *__test_mem_failure(void *__args) +{ + struct test_args *args = __args; + struct kvm_vcpu *vcpu = args->vcpu; + struct kvm_run *run = vcpu->run; + struct kvm_vm *vm = vcpu->vm; + int gmem_fd = args->gmem_fd; + struct ucall uc; + + while (!READ_ONCE(run_vcpus)) + ; + + for ( ;; ) { + vcpu_run(vcpu); + + if (run->exit_reason == KVM_EXIT_HYPERCALL) { + handle_exit_hypercall(vcpu); + continue; + } + + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "Wanted KVM_EXIT_IO, got exit reason: %u (%s)", + run->exit_reason, exit_reason_str(run->exit_reason)); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + case UCALL_SYNC: { + uint64_t gpa = uc.args[1]; + + TEST_ASSERT(uc.args[0] == HWPOISON_SHARED || + uc.args[0] == HWPOISON_PRIVATE, + "Unknown sync command '%ld'", uc.args[0]); + + if (uc.args[0] == HWPOISON_PRIVATE) { + int ret; + + inject_memory_failure(gmem_fd, gpa); + ret = _vcpu_run(vcpu); + TEST_ASSERT(ret == -1 && errno == EHWPOISON && + run->exit_reason == KVM_EXIT_MEMORY_FAULT, + "exit_reason 0x%x", + run->exit_reason); + /* Discard the poisoned page and assign new page. */ + vm_guest_mem_fallocate(vm, gpa, PAGE_SIZE, true); + } else { + uint8_t *hva = addr_gpa2hva(vm, gpa); + int r; + + r = madvise(hva, 8, MADV_HWPOISON); + __TEST_REQUIRE(!(r == -1 && errno == EPERM), + "madvise(MADV_HWPOISON) requires CAP_SYS_ADMIN"); + TEST_ASSERT(!r, "madvise(MADV_HWPOISON) should succeed"); + if (sigsetjmp(sigbuf, 1)) { + TEST_ASSERT(!sigaction(SIGBUS, NULL, NULL), + "sigaction should success"); + r = madvise(hva, PAGE_SIZE, MADV_FREE); + TEST_ASSERT(!r, "madvise(MADV_FREE) should success"); + } else { + struct sigaction sa = { + .sa_sigaction = sigbus_handler, + .sa_flags = SA_SIGINFO, + }; + TEST_ASSERT(!sigaction(SIGBUS, &sa, NULL), + "sigaction should success"); + /* Trigger SIGBUS */ + vcpu_run(vcpu); + } + } + break; + } + case UCALL_DONE: + return NULL; + default: + TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd); + } + } +} + +static void test_mem_failure(enum vm_mem_backing_src_type src_type, uint32_t nr_vcpus, + uint32_t nr_memslots, bool huge_page, bool test_shared) +{ + /* + * Allocate enough memory so that each vCPU's chunk of memory can be + * naturally aligned with respect to the size of the backing store. + */ + const size_t size = align_up(PER_CPU_DATA_SIZE, get_backing_src_pagesz(src_type)); + const size_t memfd_size = size * nr_vcpus; + struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; + pthread_t threads[KVM_MAX_VCPUS]; + uint64_t gmem_flags; + struct kvm_vm *vm; + int memfd, i; + + const struct vm_shape shape = { + .mode = VM_MODE_DEFAULT, + .type = KVM_X86_SW_PROTECTED_VM, + }; + + vm = __vm_create_with_vcpus(shape, nr_vcpus, 0, guest_code, vcpus); + + vm_enable_cap(vm, KVM_CAP_EXIT_HYPERCALL, (1 << KVM_HC_MAP_GPA_RANGE)); + + if (huge_page && !backing_src_can_be_huge(src_type)) + TEST_FAIL("Huge page is requested, but not supported"); + if (backing_src_can_be_huge(src_type)) + gmem_flags = KVM_GUEST_MEMFD_ALLOW_HUGEPAGE; + else + gmem_flags = 0; + memfd = vm_create_guest_memfd(vm, memfd_size, gmem_flags); + + for (i = 0; i < nr_memslots; i++) + vm_mem_add(vm, src_type, BASE_DATA_GPA + size * i, + BASE_DATA_SLOT + i, size / vm->page_size, + KVM_MEM_PRIVATE, memfd, size * i); + + for (i = 0; i < nr_vcpus; i++) { + uint64_t gpa = BASE_DATA_GPA + i * size; + struct test_args args; + + vcpu_args_set(vcpus[i], 3, gpa, huge_page, test_shared); + + virt_map(vm, gpa, gpa, size / vm->page_size); + + args = (struct test_args) { + .vcpu = vcpus[i], + .gmem_fd = memfd, + .huge_page = huge_page, + .test_shared = test_shared, + }; + pthread_create(&threads[i], NULL, __test_mem_failure, &args); + } + + WRITE_ONCE(run_vcpus, true); + + for (i = 0; i < nr_vcpus; i++) + pthread_join(threads[i], NULL); + + kvm_vm_free(vm); + + close(memfd); +} + +static void help(const char *prog_name) +{ + printf("usage: %s [-h] [-m] [-M] [-n nr_vcpus] [-s mem_type] [-?]\n" + " -h: use huge page\n" + " -m: use multiple memslots (default: 1)\n" + " -n: specify the number of vcpus (default: 1)\n" + " -s: specify the memory type\n" + " -?: print this message\n", + prog_name); +} + +int main(int argc, char *argv[]) +{ + enum vm_mem_backing_src_type src_type = DEFAULT_VM_MEM_SRC; + bool use_multiple_memslots = false; + bool huge_page = false; + uint32_t nr_vcpus = 1; + uint32_t nr_memslots; + int opt; + + TEST_REQUIRE(kvm_has_cap(KVM_CAP_EXIT_HYPERCALL)); + TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & + BIT(KVM_X86_SW_PROTECTED_VM)); + + while ((opt = getopt(argc, argv, "hmn:s:S?")) != -1) { + switch (opt) { + case 'h': + huge_page = true; + break; + case 'm': + use_multiple_memslots = true; + break; + case 'n': + nr_vcpus = atoi_positive("nr_vcpus", optarg); + break; + case 's': + src_type = parse_backing_src_type(optarg); + break; + case '?': + default: + help(argv[0]); + exit(0); + } + } + + nr_memslots = use_multiple_memslots ? nr_vcpus : 1; + + test_mem_failure(src_type, nr_vcpus, nr_memslots, huge_page, true); + test_mem_failure(src_type, nr_vcpus, nr_memslots, huge_page, false); + + return 0; +}