From patchwork Fri Jun 21 20:15:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Kagan, Roman" X-Patchwork-Id: 13708038 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 5E274C2BB85 for ; Fri, 21 Jun 2024 20:15:23 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id DFF3B8D019E; Fri, 21 Jun 2024 16:15:22 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id D31668D0190; Fri, 21 Jun 2024 16:15:22 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id A9A8C8D019E; Fri, 21 Jun 2024 16:15:22 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0014.hostedemail.com [216.40.44.14]) by kanga.kvack.org (Postfix) with ESMTP id 880DF8D0190 for ; Fri, 21 Jun 2024 16:15:22 -0400 (EDT) Received: from smtpin07.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay03.hostedemail.com (Postfix) with ESMTP id 39D2AA09A5 for ; Fri, 21 Jun 2024 20:15:22 +0000 (UTC) X-FDA: 82256000484.07.EC54116 Received: from smtp-fw-52005.amazon.com (smtp-fw-52005.amazon.com [52.119.213.156]) by imf14.hostedemail.com (Postfix) with ESMTP id 4052F10000C for ; Fri, 21 Jun 2024 20:15:20 +0000 (UTC) Authentication-Results: imf14.hostedemail.com; dkim=pass header.d=amazon.de header.s=amazon201209 header.b=t+pmnxLP; spf=pass (imf14.hostedemail.com: domain of "prvs=895821b17=rkagan@amazon.de" designates 52.119.213.156 as permitted sender) smtp.mailfrom="prvs=895821b17=rkagan@amazon.de"; dmarc=pass (policy=quarantine) header.from=amazon.de ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1719000915; 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-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=sakrS84DsySzhYnlE0QEvKaSeb66y1GOVt2jX6UWYYE=; b=Vq4wqQtkYJTUK/0kXzkE1LuYMYpPPbOJMAOygGqABRLyK4JYJoRUFbK3yyvGWz9VtSv4gj JY4S88vDIBPhA/mpTufaPqmV5CUG+NPxRGviUYu6yA9sScIoMiqtdcxpJY1Mg901j23APX ER3gAkWjt1s7q6uBFr8PV19QrlhlAWQ= ARC-Authentication-Results: i=1; imf14.hostedemail.com; dkim=pass header.d=amazon.de header.s=amazon201209 header.b=t+pmnxLP; spf=pass (imf14.hostedemail.com: domain of "prvs=895821b17=rkagan@amazon.de" designates 52.119.213.156 as permitted sender) smtp.mailfrom="prvs=895821b17=rkagan@amazon.de"; dmarc=pass (policy=quarantine) header.from=amazon.de ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1719000915; a=rsa-sha256; cv=none; b=znsFCXCxWikK9KTloOYQHMFvBfgVLM5OBHtSoob1F0NxYfCtFJ1oqt2Zj4IO4N0vnhhsYi UBVHP1hPzz6V9zgHs6Mo0DZy/DJsi5H12yXSctwXQEQxWcCpARC66kgw13XnAH31QQ7XXe 1UPeFEQvy4evi0sNR2M/EJUDAsssqP4= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amazon.de; i=@amazon.de; q=dns/txt; s=amazon201209; t=1719000920; x=1750536920; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=sakrS84DsySzhYnlE0QEvKaSeb66y1GOVt2jX6UWYYE=; b=t+pmnxLPO5GZZ+MbxeYQzZ9a4NhcUOlrZmMfDVPZJ+kbR9OGg1sRi/3V z0yURIffAb9Ogt7Z3dGD41sn9psG8V+H/qnk76kZmgpLR7iDB4E/QYquL GBuI970kqPbdjBTbhnVDeCfVPVsxOoXx/maC5ojfOnAb7gxrRDTVZtgst M=; X-IronPort-AV: E=Sophos;i="6.08,255,1712620800"; d="scan'208";a="662205944" Received: from iad12-co-svc-p1-lb1-vlan3.amazon.com (HELO smtpout.prod.us-east-1.prod.farcaster.email.amazon.dev) ([10.43.8.6]) by smtp-border-fw-52005.iad7.amazon.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Jun 2024 20:15:18 +0000 Received: from EX19MTAUEB002.ant.amazon.com [10.0.0.204:44808] by smtpin.naws.us-east-1.prod.farcaster.email.amazon.dev [10.0.94.206:2525] with esmtp (Farcaster) id 07fadb7d-beb5-4264-a68d-b24ca92dbe93; Fri, 21 Jun 2024 20:15:17 +0000 (UTC) X-Farcaster-Flow-ID: 07fadb7d-beb5-4264-a68d-b24ca92dbe93 Received: from EX19D008UEA002.ant.amazon.com (10.252.134.125) by EX19MTAUEB002.ant.amazon.com (10.252.135.47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1258.34; Fri, 21 Jun 2024 20:15:12 +0000 Received: from EX19MTAUEA001.ant.amazon.com (10.252.134.203) by EX19D008UEA002.ant.amazon.com (10.252.134.125) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1258.34; Fri, 21 Jun 2024 20:15:11 +0000 Received: from u40bc5e070a0153.ant.amazon.com (10.95.134.31) by mail-relay.amazon.com (10.252.134.102) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1258.34 via Frontend Transport; Fri, 21 Jun 2024 20:15:10 +0000 From: Roman Kagan To: CC: Shuah Khan , Dragan Cvetic , Fares Mehanna , Alexander Graf , "Derek Kiernan" , , , Greg Kroah-Hartman , , David Woodhouse , Andrew Morton , Arnd Bergmann Subject: [PATCH RFC 3/3] drivers/misc: add test driver and selftest for proclocal allocator Date: Fri, 21 Jun 2024 22:15:01 +0200 Message-ID: <20240621201501.1059948-4-rkagan@amazon.de> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240621201501.1059948-1-rkagan@amazon.de> References: <20240621201501.1059948-1-rkagan@amazon.de> MIME-Version: 1.0 X-Rspam-User: X-Rspamd-Server: rspam01 X-Rspamd-Queue-Id: 4052F10000C X-Stat-Signature: dd1p1emaan6smkcempwdccfuetdyotpr X-HE-Tag: 1719000920-372481 X-HE-Meta: U2FsdGVkX1/CeUHvf0XJdKX5rjD2zAMU1ZCk8AnxbrLf8Z2dNKWtpAeq5/g0g5+c9JWaoHJuTF/ouDuZIqok44Yc9wDAsMdsbrV07gZXP1f4BB+qkS49PFFee8MSQ3E24sBi/fjKknh0mfHH9TGkrDYyqhtvkQdvS7YPs1hUc9jt0PNvx28rVxiBq+f/eztny+y8ShDxAzqVYkUdNcMLiTx+Njm/g6Subvnj1Bk7kFm5xZ8X+y8CGCij2aPyz1d1IWb0Vf8kLU9/cKwbYsvzk3Wr06MXNBOGGkQIvvajUj5zpbTXvYfynaB3kJZRQBsZlYsXEKQD27lMkqE+RRxAEZkeWclnbJHlNpILUx4mFXCcSxlvTXQkoOFm/U9zIDiGWdYP1SwS5nkook4AsNA66qiXOYzhdrbIUw1Pyrsuf8A6iryB/LSMHgyTKDkGR9kA+aRcFLc+V15exU8qJBtsklfVr6LDlLRUzf2lo4TIijMIpaH/P0zaFKR6EiGNBBCLUasyvSRBWqfFzYLs5e0nmn1IF8jZxu/yD+XORWQ06gSTQ5J5suDQOm55GomnXG975X8RDa9oK4Yqg5PgpfBcHswK7f0N1zlX543UorcDzKVwZAiCxHAy6lRtnXmk7U8H9DyQZyeptFUa1qP6aAYpPQXUOgoBSDoWcMFJQA4FNLqDfP2wSNrVzZLjTyIkZDKrd2rr2VxyUsdzblAV23Xe4dHBQakxySIxPbFyJGgREKFXPVGMrqR3Ac4JSZRV3KptfhyW61F5zuB1aHfDFHNYxCNRHl7JCF2MtkuAI50ikEIIP/gc6oHjA9jcjot2c7prCuP02kngoKuTJxLeUeesykf8q529GH3WoInqr6IuPycZoNCAlnaUYcN24qFJDPbBVoXGoyAqXVluq/lZAtVzbt6/rsKgA3kztcoAUjxKbCD2quhQzFiiphEzJCYIKsa8jhQB5t2HSilDnXL+lJx 6+AynHVd APix4lqFcgbQdpuvG0dF+Wum1RuZvnL1H5gyXAS+Bm8M6WSZLY4jUPDGLcR5/w8QM90dpk7Zu/XIzp7lW+IemhSC/yOvOAhtcADVQQwyj7KM9h3l+zXBAeL0ms6rGWUdfVGm1OLegJx5zzwHQ+lXAGfWnZMesvNjNZKqj4ALn8Mjl6/9U8ES6utT+vKGGXnQz5dBEyo4BXB8YJyCN5nQUoVwjqFYUiZszp/lkTlEqcXtLg5itMoQCswWTwuwjOT11dugC145O77cLvIqYuNKh/2nZw21q1Nw7WWggOSYT4GeepDH+1vk8em7mBzKuMdqUaTZYTE1OQoufrPcu+P1slDhUIyvncT9GC+85mld5jDke5/E2ILY2QzAnYrCTz057vwJ8itscBTSj7gjHAJzUm8EEfRr5QSMrmBDGdlI/N7H+Us0= 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: List-Subscribe: List-Unsubscribe: Introduce a simple driver for functional and stress testing of proclocal kernel allocator. The driver exposes a device node /dev/proclocal-test, which allows userland programs to request creation of proclocal areas and to obtain their addresses as seen by the kernel, and in addition to read and write kernel memory at arbitrary address content (simplified /dev/kmem good enough to access proclocal allocations under selftest responsibility). The driver is not meant for use with production kernels, as it exposes internal kernel pointers and data. Also add a basic selftest that uses this driver. Signed-off-by: Roman Kagan --- drivers/misc/Makefile | 1 + tools/testing/selftests/proclocal/Makefile | 6 + drivers/misc/proclocal-test.c | 200 ++++++++++++++++++ .../selftests/proclocal/proclocal-test.c | 150 +++++++++++++ drivers/misc/Kconfig | 15 ++ tools/testing/selftests/proclocal/.gitignore | 1 + 6 files changed, 373 insertions(+) create mode 100644 tools/testing/selftests/proclocal/Makefile create mode 100644 drivers/misc/proclocal-test.c create mode 100644 tools/testing/selftests/proclocal/proclocal-test.c create mode 100644 tools/testing/selftests/proclocal/.gitignore diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 153a3f4837e8..33c244cee92d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -69,3 +69,4 @@ obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o obj-$(CONFIG_NSM) += nsm.o +obj-$(CONFIG_PROCLOCAL_TEST) += proclocal-test.o diff --git a/tools/testing/selftests/proclocal/Makefile b/tools/testing/selftests/proclocal/Makefile new file mode 100644 index 000000000000..b93baecee762 --- /dev/null +++ b/tools/testing/selftests/proclocal/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +TEST_GEN_PROGS := proclocal-test +CFLAGS += -O2 -g -Wall $(KHDR_INCLUDES) + +include ../lib.mk diff --git a/drivers/misc/proclocal-test.c b/drivers/misc/proclocal-test.c new file mode 100644 index 000000000000..9b3d0ed9b2f9 --- /dev/null +++ b/drivers/misc/proclocal-test.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All rights reserved. + * Author: Roman Kagan + * + * test driver for proclocal memory allocator + */ + +#include +#include +#include +#include +#include +#include + +struct proclocal_test_alloc { + u64 size; + u64 ptr; +}; + +#define PROCLOCAL_TEST_ALLOC _IOWR('A', 0x10, struct proclocal_test_alloc) + +#define BOUNCE_BUF_SIZE PAGE_SIZE + +struct proclocal_test { + struct secretmem_area *area; + size_t size; + void *bounce; +}; + +static int proclocal_test_open(struct inode *inode, struct file *f) +{ + struct proclocal_test *plt; + + plt = kzalloc(sizeof(*plt), GFP_KERNEL); + if (!plt) + return -ENOMEM; + + plt->bounce = kmalloc(BOUNCE_BUF_SIZE, GFP_KERNEL); + if (!plt->bounce) { + kfree(plt); + return -ENOMEM; + } + + f->f_mode |= FMODE_UNSIGNED_OFFSET; + f->private_data = plt; + return 0; +} + +static int proclocal_test_release(struct inode *inode, struct file *f) +{ + struct proclocal_test *plt = f->private_data; + if (plt->area) + secretmem_release_pages(plt->area); + kfree(plt->bounce); + kfree(plt); + return 0; +} + +static ssize_t proclocal_test_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + struct proclocal_test *plt = f->private_data; + const void *p = (const void *)*ppos; + ssize_t ret = -EFAULT; + + if (p + count < p) + return -EINVAL; + + while (count) { + size_t chunk = min_t(size_t, count, BOUNCE_BUF_SIZE); + size_t left; + + /* + * copy_to_user() disables superuser checks, so need to copy to + * bounce buffer first to test the access + */ + memcpy(plt->bounce, p, chunk); + + left = copy_to_user(buf, plt->bounce, chunk); + if (left == chunk) + goto out; + chunk -= left; + + buf += chunk; + p += chunk; + count -= chunk; + } + + ret = p - (const void *)*ppos; + *ppos = (loff_t)p; +out: + return ret; +} + +static ssize_t proclocal_test_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct proclocal_test *plt = f->private_data; + void *p = (void *)*ppos; + ssize_t ret = -EFAULT; + + if (p + count < p) + return -EINVAL; + + while (count) { + size_t chunk = min_t(size_t, count, BOUNCE_BUF_SIZE); + size_t left; + + /* + * copy_from_user() disables superuser checks, so need to copy + * to bounce buffer first to test the access + */ + left = copy_from_user(plt->bounce, buf, chunk); + if (left == chunk) + goto out; + chunk -= left; + + memcpy(p, plt->bounce, chunk); + + buf += chunk; + p += chunk; + count -= chunk; + } + + ret = p - (void *)*ppos; + *ppos = (loff_t)p; +out: + return ret; +} + +static long proclocal_test_alloc(struct proclocal_test *plt, + void __user *argp) +{ + struct proclocal_test_alloc pta; + unsigned long pages_needed; + + if (plt->size) + return -EEXIST; + + if (copy_from_user(&pta, argp, sizeof(pta))) + return -EFAULT; + + if (!pta.size) + return -EINVAL; + + pages_needed = (pta.size + PAGE_SIZE - 1) / PAGE_SIZE; + plt->area = secretmem_allocate_pages(fls(pages_needed - 1)); + if (!plt->area) + return -ENOMEM; + + plt->size = pta.size; + + pta.ptr = (u64)plt->area->ptr; + if (copy_to_user(argp, &pta, sizeof(pta))) + goto err; + + return 0; +err: + secretmem_release_pages(plt->area); + plt->area = NULL; + plt->size = 0; + return -EFAULT; +} + +static long proclocal_test_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + struct proclocal_test *plt = f->private_data; + void __user *argp = (void __user *)arg; + + switch (ioctl) { + case PROCLOCAL_TEST_ALLOC: + return proclocal_test_alloc(plt, argp); + default: + return -EINVAL; + } +} + +static const struct file_operations proclocal_test_fops = { + .owner = THIS_MODULE, + .release = proclocal_test_release, + .unlocked_ioctl = proclocal_test_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .open = proclocal_test_open, + .read = proclocal_test_read, + .write = proclocal_test_write, + .llseek = no_seek_end_llseek, +}; + +static struct miscdevice proclocal_test_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "proclocal-test", + .fops = &proclocal_test_fops, +}; +module_misc_device(proclocal_test_misc); + +MODULE_VERSION("0.0.1"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Roman Kagan"); +MODULE_DESCRIPTION("Test driver for proclocal allocator"); diff --git a/tools/testing/selftests/proclocal/proclocal-test.c b/tools/testing/selftests/proclocal/proclocal-test.c new file mode 100644 index 000000000000..386cc5d9e51a --- /dev/null +++ b/tools/testing/selftests/proclocal/proclocal-test.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All rights reserved. + * Author: Roman Kagan + * + * test for proclocal memory allocator using the corresponding test device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../kselftest_harness.h" + +struct proclocal_test_alloc { + uint64_t size; + uint64_t ptr; +}; + +#define PROCLOCAL_TEST_ALLOC _IOWR('A', 0x10, struct proclocal_test_alloc) + +const char proclocal_content[] = "this is test"; +char buf[256]; + +FIXTURE(proclocal) { + int fd; + void *ptr; +}; + +FIXTURE_SETUP(proclocal) +{ + struct proclocal_test_alloc pta = { + .size = sizeof(buf), + }; + + self->fd = open("/dev/proclocal-test", O_RDWR); + ASSERT_LE(0, self->fd); + + ASSERT_LE(0, ioctl(self->fd, PROCLOCAL_TEST_ALLOC, &pta)); + + self->ptr = (void *) pta.ptr; + TH_LOG("self->ptr = %p\n", self->ptr); +} + +FIXTURE_TEARDOWN(proclocal) +{ +} + +TEST_F(proclocal, kernel_access) +{ + ASSERT_EQ((off_t)self->ptr, + lseek(self->fd, (off_t)self->ptr, SEEK_SET)); + EXPECT_EQ(sizeof(proclocal_content), + write(self->fd, + proclocal_content, sizeof(proclocal_content))); + ASSERT_EQ((off_t)self->ptr, + lseek(self->fd, (off_t)self->ptr, SEEK_SET)); + EXPECT_EQ(sizeof(proclocal_content), + read(self->fd, buf, sizeof(proclocal_content))); + EXPECT_STREQ(proclocal_content, buf); +} + +sigjmp_buf jmpbuf; +void segv_handler(int signum, siginfo_t *si, void *uc) +{ + if (signum == SIGSEGV) + siglongjmp(jmpbuf, 1); +} + +TEST_F(proclocal, direct_access) +{ + bool access_succeeded; + struct sigaction sa; + + if (sigsetjmp(jmpbuf, 1) == 0) { + sa.sa_sigaction = segv_handler; + sa.sa_flags = SA_SIGINFO | SA_RESETHAND; + sigemptyset(&sa.sa_mask); + + sigaction(SIGSEGV, &sa, NULL); + + (void)((volatile char *)self->ptr)[0]; + + access_succeeded = true; + } else + access_succeeded = false; + + EXPECT_FALSE(access_succeeded); +} + +#define PAGE_SIZE 0x1000 + +TEST_F(proclocal, map_over) +{ + void *ptr_page = (void *)((uintptr_t)self->ptr & ~(PAGE_SIZE - 1)); + void *map; + int errno_save; + + errno = 0; + map = mmap(ptr_page, PAGE_SIZE, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + errno_save = errno; + + EXPECT_EQ(MAP_FAILED, map); + TH_LOG("errno = %d", errno_save); + + if (map != MAP_FAILED) + munmap(map, PAGE_SIZE); +} + +TEST_F(proclocal, release) +{ + EXPECT_EQ(0, close(self->fd)); +} + +TEST_F(proclocal, map_over_closed) +{ + void *ptr_page = (void *)((uintptr_t)self->ptr & ~(PAGE_SIZE - 1)); + void *map; + int errno_save; + + ASSERT_EQ(0, close(self->fd)); + + errno = 0; + map = mmap(ptr_page, PAGE_SIZE, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + errno_save = errno; + + EXPECT_EQ(ptr_page, map); + TH_LOG("errno = %d", errno_save); + + if (map != MAP_FAILED) + munmap(map, PAGE_SIZE); +} + +TEST_F(proclocal, kernel_access_closed) +{ + ASSERT_EQ(0, close(self->fd)); + self->fd = open("/dev/proclocal-test", O_RDWR); + ASSERT_LE(0, self->fd); + + ASSERT_EQ((off_t)self->ptr, + lseek(self->fd, (off_t)self->ptr, SEEK_SET)); + EXPECT_EQ(-1, read(self->fd, buf, sizeof(proclocal_content))); +} + +TEST_HARNESS_MAIN diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index faf983680040..29a334de0ca8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -585,6 +585,21 @@ config NSM To compile this driver as a module, choose M here. The module will be called nsm. +config PROCLOCAL_TEST + tristate "Proclocal allocator test driver" + depends on SECRETMEM + help + This driver allows to perform functional and stress tests for + proclocal memory allocator. It exposes /dev/proclocal-test that + userland test programs can use to create and manipulate proclocal + kernel allocations. + + To compile this driver as a module, choose M here: the module will be + called proclocal-test. + + If unsure, say N. + This driver is not meant to be used on production systems. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/tools/testing/selftests/proclocal/.gitignore b/tools/testing/selftests/proclocal/.gitignore new file mode 100644 index 000000000000..47e0fdcd6e3a --- /dev/null +++ b/tools/testing/selftests/proclocal/.gitignore @@ -0,0 +1 @@ +/proclocal-test