From patchwork Tue Aug 13 06:09:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091213 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D918B746 for ; Tue, 13 Aug 2019 06:13:18 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C8E8228438 for ; Tue, 13 Aug 2019 06:13:18 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B994F284CE; Tue, 13 Aug 2019 06:13:18 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4E36E28438 for ; Tue, 13 Aug 2019 06:13:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727003AbfHMGNS (ORCPT ); Tue, 13 Aug 2019 02:13:18 -0400 Received: from userp2130.oracle.com ([156.151.31.86]:34508 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726808AbfHMGNR (ORCPT ); Tue, 13 Aug 2019 02:13:17 -0400 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68sYR010868; Tue, 13 Aug 2019 06:12:58 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=osSqMWHv28avjbTj6QcaJxUDRAMJ3ZlAiS9rfxGjYzg=; b=mrvvWwB63K3x6ci1kK59RNlfNDoSK9mftOK1pAIyD2sL4atlRNxTN4BALkRAAcF92x1C iunLyjenghtiqknMbUJGEWJ7uKhAJjttoqq07VTfMzcD2YNQJTfq0YUencNAzMFGSf4j nVWGsVA9g+Aq+QXkktYlU3vAf2NexVAr3Ud2ORkdTKr5E9Fi9eR/8nTJPqinN5VwztyE 8pCZPIxk14S960O+PddvoyweoaLt+3FTRwb8gnO57t7lN3mJcjlRXEn7UXY6THAxw3Ku PUyEg0HLjR8kgGNYDL//MROvpkvGq1Q0ScC6XPNG4xj8a19Uw78U7Kxg7wEpQWxlcZCz IA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=osSqMWHv28avjbTj6QcaJxUDRAMJ3ZlAiS9rfxGjYzg=; b=geKy5hJ2TDNupb+fP/a2ESM5rZiqJx0vAaM/I8GC8WKJvGiulnpuj0AHfvgWB2STpf+n NcnhECINUsWxZVBlaYjq0foHIMNwehq+IhRdEGH3k9v0JcICVhUTfj/FDJ6gmnj/NXRr 2HfzlSfysRayBA7Fn43jCctNZjMWBVCydUhzV30lKenfTF1k3ODWMtcp2mKSUaHgdDLE dv8G5OW0epmiMzRr3Wbn6BJJ75jwCzTc3+J3XiUgRCvbEumbPxyEgf3Vq6EoEBux3GJw nnxEI/LLWUonnj/DUj5n/gV8u8Em4rBdFX1Qbz3m98IG4XAgr5rhYFGYNBQ76ssq+rHx qw== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by userp2130.oracle.com with ESMTP id 2u9nbtc1df-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:12:58 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67QY1096512; Tue, 13 Aug 2019 06:10:58 GMT Received: from userv0122.oracle.com (userv0122.oracle.com [156.151.31.75]) by aserp3030.oracle.com with ESMTP id 2u9m0ayd7s-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:10:57 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6Ass3025920; Tue, 13 Aug 2019 06:10:54 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:10:54 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 01/19] kbuild: Fixes to rules for host-cshlib and host-cxxshlib Date: Tue, 13 Aug 2019 08:09:16 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects. Also fix rules for both C and C++ shared libraries: - C++ shared libraries depended on .c instead of .cc files - Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*. Signed-off-by: Knut Omang --- scripts/Makefile.host | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/scripts/Makefile.host b/scripts/Makefile.host index b6a54bd..4e9bb21 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -46,8 +46,10 @@ host-cxxmulti := $(foreach m,$(__hostprogs),$(if $($(m)-cxxobjs),$(m))) host-cxxobjs := $(sort $(foreach m,$(host-cxxmulti),$($(m)-cxxobjs))) # Object (.o) files used by the shared libaries -host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-objs)))) -host-cxxshobjs := $(sort $(foreach m,$(host-cxxshlib),$($(m:.so=-objs)))) +# Note: C++ libraries may contain both C and C++ objects, compiled differently: +host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-cshobjs)))) +host-cshobjs += $(sort $(foreach m,$(host-cxxshlib),$($(m:.so=-cshobjs)))) +host-cxxshobjs := $(sort $(foreach m,$(host-cxxshlib),$($(m:.so=-cxxshobjs)))) host-csingle := $(addprefix $(obj)/,$(host-csingle)) host-cmulti := $(addprefix $(obj)/,$(host-cmulti)) @@ -130,14 +132,10 @@ quiet_cmd_host-cshobjs = HOSTCC -fPIC $@ $(host-cshobjs): $(obj)/%.o: $(src)/%.c FORCE $(call if_changed_dep,host-cshobjs) -# Compile .c file, create position independent .o file -# Note that plugin capable gcc versions can be either C or C++ based -# therefore plugin source files have to be compilable in both C and C++ mode. -# This is why a C++ compiler is invoked on a .c file. -# host-cxxshobjs -> .o +# Compile .cc (C++) file, create position independent .o file quiet_cmd_host-cxxshobjs = HOSTCXX -fPIC $@ cmd_host-cxxshobjs = $(HOSTCXX) $(hostcxx_flags) -fPIC -c -o $@ $< -$(host-cxxshobjs): $(obj)/%.o: $(src)/%.c FORCE +$(host-cxxshobjs): $(obj)/%.o: $(src)/%.cc FORCE $(call if_changed_dep,host-cxxshobjs) # Link a shared library, based on position independent .o files @@ -154,7 +152,8 @@ $(call multi_depend, $(host-cshlib), .so, -objs) # *.o -> .so shared library (host-cxxshlib) quiet_cmd_host-cxxshlib = HOSTLLD -shared $@ cmd_host-cxxshlib = $(HOSTCXX) $(KBUILD_HOSTLDFLAGS) -shared -o $@ \ - $(addprefix $(obj)/,$($(@F:.so=-objs))) \ + $(addprefix $(obj)/,$($(@F:.so=-cshobjs))) \ + $(addprefix $(obj)/,$($(@F:.so=-cxxshobjs))) \ $(KBUILD_HOSTLDLIBS) $(HOSTLDLIBS_$(@F)) $(host-cxxshlib): FORCE $(call if_changed,host-cxxshlib) From patchwork Tue Aug 13 06:09:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091219 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BD0C913AC for ; Tue, 13 Aug 2019 06:13:34 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id ACC9A27FE4 for ; Tue, 13 Aug 2019 06:13:34 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A0B892845E; Tue, 13 Aug 2019 06:13:34 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9BCED28438 for ; Tue, 13 Aug 2019 06:13:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727343AbfHMGNa (ORCPT ); Tue, 13 Aug 2019 02:13:30 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:40398 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726808AbfHMGN3 (ORCPT ); Tue, 13 Aug 2019 02:13:29 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68uMw021972; Tue, 13 Aug 2019 06:13:00 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=qbLLhu1JxazjsK8NdQnvn6HpeQ0btTaiQUzkaqX9Wv0=; b=bFGrM9LqXGC3jCaPl+YPfhMSZUBF2MQ1rAzk7OujN5Mi3KQdqwRxd/ih73YEYfo7We4c 1ESplTRoEp7E3BjEPzWRkEBePVqYyr6pSOCkECDmm4MbyzRCo1QZWnZUGcrK8b5ngAzD SRhlgsDewVEax0YN8hs41R10IZb9ydxWU7bxcrRB1JvZKsDch3JfB+rWPu4EcuQpBFFy FOZba/S0nJ7y324qxN4Jt46nO+tqHGh8GdfN1G9sdJJu9vOVY3as0mks1UZz4HFb989U DzBHG2mK1xvelbUAAtgFlRQGQdCB73foBze8JbOT+YNuWhIjbngqhTf34l41ty6EHqyw 7w== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=qbLLhu1JxazjsK8NdQnvn6HpeQ0btTaiQUzkaqX9Wv0=; b=FrQS4HHJi8i4jyN8dpYz68G7+wsvM+CCO9l2Q5nr5xaub04oazcejYPrT8D8x98pEKuF bniCka7HqLBplR4kJomgV3f1lT/U7eikpbosc0tmdHr/46JTnrJuLhrFFnCtKC6fGDOP YZCBAtt0hIF5I+JzvXdWYDK/qMWwkPClU6hQVPC0JKHuwX8XObHyroqOAG8CjVvZTjL8 1vmm8j+CnbgqCEHW0l1w2lqv9pbUPfvoTg4cC9uGBPXQ8vu0h9zr4knurjGnys0ul7+9 zm+tADGByHw2QMtG1DYwT1AcjbpvfPb4kOC3rwWMfT7clAPakE/6FVPpj9cM6eZLu57y Xw== Received: from aserp3020.oracle.com (aserp3020.oracle.com [141.146.126.70]) by userp2120.oracle.com with ESMTP id 2u9pjqbw2a-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:13:00 +0000 Received: from pps.filterd (aserp3020.oracle.com [127.0.0.1]) by aserp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67Spc157066; Tue, 13 Aug 2019 06:10:59 GMT Received: from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236]) by aserp3020.oracle.com with ESMTP id 2u9nrenkb8-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:10:59 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6AwUK013181; Tue, 13 Aug 2019 06:10:58 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:10:58 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 02/19] ktf: Introduce the main part of the kernel side of ktf Date: Tue, 13 Aug 2019 08:09:17 +0200 Message-Id: <524b4e062500c6a240d4d7c0e1d0a2996800cc0a.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The ktf module itself and basic data structures for management of test cases and tests and contexts for tests. Also contains the top level include file for kernel clients in ktf.h. More elaborate documentation follows towards the end of the patch set. This patch set contains both user level and kernel code, we'll provide the full implementation of ktf on the kernel side in this and forthcoming patches, then the user space code to execute tests within the kernel and report results, then documentation before introducing a small self test suite of tests to test ktf itself, and some very simple additional example tests. ktf.h: Defines the KTF user API for kernel clients ktf_test.c: Kernel side code for tracking and reporting ktf test results Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++++++++- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++++++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++++++++- 5 files changed, 1806 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/Makefile create mode 100644 tools/testing/selftests/ktf/kernel/ktf.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_context.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.h diff --git a/tools/testing/selftests/ktf/kernel/Makefile b/tools/testing/selftests/ktf/kernel/Makefile new file mode 100644 index 0000000..0897ae2 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/Makefile @@ -0,0 +1,15 @@ +# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 +# +# Implementation of the kernel part of Kernel Test Framework (KTF), +# a framework for running unit test like tests within the kernel. +# + +obj-m := ktf.o + +include $(srctree)/$(src)/../scripts/ktf_syms.mk + +ktf-y := ktf_context.o ktf_nl.o ktf_map.o ktf_test.o ktf_debugfs.o ktf_cov.o \ + ktf_override.o ktf_netctx.o + diff --git a/tools/testing/selftests/ktf/kernel/ktf.h b/tools/testing/selftests/ktf/kernel/ktf.h new file mode 100644 index 0000000..ea270e7 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf.h @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf.h: Defines the KTF user API for kernel clients + */ +#ifndef _KTF_H +#define _KTF_H + +#include +#include +#include +#include +#include "ktf_test.h" +#include "ktf_override.h" +#include "ktf_map.h" +#include "ktf_unlproto.h" + +#define KTF_MAX_LOG 2048 + +/* Type for an optional configuration callback for contexts. + * Implementations should copy and store data into their private + * extensions of the context structure. The data pointer is + * only valid inside the callback: + */ +typedef int (*ktf_config_cb)(struct ktf_context *ctx, const void* data, size_t data_sz); +typedef void (*ktf_context_cb)(struct ktf_context *ctx); + +struct ktf_context_type; + +struct ktf_context { + struct ktf_map_elem elem; /* Linkage for ctx_map in handle */ + char name[KTF_MAX_KEY]; /* Context name used in map */ + struct ktf_handle *handle; /* Owner of this context */ + ktf_config_cb config_cb; /* Optional configuration callback */ + ktf_context_cb cleanup; /* Optional callback upon context release */ + int config_errno; /* If config_cb set: state of configuration */ + struct ktf_context_type *type; /* Associated type, must be set */ +}; + +typedef struct ktf_context* (*ktf_context_alloc)(struct ktf_context_type *ct); + +struct ktf_context_type { + struct ktf_map_elem elem; /* Linkage for map in handle */ + char name[KTF_MAX_KEY]; /* Context type name */ + struct ktf_handle *handle; /* Owner of this context type */ + ktf_context_alloc alloc; /* Allocate a new context of this type */ + ktf_config_cb config_cb; /* Configuration callback */ + ktf_context_cb cleanup; /* Optional callback upon context release */ +}; + +#include "ktf_netctx.h" + +/* type for a test function */ +struct ktf_test; + +/* state of running test, used to pass to threads spawned by test. */ +struct ktf_test_state; + +struct ktf_thread { + int (*func)(void *); + const char *name; + struct task_struct *task; + struct ktf_test_state state; + struct completion started; + struct completion completed; +}; + +typedef void (*ktf_test_adder)(void); + +/* Generic setup function for client modules */ +void ktf_add_tests(ktf_test_adder f); +int ktf_context_add(struct ktf_handle *handle, struct ktf_context* ctx, + const char* name, ktf_config_cb cfg_cb, const char *type); +struct ktf_context *ktf_context_add_from(struct ktf_handle *handle, const char *name, + struct ktf_context_type *ct); +const char *ktf_context_name(struct ktf_context *ctx); +struct ktf_context* ktf_find_context(struct ktf_handle *handle, const char* name); +struct ktf_context *ktf_find_first_context(struct ktf_handle *handle); +struct ktf_context *ktf_find_next_context(struct ktf_context* ctx); +void ktf_context_remove(struct ktf_context *ctx); +size_t ktf_has_contexts(struct ktf_handle *handle); +void ktf_context_remove_all(struct ktf_handle *handle); + +/* Called by framework when a configuration is supplied, + * returns the return value of the configuration callback. + */ +int ktf_context_set_config(struct ktf_context *ctx, const void* data, size_t data_sz); + +struct ktf_context *ktf_find_create_context(struct ktf_handle *handle, const char *name, + const char *type_name); +int ktf_handle_add_ctx_type(struct ktf_handle *handle, struct ktf_context_type *ct); +struct ktf_context_type *ktf_handle_get_ctx_type(struct ktf_handle *handle, + const char *type_name); + +/* Declare the implicit __test_handle as extern for .c files that use it + * when adding tests with ADD_TEST but where definition is in another .c file: + */ +extern struct ktf_handle __test_handle; + +/* Add/remove/find a context to/from the default handle */ +#define KTF_CONTEXT_ADD(__context, name) \ + ktf_context_add(&__test_handle, __context, name, NULL, "default") +#define KTF_CONTEXT_ADD_CFG(__context, name, __cb, __type_name) \ + ktf_context_add(&__test_handle, __context, name, __cb, __type_name) +#define KTF_CONTEXT_FIND(name) ktf_find_context(&__test_handle, name) +#define KTF_CONTEXT_GET(name, type) \ + container_of(KTF_CONTEXT_FIND(name), type, k) + +/* Add/remove/find a context to/from a given handle */ +#define KTF_CONTEXT_ADD_TO(__handle, __context, name) \ + ktf_context_add(&__handle, __context, name, NULL, "default") +#define KTF_CONTEXT_ADD_TO_CFG(__handle, __context, name, __cb, __type_name) \ + ktf_context_add(&__handle, __context, name, __cb, __type_name) +#define KTF_CONTEXT_FIND_IN(__handle, name) ktf_find_context(&__handle, name) +#define KTF_CONTEXT_GET_IN(__handle, name, type) \ + container_of(KTF_CONTEXT_FIND_IN(__handle, name), type, k) + +/* check if a context has been configured (if needed) */ +#define KTF_CONTEXT_CFG_OK(__context) \ + (__context->config_cb && !__context->config_errno) +#define KTF_CONTEXT_REMOVE(__context) ktf_context_remove(__context) + +/* Part of KTF support for hybrid tests: Safe get the out-of-band user data + * Silently return (ignoring the test) if no data is available. + * This is to avoid failing if a generic user program without + * specific support for the hybrid test attempts to run the test. + * Fail if an object of an unexpected size is provided. + */ +#define KTF_USERDATA(__kt_ptr, __priv_datatype, __priv_data) \ + struct __priv_datatype *__priv_data = (struct __priv_datatype *)__kt_ptr->data; \ + if (!__priv_data) return; \ + ASSERT_LONG_EQ(__kt_ptr->data_sz, sizeof(struct __priv_datatype)) + +/* KTF support for entry/return probes (via kprobes kretprobes). We use + * kretprobes as they support entry/return and do not induce panics when + * mixed with gkdb usage. + */ + +#if (defined(CONFIG_KPROBES) && defined(CONFIG_KRETPROBES) && \ + (defined(CONFIG_X86_64) || defined(CONFIG_ARM) || \ + defined(CONFIG_ARM64) || defined(CONFIG_SPARC))) +#define KTF_PROBE_SUPPORT +#else +static inline int ktf_no_probe_support(void) +{ + twarn("No support for k[ret]probes, or platform not supported.") + return -ENOTSUPP); +} +#endif + +/* Entry/return probe - type is handler type (entry_handler for entry, + * handler for return), func is function to be probed; probehandler is name + * of probe handling function we will invoke on entry/return. + */ +#define KTF_PROBE(type, func, probehandler) \ + static int probehandler(struct kretprobe_instance *, struct pt_regs *);\ + static struct kretprobe __ktf_##type##_##probehandler = { \ + .type = probehandler, \ + .data_size = 0, \ + .maxactive = 0, \ + .kp = { \ + .symbol_name = #func, \ + }, \ + }; \ + static int probehandler(struct kretprobe_instance *ri, \ + struct pt_regs *regs) + +#ifdef KTF_PROBE_SUPPORT +#define KTF_REGISTER_PROBE(type, func, probehandler) \ + register_kretprobe(&__ktf_##type##_##probehandler) +#else +#define KTF_REGISTER_PROBE(type, func, probehandler) \ + ktf_no_probe_support() +#endif + +/* Note on the complexity below - to re-use a statically-defined kretprobe for + * registration, we need to clean up state in the struct kretprobe. Hence + * we zero out the kretprobe and re-set the symbol name/handler. Not doing + * this means that re-registering fails with -EINVAL. + */ +#define KTF_UNREGISTER_PROBE(type, func, probehandler) \ + do { \ + unregister_kretprobe(&__ktf_##type##_##probehandler); \ + memset(&__ktf_##type##_##probehandler, 0, \ + sizeof(struct kretprobe)); \ + __ktf_##type##_##probehandler.kp.symbol_name = #func; \ + __ktf_##type##_##probehandler.type = probehandler; \ + } while (0) + +#define KTF_ENTRY_PROBE(func, probehandler) \ + KTF_PROBE(entry_handler, func, probehandler) + +#define KTF_REGISTER_ENTRY_PROBE(func, probehandler) \ + KTF_REGISTER_PROBE(entry_handler, func, probehandler) + +/* arch-specific calling conventions for kretprobes entry handlers. Define + * more args/architectures if needed. + */ +#ifdef KTF_PROBE_SUPPORT +#ifdef CONFIG_X86_64 +#define KTF_ENTRY_PROBE_ARG0 (regs->di) +#define KTF_ENTRY_PROBE_ARG1 (regs->si) +#endif /* CONFIG_X86_64 */ +#ifdef CONFIG_ARM +#define KTF_ENTRY_PROBE_ARG0 (regs->regs[0]) +#define KTF_ENTRY_PROBE_ARG1 (regs->regs[1]) +#endif /* CONFIG_ARM */ +#ifdef CONFIG_ARM64 +#define KTF_ENTRY_PROBE_ARG0 (regs->regs[0]) +#define KTF_ENTRY_PROBE_ARG1 (regs->regs[1]) +#endif /* CONFIG_ARM64 */ +#ifdef CONFIG_SPARC +#define KTF_ENTRY_PROBE_ARG0 (regs->u_regs[UREG_I0]) +#define KTF_ENTRY_PROBE_ARG1 (regs->u_regs[UREG_I1]) +#endif /* CONFIG_SPARC */ +#endif /* KTF_PROBE_SUPPORT */ + +/* For unsupported platforms. */ +#ifndef KTF_ENTRY_PROBE_ARG0 +#define KTF_ENTRY_PROBE_ARG0 (0) +#define KTF_ENTRY_PROBE_ARG1 (1) +#endif + +#define KTF_ENTRY_PROBE_RETURN(retval) \ + do { \ + return retval; \ + } while (0) + +#define KTF_UNREGISTER_ENTRY_PROBE(func, probehandler) \ + KTF_UNREGISTER_PROBE(entry_handler, func, probehandler) + +/* KTF support for return probes (via kprobes kretprobes) */ +#define KTF_RETURN_PROBE(func, probehandler) \ + KTF_PROBE(handler, func, probehandler) + +#define KTF_REGISTER_RETURN_PROBE(func, probehandler) \ + KTF_REGISTER_PROBE(handler, func, probehandler) + +/* KTF_*RETURN_VALUE() definitions for use within KTF_RETURN_PROBE() {} only. */ + +#define KTF_RETURN_VALUE() regs_return_value(regs) + +#ifdef KTF_PROBE_SUPPORT +#ifdef CONFIG_X86_64 +#define KTF_SET_RETURN_VALUE(value) regs->ax = (value) +#endif /* CONFIG_X86_64 */ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +#define KTF_SET_RETURN_VALUE(value) regs->regs[0] = (value) +#endif /* CONFIG_ARM[64] */ +#if defined(CONFIG_SPARC) +#define KTF_SET_RETURN_VALUE(value) regs->u_regs[UREG_I0] = (value) +#endif /* CONFIG_SPARC */ +#endif /* KTF_PROBE_SUPPORT */ + +/* For unsupported platforms. */ +#ifndef KTF_PROBE_SUPPORT +#define KTF_SET_RETURN_VALUE(value) do { } while (0) +#endif /* KTF_PROBE_SUPPORT */ + +#define KTF_UNREGISTER_RETURN_PROBE(func, probehandler) \ + KTF_UNREGISTER_PROBE(handler, func, probehandler) + +#define KTF_OVERRIDE(func, probehandler) \ + static int probehandler(struct kprobe *, struct pt_regs *);\ + static struct kprobe __ktf_override_##probehandler = { \ + .symbol_name = #func, \ + .pre_handler = probehandler, \ + .post_handler = ktf_post_handler, \ + .fault_handler = NULL, \ + .flags = 0, \ + }; \ + static int probehandler(struct kprobe *kp, struct pt_regs *regs) + +#ifdef KTF_PROBE_SUPPORT +#define KTF_REGISTER_OVERRIDE(func, probehandler) \ + ktf_register_override(&__ktf_override_##probehandler) +#else +#define KTF_REGISTER_OVERRIDE(func, probehandler) \ + ktf_no_probe_support() +#endif + +#define KTF_UNREGISTER_OVERRIDE(func, probehandler) \ + do { \ + unregister_kprobe(&__ktf_override_##probehandler); \ + memset(&__ktf_override_##probehandler, 0, \ + sizeof(struct kprobe)); \ + __ktf_override_##probehandler.symbol_name = #func; \ + __ktf_override_##probehandler.pre_handler = probehandler; \ + __ktf_override_##probehandler.post_handler = ktf_post_handler; \ + } while (0) + + +#define KTF_OVERRIDE_RETURN \ + do { \ + ktf_override_function_with_return(regs); \ + return 1; \ + } while (0) + +#ifdef KTF_PROBE_SUPPORT +#define KTF_SET_INSTRUCTION_POINTER(regs, value) \ + instruction_pointer_set(regs, (value)) +#else +#define KTF_SET_INSTRUCTION_POINTER(regs, value) do { } while (0) +#endif + +/* Interfaces for creating kthreads in tests. */ +#define KTF_THREAD_INIT(threadname, t) \ + do { \ + (t)->func = threadname; \ + (t)->name = #threadname; \ + (t)->state.self = self; \ + (t)->state.ctx = ctx; \ + (t)->state.iter = _i; \ + (t)->state.value = _value; \ + init_completion(&((t)->started)); \ + init_completion(&((t)->completed)); \ + } while (0) + +#define KTF_THREAD_RUN(t) \ + ((t)->task = kthread_run((t)->func, t, (t)->name)) + +#define KTF_THREAD_STOP(t) \ + do { \ + if ((t)->task) \ + kthread_stop((t)->task); \ + } while (0) + +/* Wraps thread execution to supply same variables as test case - this allows + * us to define assertions etc in thread context. + */ +#define KTF_THREAD(name) \ + static void __##name(struct ktf_thread *thread, struct ktf_test *self, \ + struct ktf_context *ctx, int _i, u32 _value); \ + static int name(void *data) \ + { \ + struct ktf_thread *t = data; \ + complete(&t->started); \ + __##name(t, t->state.self, t->state.ctx, t->state.iter, \ + t->state.value); \ + complete(&t->completed); \ + return 0; \ + } \ + static void __##name(struct ktf_thread *_thread, struct ktf_test *self,\ + struct ktf_context *ctx, int _i, u32 _value) + +#define KTF_THREAD_WAIT_STARTED(t) (wait_for_completion(&((t)->started))) +#define KTF_THREAD_WAIT_COMPLETED(t) (wait_for_completion(&((t)->completed))) + +u32 ktf_get_assertion_count(void); + +/** + * ASSERT_TRUE() - fail and return if @C evaluates to false + * @C: Boolean expression to evaluate + * + */ +#define ASSERT_TRUE(C) do { \ + if (!ktf_assert((C))) return; \ + } while (0) + +/** + * ASSERT_FALSE() - fail and return if @C evaluates to true + * @C: Boolean expression to evaluate + */ +#define ASSERT_FALSE(C) do { \ + if (!ktf_assert(!(C))) return; \ + } while (0) + +/** + * ASSERT_TRUE_GOTO() - fail and jump to @_lbl if @C evaluates to false + * @C: Boolean expression to evaluate + * @_lbl: Label to jump to in case of failure + */ +#define ASSERT_TRUE_GOTO(C,_lbl) { \ + if (!ktf_assert((C))) goto _lbl;\ +} + +/** + * ASSERT_FALSE_GOTO() - fail and jump to @_lbl if @C evaluates to true + * @C: Boolean expression to evaluate + * @_lbl: Label to jump to in case of failure + */ +#define ASSERT_FALSE_GOTO(C,_lbl) { \ + if (!ktf_assert(!(C))) goto _lbl;\ +} + +/** + * ASSERT_TRUE_RETVAL() - fail and return @V if @C evaluates to false + * @C: Boolean expression to evaluate + * @V: Value to return on failure + */ +#define ASSERT_TRUE_RETVAL(C, V) do { \ + if (!ktf_assert((C))) return V; \ +} while (0) + +/** + * ASSERT_FALSE() - fail and return @V if @C evaluates to true + * @C: Boolean expression to evaluate + * @V: Value to return on failure + */ +#define ASSERT_FALSE_RETVAL(C, V) do { \ + if (!ktf_assert(!(C))) return V; \ +} while (0) + +/** + * ASSERT_TRUE_CONT() - fail and continue if @C evaluates to false + * @C: Boolean expression to evaluate + */ +#define ASSERT_TRUE_CONT(C) { \ + if (!ktf_assert((C))) continue; \ +} + +/** + * ASSERT_FALSE_CONT() - fail and continue if @C evaluates to true + * @C: Boolean expression to evaluate + */ +#define ASSERT_FALSE_CONT(C) { \ + if (!ktf_assert(!(C))) continue; \ +} + +/** + * ASSERT_TRUE_BREAK() - fail and break if @C evaluates to false + * @C: Boolean expression to evaluate + */ +#define ASSERT_TRUE_BREAK(C) { \ + if (!ktf_assert((C))) break; \ +} + +/** + * ASSERT_FALSE_BREAK() - fail and break if @C evaluates to true + * @C: Boolean expression to evaluate + */ +#define ASSERT_FALSE_BREAK(C) { \ + if (!ktf_assert(!(C))) break; \ +} + +/** + * ASSERT_LONG_EQ() - compare two longs, fail and return if @X != @Y + * @X: Expected value + * @Y: Actual value + */ +#define ASSERT_LONG_EQ(X, Y) \ + ktf_assert_long_ret(X, ==, Y); + +#define ASSERT_LONG_NE(X, Y) \ + ktf_assert_long_ret(X, !=, Y); + +#define ASSERT_ADDR_EQ(X, Y) \ + ktf_assert_long_ret((u64)(X), ==, (u64)(Y)); + +#define ASSERT_ADDR_NE(X, Y) \ + ktf_assert_long_ret((u64)(X), !=, (u64)(Y)); + +#define ASSERT_INT_EQ(X, Y) \ + ktf_assert_int_ret(X, ==, Y); + +#define ASSERT_INT_GT(X, Y) \ + ktf_assert_int_ret(X, >, Y); + +/** + * ASSERT_LONG_EQ() - compare two longs, jump to @_lbl if @X != @Y + * @X: Expected value + * @Y: Actual value + * @_lbl: Label to jump to in case of failure + */ +#define ASSERT_LONG_EQ_GOTO(X, Y, _lbl) \ + ktf_assert_long_goto(X, ==, Y, _lbl) + +#define ASSERT_LONG_NE_GOTO(X, Y, _lbl) \ + ktf_assert_long_goto(X, !=, Y, _lbl) + +#define ASSERT_ADDR_EQ_GOTO(X, Y, _lbl) \ + ktf_assert_long_goto((u64)(X), ==, (u64)(Y), _lbl) + +#define ASSERT_ADDR_NE_GOTO(X, Y, _lbl) \ + ktf_assert_long_goto((u64)(X), !=, (u64)(Y), _lbl) + +#define ASSERT_INT_EQ_GOTO(X, Y, _lbl) \ + ktf_assert_int_goto(X, ==, Y, _lbl) + +#define ASSERT_INT_GE_GOTO(X, Y, _lbl) \ + ktf_assert_int_goto(X, >=, Y, _lbl) + +#define ASSERT_INT_GT_GOTO(X, Y, _lbl) \ + ktf_assert_int_goto(X, >, Y, _lbl) + +#define ASSERT_INT_LT_GOTO(X, Y, _lbl) \ + ktf_assert_int_goto(X, <, Y, _lbl) + +#define ASSERT_INT_NE(X,Y) \ + ktf_assert_int_ret(X, !=, Y); + +#define ASSERT_INT_NE_GOTO(X,Y,_lbl) \ + ktf_assert_int_goto(X, !=, Y, _lbl); + +/** + * EXPECT_TRUE() - fail if @C evaluates to false but allow test to continue + * @C: Boolean expression to evaluate + * + */ +#define EXPECT_TRUE(C) ktf_assert(C) +#define EXPECT_FALSE(C) ktf_assert(!(C)) + +#define OK_ADDR(X) (X && !IS_ERR(X)) + +/* Valid kernel address check */ +#define EXPECT_OK_ADDR(X) \ + ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X)) + +#define ASSERT_OK_ADDR(X) do { \ + if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - value 0x%Lx", (X))) \ + return; \ + } while (0) +#define ASSERT_OK_ADDR_GOTO(X,_lbl) do { \ + if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X))) \ + goto _lbl; \ + } while (0) + +#define ASSERT_OK_ADDR_BREAK(X) do { \ + if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X))) \ + break; \ + } while (0) + +#define EXPECT_INT_EQ(X,Y) ktf_assert_int(X, ==, Y) +#define EXPECT_INT_GT(X,Y) ktf_assert_int(X, >, Y) +#define EXPECT_INT_GE(X,Y) ktf_assert_int(X, >=, Y) +#define EXPECT_INT_LE(X,Y) ktf_assert_int(X, <=, Y) +#define EXPECT_INT_LT(X,Y) ktf_assert_int(X, <, Y) +#define EXPECT_INT_NE(X,Y) ktf_assert_int(X, !=, Y) + +#define EXPECT_LONG_EQ(X, Y) ktf_assert_long(X, ==, Y) +#define EXPECT_LONG_NE(X, Y) ktf_assert_long(X, !=, Y) +#define EXPECT_ADDR_EQ(X, Y) ktf_assert_long((u64)(X), ==, (u64)(Y)) +#define EXPECT_ADDR_NE(X, Y) ktf_assert_long((u64)(X), !=, (u64)(Y)) +#define EXPECT_LONG_GT(X, Y) ktf_assert_long(X, >, Y) +#define EXPECT_LONG_GE(X, Y) ktf_assert_long(X, >=, Y) +#define EXPECT_LONG_LE(X, Y) ktf_assert_long(X, <=, Y) +#define EXPECT_LONG_LT(X, Y) ktf_assert_long(X, <, Y) + +#define EXPECT_STREQ(X, Y) ktf_assert_str_eq(X, Y) +#define EXPECT_STRNE(X, Y) ktf_assert_str_ne(X, Y) + +extern ulong ktf_debug_mask; + +/* Defined debug bits - higher values should represent more + * verbose categories: + */ +#define T_INFO 0x1 +#define T_LIST 0x2 +#define T_INTR 0x200 +#define T_INFO_V 0x800 +#define T_DEBUG 0x1000 +#define T_MCAST 0x2000 +#define T_TRACE 0x100000 +#define T_DEBUG_V 0x200000 + +#define tlog(class, format, arg...) \ + do { \ + if (unlikely((ktf_debug_mask) & (class))) \ + printk(KERN_INFO \ + "ktf pid [%d] " "%s: " format "\n", \ + current->pid, __func__, \ + ## arg); \ + } while (0) +#define twarn(format, arg...) \ + do { \ + printk(KERN_WARNING \ + "ktf pid [%d] " "%s: " format "\n", \ + current->pid, __func__, \ + ## arg); \ + } while (0) +#define terr(format, arg...) \ + do { \ + printk(KERN_ERR \ + "ktf pid [%d] " "%s: " format "\n", \ + current->pid, __func__, \ + ## arg); \ + } while (0) +#define tlogs(class, stmt_list) \ + do { \ + if (unlikely((ktf_debug_mask) & (class))) { \ + stmt_list;\ + } \ + } while (0) + + +/* Look up the current address of a potentially local symbol - to allow testing + * against it. NB! This is a hack for unit testing internal unexposed interfaces and + * violates the module boundaries and has no fw/bw comp gauarantees, but are + * still very useful for detailed unit testing complex logic: + */ +void* ktf_find_symbol(const char *mod, const char *sym); + +unsigned long ktf_symbol_size(unsigned long addr); + +#define ktf_resolve_symbol(mname, sname) \ + do { \ + sname = ktf_find_symbol(#mname, #sname); \ + if (!sname) \ + return -ENOENT; \ + } while (0) +#endif diff --git a/tools/testing/selftests/ktf/kernel/ktf_context.c b/tools/testing/selftests/ktf/kernel/ktf_context.c new file mode 100644 index 0000000..5ff57a9 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_context.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * kft_context.c: Main part of ktf kernel module that implements a generic + * unit test framework for tests written in kernel code, with support for + * gtest (googletest) user space tools for invocation and reporting. + */ + +#include +#include +#include +#include "ktf.h" +#include "ktf_test.h" +#include "ktf_debugfs.h" +#include "ktf_nl.h" + +MODULE_LICENSE("GPL"); + +ulong ktf_debug_mask = T_INFO; +EXPORT_SYMBOL(ktf_debug_mask); + +static unsigned int ktf_context_maxid; + +/* The role of context_lock is to synchronize modifications to + * the global list of context handles (handles that have contexts + * associated with them) and the context map. + * The map object has it's own locking, but must be kept in sync + * with changes to the global context list: + */ +static DEFINE_SPINLOCK(context_lock); + +/* global linked list of all ktf_handle objects that have contexts */ +LIST_HEAD(context_handles); + +module_param_named(debug_mask, ktf_debug_mask, ulong, 0644); + +static int __ktf_handle_add_ctx_type(struct ktf_handle *handle, + struct ktf_context_type *ct, + bool generic) +{ + unsigned long flags; + int ret; + + if (generic && !(ct->alloc && ct->config_cb)) { + terr("Mandatory configuration callbacks or values missing!"); + return -EINVAL; + } + + ct->handle = handle; + ktf_map_elem_init(&ct->elem, ct->name); + + spin_lock_irqsave(&context_lock, flags); + ret = ktf_map_insert(&handle->ctx_type_map, &ct->elem); + spin_unlock_irqrestore(&context_lock, flags); + return ret; +} + +static int __ktf_context_add(struct ktf_handle *handle, struct ktf_context *ctx, + const char *name, ktf_config_cb cfg_cb, + struct ktf_context_type *ct) +{ + unsigned long flags; + int ret; + + ktf_map_elem_init(&ctx->elem, name); + strncpy(ctx->name, name, KTF_MAX_NAME); + ctx->config_cb = cfg_cb; + ctx->config_errno = ENOENT; /* 0 here means configuration is ok */ + ctx->type = ct; + ctx->cleanup = ct->cleanup; + + spin_lock_irqsave(&context_lock, flags); + ret = ktf_map_insert(&handle->ctx_map, &ctx->elem); + if (!ret) { + ctx->handle = handle; + if (ktf_map_size(&handle->ctx_map) == 1) { + handle->id = ++ktf_context_maxid; + INIT_LIST_HEAD(&handle->handle_list); + list_add(&handle->handle_list, &context_handles); + } + } + spin_unlock_irqrestore(&context_lock, flags); + if (!ret) + tlog(T_DEBUG, "added %scontext %s with type %s", + (cfg_cb ? "configurable " : ""), name, ct->name); + return ret; +} + +int ktf_context_add(struct ktf_handle *handle, struct ktf_context *ctx, + const char *name, ktf_config_cb cfg_cb, + const char *type_name) +{ + struct ktf_context_type *ct = ktf_handle_get_ctx_type(handle, type_name); + int ret; + + if (!ct) { + ct = kzalloc(sizeof(*ct), GFP_KERNEL); + if (!ct) + return -ENOMEM; + strncpy(ct->name, type_name, KTF_MAX_KEY); + ret = __ktf_handle_add_ctx_type(handle, ct, false); + if (ret) { + kfree(ct); + return ret; + } + } + return __ktf_context_add(handle, ctx, name, cfg_cb, ct); +} +EXPORT_SYMBOL(ktf_context_add); + +struct ktf_context *ktf_context_add_from(struct ktf_handle *handle, const char *name, + struct ktf_context_type *ct) +{ + struct ktf_context *ctx; + int ret; + + if (!ct->alloc) { + terr("No alloc function supplied!"); + return NULL; + } + ctx = ct->alloc(ct); + if (!ctx) + return NULL; + ret = __ktf_context_add(handle, ctx, name, ct->config_cb, ct); + if (ret) + goto fail; + + ctx->cleanup = ct->cleanup; + return ctx; +fail: + kfree(ctx); + return NULL; +} +EXPORT_SYMBOL(ktf_context_add_from); + +int ktf_context_set_config(struct ktf_context *ctx, const void *data, size_t data_sz) +{ + int ret; + + if (ctx->config_cb) { + ret = ctx->config_cb(ctx, data, data_sz); + ctx->config_errno = ret; + } + return ctx->config_errno; +} +EXPORT_SYMBOL(ktf_context_set_config); + +const char *ktf_context_name(struct ktf_context *ctx) +{ + return ctx->elem.key; +} +EXPORT_SYMBOL(ktf_context_name); + +void ktf_context_remove(struct ktf_context *ctx) +{ + struct ktf_handle *handle; + unsigned long flags = 0; + + if (!ctx) { + terr("A test case tried to remove an invalid context!"); + return; + } + handle = ctx->handle; + + spin_lock_irqsave(&context_lock, flags); + ktf_map_remove(&handle->ctx_map, ctx->elem.key); + if (!ktf_has_contexts(handle)) + list_del(&handle->handle_list); + spin_unlock_irqrestore(&context_lock, flags); + + tlog(T_DEBUG, "removed context %s at %p", ctx->elem.key, ctx); + + if (ctx->cleanup) + ctx->cleanup(ctx); + /* Note: ctx may be freed here! */ +} +EXPORT_SYMBOL(ktf_context_remove); + +struct ktf_context *ktf_find_first_context(struct ktf_handle *handle) +{ + struct ktf_map_elem *elem = ktf_map_find_first(&handle->ctx_map); + + if (elem) + return container_of(elem, struct ktf_context, elem); + return NULL; +} + +struct ktf_context *ktf_find_context(struct ktf_handle *handle, const char *name) +{ + struct ktf_map_elem *elem; + + if (!name) + return NULL; + elem = ktf_map_find(&handle->ctx_map, name); + if (!elem) + return NULL; + return container_of(elem, struct ktf_context, elem); +} +EXPORT_SYMBOL(ktf_find_context); + +struct ktf_context *ktf_find_create_context(struct ktf_handle *handle, const char *name, + const char *type_name) +{ + struct ktf_context *ctx = ktf_find_context(handle, name); + + if (!ctx) { + struct ktf_context_type *ct = ktf_handle_get_ctx_type(handle, type_name); + + tlog(T_DEBUG, "type = %s, ct = %p", type_name, ct); + if (ct) + ctx = ktf_context_add_from(handle, name, ct); + } + return ctx; +} + +struct ktf_context *ktf_find_next_context(struct ktf_context *ctx) +{ + struct ktf_map_elem *elem = ktf_map_find_next(&ctx->elem); + + return container_of(elem, struct ktf_context, elem); +} + +size_t ktf_has_contexts(struct ktf_handle *handle) +{ + return ktf_map_size(&handle->ctx_map) > 0; +} +EXPORT_SYMBOL(ktf_has_contexts); + +void ktf_context_remove_all(struct ktf_handle *handle) +{ + struct ktf_context *curr; + + if (!ktf_has_contexts(handle)) + return; + + for (;;) { + curr = ktf_find_first_context(handle); + if (!curr) + break; + ktf_context_remove(curr); + } +} +EXPORT_SYMBOL(ktf_context_remove_all); + +/* Find the handle associated with handle id hid */ +struct ktf_handle *ktf_handle_find(int hid) +{ + struct ktf_handle *handle = NULL; + + list_for_each_entry(handle, &context_handles, handle_list) { + if (handle->id == hid) + break; + } + return handle; +} + +/* Allow user space to create new contexts of certain types + * based on configuration types. This allocates a new, uniquely named + * context type to enable it for user space usage. Caller must allocate and populate + * @ct with appropriate callbacks and value for the context type. + */ + +int ktf_handle_add_ctx_type(struct ktf_handle *handle, + struct ktf_context_type *ct) +{ + return __ktf_handle_add_ctx_type(handle, ct, true); +} +EXPORT_SYMBOL(ktf_handle_add_ctx_type); + +struct ktf_context_type *ktf_handle_get_ctx_type(struct ktf_handle *handle, + const char *type_name) +{ + struct ktf_map_elem *elem = ktf_map_find(&handle->ctx_type_map, type_name); + + tlog(T_DEBUG, "Lookup %s in map size %lu = %p\n", type_name, + ktf_map_size(&handle->ctx_type_map), elem); + if (!elem) + return NULL; + return container_of(elem, struct ktf_context_type, elem); +} + +void ktf_handle_cleanup_check(struct ktf_handle *handle) +{ + struct ktf_context *curr; + unsigned long flags; + + if (!ktf_has_contexts(handle)) + return; + + spin_lock_irqsave(&context_lock, flags); + + for (curr = ktf_find_first_context(handle); + curr; + curr = ktf_find_next_context(curr)) { + twarn("context %s found during handle %p cleanup", curr->elem.key, handle); + } + spin_unlock_irqrestore(&context_lock, flags); +} +EXPORT_SYMBOL(ktf_handle_cleanup_check); + +struct ktf_kernel_internals { + /* From module.h: Look up a module symbol - supports syntax module:name */ + unsigned long (*module_kallsyms_lookup_name)(const char *name); + /* From kallsyms.h: Look up a symbol w/size and offset */ + unsigned long (*kallsyms_lookup_size_offset)(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset); +}; + +static struct ktf_kernel_internals ki; + +static int __init ktf_init(void) +{ + int ret; + char *ks = "module_kallsyms_lookup_name"; + + /* We rely on being able to resolve this symbol for looking up module + * specific internal symbols (multiple modules may define the same symbol): + */ + ki.module_kallsyms_lookup_name = (void *)kallsyms_lookup_name(ks); + if (!ki.module_kallsyms_lookup_name) { + terr("Unable to look up \"%s\" in kallsyms - maybe interface has changed?", + ks); + return -EINVAL; + } + ks = "kallsyms_lookup_size_offset"; + ki.kallsyms_lookup_size_offset = (void *)kallsyms_lookup_name(ks); + if (!ki.kallsyms_lookup_size_offset) { + terr("Unable to look up \"%s\" in kallsyms - maybe interface has changed?", + ks); + return -EINVAL; + } + + ktf_debugfs_init(); + ret = ktf_nl_register(); + if (ret) { + terr("Unable to register protocol with netlink"); + ktf_debugfs_cleanup(); + goto failure; + } + + return 0; +failure: + return ret; +} + +static void __exit ktf_exit(void) +{ + ktf_nl_unregister(); + ktf_cleanup(); +} + +/* Generic setup function for client modules */ +void ktf_add_tests(ktf_test_adder f) +{ + f(); +} +EXPORT_SYMBOL(ktf_add_tests); + +/* Support for looking up kernel/module internal symbols to enable testing. + * A NULL mod means either we want the kernel-internal symbol or don't care + * which module the symbol is in. + */ +void *ktf_find_symbol(const char *mod, const char *sym) +{ + char sm[200]; + const char *symref; + unsigned long addr = 0; + + if (mod) { + sprintf(sm, "%s:%s", mod, sym); + symref = sm; + } else { + /* Try for kernel-internal symbol first; fall back to modules + * if that fails. + */ + symref = sym; + addr = kallsyms_lookup_name(symref); + } + if (!addr) + addr = ki.module_kallsyms_lookup_name(symref); + if (addr) { + tlog(T_DEBUG, "Found %s at %0lx\n", sym, addr); + } else { +#ifndef CONFIG_KALLSYMS_ALL + twarn("CONFIG_KALLSYMS_ALL is not set, so non-exported symbols are not available\n"); +#endif + tlog(T_INFO, "Fatal error: %s not found\n", sym); + return NULL; + } + return (void *)addr; +} +EXPORT_SYMBOL(ktf_find_symbol); + +unsigned long ktf_symbol_size(unsigned long addr) +{ + unsigned long size = 0; + + (void)ki.kallsyms_lookup_size_offset(addr, &size, NULL); + + return size; +} +EXPORT_SYMBOL(ktf_symbol_size); + +module_init(ktf_init); +module_exit(ktf_exit); diff --git a/tools/testing/selftests/ktf/kernel/ktf_test.c b/tools/testing/selftests/ktf/kernel/ktf_test.c new file mode 100644 index 0000000..1e287d0 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_test.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_test.c: Kernel side code for tracking and reporting ktf test results + */ +#include +#include +#include "ktf_test.h" +#include +#include + +#include "ktf_nl.h" +#include "ktf_unlproto.h" +#include "ktf.h" +#include "ktf_cov.h" +#include "ktf_debugfs.h" + +#define MAX_PRINTF 4096 + +/* Versioning check: + * For MAJOR or MINOR changes, both sides are required to + * have the same version. + * If MICRO has changed, some new functionality may have been added, but the + * old functionality should work as before. + * With only BUILD changes, the two versions are still compatible, + * but one might have bug fixes or minor enhancements. + */ +int ktf_version_check(u64 version) +{ + if (version != KTF_VERSION_LATEST) { + if (KTF_VERSION(MAJOR, version) == KTF_VERSION(MAJOR, KTF_VERSION_LATEST) && + KTF_VERSION(MINOR, version) == KTF_VERSION(MINOR, KTF_VERSION_LATEST)) + return 0; + terr("KTF version mismatch - expected %llu.%llu.%llu.%llu, got %llu.%llu.%llu.%llu", + KTF_VERSION(MAJOR, KTF_VERSION_LATEST), + KTF_VERSION(MINOR, KTF_VERSION_LATEST), + KTF_VERSION(MICRO, KTF_VERSION_LATEST), + KTF_VERSION(BUILD, KTF_VERSION_LATEST), + KTF_VERSION(MAJOR, version), + KTF_VERSION(MINOR, version), + KTF_VERSION(MICRO, version), + KTF_VERSION(BUILD, version)); + return -EINVAL; + } + return 0; +} + +static int ktf_handle_version_check(struct ktf_handle *th) +{ + return ktf_version_check(th->version); +} + +/* Function called when global references to test case reach 0. */ +static void ktf_case_free(struct ktf_map_elem *elem) +{ + struct ktf_case *tc = container_of(elem, struct ktf_case, kmap); + + kfree(tc); +} + +void ktf_case_get(struct ktf_case *tc) +{ + ktf_map_elem_get(&tc->kmap); +} + +void ktf_case_put(struct ktf_case *tc) +{ + ktf_map_elem_put(&tc->kmap); +} + +/* The global map from name to ktf_case */ +DEFINE_KTF_MAP(test_cases, NULL, ktf_case_free); + +/* a lock to protect this datastructure */ +static DEFINE_MUTEX(tc_lock); + +/* Current total number of test cases defined */ +size_t ktf_case_count(void) +{ + return ktf_map_size(&test_cases); +} + +const char *ktf_case_name(struct ktf_case *tc) +{ + return tc->kmap.key; +} + +static size_t ktf_case_test_count(struct ktf_case *tc) +{ + return ktf_map_size(&tc->tests); +} + +/* Called when test refcount reaches 0. */ +static void ktf_test_free(struct ktf_map_elem *elem) +{ + struct ktf_test *t = container_of(elem, struct ktf_test, kmap); + + kfree(t->log); + kfree(t); +} + +void ktf_test_get(struct ktf_test *t) +{ + ktf_map_elem_get(&t->kmap); +} + +void ktf_test_put(struct ktf_test *t) +{ + ktf_map_elem_put(&t->kmap); +} + +static struct ktf_case *ktf_case_create(const char *name) +{ + struct ktf_case *tc = kmalloc(sizeof(*tc), GFP_KERNEL); + int ret; + + if (!tc) + return tc; + + /* Initialize test case map of tests. */ + ktf_map_init(&tc->tests, NULL, ktf_test_free); + ret = ktf_map_elem_init(&tc->kmap, name); + if (ret) { + kfree(tc); + return NULL; + } + ktf_debugfs_create_testset(tc); + tlog(T_DEBUG, "ktf: Added test set %s\n", name); + return tc; +} + +struct ktf_case *ktf_case_find(const char *name) +{ + return ktf_map_find_entry(&test_cases, name, struct ktf_case, kmap); +} + +/* Returns with case refcount increased. Called with tc_lock held. */ +static struct ktf_case *ktf_case_find_create(const char *name) +{ + struct ktf_case *tc; + int ret = 0; + + tc = ktf_case_find(name); + if (!tc) { + tc = ktf_case_create(name); + if (tc) { + ret = ktf_map_insert(&test_cases, &tc->kmap); + if (ret) { + kfree(tc); + tc = NULL; + } + } + } + return tc; +} + +static atomic_t assert_cnt = ATOMIC_INIT(0); + +void flush_assert_cnt(struct ktf_test *self) +{ + if (atomic_read(&assert_cnt)) { + tlog(T_DEBUG, "update: %d asserts", atomic_read(&assert_cnt)); + if (self->skb) + nla_put_u32(self->skb, KTF_A_STAT, atomic_read(&assert_cnt)); + atomic_set(&assert_cnt, 0); + } +} + +u32 ktf_get_assertion_count(void) +{ + return atomic_read(&assert_cnt); +} +EXPORT_SYMBOL(ktf_get_assertion_count); + +static DEFINE_SPINLOCK(assert_lock); + +long _ktf_assert(struct ktf_test *self, int result, const char *file, + int line, const char *fmt, ...) +{ + int len; + va_list ap; + char *buf; + char bufprefix[256]; + unsigned long flags; + + if (result) { + atomic_inc(&assert_cnt); + } else { + flush_assert_cnt(self); + buf = kmalloc(MAX_PRINTF, GFP_KERNEL); + if (!buf) { + terr("file %s line %d: Unable to allocate memory for the error report!", + file, line); + goto out; + } + va_start(ap, fmt); + len = vsnprintf(buf, MAX_PRINTF - 1, fmt, ap); + buf[len] = 0; + va_end(ap); + if (self->skb) { + nla_put_u32(self->skb, KTF_A_STAT, result); + nla_put_string(self->skb, KTF_A_FILE, file); + nla_put_u32(self->skb, KTF_A_NUM, line); + nla_put_string(self->skb, KTF_A_STR, buf); + } + (void)snprintf(bufprefix, sizeof(bufprefix) - 1, + "file %s line %d: result %d: ", file, line, + result); + terr("%s%s", bufprefix, buf); + + /* Multiple threads may try to update log */ + spin_lock_irqsave(&assert_lock, flags); + (void)strncat(self->log, bufprefix, KTF_MAX_LOG); + (void)strncat(self->log, buf, KTF_MAX_LOG); + spin_unlock_irqrestore(&assert_lock, flags); + kfree(buf); + } +out: + return result; +} +EXPORT_SYMBOL(_ktf_assert); + +/* Add a test to a testcase: + * Tests are represented by ktf_test objects that are linked into + * a per-test case map TCase:tests map. + */ +void _ktf_add_test(struct __test_desc td, struct ktf_handle *th, + int _signal, int allowed_exit_value, + int start, int end) +{ + struct ktf_case *tc = NULL; + struct ktf_test *t; + char *log; + + if (ktf_handle_version_check(th)) + return; + + log = kzalloc(KTF_MAX_LOG, GFP_KERNEL); + if (!log) + return; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) { + kfree(log); + return; + } + t->tclass = td.tclass; + t->name = td.name; + t->fun = td.fun; + t->start = start; + t->end = end; + t->handle = th; + t->log = log; + + mutex_lock(&tc_lock); + tc = ktf_case_find_create(td.tclass); + if (!tc || ktf_map_elem_init(&t->kmap, td.name) || + ktf_map_insert(&tc->tests, &t->kmap)) { + terr("Failed to add test %s from %s to test case \"%s\"", + td.name, td.file, td.tclass); + if (tc) + ktf_case_put(tc); + mutex_unlock(&tc_lock); + kfree(log); + kfree(t); + return; + } + + ktf_debugfs_create_test(t); + + tlog(T_LIST, "Added test \"%s.%s\" start = %d, end = %d\n", + td.tclass, td.name, start, end); + + /* Now since we no longer reference tc/t outside of global map of test + * cases and per-testcase map of tests, drop their refcounts. This + * is safe to do as refcounts are > 0 due to references for map + * storage and debugfs. + */ + ktf_test_put(t); + ktf_case_put(tc); + mutex_unlock(&tc_lock); +} +EXPORT_SYMBOL(_ktf_add_test); + +void ktf_run_hook(struct sk_buff *skb, struct ktf_context *ctx, + struct ktf_test *t, u32 value, + void *oob_data, size_t oob_data_sz) +{ + int i; + + t->log[0] = '\0'; + t->skb = skb; + t->data = oob_data; + t->data_sz = oob_data_sz; + for (i = t->start; i < t->end; i++) { + if (!ctx && t->handle->require_context) { + terr("Test %s.%s requires a context, but none configured!", + t->tclass, t->name); + continue; + } + /* No need to bump refcnt, this is just for debugging. Nothing + * should reference the testcase via the handle's current test + * pointer. + */ + t->handle->current_test = t; + tlogs(T_DEBUG, + printk(KERN_INFO "Running test %s.%s", t->tclass, t->name); + if (ctx) + printk("_%s", ktf_context_name(ctx)); + printk("[%d:%d]\n", t->start, t->end); + ); + getnstimeofday(&t->lastrun); + t->fun(t, ctx, i, value); + flush_assert_cnt(t); + } + t->handle->current_test = NULL; +} + +/* Clean up all tests associated with a ktf_handle */ + +void ktf_test_cleanup(struct ktf_handle *th) +{ + struct ktf_test *t; + struct ktf_case *tc; + + /* Clean up tests which are associated with this handle. + * It's possible multiple modules contribute tests to a test case, + * so we can't just do this on a per-testcase basis. + */ + mutex_lock(&tc_lock); + + tc = ktf_map_first_entry(&test_cases, struct ktf_case, kmap); + while (tc) { + /* FIXME - this is inefficient. */ + t = ktf_map_first_entry(&tc->tests, struct ktf_test, kmap); + while (t) { + if (t->handle == th) { + tlog(T_DEBUG, "ktf: delete test %s.%s", + t->tclass, t->name); + /* removes ref for debugfs */ + ktf_debugfs_destroy_test(t); + /* removes ref for testset map of tests */ + ktf_map_remove_elem(&tc->tests, &t->kmap); + /* now remove our reference which we get + * from ktf_map_[first|next]_entry(). + * This final reference should result in + * the test being freed. + */ + ktf_test_put(t); + /* Need to reset to root */ + t = ktf_map_first_entry(&tc->tests, + struct ktf_test, kmap); + } else { + t = ktf_map_next_entry(t, kmap); + } + } + /* If no modules have tests for this test case, we can + * free resources safely. + */ + if (ktf_case_test_count(tc) == 0) { + ktf_debugfs_destroy_testset(tc); + ktf_map_remove_elem(&test_cases, &tc->kmap); + ktf_case_put(tc); + tc = ktf_map_first_entry(&test_cases, struct ktf_case, + kmap); + } else { + tc = ktf_map_next_entry(tc, kmap); + } + } + mutex_unlock(&tc_lock); +} +EXPORT_SYMBOL(ktf_test_cleanup); + +int ktf_cleanup(void) +{ + struct ktf_test *t; + struct ktf_case *tc; + + ktf_cov_cleanup(); + + /* Unloading of dependencies means we should have no testcases/tests. */ + mutex_lock(&tc_lock); + ktf_for_each_testcase(tc) { + twarn("(memory leak) test set %s still active at unload!", ktf_case_name(tc)); + ktf_testcase_for_each_test(t, tc) { + twarn("(memory leak) test set %s still active with test %s at unload!", + ktf_case_name(tc), t->name); + } + return -EBUSY; + } + ktf_debugfs_cleanup(); + mutex_unlock(&tc_lock); + return 0; +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_test.h b/tools/testing/selftests/ktf/kernel/ktf_test.h new file mode 100644 index 0000000..9769664 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_test.h @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2001, 2002, Arien Malec + * Copyright (C) 2011, 2017, Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.1 + * + * This file originates from check.h from the Check C unit test + * framework, adapted by Knut Omang to build with the linux kernel. + */ + +#ifndef KTF_TEST_H +#define KTF_TEST_H + +#include +#include +#include "ktf_map.h" +#include "ktf_unlproto.h" + +/* A test context is an extendable object that a test client module + * can supply, and that all tests will be invoked with as an implicit + * 'ctx' argument: + */ +struct ktf_context; + +struct ktf_test; + +typedef void (*ktf_test_fun) (struct ktf_test *, struct ktf_context* tdev, int, u32); + +struct ktf_debugfs { + struct dentry *debugfs_results_testset; + struct dentry *debugfs_results_test; + struct dentry *debugfs_run_testset; + struct dentry *debugfs_run_test; +}; + +struct ktf_test { + struct ktf_map_elem kmap; /* linkage for test case list */ + const char* tclass; /* test class name */ + const char* name; /* Name of the test */ + ktf_test_fun fun; + int start; /* Start and end value to argument to fun */ + int end; /* Defines number of iterations */ + struct sk_buff *skb; /* sk_buff for recording assertion results */ + char *log; /* per-test log */ + void *data; /* Test specific out-of-band data */ + size_t data_sz; /* Size of the data element, if set */ + struct timespec lastrun; /* last time test was run */ + struct ktf_debugfs debugfs; /* debugfs info for test */ + struct ktf_handle *handle; /* Handler for owning module */ +}; + +struct ktf_case { + struct ktf_map_elem kmap; /* Linkage for ktf_map */ + struct ktf_map tests; /* List of tests to run */ + struct ktf_debugfs debugfs; /* debugfs handles for testset */ +}; + +/* Used for tests that spawn kthreads to pass state. We should probably + * look at passing data to tests like this to make things more extensible, + * but will defer for now as this would disrupt KTF consumers. + */ +struct ktf_test_state { + struct ktf_test *self; + struct ktf_context *ctx; + int iter; + u32 value; +}; + +extern struct ktf_map test_cases; + +/* Current total number of test cases defined */ +size_t ktf_case_count(void); +const char *ktf_case_name(struct ktf_case *); +/* Manage test case refcount. */ +void ktf_case_get(struct ktf_case *); +void ktf_case_put(struct ktf_case *); + +int ktf_version_check(u64 version); + +void ktf_run_hook(struct sk_buff *skb, struct ktf_context *ctx, + struct ktf_test *t, u32 value, + void *oob_data, size_t oob_data_sz); +void flush_assert_cnt(struct ktf_test *self); + +/* Representation of a test case (a group of tests) */ +struct ktf_case; + +struct ktf_case *ktf_case_find(const char *name); + +/* Each module client of the test framework is required to + * declare at least one ktf_handle via the macro + * DECLARE_KTF_HANDLE (below) + * If the module require extra data of some sorts, that + * can be embedded within the handle + */ +struct ktf_handle; + +/* Find the handle associated with handle id hid */ +struct ktf_handle *ktf_handle_find(int hid); + +/* Called upon ktf unload to clean up test cases */ +int ktf_cleanup(void); + +/* The list of handles that have contexts associated with them */ +extern struct list_head context_handles; + +struct __test_desc +{ + const char* tclass; /* Test class name */ + const char* name; /* Test name */ + const char* file; /* File that implements test */ + ktf_test_fun fun; +}; + +/* Manage refcount for tests. */ +void ktf_test_get(struct ktf_test *t); +void ktf_test_put(struct ktf_test *t); + +/* Add a test function to a test case for a given handle (macro version) */ +#define ktf_add_test_to(td, __test_handle) \ + _ktf_add_test(td##_setup, &__test_handle, 0, 0, 0, 1) + +/* Add a test function to a test case (macro version) */ +#define ktf_add_test(td) \ + _ktf_add_test(td##_setup, &__test_handle, 0, 0, 0, 1) + +/* Add a looping test function to a test case (macro version) + + The test will be called in a for(i = s; i < e; i++) loop with each + iteration being executed in a new context. The loop variable 'i' is + available in the test. + */ +#define ktf_add_loop_test(td,s,e) \ + _ktf_add_test(td##_setup, &__test_handle, 0,0,(s),(e)) + +/* Add a test function to a test case + (function version -- use this when the macro won't work +*/ +void _ktf_add_test(struct __test_desc td, struct ktf_handle *th, + int _signal, int allowed_exit_value, int start, int end); + +/* Internal function to mark the start of a test function */ +void ktf_fn_start (const char *fname, const char *file, int line); + +/* Add a test previously created with TEST() or TEST_F() */ +#define ADD_TEST(__testname)\ + ktf_add_test(__testname) + +#define ADD_TEST_TO(__handle, __testname) \ + ktf_add_test_to(__testname, __handle) + +#define ADD_LOOP_TEST(__testname, from, to) \ + ktf_add_loop_test(__testname, from, to) + +/* Remove a test previously added with ADD_TEST */ +#define DEL_TEST(__testname)\ + ktf_del_test(__testname) + +/* Iterate over all test cases. Implicitly bumps refcount for pos and + * decreases it after we iterate past it. + */ +#define ktf_for_each_testcase(pos) \ + ktf_map_for_each_entry(pos, &test_cases, kmap) + +/* Iterate over all tests for testcases. Implicitly bumps refcount for pos + * and decreases it again after we iterate past it. + */ +#define ktf_testcase_for_each_test(pos, tc) \ + ktf_map_for_each_entry(pos, &tc->tests, kmap) + +#define KTF_GEN_TYPEID_MAX 3 + +/* A test_handle identifies the calling module: + * Declare one in the module global scope using + * KTF_INIT() or KTF_HANDLE_INIT() + * and call KTF_CLEANUP() or KTF_HANDLE_CLEANUP() upon unload + */ + +struct ktf_handle { + struct list_head handle_list; /* Linkage for the global list of all handles with context */ + struct ktf_map ctx_type_map; /* a map from type_id to ktf_context_type (see ktf_context.c) */ + struct ktf_map ctx_map; /* a (possibly empty) map from name to context for this handle */ + unsigned int id; /* A unique nonzero ID for this handle, set iff contexts */ + bool require_context; /* If set, tests are only valid if a context is provided */ + u64 version; /* version assoc. with handle */ + struct ktf_test *current_test;/* Current test running */ +}; + +void ktf_test_cleanup(struct ktf_handle *th); +void ktf_handle_cleanup_check(struct ktf_handle *handle); +void ktf_cleanup_check(void); + +#define KTF_HANDLE_INIT_VERSION(__test_handle, __version, __need_ctx) \ + struct ktf_handle __test_handle = { \ + .handle_list = LIST_HEAD_INIT(__test_handle.handle_list), \ + .ctx_type_map = __KTF_MAP_INITIALIZER(__test_handle, NULL, NULL), \ + .ctx_map = __KTF_MAP_INITIALIZER(__test_handle, NULL, NULL), \ + .id = 0, \ + .require_context = __need_ctx, \ + .version = __version, \ + }; + +#define KTF_HANDLE_INIT(__test_handle) \ + KTF_HANDLE_INIT_VERSION(__test_handle, KTF_VERSION_LATEST, false) + +#define KTF_INIT() KTF_HANDLE_INIT(__test_handle) + +#define KTF_HANDLE_CTX_INIT(__test_handle) \ + KTF_HANDLE_INIT_VERSION(__test_handle, KTF_VERSION_LATEST, true) + +#define KTF_CTX_INIT() KTF_HANDLE_CTX_INIT(__test_handle) + +#define KTF_HANDLE_CLEANUP(__test_handle) \ + do { \ + ktf_context_remove_all(&__test_handle); \ + ktf_test_cleanup(&__test_handle); \ + } while (0) + +#define KTF_CLEANUP() KTF_HANDLE_CLEANUP(__test_handle) + +/* Start a unit test with TEST(suite_name,unit_name) +*/ +#define TEST(__testsuite, __testname)\ + static void __testname(struct ktf_test *self, struct ktf_context *ctx, \ + int _i, u32 _value); \ + struct __test_desc __testname##_setup = \ + { .tclass = "" # __testsuite "", .name = "" # __testname "",\ + .fun = __testname, .file = __FILE__ }; \ + \ + static void __testname(struct ktf_test *self, struct ktf_context* ctx, \ + int _i, u32 _value) + +/* Start a unit test using a fixture + * NB! Note the intentionally missing start parenthesis on DECLARE_F! + * + * Prep: + * DECLARE_F(fixture_name) + * + * }; + * INIT_F(fixture_name,setup,teardown); + * + * Usage: + * SETUP_F(fixture_name,setup) + * { + * ok to true to have the test executed> + * } + * TEARDOWN_F(fixture_name,teardown) + * { + * + * } + * TEST_F(fixture_name,suite_name,test_name) + * { + * + * } + * + * setup must set ctx->ok to true to have the test itself executed + */ +#define DECLARE_F(__fixture) \ + struct __fixture { \ + void (*setup) (struct ktf_test *, struct ktf_context *, struct __fixture *); \ + void (*teardown) (struct ktf_test *, struct __fixture *); \ + bool ok; + +#define INIT_F(__fixture,__setup,__teardown) \ + void __setup(struct ktf_test *, struct ktf_context *, struct __fixture *); \ + void __teardown(struct ktf_test *, struct __fixture *); \ + static struct __fixture __fixture##_template = {\ + .setup = __setup, \ + .teardown = __teardown, \ + .ok = false,\ + } + +#define SETUP_F(__fixture, __setup) \ + void __setup(struct ktf_test *self, struct ktf_context *ctx, \ + struct __fixture *__fixture) + +#define TEARDOWN_F(__fixture, __teardown) \ + void __teardown(struct ktf_test *self, struct __fixture *__fixture) + +#define TEST_F(__fixture, __testsuite, __testname) \ + static void __testname##_body(struct ktf_test *, struct __fixture *, \ + int, u32); \ + static void __testname(struct ktf_test *, struct ktf_context *, int, \ + u32); \ + struct __test_desc __testname##_setup = \ + { .tclass = "" # __testsuite "", .name = "" # __testname "", \ + .fun = __testname }; \ + \ + static void __testname(struct ktf_test *self, struct ktf_context* ctx, \ + int _i, u32 _value) \ + { \ + struct __fixture f_ctx = __fixture##_template; \ + f_ctx.ok = false; \ + f_ctx.setup(self, ctx, &f_ctx); \ + if (!f_ctx.ok) return; \ + __testname##_body(self, &f_ctx, _i, _value); \ + f_ctx.teardown(self,&f_ctx); \ + } \ + static void __testname##_body(struct ktf_test *self, struct __fixture *ctx, \ + int _i, u32 _value) + +/* Fail the test case unless expr is true */ +/* The space before the comma sign before ## is essential to be compatible + with gcc 2.95.3 and earlier. +*/ +#define ktf_assert_msg(expr, format, ...) \ + _ktf_assert(self, expr, __FILE__, __LINE__, \ + format , ## __VA_ARGS__, NULL) + +#define ktf_assert(expr, ...)\ + _ktf_assert(self, expr, __FILE__, __LINE__, \ + "Failure '"#expr"' occurred " , ## __VA_ARGS__, NULL) + +/* Always fail */ +#define ktf_fail(...) _ktf_assert(self, 0, __FILE__, __LINE__, "Failed" , ## __VA_ARGS__, NULL) + +/* Non-macro version of ktf_assert, with more complicated interface + * returns nonzero if ok, 0 otherwise + */ +long _ktf_assert (struct ktf_test *self, int result, const char *file, + int line, const char *expr, ...); + +/* Integer comparsion macros with improved output compared to ktf_assert(). */ +/* O may be any comparion operator. */ +#define ktf_assert_int_goto(X, O, Y, _lbl) \ + do { int x = (X); int y = (Y);\ + if (!ktf_assert_msg(x O y, \ + "Assertion '"#X#O#Y"' failed: "#X"==0x%x, "#Y"==0x%x", x, y)) \ + goto _lbl;\ + } while (0) + +#define ktf_assert_int(X, O, Y) \ + do { int x = (X); int y = (Y);\ + ktf_assert_msg(x O y,\ + "Assertion '"#X#O#Y"' failed: "#X"==0x%x, "#Y"==0x%x", x, y);\ + } while (0) + +#define ktf_assert_int_ret(X, O, Y)\ + do { int x = (X); int y = (Y);\ + if (!ktf_assert_msg(x O y, \ + "Assertion '"#X#O#Y"' failed: "#X"==0x%lx, "#Y"==0x%lx", x, y)) \ + return;\ + } while (0) + +#define ktf_assert_long_goto(X, O, Y, _lbl) \ + do { long x = (X); long y = (Y);\ + if (!ktf_assert_msg(x O y, \ + "Assertion '"#X#O#Y"' failed: "#X"==0x%lx, "#Y"==0x%lx", x, y)) \ + goto _lbl;\ + } while (0) + +#define ktf_assert_long_ret(X, O, Y)\ + do { long x = (X); long y = (Y);\ + if (!ktf_assert_msg(x O y, \ + "Assertion '"#X#O#Y"' failed: "#X"==0x%lx, "#Y"==0x%lx", x, y)) \ + return;\ + } while (0) + +/* O may be any comparion operator. */ +#define ktf_assert_long(X, O, Y) \ + do { long x = (X); long y = (Y);\ + ktf_assert_msg(x O y,\ + "Assertion '"#X#O#Y"' failed: "#X"==0x%lx, "#Y"==0x%lx", x, y);\ + } while (0) + +/* String comparsion macros with improved output compared to ktf_assert() */ +#define ktf_assert_str_eq(X, Y) \ + do { const char* x = (X); const char* y = (Y);\ + ktf_assert_msg(strcmp(x,y) == 0,\ + "Assertion '"#X"=="#Y"' failed: "#X"==\"%s\", "#Y"==\"%s\"",\ + x, y);\ + } while (0) + +#define ktf_assert_str_ne(X, Y) \ + do { const char* x = (X); const char* y = (Y);\ + ktf_assert_msg(strcmp(x,y) != 0,\ + "Assertion '"#X"!="#Y"' failed: "#X"==\"%s\", "#Y"==\"%s\"",\ + x, y);\ + } while (0) + +#endif /* KTF_TEST_H */ From patchwork Tue Aug 13 06:09:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091209 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F302713AC for ; Tue, 13 Aug 2019 06:12:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E3AD327FE4 for ; Tue, 13 Aug 2019 06:12:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D811F28587; Tue, 13 Aug 2019 06:12:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 88CF927FE4 for ; Tue, 13 Aug 2019 06:12:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727582AbfHMGL0 (ORCPT ); Tue, 13 Aug 2019 02:11:26 -0400 Received: from aserp2120.oracle.com ([141.146.126.78]:58794 "EHLO aserp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727160AbfHMGLZ (ORCPT ); Tue, 13 Aug 2019 02:11:25 -0400 Received: from pps.filterd (aserp2120.oracle.com [127.0.0.1]) by aserp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68qma039301; Tue, 13 Aug 2019 06:11:03 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=Uy/BMiU/6+cWYwyuGulp2z1PvG2gjuEr4h5V5AOFnps=; b=bseU9hdVmB1A+Dk0d5EgItfE7Bqp6KFA0P5q2Ti0ZdmhBaBM7sd9z+8V8odxDX3t99o0 QFzmuKVv1TOjCmFHJlV09W+OdFFzPRrkUG7CQhBK6FWaBKNSts0XtX4Ff/735efVONc0 2BgPL3ENvQQSK2N8pPV9gG/SPdMIrGH627ux5ZImBz40p8EHwvI6hgiT5ZzYO85HqJiT G7PHNX3gUvji4nWjny/XSq8A7UUet/kLSUVVqlcXCxMKiQDYlN8MtlLidZlNF4rrYUa5 EjI7vHoRs7gql8mltjSsLqfB25V+vbDlzot5Kl0ypfH+Vzh0eA47vihvJvcp7ERyUUuJ Kg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=Uy/BMiU/6+cWYwyuGulp2z1PvG2gjuEr4h5V5AOFnps=; b=f4LOi+gT1LbC4u+uMdpH+QRodUD4BB8Ss1ST/KzYabW4UZElZLcSbCe0M94B/uvtOXl3 NJnlWXWuWYiciU6BCkGQPtaeFOG+4gKu5U00nYijXsHXVFsQUiY55wDfVjs1O7Ca0Wag zvwmopqO2LWKdUm5Yd0tBD+glqmSq8s23HrsTZCu8iifOPfhwe80fDi7fbVCwcgN4Uaf wsh0VVSA+iO4w8RN6c1UmeFdWiqyHDQFdTi4BIW0UXK7vnM/plQKI7hwOhEKI5OpbfYN A19lWWfwmY1jZLpACUSPsowNVhyzbpPGvCc6iU2jLU+Rd01sMV0JKPUbaetEqJgTdps0 pQ== Received: from aserp3020.oracle.com (aserp3020.oracle.com [141.146.126.70]) by aserp2120.oracle.com with ESMTP id 2u9nvp41a6-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:03 +0000 Received: from pps.filterd (aserp3020.oracle.com [127.0.0.1]) by aserp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67SsC157036; Tue, 13 Aug 2019 06:11:03 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by aserp3020.oracle.com with ESMTP id 2u9nrenkff-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:03 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6B23C026398; Tue, 13 Aug 2019 06:11:02 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:01 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 03/19] ktf: Introduce a generic netlink protocol for test result communication Date: Tue, 13 Aug 2019 08:09:18 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The generic netlink protocol used to communicate between kernel and user space about tests and test results, as well as some means for configuring tests within the kernel. Unlike other kernel side test code in the kernel, ktf does not print anything from inside the kernel (except for optional debugging features to help "internal" debugging of ktf or ktf tests). Instead all test results are communicated back to the user space frontend, which decides how to do the reporting. ktf_nl.c: ktf netlink protocol implementation ktf_unlproto.h: implements interfaces for user-kernel netlink interactions Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/kernel/ktf_nl.c | 516 +++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_nl.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_unlproto.h | 105 +++- 3 files changed, 636 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_nl.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_nl.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_unlproto.h diff --git a/tools/testing/selftests/ktf/kernel/ktf_nl.c b/tools/testing/selftests/ktf/kernel/ktf_nl.c new file mode 100644 index 0000000..56d5f3b --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_nl.c @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_nl.c: ktf netlink protocol implementation + */ +#include +#include +#include +#define NL_INTERNAL 1 +#include "ktf_unlproto.h" +#include "ktf_test.h" +#include "ktf_nl.h" +#include "ktf.h" +#include "ktf_cov.h" + +/* Generic netlink support to communicate with user level + * test framework. + */ + +/* Callback functions defined below */ +static int ktf_run(struct sk_buff *skb, struct genl_info *info); +static int ktf_query(struct sk_buff *skb, struct genl_info *info); +static int ktf_req(struct sk_buff *skb, struct genl_info *info); +static int ktf_resp(struct sk_buff *skb, struct genl_info *info); +static int ktf_cov_cmd(enum ktf_cmd_type type, struct sk_buff *skb, + struct genl_info *info); +static int ktf_ctx_cfg(struct sk_buff *skb, struct genl_info *info); +static int send_version_only(struct sk_buff *skb, struct genl_info *info); + +/* operation definition */ +static struct genl_ops ktf_ops[] = { + { + .cmd = KTF_C_REQ, + .flags = 0, + .doit = ktf_req, + .dumpit = NULL, + }, + { + .cmd = KTF_C_RESP, + .flags = 0, + .doit = ktf_resp, + .dumpit = NULL, + } +}; + +/* family definition */ +static struct genl_family ktf_gnl_family = { + .module = THIS_MODULE, + .hdrsize = 0, + .name = "ktf", + .version = 1, + .maxattr = KTF_A_MAX + 4, + .policy = ktf_gnl_policy, + .ops = ktf_ops, + .n_ops = ARRAY_SIZE(ktf_ops), +}; + +/* handler, returns 0 on success, negative + * values on failure. It doesn't make much difference + * what error values are used, as they are anyway discarded + * at the netlink level, but do result in a nonzero return + * from nl_wait_for_ack() in user space. + */ +static int ktf_req(struct sk_buff *skb, struct genl_info *info) +{ + enum ktf_cmd_type type; + u64 version; + + /* Dispatch on type of request */ + + if (!info->attrs[KTF_A_TYPE] || !info->attrs[KTF_A_VERSION]) { + terr("received netlink msg with no type/version!"); + return -EINVAL; + } + + version = nla_get_u64(info->attrs[KTF_A_VERSION]); + if (ktf_version_check(version)) { + /* a query is the first call for any reasonable application: + * Respond to it with a version only: + */ + if (nla_get_u32(info->attrs[KTF_A_TYPE]) == KTF_CT_QUERY) + return send_version_only(skb, info); + return -EINVAL; + } + + type = nla_get_u32(info->attrs[KTF_A_TYPE]); + switch (type) { + case KTF_CT_QUERY: + return ktf_query(skb, info); + case KTF_CT_RUN: + return ktf_run(skb, info); + case KTF_CT_COV_ENABLE: + case KTF_CT_COV_DISABLE: + return ktf_cov_cmd(type, skb, info); + case KTF_CT_CTX_CFG: + return ktf_ctx_cfg(skb, info); + default: + terr("received netlink msg with invalid type (%d)", type); + } + return -EINVAL; +} + +/* Reply with just version information to let user space report the issue: */ +static int send_version_only(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *resp_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + void *data; + int retval = 0; + + if (!resp_skb) + return -ENOMEM; + data = genlmsg_put_reply(resp_skb, info, &ktf_gnl_family, + 0, KTF_C_RESP); + if (!data) { + retval = -ENOMEM; + goto resp_failure; + } + nla_put_u32(resp_skb, KTF_A_TYPE, KTF_CT_QUERY); + nla_put_u64_64bit(resp_skb, KTF_A_VERSION, KTF_VERSION_LATEST, 0); + + /* Recompute message header */ + genlmsg_end(resp_skb, data); + + retval = genlmsg_reply(resp_skb, info); +resp_failure: + /* Free buffer if failure */ + if (retval) + nlmsg_free(resp_skb); + return retval; +} + +/* Send data about one testcase */ +static int send_test_data(struct sk_buff *resp_skb, struct ktf_case *tc) +{ + struct nlattr *nest_attr; + struct ktf_test *t; + int stat; + int cnt = 0; + + stat = nla_put_string(resp_skb, KTF_A_STR, ktf_case_name(tc)); + if (stat) + return stat; + + nest_attr = nla_nest_start(resp_skb, KTF_A_TEST); + ktf_testcase_for_each_test(t, tc) { + cnt++; + /* A test is not valid if the handle requires a context and none is present */ + if (t->handle->id) { + stat = nla_put_u32(resp_skb, KTF_A_HID, t->handle->id); + if (stat) + goto fail; + } else if (t->handle->require_context) { + continue; + } + stat = nla_put_string(resp_skb, KTF_A_STR, t->name); + if (stat) + goto fail; + } + nla_nest_end(resp_skb, nest_attr); + tlog(T_DEBUG, "Sent data about %d tests", cnt); + return 0; +fail: + twarn("Failed with status %d after sending data about %d tests", stat, cnt); + /* we hold reference to t here - drop it! */ + ktf_test_put(t); + return stat; +} + +static int send_handle_data(struct sk_buff *resp_skb, struct ktf_handle *handle) +{ + struct ktf_context_type *ct; + struct nlattr *nest_attr; + struct ktf_context *ctx; + int stat; + + tlog(T_DEBUG, "Sending context handle %d: ", handle->id); + + /* Send HID */ + stat = nla_put_u32(resp_skb, KTF_A_HID, handle->id); + if (stat) + return stat; + + /* Send contexts */ + nest_attr = nla_nest_start(resp_skb, KTF_A_LIST); + if (!nest_attr) + return -ENOMEM; + + tlog(T_DEBUG, "Sending context type list"); + /* Send any context types that user space are allowed to create contexts for */ + ktf_map_for_each_entry(ct, &handle->ctx_type_map, elem) { + if (ct->alloc) { + stat = nla_put_string(resp_skb, KTF_A_FILE, ct->name); + if (stat) + return -ENOMEM; + } + } + + /* Then send all the contexts themselves */ + ctx = ktf_find_first_context(handle); + while (ctx) { + nla_put_string(resp_skb, KTF_A_STR, ktf_context_name(ctx)); + if (ctx->config_cb) { + stat = nla_put_string(resp_skb, KTF_A_MOD, ctx->type->name); + if (stat) + return stat; + stat = nla_put_u32(resp_skb, KTF_A_STAT, ctx->config_errno); + if (stat) + return stat; + } + ctx = ktf_find_next_context(ctx); + } + nla_nest_end(resp_skb, nest_attr); + return 0; +} + +static int ktf_query(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *resp_skb; + void *data; + int retval = 0; + struct nlattr *nest_attr; + struct ktf_handle *handle; + struct ktf_case *tc; + + /* No options yet, just send a response */ + resp_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!resp_skb) + return -ENOMEM; + + data = genlmsg_put_reply(resp_skb, info, &ktf_gnl_family, + 0, KTF_C_RESP); + if (!data) { + retval = -ENOMEM; + goto resp_failure; + } + + nla_put_u64_64bit(resp_skb, KTF_A_VERSION, KTF_VERSION_LATEST, 0); + + /* Add all test sets to the report + * We send test info as follows: + * KTF_CT_QUERY hid1 [context1 [context2 ...]] hid2 [context1 [context2 ...]] + * testset_num [testset1 [name1 name2 ..] testset2 [name1 name2 ..]] + * Handle IDs without contexts are not present + */ + if (!nla_put_u32(resp_skb, KTF_A_TYPE, KTF_CT_QUERY)) { + if (!list_empty(&context_handles)) { + /* Traverse list of handles with contexts */ + nest_attr = nla_nest_start(resp_skb, KTF_A_HLIST); + list_for_each_entry(handle, &context_handles, handle_list) { + retval = send_handle_data(resp_skb, handle); + if (retval) + goto resp_failure; + } + nla_nest_end(resp_skb, nest_attr); + } + + /* Send total number of tests */ + tlog(T_DEBUG, "Total #of test cases: %ld", ktf_case_count()); + nla_put_u32(resp_skb, KTF_A_NUM, ktf_case_count()); + nest_attr = nla_nest_start(resp_skb, KTF_A_LIST); + if (!nest_attr) { + retval = -ENOMEM; + goto resp_failure; + } + ktf_for_each_testcase(tc) { + retval = send_test_data(resp_skb, tc); + if (retval) { + retval = -ENOMEM; + goto resp_failure; + } + } + nla_nest_end(resp_skb, nest_attr); + } + + /* Recompute message header */ + genlmsg_end(resp_skb, data); + + retval = genlmsg_reply(resp_skb, info); +resp_failure: + if (retval) + twarn("Message failure (status %d)", retval); + /* Free buffer if failure */ + if (retval) + nlmsg_free(resp_skb); + return retval; +} + +static int ktf_run_func(struct sk_buff *skb, const char *ctxname, + const char *setname, const char *testname, + u32 value, void *oob_data, size_t oob_data_sz) +{ + struct ktf_case *testset = ktf_case_find(setname); + struct ktf_test *t; + int tn = 0; + + if (!testset) { + tlog(T_INFO, "No such testset \"%s\"\n", setname); + return -EFAULT; + } + + /* Execute test functions */ + ktf_testcase_for_each_test(t, testset) { + if (t->fun && strcmp(t->name, testname) == 0) { + struct ktf_context *ctx = ktf_find_context(t->handle, ctxname); + + ktf_run_hook(skb, ctx, t, value, oob_data, oob_data_sz); + } else if (!t->fun) { + tlog(T_DEBUG, "** no function for test %s.%s **", t->tclass, t->name); + } + tn++; + } + tlog(T_DEBUG, "Set %s contained %d tests", ktf_case_name(testset), tn); + ktf_case_put(testset); + return 0; +} + +static int ktf_run(struct sk_buff *skb, struct genl_info *info) +{ + u32 value = 0; + struct sk_buff *resp_skb; + void *data; + int retval = 0; + struct nlattr *nest_attr, *data_attr; + char ctxname_store[KTF_MAX_NAME + 1]; + char *ctxname = ctxname_store; + char setname[KTF_MAX_NAME + 1]; + char testname[KTF_MAX_NAME + 1]; + void *oob_data = NULL; + size_t oob_data_sz = 0; + + if (info->attrs[KTF_A_STR]) + nla_strlcpy(ctxname, info->attrs[KTF_A_STR], KTF_MAX_NAME); + else + ctxname = NULL; + + if (!info->attrs[KTF_A_SNAM]) { + terr("received KTF_CT_RUN msg without testset name!"); + return -EINVAL; + } + nla_strlcpy(setname, info->attrs[KTF_A_SNAM], KTF_MAX_NAME); + + if (!info->attrs[KTF_A_TNAM]) { /* Test name wo/context */ + terr("received KTF_CT_RUN msg without test name!"); + return -EINVAL; + } + nla_strlcpy(testname, info->attrs[KTF_A_TNAM], KTF_MAX_NAME); + + if (info->attrs[KTF_A_NUM]) { + /* Using NUM field as optional u32 input parameter to test */ + value = nla_get_u32(info->attrs[KTF_A_NUM]); + } + + data_attr = info->attrs[KTF_A_DATA]; + if (data_attr) { + /* User space sends out-of-band data: */ + oob_data = nla_memdup(data_attr, GFP_KERNEL); + oob_data_sz = nla_len(data_attr); + } + + tlog(T_DEBUG, "Request for testset %s, test %s\n", setname, testname); + + /* Start building a response */ + resp_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!resp_skb) + return -ENOMEM; + + data = genlmsg_put_reply(resp_skb, info, &ktf_gnl_family, + 0, KTF_C_RESP); + if (!data) { + retval = -ENOMEM; + goto put_fail; + } + + nla_put_u32(resp_skb, KTF_A_TYPE, KTF_CT_RUN); + nest_attr = nla_nest_start(resp_skb, KTF_A_LIST); + retval = ktf_run_func(resp_skb, ctxname, setname, testname, value, oob_data, oob_data_sz); + nla_nest_end(resp_skb, nest_attr); + nla_put_u32(resp_skb, KTF_A_STAT, retval); + + /* Recompute message header */ + genlmsg_end(resp_skb, data); + + retval = genlmsg_reply(resp_skb, info); + if (!retval) + tlog(T_DEBUG, "Sent reply for test %s.%s\n", setname, testname); + else + twarn("Failed to send reply for test %s.%s - value %d", + setname, testname, retval); + + kfree(oob_data); +put_fail: + /* Free buffer if failure */ + if (retval) + nlmsg_free(resp_skb); + return retval; +} + +static int ktf_resp(struct sk_buff *skb, struct genl_info *info) +{ + /* not to expect this message here */ + terr("unexpected netlink RESP msg received"); + return 0; +} + +static int ktf_cov_cmd(enum ktf_cmd_type type, struct sk_buff *skb, + struct genl_info *info) +{ + char *cmd = type == KTF_CT_COV_ENABLE ? "COV_ENABLE" : "COV_DISABLE"; + char module[KTF_MAX_NAME + 1]; + struct sk_buff *resp_skb; + int retval = 0; + void *data; + u32 opts = 0; + + if (!info->attrs[KTF_A_MOD]) { + terr("received KTF_CT_%s msg without module name!", cmd); + return -EINVAL; + } + nla_strlcpy(module, info->attrs[KTF_A_MOD], KTF_MAX_NAME); + if (info->attrs[KTF_A_COVOPT]) + opts = nla_get_u32(info->attrs[KTF_A_COVOPT]); + + /* Start building a response */ + resp_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!resp_skb) + return -ENOMEM; + + tlog(T_DEBUG, "%s coverage for %s\n", cmd, module); + if (type == KTF_CT_COV_ENABLE) + retval = ktf_cov_enable(module, opts); + else + ktf_cov_disable(module); + + data = genlmsg_put_reply(resp_skb, info, &ktf_gnl_family, + 0, KTF_C_RESP); + if (!data) { + retval = -ENOMEM; + goto put_fail; + } + nla_put_u32(resp_skb, KTF_A_TYPE, type); + nla_put_u32(resp_skb, KTF_A_STAT, retval); + /* Recompute message header */ + genlmsg_end(resp_skb, data); + + retval = genlmsg_reply(resp_skb, info); + if (!retval) + tlog(T_DEBUG, "Sent reply for %s module %s\n", + cmd, module); + else + twarn("Failed to send reply for %s module %s - value %d", + cmd, module, retval); +put_fail: + /* Free buffer if failure */ + if (retval) + nlmsg_free(resp_skb); + return retval; +} + +/* Process request to configure a configurable context: + * Expected format: KTF_CT_CTX_CFG hid type_name context_name data + * placed in A_HID, A_FILE, A_STR and A_DATA respectively. + */ +static int ktf_ctx_cfg(struct sk_buff *skb, struct genl_info *info) +{ + char ctxname[KTF_MAX_NAME + 1]; + char type_name[KTF_MAX_NAME + 1]; + struct nlattr *data_attr; + void *ctx_data = NULL; + size_t ctx_data_sz = 0; + int hid; + struct ktf_handle *handle; + struct ktf_context *ctx; + int ret; + + if (!info->attrs[KTF_A_STR] || !info->attrs[KTF_A_HID]) + return -EINVAL; + data_attr = info->attrs[KTF_A_DATA]; + if (!data_attr) + return -EINVAL; + hid = nla_get_u32(info->attrs[KTF_A_HID]); + handle = ktf_handle_find(hid); + if (!handle) + return -EINVAL; + if (info->attrs[KTF_A_FILE]) + nla_strlcpy(type_name, info->attrs[KTF_A_FILE], KTF_MAX_NAME); + else + strcpy(type_name, "default"); + nla_strlcpy(ctxname, info->attrs[KTF_A_STR], KTF_MAX_NAME); + tlog(T_DEBUG, "Trying to find/create context %s with type %s\n", ctxname, type_name); + ctx = ktf_find_create_context(handle, ctxname, type_name); + if (!ctx) + return -ENODEV; + + tlog(T_DEBUG, "Received context configuration for context %s, handle %d\n", + ctxname, hid); + + ctx_data = nla_memdup(data_attr, GFP_KERNEL); + ctx_data_sz = nla_len(data_attr); + ret = ktf_context_set_config(ctx, ctx_data, ctx_data_sz); + kfree(ctx_data); + return ret; +} + +int ktf_nl_register(void) +{ + int stat = genl_register_family(&ktf_gnl_family); + return stat; +} + +void ktf_nl_unregister(void) +{ + genl_unregister_family(&ktf_gnl_family); +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_nl.h b/tools/testing/selftests/ktf/kernel/ktf_nl.h new file mode 100644 index 0000000..87d8012 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_nl.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * nl.h: ktf netlink protocol interface + */ +#ifndef KTF_NL_H +#define KTF_NL_H + +int ktf_nl_register(void); +void ktf_nl_unregister(void); + +#endif diff --git a/tools/testing/selftests/ktf/kernel/ktf_unlproto.h b/tools/testing/selftests/ktf/kernel/ktf_unlproto.h new file mode 100644 index 0000000..e6d4525 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_unlproto.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_unlproto.h: implements interfaces for user-kernel netlink interactions + * for querying/running tests. + */ +#ifndef _KTF_UNLPROTO_H +#define _KTF_UNLPROTO_H +#ifdef __cplusplus +extern "C" { +#endif + +enum ktf_cmd_type { + KTF_CT_UNSPEC, + KTF_CT_QUERY, + KTF_CT_RUN, + KTF_CT_COV_ENABLE, + KTF_CT_COV_DISABLE, + KTF_CT_CTX_CFG, + KTF_CT_MAX, +}; + +/* Netlink protocol definition shared between user and kernel space + * Include once per user app as it defines struct values! + */ + +/* supported attributes */ +enum ktf_attr { + KTF_A_UNSPEC, + KTF_A_TYPE, + KTF_A_VERSION, + KTF_A_SNAM, /* Test suite name */ + KTF_A_TNAM, /* Test name */ + KTF_A_NUM, + KTF_A_STR, + KTF_A_FILE, + KTF_A_STAT, + KTF_A_LIST, + KTF_A_TEST, + KTF_A_HID, /* Test handle ID */ + KTF_A_HLIST, /* List of handles repr. as a LIST of contexts for a given HID */ + KTF_A_MOD, /* module for coverage analysis, also used for context type */ + KTF_A_COVOPT, /* options for coverage analysis */ + KTF_A_DATA, /* Binary data used by a.o. hybrid tests */ + KTF_A_MAX +}; + +/* attribute policy */ +#ifdef NL_INTERNAL +static struct nla_policy ktf_gnl_policy[KTF_A_MAX] = { + [KTF_A_TYPE] = { .type = NLA_U32 }, + [KTF_A_VERSION] = { .type = NLA_U64 }, + [KTF_A_SNAM] = { .type = NLA_STRING }, + [KTF_A_TNAM] = { .type = NLA_STRING }, + [KTF_A_NUM] = { .type = NLA_U32 }, + [KTF_A_STAT] = { .type = NLA_U32 }, + [KTF_A_HID] = { .type = NLA_U32 }, + [KTF_A_LIST] = { .type = NLA_NESTED }, + [KTF_A_TEST] = { .type = NLA_NESTED }, + [KTF_A_HLIST] = { .type = NLA_NESTED }, + [KTF_A_STR] = { .type = NLA_STRING }, + [KTF_A_FILE] = { .type = NLA_STRING }, + [KTF_A_MOD] = { .type = NLA_STRING }, + [KTF_A_COVOPT] = { .type = NLA_U32 }, + [KTF_A_DATA] = { .type = NLA_BINARY }, +}; +#endif + +/* supported commands */ +enum ktf_cmd { + KTF_C_UNSPEC, + KTF_C_REQ, + KTF_C_RESP, + KTF_C_MAX +}; + +enum ktf_vshift { + KTF_VSHIFT_BUILD = 0, + KTF_VSHIFT_MICRO = 16, + KTF_VSHIFT_MINOR = 32, + KTF_VSHIFT_MAJOR = 48 +}; + +#define KTF_VERSION(__field, __v) \ + ((__v & (0xffffULL << KTF_VSHIFT_##__field)) \ + >> KTF_VSHIFT_##__field) + +#define KTF_VERSION_SET(__field, __v) \ + ((__v & 0xffffULL) << KTF_VSHIFT_##__field) + +#define KTF_VERSION_LATEST \ + (KTF_VERSION_SET(MAJOR, 0ULL) | KTF_VERSION_SET(MINOR, 2ULL) | KTF_VERSION_SET(MICRO, 1ULL)) + +/* Coverage options */ +#define KTF_COV_OPT_MEM 0x1 + +struct nla_policy *ktf_get_gnl_policy(void); + +#ifdef __cplusplus +} +#endif +#endif From patchwork Tue Aug 13 06:09:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091205 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E7B51746 for ; Tue, 13 Aug 2019 06:12:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D83BA28438 for ; Tue, 13 Aug 2019 06:12:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CBE1428587; Tue, 13 Aug 2019 06:12:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 041312845E for ; Tue, 13 Aug 2019 06:12:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727629AbfHMGLd (ORCPT ); Tue, 13 Aug 2019 02:11:33 -0400 Received: from userp2130.oracle.com ([156.151.31.86]:59996 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727160AbfHMGLc (ORCPT ); Tue, 13 Aug 2019 02:11:32 -0400 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68vbv010941; Tue, 13 Aug 2019 06:11:09 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=riCWR7mvkEEzB8Hnj6RmL24fxUl4FVMwMdqKUi2IlJM=; b=lBowMwvd4Th1sxDn2mY448tzEh9z8fZLB8EUVAfCdsRgsIKFD1l1ymj09aDhjn565vce YD6C7V01vBJZ755ZL47x97n7Zk9VSkudLUHni9ZL7sOFXoTZq0vEgKtJP2EQfCf+0O0f L5Wa6ZT+mN1CvdhUXK8aJMAzorCZX+0QFeG4hO8bsGKPFCDdMaYGW/wdsrAS/nYvs0jk 8e2VDoveEexSyO5u6d499mFMw4+YAxTUk6Ydn4opVZSSVZJWhbCXNwwa2ibHheOQqS+H vvd5viRkBpgD9a4pfqA09v2ndAk7rDJUR7HlFLqhTli6i0GItqSaaOk2Xn5Qdormuuh4 +g== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=riCWR7mvkEEzB8Hnj6RmL24fxUl4FVMwMdqKUi2IlJM=; b=HUSyqWGUtptLjrwDWbKzdyJDj3ruplWKeJG5C6nFLNuSM8JQTs4BLCzqgmNV58zz8YBh WwoVDzMm2weNVIdE+aMNSYupgqLxUuhDcrC6pkZd01S4K+R6ObWv2r6NQ7EmQac0Z8z9 RaDdH1ETlvmHqaqVWRVEzv78wA606u/gTm1euv/MxgLTPdSHiX9KUf2O+ytoCTdG1owC I3Hb/Zu6iD1l4GWwLRFCq1Eqn4BSoIwB19+o5b3d5jHiy06+ALfcNSSZoJ6UcBjsTMOb vZ1I4ZfTp1QX9rN8c9CLSsQ+oUM2pFHU69Gqb3BF++zU3pIqYNc662VWvnbFdZsylyYo rA== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by userp2130.oracle.com with ESMTP id 2u9nbtc0t5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:09 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67QcY096480; Tue, 13 Aug 2019 06:11:08 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by aserp3030.oracle.com with ESMTP id 2u9m0aydjp-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:07 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6B6P9010982; Tue, 13 Aug 2019 06:11:06 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:05 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 04/19] ktf: An implementation of a generic associative array container Date: Tue, 13 Aug 2019 08:09:19 +0200 Message-Id: <6fb12b13002454d5bcba407cbd51c1ee9ecf3ec6.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This container is mapping from an index datatype to a "value" datatatype, using rbtree for the implementation. This datatype is used for various purposes by ktf to store and find data related to the actual test cases. ktf_map.c: Implementation of a kernel version independent std::map like API ktf_map.h: simple objects with key lookup to be inlined into a Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/kernel/ktf_map.c | 261 ++++++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_map.h | 154 ++++++++++++- 2 files changed, 415 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_map.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_map.h diff --git a/tools/testing/selftests/ktf/kernel/ktf_map.c b/tools/testing/selftests/ktf/kernel/ktf_map.c new file mode 100644 index 0000000..78ae5fb --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_map.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_map.c: Implementation of a kernel version independent std::map like API + * (made abstract to allow impl to change) + */ + +#include "ktf_map.h" +#include "ktf.h" + +void ktf_map_init(struct ktf_map *map, ktf_map_elem_comparefn elem_comparefn, + ktf_map_elem_freefn elem_freefn) +{ + map->root = RB_ROOT; + map->size = 0; + map->elem_comparefn = elem_comparefn; + map->elem_freefn = elem_freefn; + spin_lock_init(&map->lock); +} + +int ktf_map_elem_init(struct ktf_map_elem *elem, const char *key) +{ + memcpy(elem->key, key, KTF_MAX_KEY); + /* For strings that are too long, ensure truncation at + * KTF_MAX_NAME == KTF_MAX_KEY - 1 length: + */ + elem->key[KTF_MAX_NAME] = '\0'; + elem->map = NULL; + kref_init(&elem->refcount); + return 0; +} + +/* A convenience unsigned int compare function as an alternative + * to the string compare: + */ +int ktf_uint_compare(const char *ac, const char *bc) +{ + unsigned int a = *((unsigned int *)ac); + unsigned int b = *((unsigned int *)bc); + + return a > b ? 1 : (a < b ? -1 : 0); +} +EXPORT_SYMBOL(ktf_uint_compare); + +/* Copy "elem"s key representation into "name". For cases where no + * compare function is defined - i.e. string keys - just copy string, + * otherwise name is hexascii of first 8 bytes of key. + */ +char * +ktf_map_elem_name(struct ktf_map_elem *elem, char *name) +{ + if (!name) + return NULL; + + if (!elem || !elem->map) + (void)strlcpy(name, "", KTF_MAX_NAME); + else if (!elem->map->elem_comparefn) + (void)strlcpy(name, elem->key, KTF_MAX_NAME); + else + (void)snprintf(name, KTF_MAX_NAME, "'%*ph'", 8, elem->key); + + return name; +} + +/* Called when refcount of elem is 0. */ +static void ktf_map_elem_release(struct kref *kref) +{ + struct ktf_map_elem *elem = container_of(kref, struct ktf_map_elem, + refcount); + struct ktf_map *map = elem->map; + char name[KTF_MAX_KEY]; + + tlog(T_DEBUG_V, "Releasing %s, %s free function", + ktf_map_elem_name(elem, name), + map && map->elem_freefn ? "calling" : "no"); + if (map && map->elem_freefn) + map->elem_freefn(elem); +} + +void ktf_map_elem_put(struct ktf_map_elem *elem) +{ + char name[KTF_MAX_KEY]; + + tlog(T_DEBUG_V, "Decreasing refcount for %s to %d", + ktf_map_elem_name(elem, name), + refcount_read(&elem->refcount.refcount) - 1); + kref_put(&elem->refcount, ktf_map_elem_release); +} + +void ktf_map_elem_get(struct ktf_map_elem *elem) +{ + char name[KTF_MAX_KEY]; + + tlog(T_DEBUG_V, "Increasing refcount for %s to %d", + ktf_map_elem_name(elem, name), + refcount_read(&elem->refcount.refcount) + 1); + kref_get(&elem->refcount); +} + +struct ktf_map_elem *ktf_map_find(struct ktf_map *map, const char *key) +{ + struct rb_node *node; + unsigned long flags; + + /* may be called in interrupt context */ + spin_lock_irqsave(&map->lock, flags); + node = map->root.rb_node; + + while (node) { + struct ktf_map_elem *elem = container_of(node, struct ktf_map_elem, node); + int result; + + if (map->elem_comparefn) + result = map->elem_comparefn(key, elem->key); + else + result = strncmp(key, elem->key, KTF_MAX_KEY); + + if (result < 0) { + node = node->rb_left; + } else if (result > 0) { + node = node->rb_right; + } else { + ktf_map_elem_get(elem); + spin_unlock_irqrestore(&map->lock, flags); + return elem; + } + } + spin_unlock_irqrestore(&map->lock, flags); + return NULL; +} + +/* Find the first map elem in 'map' */ +struct ktf_map_elem *ktf_map_find_first(struct ktf_map *map) +{ + struct ktf_map_elem *elem = NULL; + struct rb_node *node; + unsigned long flags; + + spin_lock_irqsave(&map->lock, flags); + node = rb_first(&map->root); + if (node) { + elem = container_of(node, struct ktf_map_elem, node); + ktf_map_elem_get(elem); + } + spin_unlock_irqrestore(&map->lock, flags); + return elem; +} + +/* Find the next element in the map after 'elem' if any */ +struct ktf_map_elem *ktf_map_find_next(struct ktf_map_elem *elem) +{ + struct ktf_map_elem *next = NULL; + struct ktf_map *map = elem->map; + struct rb_node *node; + unsigned long flags; + + if (!elem->map) + return NULL; + spin_lock_irqsave(&map->lock, flags); + node = rb_next(&elem->node); + + /* Assumption here - we don't need ref to elem any more. + * Common usage pattern is + * + * for (elem = ktf_map_elem_first(map); elem != NULL; + * elem = ktf_map_find_next(elem)) + * + * but if other use cases occur we may need to revisit. + * This assumption allows us to define our _for_each macros + * and still manage refcounts. + */ + ktf_map_elem_put(elem); + + if (node) { + next = container_of(node, struct ktf_map_elem, node); + ktf_map_elem_get(next); + } + spin_unlock_irqrestore(&map->lock, flags); + return next; +} + +int ktf_map_insert(struct ktf_map *map, struct ktf_map_elem *elem) +{ + struct rb_node **newobj, *parent = NULL; + unsigned long flags; + + spin_lock_irqsave(&map->lock, flags); + newobj = &map->root.rb_node; + while (*newobj) { + struct ktf_map_elem *this = container_of(*newobj, struct ktf_map_elem, node); + int result; + + if (map->elem_comparefn) + result = map->elem_comparefn(elem->key, this->key); + else + result = strncmp(elem->key, this->key, KTF_MAX_KEY); + + parent = *newobj; + if (result < 0) { + newobj = &((*newobj)->rb_left); + } else if (result > 0) { + newobj = &((*newobj)->rb_right); + } else { + spin_unlock_irqrestore(&map->lock, flags); + return -EEXIST; + } + } + + /* Add newobj node and rebalance tree. */ + rb_link_node(&elem->node, parent, newobj); + rb_insert_color(&elem->node, &map->root); + elem->map = map; + map->size++; + /* Bump reference count for map reference */ + ktf_map_elem_get(elem); + spin_unlock_irqrestore(&map->lock, flags); + return 0; +} + +void ktf_map_remove_elem(struct ktf_map *map, struct ktf_map_elem *elem) +{ + if (elem) { + rb_erase(&elem->node, &map->root); + map->size--; + ktf_map_elem_put(elem); + } +} + +struct ktf_map_elem *ktf_map_remove(struct ktf_map *map, const char *key) +{ + struct ktf_map_elem *elem; + unsigned long flags; + + elem = ktf_map_find(map, key); + spin_lock_irqsave(&map->lock, flags); + ktf_map_remove_elem(map, elem); + spin_unlock_irqrestore(&map->lock, flags); + return elem; +} + +void ktf_map_delete_all(struct ktf_map *map) +{ + struct ktf_map_elem *elem; + struct rb_node *node; + unsigned long flags; + + spin_lock_irqsave(&map->lock, flags); + do { + node = rb_first(&(map)->root); + if (node) { + rb_erase(node, &(map)->root); + map->size--; + elem = container_of(node, struct ktf_map_elem, node); + ktf_map_elem_put(elem); + } + } while (node); + spin_unlock_irqrestore(&map->lock, flags); +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_map.h b/tools/testing/selftests/ktf/kernel/ktf_map.h new file mode 100644 index 0000000..1c8ae9b --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_map.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_map.h: simple objects with key lookup to be inlined into a + * larger object + */ + +#ifndef _KTF_MAP_H +#define _KTF_MAP_H +#include +#include +#include + +#define KTF_MAX_KEY 64 +#define KTF_MAX_NAME (KTF_MAX_KEY - 1) + +struct ktf_map_elem; + +/* Compare function called to compare element keys - optional and if + * not specified we revert to string comparison. Should return < 0 + * if first key < second, > 0 if first key > second, and 0 if they are + * identical. + */ +typedef int (*ktf_map_elem_comparefn)(const char *, const char *); + +/* A convenience unsigned int compare function as an alternative + * to the string compare: + */ +int ktf_uint_compare(const char *a, const char *b); + +/* Free function called when elem refcnt is 0 - optional and of course for + * dynamically-allocated elems only. + */ +typedef void (*ktf_map_elem_freefn)(struct ktf_map_elem *); + +struct ktf_map { + struct rb_root root; /* The rb tree holding the map */ + size_t size; /* Current size (number of elements) of the map */ + spinlock_t lock; /* held for map lookup etc */ + ktf_map_elem_comparefn elem_comparefn; /* Key comparison function */ + ktf_map_elem_freefn elem_freefn; /* Free function */ +}; + +struct ktf_map_elem { + struct rb_node node; /* Linkage for the map */ + char key[KTF_MAX_KEY+1] __aligned(8); + /* Key of the element - must be unique within the same map */ + struct ktf_map *map; /* owning map */ + struct kref refcount; /* reference count for element */ +}; + +#define __KTF_MAP_INITIALIZER(_mapname, _elem_comparefn, _elem_freefn) \ + { \ + .root = RB_ROOT, \ + .size = 0, \ + .lock = __SPIN_LOCK_UNLOCKED(_mapname), \ + .elem_comparefn = _elem_comparefn, \ + .elem_freefn = _elem_freefn, \ + } + +#define DEFINE_KTF_MAP(_mapname, _elem_comparefn, _elem_freefn) \ + struct ktf_map _mapname = __KTF_MAP_INITIALIZER(_mapname, _elem_comparefn, _elem_freefn) + +void ktf_map_init(struct ktf_map *map, ktf_map_elem_comparefn elem_comparefn, + ktf_map_elem_freefn elem_freefn); + +/* returns 0 upon success or -errno upon error */ +int ktf_map_elem_init(struct ktf_map_elem *elem, const char *key); + +/* increase/reduce reference count to element. If count reaches 0, the + * free function associated with map (if any) is called. + */ +void ktf_map_elem_get(struct ktf_map_elem *elem); +void ktf_map_elem_put(struct ktf_map_elem *elem); + +char *ktf_map_elem_name(struct ktf_map_elem *elem, char *name); + +/* Insert a new element in map - return 0 iff 'elem' was inserted or -1 if + * the key already existed - duplicates are not insterted. + */ +int ktf_map_insert(struct ktf_map *map, struct ktf_map_elem *elem); + +/* Find and return the element with 'key' */ +struct ktf_map_elem *ktf_map_find(struct ktf_map *map, const char *key); + +/* Find the first map elem in 'map' with reference count increased. */ +struct ktf_map_elem *ktf_map_find_first(struct ktf_map *map); + +/* Find the next element in the map after 'elem' if any. Decreases refcount + * for "elem" and increases it for returned map element - this helps manage + * reference counts when iterating over map elements. + */ +struct ktf_map_elem *ktf_map_find_next(struct ktf_map_elem *elem); + +/* Remove the element 'key' from the map and return a pointer to it with + * refcount increased. + */ +struct ktf_map_elem *ktf_map_remove(struct ktf_map *map, const char *key); + +/* Remove specific element elem from the map. Refcount is not increased + * as caller must already have had a reference. + */ +void ktf_map_remove_elem(struct ktf_map *map, struct ktf_map_elem *elem); + +void ktf_map_delete_all(struct ktf_map *map); + +static inline size_t ktf_map_size(struct ktf_map *map) { + return map->size; +} + +static inline bool ktf_map_empty(struct ktf_map *map) { + return map->size == 0; +} + +/* Gets first entry with refcount of entry increased for caller. */ +#define ktf_map_first_entry(_map, _type, _member) \ + ktf_map_empty(_map) ? NULL : \ + container_of(ktf_map_find_first(_map), _type, _member) + +/* Gets next elem after "pos", decreasing refcount for pos and increasing + * it for returned entry. + */ +#define ktf_map_next_entry(_pos, _member) ({ \ + struct ktf_map_elem *_e = ktf_map_find_next(&(_pos)->_member); \ + _e ? container_of(_e, typeof(*_pos), _member) : NULL; \ +}) + +/* Iterates over map elements, incrementing refcount for current element and + * decreasing it when we iterate to the next element. Important - if you + * break out of the loop via break/return, ensure ktf_map_elem_put(pos) + * is called for current element since we have a reference to it for the + * current loop body iteration. + */ +#define ktf_map_for_each(pos, map) \ + for (pos = ktf_map_find_first(map); pos != NULL; pos = ktf_map_find_next(pos)) + +/* Iterate over map elements in similar manner as above but using + * container_of() wrappers to work with the type embedding a + * "struct ktf_map_elem". + */ +#define ktf_map_for_each_entry(_pos, _map, _member) \ + for (_pos = ktf_map_first_entry(_map, typeof(*_pos), _member); \ + _pos != NULL; \ + _pos = ktf_map_next_entry(_pos, _member)) + +#define ktf_map_find_entry(_map, _key, _type, _member) ({ \ + struct ktf_map_elem *_entry = ktf_map_find(_map, _key); \ + _entry ? container_of(_entry, _type, _member) : NULL; \ +}) + +#endif From patchwork Tue Aug 13 06:09:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091143 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E7E2B746 for ; Tue, 13 Aug 2019 06:11:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D7D3E28438 for ; Tue, 13 Aug 2019 06:11:33 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CAB02284CE; Tue, 13 Aug 2019 06:11:33 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 50EE728438 for ; Tue, 13 Aug 2019 06:11:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727616AbfHMGLc (ORCPT ); Tue, 13 Aug 2019 02:11:32 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:37120 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727614AbfHMGLb (ORCPT ); Tue, 13 Aug 2019 02:11:31 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68wnv022022; Tue, 13 Aug 2019 06:11:11 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=Xgef0XFFFPz0RPMRvPeUpqgM+lNFsR5792wofX4MhfM=; b=QTBRw1J8EwzFKxTiUFmLQgNu5gJSIlHqxIXINo/GKFDy5+tNbI6IEjURZfo8AowNa2RE HRbAaqZ3IYt5aDtt8KrnQa56YdCAoa+U49qwVSN8n0bSTuFfvXX7Y4DsSvTCR5Cpk4/h QW8xlED9BzhsCmcmoD4sny1+qHo8meLw4/wvHZvHdZ5fdwneMqAJ6ZjLSQ2GcWJCnpKz Bs1ZQOHbykMMpo+/94Mrav4hM7xrrhKy8Oqgloat9Bc3tGiTHL2WrqaqV93a61puONki 3C6OFymR8D7IcthR2aE/6Sb7TZAzwTm7hgQAPry4VsOOY3jZ4UfCCITlQwgzJjtA0ZzW ow== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=Xgef0XFFFPz0RPMRvPeUpqgM+lNFsR5792wofX4MhfM=; b=Q/qjpYmChiHk6d3bxVVA9c4vw6ca/+dFQ+6gPMWpmbcZWJ5JmZhA1UWn0q+h3la2ajSX 6PiUwAACJVI0nHX4sH2xeyFtNHhRz3o+j5R2TM5sfGE7zXMtf6z+59Q6rXYzu3JyWXfC tYG//zJAueUGNN0T3aQW6eZ+8a9lqxx8MV3lM68DYYiSDuJ57UbDBAcy8xSJo/OMbLRI fPclPxgYrktrriyOddNXcJ375J3loPtH73vPBgHPiRGZ5HHbBPuhbKmLWtbcNJe4ZtM5 F+GJDpeoORDqdBjgmRiFWoqAzL8okVGvjn4CYJxDiy1AY+0oNVDk/Xt8+idc5jFlV54M zw== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by userp2120.oracle.com with ESMTP id 2u9pjqbvd2-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:11 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67Q1i096475; Tue, 13 Aug 2019 06:11:10 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by aserp3030.oracle.com with ESMTP id 2u9m0aydn9-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:10 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6B9SB026468; Tue, 13 Aug 2019 06:11:09 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:09 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 05/19] ktf: Implementation of ktf support for overriding function entry and return. Date: Tue, 13 Aug 2019 08:09:20 +0200 Message-Id: <1fccfe3062ba6c7a2c8171df8e41975e28dae9ec.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Alan Maguire This is a very powerful and yet simple way to verify or modify behaviour of kernel calls. It uses the same technique as the error injection framework in kernel/fail_function.c to to override function entry and return. In addition to error injection, this is very useful to for instance verify that a particular API actually ends up being called, and in the right way, as an effect of a test. ktf_override.c: support for overriding function entry. ktf_override.h: Function override support interface for KTF. Signed-off-by: Alan Maguire Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/kernel/ktf_override.c | 45 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_override.h | 15 +++++- 2 files changed, 60 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_override.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_override.h diff --git a/tools/testing/selftests/ktf/kernel/ktf_override.c b/tools/testing/selftests/ktf/kernel/ktf_override.c new file mode 100644 index 0000000..7f046c8 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_override.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_override.c: support for overriding function entry. + */ +#include +#include +#include "ktf.h" +#include "ktf_override.h" + +asmlinkage void ktf_just_return_func(void); + +asm( + ".type ktf_just_return_func, @function\n" + ".globl ktf_just_return_func\n" + "ktf_just_return_func:\n" + " ret\n" + ".size ktf_just_return_func, .-ktf_just_return_func\n" +); + +void ktf_post_handler(struct kprobe *kp, struct pt_regs *regs, + unsigned long flags) +{ + /* + * A dummy post handler is required to prohibit optimizing, because + * jump optimization does not support execution path overriding. + */ +} +EXPORT_SYMBOL(ktf_post_handler); + +void ktf_override_function_with_return(struct pt_regs *regs) +{ + KTF_SET_INSTRUCTION_POINTER(regs, (unsigned long)&ktf_just_return_func); +} +EXPORT_SYMBOL(ktf_override_function_with_return); +NOKPROBE_SYMBOL(ktf_override_function_with_return); + +int ktf_register_override(struct kprobe *kp) +{ + return register_kprobe(kp); +} +EXPORT_SYMBOL(ktf_register_override); diff --git a/tools/testing/selftests/ktf/kernel/ktf_override.h b/tools/testing/selftests/ktf/kernel/ktf_override.h new file mode 100644 index 0000000..8a9cf39 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_override.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_override.h: Function override support interface for KTF. + */ +#include +#include "ktf.h" + +void ktf_post_handler(struct kprobe *kp, struct pt_regs *regs, + unsigned long flags); +void ktf_override_function_with_return(struct pt_regs *regs); +int ktf_register_override(struct kprobe *kp); From patchwork Tue Aug 13 06:09:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091145 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0298013AC for ; Tue, 13 Aug 2019 06:11:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E4C212845E for ; Tue, 13 Aug 2019 06:11:38 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D823E28438; Tue, 13 Aug 2019 06:11:38 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E346728438 for ; Tue, 13 Aug 2019 06:11:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727383AbfHMGLg (ORCPT ); Tue, 13 Aug 2019 02:11:36 -0400 Received: from userp2130.oracle.com ([156.151.31.86]:60092 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727633AbfHMGLf (ORCPT ); Tue, 13 Aug 2019 02:11:35 -0400 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68wPH010973; Tue, 13 Aug 2019 06:11:16 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=pxWQMkeXEo6VK4H0xnGRztYzh1TzSktDwlYf6T9k180=; b=kgiwVKgPjPDCglibAMRFBnvyiQK3Q4CLKUGpGS/t+UUoombTyEKmaSlicV9y40Nk/gVK deAVKRl55c4l6FKW15TWhNfS6XGl3m1Q3taJHr/bzbh8n4qbwOgULIkO5oGrwkkBKORS NYRQm62C4BOcJZu00baRgZNpMnJRgdR6/yNJkToMXgtVFhLvEEuPl29BLhmXrjKvEqGj 7trPciy6yqU9mizDXrM1fVgPCAsXK56VAY0dEubzfNdqWNaSxMdIn003bnlJWv/DmCBp gXPB6+LPyNfJoDg6GC3HXNPoNBrtZW3u64V32lhqFa66AbABesBmU993BR26cP43CYgH Vw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=pxWQMkeXEo6VK4H0xnGRztYzh1TzSktDwlYf6T9k180=; b=1RfCiHk2eZTGIi7RCgPiHW4m5uUwcs0myIWjsNnlWqxacHVFmQ+JqwcFvdu1nPnLZaQh 9U5hSVYuwPMr/SdxzVynnxQZHgVjyHc4D3cI+DvkjNL+04+XkteABuAblsCpAKqAlX0W GoxFTZtwQecm+PRgfif0KrXD8ALt1qTnCpYsYD40/6xLzNVQiajB4tDNNyvh7MEpjepJ vN9cmpVK7115D5H006GBN3rWcW78nz8ZoBYcRKL2qw28Ar7tZ0SDPwT0ilEW8dBW5GNW mEGeRa8KhYuDSD8XwQy7GQK7Er9C4UrzvjpF7zgTCekJZhNlCpq5pcpaNRE9ULNb2FEZ Dw== Received: from userp3020.oracle.com (userp3020.oracle.com [156.151.31.79]) by userp2130.oracle.com with ESMTP id 2u9nbtc0vy-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:16 +0000 Received: from pps.filterd (userp3020.oracle.com [127.0.0.1]) by userp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68TeZ056702; Tue, 13 Aug 2019 06:11:15 GMT Received: from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236]) by userp3020.oracle.com with ESMTP id 2u9n9hs2cq-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:15 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6BEPf013396; Tue, 13 Aug 2019 06:11:14 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:13 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 06/19] ktf: A simple debugfs interface to test results Date: Tue, 13 Aug 2019 08:09:21 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Alan Maguire While test results is available via netlink from user space, sometimes it may be useful to be able to access the results from the kernel as well, for instance due to a crash. Make that possible via debugfs. ktf_debugfs.h: Support for creating a debugfs representation of test Signed-off-by: Alan Maguire Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 ++- 2 files changed, 390 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.h diff --git a/tools/testing/selftests/ktf/kernel/ktf_debugfs.c b/tools/testing/selftests/ktf/kernel/ktf_debugfs.c new file mode 100644 index 0000000..a20fbd2 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_debugfs.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire + * + * SPDX-License-Identifier: GPL-2.0 + * + */ +#include +#include +#include +#include "ktf_debugfs.h" +#include "ktf.h" +#include "ktf_test.h" +#include "ktf_cov.h" + +/* Create a debugfs representation of test sets/tests. Hierarchy looks like + * this: + * + * Path Semantics + * /sys/kernel/debug/ktf/run/ Run all tests in testset + * /sys/kernel/debug/ktf/run// Run specific test in testset + * /sys/kernel/debug/ktf/results/ Show results of last run for + * testset + * + * /sys/kernel/debug/ktf/results// + * Show results of last run for + * test + * + */ + +static struct dentry *ktf_debugfs_rootdir; +static struct dentry *ktf_debugfs_rundir; +static struct dentry *ktf_debugfs_resultsdir; +static struct dentry *ktf_debugfs_cov_file; + +static void ktf_debugfs_print_result(struct seq_file *seq, struct ktf_test *t) +{ + struct timespec now; + + if (t && strlen(t->log) > 0) { + getnstimeofday(&now); + seq_printf(seq, "[%s/%s, %ld seconds ago] %s\n", + t->tclass, t->name, + now.tv_sec - t->lastrun.tv_sec, t->log); + } +} + +/* /sys/kernel/debug/ktf/results/-tests/ shows specific result */ +static int ktf_debugfs_result(struct seq_file *seq, void *v) +{ + struct ktf_test *t = (struct ktf_test *)seq->private; + + ktf_debugfs_print_result(seq, t); + + return 0; +} + +/* /sys/kernel/debug/ktf/results/ shows all results for testset. */ +static int ktf_debugfs_results_all(struct seq_file *seq, void *v) +{ + struct ktf_case *testset = (struct ktf_case *)seq->private; + struct ktf_test *t; + + if (!testset) + return 0; + + seq_printf(seq, "%s results:\n", ktf_case_name(testset)); + ktf_testcase_for_each_test(t, testset) + ktf_debugfs_print_result(seq, t); + + return 0; +} + +/* /sys/kernel/debug/ktf/run/-tests/ runs specific test. */ +static int ktf_debugfs_run(struct seq_file *seq, void *v) +{ + struct ktf_test *t = (struct ktf_test *)seq->private; + + if (!t) + return 0; + + ktf_run_hook(NULL, NULL, t, 0, NULL, 0); + ktf_debugfs_print_result(seq, t); + + return 0; +} + +/* /sys/kernel/debug/ktf/run/ runs all tests in testset. */ +static int ktf_debugfs_run_all(struct seq_file *seq, void *v) +{ + struct ktf_case *testset = (struct ktf_case *)seq->private; + struct ktf_test *t; + + if (!testset) + return 0; + + seq_printf(seq, "Running %s\n", ktf_case_name(testset)); + ktf_testcase_for_each_test(t, testset) { + ktf_run_hook(NULL, NULL, t, 0, NULL, 0); + ktf_debugfs_print_result(seq, t); + } + + return 0; +} + +static int ktf_run_test_open(struct inode *inode, struct file *file) +{ + struct ktf_test *t; + + if (!try_module_get(THIS_MODULE)) + return -EIO; + + t = (struct ktf_test *)inode->i_private; + + return single_open(file, ktf_debugfs_run, t); +} + +static int ktf_debugfs_release(struct inode *inode, struct file *file) +{ + module_put(THIS_MODULE); + return single_release(inode, file); +} + +static const struct file_operations ktf_run_test_fops = { + .open = ktf_run_test_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +static int ktf_results_test_open(struct inode *inode, struct file *file) +{ + struct ktf_test *t; + + if (!try_module_get(THIS_MODULE)) + return -EIO; + + t = (struct ktf_test *)inode->i_private; + + return single_open(file, ktf_debugfs_result, t); +} + +static const struct file_operations ktf_results_test_fops = { + .open = ktf_results_test_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +static void _ktf_debugfs_destroy_test(struct ktf_test *t) +{ + if (!t) + return; + + tlog(T_DEBUG, "Destroying debugfs test %s", t->name); + debugfs_remove(t->debugfs.debugfs_results_test); + debugfs_remove(t->debugfs.debugfs_run_test); + memset(&t->debugfs, 0, sizeof(t->debugfs)); +} + +void ktf_debugfs_create_test(struct ktf_test *t) +{ + struct ktf_case *testset = ktf_case_find(t->tclass); + + if (!testset) + return; + + memset(&t->debugfs, 0, sizeof(t->debugfs)); + + t->debugfs.debugfs_results_test = + debugfs_create_file(t->name, S_IFREG | 0444, + testset->debugfs.debugfs_results_test, + t, &ktf_results_test_fops); + + if (t->debugfs.debugfs_results_test) { + t->debugfs.debugfs_run_test = + debugfs_create_file(t->name, S_IFREG | 0444, + testset->debugfs.debugfs_run_test, + t, &ktf_run_test_fops); + if (!t->debugfs.debugfs_run_test) { + _ktf_debugfs_destroy_test(t); + } else { + /* Take reference for test for debugfs */ + ktf_test_get(t); + } + } + /* Drop reference to testset from ktf_case_find(). */ + ktf_case_put(testset); +} + +void ktf_debugfs_destroy_test(struct ktf_test *t) +{ + _ktf_debugfs_destroy_test(t); + /* Release reference now debugfs files are gone. */ + ktf_test_put(t); +} + +static int ktf_results_testset_open(struct inode *inode, struct file *file) +{ + struct ktf_case *testset; + + if (!try_module_get(THIS_MODULE)) + return -EIO; + + testset = (struct ktf_case *)inode->i_private; + + return single_open(file, ktf_debugfs_results_all, testset); +} + +static const struct file_operations ktf_results_testset_fops = { + .open = ktf_results_testset_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +static int ktf_run_testset_open(struct inode *inode, struct file *file) +{ + struct ktf_case *testset; + + if (!try_module_get(THIS_MODULE)) + return -EIO; + + testset = (struct ktf_case *)inode->i_private; + + return single_open(file, ktf_debugfs_run_all, testset); +} + +static const struct file_operations ktf_run_testset_fops = { + .open = ktf_run_testset_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +static void _ktf_debugfs_destroy_testset(struct ktf_case *testset) +{ + debugfs_remove(testset->debugfs.debugfs_run_testset); + debugfs_remove(testset->debugfs.debugfs_run_test); + debugfs_remove(testset->debugfs.debugfs_results_testset); + debugfs_remove(testset->debugfs.debugfs_results_test); +} + +void ktf_debugfs_create_testset(struct ktf_case *testset) +{ + char tests_subdir[KTF_DEBUGFS_NAMESZ]; + const char *name = ktf_case_name(testset); + + memset(&testset->debugfs, 0, sizeof(testset->debugfs)); + + /* First add /sys/kernel/debug/ktf/[results|run]/ */ + testset->debugfs.debugfs_results_testset = + debugfs_create_file(name, S_IFREG | 0444, + ktf_debugfs_resultsdir, + testset, &ktf_results_testset_fops); + if (!testset->debugfs.debugfs_results_testset) + goto err; + + testset->debugfs.debugfs_run_testset = + debugfs_create_file(name, S_IFREG | 0444, + ktf_debugfs_rundir, + testset, &ktf_run_testset_fops); + if (!testset->debugfs.debugfs_run_testset) + goto err; + + /* Now add parent directories for individual test result/run tests + * which live in + * /sys/kernel/debug/ktf/[results|run]/-tests/ + */ + (void)snprintf(tests_subdir, sizeof(tests_subdir), "%s%s", + name, KTF_DEBUGFS_TESTS_SUFFIX); + + testset->debugfs.debugfs_results_test = + debugfs_create_dir(tests_subdir, ktf_debugfs_resultsdir); + if (!testset->debugfs.debugfs_results_test) + goto err; + + testset->debugfs.debugfs_run_test = + debugfs_create_dir(tests_subdir, ktf_debugfs_rundir); + if (!testset->debugfs.debugfs_run_test) + goto err; + + /* Take reference count for testset. One will do as we will always + * free testset debugfs resources together. + */ + ktf_case_get(testset); + return; +err: + _ktf_debugfs_destroy_testset(testset); +} + +void ktf_debugfs_destroy_testset(struct ktf_case *testset) +{ + tlog(T_DEBUG, "Destroying debugfs testset %s", ktf_case_name(testset)); + _ktf_debugfs_destroy_testset(testset); + /* Remove our debugfs reference cout to testset */ + ktf_case_put(testset); +} + +/* /sys/kernel/debug/ktf/coverage shows coverage statistics. */ +static int ktf_debugfs_cov(struct seq_file *seq, void *v) +{ + ktf_cov_seq_print(seq); + + return 0; +} + +static int ktf_cov_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -EIO; + + return single_open(file, ktf_debugfs_cov, NULL); +} + +static const struct file_operations ktf_cov_fops = { + .open = ktf_cov_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +void ktf_debugfs_cleanup(void) +{ + tlog(T_DEBUG, "Removing ktf debugfs dirs..."); + debugfs_remove(ktf_debugfs_cov_file); + debugfs_remove(ktf_debugfs_rundir); + debugfs_remove(ktf_debugfs_resultsdir); + debugfs_remove(ktf_debugfs_rootdir); +} + +void ktf_debugfs_init(void) +{ + ktf_debugfs_rootdir = debugfs_create_dir(KTF_DEBUGFS_ROOT, NULL); + if (!ktf_debugfs_rootdir) + goto err; + ktf_debugfs_rundir = debugfs_create_dir(KTF_DEBUGFS_RUN, + ktf_debugfs_rootdir); + if (!ktf_debugfs_rundir) + goto err; + ktf_debugfs_resultsdir = debugfs_create_dir(KTF_DEBUGFS_RESULTS, + ktf_debugfs_rootdir); + if (!ktf_debugfs_resultsdir) + goto err; + + ktf_debugfs_cov_file = debugfs_create_file(KTF_DEBUGFS_COV, + S_IFREG | 0444, + ktf_debugfs_rootdir, + NULL, + &ktf_cov_fops); + if (ktf_debugfs_cov_file) + return; +err: + terr("Could not init %s\n", KTF_DEBUGFS_ROOT); + ktf_debugfs_cleanup(); +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_debugfs.h b/tools/testing/selftests/ktf/kernel/ktf_debugfs.h new file mode 100644 index 0000000..4dab1ea --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_debugfs.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_debugfs.h: Support for creating a debugfs representation of test + * sets/tests. + */ + +#ifndef KTF_DEBUGFS_H +#define KTF_DEBUGFS_H +#include + +#define KTF_DEBUGFS_ROOT "ktf" +#define KTF_DEBUGFS_RUN "run" +#define KTF_DEBUGFS_RESULTS "results" +#define KTF_DEBUGFS_COV "coverage" +#define KTF_DEBUGFS_TESTS_SUFFIX "-tests" + +#define KTF_DEBUGFS_NAMESZ 256 + +struct ktf_test; +struct ktf_case; + +void ktf_debugfs_create_test(struct ktf_test *); +void ktf_debugfs_destroy_test(struct ktf_test *); +void ktf_debugfs_create_testset(struct ktf_case *); +void ktf_debugfs_destroy_testset(struct ktf_case *); +void ktf_debugfs_init(void); +void ktf_debugfs_cleanup(void); + + +#endif From patchwork Tue Aug 13 06:09:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091161 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 69C77746 for ; Tue, 13 Aug 2019 06:11:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5A61A28438 for ; Tue, 13 Aug 2019 06:11:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4E048284CE; Tue, 13 Aug 2019 06:11:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D9EB028438 for ; Tue, 13 Aug 2019 06:11:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727677AbfHMGLl (ORCPT ); Tue, 13 Aug 2019 02:11:41 -0400 Received: from aserp2120.oracle.com ([141.146.126.78]:59186 "EHLO aserp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727667AbfHMGLj (ORCPT ); Tue, 13 Aug 2019 02:11:39 -0400 Received: from pps.filterd (aserp2120.oracle.com [127.0.0.1]) by aserp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68qlv039282; Tue, 13 Aug 2019 06:11:20 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=MkGlaRzfhOaspuCgCSxv0mcumwSprFHBy9GelM51bZI=; b=cXO/RaN4hhWyYwTI/ANiRV7dNU7drhUyZyVr/adYVTkP1RuK4WkzvjLB9Peeg7ZhR9zd PlKH+Ohmmmw/buNLkc0XHTBvHzXjG+42G6e7y806+teV5g8PeHT+wrO6ID+gyTkoAtYo JaawzE9cJ0iYQKE0ozcSThhlM/bhPDG2AKaKKq9mWRr8+e9XQ7nAxAWwMpPTWoKbUr2r bf0qOs8/ata9OtLbulu/KzU+2ImgEgV3qItogvtV1LuSGRg1wuyFCGuVBbYnUcdf5jtR ndz66saasOvb7skjWTuXO9E5FzRxnsVZECSdx1xd/4sC2CzFMHYyqxR8/1IxG7hN/bxF 4A== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=MkGlaRzfhOaspuCgCSxv0mcumwSprFHBy9GelM51bZI=; b=aCt6nQ319nkZ7IZwz8tkum/s1PGwUPj9v8VbBEz7OnEBWLqoiR8KLw7xTVJHnDwe3uz0 0a+N0xPESPrvVcjL6EQhMDSDwZFZqK7uyIF/7Zia3HC69ziDE9IskQ7m6WBhy4uvpeZC mfL3z4p3+Cet93Ye8wK2yw+lZAMXqRSbkUN+rDW2Fa6Rn+vPjYudlwk186So+3/txj9l ezSK5M4Jdd9h44ur22jRqBGn7SOhpMmhbviGU/zuYhA+KoLfNhaqsEv8d/4JOFY9nhjj dLFzWCXxi0+DLaKXM4bBVpbJE2cWyfZQxidpEenO9lEepn4cnEgvHElgi+f0fkHK8enL Ig== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by aserp2120.oracle.com with ESMTP id 2u9nvp41g6-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:19 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67Rqj096715; Tue, 13 Aug 2019 06:11:19 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by aserp3030.oracle.com with ESMTP id 2u9m0aydx7-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:19 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6BIs8026626; Tue, 13 Aug 2019 06:11:18 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:17 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 07/19] ktf: Simple coverage support Date: Tue, 13 Aug 2019 08:09:22 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Alan Maguire Allows reports of code coverage and memory allocation. ktf_cov.c: Code coverage support implementation for KTF. ktf_cov.h: Code coverage support interface for KTF. Signed-off-by: Alan Maguire Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/kernel/ktf_cov.c | 690 ++++++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_cov.h | 94 +++- 2 files changed, 784 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_cov.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_cov.h diff --git a/tools/testing/selftests/ktf/kernel/ktf_cov.c b/tools/testing/selftests/ktf/kernel/ktf_cov.c new file mode 100644 index 0000000..f917994 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_cov.c @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_cov.c: Code coverage support implementation for KTF. + */ +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SLUB +#include +#endif /* CONFIG_SLUB */ +#ifdef CONFIG_SLAB +#include +#endif +#include +#include +#include +#include "ktf.h" +#include "ktf_map.h" +#include "ktf_cov.h" + +/* It may seem odd that we use a refcnt field in ktf_cov_entry structures + * in addition to using krefcount management via the ktf_map. The reasoning + * here is that if we enable and then disable coverage, we do not want to + * purge the entry data as we likely want to examine counts after disabling + * coverage. So the first enable will add entries to the cov_entry map and + * subsequent disable/enables will simply update the entry's refcnt. The + * free function below should only be called therefore from cleanup context + * when the cov entries are finally removed from the cov_entry map. + */ +static void ktf_cov_entry_free(struct ktf_map_elem *elem) +{ + struct ktf_cov_entry *entry = container_of(elem, struct ktf_cov_entry, + kmap); + if (entry->refcnt > 0) + unregister_kprobe(&entry->kprobe); + kfree(entry); +} + +/* Comparison function is subtle. We want to be able to compare key1 + * and key2 here, where key1 may either be an existing object, in which + * case it has an address and size; or it may be an object offset, in which + * case k1's address will be the address with offset of size 0. In both + * cases for the -1 case we can simply check if k1's address is less than + * k2's. For the 1 case, we need to ensure that the address is >= + * k2's address and it's size, since this ensures the address does not + * fall within the object bounds. Finally we are left with the case + * that k1.address >= k2.address _and_ it falls within the bounds of k2, + * which we consider a match. For a concrete example of how this matching + * is used, see how we walk the stack of functions within the kmalloc + * kretprobe below: we will have a function + offset on the stack, and we + * want to see if this offset falls within a function in our coverage entry + * map. If it does, we track the allocation. The implicit assumption is + * no overlap between different objects. + */ +static int ktf_cov_obj_compare(const char *key1, const char *key2) +{ + struct ktf_cov_obj_key *k1 = (struct ktf_cov_obj_key *)key1; + struct ktf_cov_obj_key *k2 = (struct ktf_cov_obj_key *)key2; + + if (k1->address < k2->address) + return -1; + if (k1->address >= (k2->address + k2->size)) + return 1; + return 0; +} + +void ktf_cov_entry_get(struct ktf_cov_entry *entry) +{ + ktf_map_elem_get(&entry->kmap); +} + +void ktf_cov_entry_put(struct ktf_cov_entry *entry) +{ + ktf_map_elem_put(&entry->kmap); +} + +/* Global map for address-> symbol/module mapping. Sort via symbol address + * and size combination, see ktf_cov_obj_compare() above for comparison + * logic. + */ +static DEFINE_KTF_MAP(cov_entry_map, ktf_cov_obj_compare, ktf_cov_entry_free); + +struct ktf_cov_entry *ktf_cov_entry_find(unsigned long addr, unsigned long size) +{ + struct ktf_cov_obj_key k; + + k.address = addr; + k.size = size; + + return ktf_map_find_entry(&cov_entry_map, (char *)&k, + struct ktf_cov_entry, kmap); +} + +static void ktf_cov_free(struct ktf_map_elem *elem) +{ + struct ktf_cov *cov = container_of(elem, struct ktf_cov, kmap); + + kfree(cov); +} + +void ktf_cov_put(struct ktf_cov *cov) +{ + ktf_map_elem_put(&cov->kmap); +} + +/* Coverage object map. Just modules supported for now, sort by name. */ +static DEFINE_KTF_MAP(cov_map, NULL, ktf_cov_free); + +struct ktf_cov *ktf_cov_find(const char *module) +{ + return ktf_map_find_entry(&cov_map, module, struct ktf_cov, kmap); +} + +/* cache for memory objects used to track allocations */ +static struct kmem_cache *cov_mem_cache; + +static void ktf_cov_mem_free(struct ktf_map_elem *elem) +{ + struct ktf_cov_mem *m = container_of(elem, struct ktf_cov_mem, + kmap); + + kmem_cache_free(cov_mem_cache, m); +} + +/* Global map for tracking memory allocations */ +DEFINE_KTF_MAP(cov_mem_map, ktf_cov_obj_compare, ktf_cov_mem_free); +EXPORT_SYMBOL(cov_mem_map); + +struct ktf_cov_mem *ktf_cov_mem_find(unsigned long addr, unsigned long size) +{ + struct ktf_cov_obj_key k; + + k.address = addr; + k.size = size; + + return ktf_map_find_entry(&cov_mem_map, (char *)&k, + struct ktf_cov_mem, kmap); +} + +void ktf_cov_mem_get(struct ktf_cov_mem *m) +{ + ktf_map_elem_get(&m->kmap); +} + +void ktf_cov_mem_put(struct ktf_cov_mem *m) +{ + ktf_map_elem_put(&m->kmap); +} + +static void ktf_cov_mem_remove(struct ktf_cov_mem *m) +{ + ktf_map_remove_elem(&cov_mem_map, &m->kmap); +} + +/* Do not use ktf_cov_entry_find() here as we can get entry directly + * from probe address (as probe is first field in struct ktf_cov_entry). + * No reference counting issues should apply as when entry refcnt drops + * to 0 we unregister the kprobe prior to freeing the entry. + */ +static int ktf_cov_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct ktf_cov_entry *entry = (struct ktf_cov_entry *)p; + + /* Make sure probe is ours... */ + if (!entry || entry->magic != KTF_COV_ENTRY_MAGIC) + return 0; + entry->count++; + if (entry->count == 1 && entry->cov) + entry->cov->count++; + return 0; +} + +static int ktf_cov_init_symbol(void *data, const char *name, + struct module *mod, unsigned long addr) +{ + struct ktf_cov_entry *entry; + struct ktf_cov *cov = data; + char buf[256]; + + if (!mod || !cov) + return 0; + + if (!try_module_get(mod)) + return 0; + + /* module_mutex is grabbed by register_kprobe() */ + mutex_unlock(&module_mutex); + + /* We only care about symbols for cov-specified module. */ + if (strcmp(mod->name, cov->kmap.key)) + goto out; + + /* We don't probe ourselves and functions called within probe ctxt. */ + if (strncmp(name, "ktf_cov", strlen("ktf_cov")) == 0 || + strcmp(name, "ktf_map_find") == 0) + goto out; + + /* Check if we're already covered for this module/symbol. */ + entry = ktf_cov_entry_find(addr, 0); + if (entry) { + tlog(T_DEBUG, "%s already present in coverage: %s", + name, entry->name); + ktf_cov_entry_put(entry); + goto out; + } + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + (void)strlcpy(entry->name, name, sizeof(entry->name)); + entry->magic = KTF_COV_ENTRY_MAGIC; + entry->cov = cov; + entry->refcnt = 1; + + entry->kprobe.pre_handler = ktf_cov_handler; + entry->kprobe.symbol_name = entry->name; + + /* Ugh - we try to register a kprobe as a means of determining + * if the symbol is a function. + */ + if (register_kprobe(&entry->kprobe) < 0) { + /* not a probe-able function */ + kfree(entry); + goto out; + } + entry->key.address = addr; + entry->key.size = ktf_symbol_size(addr); + (void)sprint_symbol(buf, entry->key.address); + if (ktf_map_elem_init(&entry->kmap, (char *)&entry->key) < 0 || + ktf_map_insert(&cov_entry_map, &entry->kmap) < 0) { + unregister_kprobe(&entry->kprobe); + kfree(entry); + goto out; + } + tlog(T_DEBUG, "Added %s/%s (%p, size %lu) to coverage: %s", + mod->name, entry->name, (void *)entry->kprobe.addr, + entry->key.size, buf); + + cov->total++; + ktf_cov_entry_put(entry); + +out: + mutex_lock(&module_mutex); + module_put(mod); + return 0; +} + +static int ktf_cov_kmem_cache_alloc_handler(struct kretprobe_instance *, + struct pt_regs *); + +static unsigned long register_kretprobe_size; + +/* Handler tracking allocations. Determine if any functions we are + * tracking coverage for (coverage entries) are on the stack; if so + * we track the allocation. + */ +static int ktf_cov_kmem_alloc_entry(struct ktf_cov_mem *m, unsigned long bytes) +{ + struct ktf_cov_entry *entry = NULL; + int n; + + /* We don't care about 0-length allocations. */ + if (!bytes) + return 0; + + /* Find first cov entry on stack to allow us to attribute traced + * allocation to first coverage entry we come across. + */ + m->nr_entries = stack_trace_save(m->stack_entries, KTF_COV_MAX_STACK_DEPTH, 1); + for (n = 0; n < m->nr_entries; n++) { + /* avoid recursive enter when allocating cov mem */ + if (m->stack_entries[n] == + (unsigned long)ktf_cov_kmem_cache_alloc_handler) + break; + /* ignore allocs as a result of registering probes */ + if (m->stack_entries[n] > + (unsigned long)register_kretprobe && + m->stack_entries[n] < ((unsigned long)register_kretprobe + + register_kretprobe_size)) + break; + entry = ktf_cov_entry_find(m->stack_entries[n], 0); + if (entry) + break; + } + if (!entry) { + m->nr_entries = 0; + return 0; + } + ktf_cov_entry_put(entry); + + m->key.size = bytes; + /* Have to wait until alloc returns to get key.address */ + + return 0; +} + +/* Handler tracking kmalloc allocations. */ +static int ktf_cov_kmalloc_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct ktf_cov_mem *m = (struct ktf_cov_mem *)ri->data; + unsigned long bytes = (unsigned long)KTF_ENTRY_PROBE_ARG0; + + return ktf_cov_kmem_alloc_entry(m, bytes); +} + +static int ktf_cov_kmem_cache_alloc_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct kmem_cache *cache = + (struct kmem_cache *)KTF_ENTRY_PROBE_ARG0; + struct ktf_cov_mem *m = (struct ktf_cov_mem *)ri->data; + unsigned long bytes; + + if (!cache) + return 0; + + bytes = cache->object_size; + if (cache == cov_mem_cache) + return 0; + return ktf_cov_kmem_alloc_entry(m, bytes); +} + +static int ktf_cov_kmem_alloc_return(struct ktf_cov_mem *m, + unsigned long ret) +{ + struct ktf_cov_mem *mm; + + m->key.address = ret; + mm = kmem_cache_alloc(cov_mem_cache, GFP_NOWAIT); + if (!mm) + return 0; + memcpy(mm, m, sizeof(*mm)); + if (ktf_map_elem_init(&mm->kmap, (char *)&mm->key) < 0 || + ktf_map_insert(&cov_mem_map, &mm->kmap) < 0) { + /* This can happen as inexplicably the same probe + * can fire twice for _kmalloc; this results in + * us attempting to add the same address twice, with + * the result that we get -EEXIST from ktf_map_insert() + * the second time. Annoying but the end result is + * we track the allocation once, which is what we want. + */ + terr("Failed to insert cov_mem %p", (void *)ret); + kmem_cache_free(cov_mem_cache, mm); + } + tlog(T_DEBUG, "cov_mem: tracking allocation %p", (void *)m->key.address); + m->nr_entries = 0; + return 0; +} + +static int ktf_cov_kmalloc_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct ktf_cov_mem *m = (struct ktf_cov_mem *)ri->data; + unsigned long ret = regs_return_value(regs); + + if (m->nr_entries) + return ktf_cov_kmem_alloc_return(m, ret); + return 0; +} + +static int ktf_cov_kmem_cache_alloc_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct kmem_cache *cache = + (struct kmem_cache *)KTF_ENTRY_PROBE_ARG0; + struct ktf_cov_mem *m = (struct ktf_cov_mem *)ri->data; + unsigned long ret = regs_return_value(regs); + + if (cache == cov_mem_cache) + return 0; + + if (m->nr_entries) + return ktf_cov_kmem_alloc_return(m, ret); + return 0; +} + +static int ktf_cov_kmem_free_entry(unsigned long tofree) +{ + struct ktf_cov_mem *m; + + if (!tofree) + return 0; + + m = ktf_cov_mem_find(tofree, 0); + if (m) { + tlog(T_DEBUG, "cov_mem: freeing allocation %p", + (void *)m->key.address); + ktf_cov_mem_remove(m); + ktf_cov_mem_put(m); + } + return 0; +} + +static int ktf_cov_kfree_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + unsigned long tofree = (unsigned long)KTF_ENTRY_PROBE_ARG0; + + if (!tofree) + return 0; + + return ktf_cov_kmem_free_entry(tofree); +} + +static int ktf_cov_kmem_cache_free_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct kmem_cache *cache = + (struct kmem_cache *)KTF_ENTRY_PROBE_ARG0; + unsigned long tofree = (unsigned long)KTF_ENTRY_PROBE_ARG1; + + if (!tofree || cache == cov_mem_cache) + return 0; + + return ktf_cov_kmem_free_entry(tofree); +} + +static struct kretprobe cov_mem_probes[] = { + { .kp = { .symbol_name = "__kmalloc" }, + .handler = ktf_cov_kmalloc_handler, + .entry_handler = ktf_cov_kmalloc_entry_handler, + .data_size = sizeof(struct ktf_cov_mem), + .maxactive = 0, /* assumes default value */ + }, + { .kp = { .symbol_name = "kmem_cache_alloc" }, + .handler = ktf_cov_kmem_cache_alloc_handler, + .entry_handler = ktf_cov_kmem_cache_alloc_entry_handler, + .data_size = sizeof(struct ktf_cov_mem), + .maxactive = 0, /* assumes default value */ + }, + { .kp = { .symbol_name = "kfree" }, + .handler = NULL, + .entry_handler = ktf_cov_kfree_entry_handler, + .data_size = 0, + .maxactive = 0, /* assumes default value */ + }, + { .kp = { .symbol_name = "kmem_cache_free" }, + .handler = NULL, + .entry_handler = ktf_cov_kmem_cache_free_entry_handler, + .data_size = 0, + .maxactive = 0, /* assumes default value */ + } +}; + +static int cov_opt_mem_cnt; + +static int ktf_cov_init_opts(struct ktf_cov *cov) +{ + int i, ret = 0; + + if (cov->opts & KTF_COV_OPT_MEM && ++cov_opt_mem_cnt == 1) { + if (!cov_mem_cache) { + cov_mem_cache = + kmem_cache_create("ktf_cov_mem_cache", + sizeof(struct ktf_cov_mem), 0, + SLAB_HWCACHE_ALIGN | SLAB_PANIC, + NULL); + + if (!cov_mem_cache) + return -ENOMEM; + } + + for (i = 0; i < ARRAY_SIZE(cov_mem_probes); i++) { + /* reset in case we're re-registering */ + cov_mem_probes[i].kp.addr = NULL; + cov_mem_probes[i].kp.flags = 0; + ret = register_kretprobe(&cov_mem_probes[i]); + if (ret) { + tlog(T_DEBUG, + "%d: failed to register retprobe for %s", + ret, cov_mem_probes[i].kp.symbol_name); + return ret; + } + } + } + + return ret; +} + +static void ktf_cov_cleanup_opts(struct ktf_cov *cov) +{ + int i; + + if (cov->opts & KTF_COV_OPT_MEM && --cov_opt_mem_cnt == 0) { + for (i = 0; i < ARRAY_SIZE(cov_mem_probes); i++) { + if (cov_mem_probes[i].nmissed > 0) { + tlog(T_INFO, "%s: retprobe missed %d.", + cov_mem_probes[i].kp.symbol_name, + cov_mem_probes[i].nmissed); + } + if (cov_mem_probes[i].kp.addr) + unregister_kretprobe(&cov_mem_probes[i]); + } + } +} + +/* If the module we are monitoring coverage for was unloaded/reloaded + * while coverage was disabled, we can end up re-enabling kprobes at + * different addresses for the same function. The problem is however + * we reference coverage entries by their address in the coverage + * entry map, so we need to clean it up to reflect the new locations + * of the probes. So we remove/re-add the entries with the updated + * addresses. It would obviously be easier to just remove the entries + * on coverage disable, but that limits our ability to examine coverage + * data - a common pattern is enable coverage, run test(s), disable + * coverage, check coverage data. + */ +static void ktf_cov_update_entries(const char *name, struct ktf_cov *cov) +{ + struct ktf_cov_entry *entry = ktf_map_first_entry(&cov_entry_map, + struct ktf_cov_entry, + kmap); + + while (entry) { + if (entry->cov != cov || + (unsigned long)entry->kprobe.addr == entry->key.address) { + entry = ktf_map_next_entry(entry, kmap); + continue; + } + + /* Address has changed; remove entry with old address as key + * and re-add with new address/size as key (size may have + * changed if module was re-compiled). + */ + ktf_map_remove_elem(&cov_entry_map, &entry->kmap); + entry->key.address = (unsigned long)entry->kprobe.addr; + entry->key.size = ktf_symbol_size(entry->key.address); + if (ktf_map_elem_init(&entry->kmap, (char *)&entry->key) < 0 || + ktf_map_insert(&cov_entry_map, &entry->kmap) < 0) { + tlog(T_DEBUG, "Failed to add %s/%s", name, entry->name); + unregister_kprobe(&entry->kprobe); + entry->refcnt--; + entry = ktf_map_next_entry(entry, kmap); + } else { + tlog(T_DEBUG, "Added %s/%s (%p, size %lu) to coverage", + name, entry->name, (void *)entry->key.address, + entry->key.size); + /* Map has changed, reset to root. */ + entry = ktf_map_first_entry(&cov_entry_map, + struct ktf_cov_entry, kmap); + } + } +} + +int ktf_cov_enable(const char *name, unsigned int opts) +{ + struct ktf_cov *cov = ktf_cov_find(name); + struct ktf_cov_entry *entry; + int ret = 0; + +#ifndef KTF_PROBE_SUPPORT + return -ENOTSUPP; +#endif + if (!cov) { + cov = kzalloc(sizeof(*cov), GFP_KERNEL); + if (!cov) + return -ENOMEM; + + cov->type = KTF_COV_TYPE_MODULE; + cov->opts = opts; + if (ktf_map_elem_init(&cov->kmap, name) < 0 || + ktf_map_insert(&cov_map, &cov->kmap) < 0) { + tlog(T_DEBUG, "cov %s already present", cov->kmap.key); + kfree(cov); + return -EEXIST; + } + register_kretprobe_size = + ktf_symbol_size((unsigned long)register_kretprobe); + mutex_lock(&module_mutex); + kallsyms_on_each_symbol(ktf_cov_init_symbol, cov); + mutex_unlock(&module_mutex); + } else { + ktf_map_for_each_entry(entry, &cov_entry_map, kmap) { + if (entry->cov != cov) + continue; + if (++entry->refcnt == 1) { + /* reset kprobe as we're re-registering */ + memset(&entry->kprobe, 0, + sizeof(entry->kprobe)); + entry->kprobe.pre_handler = ktf_cov_handler; + entry->kprobe.symbol_name = entry->name; + ret = register_kprobe(&entry->kprobe); + if (ret) { + tlog(T_DEBUG, "Failed to add %s/%s", + name, entry->name); + entry->refcnt--; + } + } + } + /* Probe addresses/function sizes for functions may have + * changed if module was unloaded/reloaded - entry map + * needs to be updated to use new address/size as key. + */ + ktf_cov_update_entries(name, cov); + } + + ret = ktf_cov_init_opts(cov); + + ktf_cov_put(cov); + + return ret; +} + +void ktf_cov_disable(const char *module) +{ + struct ktf_cov *cov = ktf_cov_find(module); + struct ktf_cov_entry *entry; + +#ifndef KTF_PROBE_SUPPORT + return; +#endif + + if (!cov) + return; + + ktf_map_for_each_entry(entry, &cov_entry_map, kmap) { + if (entry->cov == cov) { + if (--entry->refcnt == 0) { + unregister_kprobe(&entry->kprobe); + tlog(T_DEBUG, "Removed coverage %s/%s", + cov->kmap.key, entry->name); + } + } + } + ktf_cov_cleanup_opts(cov); + ktf_cov_put(cov); +} + +static void ktf_cov_mem_seq_print(struct seq_file *seq) +{ + struct ktf_cov_mem *m; + char buf[256]; + int n; + + seq_puts(seq, "\nMemory in use allocated by covered functions:\n\n"); + seq_printf(seq, "%44s %16s %10s\n", "ALLOCATION STACK", "ADDRESS", + "SIZE"); + ktf_for_each_cov_mem(m) { + for (n = 0; n < m->nr_entries; n++) { + sprint_symbol(buf, m->stack_entries[n]); + seq_printf(seq, "%44s", buf); + if (n == 0) + seq_printf(seq, " %16p %10lu", + (void *)m->key.address, + m->key.size); + seq_puts(seq, "\n"); + } + seq_puts(seq, "\n"); + } +} + +void ktf_cov_seq_print(struct seq_file *seq) +{ + struct ktf_cov_entry *entry; + struct ktf_cov *cov; + + seq_printf(seq, "%10s %44s %10s\n", "MODULE", "#FUNCTIONS", + "#CALLED"); + ktf_map_for_each_entry(cov, &cov_map, kmap) + seq_printf(seq, "%10s %44d %10d\n", + cov->kmap.key, cov->total, cov->count); + + seq_printf(seq, "\n%10s %44s %10s\n", "MODULE", "FUNCTION", "COUNT"); + ktf_map_for_each_entry(entry, &cov_entry_map, kmap) + seq_printf(seq, "%10s %44s %10d\n", + entry->cov ? entry->cov->kmap.key : "-", + entry->name, entry->count); + + ktf_cov_mem_seq_print(seq); +} + + +void ktf_cov_cleanup(void) +{ + struct ktf_cov *cov; + char name[KTF_MAX_KEY]; + + ktf_map_for_each_entry(cov, &cov_map, kmap) { + ktf_cov_disable(ktf_map_elem_name(&cov->kmap, name)); + } + ktf_map_delete_all(&cov_map); + ktf_map_delete_all(&cov_entry_map); + ktf_map_delete_all(&cov_mem_map); + kmem_cache_destroy(cov_mem_cache); +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_cov.h b/tools/testing/selftests/ktf/kernel/ktf_cov.h new file mode 100644 index 0000000..56ae753 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_cov.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_cov.h: Code coverage support interface for KTF. + */ + +#ifndef KTF_COV_H +#define KTF_COV_H + +#include +#include +#include +#include "ktf.h" +#include "ktf_map.h" + +enum ktf_cov_type { + KTF_COV_TYPE_MODULE, + KTF_COV_TYPE_MAX, +}; + +struct ktf_cov { + struct ktf_map_elem kmap; + enum ktf_cov_type type; /* only modules supported for now. */ + int count; /* number of unique functions called */ + int total; /* total number of functions */ + unsigned int opts; +}; + +/* Key for coverage entries (functions) consists in function address _and_ + * size - this allows us to find offsets in function on stack. Also used + * to track allocated memory - allocated address + size. + */ +struct ktf_cov_obj_key { + unsigned long address; + unsigned long size; +}; + +#define KTF_COV_ENTRY_MAGIC 0xc07e8a5e +struct ktf_cov_entry { + struct kprobe kprobe; + int magic; /* magic number identifying entry */ + struct ktf_map_elem kmap; + char name[KTF_MAX_KEY]; + struct ktf_cov_obj_key key; + struct ktf_cov *cov; + struct ktf_map cov_mem; + int refcnt; + int count; +}; + +#define KTF_COV_MAX_STACK_DEPTH 32 + +struct ktf_cov_mem { + struct ktf_map_elem kmap; + struct ktf_cov_obj_key key; + unsigned long flags; + unsigned int nr_entries; + unsigned long stack_entries[KTF_COV_MAX_STACK_DEPTH]; +}; + +#define KTF_COV_MEM_IGNORE 0x1 /* avoid recursive enter */ + +struct ktf_cov_mem_probe { + const char *name; + struct kretprobe kretprobe; +}; + +extern struct ktf_map cov_mem_map; + +#define ktf_for_each_cov_mem(pos) \ + ktf_map_for_each_entry(pos, &cov_mem_map, kmap) + +struct ktf_cov_entry *ktf_cov_entry_find(unsigned long, unsigned long); +void ktf_cov_entry_put(struct ktf_cov_entry *); +void ktf_cov_entry_get(struct ktf_cov_entry *); + +struct ktf_cov *ktf_cov_find(const char *); +void ktf_cov_put(struct ktf_cov *); +void ktf_cov_get(struct ktf_cov *); + +struct ktf_cov_mem *ktf_cov_mem_find(unsigned long, unsigned long); +void ktf_cov_mem_put(struct ktf_cov_mem *); +void ktf_cov_mem_get(struct ktf_cov_mem *); + +void ktf_cov_seq_print(struct seq_file *); +void ktf_cov_cleanup(void); + +int ktf_cov_enable(const char *, unsigned int); +void ktf_cov_disable(const char *); + +#endif From patchwork Tue Aug 13 06:09:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091151 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A07AF13AC for ; Tue, 13 Aug 2019 06:11:49 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8EDE528438 for ; Tue, 13 Aug 2019 06:11:49 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 82AE3284CE; Tue, 13 Aug 2019 06:11:49 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CA78628438 for ; Tue, 13 Aug 2019 06:11:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727692AbfHMGLn (ORCPT ); Tue, 13 Aug 2019 02:11:43 -0400 Received: from aserp2120.oracle.com ([141.146.126.78]:59252 "EHLO aserp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727633AbfHMGLm (ORCPT ); Tue, 13 Aug 2019 02:11:42 -0400 Received: from pps.filterd (aserp2120.oracle.com [127.0.0.1]) by aserp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68q2U039297; Tue, 13 Aug 2019 06:11:23 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=3I+2e/NCnQWfD8Vz78i6DuiXG2jHYFkQRKPcdrFu7Xc=; b=ZCaPbWlKx/J6N0HGU5IRAjsBK9CnXsfwASMXQXU1DR8QC5w2JtNF2CqCRQwAzqt/ExYb kTeV3cdG4eks3RYMPpZuWahp0Vrn+zDCuvgsNto1/G0W4jeiDq/OAjd9Qy1c/z7plQLi CkVz+EPzukG/gbTIo8e/aF3IRG2H69rGVstTTd4gUFw/Aq+LmnVCQK+veg4aGKuK9BX3 3WrcKfmir+qW4diDSQR4/Yml/sLHypnchluMY8apCAYef3/VKl08RYXfesXfwuB344X1 hA8SvSIqC6Q39AZJZxHbhqyrMOwv4irD73o/EwB+qUyLgWUHeqgJ2OizO69xBDS1v65/ tg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=3I+2e/NCnQWfD8Vz78i6DuiXG2jHYFkQRKPcdrFu7Xc=; b=DnRVNvIk1/YBqv2nBdtgOVL68mfYb1MEbcdMp6tJxh7qwjZCDKx1EQSXlzN5Kvh0+WZM 9Rn0YIu+29UYRk8wTzVBaJoYsXvYjjFQqlPzRG7vUPg55qoj4/cI8JIK9HZDsZNdJbXO xsy82PtovUatao+dLEfH5VoOMYnnZ6CbSQVMePA0TC/u7Q0X90n1eROVnh2v3+dIr/sF Geob03XS07Ydkg2eMqjbyWNbDZ7Eko6kN2FgqHrFJGDFqf44fFAVlkkSVuL6p7Wf1eqs mA8iXIGoJVeLZAYEvV20aoapyaigX6VN95BK4aWy3XrWEq5OvvSJXcvr3Ibu5YMYRvF0 Fw== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by aserp2120.oracle.com with ESMTP id 2u9nvp41hd-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:22 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67QrL096452; Tue, 13 Aug 2019 06:11:22 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by aserp3030.oracle.com with ESMTP id 2u9m0aye0w-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:22 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6BLtr026649; Tue, 13 Aug 2019 06:11:21 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:21 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 08/19] ktf: Configurable context support for network info setup Date: Tue, 13 Aug 2019 08:09:23 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=4 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=4 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP An implementation of configurable contexts for exchanging network information, to make it easier to run network tests potentially involving more than one kernel. ktf_netctx.h: Configurable context setup for multinode network tests Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/kernel/ktf_netctx.c | 132 +++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_netctx.h | 64 ++++++++- 2 files changed, 196 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_netctx.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_netctx.h diff --git a/tools/testing/selftests/ktf/kernel/ktf_netctx.c b/tools/testing/selftests/ktf/kernel/ktf_netctx.c new file mode 100644 index 0000000..84ef6ac --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_netctx.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_netcfg.h: Configurable context setup for multinode network tests + * + */ + +#include "ktf.h" +#include "ktf_netctx.h" +#include + +/* Configuration callback to configure a network context */ + +int ktf_netctx_cb(struct ktf_context *ctx, const void *data, size_t data_sz) +{ + struct ktf_netctx *nc = container_of(ctx, struct ktf_netctx, k); + struct ktf_addrinfo *kai = (struct ktf_addrinfo *)data; + short n = kai->n; + size_t param_sz; + + if (n < 2) { + terr("Unsupported number of nodes (%d) - must be at least 2", n); + return -EINVAL; + } + + param_sz = sizeof(*kai) + sizeof(kai->a) * (n - 2); + + if (n > nc->max_nodes || n < nc->min_nodes) { + terr("Unsupported number of nodes (%d) - must be between %d and %d!", + n, nc->min_nodes, nc->max_nodes); + return -EINVAL; + } + + if (param_sz != data_sz) { + terr("Expected %lu bytes of parameter data, received %lu!", + param_sz, data_sz); + return -EINVAL; + } + + if (nc->a && nc->a_sz != data_sz) { + kfree(nc->a); + nc->a = NULL; + } + + if (!nc->a) { + nc->a = kzalloc(data_sz, GFP_KERNEL); + if (!nc->a) + return -ENOMEM; + } + + memcpy(nc->a, kai, data_sz); + return 0; +} +EXPORT_SYMBOL(ktf_netctx_cb); + +void ktf_netctx_cleanup(struct ktf_context *ctx) +{ + struct ktf_netctx *nc = container_of(ctx, struct ktf_netctx, k); + + kfree(nc->a); +} +EXPORT_SYMBOL(ktf_netctx_cleanup); + +/* Make network contexts dynamically allocatable from user mode + * Caller must supply desired values for callback functions in @nct. + */ +int ktf_netctx_enable(struct ktf_handle *handle, struct ktf_netctx_type *nct, + short min_nodes, short max_nodes) +{ + struct ktf_context *lo_ctx; + struct ktf_addrinfo ai = { + .n = 2, + .rank = 0 + }; + int ret; + int i; + + ret = ktf_handle_add_ctx_type(handle, &nct->t); + if (ret) + return ret; + + nct->min_nodes = min_nodes; + nct->max_nodes = max_nodes; + strcpy(nct->t.name, "netctx"); + + for (i = 0; i < 2; i++) { + struct sockaddr_in *ai_in = (struct sockaddr_in *)&ai.a[i].addr; + + ai.a[i].addr.ss_family = AF_INET; + strcpy(ai.a[i].ifname, "lo"); + ai_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + + /* create and configure the loopback network context */ + lo_ctx = ktf_context_add_from(handle, "lo", &nct->t); + if (!lo_ctx) + return -ENOMEM; + + ret = ktf_context_set_config(lo_ctx, &ai, sizeof(ai)); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(ktf_netctx_enable); + +struct sockaddr_storage *ktf_netctx_addr(struct ktf_netctx *ctx, short rank) +{ + return &ctx->a->a[rank].addr; +} +EXPORT_SYMBOL(ktf_netctx_addr); + +const char *ktf_netctx_ifname(struct ktf_netctx *ctx, short rank) +{ + return ctx->a->a[rank].ifname; +} +EXPORT_SYMBOL(ktf_netctx_ifname); + +short ktf_netctx_rank(struct ktf_netctx *ctx) +{ + return ctx->a->rank; +} +EXPORT_SYMBOL(ktf_netctx_rank); + +short ktf_netctx_n(struct ktf_netctx *ctx) +{ + return ctx->a->n; +} +EXPORT_SYMBOL(ktf_netctx_n); diff --git a/tools/testing/selftests/ktf/kernel/ktf_netctx.h b/tools/testing/selftests/ktf/kernel/ktf_netctx.h new file mode 100644 index 0000000..414c744 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_netctx.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_netctx.h: Configurable context setup for multinode network tests + * + * KTF implements handling on the kernel side for this but leaves + * user space implementation to construct the corresponding + * stuct ktf_addrinfo parameter block + */ + +#ifndef _KTF_NETCTX_H +#define _KTF_NETCTX_H + +/* KTF matches against this type id as a possible discriminator: */ +#define KTF_NETCTX_TYPE_ID 0x2222 + +#define IFNAMSZ 16 + +struct ktf_peer_address +{ + struct sockaddr_storage addr; /* Address to use for this peer */ + char ifname[IFNAMSZ]; /* Local name of the interface with this address at peer */ +}; + +struct ktf_addrinfo +{ + short n; /* Number of nodes involved, including the local */ + short rank; /* Index into ktf_peer_address that corresponds to local host */ + struct ktf_peer_address a[2]; /* KTF expects size n instead of 2 here */ +}; + +#ifdef __KERNEL__ + +struct ktf_netctx { + struct ktf_context k; + struct ktf_addrinfo *a; /* Addr.info dyn.allocated based on incoming data */ + size_t a_sz; /* Size of the allocation in a, if any */ + short min_nodes; /* Minimum number of nodes for this context */ + short max_nodes; /* Maximum number of nodes this context supports */ +}; + +struct ktf_netctx_type { + struct ktf_context_type t; + short min_nodes; /* Minimum number of nodes for the context type */ + short max_nodes; /* Maximum number of nodes for the context type */ +}; + +int ktf_netctx_enable(struct ktf_handle *handle, struct ktf_netctx_type *nct, + short min_nodes, short max_nodes); + +int ktf_netctx_cb(struct ktf_context *ctx, const void *data, size_t data_sz); +void ktf_netctx_cleanup(struct ktf_context *ctx); + +struct sockaddr_storage *ktf_netctx_addr(struct ktf_netctx *ctx, short rank); +const char *ktf_netctx_ifname(struct ktf_netctx *ctx, short rank); +short ktf_netctx_rank(struct ktf_netctx *ctx); +short ktf_netctx_n(struct ktf_netctx *ctx); + +#endif + +#endif From patchwork Tue Aug 13 06:09:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091155 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6CD7C746 for ; Tue, 13 Aug 2019 06:11:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5A0B42845E for ; Tue, 13 Aug 2019 06:11:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 42C9E28587; Tue, 13 Aug 2019 06:11:55 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 895C728438 for ; Tue, 13 Aug 2019 06:11:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727417AbfHMGLu (ORCPT ); Tue, 13 Aug 2019 02:11:50 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:37622 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727246AbfHMGLt (ORCPT ); Tue, 13 Aug 2019 02:11:49 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68xaE022057; Tue, 13 Aug 2019 06:11:28 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=HhMcyroqMij2d5Lg82WAj2ypfByRL1QVurPA9HxkD10=; b=evEJsYytDvz7TszRj3tKgr0f8rPrOK8GLUt+racWZSc9xGskbSd61/F+PskkMlEoos2x mS0Uq1Y59M02yvsarvALYv3o0gRVn6SVUp3GWvptZ22sT0kE9Zw4WqSk4y6f9XhWfTDY Y01ew73D+sh/bCIu+igf7SKNEHCUqs8o22/6akImSyPqp1VHsxuOrAuFJUwRxGWG5WDA EK4Zz7mkjXGU6PICiS2uXstF7iiG5uBLEZjDUDR+LNlDc5kZ/Figfm8SRpm6unAfRuLS xWWW7koG50Rbt+SBNd6Iy665SBvPhFt2eGeYFQHHnd8eQjhwF7NmLpdal/relVp0URfD BQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=HhMcyroqMij2d5Lg82WAj2ypfByRL1QVurPA9HxkD10=; b=pd2ldQlK7kezzlqOr6fZolDPz0faMs3pLmMHzqqXPzZ6Nre82W5hazHH4zo4MIKK3C8R Dzhrx9jEplMlSENGA1sbohJ01q8FwWCe1MD1gov1yd+LRWDz6lvMtpoHD6DUXeiFPnQO fLwo4njOtLTTYlrB036zledWx8Oi6OXfpN1+snxLuHgnKk0D0kyDJnmvhUoc+QvO64Dq B3cSQp2lG7NDK6/okI7DqY/itFQdBgsnJaKPH50pWydmUUYJCoT0mDhhfTLD6UA/9k+n ZX0koUsBIijOkgXT9h6wumr+i9MlPepjvOnEcZRfPSJ956VVnAWWhoqF6D9CIbfjstvn Yg== Received: from aserp3020.oracle.com (aserp3020.oracle.com [141.146.126.70]) by userp2120.oracle.com with ESMTP id 2u9pjqbvkh-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:28 +0000 Received: from pps.filterd (aserp3020.oracle.com [127.0.0.1]) by aserp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67ShE157015; Tue, 13 Aug 2019 06:11:27 GMT Received: from userv0122.oracle.com (userv0122.oracle.com [156.151.31.75]) by aserp3020.oracle.com with ESMTP id 2u9nrenm67-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:27 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6BPgd026354; Tue, 13 Aug 2019 06:11:25 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:25 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 09/19] ktf: resolve: A helper utility to aid in exposing private kernel symbols to KTF tests. Date: Tue, 13 Aug 2019 08:09:24 +0200 Message-Id: <09d79897772dd370a61967b03e83fa4b8663698b.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Takes an input file foo.txt with a list of symbols on the form: #module foo #header foo.h private_foo_symbol_1 private_foo_symbol_2 and creates usable definitions to access these (requires CONFIG_KALLSYMS_ALL). This is useful to be able to easily and cleanly test internal interfaces of a module. Examples follows in the selftests later in the series. Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/scripts/resolve | 188 +++++++++++++++++++++- 1 file changed, 188 insertions(+) create mode 100755 tools/testing/selftests/ktf/scripts/resolve diff --git a/tools/testing/selftests/ktf/scripts/resolve b/tools/testing/selftests/ktf/scripts/resolve new file mode 100755 index 0000000..74005f7 --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/resolve @@ -0,0 +1,188 @@ +#!/usr/bin/python + +# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. +# Author: Knut Omang +# +# SPDX-License-Identifier: GPL-2.0 +# +# A script to generate code and header definitions for +# module and kernel symbols not directly exposed to KTF. +# (See the documentation for KTF for details) +# + +import os, sys, re, shutil, string, ConfigParser + +def usage(): + print "Usage: resolve symbolfile outputfile" + exit(0) + +class FuncInfo: + def __init__(self, sym, re_sym, re_decl): + self.symbol = sym + self.re_sym = re_sym + self.re_decl = re_decl + + def __repr__(self): + return "FuncInfo: [%s: %s]" % (self.symbol, self.re_decl) + +class Module: + def __init__(self, name, header): + self.cur_header = header + self.prefix = "Z" + self.name = name + self.symbols = {} # Input: headerfile -> symbol list + self.func_def = [] # FuncInfo list + self.debug = False + all_modules.append(self) + + def log(self, str): + if self.debug: + print str + + def SetHeader(self, header): + self.cur_header = header + + def AddSymbol(self, sym): + try: + h = self.symbols[self.cur_header] + h.append(sym) + except: + self.symbols[self.cur_header] = [sym] + + # Open along include path: + def Open(self, filename): + for p in includepath: + try: + f = os.path.join(p, filename) + self.log(" -- trying " + f) + header = open(f,'r') + return header + except: + continue + sys.stderr.write(" ** unable to open \"%s\"\n" % filename) + return None + + # Parse the relevant header files for function defs: + def ParseDefs(self): + for hf in self.symbols.keys(): + self.log(" ** Parsing %s:" % hf) + header = self.Open(hf) + if header == None: + return + content = header.read() + symbols = self.symbols[hf] + types = r"(extern|u8|u16|u32|u64|int|long|size_t|off_t|loff_t|void|struct|union\s)(.*[\*\s]" + funor = ")(" + "|".join(symbols) + r")(\([^\)]+\);)$" + tt = types + funor + s = re.compile(tt, re.MULTILINE) + miter = s.finditer(content) + s_count = 0 + for m in miter: + sym = m.group(3) + re_name = "_".join([self.prefix, sym]) + re_decl = "%s%s(*%s)%s" % (m.group(1), m.group(2), re_name, m.group(4)) + self.func_def.append(FuncInfo(sym, re_name, re_decl)) + s_count = s_count + 1 + + if s_count != len(symbols): + print " ** Warning: File %s: Found %d definitions from %d symbols!" % \ + (hf, s_count, len(symbols)) + print " ** - please check/fix output manually!" + + # Output functions: + def write_funcs(self, file): + for fi in self.func_def: + file.write("\t%s\n"% fi.re_decl) + + def write_defines(self, file): + for fi in self.func_def: + file.write("#define %s ktf_syms.%s\n" % (fi.symbol, fi.re_sym)) + + def write_resolve_calls(self, file): + for fi in self.func_def: + file.write("\tktf_resolve_symbol(%s, %s);\n" % (self.name, fi.symbol)) + +usage_h = False +my_argv = [] +includepath = [""] + +for arg in sys.argv[1:]: + if arg == "-h": + usage_h = True + continue + incl = re.match(r"-I([^\s]+)", arg) + if incl != None: + includepath.append(incl.group(1)) + continue + genopt = re.match(r"-([^\s]+)", arg) + if genopt != None: + # Silently ignore other cc options as we accept cc-flags-y: + # + continue + my_argv.append(arg) + +# Main program: + +if len(my_argv) != 2 or usage_h: + usage() + +symfile = my_argv[0] +outputfile = my_argv[1] + +all_modules = [] +module = Module("kernel", None) # Default, at the top of the file is the main kernel symbols +header = None # A header directive is required before any symbols + +try: + file = open(symfile, 'r') +except: + print "Unable to open config file \"%s\"" % symfile + exit(1) +for line in file: + match = re.match(r"^#(\w+) ([\w\.]+)\s*$", line) + if match != None: + cmd = match.group(1) + value = match.group(2) + if cmd == "module": + module = Module(value, header) + elif cmd == "header": + header = value + module.SetHeader(header) + else: + raise ResolveError("Unknown directive \"%s\"" % cmd) + #print "%s set to %s" % (cmd, value) + continue + match = re.match(r"\s*(\w+)\s*", line) + if match != None: + s = match.group(1) + module.AddSymbol(s) + +for m in all_modules: + m.ParseDefs() + +try: + output = open(outputfile, "w") +except: + print "Failed to open output header file \"%s\"" % outputfile +output.write('#ifndef _KTF_RESOLVE_H\n#define _KTF_RESOLVE_H\n') +output.write('#include "ktf.h"\n\nstruct ktf_syms {\n') + +for m in all_modules: + m.write_funcs(output) + +output.write('};\n\n') + +for m in all_modules: + m.write_defines(output) + +output.write('\n\nextern struct ktf_syms ktf_syms;\n') +output.write('\nint ktf_resolve_symbols(void);\n#ifndef KTF_CLIENT\n') +output.write('struct ktf_syms ktf_syms;\n\n') + +output.write('\nint ktf_resolve_symbols(void)\n{\n') + +for m in all_modules: + m.write_resolve_calls(output) + +output.write('\treturn 0;\n}\n\n#endif /* !KTF_CLIENT */\n#endif /* _KTF_RESOLVE_H */ \n') +output.close() From patchwork Tue Aug 13 06:09:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091199 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 21B68746 for ; Tue, 13 Aug 2019 06:12:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 088B527FE4 for ; Tue, 13 Aug 2019 06:12:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EF16B285DD; Tue, 13 Aug 2019 06:12:55 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9867627FE4 for ; Tue, 13 Aug 2019 06:12:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727471AbfHMGL5 (ORCPT ); Tue, 13 Aug 2019 02:11:57 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:37712 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727729AbfHMGL4 (ORCPT ); Tue, 13 Aug 2019 02:11:56 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68wdd022017; Tue, 13 Aug 2019 06:11:31 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=Ps8fgodfDdVfzRX6A/PCjAl5PtBQ0lTzWUwwAkpC9kM=; b=F6qA0XyOvx3AWMxyBWPDs6X6gsL0Ix6YNbPl4qVTJN41Bx935sBbowiWBAhJHDjzlVcK F4O+KMvv2lmMf7xgplwbFTE/VKy/uEJ7Mgw2uwEWMxlI7PQUXQ0U8dpvI16AXQcMVYeG +PU4MP79BO3xHxFvX9P0Jq+ntlTsYSVLivpNHbMFkd4XadrNe7EMk+VoNMe0YB9m1onF iJR1yxhWnUSjVNz0iN4tUNirC/9fbqWFo9Gl2Cf21D7/qQsk1odDC6M1VSq4/XYXF4r+ DYSy17YZSG6815gyXDvW1ByRsScdV54Mu14eLRMduauLk3nqkRzMhSD3ozJ6PEb48UWB Hw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=Ps8fgodfDdVfzRX6A/PCjAl5PtBQ0lTzWUwwAkpC9kM=; b=dasek7rrYKRTtRucf7GO4y4fFvxZuw2/hCOntkkiJJ1KB96ZhQV5tS18prv+C2kShz8B 91tzWWqDtPjzAIG2pHxxVFIDTQndpcwmvzpHUDydFmCpQIfHtFFHCw9Bgj7c/neB67W2 QGObyhs9faT5HHlSV57RoJfxg9OA+uZamv/5G1eROvLmUNIeaKvusBtIQVMV0BTkK9Zz 5/jY3NYee2IXSf2p2HZNUKl+b1pFY0cw3Pyw5J4iXkcjGhexm9/niq6mZ0vQTdIv1AcO rfp2pZcqYVHdo1xwpdHIX5irjms5PT0/BxkztclPBue0De8uLSMN5pSSI1SS9/bx4Pwh 3Q== Received: from userp3020.oracle.com (userp3020.oracle.com [156.151.31.79]) by userp2120.oracle.com with ESMTP id 2u9pjqbvku-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:31 +0000 Received: from pps.filterd (userp3020.oracle.com [127.0.0.1]) by userp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68Phv056320; Tue, 13 Aug 2019 06:11:30 GMT Received: from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236]) by userp3020.oracle.com with ESMTP id 2u9n9hs2rq-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:30 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6BTx5013533; Tue, 13 Aug 2019 06:11:29 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:28 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 10/19] ktf: Add documentation for Kernel Test Framework (KTF) Date: Tue, 13 Aug 2019 08:09:25 +0200 Message-Id: <2c37526d9d5378d92819ab620b41140edeccaa1c.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Knut Omang --- Documentation/dev-tools/ktf/concepts.rst | 242 ++++++++++++++- Documentation/dev-tools/ktf/debugging.rst | 248 +++++++++++++++- Documentation/dev-tools/ktf/examples.rst | 26 ++- Documentation/dev-tools/ktf/features.rst | 307 ++++++++++++++++++- Documentation/dev-tools/ktf/implementation.rst | 70 ++++- Documentation/dev-tools/ktf/index.rst | 14 +- Documentation/dev-tools/ktf/installation.rst | 73 ++++- Documentation/dev-tools/ktf/introduction.rst | 134 ++++++++- Documentation/dev-tools/ktf/progref.rst | 144 ++++++++- 9 files changed, 1258 insertions(+) create mode 100644 Documentation/dev-tools/ktf/concepts.rst create mode 100644 Documentation/dev-tools/ktf/debugging.rst create mode 100644 Documentation/dev-tools/ktf/examples.rst create mode 100644 Documentation/dev-tools/ktf/features.rst create mode 100644 Documentation/dev-tools/ktf/implementation.rst create mode 100644 Documentation/dev-tools/ktf/index.rst create mode 100644 Documentation/dev-tools/ktf/installation.rst create mode 100644 Documentation/dev-tools/ktf/introduction.rst create mode 100644 Documentation/dev-tools/ktf/progref.rst diff --git a/Documentation/dev-tools/ktf/concepts.rst b/Documentation/dev-tools/ktf/concepts.rst new file mode 100644 index 0000000..9b9ef1a --- /dev/null +++ b/Documentation/dev-tools/ktf/concepts.rst @@ -0,0 +1,242 @@ + +5. KTF Basic Concepts +--------------------- + +Tests and test suites +********************* + +The simplest form of test is to just specify:: + + TEST(suite_name, test_name) + { + + } + +A KTF test is declared with TEST() or TEST_F(), which both +takes both a test suite name and a test name, which are two different +name spaces. Consequently, each test belongs to one test suite, and +the test suites are created based on what tests that exists. +A test suite is just a container of tests which in user space +contributes to the extended name of a test. Test names must be +unique within a suite, and test names must also be unique within a +source file, since the test name is the only parameter needed +when adding a test. + +All tests must be added using ADD_TEST or ADD_LOOP_TEST to be visible +to KTF's runtime framework. This allows tests to be declared while +under development, but not added (or the ADD_TEST could be commented +out) if the test or the kernel module under test is not ready +yet for some reason. + +Test fixtures +************* + +As in other unit test frameworks, a test fixture is a mechanism to +allow several tests to run under the same conditions, in that setup +and teardown is done before and after each test. In KTF a test fixture +must first be declared with DECLARE_F() which takes a fixture name +followed by a list of attributes and an end brace, and initialized +with INIT_F() which takes the fixture name and a setup and teardown +function to be defined subsequently. Note that there are +no start brace, which is intentional:: + + DECLARE_F(a_fixture) + int value; + struct my_details; + ... + }; + INIT_F(a_fixture, a_setup, a_teardown); + +Then to the implementation of the fixture, in the form of actual setup and +a teardown functions that may operate on the attributes of the fixture:: + + SETUP_F(a_fixture, a_setup) + { + a_fixture->value = 42; /* or whatever.. */ + + /* If everything went well during setup: */ + a_fixture->ok = true; + } + + TEARDOWN_F(a_fixture, a_teardown) + { + + } + +Now individual tests that uses this fixture can be declared with:: + + TEST_F(a_fixture, suite_name, test_name) + { + + } + +Contexts +******** + +A context provides a way to instantiate a test in multiple ways. +A typical use case is if you have multiple similar devices +you want to run a set of tests on. Another use case could be that +you want to run a set of tests under different configurations. + +You are free to let the number and names of these contexts +vary as to where you run your test. For the devices use case, you can +have the init function loop through all available devices, to identify +the ones the tests applies to, then instantiate a context for each +device, possibly using the device name for trackability. The context +names will be prepended to the test name and the number of available +tests will be multipled by the number of contexts. + +Note that the state of a context persists through the whole "life" of +the module (until it gets unloaded) so it can be used to store more +long term bookeeping in addition to any configuration information. +The test writer must make sure that subsequent runs of the test suite +(or parallel runs!) does not interfere with +each other. Similar to fixtures, there's a generic part that KTF uses, +and it can be extended the normal way. Make sure to declare the +test specific context struct type with an element named:: + + struct ktf_context k; + +typically as the first element of the struct, then you can continue +with whatever other datastructure desired. A test module can declare +and use as many contexts as desired. Note that contexts are associated +with and unique within a ``handle`` (see below). So if you need +to use a different set of contexts for different tests, you need to +put these contexts and tests into different handles. + +A context can be added using something like:: + + KTF_CONTEXT_ADD(&my_struct.k, "mycfg") + +where the first argument is a reference to the ktf_context structure +within the test specific structure, and the second argument is a text +name to use to refer to the context. Once one or more contexts exists +for a handle, tests for that handle will show up with names postfixed +by the context name, and there will be a distinct version of the test +for each context, e.g if a handle has contexts named ``c1`` and +``c2``, and tests declared with ``TEST(x, t1)`` and ``TEST(x, t2)``, +then this will manifest as 4 tests:: + + x.t1_c1 + x.t1_c2 + x.t2_c1 + x.t2_c2 + +Tests that depends on having a context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +It is likely that once you have a set of tests that uses contexts, +that they also depends on a context being available, e.g. that the +``ctx`` variable inside a test points to a non-nil context. One example +use case for contexts would be a certain class of device. Such a +device might or might not be available in the system. If one or more +devices are available, you might want to have tests named +``c.t1_dev1`` and ``c.t1_dev2`` where ``dev1`` and ``dev2`` are the +device names for these devices in the system, but never have any +``c.t1`` test without a device. You can +enforce this by letting tests associated with a handle requiring a +context to even show up as a test in the list or be available for +execution. Instead of using ``KTF_INIT()`` or ``KTF_HANDLE_INIT()``, +use ``KTF_CTX_INIT()`` or ``KTF_HANDLE_CTX_INIT()``. + +Configurable contexts +~~~~~~~~~~~~~~~~~~~~~ +Sometimes it might be useful to be able to configure a context for the +execution of some (or all) of the tests using the context. +This can be because the system the tests are running on might have +different hardware or software capabilities, or might rely on +differing device or network setup or naming. Typically we want a unit +test suite to have as little configuration and parameterization as +possible, so recommended use is for parameters that is not directly +related to the operation of the test, but more for situations where +parameters outside the test itself needs to be set up, such as connect +details for a network service to test against, or a peer unit test +process for network related tests that require more than one +system to run. To specify a configurable context, use:: + + int my_cfg_callback(struct ktf_context *ctx, const void* data, size_t data_sz); + + KTF_CONTEXT_ADD_CFG(&my_struct.k, "mycfg", my_cfg_callback, type_id) + +The ``data`` pointer (and it's length) should be provided from user +space, and it is up to the test specific user space and kernel space +code to decide with the configuration is all about. If 0 is returned, +KTF considers the context to be configured, otherwise it will retain +it's current state, which will initially be unconfigured. +The callback return value is stored as an errno value in ``ktf_context`` in the +variable ``config_errno``, which will initially be set to ``ENOENT``, +to indicate unconfigured. The test can use this value +to decide what to do, such as failing with a message about missing +configuration or just silently pass and ignore the case if not +configured. The ``type_id`` parameter is used as a unique +identifier for the kernel side to decide how to interpret the +parameter, which is useful if different contexts wants to implement +very different configuration options. It also allows two different +test modules to use the same context names but with different +parameters by using different context types. + +In the user space part of the test, configuration information +can be set for a context using:: + + KTF_CONTEXT_CFG(name, type_id, parameter_type, parameter_ref) + +A simple example of a configurable test can be seen in +the selftests test in ``selftest/context.c`` (kernel part) and +``user/context.cpp`` (user part) and the header file +``selftest/context_self.h`` shared between user space and kernel space. + +Context types and user space created contexts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Contexts belong to a ``context type``, which is a mechanism to group +contexts into types that have similar properties. It is up to the +kernel test module using these contexts what the meaning of this is, +but a simple semantics can be that all contexts of a certain type has +the same parameter block and the type ID can be used to check what +type of context it is before trying to resolve or verify the +parameters. For contexts pre-created by the kernel module, type IDs +can be freely selected and does not have any further meaning beyond +this. + +Context types can however also be used to selectively allow user +space to dynamically create new contexts of a specific type. To enable +such functionality, the kernel test module will need to enable it for +one or more context types. This is done by means of the following call:: + + ktf_handle_add_ctx_type(struct ktf_handle *handle, + struct ktf_context_type *type) + +kernel side call, which tells KTF that a new context type with a given type ID +permits user applications to create new contexts. This is useful for +instance if user parameters or other information most easily +obtainable from user land at test runtime is most easily available +from user space. + +Handles +******* + +Unlike user land unit test frameworks, which can rely on everything +being cleaned up when the test program finished, KTF and test writers +must pay the normal kernel level attention to allocations, references and +deallocations. + +KTF itself uses the concept of a *handle* to track tests, +test suites and contexts associated with a kernel module. +Contexts are also associated with a handle. Since the availability of +contexts for a handle determines the availability of tests and the +naming of them, it can be useful to have separate spaces for tests +that relies on some context and tests that do not, to avoid +aggregating up multiple test cases that are identical. +Handles thus also have a namespace effect in that it is possible to +have two contexts with the same name, and possibly a different type, +by putting them in different handles. + +The simplest mode of usage is for each module to use KTF_INIT() and +KTF_CLEANUP() in it's __init and __exit functions. KTF_INIT implicitly declares and +initializes a global handle __test_handle that gets cleaned up again +in KTF_CLEANUP, making the handle something a test developer does not +need to think too much about. However, sometimes a KTF kernel module +may be such organized that it makes sense to use more than one handle. +KTF allows the creation/cleanup of explicitly named handles by means of +KTF_HANDLE_INIT(name) and KTF_HANDLE_CLEANUP(name). This can be used +as an alternative to KTF_INIT()/KTF_CLEANUP() but requires the use of +ADD_TEST_TO(handle, testname) instead of the normal ADD_TEST(testname) +for adding tests to be executed. diff --git a/Documentation/dev-tools/ktf/debugging.rst b/Documentation/dev-tools/ktf/debugging.rst new file mode 100644 index 0000000..4f1d0ff --- /dev/null +++ b/Documentation/dev-tools/ktf/debugging.rst @@ -0,0 +1,248 @@ +8. Debugging KTF +-------------------- + +Structured debugging/tracing by printf +====================================== + +The kernel side of KTF implements it's own simple printk based logging +abstraction. You can create log entries by means of calls to the macro +tlog(level, format,...) - level is a bitmask where individual bits +represents log classes. You can read and set this bitmask via a +writable ``/sys`` file:: + + # cat /sys/module/ktf/parameters/debug_mask + 0x1 + echo 0x3 > /sys/module/ktf/parameters/debug_mask + # cat /sys/module/ktf/parameters/debug_mask + 0x3 + +The default value of ``debug_mask`` is 0x1 = INFO level. +Bits are by convention supposed to be +ordered by verbosity, with the lowest bits reserved for low volume, +important messages, while the higher bist are left for more verbose +debugging. You can also use this mechanism for simple debugging of KTF's +interaction with your tests, as the core KTF code contains some log +statements to make it easier to follow and debug involved +instances of KTF objects. + +Similarly, the user library implementing the interaction with the +user land test runner can log details about this. You can enable such +logging by providing a similar bitmask via the environment variable +KTF_DEBUG_MASK. + +Debugging fatal errors in tests +=============================== + +So your KTF test crashed the kernel? Let's see how you can use crash to +examine KTF test cases, individual test logs and see which test is running. + +First step is we need to load symbols for KTF. To get text, data and +bss section locations for the ktf module (assuming it's currently +loaded):: + + # cd /sys/module/ktf/sections + # cat .text .data .bss + 0xffffffffa0bdb000 + 0xffffffffa0bdf000 + 0xffffffffa0bdf7a0 + +Now run crash on your corefile (or /proc/kcore for a live kernel):: + + # crash + crash> add-symbol-file /path/to/kernel/ktf.ko 0xffffffffa0bdb000 -s .data 0xffffffffa0bdf000 -s .bss 0xffffffffa0bdf7a0 + +Now we can see the global test_cases rbtree via the handy +"tree" command. It displays an rbtree, and because test_cases +is an rbtree under the hood we can display the set of test +cases as follows:: + + crash> tree -t rbtree -s ktf_case test_cases + ffff88036f710c00 + struct ktf_case { + kmap = { + node = { + __rb_parent_color = 1, + rb_right = 0x0, + rb_left = 0x0 + }, + key = "selftest\000cov\000probereturn\000probeentry\000wrongversion\000dummy\000simplemap", + map = 0xffffffffa0bdd1a0 , + refcount = { + refcount = { + counter = 2 + } + } + }, + tests = { + root = { + rb_node = 0xffff880250ac4a00 + }, + size = 5, + lock = { + { + rlock = { + raw_lock = { + { + head_tail = 655370, + tickets = { + head = 10, + tail = 10 + } + } + } + } + } + }, + elem_comparefn = 0x0, + elem_freefn = 0xffffffffa0bd8760 + }, + debugfs = { + debugfs_results_testset = 0xffff88021a18a3c0, + debugfs_results_test = 0xffff88021a18aa80, + debugfs_run_testset = 0xffff88021a18a300, + debugfs_run_test = 0xffff88021a18a840 + } + } + +Here we had 1 test case - from the "key" field +we can see it is called "selftest" - in fact it is +KTF's self tests. Within that one test cases we see +the rbtree for the indivdual selftest tests has a root +rb_node:: + + tests = { + root = { + rb_node = 0xffff880250ac4a00 + }, + +By printing _that_ tree of ktf_test structures from +root node (-N) 0xffff880250ac4a00 we can see our +individual tests:: + + crash> tree -t rbtree -s ktf_test -N 0xffff880250ac4a00 + ffff880250ac4a00 + struct ktf_test { + kmap = { + node = { + __rb_parent_color = 1, + rb_right = 0xffff880250ac5b00, + rb_left = 0xffff880250ac5d00 + }, + key = "probeentry\000wrongversion\000dummy\000simplemap\000\000\000\000\000\020\276\240\377\377\377\377 \020\276\240\377\377\377\377@\020\276\240\377", + map = 0xffff88036f710c68, + refcount = { + refcount = { + counter = 2 + } + } + }, + tclass = 0xffffffffa0be41a4 "selftest", + name = 0xffffffffa0be41bd "probeentry", + fun = 0xffffffffa0be1920, + start = 0, + end = 1, + skb = 0xffff88003fc03800, + log = 0xffff88003fa58000 "", + lastrun = { + tv_sec = 1506072537, + tv_nsec = 289494591 + }, + debugfs = { + debugfs_results_testset = 0x0, + debugfs_results_test = 0xffff88021a18ac00, + debugfs_run_testset = 0x0, + debugfs_run_test = 0xffff88021a18af00 + }, + handle = 0xffffffffa0be5480 + } + ffff880250ac5d00 + struct ktf_test { + kmap = { + node = { + __rb_parent_color = 18446612142257621505, + rb_right = 0x0, + rb_left = 0xffff880250ac4b00 + }, + key = "dummy\000simplemap\000\000\000\000\000\020\276\240\377\377\377\377 \020\276\240\377\377\377\377@\020\276\240\377\377\377\377`\020\276\240\377\377\377\377\200\020\276\240\377\377\377\377\320\020\276\240\377", + map = 0xffff88036f710c68, + refcount = { + refcount = { + counter = 2 + } + } + }, + tclass = 0xffffffffa0be41a4 "selftest", + name = 0xffffffffa0be41d5 "dummy", + fun = 0xffffffffa0be10f0, + start = 0, + end = 1, + skb = 0xffff88003fc03800, + log = 0xffff88003fa59800 "", + lastrun = { + tv_sec = 1506072537, + tv_nsec = 289477354 + }, + debugfs = { + debugfs_results_testset = 0x0, + debugfs_results_test = 0xffff88021a18a900, + debugfs_run_testset = 0x0, + debugfs_run_test = 0xffff88021a18a9c0 + }, + handle = 0xffffffffa0be5480 + } + ... + crash> + + +The "log" fields are empty as each test passed, but we can +see from the "lastrun" times when the tests were run. +Logs will contain assertion failures etc in case of failure. + +Note that each test has a "handle" field also - this is +the KTF handle which was used to register the test. Each +handle also shows the currently-executing (if in the middle +of a test run) test associated with it, so if we want to +see where test execution was we can simply print the handle:: + + crash> print *(struct ktf_handle *)0xffffffffa0be5480 + $13 = { + test_list = { + next = 0xffffffffa0be5480, + prev = 0xffffffffa0be5480 + }, + handle_list = { + next = 0xffffffffa0be5490, + prev = 0xffffffffa0be5490 + }, + ctx_map = { + root = { + rb_node = 0x0 + }, + size = 0, + lock = { + { + rlock = { + raw_lock = { + { + head_tail = 0, + tickets = { + head = 0, + tail = 0 + } + } + } + } + } + }, + elem_comparefn = 0x0, + elem_freefn = 0x0 + }, + id = 0, + version = 4294967296, + current_test = 0x0 + } + crash> + +In this case current_test is NULL, but if we crashed in the +middle of executing a test it would show us which struct ktf_test * +it was. diff --git a/Documentation/dev-tools/ktf/examples.rst b/Documentation/dev-tools/ktf/examples.rst new file mode 100644 index 0000000..ec1f6e9 --- /dev/null +++ b/Documentation/dev-tools/ktf/examples.rst @@ -0,0 +1,26 @@ +6. Example test code +-------------------- + +Here is a minimal dummy example of a KTF unit test suite that defines +two tests, ``hello_ok`` and ``hello_fail``. The test is in the examples +directory and is built with KTF: + +.. literalinclude:: ../examples/hello.c + :language: c + +To run the test, cd to your KTF build tree and insmod the ktf module and +the module that provides the test:: + + insmod kernel/ktf.ko + insmod examples/hello.ko + +Now you should be able to run one or more of the tests by running the +application ``ktfrun`` built in ``user/ktfrun``. You should be able to run +that application as an ordinary user:: + + ktfrun --gtest_list_tests + ktfrun --gtest_filter='*fail' + ktfrun --gtest_filter='*ok' + +There are more examples in the examples directory. KTF also includes a +``selftest`` directory used to test/check the KTF implementation itself. diff --git a/Documentation/dev-tools/ktf/features.rst b/Documentation/dev-tools/ktf/features.rst new file mode 100644 index 0000000..2dc736c --- /dev/null +++ b/Documentation/dev-tools/ktf/features.rst @@ -0,0 +1,307 @@ +3. KTF kernel specific features +------------------------------- + +Reference to module internal symbols +************************************ + +When working with unit tests, the need to access non-public interfaces +often arises. In general non-public interfaces is of course not intended to +be used by outside programs, but a test framework is somewhat special here +in that it is often necessary or desirable to unit test internal +data structures or algorithms even if they are not exposed. The program +under test may be a complicated beast by itself and merely exercising the +public interfaces may not be flexible enough to stress the internal code. +Even if it is possible to get the necessary "pressure" from the outside +like that, it might be much more challenging or require a lot more work. + +The usual method to gain access to internal interfaces is to be part of the +internals. To some extent this is the way a lot of the kernel testing +utilities operate. The obvious advantages of this is that the test code +'automatically' follows the module and it's changes. The disadvantage is +that test code is tightly integrated with the code itself. One important +goal with KTF is to make it possible to write detailed and sophisticated +test code which does not affect the readability or complexity of the tested +code. + +KTF contains a small python program, ``resolve``, which +parses a list of symbol names on the form:: + + #module first_module + #header first_header.h + private_symbol1 + private_symbol2 + ... + #header second_header.h + #module next_module + ... + +The output is a header file and a struct containing function pointers and +some convenience macro definitions to make it possible to 'use' the +internal functions just as one would if within the module. This logic is +based on kallsyms, and would of course only work if that functionality is +enabled in the kernel KTF compiles against. Access to internal symbols +this way is controlled by the kernel config options CONFIG_KALLSYMS +and CONFIG_KALLSYMS_ALL, which must be set to "y". + +If you create a new test project using the ``ktfnew`` script, you can +put your private symbol definitions in a file ``ktf_syms.txt`` in the +kernel directory, and KTF will automatically generate ``ktf_syms.h``, +which you can then include in your test file to get to these symbols. +This functionality is also used by the KTF selftests, which might +serve as an example to get started. + +Note also that for exported symbols, if you build your module out-of-tree in +addition to KTF and the test modules, you might need to also add those +module's ``Module.symvers`` files to ``KBUILD_EXTRA_SYMBOLS`` +(See kernel documentation for this) to find them during test module build. + +Requesting callbacks when a certain function gets called/returns +**************************************************************** + +Tap into function entry using KTF entry probes. Many tests need to +move beyond kernel APIs and ensure that side effects (logging a +message etc) occur. A good way to do this is to probe entry of relevant +functions. In order to do so in KTF you need to: + + - define an entry probe function with the same return value and arguments + as the function to be probed + + - within the body of the entry probe function, ensure return is wrapped with + KTF_ENTRY_PROBE_RETURN(); + + - entry probes need to registered for use and de-registered when done via + KTF_[UN]REGISTER_ENTRY_PROBE(, ). + +See example h4.c in examples/ for a simple case where we probe printk() and +ensure it is called. + +Sometimes is is also useful to check that an intermediate function is returning +an expected value. Return probes can be used to register/probe function +return. In order to probe function return: + + - define a return probe point; i.e + KTF_RETURN_PROBE(, ) + + - within the body of the return probe the return value can be retrieved + via KTF_RETURN_VALUE(). Type will obviously depend on the function + probed so should be cast if dereferencing is required. + + - return probes need to be registered for use and unregistered when done + via KTF_[UN]REGISTER_RETURN_PROBE(, ). + +See example h4.c in examples/ for a simple case where we verify return value +of printk(). + +Note that this functionality is only available on kernels with CONFIG_KPPROBES +and CONFIG_KRETPROBES set to "y". + +Overriding functions +******************** +in some cases, we wish to override harmful functions when inducing failues in +tests (e.g. skb_panic()). Override is done via kprobes and we define as follows:: + + KTF_OVERRIDE(oldfunc, newfunc) + { + ... + KTF_SET_RETURN_VALUE(1); + KTF_OVERRIDE_RETURN; + } + + TEST(...) + { + KTF_REGISTER_OVERRIDE(oldfunc, newfunc); + ... + KTF_UNREGISTER_OVERRIDE(oldfunc, newfunc); + } + +Override should be used sparingly; we'd rather test the code as-is and use +entry/return probes where possible. + +Note that this functionality is only available on kernels with CONFIG_KPPROBES +and CONFIG_KRETPROBES set to "y". + +Coverage analytics +****************** + +While other coverage tools exist, they generally involve gcc-level support +which is required at compile-time. KTF offers kernel module coverage +support via kprobes instead. Tests can enable/disable coverage on a +per-module basis, and coverage data can be retrieved via:: + + # more /sys/kernel/debug/ktf/coverage + +For a given module we show how many of its functions were called versus the +total, e.g.:: + + # cat /sys/kernel/debug/ktf/coverage + MODULE #FUNCTIONS #CALLED + selftest 14 1 + +We see 1 out of 14 functions was called when coverage was enabled. + +We can also see how many times each function was called:: + + MODULE FUNCTION COUNT + selftest myelem_free 0 + selftest myelem_cmp 0 + selftest ktf_return_printk 0 + selftest cov_counted 1 + selftest dummy 0 + +In addition, we can track memory allocated via kmem_cache_alloc()/kmalloc() +originating from module functions we have enabled coverage for. This +allows us to track memory associated with the module specifically to find +leaks etc. If memory tracking is enabled, /sys/kernel/debug/ktf/coverage +will show outstanding allocations - the stack at allocation time; the +memory address and size. + +Coverage can be enabled via the "ktfcov" utility. Syntax is as follows:: + + ktfcov [-d module] [-e module [-m]] + +"-e" enables coverage for the specified module; "-d" disables coverage. +"-m" in combination with "-e" enables memory tracking for the module under +test. + +Note that this functionality is only available on kernels with CONFIG_KPPROBES +and CONFIG_KRETPROBES set to "y", and that CONFIG_KALLSYMS and +CONFIG_KALLSYMS_ALL should be set to "y" also to get all exported and +non-exported symbols. + +Thread execution +**************** + +KTF provides easy mechanisms to create and use kernel threads. +Assertions can then be carried out in the created thread context +also. Threads can be created as follows, and we can if we wish +wait for thread completion:: + + + TEST(foo, bar) + { + struct ktf_thread t; + + ... + KTF_THREAD_INIT(mythread, &t); + KTF_THREAD_RUN(&t); + KTF_THREAD_WAIT_COMPLETED(&t); + ... + } + +The thread itself is defined as follows:: + + KTF_THREAD(mythread) + { + ... + } + +We can add assertions to the thread and they will be recorded/logged +as part of the test. + +Hybrid tests +************ + +KTF also allows mixing of user and kernel side code in the same test. +This is useful if one wants for instance to verify that user land operations +has certain effects in the kernel, for instance verify that a parameter is +transferred or handled correctly in the kernel. + +Hybrid tests are specified by writing a user mode test using the special +``HTEST()`` macro instead of the normal ``TEST()`` macro. This macro takes +Inside the macro, the special variable ``self`` can be used to refer to the +test itself, and the macro ``KTF_USERDATA()`` can be used to get a pointer to +an allocated instance of a test specific parameter struct. The user land test +can then call the kernel side directly using ``ktf::run_kernel_test(self)`` An +optional context name can be specified as a second argument to the call if +needed. This can be done any number of times during the user land test and +each call will transmit the struct value out-of-band to the kernel side. To +the kernel this appears as separate test calls, but the kernel side have the +option of aggregating or otherwise maintain state for the duration of the +test. + +Declare the data structure to use for user/kernel out-of-band communication +in a header file that is included both by the user and the kernel side:: + + struct my_params + { + char expected[128]; + unsigned long mode; + }; + +The user land side of the test itself can then look like this:: + + HTEST(foo, hybrid) + { + KTF_USERDATA(self, my_params, data); + + + + strcpy(data->expected, "something"); + data->mode = 0; + ktf::run_kernel_test(self); + + strcpy(data->expected, "something_else"); + ktf::run_kernel_test(self); + + + + ... + } + +On the kernel side, a hybrid test is written as a normal kernel test using +the ``TEST()`` macro, and the test must be added using ``ADD_TEST()`` as +usual. Include the user land header file to know the data type of the +out-of-band parameter block. Invoke the macro ``KTF_USERDATA()`` to get a +size validated pointer to the user land provided data. If no data is +available, the test will silently exit. This is by purpose - if the kernel +test is executed from a test program that does not have the associated user +land code, such as for instance ``ktfrun``, it will just appear as a test +with no assertions in it, and not create any errors. If on the other hand the +parameter block does not match in size, an assertion is thrown and the test +exits:: + + TEST(foo, hybrid) + { + KTF_USERDATA(self, my_params, data); + + ... + if (strcmp(data->expected, "something") == 0) + ... + EXPECT( ... ) + + ... + } + + +Running tests and examining results via debugfs +*********************************************** + +In addition to the netlink interface used by the Googletest integrated frontend code, +we provide debugfs interfaces for examining the results of the +last test run and for running tests which do not require configuration +specification. Individual ktf testsets can be run via:: + + cat /sys/kernel/debug/ktf/run/ + +Individual tests can be run via:: + + cat /sys/kernel/debug/ktf/run/-tests/ + +Results can be displayed for the last run via:: + + cat /sys/kernel/debug/ktf/results/ + +Individual tests can be run via:: + + cat /sys/kernel/debug/ktf/results/-tests/ + +These interfaces bypasses use of the netlink socket API +and provide a simple way to keep track of test failures. It can +be useful to log into a machine and examine what tests were run +without having console history available. + +In particular:: + + cat /sys/kernel/debug/ktf/run/* + +...is a useful way of running all KTF tests. diff --git a/Documentation/dev-tools/ktf/implementation.rst b/Documentation/dev-tools/ktf/implementation.rst new file mode 100644 index 0000000..2bc4335 --- /dev/null +++ b/Documentation/dev-tools/ktf/implementation.rst @@ -0,0 +1,70 @@ + +2. Implementation +----------------- + +KTF consists of a kernel part and a user part. The role of the user part is to query the kernel +for available tests, and provide mechanisms for executing a selected set or all the available +tests, and report the results. The core ktf kernel module simply provides some APIs to write +assertions and run tests and to communicate about tests and results with user mode. +A simple generic Netlink protocol is used for the communication. + +User mode implementation +************************ + +Since test filtering and reporting is something existing unit test frameworks for +user space code already does well, the implementation of KTF simply leverages that. +The current version supports an integration with gtest (Googletest), which provides a lot of +these features in a flexible way, but in principle alternative implementations could +use the reporting of any other user level unit test framework. The use of gtest also allows this +documentation to be shorter, as many of the features in gtest are automatically available for KTF as well. +More information about Googletest features can be found here: https://github.com/google/googletest + +Kernel mode implementation +************************** + +The kernel side of KTF implements a simple API for tracking test modules, +writing tests, support functions and and a set of assertion macros, some +tailored for typical kernel usage, such as ``ASSERT_OK_ADDR_GOTO()`` +as a kernel specific macro to check for a valid address with a label to jump to if the +assertion fails. After all as we are still in the kernel, tests would always need to clean up for +themselves even though in the context of ktf. + +KTF supports two distinct classes of tests: + +* Pure kernel mode tests +* Hybrid tests + +Pure kernel mode tests are tests that are fully implemented in kernel space. +This is the most straightforward mode and resembles ordinary user land unit testing +in kernel mode. If you only have kernel mode tests, you will only ever need one user level program +similar to user/ktfrun.cpp, since all test development takes place on the kernel side. + +Hybrid tests are for testing and making assumptions about the user/kernel communication, for instance +if a parameter supplied from user mode is interpreted the intended way when it arrives at it's kernel +destination. For such tests you need to tell ktf (from user space) when the kernel part of the test +is going to be executed - this can happen multiple times depending on your test needs. +Apart from that it works mostly like a normal gtest user level test. + +Kernel integration of KTF or KTF as a separate git project? +*********************************************************** + +Yes. A lot of test infrastructure and utilities for the Linux kernel +is implemented as part of the linux kernel git project. +This has some obvious benefits, such as + +* Always being included +* When APIs are changed, test code can be updated atomically with the rest of the kernel +* Higher visibility and easier access +* Easier integration with internal kernel interfaces useful for testing. + +On the other hand providing KTF as a separate project allows + +* With some use of ``KERNEL_VERSION`` and ``LINUX_VERSION_CODE``, up-to-date KTF code and tests + can be allowed to work across kernel versions. +* This in turn allows a single set of newly developed tests to be + simultaneously tested against multiple older kernels, possibly + detecting more bugs, or instances of bugs not backported. + +So we will continue to support both, and have work in progress to simplify +the maintenance and synchronization of the two versions, and allow the +additional tooling to extend to KTF client test suites as well. diff --git a/Documentation/dev-tools/ktf/index.rst b/Documentation/dev-tools/ktf/index.rst new file mode 100644 index 0000000..25db49b --- /dev/null +++ b/Documentation/dev-tools/ktf/index.rst @@ -0,0 +1,14 @@ +Kernel Test Framework documentation +=================================== + +.. toctree:: + :maxdepth: 2 + + introduction + implementation + features + installation + concepts + examples + progref + debugging diff --git a/Documentation/dev-tools/ktf/installation.rst b/Documentation/dev-tools/ktf/installation.rst new file mode 100644 index 0000000..1bfccc1 --- /dev/null +++ b/Documentation/dev-tools/ktf/installation.rst @@ -0,0 +1,73 @@ +4. Building and installing KTF +------------------------------ + +KTF's user land side depends on googletest. +The googletest project has seen some structural changes in moving from a +project specific gtest-config via no package management support at all to +recently introduce pkgconfig support. This version of KTF only supports +building against a googletest (gtest) with pkgconfig support, which means +that as of February 2018 you have to build googletest from source at +github. + +Googletest has also recently been fairly in flux, and while we +try to keep up to date with the official googletest version on Github, +we have seen issues with changes that breaks KTF. We also have a small +queue of enhancements and fixes to Googletest based on our experience +and use of it a.o. with KTF. You can find the latest rebase of this +version in the ktf branch of knuto/googletest at Github, but expect it +to rebase as we move forward to keep it up-to-date. +This version will at any time have been tested with KTF by us, since +we use it internally. Let's assume for the rest of these instructions +that your source trees are below ``~/src`` and your build trees are +under ``~/build``:: + + cd ~/src + git clone https://github.com/knuto/googletest.git + +or:: + + cd ~/src + git clone https://github.com/google/googletest.git + +then:: + + mkdir -p ~/build/$(uname -r) + cd ~/build/$(uname -r) + mkdir googletest + cd googletest + cmake ~/src/googletest + make + sudo make install + +Default for googletest is to use static libraries. If you want to use shared +libraries for googletest, you can specify ``-DBUILD_SHARED_LIBS=ON`` to +cmake. If you don't want to install googletest into /usr/local, you can +specify an alternate install path using ``-DCMAKE_INSTALL_PREFIX=`` +to cmake for googletest, and similarly use ``--prefix=`` both for +KTF and your own test modules. Note that on some distros, cmake version +2 and 3 comes as different packages, make sure you get version 3, which may +require you to use ``cmake3`` as command instead of cmake above. + +Building the in-kernel version of KTF and running KTF selftests +*************************************************************** + +The environment needs to have the path to the +gtest (Googletest) build set to the directory above the lib and +include directories:: + + export GTEST_PATH=$HOME/install + +KTF can then be built using the module target, eg. from the top level +kernel build tree. + + make M=tools/testing/selftests/ktf + +You can run also build (and run) KTF tests as selftests tests +via the kselftest target:: + + make TARGETS="ktf" kselftest + +You can invoke this command to let the tests run as a normal user, but +root access is needed to load and unload ktf.ko and the test +module(s). This will happen as part of the kselftest target even as a +normal user if the user has sudo privileges. diff --git a/Documentation/dev-tools/ktf/introduction.rst b/Documentation/dev-tools/ktf/introduction.rst new file mode 100644 index 0000000..5c861f4 --- /dev/null +++ b/Documentation/dev-tools/ktf/introduction.rst @@ -0,0 +1,134 @@ +:Author: Knut Omang +:Last Updated: Alan Maguire + +1. Background and motivation +---------------------------- + +Kernel Test Framework (KTF) implements a unit test framework for the Linux kernel. +There's a wide selection of unit test frameworks available for normal user land +code testing, but we have so far not seen any similar frameworks that can be used +with kernel code, to test details of both exported and non-exported kernel APIs. +The hope is that providing an easy to use and convenient way to write simple unit +tests for kernel internals, that this can promote a more test driven approach to +kernel development, where appropriate. + +An important design goal is to make KTF in a way that it lend itself well to a normal kernel +developer cycle, and that it integrates well with user land unit testing, to allow kernel and +user land tests to behave, look and feel as similar as possible. This should hopefully make it +more intuitive to use as well as more rewarding. We also believe that even a kernel test that +passes should have a nice, easy to read and pleasant output, and that a test framework must have +good observability, that is good mechanisms for debugging what went wrong, both in case of bugs +in the tests and and the test framework itself. + +KTF is designed to test the kernel in the same ways it runs. This means we want to stay away from +changing configuration options, or otherwise make changes that makes it hard to logically tell +from a high level perspective whether the kernel with KTF is really logically "the same" as the +kernel our users are exposed to. Of course we all know that it is very hard to test anything +without affecting it, with quantum mechanics as the extreme, but at least we want to make an +effort to make the footprint as small as possible. + +KTF tests kernel code by running tests in kernel context - or in the case of hybrid tests - in +both user- and kernel contexts. Doing this ensures that we test kernel codepaths in a real way, +without emulating a kernel execution environment. This gives us vastly more control over what +tests can do versus user-space driven testing, and increases confidence that what the tests test +matches what the kernel does since the test execution environment is identical. + +KTF is a product of a refactoring of code used as part of test driven development of a Linux +driver for an Infiniband HCA. It is in active use for kernel component testing within Oracle. + +Test driven development +*********************** + +Unit testing is an important component of Test driven development (TDD). +The idea of test driven development is that when you have some code to write, +whether it is a bug to fix, or a new feature or enhancement, that you start by writing +one or more tests for it, have those tests fail, and then do the actual development. + +Typically a test driven development cycle would have several rounds of development and +test using the new unit tests, and once the (new) tests pass, you would +also run all or a suitable subset (limited by execution time) of the old tests to verify +that you have not broken any old functionality by the new code. + +At this stage it is important that the tests that are run can be run quickly to allow +them to be actively used in the development cycle. When time comes for +submission of the code, a full, extensive set of both the tests the developer thinks +can touch the affected code *and* all the other tests should be run, and a longer time +to run tests can be afforded. + +KTF tries to support this by using the module system of the kernel to support +modularized test suites, where a user only need to insmod the test subsets that he/she wants +to use right then. Different test suites may touch and require different kernel APIs and have +lots of different module and device requirements. To enable as much reuse of the functionality +of code developed within KTF it is important that any piece of test code has as few dependencies +as possible. + +Good use cases for KTF +********************** + +Unit testing is at it's most valuable when the code to test is relatively error prone, but still +might be difficult to test in a systematic and reproducable way from a normal application level. +It can be difficult to trigger corner cases from a high abstraction layer, +the code paths we want to exercise might only be used occasionally, or we want to exercise +that error/exception scenarios are handled gracefully. + +KTF comes to it's strength in testing kernel APIs that are fairly integrated into the kernel, +and depend upon lots of components, making them difficult or error prone to mock. Good examples +are module APIs not easily testable from user land. Exported module APIs are usually only used +by one or a few other kernel clients, and hitting buggy corner cases with these might be hard or +impossible. This typically leads to bugs detected "down the road", when some new client appears +and starts using the API in a new way, or instabilities that go undetected because underlying +semantics that the implementation implicitly depend upon changes in subtle ways. + +KTF can use mechanisms such as KTF probes in cases where calls to other functions needs to be +intercepted and/or modified to create the right test condition, whether it means waiting for a +potential race condition to occur, or return an error value, or just collect state to make assertions. + +Typical classical use cases that lend itself well to unit testing are simple APIs with a relativ +complex implementation - such as container implementations. Typical kernel examples of these +in the kernel are scatterlist, rbtree, list, XArray etc. When testing the base implementations of such +containers, bringing them entirely out into user space and compiling them standalone require some +additional work up-front to implement mock interfaces to the services provided by the kernel, +but may nonetheless be rewarding in the longer run, as such tests have at it's disposal the whole +arsenal of user land tools, such as gdb, valgrind etc. This, however does not guarantee against +wrong use of a container, such as with interactions between a container and a driver +datastructure. + +Testing the *instantiations* of these container implementations inside drivers or +the kernels's own internals might not be that easy with a user land approach, as it very quickly +requires a prohibitive amount of mock interfaces to be written. And even when such mock +interfaces can be written, one cannot be sure that they implement exactly the same as the +environment that the code executes in within the kernel. Having the ability to make tests within +a release kernel, even run the same tests against multiple such kernels is something KTF +supports well. Our experience is that even error scenarios that are hard to reproduce by +running applications on the kernel can often be reproduced with a surprisingly small +number of lines of code in a KTF test, once the problem is understood. And writing that code can +be a very rewarding way of narrowing down a hard bug. + +When *not* to use KTF +********************* + +Writing kernel code has some challenges compared to user land code. +KTF is intended for the cases where it is not easy to get coverage by writing +simple tests from user land, using an existing rich and well proven user land unit test +framework. + +Why *you* would want to write and run KTF tests +*********************************************** + +Besides the normal write test, write code, run test cycle of development and the obvious benefits of +delivering better quality code with fewer embarrassments, there's a few other upsides from +developing unit test for a particular area of the kernel: + +* A test becomes an invariant for how the code is supposed to work. + If someone breaks it, they should detect it and either document the changes that caused the breakage + by fixing the test or realize that their fix is broken before you even get to spend time on it. + +* Kernel documentation while quite good in some places, does not always + cover the full picture, or you might not find that sentence you needed while looking for it. + If you want to better understand how a particular kernel module actually works, a good way is to + write a test that codes your assumptions. If it passes, all is well, if not, then you have gained some + understanding of the kernel. + +* Sometimes you may find yourself relying on some specific feature or property of the kernel. + If you encode a test that guards the assumptions you have made, you will capture if someone + changes it, or if your code is ported to an older kernel which does not support it. diff --git a/Documentation/dev-tools/ktf/progref.rst b/Documentation/dev-tools/ktf/progref.rst new file mode 100644 index 0000000..2f0fa48 --- /dev/null +++ b/Documentation/dev-tools/ktf/progref.rst @@ -0,0 +1,144 @@ +7. KTF programming reference +---------------------------- + +KTF itself contains no tests but provides primitives and data structures to +allow tests to be maintained and written in separate test modules that +depend on the KTF APIs. + +KTF API Overview +**************** + +For reference, the following table lists a few terms and classes of +abstractions provided by KTF. These are kernel side, if not otherwise noted: + ++----------------------------+--------------------------------------------------+ +| **Item** | **description** | ++============================+==================================================+ +| Test module | A kernel object file (.ko) with ktf tests in it | ++----------------------------+--------------------------------------------------+ +| struct ktf_handle | At least 1 per test module. | +| | Use macros KTF_INIT() and KTF_CLEANUP() to set up| +| | and tear down handles. | ++----------------------------+--------------------------------------------------+ +| struct ktf_context | 0-n per test module - test module specific | +| | context for the test, such as eg. a device or | +| | another kernel object. | ++----------------------------+--------------------------------------------------+ +| KTF_INIT() | Call this at the global level in the main file | +| | for each test module. Declares an implicit, | +| | default test handle used by macros which do not | +| | provide a handle argument. | ++----------------------------+--------------------------------------------------+ +| KTF_CTX_INIT() | Use this instead of KTF_INIT if the tests require| +| | a context to execute. Tests will only show up as | +| | options if a context has been provided. | ++----------------------------+--------------------------------------------------+ +| KTF_HANDLE_INIT(handle) | Declare a named handle to associate tests and | +| | contexts with. This is an alternative to | +| | KTF_INIT() to allow the use of separate test | +| | handles for separate sets of tests. | ++----------------------------+--------------------------------------------------+ +| KTF_HANDLE_CTX_INIT(handle)| Equivalent of KTF_CTX_INIT for a named handle | ++----------------------------+--------------------------------------------------+ +| KTF_CLEANUP() | Call this in the __exit function to clean up | ++----------------------------+--------------------------------------------------+ +| KTF_CONTEXT_ADD(ctx, name) | Add a new context to the default handle | ++----------------------------+--------------------------------------------------+ +| KTF_CONTEXT_FIND(name) | Return a struct ktf_context reference to context | +| | 'name', if it exists | ++----------------------------+--------------------------------------------------+ +| KTF_CONTEXT_GET(name,type) | Return the structure of type 'type' containing | +| | the ktf_context named 'name', if 'name' exists. | ++----------------------------+--------------------------------------------------+ +| KTF_CONTEXT_REMOVE(ctx) | Remove a previously added context from KTF | ++----------------------------+--------------------------------------------------+ +| EXPECT_* | non-fatal assertions | ++----------------------------+--------------------------------------------------+ +| ASSERT_* | "fatal" assertions that would cause return/goto | ++----------------------------+--------------------------------------------------+ +| TEST(s, n) {...} | Define a simple test named 's.n' with implicit | +| | arguments 'ctx' and '_i' for context/iteration. | ++----------------------------+--------------------------------------------------+ +| DECLARE_F(f) {...} | Declare a new test fixture named 'f' with | +| | additional data structure | ++----------------------------+--------------------------------------------------+ +| SETUP_F(f, s) {...} | Define setup function for the fixture | ++----------------------------+--------------------------------------------------+ +| TEARDOWN_F(f, t) {...} | Define teardown function for the fixture | ++----------------------------+--------------------------------------------------+ +| INIT_F(f, s, t) {...} | Declare the setup and tear down functions for the| +| | fixture | ++----------------------------+--------------------------------------------------+ +| TEST_F(s, f, n) {...} | Define a test named 's.n' operating in fixture f | ++----------------------------+--------------------------------------------------+ +| ADD_TEST(n) | Add a test previously declared with TEST or | +| | TEST_F to the default handle. | ++----------------------------+--------------------------------------------------+ +| ADD_LOOP_TEST(n, from, to) | Add a test to be executed repeatedly with a range| +| | of values [from,to] to the implicit variable _i | ++----------------------------+--------------------------------------------------+ +| DEL_TEST(n) | Remove a test previously added with ADD_TEST | ++----------------------------+--------------------------------------------------+ +| KTF_ENTRY_PROBE(f, h) | Define function entry probe for function f with | +| {...} | handler function h. Must be used at global level.| ++----------------------------+--------------------------------------------------+ +| KTF_ENTRY_PROBE_RETURN(r) | Return from probed function with return value r. | +| | Must be called within KTF_ENTRY_PROBE(). | ++----------------------------+--------------------------------------------------+ +| KTF_REGISTER_ENTRY_PROBE | Enable probe on entry to kernel function f | +| (f, h) | with handler h. | ++----------------------------+--------------------------------------------------+ +| KTF_UNREGISTER_ENTRY_PROBE | Disable probe on entry to kernel function f | +| (f, h) | which used handler h. | ++----------------------------+--------------------------------------------------+ +| KTF_RETURN_PROBE(f, h) | Define function return probe for function f with | +| {..} | handler h. Must be used at a global level. | ++----------------------------+--------------------------------------------------+ +| KTF_RETURN_VALUE() | Retrieve return value in body of return probe. | ++----------------------------+--------------------------------------------------+ +| KTF_REGISTER_RETURN_PROBE | Enable probe for return of function f with | +| (f, h) | handler h. | ++----------------------------+--------------------------------------------------+ +| KTF_UNREGISTER_RETURN_PROBE| Disable probe for return of function f and | +| (f, h) | handler h. | ++----------------------------+--------------------------------------------------+ +| ktf_cov_enable(m, flags) | Enable coverage analytics for module m. | +| | Flag must be either 0 or KTF_COV_OPT_MEM. | ++----------------------------+--------------------------------------------------+ +| ktf_cov_disable(m) | Disable coverage analytics for module m. | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_INIT(name, t) | Initialize thread name, struct ktf_thread * t. | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_RUN(t) | Run initialized struct ktf_thread * t. | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_STOP(t) | Stop thread via kthread_stop() | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_WAIT_STARTED(t) | Wait for start of struct ktf_thread * t. | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_WAIT_COMPLETED | Wait for completion of struct ktf_thread * t. | +| (t) | | ++----------------------------+--------------------------------------------------+ +| HTEST(s, n) { ... } | Declares a hybrid test. A correspondingly named | +| (NB! User mode only!) | test must be declared using TEST() from kernel | +| | space for the hybrid test to be executed. | ++----------------------------+--------------------------------------------------+ +| KTF_USERDATA(self, type, d)| Declare/get a pointer to user/kernel aux.data | +| (NB! both kernel and | for a test that declares such extra data. Used | +| user space!) | for hybrid tests. | ++----------------------------+--------------------------------------------------+ + +The ``KTF_INIT()`` macro must be called at a global level as it just +defines a variable ``__test_handle`` which is referred to, and which existence +is assumed to continue until the call to KTF_CLEANUP(), typically done in +the ``__exit`` function of the test module. + + + +Assertions +********** + +Below is example documentation for some of the available assertion macros. +For a full overview, see ``kernel/ktf.h`` + +.. kernel-doc:: kernel/ktf.h + :internal: From patchwork Tue Aug 13 06:09:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091191 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CE80F746 for ; Tue, 13 Aug 2019 06:12:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA0AB27FE4 for ; Tue, 13 Aug 2019 06:12:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id ACB6A284CE; Tue, 13 Aug 2019 06:12:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DD0A128438 for ; Tue, 13 Aug 2019 06:12:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727752AbfHMGL7 (ORCPT ); Tue, 13 Aug 2019 02:11:59 -0400 Received: from userp2130.oracle.com ([156.151.31.86]:60640 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727246AbfHMGL6 (ORCPT ); Tue, 13 Aug 2019 02:11:58 -0400 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D693sm011117; Tue, 13 Aug 2019 06:11:36 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=qpt6s5f2ZMaRIgJiplhgON8Bp1uiCExpeX8yfJisRo0=; b=RIvpVm8o8LO7TiFDQSKpSMjoPd96rRCcbOYvoTM6hGDerfSraoVTsssN1c71EYbIkf5D w+Wd5MM1frqreAWrhbQ7tU1Cwe1jYUp43lkDUMwnBIIA8v/FYUl+7DGbmPBZWbwthTPp svqSZj8Wk7ajsd7Y/b+8MEuW26iluj5Yb9aQWR6Sqwx74dUQ9sjLYv+eEx++53qfnck7 cICnj29SRq6J+okWyi5AQbDY1T5xwnmY2ooiQjGgYm/Rt4Pi1shrMhdDwfd0RLOf8LbD oF+sEHEn8qPgS4mM1MlZ6FZRrF8fyFqmpvVZcJ/601qe7R88zTPsiQOllwsJkkpf/KRx ew== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=qpt6s5f2ZMaRIgJiplhgON8Bp1uiCExpeX8yfJisRo0=; b=OBswWIC+orkFZ8bCLW/oOxXhUnAvhETNpobsidoTyF7lAwlj5AlRJFd1Gr5zZ6KibMxd 2zVaB/zVCQoGw+Ynyw8JDRnlW4lpHZ4pTdI3bA1+0GELDIIEjK78PlgXg3v6eQ/UQ/Mm /bizjXp+mes44tevw3dppI3Tf4CPNpZHfYL20BBLHLBHxFMAz4GKn//aME+WkwdKJqF0 8xkt6UmIn+OZFC3mmFlKzuYKVdVvTSHw07ahNRfkAV2jeJ6GL6vOThPoDR8EcX84Enn+ wRDvBFUeLmCxuqmnXpFTi/hkfcNAsur+nOvF6U98asuLCQtBgPZqmiSUSZFKrjYBAQ3J tg== Received: from aserp3020.oracle.com (aserp3020.oracle.com [141.146.126.70]) by userp2130.oracle.com with ESMTP id 2u9nbtc12u-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:36 +0000 Received: from pps.filterd (aserp3020.oracle.com [127.0.0.1]) by aserp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67TTd157170; Tue, 13 Aug 2019 06:11:35 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by aserp3020.oracle.com with ESMTP id 2u9nrenm99-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:35 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6BXm4011235; Tue, 13 Aug 2019 06:11:33 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:32 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 11/19] ktf: Add a small test suite with a few tests to test KTF itself Date: Tue, 13 Aug 2019 08:09:26 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP context.c: Parameterized context test case, kernel side: context.h: Parameterized context test case, kernel side. context_self.h: The data structure passed between user level and kernel for the hybrid.c: Hybrid (combined user level and kernel) self tests, hybrid.h: Hybrid (combined user level and kernel) self tests, hybrid_self.h: The data structure passed between user level and kernel for the self.c: Some simple self tests for KTF Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/selftest/Makefile | 17 +- tools/testing/selftests/ktf/selftest/context.c | 149 +++- tools/testing/selftests/ktf/selftest/context.h | 15 +- tools/testing/selftests/ktf/selftest/context_self.h | 34 +- tools/testing/selftests/ktf/selftest/hybrid.c | 35 +- tools/testing/selftests/ktf/selftest/hybrid.h | 24 +- tools/testing/selftests/ktf/selftest/hybrid_self.h | 27 +- tools/testing/selftests/ktf/selftest/ktf_syms.txt | 17 +- tools/testing/selftests/ktf/selftest/self.c | 661 +++++++++++++- 9 files changed, 979 insertions(+) create mode 100644 tools/testing/selftests/ktf/selftest/Makefile create mode 100644 tools/testing/selftests/ktf/selftest/context.c create mode 100644 tools/testing/selftests/ktf/selftest/context.h create mode 100644 tools/testing/selftests/ktf/selftest/context_self.h create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.c create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.h create mode 100644 tools/testing/selftests/ktf/selftest/hybrid_self.h create mode 100644 tools/testing/selftests/ktf/selftest/ktf_syms.txt create mode 100644 tools/testing/selftests/ktf/selftest/self.c diff --git a/tools/testing/selftests/ktf/selftest/Makefile b/tools/testing/selftests/ktf/selftest/Makefile new file mode 100644 index 0000000..8737bf4 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/Makefile @@ -0,0 +1,17 @@ +# Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 +# +# Kernel module implementing a test suite for testing KTF itself +# + +ccflags-y += -Wno-vla + +ccflags-y += -I$(srctree)/$(src)/../kernel -I$(src) + +obj-m := selftest.o + +include $(srctree)/$(src)/../scripts/ktf_syms.mk + +selftest-y := self.o hybrid.o context.o + diff --git a/tools/testing/selftests/ktf/selftest/context.c b/tools/testing/selftests/ktf/selftest/context.c new file mode 100644 index 0000000..9129b5b --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/context.c @@ -0,0 +1,149 @@ +/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * context.c: Parameterized context test case, kernel side: + */ + +#include "ktf.h" +#include "context.h" + +/* Declare a specific handle for this test to avoid interfering with the + * other tests: + */ +static KTF_HANDLE_INIT(ct_handle); + +struct param_test_ctx { + struct ktf_context k; + struct test_parameter_block p; +}; + +struct param_test_ctx param_ctx[2]; + +#define MYVALUE 0xdabadaba + +/* Declare the callback that accepts a parameter block */ +static int param_ctx_cb(struct ktf_context *ctx, const void *data, size_t data_sz) +{ + struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k); + struct test_parameter_block *pb = (struct test_parameter_block *)data; + long orig_myvalue; + + if (data_sz != sizeof(*pb)) + return -EINVAL; + /* check data validity here, if possible.. */ + orig_myvalue = px->p.myvalue; + memcpy(&px->p, pb, data_sz); + /* Enforce "policies" */ + px->p.myvalue = orig_myvalue; + return 0; +} + +TEST(selftest, param) +{ + struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k); + + /* Now, here we can fail (using ASSERT) or ignore by silently return + * depending on what's most useful, if a test hasn't been configured. + * For this selftest we just use EXPECT so we can have the actual current + * parameter values reported as well. + * + * Notice that these parameters are + * persistent throughout the instance 'life' of the kernel test module, + * so if one user program has configured them, then + * programs ignorant of the parameters may still end up + * executing the tests with previously configured parameters: + * + * This simplified example uses the same configuration struct for both + * context type IDs, but the idea is that they can be completely different. + */ + EXPECT_INT_EQ(ctx->config_errno, 0); + if (KTF_CONTEXT_CFG_OK(ctx)) { + switch (ctx->type->name[13]) { + case '1': + EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC1); + break; + case '2': + EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC2); + break; + case '3': + EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC3); + EXPECT_LONG_EQ(px->p.myvalue, MYVALUE); + break; + } + EXPECT_STREQ(px->p.s, CONTEXT_MSG); + } else { + EXPECT_LONG_EQ(px->p.magic, 0); + EXPECT_STREQ(px->p.s, ""); + } +} + +struct param_test_type { + struct ktf_context_type kt; + /* space for cfg data (such as constraints) for the context type */ + long myvalue; +}; + +static struct ktf_context *type3_alloc(struct ktf_context_type *ct) +{ + struct param_test_type *pct = container_of(ct, struct param_test_type, kt); + struct param_test_ctx *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + + ctx->p.myvalue = pct->myvalue; + return &ctx->k; +} + +static void type3_cleanup(struct ktf_context *ctx) +{ + struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k); + + kfree(px); +} + +TEST(selftest, dupltype) +{ + /* Verify that we cannot add the same context type twice */ + + static struct param_test_type dupltype = { + .myvalue = 0, + .kt.alloc = type3_alloc, + .kt.config_cb = param_ctx_cb, + .kt.cleanup = type3_cleanup, + .kt.name = "context_type_3" + }; + + ASSERT_INT_EQ(-EEXIST, ktf_handle_add_ctx_type(&ct_handle, &dupltype.kt)); +} + +void add_context_tests(void) +{ + int ret = KTF_CONTEXT_ADD_TO_CFG(ct_handle, ¶m_ctx[0].k, "context1", + param_ctx_cb, "context_type_1"); + + if (ret) + return; + + ret = KTF_CONTEXT_ADD_TO_CFG(ct_handle, ¶m_ctx[1].k, "context2", + param_ctx_cb, "context_type_2"); + if (ret) + return; + + { + static struct param_test_type ctx_type3 = { + .myvalue = MYVALUE, + .kt.alloc = type3_alloc, + .kt.config_cb = param_ctx_cb, + .kt.cleanup = type3_cleanup, + .kt.name = "context_type_3" + }; + ret = ktf_handle_add_ctx_type(&ct_handle, &ctx_type3.kt); + } + + ADD_TEST_TO(ct_handle, param); + ADD_TEST(dupltype); +} + +void context_tests_cleanup(void) +{ + KTF_HANDLE_CLEANUP(ct_handle); +} diff --git a/tools/testing/selftests/ktf/selftest/context.h b/tools/testing/selftests/ktf/selftest/context.h new file mode 100644 index 0000000..69b970a --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/context.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * context.h: Parameterized context test case, kernel side. + */ +#ifndef _CONTEXT_H +#define _CONTEXT_H + +#include "context_self.h" + +void add_context_tests(void); +void context_tests_cleanup(void); + +#endif diff --git a/tools/testing/selftests/ktf/selftest/context_self.h b/tools/testing/selftests/ktf/selftest/context_self.h new file mode 100644 index 0000000..3939559 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/context_self.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * context_self.h: The data structure passed between user level and kernel for the + * hybrid self tests. Included both from user space and kernel space and + * needs to be a C struct. + */ + +#ifndef KTF_CONTEXT_SELF_H +#define KTF_CONTEXT_SELF_H + +#define CONTEXT_SELF_MAX_TEXT 30 + +/* A simple example parameter block: + * For verification purposes it can be useful to have a field + * like 'magic' below, which serves for the purpose of + * a sanity check that the parameters sent by the user program + * actually corresponds to what the kernel expects: + */ +struct test_parameter_block { + long magic; + long myvalue; + char s[CONTEXT_SELF_MAX_TEXT+1]; +}; + +/* Constants for the selftest.param_context test: */ +#define CONTEXT_MSG "from user to kernel" +#define CONTEXT_MAGIC1 0xfaaa1234UL +#define CONTEXT_MAGIC2 0xaabbccUL +#define CONTEXT_MAGIC3 0x123456UL + +#endif diff --git a/tools/testing/selftests/ktf/selftest/hybrid.c b/tools/testing/selftests/ktf/selftest/hybrid.c new file mode 100644 index 0000000..999a7d8 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/hybrid.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * hybrid.c: Hybrid (combined user level and kernel) self tests, + * kernel side: + */ + +#include "ktf.h" +#include "hybrid.h" + +/* First a simple message passing test that just verifies that we receive + * "out-of-band" data from user space: + */ + +TEST(selftest, msg) +{ + /* Accept data of type 'struct hybrid_self_params' (defined in hybrid_self.h) + * from user mode. This functionality is to allow user mode to test something, + * for instance that a certain parameter is handled in a specific way in the kernel. + * The user then has the option to provide data to the kernel out-of-band to + * tell the kernel side what to expect. + * In this test, just verify that data has been transmitted correctly: + */ + KTF_USERDATA(self, hybrid_self_params, data); + + EXPECT_STREQ(data->text_val, HYBRID_MSG); + EXPECT_LONG_EQ(data->val, HYBRID_MSG_VAL); +} + +void add_hybrid_tests(void) +{ + ADD_TEST(msg); +} diff --git a/tools/testing/selftests/ktf/selftest/hybrid.h b/tools/testing/selftests/ktf/selftest/hybrid.h new file mode 100644 index 0000000..0ba6f72 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/hybrid.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * hybrid.h: Hybrid (combined user level and kernel) self tests, + * kernel side, internal interface: + */ + +#ifndef KTF_HYBRID_H +#define KTF_HYBRID_H + +#include "hybrid_self.h" + +/* The kernel part of hybrid tests must be added to KTFs set of tests like any other tests, + * in fact from KTF's kernel perspective it is like any other test, except that it likely will + * fail if called without the context provided from the user space side. + * + * This function adds the tests declared in hybrid.c + */ +void add_hybrid_tests(void); + + +#endif diff --git a/tools/testing/selftests/ktf/selftest/hybrid_self.h b/tools/testing/selftests/ktf/selftest/hybrid_self.h new file mode 100644 index 0000000..21c6c92 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/hybrid_self.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * hybrid_self.h: The data structure passed between user level and kernel for the + * hybrid self tests. Included both from user space and kernel space and + * needs to be a C struct. + */ + +#ifndef KTF_HYBRID_SELF_H +#define KTF_HYBRID_SELF_H + +#define HYBRID_SELF_MAX_TEXT 127 + +struct hybrid_self_params +{ + char text_val[HYBRID_SELF_MAX_TEXT+1]; + unsigned long val; +}; + + +/* Constants for the selftest.msg test: */ +#define HYBRID_MSG "a little test string" +#define HYBRID_MSG_VAL 0xffUL + +#endif diff --git a/tools/testing/selftests/ktf/selftest/ktf_syms.txt b/tools/testing/selftests/ktf/selftest/ktf_syms.txt new file mode 100644 index 0000000..721ae98 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/ktf_syms.txt @@ -0,0 +1,17 @@ +#module ktf +#header ktf_map.h +ktf_map_init +ktf_map_elem_init +ktf_map_insert +ktf_map_find +ktf_map_find_first +ktf_map_remove +ktf_map_elem_get +ktf_map_elem_put +ktf_map_find_next +ktf_map_delete_all +#header ktf_cov.h +ktf_cov_entry_find +ktf_cov_entry_put +ktf_cov_enable +ktf_cov_disable diff --git a/tools/testing/selftests/ktf/selftest/self.c b/tools/testing/selftests/ktf/selftest/self.c new file mode 100644 index 0000000..8b7a582 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/self.c @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * self.c: Some simple self tests for KTF + */ +#include +#include +#include +#include + +#include "ktf.h" +#include "ktf_map.h" +#include "ktf_cov.h" +#include "ktf_syms.h" + +#include "hybrid.h" +#include "context.h" + +MODULE_LICENSE("GPL"); + +struct map_test_ctx { + struct ktf_context k; +}; + +static struct map_test_ctx s_mctx[4]; + +/* Declare a simple handle with no contexts for simple (unparameterized) tests: */ +KTF_INIT(); + +/* For tests that defines multiple test cases + * (e.g. if the test scope requires application of each test on several devices or + * other abstract contexts, definable by the test module) + */ +static KTF_HANDLE_INIT(dual_handle); +static KTF_HANDLE_INIT(single_handle); +static KTF_HANDLE_INIT(no_handle); +static KTF_HANDLE_INIT_VERSION(wrongversion_handle, 0, false); + +static struct map_test_ctx *to_mctx(struct ktf_context *ctx) +{ + return container_of(ctx, struct map_test_ctx, k); +} + +struct myelem { + struct ktf_map_elem foo; + int freed; + int order; +}; + +/* --- Simple insertion and removal test --- */ + +TEST(selftest, simplemap) +{ + int i; + const int nelems = 3; + struct map_test_ctx *mctx = to_mctx(ctx); + struct ktf_map tm; + struct myelem e[nelems]; + + if (mctx) + tlog(T_DEBUG, "ctx %s", mctx->k.elem.key); + else + tlog(T_DEBUG, "ctx "); + + ktf_map_init(&tm, NULL, NULL); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax")); + + for (i = 0; i < nelems; i++) { + EXPECT_LONG_EQ(i, ktf_map_size(&tm)); + EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo)); + } + EXPECT_LONG_EQ(i, ktf_map_size(&tm)); + + /* Should be sorted alphabetically so we get 'bar' back: */ + EXPECT_ADDR_EQ(&e[1].foo, ktf_map_find_first(&tm)); + + for (i = 0; i < nelems; i++) { + EXPECT_LONG_EQ(nelems - i, ktf_map_size(&tm)); + EXPECT_ADDR_EQ(&e[i].foo, ktf_map_remove(&tm, e[i].foo.key)); + } + EXPECT_LONG_EQ(0, ktf_map_size(&tm)); +} + +/* --- Reference counting test --- */ + +/* should be called when refcount is 0. */ +static void myelem_free(struct ktf_map_elem *elem) +{ + struct myelem *myelem = container_of(elem, struct myelem, foo); + + myelem->freed = 1; +} + +TEST(selftest, mapref) +{ + int i; + const int nelems = 3; + struct myelem e[nelems], *ep; + struct ktf_map tm; + struct ktf_map_elem *elem; + + ktf_map_init(&tm, NULL, myelem_free); + /* Init map elems with "foo" "bar" "zax" */ + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax")); + + /* Insert elems and drop our refcounts (map still holds ref) */ + for (i = 0; i < nelems; i++) { + EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo)); + ktf_map_elem_put(&e[i].foo); + } + + /* This macro takes (and drops) refcount for each elem */ + ktf_map_for_each_entry(ep, &tm, foo) + ep->freed = 0; + + for (i = 0; i < nelems; i++) { + elem = ktf_map_remove(&tm, e[i].foo.key); + EXPECT_INT_EQ(0, e[i].freed); + /* free our ref, now free function should be called. */ + ktf_map_elem_put(elem); + EXPECT_INT_EQ(1, e[i].freed); + } + + ktf_map_delete_all(&tm); + EXPECT_LONG_EQ(0, ktf_map_size(&tm)); +} + +/* --- Test that the expect macros work as if-then-else single statement */ +TEST(selftest, statements) +{ + char c; + char *cp = &c; + /* These are mostly intended as compilation syntax tests */ + if (_i) + EXPECT_TRUE(true); + else + EXPECT_FALSE(false); + if (_i) + ASSERT_TRUE(true); + else + ASSERT_FALSE(false); + if (_i) + ASSERT_OK_ADDR(cp); + else + ASSERT_OK_ADDR_GOTO(cp, out); + if (_i) + ASSERT_OK_ADDR_BREAK(cp); +out: + EXPECT_TRUE(true); +} + +/* --- Compare function test --- */ + +/* key comparison function */ +static int myelem_cmp(const char *key1, const char *key2) +{ + int i1 = *((int *)key1); + int i2 = *((int *)key2); + + if (i1 < i2) + return -1; + else if (i1 > i2) + return 1; + return 0; +} + +TEST(selftest, mapcmpfunc) +{ + int i; + const int nelems = 3; + struct myelem e[nelems], *ep; + struct ktf_map tm; + + ktf_map_init(&tm, myelem_cmp, NULL); + /* Init map elems with keys "foo" "bar" "zax" */ + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax")); + + /* Insert elems with order values 3, 2, 1. Ensure we see order + * 1, 2, 3 on retrieval. + */ + for (i = 0; i < nelems; i++) { + e[i].order = nelems - i; + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[i].foo, + (char *)&e[i].order)); + EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo)); + } + i = 1; + /* Ensure ordering via compare function is respected */ + ktf_map_for_each_entry(ep, &tm, foo) + EXPECT_INT_EQ(ep->order, i++); + + ktf_map_delete_all(&tm); + EXPECT_LONG_EQ(0, ktf_map_size(&tm)); +} + +/* --- Verify that key name is truncated at KTF_MAX_NAME length --- */ + +TEST(selftest, map_keyoverflow) +{ + struct myelem e; + struct ktf_map tm; + char jumbokey[KTF_MAX_NAME + 2]; + char jumbokey_truncated[KTF_MAX_NAME + 1]; + + ktf_map_init(&tm, NULL, NULL); + memset(jumbokey, 'x', KTF_MAX_NAME + 1); + memset(jumbokey_truncated, 'x', KTF_MAX_NAME); + jumbokey_truncated[KTF_MAX_NAME] = '\0'; + EXPECT_INT_EQ(0, ktf_map_elem_init(&e.foo, jumbokey)); + EXPECT_TRUE(strcmp(e.foo.key, jumbokey_truncated) == 0); +} + +struct mykey { + unsigned long address; + unsigned long size; +}; + +/* Comparison here is to check if k1's address falls in range + * [k2->address, k2->address + k2->size]. Similar compare used in + * ktf_cov to figure out if a function address lies within the function + * code. + */ +static int custom_compare(const char *key1, const char *key2) +{ + struct mykey *k1 = (struct mykey *)key1; + struct mykey *k2 = (struct mykey *)key2; + + if (k1->address < k2->address) + return -1; + if (k1->address >= (k2->address + k2->size)) + return 1; + return 0; +} + +/* --- Verify that opaque keys with custom compare function work --- */ + +TEST(selftest, map_customkey) +{ + const int nelems = 3; + int baseaddr = 1024; + struct ktf_map cm; + struct mykey keys[nelems], search; + struct myelem elems[nelems]; + int i, j; + + ktf_map_init(&cm, custom_compare, NULL); + + /* Ensure we can add entries and then retrieve them via search key. */ + for (i = 0; i < nelems; i++) { + baseaddr += (i << 2); + keys[i].address = baseaddr; + keys[i].size = (i + 1) << 2; + ASSERT_INT_EQ_GOTO(ktf_map_elem_init(&elems[i].foo, + (char *)&keys[i]), + 0, done); + ASSERT_INT_EQ_GOTO(ktf_map_insert(&cm, &elems[i].foo), 0, done); + } + + baseaddr = 1024; + + /* Ensure all search addresses within range of [base address, size] + * find appropriate entries. + */ + for (i = 0; i < nelems; i++) { + baseaddr += (i << 2); + for (j = 0; j < (i + 1) << 2; j++) { + search.address = baseaddr + j; + search.size = 0; + ASSERT_ADDR_EQ_GOTO(ktf_map_find_entry(&cm, + (char *)&search, + struct myelem, + foo), + &elems[i], done); + } + } + +done: + ktf_map_delete_all(&cm); +} + +TEST(selftest, dummy) +{ + /* The default handle does not have any contexts in this test set */ + ASSERT_FALSE(ctx); +} + +TEST(selftest, wrongversion) +{ + tlog(T_INFO, "This test should never have run - wrong version\n!!!"); + EXPECT_TRUE(false); +} + +static void add_map_tests(void) +{ + ADD_TEST(dummy); + ADD_LOOP_TEST(statements, 0, 2); + ADD_TEST_TO(dual_handle, simplemap); + ADD_TEST_TO(dual_handle, mapref); + ADD_TEST_TO(dual_handle, mapcmpfunc); + ADD_TEST(map_keyoverflow); + ADD_TEST(map_customkey); + + terr("-- version check test: --"); + /* This should fail */ + ADD_TEST_TO(wrongversion_handle, wrongversion); +} + +static int probecount; +static int proberet; + +KTF_ENTRY_PROBE(printk, printkhandler) +{ + probecount++; + + KTF_ENTRY_PROBE_RETURN(0); +} + +static int entryarg0, entryarg1; + +KTF_ENTRY_PROBE(probeargtest, probeargtesthandler) +{ + entryarg0 = (int)KTF_ENTRY_PROBE_ARG0; + entryarg1 = (int)KTF_ENTRY_PROBE_ARG1; + KTF_ENTRY_PROBE_RETURN(0); +} + +noinline void probeargtest(int arg0, int arg1) +{ + tlog(T_INFO, "got args %d, %d\n", arg0, arg1); +} + +TEST(selftest, probeentry) +{ + probecount = 0; + ASSERT_INT_EQ(KTF_REGISTER_ENTRY_PROBE(printk, printkhandler), 0); + /* Need T_WARN for unconditional printk() */ + twarn("Testing kprobe entry..."); + ASSERT_INT_GT_GOTO(probecount, 0, done); + ASSERT_INT_EQ_GOTO(KTF_REGISTER_ENTRY_PROBE(probeargtest, + probeargtesthandler), + 0, done); + probeargtest(1, 2); + ASSERT_INT_EQ_GOTO(entryarg0, 1, done); + ASSERT_INT_EQ_GOTO(entryarg1, 2, done); +done: + KTF_UNREGISTER_ENTRY_PROBE(probeargtest, probeargtesthandler); + KTF_UNREGISTER_ENTRY_PROBE(printk, printkhandler); +} + +static int override_failed; + +noinline int myfunc(int i) +{ + override_failed = 1; + return i; +} + +KTF_OVERRIDE(myfunc, myfunc_override) +{ + KTF_SET_RETURN_VALUE(0); + KTF_OVERRIDE_RETURN; +} + +TEST(selftest, override) +{ + override_failed = 0; + + ASSERT_INT_EQ(KTF_REGISTER_OVERRIDE(myfunc, myfunc_override), 0); + + (void)myfunc(0); + + /* Verify override function runs instead. */ + ASSERT_TRUE_GOTO(override_failed == 0, done); + + /* Verify override function modifies return value. */ + ASSERT_INT_EQ_GOTO(myfunc(100), 0, done); + ASSERT_TRUE_GOTO(override_failed == 0, done); +done: + KTF_UNREGISTER_OVERRIDE(myfunc, myfunc_override); +} + +noinline int probesum(int a, int b) +{ + tlog(T_INFO, "Adding %d + %d", a, b); + return a + b; +} + +KTF_RETURN_PROBE(probesum, probesumhandler) +{ + tlog(T_DEBUG, "return value before modifying %ld", + regs_return_value(regs)); + KTF_SET_RETURN_VALUE(-1); + tlog(T_DEBUG, "return value after modifying %ld", + regs_return_value(regs)); + return 0; +} + +KTF_RETURN_PROBE(printk, printkrethandler) +{ + proberet = KTF_RETURN_VALUE(); + + return 0; +} + +TEST(selftest, probereturn) +{ + char *teststr = "Testing kprobe return..."; + + proberet = -1; + ASSERT_INT_EQ_GOTO(KTF_REGISTER_RETURN_PROBE(printk, printkrethandler), + 0, done); + printk(KERN_INFO "%s", teststr); + ASSERT_INT_EQ_GOTO(proberet, strlen(teststr), done); + + /* Now test modification of return value */ + ASSERT_INT_EQ_GOTO(probesum(1, 1), 2, done); + ASSERT_INT_EQ_GOTO(KTF_REGISTER_RETURN_PROBE(probesum, probesumhandler), + 0, done); + ASSERT_INT_EQ_GOTO(probesum(1, 1), -1, done); +done: + KTF_UNREGISTER_RETURN_PROBE(printk, printkrethandler); + KTF_UNREGISTER_RETURN_PROBE(probesum, probesumhandler); +} + +static void add_probe_tests(void) +{ + ADD_TEST(probeentry); + ADD_TEST(probereturn); + ADD_TEST(override); +} + +noinline void cov_counted(void) +{ + tlog(T_INFO, "got called!"); +} + +noinline void *doalloc(struct kmem_cache *c, size_t sz) +{ + if (c) + return kmem_cache_alloc(c, GFP_KERNEL); + return kmalloc(sz, GFP_KERNEL); +} + +TEST(selftest, acov) +{ + /* A very basic test just to enable and disable the coverage support, + * without the memory tracking option and without making use of it: + */ + ASSERT_INT_EQ(0, ktf_cov_enable((THIS_MODULE)->name, 0)); + ktf_cov_disable((THIS_MODULE)->name); +} + +TEST(selftest, cov) +{ + int foundp1 = 0, foundp2 = 0, foundp3 = 0, foundp4 = 0; + struct ktf_cov_entry *e; + struct ktf_cov_mem *m; + char *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL; + struct kmem_cache *c = NULL; + int oldcount; + + c = kmem_cache_create("selftest_cov_cache", + 32, 0, + SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); + + ASSERT_ADDR_NE(NULL, c); + + tlog(T_INFO, "Allocated cache %p : %s %u\n", c, c->name, c->object_size); + ASSERT_INT_EQ(0, ktf_cov_enable((THIS_MODULE)->name, KTF_COV_OPT_MEM)); + + e = ktf_cov_entry_find((unsigned long)cov_counted, 0); + ASSERT_ADDR_NE_GOTO(e, NULL, done); + oldcount = e->count; + ktf_cov_entry_put(e); + cov_counted(); + e = ktf_cov_entry_find((unsigned long)cov_counted, 0); + ASSERT_ADDR_NE_GOTO(e, NULL, done); + if (e) { + ASSERT_INT_EQ(e->count, oldcount + 1); + ktf_cov_entry_put(e); + } + + /* Need to call a noinline fn to do allocs since this test function + * will be inlined; and to track allocations they need to come + * from this module. Don't need to do the same for kfree since + * we check every kfree() to see if it is freeing a tracked allocation. + */ + p1 = doalloc(NULL, 8); + ASSERT_ADDR_NE_GOTO(p1, NULL, done); + p2 = doalloc(NULL, 16); + ASSERT_ADDR_NE_GOTO(p2, NULL, done); + p3 = doalloc(c, 0); + ASSERT_ADDR_NE_GOTO(p3, NULL, done); + p4 = doalloc(c, 0); + ASSERT_ADDR_NE_GOTO(p4, NULL, done); + + ktf_for_each_cov_mem(m) { + if (m->key.address == (unsigned long)p1) + foundp1 = 1; + if (m->key.address == (unsigned long)p2 && m->key.size == 16) + foundp2 = 1; + if (m->key.address == (unsigned long)p3 && m->key.size == 32) + foundp3 = 1; + if (m->key.address == (unsigned long)p4) + foundp4 = 1; + } + ASSERT_INT_EQ_GOTO(foundp1, 1, done); + ASSERT_INT_EQ_GOTO(foundp2, 1, done); + ASSERT_INT_EQ_GOTO(foundp3, 1, done); + ASSERT_INT_EQ_GOTO(foundp4, 1, done); + kfree(p1); + kmem_cache_free(c, p4); + /* Didn't free p2/p3 - should still be on our cov_mem list */ + foundp1 = 0; + foundp2 = 0; + foundp3 = 0; + foundp4 = 0; + ktf_for_each_cov_mem(m) { + if (m->key.address == (unsigned long)p1) + foundp1 = 1; + if (m->key.address == (unsigned long)p2) + foundp2 = 1; + if (m->key.address == (unsigned long)p3) + foundp3 = 1; + if (m->key.address == (unsigned long)p4) + foundp4 = 1; + } + ASSERT_INT_EQ_GOTO(foundp2, 1, done); + ASSERT_INT_EQ_GOTO(foundp3, 1, done); + ASSERT_INT_EQ_GOTO(foundp1, 0, done); + ASSERT_INT_EQ_GOTO(foundp4, 0, done); +done: + kfree(p2); + if (p3) + kmem_cache_free(c, p3); + ktf_cov_disable((THIS_MODULE)->name); + kmem_cache_destroy(c); +} + +static void add_cov_tests(void) +{ + ADD_TEST(acov); + /* We still seem to have some subtle issues with the memory coverage test feature, + * as sometimes allocations made by the coverage framework itself, + * for this particular test survives the cleanup function. + * Whether it is our attempt to test ourselves or a more generic problem + * is not fully understood yet, so disable this test for now: + */ + /* ADD_TEST(cov); */ +} + +KTF_THREAD(test_thread) +{ + /* ensure assertions can work in thread context */ + ASSERT_INT_EQ(1, 1); +} + +#define NUM_TEST_THREADS 20 + +static struct ktf_thread test_threads[NUM_TEST_THREADS]; + +TEST(selftest, thread) +{ + int assertions, i; + + for (i = 0; i < NUM_TEST_THREADS; i++) { + KTF_THREAD_INIT(test_thread, &test_threads[i]); + KTF_THREAD_RUN(&test_threads[i]); + } + for (i = 0; i < NUM_TEST_THREADS; i++) + KTF_THREAD_WAIT_COMPLETED(&test_threads[i]); + + assertions = (int)ktf_get_assertion_count(); + + /* Verify assertion in thread */ + ASSERT_INT_EQ(assertions, NUM_TEST_THREADS); +} + +static void add_thread_tests(void) +{ + ADD_TEST(thread); +} + +static int selftest_module_var; + +/* + * Test that ktf_find_symbol works both for module symbols and + * core kernel symbols: + */ +TEST(selftest, symbol) +{ + /* Verify finding kernel-internal symbol works. */ + ASSERT_ADDR_NE(ktf_find_symbol(NULL, "skbuff_head_cache"), NULL); + + /* Verify finding module symbols works, both when we specify the + * module name and when we don't. + */ + ASSERT_ADDR_EQ(ktf_find_symbol(NULL, "selftest_module_var"), + &selftest_module_var); + + ASSERT_ADDR_EQ(ktf_find_symbol("selftest", "selftest_module_var"), + &selftest_module_var); +} + +static void add_symbol_tests(void) +{ + ADD_TEST(symbol); +} + +static int __init selftest_init(void) +{ + int ret = KTF_CONTEXT_ADD_TO(dual_handle, &s_mctx[1].k, "map1"); + + tlog(T_DEBUG, "map1 gets %d", ret); + if (ret) + return ret; + + ret = KTF_CONTEXT_ADD_TO(dual_handle, &s_mctx[2].k, "map2"); + if (ret) + goto fail; + + ret = KTF_CONTEXT_ADD_TO(single_handle, &s_mctx[3].k, "map3"); + if (ret) + goto fail; + + ktf_resolve_symbols(); + + add_map_tests(); + add_probe_tests(); + add_cov_tests(); + add_thread_tests(); + add_hybrid_tests(); + add_context_tests(); + add_symbol_tests(); + tlog(T_INFO, "selftest: loaded"); + return 0; +fail: + KTF_CLEANUP(); + return ret; +} + +static void __exit selftest_exit(void) +{ + context_tests_cleanup(); + KTF_HANDLE_CLEANUP(single_handle); + KTF_HANDLE_CLEANUP(dual_handle); + KTF_HANDLE_CLEANUP(no_handle); + KTF_CLEANUP(); + tlog(T_INFO, "selftest: unloaded"); +} + +module_init(selftest_init); +module_exit(selftest_exit); From patchwork Tue Aug 13 06:09:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091189 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 44E0B746 for ; Tue, 13 Aug 2019 06:12:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 376C527FE4 for ; Tue, 13 Aug 2019 06:12:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2B8E028587; Tue, 13 Aug 2019 06:12:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1A07C27FE4 for ; Tue, 13 Aug 2019 06:12:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727259AbfHMGMC (ORCPT ); Tue, 13 Aug 2019 02:12:02 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:37862 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727748AbfHMGMB (ORCPT ); Tue, 13 Aug 2019 02:12:01 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D690jC022084; Tue, 13 Aug 2019 06:11:38 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=d0uU65Z04im+BrtimXWGyB6YZa/jBywtmtJQJXOqv7E=; b=HkRnfsIO/EN5rfLivPXtBjOrKEnDUTWwo1Veec7kOoSQI+113RzqCUtaVxKYAonmbj6s e1mnqEEldeEqx/bJAvBk4ctZt+As+YzpKp59ymGj9VvaW2jTbwsZS9lWiljFjXBEgLAJ 0pvXgOecdB+Ed0qXc/UfldfL8tXqMFmMA66qHv697NdvsMnIyA0hG8fvx22Fs1IZfGmE XviXlGChg8qY6ugUU/d8pCG/qDAilMuvgnd4JgSBya1Il1WP0PE0lQ3mOwfz5afdinKj LN4eLRrtntCpoIUidv4qjcTkmOHRBcqzCXLr5LrLjy79cgOlo45BByzwaeGdvbSee5HO aw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=d0uU65Z04im+BrtimXWGyB6YZa/jBywtmtJQJXOqv7E=; b=PSpUlK/ikqLNeuQQltf+DgLyJBJcWWu3Iwp9nFmMQnG9/ALsbuXD2pyphmqxLbOJ2FqL Qy6U4elNXd52nesqzG7+aa+p6WSNz0qmb9Tu8O5DI5m2FZIFlVPMsHT4f5scLzu1jHAx 2L/die7wqSIthSCB1ES96oQsJ2yY232utujPOEO81evLj/AI3ZjIt1MfI3fXUHUukyZx qodzVLlfiYNJCc15FGmqBxVnBDCIp8A6sONf3x1qfPBQyvpLOigkSWO35/ALQU83XG0S n5hPpQm5VpmuvfDKJtlvM7EKP/6/zxCC/Y5z7aCDb+BnD2xGIAyCCNWvEOl/Ki4FsOJr tA== Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by userp2120.oracle.com with ESMTP id 2u9pjqbvms-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:38 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68YLF049765; Tue, 13 Aug 2019 06:11:38 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by userp3030.oracle.com with ESMTP id 2u9k1w41j4-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:37 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6BawQ026730; Tue, 13 Aug 2019 06:11:36 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:36 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 12/19] ktf: Main part of user land library for executing tests Date: Tue, 13 Aug 2019 08:09:27 +0200 Message-Id: <45a49a1c8c826254db169e104f1d50b389e02a03.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Implementation of the main part of the user library to communicate with the kernel side of ktf about tests, configuration and test results. ktf.h: User mode side of KTF extensions to the gtest unit test framework. ktf_int.cc: Implementation of Gtest user land test management ktf_int.h: User mode side of extension to the gtest unit test framework: Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/lib/Makefile | 21 +- tools/testing/selftests/ktf/lib/ktf.h | 114 ++- tools/testing/selftests/ktf/lib/ktf_int.cc | 1031 +++++++++++++++++++++- tools/testing/selftests/ktf/lib/ktf_int.h | 84 ++- 4 files changed, 1250 insertions(+) create mode 100644 tools/testing/selftests/ktf/lib/Makefile create mode 100644 tools/testing/selftests/ktf/lib/ktf.h create mode 100644 tools/testing/selftests/ktf/lib/ktf_int.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_int.h diff --git a/tools/testing/selftests/ktf/lib/Makefile b/tools/testing/selftests/ktf/lib/Makefile new file mode 100644 index 0000000..c2be04b --- /dev/null +++ b/tools/testing/selftests/ktf/lib/Makefile @@ -0,0 +1,21 @@ + +GTEST_CFLAGS ?= -I$(GTEST_PATH)/include -DGTEST_HAS_PTHREAD=1 -lpthread +GTEST_LIBS ?= -L$(GTEST_PATH)/lib64 -lgtest -lpthread +NETLINK_CFLAGS ?= $(shell pkgconf --cflags libnl-genl-3.0) +HOST_EXTRACFLAGS = -I$(srctree)/$(src)/.. $(NETLINK_CFLAGS) $(GTEST_CFLAGS) \ + -Wall -Werror -Wno-packed-bitfield-compat -D_GNU_SOURCE +HOST_EXTRACXXFLAGS = -I$(srctree)/$(src)/.. $(NETLINK_CFLAGS) $(GTEST_CFLAGS) \ + -Wall \ + -Wno-packed-bitfield-compat \ + -Wno-pointer-arith -Werror \ + -D__FILENAME__=\"`basename $<`\" + +hostcxxlibs-y := libktf.so +libktf-cshobjs = ktf_unlproto.o +libktf-cxxshobjs = ktf_int.o ktf_run.o ktf_debug.o + +targets := $(addprefix $(obj)/,$(libktf-cshobjs)) \ + $(addprefix $(obj)/,$(libktf-cxxshobjs)) \ + $(addprefix $(obj)/,$(hostcxxlibs-y)) + +__build: $(targets) diff --git a/tools/testing/selftests/ktf/lib/ktf.h b/tools/testing/selftests/ktf/lib/ktf.h new file mode 100644 index 0000000..942eb28 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf.h: User mode side of KTF extensions to the gtest unit test framework. + * Include this to write hybrid tests + * + */ +#ifndef _KTF_H +#define _KTF_H +#include + +namespace ktf +{ + + /* Interfaces intended to be used directly by programs: + * ---------------------------------------------------- + */ + class KernelTest; + + /* Invoke the kernel test - to be called directly from user mode + * hybrid tests: + */ + void run(KernelTest* kt, std::string ctx = ""); + + /* Function for enabling/disabling coverage for module */ + int set_coverage(std::string module, unsigned int opts, bool enabled); + + typedef void (*configurator)(void); + + // Initialize KTF: + // If necessary, supply a callback that uses the KTF_CONTEXT_CFG* macros below + // to configure any necessary contexts: + void setup(configurator c = NULL); + +} // end namespace ktf + +/* HTEST: Define user part of a hybrid test. + * Hybrid tests are tests that have a user and a kernel counterpart, + * to allow testing of interaction between user mode and the kernel: + */ +#define HTEST(__setname,__testname) \ + class __setname ## _ ## __testname : public ktf::test_cb \ + {\ + public:\ + __setname ## _ ## __testname() {\ + ktf::add_wrapper(#__setname,#__testname,as_test_cb()); \ + }\ + virtual void fun(ktf::KernelTest* kt); \ + }; \ + __setname ## _ ## __testname \ + __setname ## _ ## __testname ## _value;\ + void __setname ## _ ## __testname::fun(ktf::KernelTest* self) + + +/* Part of KTF support for hybrid tests: allocate/get a reference to + * an out-of-band user data pointer: + */ +#define KTF_USERDATA(__kt_ptr, __priv_datatype, __priv_data) \ + struct __priv_datatype *__priv_data = \ + (struct __priv_datatype *)get_priv(__kt_ptr, sizeof(struct __priv_datatype)); \ + ASSERT_TRUE(__priv_data); \ + ASSERT_EQ(get_priv_sz(__kt_ptr), sizeof(struct __priv_datatype)) + +/* KTF support for configurable contexts: + * Send a configuation data structure to the given context name. + */ +#define KTF_CONTEXT_CFG(__context_name, __context_type_name, __priv_datatype, __priv_data) \ + ktf::configure_context(__context_name, __context_type_name, \ + (struct __priv_datatype *)__priv_data, \ + sizeof(__priv_datatype)) +/* Alternative to KTF_CONTEXT_CFG: If there are multiple contexts with the same name + * (but with different handles) use a test name to identify the context to be configured + */ +#define KTF_CONTEXT_CFG_FOR_TEST(__test_name, __context_type_name, __priv_datatype, __priv_data) \ + ktf::configure_context_for_test(__test_name, __context_type_name, \ + (struct __priv_datatype *)__priv_data, \ + sizeof(__priv_datatype)) + + + +/* Private interfaces (needed by macro definitions above) + * ------------------------------------------------------ + */ + +namespace ktf { + class test_cb + { + public: + virtual ~test_cb() {} + virtual test_cb* as_test_cb() { return this; } + virtual void fun(KernelTest* kt) {} + }; + + /* Function for adding a user level test wrapper */ + void add_wrapper(const std::string setname, const std::string testname, + test_cb* tcb); + + /* get a priv pointer of the given size, allocate if necessary */ + void* get_priv(KernelTest* kt, size_t priv_sz); + + /* Get the size of the existing priv data */ + size_t get_priv_sz(KernelTest *kt); + + // Configure ktf context - to be used via KTF_CONTEXT_CFG*(): + void configure_context(const std::string context, const std::string type_name, + void *data, size_t data_sz); + void configure_context_for_test(const std::string testname, const std::string type_name, + void *data, size_t data_sz); +} // end namespace ktf + +#endif diff --git a/tools/testing/selftests/ktf/lib/ktf_int.cc b/tools/testing/selftests/ktf/lib/ktf_int.cc new file mode 100644 index 0000000..6ac1f54 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_int.cc @@ -0,0 +1,1031 @@ +/* + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_int.cpp: Implementation of Gtest user land test management + * for kernel and hybrid test functionality provided by KTF. + */ +#include +#include +#include +#include +#include "kernel/ktf_unlproto.h" +#include +#include +#include +#include +#include +#include +#include "ktf_int.h" +#include "ktf_debug.h" + +#include + +int devcnt = 0; + +namespace ktf +{ + +struct nl_sock* sock = NULL; +int family = -1; + +int printed_header = 0; + +typedef std::map testmap; +typedef std::map wrappermap; + +class testset +{ +public: + testset() : setnum(0) + { } + + ~testset() + { + for (testmap::iterator it = tests.begin(); it != tests.end(); ++it) + delete it->second; + } + + testmap tests; + stringvec test_names; + wrappermap wrapper; + int setnum; +}; + +/* ConfigurableContext keeps track of a ktf_context that requires configuration. + * Context names are unique within a handle, so a handle ID is necessary to + * identify the context. The actual configuration data must be agreed upon between + * user mode and kernel mode on a per context basis. They can use type_id + * to identify which type of parameter a context needs. + * The type_id is also used to create new contexts in the kernel. + * The kernel implementation must enable such dynamically extensible context sets + * on a per type_id basis. + */ +class ConfigurableContext +{ +public: + ConfigurableContext(const std::string& name, const std::string& type_name, + unsigned int hid, int cfg_stat); + + std::string str_state(); + int Configure(void *data, size_t data_sz); + + const std::string& Type() + { + return type_name; + } + + std::string name; + int handle_id; + std::string type_name; + int cfg_stat; +}; + +typedef std::map setmap; +typedef std::set stringset; +typedef std::vector context_vector; + +struct name_iter +{ + setmap::iterator it; + std::string setname; +}; + +class ContextType +{ +public: + ContextType(int handle_id, const std::string& type_name); + int handle_id; + std::string type_name; +}; + + ContextType::ContextType(int hid, const std::string& tn) + : handle_id(hid), + type_name(tn) +{} + +/* We trick the gtest template framework + * to get a new set of test names as a side effect of + * invocation of get_test_names() + */ + +/* Wrap globals in an object to control init order and + * memory cleanup: + */ +class KernelTestMgr +{ +public: + KernelTestMgr() : next_set(0), cur(NULL) + { } + + ~KernelTestMgr(); + + testset& find_add_set(std::string& setname); + testset& find_add_test(std::string& setname, std::string& testname); + void add_test(const std::string& setname, const char* tname, unsigned int handle_id); + KernelTest* find_test(const std::string&setname, const std::string& testname, std::string* ctx); + void add_wrapper(const std::string setname, const std::string testname, test_cb* tcb); + + stringvec& get_set_names() { return set_names; } + stringvec get_test_names(); + + stringvec get_testsets() + { + return set_names; + } + + std::string get_current_setname() + { + return cur->setname; + } + + stringvec& get_contexts(unsigned int id) + { + return handle_to_ctxvec[id]; + } + + void add_cset(unsigned int hid, stringvec& ctxs); + void add_ctype(unsigned int hid, const std::string& type_name); + std::vector add_configurable_context(const std::string& ctx, + const std::string& type_name, + unsigned int hid, int cfg_stat); + std::vector add_configurable_contexts(const std::string& ctx, + std::vector type_vec); + std::vector find_contexts(const std::string& ctx, const std::string& type_name); + + /* Contexts may be created on the fly if the kernel supports it for this type_name: */ + std::vector maybe_create_context(const std::string& ctx, + const std::string& type_name); + + /* Update the list of contexts returned from the kernel with a newly created one */ + void add_context(unsigned int hid, const std::string& ctx); +private: + setmap sets; + stringvec test_names; + stringvec set_names; + stringset kernelsets; + std::map handle_to_ctxvec; + std::map cfg_contexts; + + // Context types that allows dynamically created contexts: + std::map > ctx_types; + int next_set; + name_iter* cur; +}; + +KernelTestMgr::~KernelTestMgr() +{ + std::map::iterator it; + for (it = cfg_contexts.begin(); it != cfg_contexts.end(); ++it) + { + context_vector::iterator vit; + for (vit = it->second.begin(); vit != it->second.end(); ++vit) + delete *vit; + } + + std::map >::iterator tit; + for (tit = ctx_types.begin(); tit != ctx_types.end(); ++tit) + { + std::vector::iterator ttit; + for (ttit = tit->second.begin(); ttit != tit->second.end(); ++ttit) + delete *ttit; + } +} + +context_vector KernelTestMgr::find_contexts(const std::string& ctx, const std::string& type_name) +{ + std::map::iterator it; + it = cfg_contexts.find(ctx); + if (it == cfg_contexts.end()) + return maybe_create_context(ctx, type_name); + else + return it->second; +} + +context_vector KernelTestMgr::maybe_create_context(const std::string& ctx, const std::string& type_name) +{ + std::map >::iterator it; + it = ctx_types.find(type_name); + if (it == ctx_types.end()) + return context_vector(); + else + return add_configurable_contexts(ctx, it->second); +} + +void KernelTestMgr::add_context(unsigned int hid, const std::string& ctx) +{ + handle_to_ctxvec[hid].push_back(ctx); +} + + +KernelTestMgr& kmgr() +{ + static KernelTestMgr kmgr_; + return kmgr_; +} + +testset& KernelTestMgr::find_add_test(std::string& setname, std::string& testname) +{ + testset& ts(find_add_set(setname)); + test_names.push_back(testname); + return ts; +} + +testset& KernelTestMgr::find_add_set(std::string& setname) +{ + bool new_set = false; + + log(KTF_DEBUG, "find_add_set(%s)\n", setname.c_str()); + + stringset::iterator it = kernelsets.find(setname); + if (it == kernelsets.end()) { + kernelsets.insert(setname); + set_names.push_back(setname); + new_set = true; + } + + /* This implicitly adds a new testset to sets, if it's not there: */ + testset& ts = sets[setname]; + if (new_set) + { + ts.setnum = next_set++; + log(KTF_INFO, "added %s (set %d) total %lu sets\n", setname.c_str(), ts.setnum, sets.size()); + } + return ts; +} + + +void KernelTestMgr::add_test(const std::string& setname, const char* tname, + unsigned int handle_id) +{ + log(KTF_INFO_V, "add_test: %s.%s", setname.c_str(),tname); + logs(KTF_INFO_V, + if (handle_id) + fprintf(stderr, " [id %d]\n", handle_id); + else + fprintf(stderr, "\n")); + std::string name(tname); + new KernelTest(setname, tname, handle_id); +} + + +/* Here we might get called with test names expanded with context names */ +KernelTest* KernelTestMgr::find_test(const std::string&setname, + const std::string& testname, + std::string* pctx) +{ + size_t pos; + log(KTF_DEBUG, "find test %s.%s\n", setname.c_str(), testname.c_str()); + + /* Try direct lookup first: */ + KernelTest* kt = sets[setname].tests[testname]; + if (kt) { + *pctx = std::string(); + return kt; + } + + /* If we don't have any contexts set, no need to parse name: */ + if (handle_to_ctxvec.empty()) + return NULL; + + pos = testname.find_last_of('_'); + while (pos >= 0) { + std::string tname = testname.substr(0,pos); + std::string ctx = testname.substr(pos + 1, testname.npos); + *pctx = ctx; + kt = sets[setname].tests[tname]; + if (kt) + return kt; + /* context name might contain an '_' , iterate on: */ + pos = tname.find_last_of('_'); + } + return NULL; +} + + +void KernelTestMgr::add_cset(unsigned int hid, stringvec& ctxs) +{ + log(KTF_INFO, "hid %d: ", hid); + logs(KTF_INFO, for (stringvec::iterator it = ctxs.begin(); it != ctxs.end(); ++it) + fprintf(stderr, "%s ", it->c_str()); + fprintf(stderr, "\n")); + handle_to_ctxvec[hid] = ctxs; +} + +void KernelTestMgr::add_ctype(unsigned int hid, const std::string& type_name) +{ + log(KTF_INFO, "hid %d: dynamical type: %s\n", hid, type_name.c_str()); + ctx_types[type_name].push_back(new ContextType(hid, type_name)); +} + +std::vector KernelTestMgr::add_configurable_context(const std::string& ctx, + const std::string& type_name, + unsigned int hid, int cfg_stat) +{ + cfg_contexts[ctx].push_back(new ConfigurableContext(ctx, type_name, hid, cfg_stat)); + return cfg_contexts[ctx]; +} + +/* Function for adding a wrapper user level test */ +void KernelTestMgr::add_wrapper(const std::string setname, const std::string testname, + test_cb* tcb) +{ + log(KTF_DEBUG, "add_wrapper: %s.%s\n", setname.c_str(),testname.c_str()); + testset& ts = sets[setname]; + + /* Depending on C++ initialization order which vary between compiler version + * (sigh!) either the kernel tests have already been processed or we have to store + * this object in wrapper for later insertion: + */ + KernelTest *kt = ts.tests[testname]; + if (kt) { + log(KTF_DEBUG_V, "Assigning user_test for %s.%s\n", + setname.c_str(), testname.c_str()); + kt->user_test = tcb; + } else { + log(KTF_DEBUG_V, "Set wrapper for %s.%s\n", + setname.c_str(), testname.c_str()); + ts.wrapper[testname] = tcb; + } +} + +std::vector KernelTestMgr::add_configurable_contexts(const std::string& ctx, + std::vector type_vec) +{ + std::vector::iterator it = type_vec.begin(); + for (; it != type_vec.end(); ++it) { + /* We use ENODEV (instead of the kernel's ENOENT to indicate to ConfigurableContext that + * this context was not reported in the query, and thus need to be added locally upon a + * successful configuration: + */ + cfg_contexts[ctx].push_back(new ConfigurableContext(ctx, (*it)->type_name, (*it)->handle_id, ENODEV)); + } + return cfg_contexts[ctx]; +} + + +stringvec KernelTestMgr::get_test_names() +{ + if (!cur) { + cur = new name_iter(); + cur->it = sets.begin(); + } + + /* Filter out any combined tests that do not have a kernel counterpart loaded */ + while (cur->it->second.wrapper.size() != 0 && cur->it != sets.end()) { + if (cur->it->second.test_names.size() == 0) + log(KTF_INFO, "Note: Skipping test suite %s which has combined tests with no kernel counterpart\n", + cur->it->first.c_str()); + ++(cur->it); + } + + if (cur->it == sets.end()) { + delete cur; + cur = NULL; + return stringvec(); + } + + stringvec& v = cur->it->second.test_names; + cur->setname = cur->it->first; + + ++(cur->it); + return v; +} + +ConfigurableContext::ConfigurableContext(const std::string& name_, const std::string& type_name_, + unsigned int hid, int cfg_stat_) + : name(name_), + handle_id(hid), + type_name(type_name_), + cfg_stat(cfg_stat_) +{ + log(KTF_INFO, "%s[%s] (hid %d): state: %s\n", + name.c_str(), type_name.c_str(), hid, str_state().c_str()); +} + +std::string ConfigurableContext::str_state() +{ + switch (cfg_stat) { + case 0: + return std::string("READY"); + case ENOENT: + return std::string("UNCONFIGURED"); + case ENODEV: + return std::string("UNCREATED"); + default: + char tmp[100]; + sprintf(tmp, "ERROR(%d)", cfg_stat); + return std::string(tmp); + } +} + +int ConfigurableContext::Configure(void *data, size_t data_sz) +{ + struct nl_msg *msg = nlmsg_alloc(); + int err; + + log(KTF_INFO, "%s, data_sz %lu\n", name.c_str(), data_sz); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, + KTF_C_REQ, 1); + nla_put_u32(msg, KTF_A_TYPE, KTF_CT_CTX_CFG); + nla_put_u64(msg, KTF_A_VERSION, KTF_VERSION_LATEST); + nla_put_string(msg, KTF_A_STR, name.c_str()); + nla_put_u32(msg, KTF_A_HID, handle_id); + nla_put_string(msg, KTF_A_FILE, type_name.c_str()); + nla_put(msg, KTF_A_DATA, data_sz, data); + + // Send message over netlink socket + nl_send_auto_complete(sock, msg); + + // Free message + nlmsg_free(msg); + + // Wait for acknowledgement: + // This function also returns error status if the message + // was not deemed ok by the kernel, but the error status + // does not resemble what the netlink recipient returned. + // + // This message receives no response beyond the error code. + // + err = nl_wait_for_ack(sock); + + if (!err && cfg_stat == ENODEV) { + // Successfully added a new context, update it's state and + // tell kmgr() about it: + kmgr().add_context(handle_id, name); + cfg_stat = 0; + } + return err; +} + +void *get_priv(KernelTest *kt, size_t sz) +{ + return kt->get_priv(sz); +} + +size_t get_priv_sz(KernelTest *kt) +{ + return kt->user_priv_sz; +} + +int set_coverage(std::string module, unsigned int opts, bool enabled) +{ + struct nl_msg *msg; + int err; + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, + KTF_C_REQ, 1); + nla_put_u32(msg, KTF_A_TYPE, + enabled ? KTF_CT_COV_ENABLE : KTF_CT_COV_DISABLE); + nla_put_u32(msg, KTF_A_COVOPT, opts); + nla_put_u64(msg, KTF_A_VERSION, KTF_VERSION_LATEST); + nla_put_string(msg, KTF_A_MOD, module.c_str()); + + // Send message over netlink socket + nl_send_auto_complete(sock, msg); + + // Free message + nlmsg_free(msg); + + //Wait for acknowledgement: + // This function also returns error status if the message + // was not deemed ok by the kernel. + // + err = nl_wait_for_ack(sock); + if (err == 0) { + // Then wait for the answer and receive it + nl_recvmsgs_default(sock); + } + return err; +} + + KernelTest::KernelTest(const std::string& sn, const char* tn, unsigned int handle_id) + : setname(sn), + testname(tn), + setnum(0), + testnum(0), + user_priv(NULL), + user_priv_sz(0), + user_test(NULL), + file(NULL), + line(-1) +{ + + name = setname; + name.append("."); + name.append(testname); + + testset& ts(kmgr().find_add_test(setname, testname)); + setnum = ts.setnum; + ts.tests[testname] = this; + + if (!handle_id) + ts.test_names.push_back(testname); + else { + stringvec& ctxv = kmgr().get_contexts(handle_id); + for (stringvec::iterator it = ctxv.begin(); it != ctxv.end(); ++it) + ts.test_names.push_back(testname + "_" + *it); + } + testnum = ts.tests.size(); + + wrappermap::iterator hit = ts.wrapper.find(testname); + if (hit != ts.wrapper.end()) { + log(KTF_DEBUG_V, "Assigning user_test from wrapper for %s.%s\n", + setname.c_str(), testname.c_str()); + user_test = hit->second; + /* Clear out wrapper entry as we skip any test sets + * with nonempty wrapper lists during test execution: + */ + ts.wrapper.erase(hit); + } +} + + +KernelTest::~KernelTest() +{ + if (user_priv) + free(user_priv); +} + +void* KernelTest::get_priv(size_t p_sz) +{ + if (!user_priv) { + user_priv = malloc(p_sz); + if (user_priv) + user_priv_sz = p_sz; + } + return user_priv; +} + +static int parse_cb(struct nl_msg *msg, void *arg); +static int debug_cb(struct nl_msg *msg, void *arg); +static int error_cb(struct nl_msg *msg, void *arg); + +int nl_connect(void) +{ + /* Allocate a new netlink socket */ + sock = nl_socket_alloc(); + if (sock == NULL){ + fprintf(stderr, "Failed to allocate a nl socket"); + exit(1); + } + + /* Connect to generic netlink socket on kernel side */ + int stat = genl_connect(sock); + if (stat) { + fprintf(stderr, "Failed to open generic netlink connection"); + exit(1); + } + + /* Ask kernel to resolve family name to family id */ + family = genl_ctrl_resolve(sock, "ktf"); + if (family <= 0) { + fprintf(stderr, "Netlink protocol family for ktf not found - is the ktf module loaded?\n"); + exit(1); + } + + /* Specify the generic callback functions for messages */ + nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL); + nl_socket_modify_cb(sock, NL_CB_INVALID, NL_CB_CUSTOM, error_cb, NULL); + return 0; +} + + +void default_test_handler(int result, const char* file, int line, const char* report) +{ + if (result >= 0) { + fprintf(stderr, "default_test_handler: Result %d: %s,%d\n",result,file,line); + } else { + fprintf(stderr, "default_test_handler: Result %d\n",result); + } +} + +test_handler handle_test = default_test_handler; + +bool setup(test_handler ht) +{ + ktf_debug_init(); + handle_test = ht; + return nl_connect() == 0; +} + + +configurator do_context_configure = NULL; + +void set_configurator(configurator c) +{ + do_context_configure = c; +} + +/* Query kernel for available tests in index order */ +stringvec& query_testsets() +{ + struct nl_msg *msg; + int err; + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, + KTF_C_REQ, 1); + nla_put_u32(msg, KTF_A_TYPE, KTF_CT_QUERY); + nla_put_u64(msg, KTF_A_VERSION, KTF_VERSION_LATEST); + + // Send message over netlink socket + nl_send_auto_complete(sock, msg); + + // Free message + nlmsg_free(msg); + + // Wait for acknowledgement: + // This function also returns error status if the message + // was not deemed ok by the kernel. + // + err = nl_wait_for_ack(sock); + if (err < 0) { + errno = -err; + return kmgr().get_set_names(); + } + + // Then wait for the answer and receive it + nl_recvmsgs_default(sock); + return kmgr().get_set_names(); +} + +stringvec get_test_names() +{ + return kmgr().get_test_names(); +} + +std::string get_current_setname() +{ + return kmgr().get_current_setname(); +} + +KernelTest* find_test(const std::string&setname, const std::string& testname, std::string* ctx) +{ + return kmgr().find_test(setname, testname, ctx); +} + +void add_wrapper(const std::string setname, const std::string testname, test_cb* tcb) +{ + kmgr().add_wrapper(setname, testname, tcb); +} + +void run_test(KernelTest* kt, std::string& ctx) +{ + if (kt->user_test) + kt->user_test->fun(kt); + else + run(kt, ctx); +} + +/* Run the kernel test */ +void run(KernelTest* kt, std::string context) +{ + struct nl_msg *msg; + + log(KTF_DEBUG_V, "START kernel test (%ld,%ld): %s\n", kt->setnum, + kt->testnum, kt->name.c_str()); + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, + KTF_C_REQ, 1); + nla_put_u32(msg, KTF_A_TYPE, KTF_CT_RUN); + nla_put_u64(msg, KTF_A_VERSION, KTF_VERSION_LATEST); + nla_put_string(msg, KTF_A_SNAM, kt->setname.c_str()); + nla_put_string(msg, KTF_A_TNAM, kt->testname.c_str()); + + if (!context.empty()) + nla_put_string(msg, KTF_A_STR, context.c_str()); + + /* Send any test specific out-of-band data */ + if (kt->user_priv) + nla_put(msg, KTF_A_DATA, kt->user_priv_sz, kt->user_priv); + + // Send message over netlink socket + nl_send_auto_complete(sock, msg); + + // Free message + nlmsg_free(msg); + + // Wait for acknowledgement - otherwise + // nl_recvmsg_default will sometimes take the ack for the next message.. + int err = nl_wait_for_ack(sock); + if (err < 0) { + errno = -err; + return; + } + + // Wait for the answer and receive it + nl_recvmsgs_default(sock); + + log(KTF_DEBUG_V, "END ktf::run_kernel_test %s\n", kt->name.c_str()); +} + + +void configure_context(const std::string context, const std::string type_name, void *data, size_t data_sz) +{ + context_vector ct = kmgr().find_contexts(context, type_name); + ASSERT_GE(ct.size(), 1UL) << " - no context found named " << context; + ASSERT_EQ(ct.size(), 1UL) << " - More than one context named " << context + << " - use KTF_CONTEXT_CFG_FOR_TEST to uniquely identify context."; + ASSERT_EQ(type_name, ct[0]->Type()); + ASSERT_EQ(ct[0]->Configure(data, data_sz), 0); +} + +void configure_context_for_test(const std::string& setname, const std::string& testname, + const std::string& type_name, void *data, size_t data_sz) +{ + std::string context; + KernelTest *kt = kmgr().find_test(setname, testname, &context); + context_vector ct = kmgr().find_contexts(context, type_name); + ASSERT_TRUE(kt) << " Could not find test " << setname << "." << testname; + int handle_id = kt->handle_id; + ASSERT_NE(handle_id, 0) << " test " << setname << "." << testname << " does not have a context"; + + for (context_vector::iterator it = ct.begin(); it != ct.end(); ++it) + if ((*it)->handle_id == handle_id) + { + ASSERT_EQ(type_name, (*it)->Type()); + ASSERT_EQ((*it)->Configure(data, data_sz), 0); + return; + } + ASSERT_TRUE(false) << " unconfigurable context found for test " << setname << "." << testname << "?"; +} + + +static nl_cb_action parse_one_set(std::string& setname, + std::string& testname, struct nlattr* attr) +{ + int rem = 0; + struct nlattr *nla; + const char* msg; + unsigned int handle_id = 0; + + nla_for_each_nested(nla, attr, rem) { + switch (nla_type(nla)) { + case KTF_A_HID: + handle_id = nla_get_u32(nla); + break; + case KTF_A_STR: + msg = nla_get_string(nla); + kmgr().add_test(setname, msg, handle_id); + handle_id = 0; + break; + default: + fprintf(stderr,"parse_result: Unexpected attribute type %d\n", nla_type(nla)); + return NL_SKIP; + } + } + return NL_OK; +} + + + +static int parse_query(struct nl_msg *msg, struct nlattr** attrs) +{ + int alloc = 0, rem = 0, rem2 = 0, cfg_stat; + nl_cb_action stat; + std::string setname,testname,ctx; + + /* Version 0.1.0.0 did not report version back from the kernel */ + uint64_t kernel_version = (KTF_VERSION_SET(MAJOR, 0ULL) | KTF_VERSION_SET(MINOR, 1ULL)); + + if (attrs[KTF_A_VERSION]) + kernel_version = nla_get_u64(attrs[KTF_A_VERSION]); + + /* We only got here if we were compatible enough, log that we had differences */ + if (kernel_version != KTF_VERSION_LATEST) + { + const char* note = "Note"; + bool is_compatible = + KTF_VERSION(MAJOR, KTF_VERSION_LATEST) == KTF_VERSION(MAJOR, kernel_version) && + KTF_VERSION(MINOR, KTF_VERSION_LATEST) == KTF_VERSION(MINOR, kernel_version); + if (!is_compatible) + note = "Error"; + + fprintf(stderr, + "%s: KTF version difference - user lib %llu.%llu.%llu.%llu, kernel has %llu.%llu.%llu.%llu\n", + note, + KTF_VERSION(MAJOR, KTF_VERSION_LATEST), + KTF_VERSION(MINOR, KTF_VERSION_LATEST), + KTF_VERSION(MICRO, KTF_VERSION_LATEST), + KTF_VERSION(BUILD, KTF_VERSION_LATEST), + KTF_VERSION(MAJOR, kernel_version), + KTF_VERSION(MINOR, kernel_version), + KTF_VERSION(MICRO, kernel_version), + KTF_VERSION(BUILD, kernel_version)); + if (!is_compatible) + return NL_SKIP; + } + + if (attrs[KTF_A_HLIST]) { + struct nlattr *nla, *nla2; + stringvec contexts; + unsigned int handle_id = 0; + const char* type_name = NULL; + + /* Parse info on handle IDs and associated contexts and/or + * types that allows dynamical creation of new contexts + * (defined here via KTF_A_FILE): + */ + nla_for_each_nested(nla, attrs[KTF_A_HLIST], rem) { + switch (nla_type(nla)) { + case KTF_A_HID: + handle_id = nla_get_u32(nla); + break; + case KTF_A_LIST: + nla_for_each_nested(nla2, nla, rem2) { + switch (nla_type(nla2)) { + case KTF_A_FILE: + type_name = nla_get_string(nla2); + kmgr().add_ctype(handle_id, type_name); + break; + case KTF_A_STR: + ctx = nla_get_string(nla2); + contexts.push_back(ctx); + break; + case KTF_A_MOD: + type_name = nla_get_string(nla2); + break; + case KTF_A_STAT: + cfg_stat = nla_get_u32(nla2); + kmgr().add_configurable_context(ctx, type_name, handle_id, cfg_stat); + break; + } + } + /* Add this set of contexts for the handle_id */ + kmgr().add_cset(handle_id, contexts); + handle_id = 0; + contexts.clear(); + break; + default: + fprintf(stderr,"parse_query[HLIST]: Unexpected attribute type %d\n", nla_type(nla)); + return NL_SKIP; + } + } + } + + // Now we know enough about contexts and type_ids to actually configure + // any contexts that needs to be configured, and this must be + // done before the list of tests gets spanned out because addition + // of new contexts can lead to more tests being "generated": + // + if (do_context_configure) + do_context_configure(); + + if (attrs[KTF_A_NUM]) { + alloc = nla_get_u32(attrs[KTF_A_NUM]); + log(KTF_DEBUG, "Kernel offers %d test sets:\n", alloc); + } else { + fprintf(stderr,"No test set count in kernel response??\n"); + return -1; + } + + if (attrs[KTF_A_LIST]) { + struct nlattr *nla; + + /* Parse info on test sets */ + nla_for_each_nested(nla, attrs[KTF_A_LIST], rem) { + switch (nla_type(nla)) { + case KTF_A_STR: + setname = nla_get_string(nla); + break; + case KTF_A_TEST: + stat = parse_one_set(setname, testname, nla); + if (stat != NL_OK) + return stat; + break; + default: + fprintf(stderr,"parse_query[LIST]: Unexpected attribute type %d\n", nla_type(nla)); + return NL_SKIP; + } + kmgr().find_add_set(setname); /* Just to make sure empty sets are also added */ + } + } + + return NL_OK; +} + + +static enum nl_cb_action parse_result(struct nl_msg *msg, struct nlattr** attrs) +{ + int assert_cnt = 0, fail_cnt = 0; + int rem = 0, stat; + const char *file = "no_file",*report = "no_report"; + + if (attrs[KTF_A_STAT]) { + stat = nla_get_u32(attrs[KTF_A_STAT]); + log(KTF_DEBUG, "parsed test status %d\n", stat); + if (stat) { + fprintf(stderr, "Failed to execute test in kernel - status %d\n", stat); + } + } + if (attrs[KTF_A_LIST]) { + /* Parse list of test results */ + struct nlattr *nla; + int result = -1, line = 0; + nla_for_each_nested(nla, attrs[KTF_A_LIST], rem) { + switch (nla_type(nla)) { + case KTF_A_STAT: + /* Flush previous test, if any */ + handle_test(result,file,line,report); + result = nla_get_u32(nla); + /* Our own count and report since check does such a lousy + * job in counting individual checks */ + if (result) + assert_cnt += result; + else { + fail_cnt++; + assert_cnt++; + } + break; + case KTF_A_FILE: + file = nla_get_string(nla); + if (!file) + file = "no_file"; + break; + case KTF_A_NUM: + line = nla_get_u32(nla); + break; + case KTF_A_STR: + report = nla_get_string(nla); + if (!report) + report = "no_report"; + break; + default: + fprintf(stderr,"parse_result: Unexpected attribute type %d\n", nla_type(nla)); + return NL_SKIP; + } + } + /* Handle last test */ + handle_test(result,file,line,report); + } + + return NL_OK; +} + +static enum nl_cb_action parse_cov_endis(struct nl_msg *msg, struct nlattr** attrs) +{ + enum ktf_cmd_type type = (ktf_cmd_type)nla_get_u32(attrs[KTF_A_TYPE]); + const char *cmd = type == KTF_CT_COV_ENABLE ? "enable" : "disable"; + int retval = nla_get_u32(attrs[KTF_A_STAT]); + + if (retval) + fprintf(stderr, "Coverage %s operation failed with status %d\n", cmd, retval); + return NL_OK; +} + +static int parse_cb(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + int maxtype = KTF_A_MAX+10; + struct nlattr *attrs[maxtype]; + enum ktf_cmd_type type; + + // memset(attrs, 0, sizeof(attrs)); + + /* Validate message and parse attributes */ + int err = genlmsg_parse(nlh, 0, attrs, KTF_A_MAX, ktf_get_gnl_policy()); + if (err < 0) return err; + + if (!attrs[KTF_A_TYPE]) { + fprintf(stderr, "Received kernel response without a type\n"); + return NL_SKIP; + } + + type = (ktf_cmd_type)nla_get_u32(attrs[KTF_A_TYPE]); + switch (type) { + case KTF_CT_QUERY: + return parse_query(msg, attrs); + case KTF_CT_RUN: + return parse_result(msg, attrs); + case KTF_CT_COV_ENABLE: + case KTF_CT_COV_DISABLE: + return parse_cov_endis(msg, attrs); + default: + debug_cb(msg, attrs); + } + return NL_SKIP; +} + + +static int error_cb(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + fprintf(stderr, "Received invalid netlink message - type %d\n", nlh->nlmsg_type); + return NL_OK; +} + + +static int debug_cb(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + fprintf(stderr, "[Received netlink message of type %d]\n", nlh->nlmsg_type); + nl_msg_dump(msg, stderr); + return NL_OK; +} + +} // end namespace ktf diff --git a/tools/testing/selftests/ktf/lib/ktf_int.h b/tools/testing/selftests/ktf/lib/ktf_int.h new file mode 100644 index 0000000..1a06533 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_int.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_int.h: User mode side of extension to the gtest unit test framework: + * 1) Kernel test support via netlink + * 2) Standard command line parameters + * + * This file exposes some internals - for users of hybrid tests including + * ktf.h should be sufficient: + */ + +#ifndef KTF_INT_H +#define KTF_INT_H +#include +#include +#include "ktf.h" + +typedef std::vector stringvec; + +namespace ktf +{ + + /* A callback handler to be called for each assertion result */ + typedef void (*test_handler)(int result, const char* file, int line, const char* report); + + class KernelTest + { + public: + KernelTest(const std::string& setname, const char* testname, unsigned int handle_id); + ~KernelTest(); + void* get_priv(size_t priv_sz); + size_t get_priv_sz(KernelTest *kt); + std::string setname; + std::string testname; + unsigned int handle_id; + std::string name; + size_t setnum; /* This test belongs to this set in the kernel */ + size_t testnum; /* This test's index (test number) in the kernel */ + void* user_priv; /* Optional private data for the test */ + size_t user_priv_sz; /* Size of the user_priv data if used */ + test_cb* user_test; /* Optional user level wrapper function for the kernel test */ + char* file; + int line; + }; + + void *get_priv(KernelTest *kt, size_t priv_sz); + + // Set up connection to the kernel test driver: + // @handle_test contains the test framework's handling code for test assertions */ + bool setup(test_handler handle_test); + + void set_configurator(configurator c); + + // Parse command line args (call after gtest arg parsing) + char** parse_opts(int argc, char** argv); + + /* Query kernel for available tests in index order */ + stringvec& query_testsets(); + + stringvec get_testsets(); + std::string get_current_setname(); + stringvec get_test_names(); + + KernelTest* find_test(const std::string& setname, const std::string& testname, + std::string* ctx); + + /* "private" - only run from gtest framework */ + void run_test(KernelTest* test, std::string& ctx); +} // end namespace ktf + + +/* Redefine for C++ until we can get it patched - type mismatch by default */ +#ifdef nla_for_each_nested +#undef nla_for_each_nested +#endif +#define nla_for_each_nested(pos, nla, rem) \ + for (pos = (struct nlattr*)nla_data(nla), rem = nla_len(nla); \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +#endif From patchwork Tue Aug 13 06:09:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091197 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5BA5B13AC for ; Tue, 13 Aug 2019 06:12:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4AF6327FE4 for ; Tue, 13 Aug 2019 06:12:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3F1AC2845E; Tue, 13 Aug 2019 06:12:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A8F8C28438 for ; Tue, 13 Aug 2019 06:12:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727517AbfHMGMp (ORCPT ); Tue, 13 Aug 2019 02:12:45 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:37820 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727467AbfHMGL6 (ORCPT ); Tue, 13 Aug 2019 02:11:58 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68qNb021612; Tue, 13 Aug 2019 06:11:42 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=ejlUs24YGrV79jcN352Y8l7jHkjpvnNttU06LwDBubY=; b=ixpGsi+O3VJleSP15ikG8DnVPfw1bgBu5FUuB5N5uvhqhduEomHnyeaEBlIeogY6LPbg P6c9QmNrxCCQCfhm6QXI/Jtf3rI/HpEyrwd6Dotqv2oX4l37UJTEB21JsRzA0iPdaqHG lgqDnsszVVkBDktpxpPsGRd6AF3XTZWKbgOIjLECaYaQ/uJI3ralpC56QOiT1sxZX124 E9pUHQU5yNn4RSVLYU3sXNMBTdNputCLl6qL8dte6DAgJwVzRXiVwHDR31ZutudaCAaC ZwByunbLN1tEsaUjiTARCGzn7Wkjno0duR9wyVvLaqQd+i1Izu016iHv8MpaV9Op/Sma oQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=ejlUs24YGrV79jcN352Y8l7jHkjpvnNttU06LwDBubY=; b=oks5hyZuQI3+W8D3FxxKvELSS5HyiK1rP0bJnQgVdZ6wy1I2Aa4o+hC2BEYFzej7joV6 Pa7WfQXv73I3LfwRYN8JGmfr1vAZVeXqPw/gDMUyiBxZLvLLGbNkoOl9GCkxv6iIQ5ga HTjzgK13AKXLmrH/87HER4YJi1crnSBme7MFcXRxVwl3hRtlMvW1s5d892KWEfbxuSxW 6ZUa7rftxZRsU/bZasqQv30o5zVs+VX/+JGOUNBMWjo/PTkCAzRts3d0ARQcmH3FEGQy rNgARtAXp+yLf7LKUSa7OMCc6bjmssUAcQdhDRllo/Xt9nbE5A0TgvHvIVMSwFv8Fid6 UA== Received: from userp3020.oracle.com (userp3020.oracle.com [156.151.31.79]) by userp2120.oracle.com with ESMTP id 2u9pjqbvn9-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:42 +0000 Received: from pps.filterd (userp3020.oracle.com [127.0.0.1]) by userp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68PHb056292; Tue, 13 Aug 2019 06:11:42 GMT Received: from userv0122.oracle.com (userv0122.oracle.com [156.151.31.75]) by userp3020.oracle.com with ESMTP id 2u9n9hs2yk-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:42 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6BeC8026485; Tue, 13 Aug 2019 06:11:40 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:40 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 13/19] ktf: Integration logic for running ktf tests from googletest Date: Tue, 13 Aug 2019 08:09:28 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Currently ktf only supports integration with googletest on the user side, but there's nothing that prevents integration towards other user land frameworks for running and reporting, if so desired. Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/lib/ktf_run.cc | 177 ++++++++++++++++++- tools/testing/selftests/ktf/lib/ktf_unlproto.c | 21 ++- 2 files changed, 198 insertions(+) create mode 100644 tools/testing/selftests/ktf/lib/ktf_run.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_unlproto.c diff --git a/tools/testing/selftests/ktf/lib/ktf_run.cc b/tools/testing/selftests/ktf/lib/ktf_run.cc new file mode 100644 index 0000000..a26e04c --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_run.cc @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_run.cpp: + * Gtest integration of ktf kernel tests - + * e.g. tests that are fully implemented on the test driver side + * and only initiated via run_test below + */ + +#include "ktf_int.h" +#include +#include +#include "ktf_debug.h" + +namespace ktf +{ + +class KernelMetaFactory; + +class Kernel : public ::testing::TestWithParam +{ +public: + Kernel() + { + assert(false); // Should not be hit but is needed for template resolving + } + + Kernel(std::string& setname, std::string& testname) + { + log(KTF_INFO, "%s.%s\n", setname.c_str(), testname.c_str()); + + ukt = ktf::find_test(setname,testname,&ctx); + if (!ukt) { + fprintf(stderr, "**** Internal error: Could not find test %s.%s (set %s, name %s) ****\n", + setname.c_str(), testname.c_str(), setname.c_str(), testname.c_str()); + exit(7); + } + log(KTF_INFO, "### Kernel ctor %s (%ld,%ld)\n", ukt->name.c_str(), ukt->setnum, ukt->testnum); + } + + virtual ~Kernel() + { + log(KTF_INFO, "### Kernel dtor %s\n", ukt->name.c_str()); + + /* For some reason errno sometimes get set + * TBD: Figure out why - for now just reset it to avoid confusing the next test! + */ + if (errno) { + log(KTF_INFO, "### %s: errno was set to %d - resetting..\n", ukt->name.c_str(), errno); + errno = 0; + } + } + + virtual void TestBody(); +private: + ktf::KernelTest* ukt; + std::string ctx; + friend void setup(configurator c); + static int AddToRegistry(); + static configurator configurator_; +}; + + + +class TFactory : public ::testing::internal::ParameterizedTestFactory +{ +public: + TFactory(std::string s, ParamType parameter) + : ::testing::internal::ParameterizedTestFactory(parameter), + setname(s) + { + testname = parameter.c_str(); + } + + virtual ::testing::Test* CreateTest() + { + return new Kernel(setname,testname); + } + +private: + std::string setname; + std::string testname; +}; + + +class KernelMetaFactory : public ::testing::internal::TestMetaFactory +{ +public: + virtual ::testing::internal::TestFactoryBase* CreateTestFactory(ParamType parameter) { + TFactory* tf; + std::string setname = get_current_setname(); + tf = new TFactory(setname, parameter.c_str()); + return tf; + } +}; + +testing::internal::ParamGenerator gtest_query_tests(void); +std::string gtest_name_from_info(const testing::TestParamInfo&); +void gtest_handle_test(int result, const char* file, int line, const char* report); + +#ifndef INSTANTIATE_TEST_SUITE_P +/* This rename happens in Googletest commit 3a460a26b7. + * Make sure we compile both before and after it: + */ +#define AddTestSuiteInstantiation AddTestCaseInstantiation +#endif + +int Kernel::AddToRegistry() +{ + if (!ktf::setup(ktf::gtest_handle_test)) return 1; + + /* Run query against kernel to figure out which tests that exists: */ + stringvec& t = ktf::query_testsets(); + + ::testing::internal::ParameterizedTestCaseInfo* tci = + ::testing::UnitTest::GetInstance()->parameterized_test_registry() + .GetTestCasePatternHolder( "Kernel", ::testing::internal::CodeLocation("", 0)); + + for (stringvec::iterator it = t.begin(); it != t.end(); ++it) + { + ::testing::internal::TestMetaFactory* mf = new KernelMetaFactory(); + tci->AddTestPattern(it->c_str(), "", mf); + } + + tci->AddTestSuiteInstantiation("", >est_query_tests, >est_name_from_info, NULL, 0); + return 0; +} + +void setup(configurator c) +{ + ktf::set_configurator(c); + Kernel::AddToRegistry(); +} + + +void Kernel::TestBody() +{ + run_test(ukt, ctx); +} + + +void gtest_handle_test(int result, const char* file, int line, const char* report) +{ + if (result >= 0) { + const ::testing::AssertionResult gtest_ar = + !result ? (testing::AssertionFailure() << report) : testing::AssertionSuccess(); + + if (result) { + /* We might get multiple partial results from the kernel in one positive + * result report: + */ +#if HAVE_ASSERT_COUNT + ::testing::UnitTest::GetInstance()->increment_success_assert_count(result); +#else + GTEST_SUCCEED(); +#endif + } else { + ::testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure, + file, line, gtest_ar.failure_message()) = ::testing::Message(); + } + } +} + +testing::internal::ParamGenerator gtest_query_tests() +{ + return testing::ValuesIn(ktf::get_test_names()); +} + +std::string gtest_name_from_info(const testing::TestParamInfo& info) +{ + return info.param; +} + +} // end namespace ktf diff --git a/tools/testing/selftests/ktf/lib/ktf_unlproto.c b/tools/testing/selftests/ktf/lib/ktf_unlproto.c new file mode 100644 index 0000000..3929b03 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_unlproto.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * unlproto.c: This file is needed because the C struct init + * used in kernel/unlproto.h is not allowed in C++ + */ + +#include +#include +#include +#define NL_INTERNAL 1 +#include "kernel/ktf_unlproto.h" + + +struct nla_policy *ktf_get_gnl_policy(void) +{ + return ktf_gnl_policy; +} From patchwork Tue Aug 13 06:09:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091187 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0B199746 for ; Tue, 13 Aug 2019 06:12:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F1F7128438 for ; Tue, 13 Aug 2019 06:12:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E5B39284CE; Tue, 13 Aug 2019 06:12:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7F0082845E for ; Tue, 13 Aug 2019 06:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727790AbfHMGMF (ORCPT ); Tue, 13 Aug 2019 02:12:05 -0400 Received: from userp2130.oracle.com ([156.151.31.86]:60904 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727776AbfHMGME (ORCPT ); Tue, 13 Aug 2019 02:12:04 -0400 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68sFA010844; Tue, 13 Aug 2019 06:11:45 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=tfjxeCtOt/zjkP/k6+2p52MgrQfqToiEPXPQHRk3l/0=; b=SI1H4ybBHfNsYu/Iq7509UOI1VugUK3iV4C2jN5o9Uhac0JNJ5it4UX5QPSrxZf2oGwj RgoEUetbN1autivZAxCizMV0pdEiZIP1cs4cCamatDBjsbUNirmTF2NLi1kQZsfJJobe kkaHIt4nCocvIrRkxPgSCvWDCH6Owzy7rK+my4nThcBKzl+lTWRJ0Co/xM3G3FqpDK+7 AWyaCQWaDGQcZI0GPy8NitoNK5HE9n/UEQkv4RZfMTNf8tQDpfeEdGC6XF4QEKtl6Img gOe94eUzCaozXo25qnIpufC9qIGBjOMClQWqDDMLbmJvCnsqYZB48QT/FFhRRpGqcCFG Bg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=tfjxeCtOt/zjkP/k6+2p52MgrQfqToiEPXPQHRk3l/0=; b=hWzOqaGXKwpzIdSDdWIddVoqjiwHK+49GsjUFU8opMFYOQuSPlN+ZgJPKMDvR1M3gfg+ d2qRY4wIZVnjuCQqSII+S5LQozuTfzSNb2KGWogQbeWS4zxqoEiZ9rfOtfsdkmcyN9Xl V/cMLDe5JB4Uu3koOm/K3WJaWHPmnvx+c7/ESMgCzjvcVYyKlyO9Ue0xaNJLHiDOxsCW Irbhua8yDqmDBf+qCnix/kxI+UZnuklVMk1nZi+qTkFyzd/UrdEWNVaVEUKk7/wxruor ZHmvkm4wzvdBbv3pB1jbz9R7x20sT6lHImhDN9KjjiwEVCKqe+eZlFwmHHfQlCksWtkS Lg== Received: from aserp3020.oracle.com (aserp3020.oracle.com [141.146.126.70]) by userp2130.oracle.com with ESMTP id 2u9nbtc13t-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:45 +0000 Received: from pps.filterd (aserp3020.oracle.com [127.0.0.1]) by aserp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67Txc157148; Tue, 13 Aug 2019 06:11:44 GMT Received: from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236]) by aserp3020.oracle.com with ESMTP id 2u9nrenmd3-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:44 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6Bif7013702; Tue, 13 Aug 2019 06:11:44 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:43 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 14/19] ktf: Internal debugging facilities Date: Tue, 13 Aug 2019 08:09:29 +0200 Message-Id: <2e0a915bf97d241eced54efa540c9a11a1996c27.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Utilities for convenient and runtime enabled/disabled printk debugging mainly intended for debugging ktf itself and subtle early issues with execution/running of tests. ktf_debug.h: User mode debug function definitions Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/lib/ktf_debug.cc | 20 +++++++- tools/testing/selftests/ktf/lib/ktf_debug.h | 59 +++++++++++++++++++++- 2 files changed, 79 insertions(+) create mode 100644 tools/testing/selftests/ktf/lib/ktf_debug.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_debug.h diff --git a/tools/testing/selftests/ktf/lib/ktf_debug.cc b/tools/testing/selftests/ktf/lib/ktf_debug.cc new file mode 100644 index 0000000..18ff443 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_debug.cc @@ -0,0 +1,20 @@ +/* Copyright (c) 2012 Oracle Corporation. All rights reserved + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include "ktf_debug.h" +#include + +unsigned long ktf_debug_mask = 0; + + +void ktf_debug_init() +{ + ktf_debug_mask = 0; + char* dbg_mask_str = getenv("KTF_DEBUG_MASK"); + if (dbg_mask_str) { + ktf_debug_mask = strtol(dbg_mask_str, NULL, 0); + log(KTF_INFO_V, "debug mask set to 0x%lx\n", ktf_debug_mask); + } +} diff --git a/tools/testing/selftests/ktf/lib/ktf_debug.h b/tools/testing/selftests/ktf/lib/ktf_debug.h new file mode 100644 index 0000000..dc761a4 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_debug.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2012 Oracle Corporation. All rights reserved + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_debug.h: User mode debug function definitions + * - intended for test debugging. + * + * Enabled by setting bits in the environment variable KTF_DEBUG_MASK + */ + +#ifndef _KTF_DEBUG_H +#define _KTF_DEBUG_H +#include +#include +#include +#include +#include +#include + +extern unsigned long ktf_debug_mask; + + +#define KTF_ERR 0x1 +#define KTF_WARN 0x2 +#define KTF_INFO 0x4 +#define KTF_INFO_V 0x100 +#define KTF_MR 0x2000 +#define KTF_DEBUG 0x10000 +#define KTF_POLL 0x20000 +#define KTF_EVENT 0x40000 +#define KTF_DEBUG_V 0x1000000 +#define KTF_DUMP 0x2000000 + +/* Call this to initialize the debug logic from + * environment KTF_DEBUG_MASK + */ +void ktf_debug_init(); + +#define log(level, format, arg...) \ +do {\ + if (level & ktf_debug_mask) {\ + char _tm[30]; \ + time_t _tv = time(NULL);\ + ctime_r(&_tv,_tm);\ + _tm[24] = '\0';\ + fprintf(stderr, "%s [%ld] %s: " format, \ + _tm, (long unsigned int) pthread_self(), __func__, ## arg); \ + }\ +} while (0) + +#define logs(class, stmt_list) \ + do { \ + if (ktf_debug_mask & class) { \ + stmt_list; \ + } \ + } while (0) + +#endif From patchwork Tue Aug 13 06:09:30 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091171 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F3B5613AC for ; Tue, 13 Aug 2019 06:12:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E582C285C9 for ; Tue, 13 Aug 2019 06:12:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D9D34285F0; Tue, 13 Aug 2019 06:12:30 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0D0DF285C9 for ; Tue, 13 Aug 2019 06:12:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727503AbfHMGMM (ORCPT ); Tue, 13 Aug 2019 02:12:12 -0400 Received: from aserp2120.oracle.com ([141.146.126.78]:60114 "EHLO aserp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727485AbfHMGMM (ORCPT ); Tue, 13 Aug 2019 02:12:12 -0400 Received: from pps.filterd (aserp2120.oracle.com [127.0.0.1]) by aserp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68qHL039286; Tue, 13 Aug 2019 06:11:50 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=pIx9k7V10F3qq6zstYwjlNx3ARnd/lV+BoLCek/UQ8Y=; b=qU30W5Z9X04wZ/zVw94pIo1qqti7CZ3UDM5I98fqGb1n9n+cUfoyHUFCFWNO0u7JLzN6 iCXmAXGMgz7Nl22ud4CNWcl3dsaB55ntu9bLCPgNXv0F3PDJgWrcWfCshmO3IwPYdbMF j0aKiMKj/7311DVnVnyCUDFyvWQcStUGctyUf76eaU+vasQM4BVBXxrnXrf+BnuwbkEN AaYZQt2aVjzteBbUdGR7v5YtsEi9+Bm2zQQeDaSD+vz6+ZtLn4B28Zl4HabpIwI5Uhn8 M4VON0H+rDmuATneRFXI8DCUTgw/K67r/6fFzbBaPqabham/7jvHNBYqemj0tRuvYV6e VA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=pIx9k7V10F3qq6zstYwjlNx3ARnd/lV+BoLCek/UQ8Y=; b=Fd3rlzZ/3eBRkijPsQacD86/FW+A5HYeXchqDqVq7i1BtbHAo1knoUhH77UPKYel0eJq F0OxEExtqW4wO6kQA0khzl+ELqO1eLaaHCxwVDthXbnrfH/pSOzobvtblsSdy+whFHYW 8FZWiKdTgvubYTQaJv9sGGcZjm8+akQdkY3AyNSV8caQL2bTZcc0uS+9zF1veHXcsyOA b9H/c5geQWNt487Y/aawpHT/EHEecf8X8B+OjOShL57AP1PcRoRjHpoiP1Yo+Q+ertAw vMsoGZgUMwqX1Bmap7H1tzHjRw+owzodWBSdyGNTIrfm7kWYEzekJsWAoejB4ypf0MZ6 Qw== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by aserp2120.oracle.com with ESMTP id 2u9nvp41n6-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:50 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D67Qx2096547; Tue, 13 Aug 2019 06:11:50 GMT Received: from userv0122.oracle.com (userv0122.oracle.com [156.151.31.75]) by aserp3030.oracle.com with ESMTP id 2u9m0ayefx-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:49 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6Bm70026524; Tue, 13 Aug 2019 06:11:48 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:47 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 15/19] ktf: Some simple examples Date: Tue, 13 Aug 2019 08:09:30 +0200 Message-Id: <930ceefe7f08e47c66cc43404d6e67b310cbec60.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP A few simple examples, and example of other test modules to make it easier to get started with ktf. Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/examples/Makefile | 17 ++++- tools/testing/selftests/ktf/examples/h2.c | 45 +++++++++++- tools/testing/selftests/ktf/examples/h3.c | 84 ++++++++++++++++++++- tools/testing/selftests/ktf/examples/h4.c | 62 +++++++++++++++- tools/testing/selftests/ktf/examples/hello.c | 38 +++++++++- tools/testing/selftests/ktf/examples/kgdemo.c | 61 +++++++++++++++- 6 files changed, 307 insertions(+) create mode 100644 tools/testing/selftests/ktf/examples/Makefile create mode 100644 tools/testing/selftests/ktf/examples/h2.c create mode 100644 tools/testing/selftests/ktf/examples/h3.c create mode 100644 tools/testing/selftests/ktf/examples/h4.c create mode 100644 tools/testing/selftests/ktf/examples/hello.c create mode 100644 tools/testing/selftests/ktf/examples/kgdemo.c diff --git a/tools/testing/selftests/ktf/examples/Makefile b/tools/testing/selftests/ktf/examples/Makefile new file mode 100644 index 0000000..f3cfcc9 --- /dev/null +++ b/tools/testing/selftests/ktf/examples/Makefile @@ -0,0 +1,17 @@ +# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 +# +# Kernel module implementing a few simple examples of KTF tests +# + +include $(srctree)/$(src)/../scripts/ktf_syms.mk + +ccflags-y += -I$(srctree)/$(src)/../kernel -I$(src) + +obj-m := hello.o h2.o h3.o h4.o + +ifdef CONFIG_KGDB +obj-m += kgdemo.o +endif + diff --git a/tools/testing/selftests/ktf/examples/h2.c b/tools/testing/selftests/ktf/examples/h2.c new file mode 100644 index 0000000..37a6fbb --- /dev/null +++ b/tools/testing/selftests/ktf/examples/h2.c @@ -0,0 +1,45 @@ +#include +#include "ktf.h" + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +#define MAX_CNT 3 + +struct hello_ctx { + struct ktf_context k; + int value[MAX_CNT]; +}; + +static struct hello_ctx myctx = { .value = { 0, 1, 4 } }; + +TEST(examples, cmp) +{ + struct hello_ctx *hctx = KTF_CONTEXT_GET("value", struct hello_ctx); + + EXPECT_INT_EQ(_i, hctx->value[_i]); +} + +static void add_tests(void) +{ + ADD_LOOP_TEST(cmp, 0, MAX_CNT); +} + +static int __init hello_init(void) +{ + KTF_CONTEXT_ADD(&myctx.k, "value"); + add_tests(); + return 0; +} + +static void __exit hello_exit(void) +{ + struct ktf_context *kctx = KTF_CONTEXT_FIND("value"); + + KTF_CONTEXT_REMOVE(kctx); + KTF_CLEANUP(); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/tools/testing/selftests/ktf/examples/h3.c b/tools/testing/selftests/ktf/examples/h3.c new file mode 100644 index 0000000..a6aca98 --- /dev/null +++ b/tools/testing/selftests/ktf/examples/h3.c @@ -0,0 +1,84 @@ +#include +#include "ktf.h" + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +DECLARE_F(hello_fixture) + struct list_head head; +}; + +struct my_element { + struct list_head list; + int value; +}; + +SETUP_F(hello_fixture, hello_setup) +{ + int i; + + INIT_LIST_HEAD(&hello_fixture->head); + for (i = 0; i < 10; i++) { + struct my_element *e = kzalloc(sizeof(*e), GFP_KERNEL); + + e->value = i; + list_add_tail(&e->list, &hello_fixture->head); + } + hello_fixture->ok = true; +} + +TEARDOWN_F(hello_fixture, hello_teardown) +{ + struct list_head *p, *next_p; + + /* Just cleanup whatever is left after the test */ + list_for_each_safe(p, next_p, &hello_fixture->head) { + struct my_element *e = list_entry(p, struct my_element, list); + + list_del(&e->list); + kfree(e); + } + EXPECT_TRUE(list_empty(&hello_fixture->head)); +} + +INIT_F(hello_fixture, hello_setup, hello_teardown); + +TEST_F(hello_fixture, examples, hello_del) +{ + int cnt = 0; + int cnt_ones = 0; + struct my_element *e = kzalloc(sizeof(*e), GFP_KERNEL); + + e->value = 1; + list_add(&e->list, &ctx->head); + + list_for_each_entry(e, &ctx->head, list) { + if (e->value == 1) + cnt_ones++; + cnt++; + } + EXPECT_INT_EQ(11, cnt); + EXPECT_INT_EQ(2, cnt_ones); +} + +static void add_tests(void) +{ + ADD_TEST(hello_del); +} + +static int __init hello_init(void) +{ + add_tests(); + tlog(T_INFO, "hello: loaded"); + return 0; +} + +static void __exit hello_exit(void) +{ + KTF_CLEANUP(); + tlog(T_INFO, "hello: unloaded"); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/tools/testing/selftests/ktf/examples/h4.c b/tools/testing/selftests/ktf/examples/h4.c new file mode 100644 index 0000000..3e85fef --- /dev/null +++ b/tools/testing/selftests/ktf/examples/h4.c @@ -0,0 +1,62 @@ +#include +#include "ktf.h" + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +static int count; +static int ret; + +KTF_ENTRY_PROBE(printk, printkhandler) +{ + count++; + + KTF_ENTRY_PROBE_RETURN(0); +} + +TEST(examples, entrycheck) +{ + count = 0; + ASSERT_INT_EQ_GOTO(KTF_REGISTER_ENTRY_PROBE(printk, printkhandler), + 0, done); + printk(KERN_INFO "Testing kprobe entry..."); + ASSERT_INT_GT_GOTO(count, 0, done); +done: + KTF_UNREGISTER_ENTRY_PROBE(printk, printkhandler); +} + +KTF_RETURN_PROBE(printk, printkrethandler) +{ + ret = KTF_RETURN_VALUE(); + + return 0; +} + +TEST(examples, returncheck) +{ + char *teststr = "Testing kprobe return..."; + + ret = -1; + ASSERT_INT_EQ_GOTO(KTF_REGISTER_RETURN_PROBE(printk, printkrethandler), + 0, done); + printk(KERN_INFO "%s", teststr); + ASSERT_INT_EQ_GOTO(ret, strlen(teststr), done); +done: + KTF_UNREGISTER_RETURN_PROBE(printk, printkrethandler); +} + +static int __init hello_init(void) +{ + ADD_TEST(entrycheck); + ADD_TEST(returncheck); + return 0; +} + +static void __exit hello_exit(void) +{ + KTF_CLEANUP(); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/tools/testing/selftests/ktf/examples/hello.c b/tools/testing/selftests/ktf/examples/hello.c new file mode 100644 index 0000000..9c4713f --- /dev/null +++ b/tools/testing/selftests/ktf/examples/hello.c @@ -0,0 +1,38 @@ +#include +#include "ktf.h" + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +TEST(examples, hello_ok) +{ + EXPECT_TRUE(true); +} + +TEST(examples, hello_fail) +{ + EXPECT_TRUE(false); +} + +static void add_tests(void) +{ + ADD_TEST(hello_ok); + ADD_TEST(hello_fail); +} + +static int __init hello_init(void) +{ + add_tests(); + tlog(T_INFO, "hello: loaded"); + return 0; +} + +static void __exit hello_exit(void) +{ + KTF_CLEANUP(); + tlog(T_INFO, "hello: unloaded"); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/tools/testing/selftests/ktf/examples/kgdemo.c b/tools/testing/selftests/ktf/examples/kgdemo.c new file mode 100644 index 0000000..9ce19ff --- /dev/null +++ b/tools/testing/selftests/ktf/examples/kgdemo.c @@ -0,0 +1,61 @@ +#include +#include "ktf.h" + +/* + * A trivial and somewhat rough example used by the author + * for pedagogical purposes, to demonstrate + * interactive debugging with kgdb. + * + * Requires a kernel built with CONFIG_KGDB + * + * Note: these test breaks into kgdb and/or creates a NULL + * pointer exception and corresponding stack dump, so + * try out in a test environment only! + */ + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +#define MAX_CNT 3 +#include + +static int kgdemo_cnt; +static int *bogus_ref; + +TEST(kgdb, breakpoint) +{ + kgdemo_cnt = 0; + printk(KERN_INFO "** Please set kgdemo_cnt = 1 **\n"); + kgdb_breakpoint(); + EXPECT_INT_EQ(1, kgdemo_cnt); +} + +TEST(kgdb, nullpointer) +{ + int pre = kgdemo_cnt; + + int b = *bogus_ref++; + + EXPECT_INT_EQ(pre + 1, b); +} + +static void add_tests(void) +{ + ADD_TEST(breakpoint); + ADD_TEST(nullpointer); +} + +static int __init kgdemo_init(void) +{ + add_tests(); + return 0; +} + +static void __exit kgdemo_exit(void) +{ + KTF_CLEANUP(); +} + +module_init(kgdemo_init); +module_exit(kgdemo_exit); From patchwork Tue Aug 13 06:09:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091179 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 152DD14D5 for ; Tue, 13 Aug 2019 06:12:32 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 06DC327FE4 for ; Tue, 13 Aug 2019 06:12:32 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EE64F28438; Tue, 13 Aug 2019 06:12:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 00CEE28587 for ; Tue, 13 Aug 2019 06:12:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727829AbfHMGMP (ORCPT ); Tue, 13 Aug 2019 02:12:15 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:38520 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727485AbfHMGMO (ORCPT ); Tue, 13 Aug 2019 02:12:14 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68uMj021972; Tue, 13 Aug 2019 06:11:54 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=gq6T668rkWls/bxZrbwyu380I/xpVBippRU1jyjSOOg=; b=d/573iIpCX9yPFLlxk1CgqdR4610RRj0mAF5cWpbaB0bIrd4se+eZexwhA1mCf66xYEO CsPxmkJeMxBzS3SmeEZGsWPbtkzeobJU+CBLiP72OPRgEoZRx9lUmDh63M5eJSZwE5jT B2yddnuNpLgBZ60qwUqmfbOedRcJVd81zKD4esydlZunooXGtAXfynMASGJMYwtZ+dps J5gyh2NlIF8ReoaQCiGuzEWw+AQDS3TaZT5AUJSiNewgbnkDjdV6ja8+qdLTxtcuh1sj yYHR8nqRaqCw73FNli6D0ww3rCaD/xXCtUsiMMHcwASBclq2mr5zDPXQzlqtnpJOhIQD 8g== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=gq6T668rkWls/bxZrbwyu380I/xpVBippRU1jyjSOOg=; b=RenkVvOiBOTkk1/wEhNe2wlXEWi74QJwkZzEHHDf388OZhzhGP33qV2AbLfjbW48hhzL u+v+hCJLPYVB7hCiKKTnn0To9BxG7nlAeKp4V/6r8xttNDWTP6XpAxGSUnLd8XX5+BR6 +GqmBFcyX4Z5Kszt6G1woLwp+9ENHtJFcL7WmXX3BFyl2ycELdLdvtLLMlNCHmLh4kSg hXzLriprsvUv0PlYGq2sTiHH5aotkfM3+EwGiOr4H+fd2jC/DKBQyiEwEn4uBKKsxkkl wDmWrk7YAiEJVjXgXMwgbeaW0bO02Huacz7YqN0riVSPsXQ+uAieoaRj2DRkXxrL3/pW Qw== Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by userp2120.oracle.com with ESMTP id 2u9pjqbvpd-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:54 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68YNc049789; Tue, 13 Aug 2019 06:11:54 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by userp3030.oracle.com with ESMTP id 2u9k1w41vc-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:54 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6BqwY011407; Tue, 13 Aug 2019 06:11:52 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:51 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 16/19] ktf: Some user applications to run tests Date: Tue, 13 Aug 2019 08:09:31 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Some minimal user land executables to run tests and to enable/disable coverage: ktfrun: Simple generic test runner for generic ktf tests ktftest: A test runner for the ktf selftests. Contains code to configure specific selftest context objects. ktfcov: A utility to selectively enable coverage support for a kernel module. Coverage support can also be enabled in code by tests, if desired. hybrun: A test that implements a hybrid test runner. hybrid.cc: User mode part of the hybrid_self ktfrun.cc: Generic user level application to run kernel tests Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/user/Makefile | 26 ++++++++- tools/testing/selftests/ktf/user/hybrid.cc | 39 +++++++++++++- tools/testing/selftests/ktf/user/ktfcov.cc | 68 ++++++++++++++++++++++- tools/testing/selftests/ktf/user/ktfrun.cc | 20 ++++++- tools/testing/selftests/ktf/user/ktftest.cc | 46 +++++++++++++++- 5 files changed, 199 insertions(+) create mode 100644 tools/testing/selftests/ktf/user/Makefile create mode 100644 tools/testing/selftests/ktf/user/hybrid.cc create mode 100644 tools/testing/selftests/ktf/user/ktfcov.cc create mode 100644 tools/testing/selftests/ktf/user/ktfrun.cc create mode 100644 tools/testing/selftests/ktf/user/ktftest.cc diff --git a/tools/testing/selftests/ktf/user/Makefile b/tools/testing/selftests/ktf/user/Makefile new file mode 100644 index 0000000..04c8e7e --- /dev/null +++ b/tools/testing/selftests/ktf/user/Makefile @@ -0,0 +1,26 @@ + +GTEST_CFLAGS ?= -I$(GTEST_PATH)/include -DGTEST_HAS_PTHREAD=1 -lpthread +GTEST_LIBS ?= -L$(GTEST_PATH)/lib64 -lgtest -lpthread +NETLINK_CFLAGS ?= $(shell pkgconf --cflags libnl-genl-3.0) +HOST_EXTRACFLAGS = -I$(srctree)/$(src)/../lib $(NETLINK_CFLAGS) $(GTEST_CFLAGS) \ + -Wall -Werror \ + -Wno-packed-bitfield-compat -D_GNU_SOURCE + +HOST_EXTRACXXFLAGS = -I$(srctree)/$(src)/../lib $(NETLINK_CFLAGS) $(GTEST_CFLAGS) \ + -Wall \ + -Wno-packed-bitfield-compat \ + -Wno-pointer-arith -Werror \ + -D__FILENAME__=\"`basename $<`\" +NETLINK_LIBS ?= $(shell pkgconf --libs libnl-genl-3.0) +KBUILD_HOSTLDLIBS = -L$(obj)/../lib -lktf $(NETLINK_LIBS) $(GTEST_LIBS) + +hostprogs-y := ktfrun ktfcov ktftest + +__build: $(addprefix $(obj)/,$(hostprogs-y)) + +## Simple kernel test runner sample program: +ktfrun-cxxobjs = ktfrun.o +ktfcov-cxxobjs = ktfcov.o + +## Configure and run the KTF selftests: +ktftest-cxxobjs = ktftest.o hybrid.o diff --git a/tools/testing/selftests/ktf/user/hybrid.cc b/tools/testing/selftests/ktf/user/hybrid.cc new file mode 100644 index 0000000..6aa5ad2 --- /dev/null +++ b/tools/testing/selftests/ktf/user/hybrid.cc @@ -0,0 +1,39 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * hybrid.cpp: User mode part of the hybrid_self + * test in selftests + */ + +#include "ktf.h" +#include + +extern "C" { +#include "../selftest/hybrid_self.h" +} + +/* User side of a simple hybrid test that just sends an out-of-band message + * to the kernel side - the kernel implementation picks it up and verifies + * that it is the expected string and integer values. + * + * This form of test allows the mixing of normal gtest user land assertions + * with one or more calls to the kernel side to run tests there: + */ + +HTEST(selftest, msg) +{ + KTF_USERDATA(self, hybrid_self_params, data); + + strcpy(data->text_val, HYBRID_MSG); + data->val = HYBRID_MSG_VAL; + + /* assertions can be specified here: */ + EXPECT_TRUE(true); + + ktf::run(self); + + /* and here.. */ + EXPECT_TRUE(true); +} diff --git a/tools/testing/selftests/ktf/user/ktfcov.cc b/tools/testing/selftests/ktf/user/ktfcov.cc new file mode 100644 index 0000000..d5a9ef4 --- /dev/null +++ b/tools/testing/selftests/ktf/user/ktfcov.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktfcov.cpp: + * User level application to enable/disable coverage of kernel modules. + */ +#include +#include +#include +#include +#include "ktf.h" +#include "../kernel/ktf_unlproto.h" + +using namespace std; + +void +usage(char *progname) +{ + cerr << "Usage: " << progname << " [-e module[-m]] [-d module]\n"; +} + +int main (int argc, char** argv) +{ + int opt, nopts = 0; + unsigned int cov_opts = 0; + std::string modname = std::string(); + bool enable = false; + + ktf::setup(); + testing::InitGoogleTest(&argc,argv); + + if (argc < 3) { + usage(argv[0]); + return -1; + } + + while ((opt = getopt(argc, argv, "e:d:m")) != -1) { + switch (opt) { + case 'e': + nopts++; + enable = true; + modname = optarg; + break; + case 'd': + nopts++; + enable = false; + modname = optarg; + break; + case 'm': + cov_opts |= KTF_COV_OPT_MEM; + break; + default: + cerr << "Unknown option '" << char(optopt) << "'"; + return -1; + } + } + /* Either enable or disable must be specified, and -m is only valid + * for enable. + */ + if (modname.size() == 0 || nopts != 1 || (cov_opts && !enable)) { + usage(argv[0]); + return -1; + } + return ktf::set_coverage(modname, cov_opts, enable); +} diff --git a/tools/testing/selftests/ktf/user/ktfrun.cc b/tools/testing/selftests/ktf/user/ktfrun.cc new file mode 100644 index 0000000..9229b21 --- /dev/null +++ b/tools/testing/selftests/ktf/user/ktfrun.cc @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktfrun.cpp: Generic user level application to run kernel tests + * provided by modules subscribing to ktf services. + */ +#include +#include +#include + +int main (int argc, char** argv) +{ + ktf::setup(); + testing::InitGoogleTest(&argc,argv); + + return RUN_ALL_TESTS(); +} diff --git a/tools/testing/selftests/ktf/user/ktftest.cc b/tools/testing/selftests/ktf/user/ktftest.cc new file mode 100644 index 0000000..fda625d --- /dev/null +++ b/tools/testing/selftests/ktf/user/ktftest.cc @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktfrun.cpp: Generic user level application to run kernel tests + * provided by modules subscribing to ktf services. + */ +#include +#include +#include + +extern "C" { +#include "../selftest/context_self.h" +} + +void selftest_configure() +{ + struct test_parameter_block p; + memset(&p, 0, sizeof(p)); + strcpy(p.s, CONTEXT_MSG); + + /* First configure two contexts provided by the kernel part: */ + p.magic = CONTEXT_MAGIC1; + KTF_CONTEXT_CFG("context1", "context_type_1", test_parameter_block, &p); + p.magic = CONTEXT_MAGIC2; + KTF_CONTEXT_CFG("context2", "context_type_2", test_parameter_block, &p); + + /* Configure a 3rd, dynamically created context, using CONTEXT3_TYPE_ID + * which the kernel part has enabled for dynamic creation of contexts + * from user space (see kernel/context.c: add_context_tests() + * for details of setup) + */ + p.magic = CONTEXT_MAGIC3; + KTF_CONTEXT_CFG("context3", "context_type_3", test_parameter_block, &p); +} + + +int main (int argc, char** argv) +{ + ktf::setup(selftest_configure); + testing::InitGoogleTest(&argc,argv); + + return RUN_ALL_TESTS(); +} From patchwork Tue Aug 13 06:09:32 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091175 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A276E184E for ; Tue, 13 Aug 2019 06:12:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9473128438 for ; Tue, 13 Aug 2019 06:12:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 88850285C9; Tue, 13 Aug 2019 06:12:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BDD412845E for ; Tue, 13 Aug 2019 06:12:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727504AbfHMGMT (ORCPT ); Tue, 13 Aug 2019 02:12:19 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:38666 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727485AbfHMGMS (ORCPT ); Tue, 13 Aug 2019 02:12:18 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68wde022017; Tue, 13 Aug 2019 06:11:58 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=1nCZBmer4tuZfGj4X7/H4PALF0jE4YsRRquvWXv0zVg=; b=YmoF9KiaMHCxdHUaXog9rNzz2szEp6tFHb5QH0erJtblSLpiIUy3J9IPMO0ufXroDwE+ Ugz8ZuoY8NtfLK87dkL/9Jt/oprgtqDmlQFw00Ofe5UNll7UeUm22neDpUWAEApRLhw3 /Ya2KaIYdaQ5MB7pV6ELykxJ4crsaAWPJ9k2Mp1F3kNGB0hjU9DEo5FUQ2qqK528dAh9 JZoAlTp3aFAfEh465bN44rXVvM/5m17uxw5kZnSEBlXZqsYOHntaiCKZO7oOJ0kwKqHJ 1CPNFVt13YWDZ6a6bK7A7rCLPP7gRwOQfKDk5eX1G9ekdOltQDdROcAsvv26nqbNsBPa 6w== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=1nCZBmer4tuZfGj4X7/H4PALF0jE4YsRRquvWXv0zVg=; b=fzC+CrPQTX3tB3BBPPNvKWnaLblL0DVRoP4FjOSw8ZRuv7AXsAy5dxah3qnCSzd/R5Mf jnxRT0eWZwvuO3h1IZVCSr89hePEeC8P1PoY4kmuwSiEhCbIyQeNzUtMbinDjmjThmlR DAkmFzOqX++HQMTbgGsCebW145+QkrTBvBcvZ+bNO95sMhmS06X4E7bnYDwiEBv6q+FJ o0MOcAneMv9dACGKc95zAt5WWxLPoNID5ocSQv3ozvOz+8p0jFCKIsCv7a5QMMtx8hJc MXaJTYsuXrtUcLaxEImIL3lAAeegxfjuo5Z+maSzwWqzU81RkTuUPfCPPChbG4oFM7fp HQ== Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by userp2120.oracle.com with ESMTP id 2u9pjqbvpv-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:58 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68ZEh049809; Tue, 13 Aug 2019 06:11:57 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by userp3030.oracle.com with ESMTP id 2u9k1w41xf-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:11:57 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6Bu10011420; Tue, 13 Aug 2019 06:11:56 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:55 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 17/19] ktf: Toplevel ktf Makefile/makefile includes and scripts to run from kselftest Date: Tue, 13 Aug 2019 08:09:32 +0200 Message-Id: <79b7a9ac73c098d3d6ed7a4dde6952b655232b1c.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Knut Omang --- tools/testing/selftests/ktf/Makefile | 21 ++++- tools/testing/selftests/ktf/scripts/ktf_syms.mk | 16 +++- tools/testing/selftests/ktf/scripts/runtests.mk | 3 +- tools/testing/selftests/ktf/scripts/runtests.sh | 100 +++++++++++++++++- tools/testing/selftests/ktf/scripts/top_make.mk | 14 ++- 5 files changed, 154 insertions(+) create mode 100644 tools/testing/selftests/ktf/Makefile create mode 100644 tools/testing/selftests/ktf/scripts/ktf_syms.mk create mode 100644 tools/testing/selftests/ktf/scripts/runtests.mk create mode 100755 tools/testing/selftests/ktf/scripts/runtests.sh create mode 100644 tools/testing/selftests/ktf/scripts/top_make.mk diff --git a/tools/testing/selftests/ktf/Makefile b/tools/testing/selftests/ktf/Makefile new file mode 100644 index 0000000..0fef39c --- /dev/null +++ b/tools/testing/selftests/ktf/Makefile @@ -0,0 +1,21 @@ + +ifneq ($(TARGETS),) +# We end up here if called from selftests/Makefile +# Invoke our "module target" to get everything built +all: + $(Q)$(MAKE) -C $(abs_objtree) M=tools/testing/selftests/ktf + +clean: + $(Q)$(MAKE) -C $(abs_objtree) M=tools/testing/selftests/ktf clean + +run_tests: + @echo "running tests" + $(MAKE) BUILD=$(abs_objtree)/tools/testing/selftests -f scripts/runtests.mk $@ + +endif +obj-m += kernel/ +obj-m += selftest/ +obj-m += examples/ +obj-m += lib/ +obj-m += user/ + diff --git a/tools/testing/selftests/ktf/scripts/ktf_syms.mk b/tools/testing/selftests/ktf/scripts/ktf_syms.mk new file mode 100644 index 0000000..a332223 --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/ktf_syms.mk @@ -0,0 +1,16 @@ +ktf_symfile=$(shell (cd $(srctree)/$(src) && ls ktf_syms.txt 2> /dev/null || true)) +ktf_syms = $(ktf_symfile:%.txt=%.h) + +ifneq ($(ktf_symfile),) + +$(obj)/self.o: $(obj)/$(ktf_syms) + +ktf_scripts = $(srctree)/$(src)/../scripts + +$(obj)/$(ktf_syms): $(srctree)/$(src)/ktf_syms.txt $(ktf_scripts)/resolve + @echo " KTFSYMS $@" + $(Q)$(ktf_scripts)/resolve $(ccflags-y) $< $@ + +clean-files += $(ktf_syms) + +endif diff --git a/tools/testing/selftests/ktf/scripts/runtests.mk b/tools/testing/selftests/ktf/scripts/runtests.mk new file mode 100644 index 0000000..7fd3651 --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/runtests.mk @@ -0,0 +1,3 @@ +TEST_PROGS := scripts/runtests.sh + +include ../lib.mk diff --git a/tools/testing/selftests/ktf/scripts/runtests.sh b/tools/testing/selftests/ktf/scripts/runtests.sh new file mode 100755 index 0000000..3396aec --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/runtests.sh @@ -0,0 +1,100 @@ +#!/bin/bash -e + +verbose=1 + +# Convenience function to return a string that +# is a reverse list of the incoming arguments: +# +reverse() +{ + args=($*) + for (( i=((${#args[*]} - 1)); i >= 0; i-- )); do + echo ${args[$i]} + done +} + +# Set paths to a particular module - if no path is set to a module, use modprobe: +# +declare -A a_mpath +mpath() +{ + local module="$1" + local mpath="$2" + [[ $mpath != "" ]] || fail "Usage: mpath module path" + + a_mpath[$module]="$BUILD/$mpath" +} + +# Set parameters to load a given module with for test purposes: +declare -A a_params +params() +{ + local module="$1" + shift + a_params[$module]="$*" +} + +log() +{ + (( $verbose )) && echo $* +} + +mod_probe() +{ + local fm="" + local name="$1" + shift + + mp=${a_mpath[$name]} + if [[ $mp != "" ]]; then + fm="$mp" + fi + + is_loaded=$(lsmod | egrep "^$name" || true) + if [[ $is_loaded != "" ]]; then + echo "Module \"$name\" is already loaded!" 1>&2 + return 0 + fi + + if [[ $fm == "" ]]; then + log "Modprobing $name" + $sudo modprobe $name ${a_params[$name]} + else + fm=${a_mpath[$name]} + log "Insmod'ing module \"$name\"" 1>&2 + $sudo insmod $fm ${a_params[$name]} + fi +} + +# If/when more modules are to be loaded, this could go in a config file +# but for the purpose of this example, just do it inline: +# +mpath ktf ktf/kernel/ktf.ko +mpath selftest ktf/selftest/selftest.ko + +load_modules="ktf selftest" + +unload_modules=$(reverse $load_modules) + +sudo="" +if [[ $USER != "root" ]]; then + sudo="sudo" +fi + +for m in $load_modules; do + mod_probe $m +done + +if [[ $GTEST_PATH == "" ]];then + echo "Set environment variable GTEST_PATH to point to your googletest build!" + exit 1 +fi + +export LD_LIBRARY_PATH="$BUILD/ktf/lib:$GTEST_PATH/lib64:$GTEST_PATH/lib" +$BUILD/ktf/user/ktftest || stat=$? + +for m in $unload_modules; do + $sudo rmmod $m +done + +exit $stat diff --git a/tools/testing/selftests/ktf/scripts/top_make.mk b/tools/testing/selftests/ktf/scripts/top_make.mk new file mode 100644 index 0000000..978068e --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/top_make.mk @@ -0,0 +1,14 @@ +ifneq ($(TARGETS),) +# We end up here if called from selftests/Makefile +# Invoke our "module target" to get everything built +all: + $(Q)$(MAKE) -C $(abs_objtree) M=tools/testing/selftests/ktf + +clean: + $(Q)$(MAKE) -C $(abs_objtree) M=tools/testing/selftests/ktf clean + +run_tests: + @echo "running tests" + $(MAKE) BUILD=$(abs_objtree)/tools/testing/selftests -f scripts/runtests.mk $@ + +endif From patchwork Tue Aug 13 06:09:33 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091167 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2522814D5 for ; Tue, 13 Aug 2019 06:12:27 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 17B0628438 for ; Tue, 13 Aug 2019 06:12:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0BDBF284CE; Tue, 13 Aug 2019 06:12:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BE3BA28438 for ; Tue, 13 Aug 2019 06:12:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727536AbfHMGMW (ORCPT ); Tue, 13 Aug 2019 02:12:22 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:38732 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727851AbfHMGMV (ORCPT ); Tue, 13 Aug 2019 02:12:21 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68vGS021987; Tue, 13 Aug 2019 06:12:01 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=5u1br6HB1mVIimmoZSC7Rcb1APqqzcKZvGBUFCAXVf4=; b=P4XUI9NPV9lQHJ4wbIl9xU/oLteKT4/FqEwHp+AbliPdRpeIdsloCzrsUXBKUNlQGK6g u3Va5OoC3lqUHGsSu+r83MylOeRvckT1Nyd/cR2NsW25sPPQZhABvnLEkIj+3TBIbwat VG5HqOSxz24TxbdiScqpa4YL5rLHr+4EyaIwk0usAHBFaRM2NPUuA3QA291ECAy0MASf 5tWutp5m9mzk41l2vmviTSDqT7MhPm97JhpFyQkH4ylOdyNyxMOYVUHDxZNcVPgMGlo9 Zv8oCm6GbQg0380ad6cLFSFy5xpr7+7HKwSs2z2fjj6iuZhB7adosElzybhz5ivFahyk 1A== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=5u1br6HB1mVIimmoZSC7Rcb1APqqzcKZvGBUFCAXVf4=; b=aFXMkuMbsnQjsGP1NOA8p4MGFZKkZvvH3/oU/0cxi0yfw81DJjQmmxmOHK6HEb1Lsr2k MnhkW+c5AmeJzm+0JSjxUsUCgVyuW7JShUHCbRC8vNVirJw1Nj0vep1zUMG2VtKeGGOa 6IYaFXAW67BkjfMNDUUxAzNJFgGqzjkpLRo5zqSWkLcWjp1uYg3fGrvG2vM1Abpn8cxh hvK9MezyccvmSuSXg77cCHIlVSgGxYk7IJqxgLd9z05TfwxrXPPGNhq9BF62HzB8dHSJ U3EDH/ry4ADDkCj21kfIdNputEU/xCy9X+DET+ic4lg1Q/Vx/OoeolM9C3jlo3akFwsT Wg== Received: from userp3020.oracle.com (userp3020.oracle.com [156.151.31.79]) by userp2120.oracle.com with ESMTP id 2u9pjqbvr2-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:12:01 +0000 Received: from pps.filterd (userp3020.oracle.com [127.0.0.1]) by userp3020.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68PTP056321; Tue, 13 Aug 2019 06:12:01 GMT Received: from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236]) by userp3020.oracle.com with ESMTP id 2u9n9hs3av-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:12:01 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0122.oracle.com (8.14.4/8.14.4) with ESMTP id x7D6BxiM013778; Tue, 13 Aug 2019 06:12:00 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:11:59 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 18/19] kselftests: Enable building ktf Date: Tue, 13 Aug 2019 08:09:33 +0200 Message-Id: <734d00a38cb6eecc3bdae4d131cccf615bb6dd13.1565676440.git-series.knut.omang@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Knut Omang --- tools/testing/selftests/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 9781ca7..a24c2fe 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -20,6 +20,7 @@ TARGETS += ipc TARGETS += ir TARGETS += kcmp TARGETS += kexec +TARGETS += ktf TARGETS += kvm TARGETS += lib TARGETS += livepatch From patchwork Tue Aug 13 06:09:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Knut Omang X-Patchwork-Id: 11091181 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2F7DF746 for ; Tue, 13 Aug 2019 06:12:34 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2138927FE4 for ; Tue, 13 Aug 2019 06:12:34 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 14D562845E; Tue, 13 Aug 2019 06:12:34 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B3CDE27FE4 for ; Tue, 13 Aug 2019 06:12:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727893AbfHMGMa (ORCPT ); Tue, 13 Aug 2019 02:12:30 -0400 Received: from userp2130.oracle.com ([156.151.31.86]:33392 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727535AbfHMGM3 (ORCPT ); Tue, 13 Aug 2019 02:12:29 -0400 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68sM0010867; Tue, 13 Aug 2019 06:12:05 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2019-08-05; bh=XQCN31heGj0fx7iiZojr/ruGjvl/p/j3e97NBhsXhJ4=; b=Hh5QxY6oi4DhiIPJ94kk53OFSbFbraE7zsVJEFTCxJO8rt8AXjCqA2KTyWyUGjzB6WXA zZDJjGk53FDT3N27xLaGM8A4Khycl96/dZJD4n9Ju9zEwmwZDT9Xyp7j2zylN1CavrgX lqXzVJzEiSikgYf0SAqrEo5zemw2MaWAI9KwBzfG50SBRMdMTLuNjWSXr0hWy1dEYj8j BiIod3M6WAAw0G99ArhzvNHSzWO0B6mvB0v2g0gL6gLHvErMXTAxSg12AqGBXi7Lodj0 fB+Ai1xgho8B1knHCd3ZI635InhUTgHK/1c8qEOEMVIj1aZ1V32Wfz5adNNTVENhtchg 6A== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2018-07-02; bh=XQCN31heGj0fx7iiZojr/ruGjvl/p/j3e97NBhsXhJ4=; b=IGJKk4EZ/+fm1qA7ajNbNJKALRXXJIHrIT8J3xdTfeXfqmhdA+5UzIg6P+IyX57b81x7 4Y1uZ2Zf45QhdpAqXWGCuEpFUKsRkW/pBFaY19lFPPGDeDRc9AY/kz0OsuepBgBz+jTv 1H76K34VkPA5tZHeLTKwRiv8LpgNh8NKbCHTNVjIrC+Fc50F1tsiVO2wf67kvKK4GCTd agDuynlJpl6xpbZX5aAb69Ys2ve/cvJhtCd9VOsfatc8ILfpGmxvCSp8WWbbwOQwKR4W 32asHbzwRU5Vu2Mt/hwqjlCtjBWKl6adJ/XeaSDoZyOFF7ROKsii88U1Xde9xRvMOH1k JA== Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by userp2130.oracle.com with ESMTP id 2u9nbtc171-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:12:05 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.27/8.16.0.27) with SMTP id x7D68auX050030; Tue, 13 Aug 2019 06:12:05 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by userp3030.oracle.com with ESMTP id 2u9k1w4239-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 13 Aug 2019 06:12:04 +0000 Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x7D6C3Hg026964; Tue, 13 Aug 2019 06:12:03 GMT Received: from abi.no.oracle.com (/10.172.144.123) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 12 Aug 2019 23:12:03 -0700 From: Knut Omang To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Shuah Khan , Jonathan Corbet , Masahiro Yamada , Michal Marek , Greg Kroah-Hartman , Shreyans Devendra Doshi <0xinfosect0r@gmail.com>, Alan Maguire , Brendan Higgins , Kevin Hilman , Hidenori Yamaji , Frank Rowand , Timothy Bird , Luis Chamberlain , "Theodore Ts'o" , Daniel Vetter , Stephen Boyd , Knut Omang Subject: [RFC 19/19] Documentation/dev-tools: Add index entry for KTF documentation Date: Tue, 13 Aug 2019 08:09:34 +0200 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9347 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1906280000 definitions=main-1908130067 Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Knut Omang --- Documentation/dev-tools/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst index b0522a4..f155205 100644 --- a/Documentation/dev-tools/index.rst +++ b/Documentation/dev-tools/index.rst @@ -24,6 +24,7 @@ whole; patches welcome! gdb-kernel-debugging kgdb kselftest + ktf/index .. only:: subproject and html