This test case is intended to test the nohz_full kernel feature. See file
testcases/kernel/partrt/README for more information.

Signed-off-by: Mats Liljegren <mats.liljeg...@enea.com>
---
 .gitmodules                                        |    3 +
 README.kernel_config                               |    6 +
 runtest/partrt_nohz_full                           |    1 +
 testcases/kernel/Makefile                          |    1 +
 testcases/kernel/partrt/Makefile                   |   17 +
 testcases/kernel/partrt/README                     |   66 +++
 testcases/kernel/partrt/nohz_full/.gitignore       |    1 +
 testcases/kernel/partrt/nohz_full/Makefile         |   23 +
 .../partrt/nohz_full/test_partrt_nohz_full.c       |  593 ++++++++++++++++++++
 utils/.gitignore                                   |    3 +
 utils/Makefile                                     |    7 +
 utils/rt-tools                                     |    1 +
 12 files changed, 722 insertions(+)
 create mode 100644 runtest/partrt_nohz_full
 create mode 100644 testcases/kernel/partrt/Makefile
 create mode 100644 testcases/kernel/partrt/README
 create mode 100644 testcases/kernel/partrt/nohz_full/.gitignore
 create mode 100644 testcases/kernel/partrt/nohz_full/Makefile
 create mode 100644 testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c
 create mode 160000 utils/rt-tools

diff --git a/.gitmodules b/.gitmodules
index 1c9e9c3..0461cbd 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "testcases/kernel/mce-test"]
        path = testcases/kernel/mce-test
        url = 
git://git.kernel.org/pub/scm/linux/kernel/git/gong.chen/mce-test.git
+[submodule "utils/rt-tools"]
+       path = utils/rt-tools
+       url = https://github.com/OpenEneaLinux/rt-tools
diff --git a/README.kernel_config b/README.kernel_config
index 54df0ad..5ebfe7e 100644
--- a/README.kernel_config
+++ b/README.kernel_config
@@ -311,3 +311,9 @@ And the version of packages must be 1.41.4 or above.
 For more information to build/install/run these tests, look through:
 ltp/testcases/kernel/fs/ext4-new-features/README
 ---------------------------------
+
+---------------------------------
+Enabling Kernel Configuration to run test partrt_nohz_full
+---------------------------------
+CONFIG_NO_HZ_FULL=y
+CONFIG_CPUSETS=y
diff --git a/runtest/partrt_nohz_full b/runtest/partrt_nohz_full
new file mode 100644
index 0000000..d84e192
--- /dev/null
+++ b/runtest/partrt_nohz_full
@@ -0,0 +1 @@
+partrt_nohz_full test_partrt_nohz_full -d 3000
diff --git a/testcases/kernel/Makefile b/testcases/kernel/Makefile
index 6bffe79..cdd0406 100644
--- a/testcases/kernel/Makefile
+++ b/testcases/kernel/Makefile
@@ -47,6 +47,7 @@ SUBDIRS                       += connectors \
                           logging \
                           mem \
                           numa \
+                          partrt \
                           performance_counters \
                           pty \
                           sched \
diff --git a/testcases/kernel/partrt/Makefile b/testcases/kernel/partrt/Makefile
new file mode 100644
index 0000000..120090e
--- /dev/null
+++ b/testcases/kernel/partrt/Makefile
@@ -0,0 +1,17 @@
+#    Copyright (C) 2014, Enea AB.
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+
+top_srcdir     ?= ../../..
+
+include $(top_srcdir)/include/mk/env_pre.mk
+include $(top_srcdir)/include/mk/generic_trunk_target.mk
diff --git a/testcases/kernel/partrt/README b/testcases/kernel/partrt/README
new file mode 100644
index 0000000..a461b25
--- /dev/null
+++ b/testcases/kernel/partrt/README
@@ -0,0 +1,66 @@
+partrt_nohz_full
+================
+
+This directory contains part_nohz_full test case for verifying absence of ticks
+on nohz_full enabled CPUs. For more information about nohz_full and how to
+enable this feature, see the following document in the kernel source code:
+
+        Documentation/timers/NO_HZ.txt
+
+CPU partitioning is a way of isolating work with real-time requirements from
+work without real-time requirements by assuring that the real-time work is done
+on dedicated CPUs. This uses the kernel feature cgroup/cpuset. The goal is to
+make sure that nohz_full mode can be enabled on these CPUs.
+
+Dependencies
+------------
+CPU partitioning is performed by the partrt tool, which is available as a git
+submodule. Before building, do the following from the LTP root directory to
+include rt-tools in the build:
+
+        git submodule init  # If you haven't done this since cloning
+        git submodule update utils/rt-tools
+
+The following build time kernel configurations must be enabled:
+
+        CONFIG_NO_HZ_FULL=y
+        CONFIG_CPUSETS=y
+
+These kernel configuration parameters are also documented in the
+READ.kernel_config file in LTP root directory.
+
+The following kernel boot parameter needs to be set:
+
+        nohz_full=<cpu list>
+
+There might be other parameters needed to actually get nohz_full working, but
+this is largely dependent on architecture and kernel version.
+
+How to run
+----------
+
+The partrt_nohz_full test is run by runltp like so:
+
+        ./runltp -f partrt_nohz_full
+
+To make life interesting, you can play with "-n" and "-i" options to runltp, to
+generate some load in the non-realtime partition:
+
+        ./runltp -n -i 5 -f partrt_nohz_full
+
+To get some more help with the cause of a failing test, do this as root before
+running the test:
+
+        echo 1 > /sys/kernel/debug/tracing/events/enable
+
+and then look at trace after test failed:
+
+        cat /sys/kernel/debug/tracing/trace
+
+Since "count_ticks" also uses ftrace and will update tracing_cpumask to only
+trace the nohz_full CPUs, the trace should be close to empty if the run was
+successful.
+
+Known limitations
+-----------------
+Currently this test only supports machines with up to 32 CPUs.
diff --git a/testcases/kernel/partrt/nohz_full/.gitignore 
b/testcases/kernel/partrt/nohz_full/.gitignore
new file mode 100644
index 0000000..5848b21
--- /dev/null
+++ b/testcases/kernel/partrt/nohz_full/.gitignore
@@ -0,0 +1 @@
+/test_partrt_nohz_full
diff --git a/testcases/kernel/partrt/nohz_full/Makefile 
b/testcases/kernel/partrt/nohz_full/Makefile
new file mode 100644
index 0000000..5528dc0
--- /dev/null
+++ b/testcases/kernel/partrt/nohz_full/Makefile
@@ -0,0 +1,23 @@
+#
+#  Copyright (C) 2014, Enea AB.
+#
+#  This program is free software;  you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY;  without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+#  the GNU General Public License for more details.
+#
+
+top_srcdir             ?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+LDLIBS                 += -lrt
+
+MAKE_TARGETS           := test_partrt_nohz_full
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c 
b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c
new file mode 100644
index 0000000..a5ffe3b
--- /dev/null
+++ b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 2014, Enea AB.
+ *
+ * This program is free software;  you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/sysinfo.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <time.h>
+#include <sched.h>
+#include <fcntl.h>
+
+#include <test.h>
+#include <usctest.h>
+#include <safe_macros.h>
+#include <safe_stdio.h>
+
+const char *TCID = "partrt_nohz_full";
+const int TST_TOTAL = 1;
+
+/* Used for RT load */
+volatile int dummy_value;
+
+/* When true (1), all children will terminate */
+static volatile int time_is_up;
+
+/* Name of application as given in argv[0] */
+static const char *appname;
+
+/* Boolean describing whether RT partition in cpuset needs to be created */
+static int need_partrt_create = 0;
+
+/* RT load function prototype */
+typedef void (child_func)(void);
+
+/* Amount of stack for RT load threads */
+#define STACK_SZ (64 * 1024)
+
+/* Default path to cgroups/cpuset */
+#define CPUSET_ROOT "/sys/fs/cgroup/cpuset"
+
+/* Expected path to cpuset RT partition */
+#define CPUSET_RT_PATH CPUSET_ROOT "/rt"
+
+/* Expected path to RT partition tasks list file */
+#define CPUSET_RT_TASKS_FILE CPUSET_RT_PATH "/tasks"
+
+/* Expected path to RT partition CPU list file */
+#define CPUSET_RT_CPUS_FILE CPUSET_RT_PATH "/cpuset.cpus"
+
+/* File to look for to determine whether true 0Hz can be achieved */
+#define SCHED_TICK_MAX_FILE "/sys/kernel/debug/sched_tick_max_deferment"
+
+/* File containing a list of filesystems supported by the kernel */
+#define PROC_FILESYSTEMS_FILE "/proc/filesystems"
+
+static void cleanup(void);
+
+/*
+ * CPU mask handling functions.
+ */
+
+static int cpuset_nr_cpus(void)
+{
+       static int nr_cpus;
+
+       if (nr_cpus == 0) {
+               nr_cpus = get_nprocs_conf();
+               if (nr_cpus <= 0)
+                       tst_brkm(TCONF, cleanup,
+                                "Could not determine number of CPUs");
+               tst_resm(TINFO, "%d CPUs configured in kernel", nr_cpus);
+       }
+
+       return nr_cpus;
+}
+
+static cpu_set_t *cpuset_alloc_zero(void)
+{
+       cpu_set_t *set;
+
+       set = CPU_ALLOC(cpuset_nr_cpus());
+       if (set == NULL)
+               tst_brkm(TBROK, cleanup,
+                        "Out of memory allocating CPU mask for %d CPUs",
+                       cpuset_nr_cpus());
+
+       CPU_ZERO_S(CPU_ALLOC_SIZE(cpuset_nr_cpus()), set);
+
+       return set;
+}
+
+static void cpuset_free(cpu_set_t *set)
+{
+       CPU_FREE(set);
+}
+
+static void cpuset_set(int cpu, cpu_set_t *set)
+{
+       if (cpu >= cpuset_nr_cpus())
+               tst_brkm(TBROK, cleanup, "Internal error: Illegal use of 
cpuset_set(%d,...): Only %d CPUs in the set",
+                        cpu, cpuset_nr_cpus());
+       CPU_SET_S(cpu, CPU_ALLOC_SIZE(cpuset_nr_cpus()), set);
+}
+
+static cpu_set_t *cpuset_alloc_set(int cpu)
+{
+       cpu_set_t *set = cpuset_alloc_zero();
+
+       cpuset_set(cpu, set);
+
+       return set;
+}
+
+static int cpuset_isset(int cpu, const cpu_set_t *set)
+{
+       if (cpu > cpuset_nr_cpus())
+               return 0;
+       return CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(cpuset_nr_cpus()), set);
+}
+
+static size_t cpuset_alloc_size(void)
+{
+       return CPU_ALLOC_SIZE(cpuset_nr_cpus());
+}
+
+static const char *cpuset_hex(const cpu_set_t *set)
+{
+       static char *str;
+       char *curr;
+       int cpu;
+       const int nr_cpus = cpuset_nr_cpus();
+       const size_t str_size = 1 /* NUL */ + (nr_cpus / 4);
+
+       if (str == NULL)
+               str = SAFE_MALLOC(cleanup, str_size);
+
+       curr = str + str_size - 1;
+       *curr = '\0';
+
+       while (cpu < nr_cpus) {
+               char ch = 0;
+               int bit;
+               for (bit = 0; bit < 4; bit++, cpu++) {
+                       if (cpuset_isset(cpu, set))
+                               ch |= (1 << bit);
+               }
+               curr--;
+               if (ch > 9)
+                       *curr = 'a' + (ch - 10);
+               else
+                       *curr = '0' + ch;
+       }
+
+       return str;
+}
+
+/*
+ * Check exit status value.
+ * If any signs of error is found, report error and abort testing.
+ */
+static void assert_exit_status(void (*cleanup_fn) (void), const char *cmd,
+                              int status)
+{
+       if (status == -1)
+               tst_brkm(TBROK | TERRNO, cleanup_fn,
+                       "%s: Could not execute command", cmd);
+       if (WIFSIGNALED(status)) {
+               tst_brkm(TBROK, cleanup_fn,
+                        "%s: Child terminated unexpectedly due to signal nr 
%d",
+                        cmd,
+                        WTERMSIG(status));
+       }
+       if (!WIFEXITED(status)) {
+               tst_brkm(TBROK, cleanup_fn, "%s: Child terminated unexpectedly",
+                        cmd);
+       }
+       if (WEXITSTATUS(status) != 0) {
+               tst_brkm(TBROK, cleanup_fn,
+                        "%s: Child exited with exit status %d",
+                        cmd,
+                        WEXITSTATUS(status));
+       }
+}
+
+/*
+ * Execute given command using shell, ensure success (0) is returned.
+ */
+static void shell(void (*cleanup_fn) (void), const char *cmd, ...)
+{
+       char *cmd_buf;
+       va_list va;
+
+       va_start(va, cmd);
+       if (vasprintf(&cmd_buf, cmd, va) == -1)
+               tst_brkm(TBROK, cleanup_fn,
+                        "%s: Not valid printf format, or out of memory", cmd);
+       va_end(va);
+
+       tst_resm(TINFO, "%s: Executing", cmd_buf);
+       assert_exit_status(cleanup_fn, cmd_buf, system(cmd_buf));
+       tst_resm(TINFO, "%s: Returns success", cmd_buf);
+
+       free(cmd_buf);
+}
+
+/*
+ * Convert string to unsigned long.
+ * Error prefix expressed as string.
+ */
+static unsigned long str_to_ulong(const char *str, const char *err_prefix)
+{
+       char *endptr;
+       unsigned long val;
+       val = strtoull(str, &endptr, 0);
+       if (endptr == str)
+               tst_brkm(TBROK, cleanup,
+                        "%s: %s: Expected unsigned decimal value",
+                        err_prefix, str);
+       if (*endptr != '\0')
+               tst_brkm(TBROK, cleanup,
+                        "%s: %s: Garbage character '%c' found after decimal 
value",
+                        err_prefix, str, *endptr);
+
+       return val;
+}
+
+/*
+ * Call a command using shell.
+ * Command line expressed as va_list.
+ * Returns the command's first line of output.
+ */
+static char *shell_str(char *dest, int size, const char *cmd, ...)
+{
+       int len;
+       char *cmd_buf;
+       FILE *file;
+       va_list va;
+
+       /* Build command line */
+       va_start(va, cmd);
+       if (vasprintf(&cmd_buf, cmd, va) == -1)
+               tst_brkm(TBROK, cleanup,
+                        "%s: Not valid printf format, or out of memory", cmd);
+       va_end(va);
+
+       /* Launch command */
+       file = SAFE_POPEN(cleanup, cmd_buf, "r");
+
+       tst_resm(TINFO, "%s: Executing", cmd_buf);
+
+       /* Read commands stdout */
+       if (fgets(dest, size, file) == NULL) {
+               if (feof(file))
+                       tst_brkm(TBROK, cleanup,
+                                "%s: Expected output from the command, but got 
nothing",
+                                cmd_buf);
+               else
+                       tst_brkm(TBROK | TERRNO, cleanup,
+                                "%s: Could not read command output",
+                                cmd_buf);
+       }
+
+       /* Get rid of terminating newline, if any */
+       len = strlen(dest);
+       if (dest[len-1] == '\n')
+               dest[len-1] = '\0';
+
+       /* Wait until command execution finish.
+        * Main reason for doing this even though we've got what we want is for
+        * better error detection. The alternative could be to let cleanup()
+        * handle it if process hasn't finished by then. */
+       assert_exit_status(cleanup, cmd_buf, pclose(file));
+
+       tst_resm(TINFO, "%s: Returns: %s", cmd_buf, dest);
+
+       free(cmd_buf);
+
+       return dest;
+}
+
+static int child_entry(void *arg)
+{
+       (void) arg;
+
+       while (!time_is_up)
+               dummy_value++;
+
+       return 0;
+}
+
+/*
+ * Start a new thread executing the given function, on the given cpuset
+ * partition and CPU ID. Note that the CPU ID must be one of the
+ * available CPUs in the cpuset partition.
+ */
+static void launch_child(int cpu)
+{
+       char *stack = SAFE_MALLOC(cleanup, STACK_SZ);
+       int tid;
+       cpu_set_t * const cpu_set = cpuset_alloc_set(cpu);
+
+       tid = ltp_clone(CLONE_VM, child_entry, NULL, STACK_SZ, stack);
+       if (tid == -1)
+               tst_brkm(TBROK | TERRNO, cleanup, "clone() failed");
+
+       /* Move child to RT partition */
+       SAFE_FILE_PRINTF(cleanup, CPUSET_RT_TASKS_FILE, "%ld", (long) tid);
+
+       /* Move child to indicated CPU within the RT partition */
+       if (sched_setaffinity(tid, cpuset_alloc_size(), cpu_set) < 0)
+               tst_brkm(TBROK | TERRNO, cleanup,
+                       "pid %u: sched_setaffinity() failed",
+                       tid);
+
+       tst_resm(TINFO, "pid %d: Starting RT load on cpu %d",
+                tid, cpu);
+
+       cpuset_free(cpu_set);
+}
+
+static void cleanup(void)
+{
+       pid_t pid;
+       int status;
+
+       tst_resm(TINFO, "Cleanup: Terminating children");
+
+       time_is_up = 1;
+
+       do {
+               pid = wait(&status);
+               if (pid != -1) {
+                       char cmd[64];
+                       snprintf(cmd, sizeof(cmd), "Pid %lu",
+                                (unsigned long) pid);
+                       assert_exit_status(NULL, cmd, status);
+                       tst_resm(TINFO,
+                                "Cleanup: %s: Has terminated successfully",
+                                cmd);
+               }
+       } while (pid != -1);
+
+       if (errno != ECHILD)
+               tst_brkm(TBROK | TERRNO, NULL, "Cleanup: wait() failed");
+
+       if (need_partrt_create)
+               shell(NULL, "partrt undo");
+
+       tst_resm(TINFO, "Cleanup: Done");
+}
+
+static cpu_set_t *alloc_nohz_mask(void)
+{
+       int range_first;
+       int range_last;
+       int bit;
+       cpu_set_t *mask = cpuset_alloc_zero();
+       FILE *stream = SAFE_FOPEN(cleanup, CPUSET_RT_CPUS_FILE, "r");
+       int nr_matches;
+
+       for (nr_matches = fscanf(stream, "%d-%d", &range_first, &range_last);
+            nr_matches > 0;
+            nr_matches = fscanf(stream, ",%d-%d", &range_first, &range_last)) {
+               if (nr_matches == 1)
+                       range_last = range_first;
+
+               /* Set all bits in range */
+               for (bit = range_first; bit <= range_last; bit++)
+                       cpuset_set(bit, mask);
+       }
+
+       SAFE_FCLOSE(cleanup, stream);
+
+       if (nr_matches == -1)
+               tst_brkm(TBROK | TERRNO, cleanup, "%s: fscanf() failed",
+                        CPUSET_RT_CPUS_FILE);
+
+       return mask;
+}
+
+static void tool_available(char *tool_name)
+{
+       char full_path[PATH_MAX];
+
+       if (tst_get_path(tool_name, full_path, sizeof(full_path)) != 0)
+               tst_brkm(TCONF, cleanup,
+                        "%s tool not found, skipping test. Use 'git submodule 
update utils/rt-tools' to include needed tools in the build.",
+                        tool_name);
+
+       if (access(full_path, X_OK) != 0)
+               tst_brkm(TCONF, cleanup,
+                        "%s tool found in PATH, but is not executable.",
+                        tool_name);
+}
+
+static void tools_check(void)
+{
+       tool_available("partrt");
+       tool_available("list2mask");
+       tool_available("count_ticks");
+}
+
+static void cpuset_check(void)
+{
+       FILE * const stream = SAFE_FOPEN(cleanup, PROC_FILESYSTEMS_FILE, "r");
+       char *name;
+
+       while (fscanf(stream, "%as", &name) > 0) {
+               if (strcmp(name, "cpuset") == 0) {
+                       free(name);
+                       return;
+               }
+               free(name);
+       }
+
+       tst_brkm(TCONF, cleanup, "CPUSET not configured in kernel");
+}
+
+/*
+ * Setup and return number of child threads
+ */
+static int setup_children(void)
+{
+       cpu_set_t *nohz_mask;
+       int bit;
+       int nr_children = 0;
+
+       time_is_up = 0;
+
+       tools_check();
+       tst_require_root(NULL);
+       cpuset_check();
+
+       if (access(CPUSET_RT_PATH, F_OK) == -1)
+               need_partrt_create = 1;
+
+       if (need_partrt_create)
+               shell(cleanup, "partrt create $(list2mask --nohz)");
+
+       nohz_mask = alloc_nohz_mask();
+       tst_resm(TINFO, "Nohz CPU mask: 0x%s", cpuset_hex(nohz_mask));
+
+       for (bit = 0; bit < (int) (sizeof(nohz_mask) * 8); bit++) {
+               if (cpuset_isset(bit, nohz_mask)) {
+                       launch_child(bit);
+                       nr_children++;
+               }
+       }
+
+       tst_resm(TINFO, "All children started");
+
+       cpuset_free(nohz_mask);
+
+       return nr_children;
+}
+
+/*
+ * Perform tick measurement for the given number of seconds.
+ */
+static void test(time_t duration, unsigned nr_children)
+{
+       uint64_t nr_ticks;
+       time_t time_finished;
+       /* If sched_tick_max_deferment patch has been applied, expect that the
+        * partitioning has disabled ticks completely. Otherwise, expect
+        * 1Hz ticks */
+       const int expect_0hz =
+               access(SCHED_TICK_MAX_FILE, F_OK) == 0;
+       const time_t current_time = time(NULL);
+
+       const uint64_t expected_ticks = expect_0hz ? 0 : duration * nr_children;
+       char val_str[64];
+       static const char count_ticks_end_cmd[] =
+               "count_ticks --cpu rt --batch --end";
+       int timer_fd;
+       struct itimerspec timeout = { {0}, {0} };
+       uint64_t nr_timeouts;
+
+       /* Make sure process migration has completed before looking for ticks.
+        * Sleep is somewhat primitive, but should work. */
+       sleep(2);
+
+       shell(cleanup, "count_ticks --cpu rt --start");
+
+       time_finished = time(NULL) + duration;
+
+       tst_resm(TINFO, "Execution started: %s", ctime(&current_time));
+       tst_resm(TINFO, "Execution ends   : %s", ctime(&time_finished));
+
+       timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
+       timeout.it_value.tv_sec = duration;
+       timerfd_settime(timer_fd, 0, &timeout, NULL);
+
+       /* Wait for timeout */
+       SAFE_READ(cleanup, 1, timer_fd, &nr_timeouts, sizeof(nr_timeouts));
+       if (nr_timeouts != 1)
+               tst_brkm(TBROK, cleanup,
+                       "Multiple timeouts when single timeout was requested");
+
+       shell_str(val_str, sizeof(val_str), count_ticks_end_cmd);
+       nr_ticks = str_to_ulong(val_str, count_ticks_end_cmd);
+
+       if (expect_0hz) {
+               if (nr_ticks != 0)
+                       tst_resm(TFAIL, "Expected no ticks, but got %" PRIu64,
+                                nr_ticks);
+               else
+                       tst_resm(TPASS, "No ticks occurred");
+       } else {
+               if (nr_ticks > expected_ticks)
+                       tst_resm(TFAIL,
+                                "Expected maximum %" PRIu64
+                                " ticks, but got %" PRIu64,
+                                expected_ticks, nr_ticks);
+               else
+                       tst_resm(TPASS,
+                                "%" PRIu64
+                                " ticks occurred, which was expected",
+                                nr_ticks);
+       }
+}
+
+static void usage(void)
+{
+       printf(
+               "Usage: %s [options]\n"
+               "       %s --help\n"
+               "Test whether a CPU can be isolated and made tickless even 
under load.\n"
+               "\n"
+               "  -h         Display this usage text and exit.\n"
+               "  -d <secs>  Number of seconds to run the test.\n"
+               ,
+               appname, appname
+               );
+}
+
+int main(int argc, char *argv[])
+{
+       long duration = 0;
+       int lc;
+       unsigned int nr_children;
+       char *error_msg;
+       char *duration_str = NULL;
+
+       const option_t options[] = {
+               {"d:", NULL, &duration_str},
+               {NULL, NULL, NULL}
+       };
+
+       appname = basename(argv[0]);
+
+       error_msg = parse_opts(argc, argv, options, usage);
+       if (error_msg != NULL)
+               tst_brkm(TBROK, NULL, "Error parsing command line: %s",
+                       error_msg);
+
+       if (duration_str == NULL)
+               tst_brkm(TBROK, cleanup,
+                        "No duration specified, nothing to do");
+
+       duration = (long) str_to_ulong(duration_str, "-d");
+
+       tst_resm(TINFO, "%s: Compiled %s %s", __FILE__, __DATE__, __TIME__);
+
+       nr_children = setup_children();
+
+       for (lc = 0; TEST_LOOPING(lc); lc++)
+               test(duration, nr_children);
+
+       cleanup();
+       tst_exit();
+}
diff --git a/utils/.gitignore b/utils/.gitignore
index a582748..f63e51a 100644
--- a/utils/.gitignore
+++ b/utils/.gitignore
@@ -48,3 +48,6 @@
 /sctp/func_tests/test_tcp_style_v6
 /sctp/func_tests/test_timetolive
 /sctp/func_tests/test_timetolive_v6
+/count_ticks
+/list2mask
+/partrt
diff --git a/utils/Makefile b/utils/Makefile
index 1508b35..c3df6cd 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -24,6 +24,10 @@ include $(top_srcdir)/include/mk/env_pre.mk
 
 MAKE_TARGETS         += ffsb
 
+ifneq ($(wildcard rt-tools),)
+MAKE_TARGETS         += partrt list2mask count_ticks
+endif
+
 FFSBDIR                        := ffsb-6.0-rc2
 FILTER_OUT_DIRS                := $(FFSBDIR)
 FFSB                   := $(FFSBDIR)/ffsb
@@ -35,6 +39,9 @@ $(FFSB): $(abs_srcdir)/$(FFSBDIR)
 ffsb: $(FFSB)
        cp $(FFSB) ffsb
 
+partrt list2mask count_ticks:
+       cp rt-tools/install/bin/$@ $@
+
 trunk-all: $(FFSB)
 
 trunk-clean:: | ffsb-clean
diff --git a/utils/rt-tools b/utils/rt-tools
new file mode 160000
index 0000000..f75b334
--- /dev/null
+++ b/utils/rt-tools
@@ -0,0 +1 @@
+Subproject commit f75b334922a2243d0b757c0627c6f1c8440818c0
-- 
1.7.10.4


------------------------------------------------------------------------------
"Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE
Instantly run your Selenium tests across 300+ browser/OS combos.
Get unparalleled scalability from the best Selenium testing platform available
Simple to use. Nothing to install. Get started now for free."
http://p.sf.net/sfu/SauceLabs
_______________________________________________
Ltp-list mailing list
Ltp-list@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ltp-list

Reply via email to