From patchwork Mon Aug 8 14:56:49 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lu X-Patchwork-Id: 12938862 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 kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 67E54C25B0D for ; Mon, 8 Aug 2022 14:57:43 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 5A2EB8E0006; Mon, 8 Aug 2022 10:57:42 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 52C5F8E0002; Mon, 8 Aug 2022 10:57:42 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 37CAC8E0006; Mon, 8 Aug 2022 10:57:42 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0017.hostedemail.com [216.40.44.17]) by kanga.kvack.org (Postfix) with ESMTP id 1F8B18E0002 for ; Mon, 8 Aug 2022 10:57:42 -0400 (EDT) Received: from smtpin18.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay08.hostedemail.com (Postfix) with ESMTP id E4556140F1B for ; Mon, 8 Aug 2022 14:57:41 +0000 (UTC) X-FDA: 79776729522.18.3BC0930 Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by imf30.hostedemail.com (Postfix) with ESMTP id 476C380042 for ; Mon, 8 Aug 2022 14:57:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1659970661; x=1691506661; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=smWZqXtuGrkZm9PWI4t1Ng9axLN7SJF3zgimxq/DvoY=; b=D+NB/fk+wMvfyf1nl5jxlTVetCW+TyIqZQn1q4DImY9j71qIDHIIt4PE Z+8ZzCKJrInhmH00Jwc3hcUYDTEHf9pnmcjqYccGdE7Ded5YAbGVEn2WB cbxCcSrkYpqLYEt7+AZAhHNFFs48UHjPvZDmzBO904sUvok2hN7aobE7+ jwzMblaMxA9cdhmgTdOvItnPleRWu/B+RUaWs37EPM4qWXCdTTqzenEsy P9YcjFjhipFqSJG0yK+k5yWWPqyKE+5ciPRyFB2tYne3LP/mH4YKdIJwf Z+phNxQh+6pgCSjyjSGLFq74MqM6tW1L1OqHdJLMXJXOxR6yaTmpW4x86 A==; X-IronPort-AV: E=McAfee;i="6400,9594,10433"; a="290618483" X-IronPort-AV: E=Sophos;i="5.93,222,1654585200"; d="scan'208";a="290618483" Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Aug 2022 07:57:39 -0700 X-IronPort-AV: E=Sophos;i="5.93,222,1654585200"; d="scan'208";a="663980524" Received: from ziqianlu-desk2.sh.intel.com ([10.238.2.76]) by fmsmga008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Aug 2022 07:57:38 -0700 From: Aaron Lu To: Dave Hansen , Rick Edgecombe Cc: Song Liu , linux-kernel@vger.kernel.org, linux-mm@kvack.org Subject: [TEST NOT_FOR_MERGE 4/4] x86/mm/cpa: add a test interface to split direct map Date: Mon, 8 Aug 2022 22:56:49 +0800 Message-Id: <20220808145649.2261258-5-aaron.lu@intel.com> X-Mailer: git-send-email 2.37.1 In-Reply-To: <20220808145649.2261258-1-aaron.lu@intel.com> References: <20220808145649.2261258-1-aaron.lu@intel.com> MIME-Version: 1.0 ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1659970661; a=rsa-sha256; cv=none; b=um043iJPr4xwgoF5kRgKScNz6B8Hzd68muY/RFYkvNsXuixw+cUAz5VaRuHjevgj4hTby/ J+zQMH3mPgFc9TlmUAgIK52NkECZnl3bkxSLhldq1lauTxbZgawxj8XqHBsGO/89GFGY1t 3yotWFuTb2rQiuapP83T6z5/h+zaCzk= ARC-Authentication-Results: i=1; imf30.hostedemail.com; dkim=none ("invalid DKIM record") header.d=intel.com header.s=Intel header.b="D+NB/fk+"; dmarc=pass (policy=none) header.from=intel.com; spf=pass (imf30.hostedemail.com: domain of aaron.lu@intel.com designates 192.55.52.115 as permitted sender) smtp.mailfrom=aaron.lu@intel.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1659970661; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=5ksOb88ZajfZ5D8u73Cd71utZwdki0xN1K5B+WklFTY=; b=qvJwmTy0VLbGz9g95iZn9bbC7KSaBjsKH3pmwIILslGsmoH7tbqt+AbnhQWMyy4Qv0SR6R Rz5NvFSaq81bnBfqxiJi6FGJoxBZWfECblUXUwmP3XT0cMk2ZjkcR9xYzWsP3cIbmawfJ1 Xpy8wMBv5azGBFZ2sGqMDte6gTJOgDw= X-Rspamd-Server: rspam10 X-Stat-Signature: q7js5di79pw39pfzhh95atrio55qh9gs Authentication-Results: imf30.hostedemail.com; dkim=none ("invalid DKIM record") header.d=intel.com header.s=Intel header.b="D+NB/fk+"; dmarc=pass (policy=none) header.from=intel.com; spf=pass (imf30.hostedemail.com: domain of aaron.lu@intel.com designates 192.55.52.115 as permitted sender) smtp.mailfrom=aaron.lu@intel.com X-Rspam-User: X-Rspamd-Queue-Id: 476C380042 X-HE-Tag: 1659970661-738139 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: To test this functionality, a debugfs interface is added: /sys/kernel/debug/x86/split_mapping There are three test modes. mode 0: allocate $page_nr pages and set each page's protection first to RO and X and then back to RW and NX. This is used to test multiple CPUs dealing with different address ranges. mode 1: allocate several pages and create $nr_cpu kthreads to simultaneously change those pages protection with a fixed pattern. This is used to test multiple CPUs dealing with the same address range. mode 2: same as mode 0 except using alloc_pages() instead of vmalloc() because vmalloc space is too small on x86_32/pae. On a x86_64 VM, I started mode0.sh and mode1.sh at the same time: mode0.sh: mode=0 page_nr=200000 nr_cpu=16 function test_one() { echo $mode $page_nr > /sys/kernel/debug/x86/split_mapping } while true; do for i in `seq $nr_cpu`; do test_one & done wait done mode1.sh: mode=1 page_nr=1 echo $mode $page_nr > /sys/kernel/debug/x86/split_mapping After 5 hours, no problem occured with some millions of splits and merges. For x86_32 and x86_pae, mode2 test is used and also no problem found. Signed-off-by: Aaron Lu --- arch/x86/mm/pat/set_memory.c | 206 +++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index 1be9aab42c79..4deea4de73e7 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include #include #include @@ -2556,6 +2559,209 @@ int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address, return retval; } +static int split_mapping_mode0_test(int page_nr) +{ + void **addr_buff; + void *addr; + int i, j; + + addr_buff = kvmalloc(sizeof(void *) * page_nr, GFP_KERNEL); + if (!addr_buff) { + pr_err("addr_buff: no memory\n"); + return -ENOMEM; + } + + for (i = 0; i < page_nr; i++) { + addr = vmalloc(PAGE_SIZE); + if (!addr) { + pr_err("no memory\n"); + break; + } + + set_memory_ro((unsigned long)addr, 1); + set_memory_x((unsigned long)addr, 1); + + addr_buff[i] = addr; + } + + for (j = 0; j < i; j++) { + set_memory_nx((unsigned long)addr_buff[j], 1); + set_memory_rw((unsigned long)addr_buff[j], 1); + vfree(addr_buff[j]); + } + + kvfree(addr_buff); + + return 0; +} + +struct split_mapping_mode1_data { + unsigned long addr; + int page_nr; +}; + +static int split_mapping_set_prot(void *data) +{ + struct split_mapping_mode1_data *d = data; + unsigned long addr = d->addr; + int page_nr = d->page_nr; + int m; + + m = get_random_int() % 100; + msleep(m); + + while (!kthread_should_stop()) { + set_memory_ro(addr, page_nr); + set_memory_x(addr, page_nr); + set_memory_rw(addr, page_nr); + set_memory_nx(addr, page_nr); + cond_resched(); + } + + return 0; +} + +static int split_mapping_mode1_test(int page_nr) +{ + int nr_kthreads = num_online_cpus(); + struct split_mapping_mode1_data d; + struct task_struct **kthreads; + int i, j, ret; + void *addr; + + addr = vmalloc(PAGE_SIZE * page_nr); + if (!addr) + return -ENOMEM; + + kthreads = kmalloc(nr_kthreads * sizeof(struct task_struct *), GFP_KERNEL); + if (!kthreads) { + vfree(addr); + return -ENOMEM; + } + + d.addr = (unsigned long)addr; + d.page_nr = page_nr; + for (i = 0; i < nr_kthreads; i++) { + kthreads[i] = kthread_run(split_mapping_set_prot, &d, "split_mappingd%d", i); + if (IS_ERR(kthreads[i])) { + for (j = 0; j < i; j++) + kthread_stop(kthreads[j]); + ret = PTR_ERR(kthreads[i]); + goto out; + } + } + + while (1) { + if (signal_pending(current)) { + for (i = 0; i < nr_kthreads; i++) + kthread_stop(kthreads[i]); + ret = 0; + break; + } + msleep(1000); + } + +out: + kfree(kthreads); + vfree(addr); + return ret; +} + +static int split_mapping_mode2_test(int page_nr) +{ + struct page *p, *t; + unsigned long addr; + int i; + + LIST_HEAD(head); + + for (i = 0; i < page_nr; i++) { + p = alloc_pages(GFP_KERNEL | GFP_DMA32, 0); + if (!p) { + pr_err("no memory\n"); + break; + } + + addr = (unsigned long)page_address(p); + BUG_ON(!addr); + + set_memory_ro(addr, 1); + set_memory_x(addr, 1); + + list_add(&p->lru, &head); + } + + list_for_each_entry_safe(p, t, &head, lru) { + addr = (unsigned long)page_address(p); + set_memory_nx(addr, 1); + set_memory_rw(addr, 1); + + list_del(&p->lru); + __free_page(p); + } + + return 0; +} +static ssize_t split_mapping_write_file(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int mode = 0, page_nr = 0; + char buffer[64]; + int ret; + + if (count > 64) + return -EINVAL; + + if (copy_from_user(buffer, buf, count)) + return -EFAULT; + sscanf(buffer, "%u %u", &mode, &page_nr); + + /* + * There are 3 test modes. + * mode 0: each thread allocates $page_nr pages and set each page's + * protection first to RO and X and then back to RW and NX. + * This is used to test multiple CPUs dealing with different + * pages. + * mode 1: allocate several pages and create $nr_cpu kthreads to + * simultaneously change those pages protection to a fixed + * pattern. This is used to test multiple CPUs dealing with + * some same page's protection. + * mode 2: like mode 0 but directly use alloc_pages() because vmalloc + * area on x86_32 is too small, only 128M. + */ + if (mode > 2) + return -EINVAL; + + if (page_nr == 0) + return -EINVAL; + + if (mode == 0) + ret = split_mapping_mode0_test(page_nr); + else if (mode == 1) + ret = split_mapping_mode1_test(page_nr); + else + ret = split_mapping_mode2_test(page_nr); + + return ret ? ret : count; +} + +static const struct file_operations split_mapping_fops = { + .write = split_mapping_write_file, +}; + +static int __init split_mapping_init(void) +{ + struct dentry *d = debugfs_create_file("split_mapping", S_IWUSR, arch_debugfs_dir, NULL, + &split_mapping_fops); + if (IS_ERR(d)) { + pr_err("create split_mapping failed: %ld\n", PTR_ERR(d)); + return PTR_ERR(d); + } + + return 0; +} +late_initcall(split_mapping_init); + /* * The testcases use internal knowledge of the implementation that shouldn't * be exposed to the rest of the kernel. Include these directly here.