lkml.org 
[lkml]   [2019]   [Aug]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[RFC 12/19] ktf: Main part of user land library for executing tests
Date
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 <knut.omang@oracle.com>
---
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 <knut.omang@oracle.com>
+ *
+ * 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 <gtest/gtest.h>
+
+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 <knut.omang@oracle.com>
+ *
+ * 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 <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <unistd.h>
+#include "kernel/ktf_unlproto.h"
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <map>
+#include <set>
+#include <string>
+#include "ktf_int.h"
+#include "ktf_debug.h"
+
+#include <netlink/version.h>
+
+int devcnt = 0;
+
+namespace ktf
+{
+
+struct nl_sock* sock = NULL;
+int family = -1;
+
+int printed_header = 0;
+
+typedef std::map<std::string, KernelTest*> testmap;
+typedef std::map<std::string, test_cb*> 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<std::string, testset> setmap;
+typedef std::set<std::string> stringset;
+typedef std::vector<ConfigurableContext*> 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<ConfigurableContext*> add_configurable_context(const std::string& ctx,
+ const std::string& type_name,
+ unsigned int hid, int cfg_stat);
+ std::vector<ConfigurableContext*> add_configurable_contexts(const std::string& ctx,
+ std::vector<ContextType*> type_vec);
+ std::vector<ConfigurableContext*> 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<ConfigurableContext*> 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<unsigned int, stringvec> handle_to_ctxvec;
+ std::map<std::string, context_vector> cfg_contexts;
+
+ // Context types that allows dynamically created contexts:
+ std::map<std::string, std::vector<ContextType*> > ctx_types;
+ int next_set;
+ name_iter* cur;
+};
+
+KernelTestMgr::~KernelTestMgr()
+{
+ std::map<std::string, context_vector>::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<std::string, std::vector<ContextType*> >::iterator tit;
+ for (tit = ctx_types.begin(); tit != ctx_types.end(); ++tit)
+ {
+ std::vector<ContextType*>::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<std::string,context_vector>::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<std::string, std::vector<ContextType*> >::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<ConfigurableContext*> 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<ConfigurableContext*> KernelTestMgr::add_configurable_contexts(const std::string& ctx,
+ std::vector<ContextType*> type_vec)
+{
+ std::vector<ContextType*>::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 <knut.omang@oracle.com>
+ *
+ * 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 <string>
+#include <vector>
+#include "ktf.h"
+
+typedef std::vector<std::string> 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
--
git-series 0.9.1
\
 
 \ /
  Last update: 2019-08-13 08:13    [W:0.198 / U:5.384 seconds]
©2003-2018 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site