To check a kernel configuration for common issues which may increase
latency.

Signed-off-by: Hongzhan Chen <hongzhan.c...@intel.com>

diff --git a/configure.ac b/configure.ac
index 480a94768..2bf8eece3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1017,6 +1017,7 @@ AC_CONFIG_FILES([ \
        utils/net/rtnet \
        utils/net/rtnet.conf \
        utils/net/Makefile \
+       utils/chkkconf/Makefile \
        demo/Makefile \
        demo/posix/Makefile \
        demo/posix/cyclictest/Makefile \
diff --git a/doc/asciidoc/Makefile.am b/doc/asciidoc/Makefile.am
index 8e33c7281..2bacb7c10 100644
--- a/doc/asciidoc/Makefile.am
+++ b/doc/asciidoc/Makefile.am
@@ -7,6 +7,7 @@ HTML_DOCS =                                     \
        html/asciidoc-icons                     \
        html/asciidoc-icons/callouts            \
        html/man1/autotune                      \
+       html/man1/chkkconf                      \
        html/man1/clocktest                     \
        html/man1/corectl                       \
        html/man1/dohell                        \
@@ -36,6 +37,7 @@ TXT_DOCS =                            \
 
 MAN1_DOCS =                    \
        man1/autotune.1         \
+       man1/chkkconf.1         \
        man1/clocktest.1        \
        man1/corectl.1          \
        man1/cyclictest.1       \
diff --git a/doc/asciidoc/man1/chkkconf.adoc b/doc/asciidoc/man1/chkkconf.adoc
new file mode 100644
index 000000000..5a3c9a633
--- /dev/null
+++ b/doc/asciidoc/man1/chkkconf.adoc
@@ -0,0 +1,117 @@
+// ** The above line should force tbl to be a preprocessor **
+// Man page for chkkconf
+//
+// Copyright (C) 2015 Philippe Gerum <r...@xenomai.org>
+//
+// You may distribute under the terms of the GNU General Public
+// License as specified in the file COPYING that comes with the
+// Xenomai distribution.
+//
+//
+CHKKCONF(1)
+==========
+:doctype: manpage
+:revdate: 2021/09/23
+:man source: Xenomai
+:man version: {xenover}
+:man manual: Xenomai Manual
+
+NAME
+----
+chkkconf - Check kernel .config
+
+SYNOPSIS
+---------
+*chkkconf* [ options ]
+
+DESCRIPTION
+------------
+*chkkconf* is a common utility to check kernel configuration based
+on specified checklist. The kernel configuration to verify is
+a regular .config file which contains all the settings for
+building a kernel image. The check list contains a series
+of single-line assertions which are tested against the
+contents of the kernel configuration. The default checklist
+file kconf-checklist under $datarootdir(/user/xenomai/share/
+by default) contains assertions that may influence latency
+for xenomai. When we use the default checklist, the utility checks
+a kernel configuration for common issues which may increase latency.
+
+
+OPTIONS
+--------
+*chkkconf* accepts the following options:
+
+*--file*:: Specify a regular .config file. If none is specified,
+the command defaults to reading /proc/config.gz on the current
+machine. If this fails because any of CONFIG_IKCONFIG or
+CONFIG_IKCONFIG_PROC was disabled in the running kernel, the
+command fails.
+
+*--check-list*:: Specify a file that contains a series of single-line
+assertions which are tested against the contents of the kernel
+configuration. If none is specified, a default check-list is loaded
+from $datarootdir/kconf-checklist(/user/xenomai/share/kconf-checklist
+by default). Each assertion follows the BNF-like syntax below:
+
+- assertion   : expr conditions
+            | "!" expr conditions
+
+- expr        : symbol /* matches =y and =m */
+            | symbol "=" tristate
+
+- tristate  : "y"
+          | "m"
+          | "n"
+
+- conditions  : dependency
+            | dependency arch
+
+- dependency  : "if" symbol       /* true if set as y/m */
+
+- arch        : "on" cputype
+
+- cputype     : $(uname -m)
+
+For instance:
+
+- CONFIG_FOO must be set whenever CONFIG_BAR is unset can be written as
+CONFIG_FOO if !CONFIG_BAR.
+
+- CONFIG_FOO must not be set can be written as !CONFIG_FOO, or
+conversely CONFIG_FOO=n.
+
+- CONFIG_FOO must be built as module on aarch32 or aarch64 can be
+written as CONFIG_FOO=m on aarch.
+
+- CONFIG_FOO must not be built-in on aarch64 if CONFIG_BAR is set can be
+written as !CONFIG_FOO=y if CONFIG_BAR on aarch.
+
+Assertions in the check list may apply to a particular CPU architecture.
+Normally, the command should be able to figure out which architecture
+the kernel configuration file applies to by inspecting the first lines,
+looking for the “Linux/” pattern. However, you might have to specify
+this information manually to the command using the -a option if the file
+referred to by the -f option does not contain such information.
+The architecture name (cputype) should match the output of $(uname -m)
+or some abbreviated portion of it. However, arm64 and arm are automatically
+translated to aarch64 and aarch32 when found in an assertion or passed to
+the -a option.
+
+*--arch*:: Specify CPU architecture that you want to check for.
+
+*--hash-size*:: Set the hash table size.
+
+*--quiet*:: Suppress output.
+
+*--help*::
+Display a short help.
+
+VERSIONS
+--------
+*chkkconf* appeared in Xenomai 3.2 for checking kernel .config.
+
+AUTHOR
+-------
+*chkkconf* was written by Philippe Gerum <r...@xenomai.org> and ported
+by Hongzhan Chen <hongzhan.c...@intel.com> from xenomai4.
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 81dbfda7c..4de6434a9 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -2,3 +2,4 @@ SUBDIRS = hdb
 if XENO_COBALT
 SUBDIRS += analogy autotune can net ps slackspot corectl
 endif
+SUBDIRS += chkkconf
diff --git a/utils/chkkconf/Makefile.am b/utils/chkkconf/Makefile.am
new file mode 100644
index 000000000..3befee7f1
--- /dev/null
+++ b/utils/chkkconf/Makefile.am
@@ -0,0 +1,20 @@
+
+data_DATA = kconf-checklist
+
+CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
+
+sbin_PROGRAMS = chkkconf
+
+chkkconf_SOURCES = checkconfig.c
+
+chkkconf_CPPFLAGS =            \
+       $(XENO_USER_CFLAGS)     \
+       -I$(top_srcdir)/include \
+       -DTESTDIR=\"$(datadir)\" -D_GNU_SOURCE
+
+chkkconf_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
+
+chkkconf_LDADD =                                       \
+        @XENO_CORE_LDADD@                      \
+        @XENO_USER_LDADD@                      \
+       -lpthread -lrt
diff --git a/utils/chkkconf/checkconfig.c b/utils/chkkconf/checkconfig.c
new file mode 100644
index 000000000..9429b230f
--- /dev/null
+++ b/utils/chkkconf/checkconfig.c
@@ -0,0 +1,338 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2020 Philippe Gerum  <r...@xenomai.org>
+ */
+
+#include <unistd.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <search.h>
+#include <error.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <xenomai/init.h>
+
+#define DEFAULT_KCONFIG    "/proc/config.gz"
+#define DEFAULT_CHECKLIST  TESTDIR"/kconf-checklist"
+
+#define short_optlist "@hqf:L:a:H:"
+
+static int hash_size = 16384;
+
+static bool quiet;
+
+static const struct option options[] = {
+       {"file", required_argument, NULL, 'f'},
+       {"check-list", required_argument, NULL, 'L'},
+       {"arch", required_argument, NULL, 'a'},
+       {"hash-size", required_argument, NULL, 'H'},
+       {"quiet", no_argument, NULL, 'q'},
+       {"help", no_argument, NULL, 'h'},
+       {0}
+};
+
+static char *hash_config(FILE *kconfp)
+{
+       char buf[BUFSIZ], *sym, *val, *arch = NULL;
+       ENTRY entry, *e;
+       int ret;
+
+       ret = hcreate(hash_size);
+       if (!ret)
+               error(1, errno, "hcreate(%d)", hash_size);
+
+       while (fgets(buf, sizeof(buf), kconfp)) {
+               if (*buf == '#') {
+                       sscanf(buf, "# Linux/%m[^ ]", &arch);
+                       continue;
+               }
+               ret = sscanf(buf, "%m[^=]=%ms\n", &sym, &val);
+               if (ret != 2)
+                       continue;
+               if (strncmp(sym, "CONFIG_", 7))
+                       continue;
+               if (strcmp(val, "y") && strcmp(val, "m"))
+                       continue;
+               entry.key = sym;
+               entry.data = NULL;
+               e = hsearch(entry, FIND);
+               if (e)
+                       continue; /* uhh? */
+               entry.data = val;
+               if (!hsearch(entry, ENTER))
+                       error(1, ENOMEM, "h-table full -- try -H");
+       }
+
+       return arch;
+}
+
+static char *next_token(char **next)
+{
+       char *p = *next, *s;
+
+       for (;;) {
+               if (!*p) {
+                       *next = p;
+                       return strdup("");
+               }
+               if (!isspace(*p))
+                       break;
+               p++;
+       }
+
+       s = p;
+
+       if (!isalnum(*p) && *p != '_') {
+               *next = p + 1;
+               return strndup(s, 1);
+       }
+
+       do {
+               if (!isalnum(*p) && *p != '_') {
+                       *next = p;
+                       return strndup(s, p - s);
+               }
+       } while (*++p);
+
+       *next = p;
+
+       return strdup(s);
+}
+
+static const char *get_arch_alias(const char *arch)
+{
+       if (!strcmp(arch, "arm64"))
+               return "aarch64";
+
+       if (!strcmp(arch, "arm"))
+               return "aarch32";
+
+       return arch;
+}
+
+static int apply_checklist(FILE *checkfp, const char *cpuarch)
+{
+       char buf[BUFSIZ], *token, *next, *sym, *val;
+       int lineno = 0, failed = 0;
+       bool not, notcond;
+       const char *arch;
+       ENTRY entry, *e;
+
+       while (fgets(buf, sizeof(buf), checkfp)) {
+               lineno++;
+               next = buf;
+
+               token = next_token(&next);
+               if (!*token || !strcmp(token, "#")) {
+                       free(token);
+                       continue;
+               }
+
+               not = *token == '!';
+               if (not) {
+                       free(token);
+                       token = next_token(&next);
+               }
+
+               sym = token;
+               if (strncmp(sym, "CONFIG_", 7))
+                       error(1, EINVAL,
+                               "invalid check list symbol '%s' at line %d",
+                               sym, lineno);
+
+               token = next_token(&next);
+               val = NULL;
+               if (*token == '=') {
+                       free(token);
+                       val = next_token(&next);
+                       token = next_token(&next);
+               }
+
+               if (!strcmp(token, "if")) {
+                       free(token);
+                       token = next_token(&next);
+                       notcond = *token == '!';
+                       if (notcond) {
+                               free(token);
+                               token = next_token(&next);
+                       }
+                       if (strncmp(token, "CONFIG_", 7))
+                               error(1, EINVAL,
+                                       "invalid condition symbol '%s' at line 
%d",
+                                       token, lineno);
+                       entry.key = token;
+                       entry.data = NULL;
+                       e = hsearch(entry, FIND);
+                       free(token);
+                       if (!((e && !notcond) || (!e && notcond)))
+                               continue;
+                       token = next_token(&next);
+               }
+
+               if (!strcmp(token, "on")) {
+                       free(token);
+                       token = next_token(&next);
+                       arch = get_arch_alias(token);
+                       if (strncmp(cpuarch, arch, strlen(arch))) {
+                               free(token);
+                               continue;
+                       }
+               }
+
+               free(token);
+
+               entry.key = sym;
+               entry.data = NULL;
+               e = hsearch(entry, FIND);
+
+               if (val && !strcmp(val, "n"))
+                       not = !not;
+
+               if (e && (not || (val && strcmp(val, e->data)))) {
+                       if (!quiet)
+                               printf("%s=%s\n", sym, (const char *)e->data);
+                       failed++;
+               } else if (!e && !not) {
+                       if (!quiet)
+                               printf("%s=n\n", sym);
+                       failed++;
+               }
+
+               free(sym);
+               if (val)
+                       free(val);
+       }
+
+       return failed;
+}
+
+static void usage(void)
+{
+       fprintf(stderr, "usage: %s [options]:\n", get_program_name());
+       fprintf(stderr, "-f --file=<.config>     Kconfig file to check 
[=/proc/config.gz]\n");
+       fprintf(stderr, "-L --check-list=<file>  configuration check list 
[="TESTDIR"/kconf-checklist]\n");
+       fprintf(stderr, "-a --arch=<cpuarch>     CPU architecture assumed\n");
+       fprintf(stderr, "-H --hash-size=<N>      set the hash table size 
[=16384]\n");
+       fprintf(stderr, "-q --quiet              suppress output\n");
+       fprintf(stderr, "-h --help               this help\n");
+}
+
+
+void application_usage(void)
+{
+       usage();
+}
+
+int main(int argc, char *const argv[])
+{
+       const char *kconfig = DEFAULT_KCONFIG, *check_list = NULL,
+               *defarch, *arch = NULL, *p;
+       FILE *kconfp, *checkfp;
+       struct utsname ubuf;
+       int c, ret;
+       char *cmd;
+
+       opterr = 0;
+
+       for (;;) {
+               c = getopt_long(argc, argv, short_optlist, options, NULL);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 0:
+                       break;
+               case 'f':
+                       kconfig = optarg;
+                       break;
+               case 'L':
+                       check_list = optarg;
+                       break;
+               case 'a':
+                       arch = optarg;
+                       break;
+               case 'H':
+                       hash_size = atoi(optarg);
+                       if (hash_size < 16384)
+                               hash_size = 16384;
+                       break;
+               case 'q':
+                       quiet = true;
+                       break;
+               case 'h':
+                       usage();
+                       return 0;
+               case '@':
+                       printf("check kernel configuration\n");
+                       return 0;
+               default:
+                       usage();
+                       return 1;
+               }
+       }
+       if (optind < argc) {
+               usage();
+               return 1;
+       }
+
+       /*
+        * We may be given a gzipped input file. Finding gunzip on a
+        * minimalist rootfs (e.g. busybox) may be more likely than
+        * having the zlib development files available from a common
+        * cross-toolchain. So go for popen-ing gunzip on the target
+        * instead of depending on libz on the development host.
+        */
+       if (!strcmp(kconfig, "-")) {
+               kconfp = stdin;
+       } else {
+               p = strstr(kconfig, ".gz");
+               if (!p || strcmp(p, ".gz")) {
+                       kconfp = fopen(kconfig, "r");
+               } else {
+                       if (access(kconfig, R_OK))
+                               error(1, errno, "cannot access %s%s",
+                                       kconfig,
+                                       strcmp(kconfig, DEFAULT_KCONFIG) ? "" :
+                                       "\n(you need CONFIG_IKCONFIG_PROC 
enabled)");
+                       ret = asprintf(&cmd, "gunzip -c %s", kconfig);
+                       if (ret < 0)
+                               error(1, ENOMEM, "asprintf()");
+                       kconfp = popen(cmd, "r");
+                       free(cmd);
+               }
+               if (kconfp == NULL)
+                       error(1, errno, "cannot open %s for reading", kconfig);
+       }
+
+       defarch = hash_config(kconfp);
+
+       if (check_list == NULL)
+               check_list = DEFAULT_CHECKLIST;
+
+       if (access(check_list, R_OK))
+               error(1, errno, "cannot access %s", check_list);
+
+       checkfp = fopen(check_list, "r");
+       if (checkfp == NULL)
+               error(1, errno, "cannot open %s for reading", check_list);
+
+       if (arch == NULL) {
+               if (defarch) {
+                       arch = get_arch_alias(defarch);
+               } else {
+                       ret = uname(&ubuf);
+                       if (ret)
+                               error(1, errno, "utsname()");
+                       arch = ubuf.machine;
+               }
+       } else {
+               arch = get_arch_alias(arch);
+       }
+
+       return apply_checklist(checkfp, arch);
+}
diff --git a/utils/chkkconf/kconf-checklist b/utils/chkkconf/kconf-checklist
new file mode 100644
index 000000000..ca7c0395d
--- /dev/null
+++ b/utils/chkkconf/kconf-checklist
@@ -0,0 +1,51 @@
+# Kconfig check list for dovetail-based xenomai
+#
+# This file contains assertions testing a set of configuration
+# settings from a kernel .config file, which are fed to evl-check.
+# Any failed assertion is reported.
+#
+#
+# check_list  : assertion
+#            | check_list assertion
+#
+# assertion   : expr conditions
+#            | "!" expr conditions
+#
+# expr     : symbol /* matches =y and =m */
+#          | symbol "=" tristate
+#
+# tristate  : "y"
+#          | "m"
+#          | "n"
+#
+# conditions  : dependency
+#            | dependency arch
+#
+# dependency  : "if" symbol    /* true if set as y/m */
+#
+# arch     : "on" cputype
+#
+# cputype   : $(uname -m)
+#
+# <arch> should match $(uname -m) or some abbreviated portion
+# of it.
+#
+# e.g.
+# "CONFIG_FOO must be set whenever CONFIG_BAR is UNset"
+# translates to: CONFIG_FOO if !CONFIG_BAR
+# "CONFIG_FOO must not be set"
+# translates to: !CONFIG_FOO, or conversely CONFIG_FOO=n
+# "CONFIG_FOO must be built as module on aarch32 or aarch64"
+# translates to: CONFIG_FOO=m on aarch
+
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y if CONFIG_CPU_FREQ
+CONFIG_DEBUG_HARD_LOCKS=n
+CONFIG_ACPI_PROCESSOR_IDLE=n
+CONFIG_LOCKDEP=n
+CONFIG_DEBUG_LIST=n
+CONFIG_DEBUG_VM=n
+CONFIG_DEBUG_PER_CPU_MAPS=n
+CONFIG_KASAN=n
+CONFIG_DEBUG_ENTRY=n
+CONFIG_FTRACE=n
+CONFIG_MIGRATION=n
-- 
2.17.1


Reply via email to