2015-02-05 06:37:23

by John Stultz

[permalink] [raw]
Subject: [RFC][PATCH 0/7] Start adding timetests to selftest

I've maintained a suite of timekeeping and timer tests for awhile
here: https://github.com/johnstultz-work/timetests

I've been meaning to get them reworked and submitted into the
selftest infrastructure, but haven't had much time recently.

I'm going to have a long flight coming up so I figured I'd
get started and get some initial feedback.

This series adds all the non-destructive tests from my
timetests suite. The suite has more tests which actually tweak
time state and validate things behave as expected, but I wasn't
sure how to integrate that into the selftest infrastructure
as I'm not sure everyone would want the tests to be mucking
with their system time. (Currently my plan is to add a
script similar to the runall.sh in timetests, so folks
can go in and manually run it if they're feeling daring)

Thoughts or comments here would be greatly appreciated!

thanks!
-john

Cc: Shuah Khan <[email protected]>
Cc: Prarit Bhargava <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Richard Cochran <[email protected]>

John Stultz (7):
selftests/timers: Cleanup Makefile to make it easier to add future
tests
selftest/timers: Quiet warning due to lack of return check on brk
selftest/timers: Add nanosleep test from timetest suite
selftest/timers: Add inconsistency-check test from timetests
selftest/timers: Add nsleep-lat test from timetest suite
selftests/timers: Add clock skew estimation test from timetest suite
selftest/timers: Add set-timer-lat test from timetest suite

tools/testing/selftests/timers/Makefile | 15 +-
.../testing/selftests/timers/inconsistency-check.c | 198 +++++++++++++++++++++
tools/testing/selftests/timers/nanosleep.c | 161 +++++++++++++++++
tools/testing/selftests/timers/nsleep-lat.c | 176 ++++++++++++++++++
tools/testing/selftests/timers/posix_timers.c | 5 +-
tools/testing/selftests/timers/raw_skew.c | 137 ++++++++++++++
tools/testing/selftests/timers/set-timer-lat.c | 198 +++++++++++++++++++++
7 files changed, 885 insertions(+), 5 deletions(-)
create mode 100644 tools/testing/selftests/timers/inconsistency-check.c
create mode 100644 tools/testing/selftests/timers/nanosleep.c
create mode 100644 tools/testing/selftests/timers/nsleep-lat.c
create mode 100644 tools/testing/selftests/timers/raw_skew.c
create mode 100644 tools/testing/selftests/timers/set-timer-lat.c

--
1.9.1


2015-02-05 06:37:27

by John Stultz

[permalink] [raw]
Subject: [PATCH 1/7] selftests/timers: Cleanup Makefile to make it easier to add future tests

Try to streamline the makefile so its easier to add timer/timekeeping
tests.

Cc: Shuah Khan <[email protected]>
Signed-off-by: John Stultz <[email protected]>
---
tools/testing/selftests/timers/Makefile | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile
index eb2859f..90a37b3 100644
--- a/tools/testing/selftests/timers/Makefile
+++ b/tools/testing/selftests/timers/Makefile
@@ -1,8 +1,11 @@
-all:
- gcc posix_timers.c -o posix_timers -lrt
+CFLAGS += -O3 -Wl,-no-as-needed
+LDFLAGS += -lrt -lpthread
+bins = posix_timers
+
+all: ${bins}

run_tests: all
./posix_timers

clean:
- rm -f ./posix_timers
+ rm -f ${bins}
--
1.9.1

2015-02-05 06:38:56

by John Stultz

[permalink] [raw]
Subject: [PATCH 2/7] selftest/timers: Quiet warning due to lack of return check on brk

The posix_timers.c test has a loop that tries to keep it in
kernel space, repeatedly calling brk(). Since its noise, and
a failure won't change what the test would do, add a unused
value to quiet the warning.

Cc: Shuah Khan <[email protected]>
Signed-off-by: John Stultz <[email protected]>
---
tools/testing/selftests/timers/posix_timers.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c
index f87d970..3e04cc0 100644
--- a/tools/testing/selftests/timers/posix_timers.c
+++ b/tools/testing/selftests/timers/posix_timers.c
@@ -35,10 +35,11 @@ static void user_loop(void)
static void kernel_loop(void)
{
void *addr = sbrk(0);
+ int unused;

while (!done) {
- brk(addr + 4096);
- brk(addr);
+ unused = brk(addr + 4096);
+ unused = brk(addr);
}
}

--
1.9.1

2015-02-05 06:38:41

by John Stultz

[permalink] [raw]
Subject: [PATCH 3/7] selftest/timers: Add nanosleep test from timetest suite

Add my basic nanosleep test from my timetest suite.
This test validates that nanosleep dosen't return early
against a number of clockids.

Cc: Shuah Khan <[email protected]>
Signed-off-by: John Stultz <[email protected]>
---
tools/testing/selftests/timers/Makefile | 3 +-
tools/testing/selftests/timers/nanosleep.c | 161 +++++++++++++++++++++++++++++
2 files changed, 163 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/timers/nanosleep.c

diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile
index 90a37b3..469aaa6 100644
--- a/tools/testing/selftests/timers/Makefile
+++ b/tools/testing/selftests/timers/Makefile
@@ -1,11 +1,12 @@
CFLAGS += -O3 -Wl,-no-as-needed
LDFLAGS += -lrt -lpthread
-bins = posix_timers
+bins = posix_timers nanosleep

all: ${bins}

run_tests: all
./posix_timers
+ ./nanosleep

clean:
rm -f ${bins}
diff --git a/tools/testing/selftests/timers/nanosleep.c b/tools/testing/selftests/timers/nanosleep.c
new file mode 100644
index 0000000..60caf76
--- /dev/null
+++ b/tools/testing/selftests/timers/nanosleep.c
@@ -0,0 +1,161 @@
+/* Make sure timers don't return early
+ * by: john stultz ([email protected])
+ * John Stultz ([email protected])
+ * (C) Copyright IBM 2012
+ * (C) Copyright Linaro 2013 2015
+ * Licensed under the GPLv2
+ *
+ * To build:
+ * $ gcc nanosleep.c -o nanosleep -lrt
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <string.h>
+#include <signal.h>
+
+#define NSEC_PER_SEC 1000000000ULL
+
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 1
+#define CLOCK_PROCESS_CPUTIME_ID 2
+#define CLOCK_THREAD_CPUTIME_ID 3
+#define CLOCK_MONOTONIC_RAW 4
+#define CLOCK_REALTIME_COARSE 5
+#define CLOCK_MONOTONIC_COARSE 6
+#define CLOCK_BOOTTIME 7
+#define CLOCK_REALTIME_ALARM 8
+#define CLOCK_BOOTTIME_ALARM 9
+#define CLOCK_HWSPECIFIC 10
+#define CLOCK_TAI 11
+#define NR_CLOCKIDS 12
+
+#define UNSUPPORTED 0xf00f
+
+char *clockstring(int clockid)
+{
+ switch (clockid) {
+ case CLOCK_REALTIME:
+ return "CLOCK_REALTIME";
+ case CLOCK_MONOTONIC:
+ return "CLOCK_MONOTONIC";
+ case CLOCK_PROCESS_CPUTIME_ID:
+ return "CLOCK_PROCESS_CPUTIME_ID";
+ case CLOCK_THREAD_CPUTIME_ID:
+ return "CLOCK_THREAD_CPUTIME_ID";
+ case CLOCK_MONOTONIC_RAW:
+ return "CLOCK_MONOTONIC_RAW";
+ case CLOCK_REALTIME_COARSE:
+ return "CLOCK_REALTIME_COARSE";
+ case CLOCK_MONOTONIC_COARSE:
+ return "CLOCK_MONOTONIC_COARSE";
+ case CLOCK_BOOTTIME:
+ return "CLOCK_BOOTTIME";
+ case CLOCK_REALTIME_ALARM:
+ return "CLOCK_REALTIME_ALARM";
+ case CLOCK_BOOTTIME_ALARM:
+ return "CLOCK_BOOTTIME_ALARM";
+ case CLOCK_TAI:
+ return "CLOCK_TAI";
+ };
+ return "UNKNOWN_CLOCKID";
+}
+
+/* returns 1 if a <= b, 0 otherwise */
+static inline int in_order(struct timespec a, struct timespec b)
+{
+ if(a.tv_sec < b.tv_sec)
+ return 1;
+ if(a.tv_sec > b.tv_sec)
+ return 0;
+ if(a.tv_nsec > b.tv_nsec)
+ return 0;
+ return 1;
+}
+
+struct timespec timespec_add(struct timespec ts, unsigned long long ns)
+{
+ ts.tv_nsec += ns;
+ while(ts.tv_nsec >= NSEC_PER_SEC) {
+ ts.tv_nsec -= NSEC_PER_SEC;
+ ts.tv_sec++;
+ }
+ return ts;
+}
+
+int nanosleep_test(int clockid, long long ns)
+{
+ struct timespec now, target, rel;
+
+ /* First check abs time */
+ if (clock_gettime(clockid, &now))
+ return UNSUPPORTED;
+ target = timespec_add(now, ns);
+
+ if (clock_nanosleep(clockid, TIMER_ABSTIME, &target, NULL))
+ return UNSUPPORTED;
+ clock_gettime(clockid, &now);
+
+ if (!in_order(target, now))
+ return -1;
+
+ /* Second check reltime */
+ clock_gettime(clockid, &now);
+ rel.tv_sec = 0;
+ rel.tv_nsec = 0;
+ rel = timespec_add(rel, ns);
+ target = timespec_add(now, ns);
+ clock_nanosleep(clockid, 0, &rel, NULL);
+ clock_gettime(clockid, &now);
+
+ if (!in_order(target, now))
+ return -1;
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ long long length;
+ int failed = 0, clockid, ret;
+ for (clockid = CLOCK_REALTIME; clockid < NR_CLOCKIDS; clockid++) {
+
+ /* Skip cputime clockids since nanosleep won't increment cputime */
+ if (clockid == CLOCK_PROCESS_CPUTIME_ID ||
+ clockid == CLOCK_THREAD_CPUTIME_ID ||
+ clockid == CLOCK_HWSPECIFIC)
+ continue;
+
+ printf("Nanosleep %-31s ", clockstring(clockid));
+
+ length = 10;
+ while (length <= (NSEC_PER_SEC * 10)) {
+ ret = nanosleep_test(clockid, length);
+ if (ret == UNSUPPORTED) {
+ printf("[UNSUPPORTED]\n");
+ goto next;
+ }
+ if (ret < 0) {
+ printf("[FAILED]\n");
+ return -1;
+ }
+ length *= 100;
+ }
+ printf("[OK]\n");
+next:
+ ret = 0;
+ }
+ return 0;
+}
--
1.9.1

2015-02-05 06:37:30

by John Stultz

[permalink] [raw]
Subject: [PATCH 4/7] selftest/timers: Add inconsistency-check test from timetests

This adds my inconsistency-test from my timetests suite,
which checks for (single threaded) time inconsistencies
across the various clockids.

Cc: Shuah Khan <[email protected]>
Signed-off-by: John Stultz <[email protected]>
---
tools/testing/selftests/timers/Makefile | 4 +-
.../testing/selftests/timers/inconsistency-check.c | 198 +++++++++++++++++++++
2 files changed, 200 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/timers/inconsistency-check.c

diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile
index 469aaa6..b202888 100644
--- a/tools/testing/selftests/timers/Makefile
+++ b/tools/testing/selftests/timers/Makefile
@@ -1,12 +1,12 @@
CFLAGS += -O3 -Wl,-no-as-needed
LDFLAGS += -lrt -lpthread
-bins = posix_timers nanosleep
+bins = posix_timers nanosleep inconsistency-check

all: ${bins}

run_tests: all
./posix_timers
./nanosleep
-
+ ./inconsistency-check
clean:
rm -f ${bins}
diff --git a/tools/testing/selftests/timers/inconsistency-check.c b/tools/testing/selftests/timers/inconsistency-check.c
new file mode 100644
index 0000000..9b31da3
--- /dev/null
+++ b/tools/testing/selftests/timers/inconsistency-check.c
@@ -0,0 +1,198 @@
+/* Time inconsistency check test
+ * by: john stultz ([email protected])
+ * (C) Copyright IBM 2003, 2004, 2005, 2012
+ * (C) Copyright Linaro Limited 2015
+ * Licensed under the GPLv2
+ *
+ * To build:
+ * $ gcc inconsistency-check.c -o inconsistency-check -lrt
+ *
+ * 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.
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <string.h>
+#include <signal.h>
+
+extern char *optarg;
+
+
+#define CALLS_PER_LOOP 64
+#define NSEC_PER_SEC 1000000000ULL
+
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 1
+#define CLOCK_PROCESS_CPUTIME_ID 2
+#define CLOCK_THREAD_CPUTIME_ID 3
+#define CLOCK_MONOTONIC_RAW 4
+#define CLOCK_REALTIME_COARSE 5
+#define CLOCK_MONOTONIC_COARSE 6
+#define CLOCK_BOOTTIME 7
+#define CLOCK_REALTIME_ALARM 8
+#define CLOCK_BOOTTIME_ALARM 9
+#define CLOCK_HWSPECIFIC 10
+#define CLOCK_TAI 11
+#define NR_CLOCKIDS 12
+
+char *clockstring(int clockid)
+{
+ switch (clockid) {
+ case CLOCK_REALTIME:
+ return "CLOCK_REALTIME";
+ case CLOCK_MONOTONIC:
+ return "CLOCK_MONOTONIC";
+ case CLOCK_PROCESS_CPUTIME_ID:
+ return "CLOCK_PROCESS_CPUTIME_ID";
+ case CLOCK_THREAD_CPUTIME_ID:
+ return "CLOCK_THREAD_CPUTIME_ID";
+ case CLOCK_MONOTONIC_RAW:
+ return "CLOCK_MONOTONIC_RAW";
+ case CLOCK_REALTIME_COARSE:
+ return "CLOCK_REALTIME_COARSE";
+ case CLOCK_MONOTONIC_COARSE:
+ return "CLOCK_MONOTONIC_COARSE";
+ case CLOCK_BOOTTIME:
+ return "CLOCK_BOOTTIME";
+ case CLOCK_REALTIME_ALARM:
+ return "CLOCK_REALTIME_ALARM";
+ case CLOCK_BOOTTIME_ALARM:
+ return "CLOCK_BOOTTIME_ALARM";
+ case CLOCK_TAI:
+ return "CLOCK_TAI";
+ };
+ return "UNKNOWN_CLOCKID";
+}
+
+/* returns 1 if a <= b, 0 otherwise */
+static inline int in_order(struct timespec a, struct timespec b)
+{
+ /* use unsigned to avoid false positives on 2038 rollover */
+ if((unsigned long)a.tv_sec < (unsigned long)b.tv_sec)
+ return 1;
+ if((unsigned long)a.tv_sec > (unsigned long)b.tv_sec)
+ return 0;
+ if(a.tv_nsec > b.tv_nsec)
+ return 0;
+ return 1;
+}
+
+
+
+int consistency_test(int clock_type, unsigned long seconds)
+{
+ struct timespec list[CALLS_PER_LOOP];
+ int i, inconsistent;
+ long now, then;
+ time_t t;
+ char *start_str;
+
+ clock_gettime(clock_type, &list[0]);
+ now = then = list[0].tv_sec;
+
+ /* timestamp start of test */
+ t = time(0);
+ start_str = ctime(&t);
+
+ while(seconds == -1 || now - then < seconds){
+ inconsistent = 0;
+
+ /* Fill list */
+ for(i=0; i < CALLS_PER_LOOP; i++)
+ clock_gettime(clock_type, &list[i]);
+
+ /* Check for inconsistencies */
+ for(i=0; i < CALLS_PER_LOOP-1; i++)
+ if(!in_order(list[i],list[i+1]))
+ inconsistent = i;
+
+ /* display inconsistency */
+ if(inconsistent){
+ unsigned long long delta;
+ printf("\%s\n", start_str);
+ for(i=0; i < CALLS_PER_LOOP; i++){
+ if(i == inconsistent)
+ printf("--------------------\n");
+ printf("%lu:%lu\n",list[i].tv_sec,
+ list[i].tv_nsec);
+ if(i == inconsistent + 1 )
+ printf("--------------------\n");
+ }
+ delta = list[inconsistent].tv_sec*NSEC_PER_SEC;
+ delta += list[inconsistent].tv_nsec;
+ delta -= list[inconsistent+1].tv_sec*NSEC_PER_SEC;
+ delta -= list[inconsistent+1].tv_nsec;
+ printf("Delta: %llu ns\n", delta);
+ fflush(0);
+ /* timestamp inconsistency*/
+ t = time(0);
+ printf("%s\n", ctime(&t));
+ printf("[FAILED]\n");
+ return -1;
+ }
+ now = list[0].tv_sec;
+ }
+ printf("[OK]\n");
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int clockid, userclock=-1, maxclocks, opt;
+ int runtime = 30;
+ struct timespec ts;
+
+ /* Process arguments */
+ while ((opt = getopt(argc, argv, "t:c:"))!=-1) {
+ switch(opt) {
+ case 't':
+ runtime = atoi(optarg);
+ break;
+ case 'c':
+ userclock = atoi(optarg);
+ maxclocks = userclock+1;
+ break;
+ default:
+ printf("Usage: %s [-t <secs>] [-c <clockid>]\n", argv[0]);
+ printf(" -t: Number of seconds to run\n");
+ printf(" -c: clockid to use (default, all clockids)\n");
+ exit(-1);
+ }
+ }
+
+ setbuf(stdout, NULL);
+
+ if (userclock == -1) {
+ userclock = CLOCK_REALTIME;
+ maxclocks = NR_CLOCKIDS;
+ }
+
+ for (clockid=userclock; clockid < maxclocks; clockid++) {
+
+ if (clockid == CLOCK_HWSPECIFIC)
+ continue;
+
+ if (!clock_gettime(clockid, &ts)) {
+ printf("Consistent %-30s ", clockstring(clockid));
+ if (consistency_test(clockid, runtime))
+ return -1;
+ }
+ }
+
+
+ return 0;
+}
--
1.9.1

2015-02-05 06:38:19

by John Stultz

[permalink] [raw]
Subject: [PATCH 5/7] selftest/timers: Add nsleep-lat test from timetest suite

Adds my nanosleep latency test from the timetest suite.
This checks to make sure we don't see "unreasonable"
latencies (> 40ms) when calling nanosleep.

Cc: Shuah Khan <[email protected]>
Signed-off-by: John Stultz <[email protected]>
---
tools/testing/selftests/timers/Makefile | 3 +-
tools/testing/selftests/timers/nsleep-lat.c | 176 ++++++++++++++++++++++++++++
2 files changed, 178 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/timers/nsleep-lat.c

diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile
index b202888..28c2184 100644
--- a/tools/testing/selftests/timers/Makefile
+++ b/tools/testing/selftests/timers/Makefile
@@ -1,12 +1,13 @@
CFLAGS += -O3 -Wl,-no-as-needed
LDFLAGS += -lrt -lpthread
-bins = posix_timers nanosleep inconsistency-check
+bins = posix_timers nanosleep inconsistency-check nsleep-lat

all: ${bins}

run_tests: all
./posix_timers
./nanosleep
+ ./nsleep-lat
./inconsistency-check
clean:
rm -f ${bins}
diff --git a/tools/testing/selftests/timers/nsleep-lat.c b/tools/testing/selftests/timers/nsleep-lat.c
new file mode 100644
index 0000000..fe94f3af
--- /dev/null
+++ b/tools/testing/selftests/timers/nsleep-lat.c
@@ -0,0 +1,176 @@
+/* Measure nanosleep timer latency
+ * by: john stultz ([email protected])
+ * (C) Copyright Linaro 2013
+ * Licensed under the GPLv2
+ *
+ * To build:
+ * $ gcc nsleep-lat.c -o nsleep-lat -lrt
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <string.h>
+#include <signal.h>
+
+#define NSEC_PER_SEC 1000000000ULL
+
+#define UNRESONABLE_LATENCY 40000000 /* 40ms in nanosecs */
+
+
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 1
+#define CLOCK_PROCESS_CPUTIME_ID 2
+#define CLOCK_THREAD_CPUTIME_ID 3
+#define CLOCK_MONOTONIC_RAW 4
+#define CLOCK_REALTIME_COARSE 5
+#define CLOCK_MONOTONIC_COARSE 6
+#define CLOCK_BOOTTIME 7
+#define CLOCK_REALTIME_ALARM 8
+#define CLOCK_BOOTTIME_ALARM 9
+#define CLOCK_HWSPECIFIC 10
+#define CLOCK_TAI 11
+#define NR_CLOCKIDS 12
+
+#define UNSUPPORTED 0xf00f
+
+char *clockstring(int clockid)
+{
+ switch (clockid) {
+ case CLOCK_REALTIME:
+ return "CLOCK_REALTIME";
+ case CLOCK_MONOTONIC:
+ return "CLOCK_MONOTONIC";
+ case CLOCK_PROCESS_CPUTIME_ID:
+ return "CLOCK_PROCESS_CPUTIME_ID";
+ case CLOCK_THREAD_CPUTIME_ID:
+ return "CLOCK_THREAD_CPUTIME_ID";
+ case CLOCK_MONOTONIC_RAW:
+ return "CLOCK_MONOTONIC_RAW";
+ case CLOCK_REALTIME_COARSE:
+ return "CLOCK_REALTIME_COARSE";
+ case CLOCK_MONOTONIC_COARSE:
+ return "CLOCK_MONOTONIC_COARSE";
+ case CLOCK_BOOTTIME:
+ return "CLOCK_BOOTTIME";
+ case CLOCK_REALTIME_ALARM:
+ return "CLOCK_REALTIME_ALARM";
+ case CLOCK_BOOTTIME_ALARM:
+ return "CLOCK_BOOTTIME_ALARM";
+ case CLOCK_TAI:
+ return "CLOCK_TAI";
+ };
+ return "UNKNOWN_CLOCKID";
+}
+
+struct timespec timespec_add(struct timespec ts, unsigned long long ns)
+{
+ ts.tv_nsec += ns;
+ while(ts.tv_nsec >= NSEC_PER_SEC) {
+ ts.tv_nsec -= NSEC_PER_SEC;
+ ts.tv_sec++;
+ }
+ return ts;
+}
+
+
+long long timespec_sub(struct timespec a, struct timespec b)
+{
+ long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec;
+ ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec;
+ return ret;
+}
+
+int nanosleep_lat_test(int clockid, long long ns)
+{
+ struct timespec start, end, target;
+ long long latency = 0;
+ int i, count;
+
+ target.tv_sec = ns/NSEC_PER_SEC;
+ target.tv_nsec = ns%NSEC_PER_SEC;
+
+ if (clock_gettime(clockid, &start))
+ return UNSUPPORTED;
+ if (clock_nanosleep(clockid, 0, &target, NULL))
+ return UNSUPPORTED;
+
+ count = 10;
+
+ /* First check relative latency */
+ clock_gettime(clockid, &start);
+ for (i=0;i<count;i++)
+ clock_nanosleep(clockid, 0, &target, NULL);
+ clock_gettime(clockid, &end);
+
+ if (((timespec_sub(start, end)/count)-ns) > UNRESONABLE_LATENCY) {
+ printf("Large rel latency: %lld ns :", (timespec_sub(start, end)/count)-ns);
+ return -1;
+ }
+
+ /* Next check absolute latency */
+ for (i=0;i<count;i++) {
+ clock_gettime(clockid, &start);
+ target = timespec_add(start, ns);
+ clock_nanosleep(clockid, TIMER_ABSTIME, &target, NULL);
+ clock_gettime(clockid, &end);
+ latency += timespec_sub(target, end);
+ }
+
+ if (latency/count > UNRESONABLE_LATENCY) {
+ printf("Large abs latency: %lld ns :", latency/count);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+int main(int argc, char** argv)
+{
+ long long length;
+ int failed = 0, clockid, ret;
+ for (clockid = CLOCK_REALTIME; clockid < NR_CLOCKIDS; clockid++) {
+
+ /* Skip cputime clockids since nanosleep won't increment cputime */
+ if (clockid == CLOCK_PROCESS_CPUTIME_ID ||
+ clockid == CLOCK_THREAD_CPUTIME_ID ||
+ clockid == CLOCK_HWSPECIFIC)
+ continue;
+
+ printf("nsleep latency %-31s ", clockstring(clockid));
+
+ length = 10;
+ while (length <= (NSEC_PER_SEC * 10)) {
+ ret = nanosleep_lat_test(clockid, length);
+ if (ret)
+ break;
+ length *= 100;
+
+ }
+
+ if (ret == UNSUPPORTED) {
+ printf("[UNSUPPORTED]\n");
+ continue;
+ }
+ if (ret < 0) {
+ printf("[FAILED]\n");
+ return -1;
+ }
+ printf("[OK]\n");
+ }
+ return 0;
+}
--
1.9.1

2015-02-05 06:37:33

by John Stultz

[permalink] [raw]
Subject: [PATCH 6/7] selftests/timers: Add clock skew estimation test from timetest suite

This adds my clock skew estimation test from the timetest suite.
It measures the drift between CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW
and compares it with the current frequency value from adjtimex.

It sometimes can trigger false failures when ntpd isn't in a
steady state, but its a useful too when doing adjtimex testing.

Cc: Shuah Khan <[email protected]>
Signed-off-by: John Stultz <[email protected]>
---
tools/testing/selftests/timers/Makefile | 3 +-
tools/testing/selftests/timers/raw_skew.c | 137 ++++++++++++++++++++++++++++++
2 files changed, 139 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/timers/raw_skew.c

diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile
index 28c2184..3f41d18 100644
--- a/tools/testing/selftests/timers/Makefile
+++ b/tools/testing/selftests/timers/Makefile
@@ -1,6 +1,6 @@
CFLAGS += -O3 -Wl,-no-as-needed
LDFLAGS += -lrt -lpthread
-bins = posix_timers nanosleep inconsistency-check nsleep-lat
+bins = posix_timers nanosleep inconsistency-check nsleep-lat raw_skew

all: ${bins}

@@ -9,5 +9,6 @@ run_tests: all
./nanosleep
./nsleep-lat
./inconsistency-check
+ ./raw_skew
clean:
rm -f ${bins}
diff --git a/tools/testing/selftests/timers/raw_skew.c b/tools/testing/selftests/timers/raw_skew.c
new file mode 100644
index 0000000..dc971e5
--- /dev/null
+++ b/tools/testing/selftests/timers/raw_skew.c
@@ -0,0 +1,137 @@
+/* CLOCK_MONOTONIC vs CLOCK_MONOTONIC_RAW skew test
+ * by: john stultz ([email protected])
+ * John Stultz <[email protected]>
+ * (C) Copyright IBM 2012
+ * (C) Copyright Linaro Limited 2015
+ * Licensed under the GPLv2
+ *
+ * To build:
+ * $ gcc raw_skew.c -o raw_skew -lrt
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <time.h>
+
+#define CLOCK_MONOTONIC_RAW 4
+#define NSEC_PER_SEC 1000000000LL
+
+#define shift_right(x, s) ({ \
+ __typeof__(x) __x = (x); \
+ __typeof__(s) __s = (s); \
+ __x < 0 ? -(-__x >> __s) : __x >> __s; \
+})
+
+long long llabs(long long val)
+{
+ if (val<0)
+ val = -val;
+ return val;
+}
+
+unsigned long long ts_to_nsec(struct timespec ts)
+{
+ return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
+}
+
+struct timespec nsec_to_ts(long long ns)
+{
+ struct timespec ts;
+ ts.tv_sec = ns/NSEC_PER_SEC;
+ ts.tv_nsec = ns%NSEC_PER_SEC;
+ return ts;
+}
+
+long long diff_timespec(struct timespec start, struct timespec end)
+{
+ long long start_ns, end_ns;
+
+ start_ns = ts_to_nsec(start);
+ end_ns = ts_to_nsec(end);
+ return end_ns - start_ns;
+}
+
+void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
+{
+ struct timespec start, mid, end;
+ long long diff = 0, tmp;
+ int i;
+ for(i=0; i < 3; i++) {
+ long long newdiff;
+
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
+ clock_gettime(CLOCK_MONOTONIC, &end);
+
+ newdiff = diff_timespec(start,end);
+ if (diff == 0 || newdiff < diff) {
+ diff = newdiff;
+ *raw = mid;
+ tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
+ *mon = nsec_to_ts(tmp);
+ }
+ }
+}
+
+int main(int argv, char** argc)
+{
+ struct timespec mon, raw, bound, start, end;
+ long long delta1, delta2, interval, eppm, ppm;
+ struct timex tx1,tx2;
+
+ setbuf(stdout, NULL);
+
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
+ printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
+ return -1;
+ }
+
+ tx1.modes = 0;
+ adjtimex(&tx1);
+ get_monotonic_and_raw(&mon, &raw);
+ start = mon;
+ delta1 = diff_timespec(mon, raw);
+
+ if (tx1.offset)
+ printf("WARNING: ADJ_OFFSET in progress, this will cause inaccurate results\n");
+
+ printf("Estimating clock drift: ");
+ sleep(120);
+
+ get_monotonic_and_raw(&mon, &raw);
+ end = mon;
+ tx2.modes = 0;
+ adjtimex(&tx2);
+ delta2 = diff_timespec(mon, raw);
+
+ interval = diff_timespec(start,end);
+
+ /* calculate measured ppm between MONOTONIC and MONOTONIC_RAW */
+ eppm = ((delta2-delta1)*NSEC_PER_SEC)/interval;
+ eppm = -eppm;
+ printf("%lld.%i(est)", eppm/1000, abs((int)(eppm%1000)));
+
+ /* Avg the two actual freq samples adjtimex gave us */
+ ppm = (tx1.freq + tx2.freq) * 1000 / 2;
+ ppm = (long long)tx1.freq * 1000;
+ ppm = shift_right(ppm, 16);
+ printf(" %lld.%i(act)", ppm/1000, abs((int)(ppm%1000)));
+
+ if (llabs(eppm - ppm) > 1000){
+ printf(" [FAILED]\n");
+ return -1;
+ }
+ printf(" [OK]\n");
+ return 0;
+}
--
1.9.1

2015-02-05 06:37:51

by John Stultz

[permalink] [raw]
Subject: [PATCH 7/7] selftest/timers: Add set-timer-lat test from timetest suite

Add my set-timer-lat test from the timetest suite. This
test checks the latency from set_timer and reports if
any are unreasonable (>40ms).

Cc: Shuah Khan <[email protected]>
Signed-off-by: John Stultz <[email protected]>
---
tools/testing/selftests/timers/Makefile | 5 +-
tools/testing/selftests/timers/set-timer-lat.c | 198 +++++++++++++++++++++++++
2 files changed, 202 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/timers/set-timer-lat.c

diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile
index 3f41d18..a1236cc 100644
--- a/tools/testing/selftests/timers/Makefile
+++ b/tools/testing/selftests/timers/Makefile
@@ -1,6 +1,7 @@
CFLAGS += -O3 -Wl,-no-as-needed
LDFLAGS += -lrt -lpthread
-bins = posix_timers nanosleep inconsistency-check nsleep-lat raw_skew
+bins = posix_timers nanosleep inconsistency-check nsleep-lat raw_skew \
+ set-timer-lat

all: ${bins}

@@ -8,7 +9,9 @@ run_tests: all
./posix_timers
./nanosleep
./nsleep-lat
+ ./set-timer-lat
./inconsistency-check
./raw_skew
+
clean:
rm -f ${bins}
diff --git a/tools/testing/selftests/timers/set-timer-lat.c b/tools/testing/selftests/timers/set-timer-lat.c
new file mode 100644
index 0000000..e8aff2c
--- /dev/null
+++ b/tools/testing/selftests/timers/set-timer-lat.c
@@ -0,0 +1,198 @@
+/* set_timer latency test
+ * John Stultz ([email protected])
+ * (C) Copyright Linaro 2014
+ * Licensed under the GPLv2
+ *
+ * This test makes sure the set_timer api is correct
+ *
+ * To build:
+ * $ gcc set-timer-lat.c -o set-timer-lat -lrt
+ *
+ * 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.
+ */
+
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 1
+#define CLOCK_PROCESS_CPUTIME_ID 2
+#define CLOCK_THREAD_CPUTIME_ID 3
+#define CLOCK_MONOTONIC_RAW 4
+#define CLOCK_REALTIME_COARSE 5
+#define CLOCK_MONOTONIC_COARSE 6
+#define CLOCK_BOOTTIME 7
+#define CLOCK_REALTIME_ALARM 8
+#define CLOCK_BOOTTIME_ALARM 9
+#define CLOCK_HWSPECIFIC 10
+#define CLOCK_TAI 11
+#define NR_CLOCKIDS 12
+
+
+#define NSEC_PER_SEC 1000000000ULL
+#define UNRESONABLE_LATENCY 40000000 /* 40ms in nanosecs */
+
+#define TIMER_SECS 3
+int alarmcount;
+int clock_id;
+struct timespec start_time;
+long long max_latency_ns;
+
+char *clockstring(int clockid)
+{
+ switch (clockid) {
+ case CLOCK_REALTIME:
+ return "CLOCK_REALTIME";
+ case CLOCK_MONOTONIC:
+ return "CLOCK_MONOTONIC";
+ case CLOCK_PROCESS_CPUTIME_ID:
+ return "CLOCK_PROCESS_CPUTIME_ID";
+ case CLOCK_THREAD_CPUTIME_ID:
+ return "CLOCK_THREAD_CPUTIME_ID";
+ case CLOCK_MONOTONIC_RAW:
+ return "CLOCK_MONOTONIC_RAW";
+ case CLOCK_REALTIME_COARSE:
+ return "CLOCK_REALTIME_COARSE";
+ case CLOCK_MONOTONIC_COARSE:
+ return "CLOCK_MONOTONIC_COARSE";
+ case CLOCK_BOOTTIME:
+ return "CLOCK_BOOTTIME";
+ case CLOCK_REALTIME_ALARM:
+ return "CLOCK_REALTIME_ALARM";
+ case CLOCK_BOOTTIME_ALARM:
+ return "CLOCK_BOOTTIME_ALARM";
+ case CLOCK_TAI:
+ return "CLOCK_TAI";
+ };
+ return "UNKNOWN_CLOCKID";
+}
+
+
+long long timespec_sub(struct timespec a, struct timespec b)
+{
+ long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec;
+ ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec;
+ return ret;
+}
+
+
+void sigalarm(int signo)
+{
+ long long delta_ns;
+ struct timespec ts;
+
+ clock_gettime(clock_id, &ts);
+ alarmcount++;
+
+ delta_ns = timespec_sub(start_time, ts);
+ delta_ns -= NSEC_PER_SEC * TIMER_SECS * alarmcount;
+
+ if (delta_ns < 0)
+ printf("%s timer fired early: FAIL\n", clockstring(clock_id));
+
+ if (delta_ns > max_latency_ns)
+ max_latency_ns = delta_ns;
+}
+
+int do_timer(int clock_id, int flags)
+{
+ struct sigevent se;
+ timer_t tm1;
+ struct itimerspec its1, its2;
+ int err;
+
+ /* Set up timer: */
+ memset(&se, 0, sizeof(se));
+ se.sigev_notify = SIGEV_SIGNAL;
+ se.sigev_signo = SIGRTMAX;
+ se.sigev_value.sival_int = 0;
+
+ max_latency_ns = 0;
+ alarmcount = 0;
+
+ err = timer_create(clock_id, &se, &tm1);
+ if (err) {
+ printf("%s - timer_create() failed\n", clockstring(clock_id));
+ return -1;
+ }
+
+ clock_gettime(clock_id, &start_time);
+ if (flags) {
+ its1.it_value = start_time;
+ its1.it_value.tv_sec += TIMER_SECS;
+ } else {
+ its1.it_value.tv_sec = TIMER_SECS;
+ its1.it_value.tv_nsec = 0;
+ }
+ its1.it_interval.tv_sec = TIMER_SECS;
+ its1.it_interval.tv_nsec = 0;
+
+ err = timer_settime(tm1, flags, &its1, &its2);
+ if (err) {
+ printf("%s - timer_settime() failed\n", clockstring(clock_id));
+ return -1;
+ }
+ while(alarmcount < 5)
+ sleep(1);
+
+ printf("%-22s %s max latency: %10lld ns : ",
+ clockstring(clock_id),
+ flags ? "ABSTIME":"RELTIME",
+ max_latency_ns);
+
+ timer_delete(tm1);
+ if (max_latency_ns < UNRESONABLE_LATENCY){
+ printf("[OK]\n");
+ return 0;
+ } else {
+ printf("[FAILED]\n");
+ return -1;
+ }
+
+}
+
+int main(void)
+{
+ struct timespec ts;
+ struct sigaction act;
+ sigset_t sigmask;
+ int signum = SIGRTMAX;
+ int ret = 0;
+
+ /* Set up signal handler: */
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = sigalarm;
+ sigaction(signum, &act, NULL);
+
+ printf("Setting timers for every %i seconds\n", TIMER_SECS);
+ for (clock_id = 0; clock_id < NR_CLOCKIDS; clock_id++) {
+
+ if ((clock_id == CLOCK_PROCESS_CPUTIME_ID) ||
+ (clock_id == CLOCK_THREAD_CPUTIME_ID) ||
+ (clock_id == CLOCK_MONOTONIC_RAW) ||
+ (clock_id == CLOCK_REALTIME_COARSE) ||
+ (clock_id == CLOCK_MONOTONIC_COARSE) ||
+ (clock_id == CLOCK_HWSPECIFIC))
+ continue;
+
+ ret |= do_timer(clock_id, TIMER_ABSTIME);
+ ret |= do_timer(clock_id, 0);
+ }
+
+}
+
--
1.9.1

2015-02-05 06:39:55

by John Stultz

[permalink] [raw]
Subject: Re: [PATCH 1/7] selftests/timers: Cleanup Makefile to make it easier to add future tests

On Wed, Feb 4, 2015 at 10:37 PM, John Stultz <[email protected]> wrote:
> Try to streamline the makefile so its easier to add timer/timekeeping
> tests.
>
> Cc: Shuah Khan <[email protected]>

Drat.. I forgot to refresh the patchset after adding to the CC list.
Hopefully folks can/will find the thread, but I'll resend next week
sometime if I don't hear any feedback.

thanks
-john

2015-02-05 22:13:12

by Shuah Khan

[permalink] [raw]
Subject: Re: [RFC][PATCH 0/7] Start adding timetests to selftest

On 02/04/2015 11:37 PM, John Stultz wrote:
> I've maintained a suite of timekeeping and timer tests for awhile
> here: https://github.com/johnstultz-work/timetests
>
> I've been meaning to get them reworked and submitted into the
> selftest infrastructure, but haven't had much time recently.
>
> I'm going to have a long flight coming up so I figured I'd
> get started and get some initial feedback.
>
> This series adds all the non-destructive tests from my
> timetests suite. The suite has more tests which actually tweak
> time state and validate things behave as expected, but I wasn't
> sure how to integrate that into the selftest infrastructure
> as I'm not sure everyone would want the tests to be mucking
> with their system time. (Currently my plan is to add a
> script similar to the runall.sh in timetests, so folks
> can go in and manually run it if they're feeling daring)

Providing a way to run destructive tests as a special
option is great. Non-destructive tests can be run in
default mode.

Thanks for the tests. Looks good to me. I am in the middle
of review at the moment, will send you comments if any.

thanks,
-- Shuah

--
Shuah Khan
Sr. Linux Kernel Developer
Open Source Innovation Group
Samsung Research America (Silicon Valley)
[email protected] | (970) 217-8978

2015-02-05 23:27:59

by John Stultz

[permalink] [raw]
Subject: Re: [RFC][PATCH 0/7] Start adding timetests to selftest

On Thu, Feb 5, 2015 at 2:13 PM, Shuah Khan <[email protected]> wrote:
> On 02/04/2015 11:37 PM, John Stultz wrote:
>> I've maintained a suite of timekeeping and timer tests for awhile
>> here: https://github.com/johnstultz-work/timetests
>>
>> I've been meaning to get them reworked and submitted into the
>> selftest infrastructure, but haven't had much time recently.
>>
>> I'm going to have a long flight coming up so I figured I'd
>> get started and get some initial feedback.
>>
>> This series adds all the non-destructive tests from my
>> timetests suite. The suite has more tests which actually tweak
>> time state and validate things behave as expected, but I wasn't
>> sure how to integrate that into the selftest infrastructure
>> as I'm not sure everyone would want the tests to be mucking
>> with their system time. (Currently my plan is to add a
>> script similar to the runall.sh in timetests, so folks
>> can go in and manually run it if they're feeling daring)
>
> Providing a way to run destructive tests as a special
> option is great. Non-destructive tests can be run in
> default mode.
>
> Thanks for the tests. Looks good to me. I am in the middle
> of review at the moment, will send you comments if any.

Yea, I realized after sending it that I didn't checkpatch things and
there's a bunch of style issues to be resolved. I've already fixed up
most of those, and will re-submit next week.

If you have any other feedback (other then white-space/style quirks),
do let me know and I'll also try to add that in.

I know its close to the merge window, and I'm in no rush to get these
in for 3.20, but wanted to send them out for initial thoughts now
because doing this has been on my list since kernel summit and was
finally able to steal a few evening hours to start and hopefully get
some momentum going on it. :)

thanks
-john