diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-14 18:17:12 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-14 18:17:12 -0400 |
| commit | 5d89d9f502f9c33ed0270d716f238429861e1942 (patch) | |
| tree | 6a353640e6836ca465f2dcf00323a7f8a793ae59 /tools/testing | |
| parent | 50cff89837a43a7c62ac080de9742a298d6418b3 (diff) | |
| parent | fecf861e765b2f9ce1a0487c3940afaed80ef7a8 (diff) | |
Merge tag 'linux-kselftest-4.9-rc1-update' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
Pull kselftest updates from Shuah Khan:
"This update consists of:
- Fixes and improvements to existing tests
- Moving code from Documentation to selftests, samples, and tools:
* Moves dnotify_test, prctl, ptp, vDSO, ia64, watchdog, and
networking tests from Documentation to selftests.
* Moves mic/mpssd, misc-devices/mei, timers, watchdog, auxdisplay,
and blackfin examples from Documentation to samples.
* Moves accounting, laptops/dslm, and pcmcia/crc32hash tools from
Documentation to tools.
* Deletes BUILD_DOCSRC and its dependencies"
* tag 'linux-kselftest-4.9-rc1-update' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: (21 commits)
selftests/futex: Check ANSI terminal color support
Doc: update 00-INDEX files to reflect the runnable code move
samples: move blackfin gptimers-example from Documentation
tools: move pcmcia crc32hash tool from Documentation
tools: move laptops dslm tool from Documentation
tools: move accounting tool from Documentation
samples: move auxdisplay example code from Documentation
samples: move watchdog example code from Documentation
samples: move timers example code from Documentation
samples: move misc-devices/mei example code from Documentation
samples: move mic/mpssd example code from Documentation
selftests: Move networking/timestamping from Documentation
selftests: move watchdog tests from Documentation/watchdog
selftests: move ia64 tests from Documentation/ia64
selftests: move vDSO tests from Documentation/vDSO
selftests: move ptp tests from Documentation/ptp
selftests: move prctl tests from Documentation/prctl
selftests: move dnotify_test from Documentation/filesystems
selftests/timers: Add missing error code assignment before test
selftests/zram: replace ZRAM_LZ4_COMPRESS
...
Diffstat (limited to 'tools/testing')
32 files changed, 2997 insertions, 5 deletions
diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore new file mode 100644 index 000000000000..31d6e426b6d4 --- /dev/null +++ b/tools/testing/selftests/filesystems/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| dnotify_test | |||
diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile new file mode 100644 index 000000000000..0ab11307b414 --- /dev/null +++ b/tools/testing/selftests/filesystems/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | TEST_PROGS := dnotify_test | ||
| 2 | all: $(TEST_PROGS) | ||
| 3 | |||
| 4 | include ../lib.mk | ||
| 5 | |||
| 6 | clean: | ||
| 7 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/filesystems/dnotify_test.c b/tools/testing/selftests/filesystems/dnotify_test.c new file mode 100644 index 000000000000..8b37b4a1e18d --- /dev/null +++ b/tools/testing/selftests/filesystems/dnotify_test.c | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #define _GNU_SOURCE /* needed to get the defines */ | ||
| 2 | #include <fcntl.h> /* in glibc 2.2 this has the needed | ||
| 3 | values defined */ | ||
| 4 | #include <signal.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <unistd.h> | ||
| 7 | |||
| 8 | static volatile int event_fd; | ||
| 9 | |||
| 10 | static void handler(int sig, siginfo_t *si, void *data) | ||
| 11 | { | ||
| 12 | event_fd = si->si_fd; | ||
| 13 | } | ||
| 14 | |||
| 15 | int main(void) | ||
| 16 | { | ||
| 17 | struct sigaction act; | ||
| 18 | int fd; | ||
| 19 | |||
| 20 | act.sa_sigaction = handler; | ||
| 21 | sigemptyset(&act.sa_mask); | ||
| 22 | act.sa_flags = SA_SIGINFO; | ||
| 23 | sigaction(SIGRTMIN + 1, &act, NULL); | ||
| 24 | |||
| 25 | fd = open(".", O_RDONLY); | ||
| 26 | fcntl(fd, F_SETSIG, SIGRTMIN + 1); | ||
| 27 | fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT); | ||
| 28 | /* we will now be notified if any of the files | ||
| 29 | in "." is modified or new files are created */ | ||
| 30 | while (1) { | ||
| 31 | pause(); | ||
| 32 | printf("Got event on fd=%d\n", event_fd); | ||
| 33 | } | ||
| 34 | } | ||
diff --git a/tools/testing/selftests/futex/functional/run.sh b/tools/testing/selftests/futex/functional/run.sh index e87dbe2a0b0d..7ff002eed624 100755 --- a/tools/testing/selftests/futex/functional/run.sh +++ b/tools/testing/selftests/futex/functional/run.sh | |||
| @@ -24,7 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | # Test for a color capable console | 25 | # Test for a color capable console |
| 26 | if [ -z "$USE_COLOR" ]; then | 26 | if [ -z "$USE_COLOR" ]; then |
| 27 | tput setf 7 | 27 | tput setf 7 || tput setaf 7 |
| 28 | if [ $? -eq 0 ]; then | 28 | if [ $? -eq 0 ]; then |
| 29 | USE_COLOR=1 | 29 | USE_COLOR=1 |
| 30 | tput sgr0 | 30 | tput sgr0 |
diff --git a/tools/testing/selftests/futex/run.sh b/tools/testing/selftests/futex/run.sh index 4126312ad64e..88bcb1767362 100755 --- a/tools/testing/selftests/futex/run.sh +++ b/tools/testing/selftests/futex/run.sh | |||
| @@ -23,7 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | # Test for a color capable shell and pass the result to the subdir scripts | 24 | # Test for a color capable shell and pass the result to the subdir scripts |
| 25 | USE_COLOR=0 | 25 | USE_COLOR=0 |
| 26 | tput setf 7 | 26 | tput setf 7 || tput setaf 7 |
| 27 | if [ $? -eq 0 ]; then | 27 | if [ $? -eq 0 ]; then |
| 28 | USE_COLOR=1 | 28 | USE_COLOR=1 |
| 29 | tput sgr0 | 29 | tput sgr0 |
diff --git a/tools/testing/selftests/ia64/.gitignore b/tools/testing/selftests/ia64/.gitignore new file mode 100644 index 000000000000..ab806edc8732 --- /dev/null +++ b/tools/testing/selftests/ia64/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| aliasing-test | |||
diff --git a/tools/testing/selftests/ia64/Makefile b/tools/testing/selftests/ia64/Makefile new file mode 100644 index 000000000000..2b3de2d3e945 --- /dev/null +++ b/tools/testing/selftests/ia64/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | TEST_PROGS := aliasing-test | ||
| 2 | |||
| 3 | all: $(TEST_PROGS) | ||
| 4 | |||
| 5 | include ../lib.mk | ||
| 6 | |||
| 7 | clean: | ||
| 8 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/ia64/aliasing-test.c b/tools/testing/selftests/ia64/aliasing-test.c new file mode 100644 index 000000000000..62a190d45f38 --- /dev/null +++ b/tools/testing/selftests/ia64/aliasing-test.c | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | /* | ||
| 2 | * Exercise /dev/mem mmap cases that have been troublesome in the past | ||
| 3 | * | ||
| 4 | * (c) Copyright 2007 Hewlett-Packard Development Company, L.P. | ||
| 5 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <stdlib.h> | ||
| 13 | #include <stdio.h> | ||
| 14 | #include <sys/types.h> | ||
| 15 | #include <dirent.h> | ||
| 16 | #include <fcntl.h> | ||
| 17 | #include <fnmatch.h> | ||
| 18 | #include <string.h> | ||
| 19 | #include <sys/ioctl.h> | ||
| 20 | #include <sys/mman.h> | ||
| 21 | #include <sys/stat.h> | ||
| 22 | #include <unistd.h> | ||
| 23 | #include <linux/pci.h> | ||
| 24 | |||
| 25 | int sum; | ||
| 26 | |||
| 27 | static int map_mem(char *path, off_t offset, size_t length, int touch) | ||
| 28 | { | ||
| 29 | int fd, rc; | ||
| 30 | void *addr; | ||
| 31 | int *c; | ||
| 32 | |||
| 33 | fd = open(path, O_RDWR); | ||
| 34 | if (fd == -1) { | ||
| 35 | perror(path); | ||
| 36 | return -1; | ||
| 37 | } | ||
| 38 | |||
| 39 | if (fnmatch("/proc/bus/pci/*", path, 0) == 0) { | ||
| 40 | rc = ioctl(fd, PCIIOC_MMAP_IS_MEM); | ||
| 41 | if (rc == -1) | ||
| 42 | perror("PCIIOC_MMAP_IS_MEM ioctl"); | ||
| 43 | } | ||
| 44 | |||
| 45 | addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); | ||
| 46 | if (addr == MAP_FAILED) | ||
| 47 | return 1; | ||
| 48 | |||
| 49 | if (touch) { | ||
| 50 | c = (int *) addr; | ||
| 51 | while (c < (int *) (addr + length)) | ||
| 52 | sum += *c++; | ||
| 53 | } | ||
| 54 | |||
| 55 | rc = munmap(addr, length); | ||
| 56 | if (rc == -1) { | ||
| 57 | perror("munmap"); | ||
| 58 | return -1; | ||
| 59 | } | ||
| 60 | |||
| 61 | close(fd); | ||
| 62 | return 0; | ||
| 63 | } | ||
| 64 | |||
| 65 | static int scan_tree(char *path, char *file, off_t offset, size_t length, int touch) | ||
| 66 | { | ||
| 67 | struct dirent **namelist; | ||
| 68 | char *name, *path2; | ||
| 69 | int i, n, r, rc = 0, result = 0; | ||
| 70 | struct stat buf; | ||
| 71 | |||
| 72 | n = scandir(path, &namelist, 0, alphasort); | ||
| 73 | if (n < 0) { | ||
| 74 | perror("scandir"); | ||
| 75 | return -1; | ||
| 76 | } | ||
| 77 | |||
| 78 | for (i = 0; i < n; i++) { | ||
| 79 | name = namelist[i]->d_name; | ||
| 80 | |||
| 81 | if (fnmatch(".", name, 0) == 0) | ||
| 82 | goto skip; | ||
| 83 | if (fnmatch("..", name, 0) == 0) | ||
| 84 | goto skip; | ||
| 85 | |||
| 86 | path2 = malloc(strlen(path) + strlen(name) + 3); | ||
| 87 | strcpy(path2, path); | ||
| 88 | strcat(path2, "/"); | ||
| 89 | strcat(path2, name); | ||
| 90 | |||
| 91 | if (fnmatch(file, name, 0) == 0) { | ||
| 92 | rc = map_mem(path2, offset, length, touch); | ||
| 93 | if (rc == 0) | ||
| 94 | fprintf(stderr, "PASS: %s 0x%lx-0x%lx is %s\n", path2, offset, offset + length, touch ? "readable" : "mappable"); | ||
| 95 | else if (rc > 0) | ||
| 96 | fprintf(stderr, "PASS: %s 0x%lx-0x%lx not mappable\n", path2, offset, offset + length); | ||
| 97 | else { | ||
| 98 | fprintf(stderr, "FAIL: %s 0x%lx-0x%lx not accessible\n", path2, offset, offset + length); | ||
| 99 | return rc; | ||
| 100 | } | ||
| 101 | } else { | ||
| 102 | r = lstat(path2, &buf); | ||
| 103 | if (r == 0 && S_ISDIR(buf.st_mode)) { | ||
| 104 | rc = scan_tree(path2, file, offset, length, touch); | ||
| 105 | if (rc < 0) | ||
| 106 | return rc; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | result |= rc; | ||
| 111 | free(path2); | ||
| 112 | |||
| 113 | skip: | ||
| 114 | free(namelist[i]); | ||
| 115 | } | ||
| 116 | free(namelist); | ||
| 117 | return result; | ||
| 118 | } | ||
| 119 | |||
| 120 | char buf[1024]; | ||
| 121 | |||
| 122 | static int read_rom(char *path) | ||
| 123 | { | ||
| 124 | int fd, rc; | ||
| 125 | size_t size = 0; | ||
| 126 | |||
| 127 | fd = open(path, O_RDWR); | ||
| 128 | if (fd == -1) { | ||
| 129 | perror(path); | ||
| 130 | return -1; | ||
| 131 | } | ||
| 132 | |||
| 133 | rc = write(fd, "1", 2); | ||
| 134 | if (rc <= 0) { | ||
| 135 | close(fd); | ||
| 136 | perror("write"); | ||
| 137 | return -1; | ||
| 138 | } | ||
| 139 | |||
| 140 | do { | ||
| 141 | rc = read(fd, buf, sizeof(buf)); | ||
| 142 | if (rc > 0) | ||
| 143 | size += rc; | ||
| 144 | } while (rc > 0); | ||
| 145 | |||
| 146 | close(fd); | ||
| 147 | return size; | ||
| 148 | } | ||
| 149 | |||
| 150 | static int scan_rom(char *path, char *file) | ||
| 151 | { | ||
| 152 | struct dirent **namelist; | ||
| 153 | char *name, *path2; | ||
| 154 | int i, n, r, rc = 0, result = 0; | ||
| 155 | struct stat buf; | ||
| 156 | |||
| 157 | n = scandir(path, &namelist, 0, alphasort); | ||
| 158 | if (n < 0) { | ||
| 159 | perror("scandir"); | ||
| 160 | return -1; | ||
| 161 | } | ||
| 162 | |||
| 163 | for (i = 0; i < n; i++) { | ||
| 164 | name = namelist[i]->d_name; | ||
| 165 | |||
| 166 | if (fnmatch(".", name, 0) == 0) | ||
| 167 | goto skip; | ||
| 168 | if (fnmatch("..", name, 0) == 0) | ||
| 169 | goto skip; | ||
| 170 | |||
| 171 | path2 = malloc(strlen(path) + strlen(name) + 3); | ||
| 172 | strcpy(path2, path); | ||
| 173 | strcat(path2, "/"); | ||
| 174 | strcat(path2, name); | ||
| 175 | |||
| 176 | if (fnmatch(file, name, 0) == 0) { | ||
| 177 | rc = read_rom(path2); | ||
| 178 | |||
| 179 | /* | ||
| 180 | * It's OK if the ROM is unreadable. Maybe there | ||
| 181 | * is no ROM, or some other error occurred. The | ||
| 182 | * important thing is that no MCA happened. | ||
| 183 | */ | ||
| 184 | if (rc > 0) | ||
| 185 | fprintf(stderr, "PASS: %s read %d bytes\n", path2, rc); | ||
| 186 | else { | ||
| 187 | fprintf(stderr, "PASS: %s not readable\n", path2); | ||
| 188 | return rc; | ||
| 189 | } | ||
| 190 | } else { | ||
| 191 | r = lstat(path2, &buf); | ||
| 192 | if (r == 0 && S_ISDIR(buf.st_mode)) { | ||
| 193 | rc = scan_rom(path2, file); | ||
| 194 | if (rc < 0) | ||
| 195 | return rc; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | result |= rc; | ||
| 200 | free(path2); | ||
| 201 | |||
| 202 | skip: | ||
| 203 | free(namelist[i]); | ||
| 204 | } | ||
| 205 | free(namelist); | ||
| 206 | return result; | ||
| 207 | } | ||
| 208 | |||
| 209 | int main(void) | ||
| 210 | { | ||
| 211 | int rc; | ||
| 212 | |||
| 213 | if (map_mem("/dev/mem", 0, 0xA0000, 1) == 0) | ||
| 214 | fprintf(stderr, "PASS: /dev/mem 0x0-0xa0000 is readable\n"); | ||
| 215 | else | ||
| 216 | fprintf(stderr, "FAIL: /dev/mem 0x0-0xa0000 not accessible\n"); | ||
| 217 | |||
| 218 | /* | ||
| 219 | * It's not safe to blindly read the VGA frame buffer. If you know | ||
| 220 | * how to poke the card the right way, it should respond, but it's | ||
| 221 | * not safe in general. Many machines, e.g., Intel chipsets, cover | ||
| 222 | * up a non-responding card by just returning -1, but others will | ||
| 223 | * report the failure as a machine check. | ||
| 224 | */ | ||
| 225 | if (map_mem("/dev/mem", 0xA0000, 0x20000, 0) == 0) | ||
| 226 | fprintf(stderr, "PASS: /dev/mem 0xa0000-0xc0000 is mappable\n"); | ||
| 227 | else | ||
| 228 | fprintf(stderr, "FAIL: /dev/mem 0xa0000-0xc0000 not accessible\n"); | ||
| 229 | |||
| 230 | if (map_mem("/dev/mem", 0xC0000, 0x40000, 1) == 0) | ||
| 231 | fprintf(stderr, "PASS: /dev/mem 0xc0000-0x100000 is readable\n"); | ||
| 232 | else | ||
| 233 | fprintf(stderr, "FAIL: /dev/mem 0xc0000-0x100000 not accessible\n"); | ||
| 234 | |||
| 235 | /* | ||
| 236 | * Often you can map all the individual pieces above (0-0xA0000, | ||
| 237 | * 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole | ||
| 238 | * thing at once. This is because the individual pieces use different | ||
| 239 | * attributes, and there's no single attribute supported over the | ||
| 240 | * whole region. | ||
| 241 | */ | ||
| 242 | rc = map_mem("/dev/mem", 0, 1024*1024, 0); | ||
| 243 | if (rc == 0) | ||
| 244 | fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 is mappable\n"); | ||
| 245 | else if (rc > 0) | ||
| 246 | fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 not mappable\n"); | ||
| 247 | else | ||
| 248 | fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n"); | ||
| 249 | |||
| 250 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1); | ||
| 251 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0); | ||
| 252 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1); | ||
| 253 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0); | ||
| 254 | |||
| 255 | scan_rom("/sys/devices", "rom"); | ||
| 256 | |||
| 257 | scan_tree("/proc/bus/pci", "??.?", 0, 0xA0000, 1); | ||
| 258 | scan_tree("/proc/bus/pci", "??.?", 0xA0000, 0x20000, 0); | ||
| 259 | scan_tree("/proc/bus/pci", "??.?", 0xC0000, 0x40000, 1); | ||
| 260 | scan_tree("/proc/bus/pci", "??.?", 0, 1024*1024, 0); | ||
| 261 | |||
| 262 | return rc; | ||
| 263 | } | ||
diff --git a/tools/testing/selftests/networking/timestamping/.gitignore b/tools/testing/selftests/networking/timestamping/.gitignore new file mode 100644 index 000000000000..9e69e982fb38 --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | timestamping | ||
| 2 | txtimestamp | ||
| 3 | hwtstamp_config | ||
diff --git a/tools/testing/selftests/networking/timestamping/Makefile b/tools/testing/selftests/networking/timestamping/Makefile new file mode 100644 index 000000000000..ccbb9edbbbb9 --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | TEST_PROGS := hwtstamp_config timestamping txtimestamp | ||
| 2 | |||
| 3 | all: $(TEST_PROGS) | ||
| 4 | |||
| 5 | include ../../lib.mk | ||
| 6 | |||
| 7 | clean: | ||
| 8 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/networking/timestamping/hwtstamp_config.c b/tools/testing/selftests/networking/timestamping/hwtstamp_config.c new file mode 100644 index 000000000000..e8b685a7f15f --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/hwtstamp_config.c | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /* Test program for SIOC{G,S}HWTSTAMP | ||
| 2 | * Copyright 2013 Solarflare Communications | ||
| 3 | * Author: Ben Hutchings | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <errno.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <stdlib.h> | ||
| 9 | #include <string.h> | ||
| 10 | |||
| 11 | #include <sys/socket.h> | ||
| 12 | #include <sys/ioctl.h> | ||
| 13 | |||
| 14 | #include <linux/if.h> | ||
| 15 | #include <linux/net_tstamp.h> | ||
| 16 | #include <linux/sockios.h> | ||
| 17 | |||
| 18 | static int | ||
| 19 | lookup_value(const char **names, int size, const char *name) | ||
| 20 | { | ||
| 21 | int value; | ||
| 22 | |||
| 23 | for (value = 0; value < size; value++) | ||
| 24 | if (names[value] && strcasecmp(names[value], name) == 0) | ||
| 25 | return value; | ||
| 26 | |||
| 27 | return -1; | ||
| 28 | } | ||
| 29 | |||
| 30 | static const char * | ||
| 31 | lookup_name(const char **names, int size, int value) | ||
| 32 | { | ||
| 33 | return (value >= 0 && value < size) ? names[value] : NULL; | ||
| 34 | } | ||
| 35 | |||
| 36 | static void list_names(FILE *f, const char **names, int size) | ||
| 37 | { | ||
| 38 | int value; | ||
| 39 | |||
| 40 | for (value = 0; value < size; value++) | ||
| 41 | if (names[value]) | ||
| 42 | fprintf(f, " %s\n", names[value]); | ||
| 43 | } | ||
| 44 | |||
| 45 | static const char *tx_types[] = { | ||
| 46 | #define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name | ||
| 47 | TX_TYPE(OFF), | ||
| 48 | TX_TYPE(ON), | ||
| 49 | TX_TYPE(ONESTEP_SYNC) | ||
| 50 | #undef TX_TYPE | ||
| 51 | }; | ||
| 52 | #define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0]))) | ||
| 53 | |||
| 54 | static const char *rx_filters[] = { | ||
| 55 | #define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name | ||
| 56 | RX_FILTER(NONE), | ||
| 57 | RX_FILTER(ALL), | ||
| 58 | RX_FILTER(SOME), | ||
| 59 | RX_FILTER(PTP_V1_L4_EVENT), | ||
| 60 | RX_FILTER(PTP_V1_L4_SYNC), | ||
| 61 | RX_FILTER(PTP_V1_L4_DELAY_REQ), | ||
| 62 | RX_FILTER(PTP_V2_L4_EVENT), | ||
| 63 | RX_FILTER(PTP_V2_L4_SYNC), | ||
| 64 | RX_FILTER(PTP_V2_L4_DELAY_REQ), | ||
| 65 | RX_FILTER(PTP_V2_L2_EVENT), | ||
| 66 | RX_FILTER(PTP_V2_L2_SYNC), | ||
| 67 | RX_FILTER(PTP_V2_L2_DELAY_REQ), | ||
| 68 | RX_FILTER(PTP_V2_EVENT), | ||
| 69 | RX_FILTER(PTP_V2_SYNC), | ||
| 70 | RX_FILTER(PTP_V2_DELAY_REQ), | ||
| 71 | #undef RX_FILTER | ||
| 72 | }; | ||
| 73 | #define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0]))) | ||
| 74 | |||
| 75 | static void usage(void) | ||
| 76 | { | ||
| 77 | fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n" | ||
| 78 | "tx_type is any of (case-insensitive):\n", | ||
| 79 | stderr); | ||
| 80 | list_names(stderr, tx_types, N_TX_TYPES); | ||
| 81 | fputs("rx_filter is any of (case-insensitive):\n", stderr); | ||
| 82 | list_names(stderr, rx_filters, N_RX_FILTERS); | ||
| 83 | } | ||
| 84 | |||
| 85 | int main(int argc, char **argv) | ||
| 86 | { | ||
| 87 | struct ifreq ifr; | ||
| 88 | struct hwtstamp_config config; | ||
| 89 | const char *name; | ||
| 90 | int sock; | ||
| 91 | |||
| 92 | if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) { | ||
| 93 | usage(); | ||
| 94 | return 2; | ||
| 95 | } | ||
| 96 | |||
| 97 | if (argc == 4) { | ||
| 98 | config.flags = 0; | ||
| 99 | config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]); | ||
| 100 | config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]); | ||
| 101 | if (config.tx_type < 0 || config.rx_filter < 0) { | ||
| 102 | usage(); | ||
| 103 | return 2; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | sock = socket(AF_INET, SOCK_DGRAM, 0); | ||
| 108 | if (sock < 0) { | ||
| 109 | perror("socket"); | ||
| 110 | return 1; | ||
| 111 | } | ||
| 112 | |||
| 113 | strcpy(ifr.ifr_name, argv[1]); | ||
| 114 | ifr.ifr_data = (caddr_t)&config; | ||
| 115 | |||
| 116 | if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) { | ||
| 117 | perror("ioctl"); | ||
| 118 | return 1; | ||
| 119 | } | ||
| 120 | |||
| 121 | printf("flags = %#x\n", config.flags); | ||
| 122 | name = lookup_name(tx_types, N_TX_TYPES, config.tx_type); | ||
| 123 | if (name) | ||
| 124 | printf("tx_type = %s\n", name); | ||
| 125 | else | ||
| 126 | printf("tx_type = %d\n", config.tx_type); | ||
| 127 | name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter); | ||
| 128 | if (name) | ||
| 129 | printf("rx_filter = %s\n", name); | ||
| 130 | else | ||
| 131 | printf("rx_filter = %d\n", config.rx_filter); | ||
| 132 | |||
| 133 | return 0; | ||
| 134 | } | ||
diff --git a/tools/testing/selftests/networking/timestamping/timestamping.c b/tools/testing/selftests/networking/timestamping/timestamping.c new file mode 100644 index 000000000000..5cdfd743447b --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/timestamping.c | |||
| @@ -0,0 +1,528 @@ | |||
| 1 | /* | ||
| 2 | * This program demonstrates how the various time stamping features in | ||
| 3 | * the Linux kernel work. It emulates the behavior of a PTP | ||
| 4 | * implementation in stand-alone master mode by sending PTPv1 Sync | ||
| 5 | * multicasts once every second. It looks for similar packets, but | ||
| 6 | * beyond that doesn't actually implement PTP. | ||
| 7 | * | ||
| 8 | * Outgoing packets are time stamped with SO_TIMESTAMPING with or | ||
| 9 | * without hardware support. | ||
| 10 | * | ||
| 11 | * Incoming packets are time stamped with SO_TIMESTAMPING with or | ||
| 12 | * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and | ||
| 13 | * SO_TIMESTAMP[NS]. | ||
| 14 | * | ||
| 15 | * Copyright (C) 2009 Intel Corporation. | ||
| 16 | * Author: Patrick Ohly <patrick.ohly@intel.com> | ||
| 17 | * | ||
| 18 | * This program is free software; you can redistribute it and/or modify it | ||
| 19 | * under the terms and conditions of the GNU General Public License, | ||
| 20 | * version 2, as published by the Free Software Foundation. | ||
| 21 | * | ||
| 22 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 23 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 24 | * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for | ||
| 25 | * more details. | ||
| 26 | * | ||
| 27 | * You should have received a copy of the GNU General Public License along with | ||
| 28 | * this program; if not, write to the Free Software Foundation, Inc., | ||
| 29 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 30 | */ | ||
| 31 | |||
| 32 | #include <stdio.h> | ||
| 33 | #include <stdlib.h> | ||
| 34 | #include <errno.h> | ||
| 35 | #include <string.h> | ||
| 36 | |||
| 37 | #include <sys/time.h> | ||
| 38 | #include <sys/socket.h> | ||
| 39 | #include <sys/select.h> | ||
| 40 | #include <sys/ioctl.h> | ||
| 41 | #include <arpa/inet.h> | ||
| 42 | #include <net/if.h> | ||
| 43 | |||
| 44 | #include <asm/types.h> | ||
| 45 | #include <linux/net_tstamp.h> | ||
| 46 | #include <linux/errqueue.h> | ||
| 47 | |||
| 48 | #ifndef SO_TIMESTAMPING | ||
| 49 | # define SO_TIMESTAMPING 37 | ||
| 50 | # define SCM_TIMESTAMPING SO_TIMESTAMPING | ||
| 51 | #endif | ||
| 52 | |||
| 53 | #ifndef SO_TIMESTAMPNS | ||
| 54 | # define SO_TIMESTAMPNS 35 | ||
| 55 | #endif | ||
| 56 | |||
| 57 | #ifndef SIOCGSTAMPNS | ||
| 58 | # define SIOCGSTAMPNS 0x8907 | ||
| 59 | #endif | ||
| 60 | |||
| 61 | #ifndef SIOCSHWTSTAMP | ||
| 62 | # define SIOCSHWTSTAMP 0x89b0 | ||
| 63 | #endif | ||
| 64 | |||
| 65 | static void usage(const char *error) | ||
| 66 | { | ||
| 67 | if (error) | ||
| 68 | printf("invalid option: %s\n", error); | ||
| 69 | printf("timestamping interface option*\n\n" | ||
| 70 | "Options:\n" | ||
| 71 | " IP_MULTICAST_LOOP - looping outgoing multicasts\n" | ||
| 72 | " SO_TIMESTAMP - normal software time stamping, ms resolution\n" | ||
| 73 | " SO_TIMESTAMPNS - more accurate software time stamping\n" | ||
| 74 | " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n" | ||
| 75 | " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n" | ||
| 76 | " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n" | ||
| 77 | " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n" | ||
| 78 | " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n" | ||
| 79 | " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n" | ||
| 80 | " SIOCGSTAMP - check last socket time stamp\n" | ||
| 81 | " SIOCGSTAMPNS - more accurate socket time stamp\n"); | ||
| 82 | exit(1); | ||
| 83 | } | ||
| 84 | |||
| 85 | static void bail(const char *error) | ||
| 86 | { | ||
| 87 | printf("%s: %s\n", error, strerror(errno)); | ||
| 88 | exit(1); | ||
| 89 | } | ||
| 90 | |||
| 91 | static const unsigned char sync[] = { | ||
| 92 | 0x00, 0x01, 0x00, 0x01, | ||
| 93 | 0x5f, 0x44, 0x46, 0x4c, | ||
| 94 | 0x54, 0x00, 0x00, 0x00, | ||
| 95 | 0x00, 0x00, 0x00, 0x00, | ||
| 96 | 0x00, 0x00, 0x00, 0x00, | ||
| 97 | 0x01, 0x01, | ||
| 98 | |||
| 99 | /* fake uuid */ | ||
| 100 | 0x00, 0x01, | ||
| 101 | 0x02, 0x03, 0x04, 0x05, | ||
| 102 | |||
| 103 | 0x00, 0x01, 0x00, 0x37, | ||
| 104 | 0x00, 0x00, 0x00, 0x08, | ||
| 105 | 0x00, 0x00, 0x00, 0x00, | ||
| 106 | 0x49, 0x05, 0xcd, 0x01, | ||
| 107 | 0x29, 0xb1, 0x8d, 0xb0, | ||
| 108 | 0x00, 0x00, 0x00, 0x00, | ||
| 109 | 0x00, 0x01, | ||
| 110 | |||
| 111 | /* fake uuid */ | ||
| 112 | 0x00, 0x01, | ||
| 113 | 0x02, 0x03, 0x04, 0x05, | ||
| 114 | |||
| 115 | 0x00, 0x00, 0x00, 0x37, | ||
| 116 | 0x00, 0x00, 0x00, 0x04, | ||
| 117 | 0x44, 0x46, 0x4c, 0x54, | ||
| 118 | 0x00, 0x00, 0xf0, 0x60, | ||
| 119 | 0x00, 0x01, 0x00, 0x00, | ||
| 120 | 0x00, 0x00, 0x00, 0x01, | ||
| 121 | 0x00, 0x00, 0xf0, 0x60, | ||
| 122 | 0x00, 0x00, 0x00, 0x00, | ||
| 123 | 0x00, 0x00, 0x00, 0x04, | ||
| 124 | 0x44, 0x46, 0x4c, 0x54, | ||
| 125 | 0x00, 0x01, | ||
| 126 | |||
| 127 | /* fake uuid */ | ||
| 128 | 0x00, 0x01, | ||
| 129 | 0x02, 0x03, 0x04, 0x05, | ||
| 130 | |||
| 131 | 0x00, 0x00, 0x00, 0x00, | ||
| 132 | 0x00, 0x00, 0x00, 0x00, | ||
| 133 | 0x00, 0x00, 0x00, 0x00, | ||
| 134 | 0x00, 0x00, 0x00, 0x00 | ||
| 135 | }; | ||
| 136 | |||
| 137 | static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len) | ||
| 138 | { | ||
| 139 | struct timeval now; | ||
| 140 | int res; | ||
| 141 | |||
| 142 | res = sendto(sock, sync, sizeof(sync), 0, | ||
| 143 | addr, addr_len); | ||
| 144 | gettimeofday(&now, 0); | ||
| 145 | if (res < 0) | ||
| 146 | printf("%s: %s\n", "send", strerror(errno)); | ||
| 147 | else | ||
| 148 | printf("%ld.%06ld: sent %d bytes\n", | ||
| 149 | (long)now.tv_sec, (long)now.tv_usec, | ||
| 150 | res); | ||
| 151 | } | ||
| 152 | |||
| 153 | static void printpacket(struct msghdr *msg, int res, | ||
| 154 | char *data, | ||
| 155 | int sock, int recvmsg_flags, | ||
| 156 | int siocgstamp, int siocgstampns) | ||
| 157 | { | ||
| 158 | struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; | ||
| 159 | struct cmsghdr *cmsg; | ||
| 160 | struct timeval tv; | ||
| 161 | struct timespec ts; | ||
| 162 | struct timeval now; | ||
| 163 | |||
| 164 | gettimeofday(&now, 0); | ||
| 165 | |||
| 166 | printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n", | ||
| 167 | (long)now.tv_sec, (long)now.tv_usec, | ||
| 168 | (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", | ||
| 169 | res, | ||
| 170 | inet_ntoa(from_addr->sin_addr), | ||
| 171 | msg->msg_controllen); | ||
| 172 | for (cmsg = CMSG_FIRSTHDR(msg); | ||
| 173 | cmsg; | ||
| 174 | cmsg = CMSG_NXTHDR(msg, cmsg)) { | ||
| 175 | printf(" cmsg len %zu: ", cmsg->cmsg_len); | ||
| 176 | switch (cmsg->cmsg_level) { | ||
| 177 | case SOL_SOCKET: | ||
| 178 | printf("SOL_SOCKET "); | ||
| 179 | switch (cmsg->cmsg_type) { | ||
| 180 | case SO_TIMESTAMP: { | ||
| 181 | struct timeval *stamp = | ||
| 182 | (struct timeval *)CMSG_DATA(cmsg); | ||
| 183 | printf("SO_TIMESTAMP %ld.%06ld", | ||
| 184 | (long)stamp->tv_sec, | ||
| 185 | (long)stamp->tv_usec); | ||
| 186 | break; | ||
| 187 | } | ||
| 188 | case SO_TIMESTAMPNS: { | ||
| 189 | struct timespec *stamp = | ||
| 190 | (struct timespec *)CMSG_DATA(cmsg); | ||
| 191 | printf("SO_TIMESTAMPNS %ld.%09ld", | ||
| 192 | (long)stamp->tv_sec, | ||
| 193 | (long)stamp->tv_nsec); | ||
| 194 | break; | ||
| 195 | } | ||
| 196 | case SO_TIMESTAMPING: { | ||
| 197 | struct timespec *stamp = | ||
| 198 | (struct timespec *)CMSG_DATA(cmsg); | ||
| 199 | printf("SO_TIMESTAMPING "); | ||
| 200 | printf("SW %ld.%09ld ", | ||
| 201 | (long)stamp->tv_sec, | ||
| 202 | (long)stamp->tv_nsec); | ||
| 203 | stamp++; | ||
| 204 | /* skip deprecated HW transformed */ | ||
| 205 | stamp++; | ||
| 206 | printf("HW raw %ld.%09ld", | ||
| 207 | (long)stamp->tv_sec, | ||
| 208 | (long)stamp->tv_nsec); | ||
| 209 | break; | ||
| 210 | } | ||
| 211 | default: | ||
| 212 | printf("type %d", cmsg->cmsg_type); | ||
| 213 | break; | ||
| 214 | } | ||
| 215 | break; | ||
| 216 | case IPPROTO_IP: | ||
| 217 | printf("IPPROTO_IP "); | ||
| 218 | switch (cmsg->cmsg_type) { | ||
| 219 | case IP_RECVERR: { | ||
| 220 | struct sock_extended_err *err = | ||
| 221 | (struct sock_extended_err *)CMSG_DATA(cmsg); | ||
| 222 | printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s", | ||
| 223 | strerror(err->ee_errno), | ||
| 224 | err->ee_origin, | ||
| 225 | #ifdef SO_EE_ORIGIN_TIMESTAMPING | ||
| 226 | err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ? | ||
| 227 | "bounced packet" : "unexpected origin" | ||
| 228 | #else | ||
| 229 | "probably SO_EE_ORIGIN_TIMESTAMPING" | ||
| 230 | #endif | ||
| 231 | ); | ||
| 232 | if (res < sizeof(sync)) | ||
| 233 | printf(" => truncated data?!"); | ||
| 234 | else if (!memcmp(sync, data + res - sizeof(sync), | ||
| 235 | sizeof(sync))) | ||
| 236 | printf(" => GOT OUR DATA BACK (HURRAY!)"); | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | case IP_PKTINFO: { | ||
| 240 | struct in_pktinfo *pktinfo = | ||
| 241 | (struct in_pktinfo *)CMSG_DATA(cmsg); | ||
| 242 | printf("IP_PKTINFO interface index %u", | ||
| 243 | pktinfo->ipi_ifindex); | ||
| 244 | break; | ||
| 245 | } | ||
| 246 | default: | ||
| 247 | printf("type %d", cmsg->cmsg_type); | ||
| 248 | break; | ||
| 249 | } | ||
| 250 | break; | ||
| 251 | default: | ||
| 252 | printf("level %d type %d", | ||
| 253 | cmsg->cmsg_level, | ||
| 254 | cmsg->cmsg_type); | ||
| 255 | break; | ||
| 256 | } | ||
| 257 | printf("\n"); | ||
| 258 | } | ||
| 259 | |||
| 260 | if (siocgstamp) { | ||
| 261 | if (ioctl(sock, SIOCGSTAMP, &tv)) | ||
| 262 | printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno)); | ||
| 263 | else | ||
| 264 | printf("SIOCGSTAMP %ld.%06ld\n", | ||
| 265 | (long)tv.tv_sec, | ||
| 266 | (long)tv.tv_usec); | ||
| 267 | } | ||
| 268 | if (siocgstampns) { | ||
| 269 | if (ioctl(sock, SIOCGSTAMPNS, &ts)) | ||
| 270 | printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno)); | ||
| 271 | else | ||
| 272 | printf("SIOCGSTAMPNS %ld.%09ld\n", | ||
| 273 | (long)ts.tv_sec, | ||
| 274 | (long)ts.tv_nsec); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | static void recvpacket(int sock, int recvmsg_flags, | ||
| 279 | int siocgstamp, int siocgstampns) | ||
| 280 | { | ||
| 281 | char data[256]; | ||
| 282 | struct msghdr msg; | ||
| 283 | struct iovec entry; | ||
| 284 | struct sockaddr_in from_addr; | ||
| 285 | struct { | ||
| 286 | struct cmsghdr cm; | ||
| 287 | char control[512]; | ||
| 288 | } control; | ||
| 289 | int res; | ||
| 290 | |||
| 291 | memset(&msg, 0, sizeof(msg)); | ||
| 292 | msg.msg_iov = &entry; | ||
| 293 | msg.msg_iovlen = 1; | ||
| 294 | entry.iov_base = data; | ||
| 295 | entry.iov_len = sizeof(data); | ||
| 296 | msg.msg_name = (caddr_t)&from_addr; | ||
| 297 | msg.msg_namelen = sizeof(from_addr); | ||
| 298 | msg.msg_control = &control; | ||
| 299 | msg.msg_controllen = sizeof(control); | ||
| 300 | |||
| 301 | res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT); | ||
| 302 | if (res < 0) { | ||
| 303 | printf("%s %s: %s\n", | ||
| 304 | "recvmsg", | ||
| 305 | (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", | ||
| 306 | strerror(errno)); | ||
| 307 | } else { | ||
| 308 | printpacket(&msg, res, data, | ||
| 309 | sock, recvmsg_flags, | ||
| 310 | siocgstamp, siocgstampns); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | int main(int argc, char **argv) | ||
| 315 | { | ||
| 316 | int so_timestamping_flags = 0; | ||
| 317 | int so_timestamp = 0; | ||
| 318 | int so_timestampns = 0; | ||
| 319 | int siocgstamp = 0; | ||
| 320 | int siocgstampns = 0; | ||
| 321 | int ip_multicast_loop = 0; | ||
| 322 | char *interface; | ||
| 323 | int i; | ||
| 324 | int enabled = 1; | ||
| 325 | int sock; | ||
| 326 | struct ifreq device; | ||
| 327 | struct ifreq hwtstamp; | ||
| 328 | struct hwtstamp_config hwconfig, hwconfig_requested; | ||
| 329 | struct sockaddr_in addr; | ||
| 330 | struct ip_mreq imr; | ||
| 331 | struct in_addr iaddr; | ||
| 332 | int val; | ||
| 333 | socklen_t len; | ||
| 334 | struct timeval next; | ||
| 335 | |||
| 336 | if (argc < 2) | ||
| 337 | usage(0); | ||
| 338 | interface = argv[1]; | ||
| 339 | |||
| 340 | for (i = 2; i < argc; i++) { | ||
| 341 | if (!strcasecmp(argv[i], "SO_TIMESTAMP")) | ||
| 342 | so_timestamp = 1; | ||
| 343 | else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS")) | ||
| 344 | so_timestampns = 1; | ||
| 345 | else if (!strcasecmp(argv[i], "SIOCGSTAMP")) | ||
| 346 | siocgstamp = 1; | ||
| 347 | else if (!strcasecmp(argv[i], "SIOCGSTAMPNS")) | ||
| 348 | siocgstampns = 1; | ||
| 349 | else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP")) | ||
| 350 | ip_multicast_loop = 1; | ||
| 351 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE")) | ||
| 352 | so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE; | ||
| 353 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE")) | ||
| 354 | so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE; | ||
| 355 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE")) | ||
| 356 | so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE; | ||
| 357 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE")) | ||
| 358 | so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE; | ||
| 359 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE")) | ||
| 360 | so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE; | ||
| 361 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE")) | ||
| 362 | so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; | ||
| 363 | else | ||
| 364 | usage(argv[i]); | ||
| 365 | } | ||
| 366 | |||
| 367 | sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
| 368 | if (sock < 0) | ||
| 369 | bail("socket"); | ||
| 370 | |||
| 371 | memset(&device, 0, sizeof(device)); | ||
| 372 | strncpy(device.ifr_name, interface, sizeof(device.ifr_name)); | ||
| 373 | if (ioctl(sock, SIOCGIFADDR, &device) < 0) | ||
| 374 | bail("getting interface IP address"); | ||
| 375 | |||
| 376 | memset(&hwtstamp, 0, sizeof(hwtstamp)); | ||
| 377 | strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name)); | ||
| 378 | hwtstamp.ifr_data = (void *)&hwconfig; | ||
| 379 | memset(&hwconfig, 0, sizeof(hwconfig)); | ||
| 380 | hwconfig.tx_type = | ||
| 381 | (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ? | ||
| 382 | HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; | ||
| 383 | hwconfig.rx_filter = | ||
| 384 | (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ? | ||
| 385 | HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE; | ||
| 386 | hwconfig_requested = hwconfig; | ||
| 387 | if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) { | ||
| 388 | if ((errno == EINVAL || errno == ENOTSUP) && | ||
| 389 | hwconfig_requested.tx_type == HWTSTAMP_TX_OFF && | ||
| 390 | hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE) | ||
| 391 | printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n"); | ||
| 392 | else | ||
| 393 | bail("SIOCSHWTSTAMP"); | ||
| 394 | } | ||
| 395 | printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n", | ||
| 396 | hwconfig_requested.tx_type, hwconfig.tx_type, | ||
| 397 | hwconfig_requested.rx_filter, hwconfig.rx_filter); | ||
| 398 | |||
| 399 | /* bind to PTP port */ | ||
| 400 | addr.sin_family = AF_INET; | ||
| 401 | addr.sin_addr.s_addr = htonl(INADDR_ANY); | ||
| 402 | addr.sin_port = htons(319 /* PTP event port */); | ||
| 403 | if (bind(sock, | ||
| 404 | (struct sockaddr *)&addr, | ||
| 405 | sizeof(struct sockaddr_in)) < 0) | ||
| 406 | bail("bind"); | ||
| 407 | |||
| 408 | /* set multicast group for outgoing packets */ | ||
| 409 | inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */ | ||
| 410 | addr.sin_addr = iaddr; | ||
| 411 | imr.imr_multiaddr.s_addr = iaddr.s_addr; | ||
| 412 | imr.imr_interface.s_addr = | ||
| 413 | ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr; | ||
| 414 | if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, | ||
| 415 | &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0) | ||
| 416 | bail("set multicast"); | ||
| 417 | |||
| 418 | /* join multicast group, loop our own packet */ | ||
| 419 | if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, | ||
| 420 | &imr, sizeof(struct ip_mreq)) < 0) | ||
| 421 | bail("join multicast group"); | ||
| 422 | |||
| 423 | if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, | ||
| 424 | &ip_multicast_loop, sizeof(enabled)) < 0) { | ||
| 425 | bail("loop multicast"); | ||
| 426 | } | ||
| 427 | |||
| 428 | /* set socket options for time stamping */ | ||
| 429 | if (so_timestamp && | ||
| 430 | setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, | ||
| 431 | &enabled, sizeof(enabled)) < 0) | ||
| 432 | bail("setsockopt SO_TIMESTAMP"); | ||
| 433 | |||
| 434 | if (so_timestampns && | ||
| 435 | setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, | ||
| 436 | &enabled, sizeof(enabled)) < 0) | ||
| 437 | bail("setsockopt SO_TIMESTAMPNS"); | ||
| 438 | |||
| 439 | if (so_timestamping_flags && | ||
| 440 | setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, | ||
| 441 | &so_timestamping_flags, | ||
| 442 | sizeof(so_timestamping_flags)) < 0) | ||
| 443 | bail("setsockopt SO_TIMESTAMPING"); | ||
| 444 | |||
| 445 | /* request IP_PKTINFO for debugging purposes */ | ||
| 446 | if (setsockopt(sock, SOL_IP, IP_PKTINFO, | ||
| 447 | &enabled, sizeof(enabled)) < 0) | ||
| 448 | printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno)); | ||
| 449 | |||
| 450 | /* verify socket options */ | ||
| 451 | len = sizeof(val); | ||
| 452 | if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0) | ||
| 453 | printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno)); | ||
| 454 | else | ||
| 455 | printf("SO_TIMESTAMP %d\n", val); | ||
| 456 | |||
| 457 | if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0) | ||
| 458 | printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS", | ||
| 459 | strerror(errno)); | ||
| 460 | else | ||
| 461 | printf("SO_TIMESTAMPNS %d\n", val); | ||
| 462 | |||
| 463 | if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) { | ||
| 464 | printf("%s: %s\n", "getsockopt SO_TIMESTAMPING", | ||
| 465 | strerror(errno)); | ||
| 466 | } else { | ||
| 467 | printf("SO_TIMESTAMPING %d\n", val); | ||
| 468 | if (val != so_timestamping_flags) | ||
| 469 | printf(" not the expected value %d\n", | ||
| 470 | so_timestamping_flags); | ||
| 471 | } | ||
| 472 | |||
| 473 | /* send packets forever every five seconds */ | ||
| 474 | gettimeofday(&next, 0); | ||
| 475 | next.tv_sec = (next.tv_sec + 1) / 5 * 5; | ||
| 476 | next.tv_usec = 0; | ||
| 477 | while (1) { | ||
| 478 | struct timeval now; | ||
| 479 | struct timeval delta; | ||
| 480 | long delta_us; | ||
| 481 | int res; | ||
| 482 | fd_set readfs, errorfs; | ||
| 483 | |||
| 484 | gettimeofday(&now, 0); | ||
| 485 | delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 + | ||
| 486 | (long)(next.tv_usec - now.tv_usec); | ||
| 487 | if (delta_us > 0) { | ||
| 488 | /* continue waiting for timeout or data */ | ||
| 489 | delta.tv_sec = delta_us / 1000000; | ||
| 490 | delta.tv_usec = delta_us % 1000000; | ||
| 491 | |||
| 492 | FD_ZERO(&readfs); | ||
| 493 | FD_ZERO(&errorfs); | ||
| 494 | FD_SET(sock, &readfs); | ||
| 495 | FD_SET(sock, &errorfs); | ||
| 496 | printf("%ld.%06ld: select %ldus\n", | ||
| 497 | (long)now.tv_sec, (long)now.tv_usec, | ||
| 498 | delta_us); | ||
| 499 | res = select(sock + 1, &readfs, 0, &errorfs, &delta); | ||
| 500 | gettimeofday(&now, 0); | ||
| 501 | printf("%ld.%06ld: select returned: %d, %s\n", | ||
| 502 | (long)now.tv_sec, (long)now.tv_usec, | ||
| 503 | res, | ||
| 504 | res < 0 ? strerror(errno) : "success"); | ||
| 505 | if (res > 0) { | ||
| 506 | if (FD_ISSET(sock, &readfs)) | ||
| 507 | printf("ready for reading\n"); | ||
| 508 | if (FD_ISSET(sock, &errorfs)) | ||
| 509 | printf("has error\n"); | ||
| 510 | recvpacket(sock, 0, | ||
| 511 | siocgstamp, | ||
| 512 | siocgstampns); | ||
| 513 | recvpacket(sock, MSG_ERRQUEUE, | ||
| 514 | siocgstamp, | ||
| 515 | siocgstampns); | ||
| 516 | } | ||
| 517 | } else { | ||
| 518 | /* write one packet */ | ||
| 519 | sendpacket(sock, | ||
| 520 | (struct sockaddr *)&addr, | ||
| 521 | sizeof(addr)); | ||
| 522 | next.tv_sec += 5; | ||
| 523 | continue; | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | return 0; | ||
| 528 | } | ||
diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.c b/tools/testing/selftests/networking/timestamping/txtimestamp.c new file mode 100644 index 000000000000..5df07047ca86 --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/txtimestamp.c | |||
| @@ -0,0 +1,549 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2014 Google Inc. | ||
| 3 | * Author: willemb@google.com (Willem de Bruijn) | ||
| 4 | * | ||
| 5 | * Test software tx timestamping, including | ||
| 6 | * | ||
| 7 | * - SCHED, SND and ACK timestamps | ||
| 8 | * - RAW, UDP and TCP | ||
| 9 | * - IPv4 and IPv6 | ||
| 10 | * - various packet sizes (to test GSO and TSO) | ||
| 11 | * | ||
| 12 | * Consult the command line arguments for help on running | ||
| 13 | * the various testcases. | ||
| 14 | * | ||
| 15 | * This test requires a dummy TCP server. | ||
| 16 | * A simple `nc6 [-u] -l -p $DESTPORT` will do | ||
| 17 | * | ||
| 18 | * | ||
| 19 | * This program is free software; you can redistribute it and/or modify it | ||
| 20 | * under the terms and conditions of the GNU General Public License, | ||
| 21 | * version 2, as published by the Free Software Foundation. | ||
| 22 | * | ||
| 23 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 24 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 25 | * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for | ||
| 26 | * more details. | ||
| 27 | * | ||
| 28 | * You should have received a copy of the GNU General Public License along with | ||
| 29 | * this program; if not, write to the Free Software Foundation, Inc., | ||
| 30 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 31 | */ | ||
| 32 | |||
| 33 | #define _GNU_SOURCE | ||
| 34 | |||
| 35 | #include <arpa/inet.h> | ||
| 36 | #include <asm/types.h> | ||
| 37 | #include <error.h> | ||
| 38 | #include <errno.h> | ||
| 39 | #include <inttypes.h> | ||
| 40 | #include <linux/errqueue.h> | ||
| 41 | #include <linux/if_ether.h> | ||
| 42 | #include <linux/net_tstamp.h> | ||
| 43 | #include <netdb.h> | ||
| 44 | #include <net/if.h> | ||
| 45 | #include <netinet/in.h> | ||
| 46 | #include <netinet/ip.h> | ||
| 47 | #include <netinet/udp.h> | ||
| 48 | #include <netinet/tcp.h> | ||
| 49 | #include <netpacket/packet.h> | ||
| 50 | #include <poll.h> | ||
| 51 | #include <stdarg.h> | ||
| 52 | #include <stdbool.h> | ||
| 53 | #include <stdio.h> | ||
| 54 | #include <stdlib.h> | ||
| 55 | #include <string.h> | ||
| 56 | #include <sys/ioctl.h> | ||
| 57 | #include <sys/select.h> | ||
| 58 | #include <sys/socket.h> | ||
| 59 | #include <sys/time.h> | ||
| 60 | #include <sys/types.h> | ||
| 61 | #include <time.h> | ||
| 62 | #include <unistd.h> | ||
| 63 | |||
| 64 | /* command line parameters */ | ||
| 65 | static int cfg_proto = SOCK_STREAM; | ||
| 66 | static int cfg_ipproto = IPPROTO_TCP; | ||
| 67 | static int cfg_num_pkts = 4; | ||
| 68 | static int do_ipv4 = 1; | ||
| 69 | static int do_ipv6 = 1; | ||
| 70 | static int cfg_payload_len = 10; | ||
| 71 | static bool cfg_show_payload; | ||
| 72 | static bool cfg_do_pktinfo; | ||
| 73 | static bool cfg_loop_nodata; | ||
| 74 | static uint16_t dest_port = 9000; | ||
| 75 | |||
| 76 | static struct sockaddr_in daddr; | ||
| 77 | static struct sockaddr_in6 daddr6; | ||
| 78 | static struct timespec ts_prev; | ||
| 79 | |||
| 80 | static void __print_timestamp(const char *name, struct timespec *cur, | ||
| 81 | uint32_t key, int payload_len) | ||
| 82 | { | ||
| 83 | if (!(cur->tv_sec | cur->tv_nsec)) | ||
| 84 | return; | ||
| 85 | |||
| 86 | fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)", | ||
| 87 | name, cur->tv_sec, cur->tv_nsec / 1000, | ||
| 88 | key, payload_len); | ||
| 89 | |||
| 90 | if ((ts_prev.tv_sec | ts_prev.tv_nsec)) { | ||
| 91 | int64_t cur_ms, prev_ms; | ||
| 92 | |||
| 93 | cur_ms = (long) cur->tv_sec * 1000 * 1000; | ||
| 94 | cur_ms += cur->tv_nsec / 1000; | ||
| 95 | |||
| 96 | prev_ms = (long) ts_prev.tv_sec * 1000 * 1000; | ||
| 97 | prev_ms += ts_prev.tv_nsec / 1000; | ||
| 98 | |||
| 99 | fprintf(stderr, " (%+" PRId64 " us)", cur_ms - prev_ms); | ||
| 100 | } | ||
| 101 | |||
| 102 | ts_prev = *cur; | ||
| 103 | fprintf(stderr, "\n"); | ||
| 104 | } | ||
| 105 | |||
| 106 | static void print_timestamp_usr(void) | ||
| 107 | { | ||
| 108 | struct timespec ts; | ||
| 109 | struct timeval tv; /* avoid dependency on -lrt */ | ||
| 110 | |||
| 111 | gettimeofday(&tv, NULL); | ||
| 112 | ts.tv_sec = tv.tv_sec; | ||
| 113 | ts.tv_nsec = tv.tv_usec * 1000; | ||
| 114 | |||
| 115 | __print_timestamp(" USR", &ts, 0, 0); | ||
| 116 | } | ||
| 117 | |||
| 118 | static void print_timestamp(struct scm_timestamping *tss, int tstype, | ||
| 119 | int tskey, int payload_len) | ||
| 120 | { | ||
| 121 | const char *tsname; | ||
| 122 | |||
| 123 | switch (tstype) { | ||
| 124 | case SCM_TSTAMP_SCHED: | ||
| 125 | tsname = " ENQ"; | ||
| 126 | break; | ||
| 127 | case SCM_TSTAMP_SND: | ||
| 128 | tsname = " SND"; | ||
| 129 | break; | ||
| 130 | case SCM_TSTAMP_ACK: | ||
| 131 | tsname = " ACK"; | ||
| 132 | break; | ||
| 133 | default: | ||
| 134 | error(1, 0, "unknown timestamp type: %u", | ||
| 135 | tstype); | ||
| 136 | } | ||
| 137 | __print_timestamp(tsname, &tss->ts[0], tskey, payload_len); | ||
| 138 | } | ||
| 139 | |||
| 140 | /* TODO: convert to check_and_print payload once API is stable */ | ||
| 141 | static void print_payload(char *data, int len) | ||
| 142 | { | ||
| 143 | int i; | ||
| 144 | |||
| 145 | if (!len) | ||
| 146 | return; | ||
| 147 | |||
| 148 | if (len > 70) | ||
| 149 | len = 70; | ||
| 150 | |||
| 151 | fprintf(stderr, "payload: "); | ||
| 152 | for (i = 0; i < len; i++) | ||
| 153 | fprintf(stderr, "%02hhx ", data[i]); | ||
| 154 | fprintf(stderr, "\n"); | ||
| 155 | } | ||
| 156 | |||
| 157 | static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr) | ||
| 158 | { | ||
| 159 | char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN]; | ||
| 160 | |||
| 161 | fprintf(stderr, " pktinfo: ifindex=%u src=%s dst=%s\n", | ||
| 162 | ifindex, | ||
| 163 | saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown", | ||
| 164 | daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown"); | ||
| 165 | } | ||
| 166 | |||
| 167 | static void __poll(int fd) | ||
| 168 | { | ||
| 169 | struct pollfd pollfd; | ||
| 170 | int ret; | ||
| 171 | |||
| 172 | memset(&pollfd, 0, sizeof(pollfd)); | ||
| 173 | pollfd.fd = fd; | ||
| 174 | ret = poll(&pollfd, 1, 100); | ||
| 175 | if (ret != 1) | ||
| 176 | error(1, errno, "poll"); | ||
| 177 | } | ||
| 178 | |||
| 179 | static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len) | ||
| 180 | { | ||
| 181 | struct sock_extended_err *serr = NULL; | ||
| 182 | struct scm_timestamping *tss = NULL; | ||
| 183 | struct cmsghdr *cm; | ||
| 184 | int batch = 0; | ||
| 185 | |||
| 186 | for (cm = CMSG_FIRSTHDR(msg); | ||
| 187 | cm && cm->cmsg_len; | ||
| 188 | cm = CMSG_NXTHDR(msg, cm)) { | ||
| 189 | if (cm->cmsg_level == SOL_SOCKET && | ||
| 190 | cm->cmsg_type == SCM_TIMESTAMPING) { | ||
| 191 | tss = (void *) CMSG_DATA(cm); | ||
| 192 | } else if ((cm->cmsg_level == SOL_IP && | ||
| 193 | cm->cmsg_type == IP_RECVERR) || | ||
| 194 | (cm->cmsg_level == SOL_IPV6 && | ||
| 195 | cm->cmsg_type == IPV6_RECVERR)) { | ||
| 196 | serr = (void *) CMSG_DATA(cm); | ||
| 197 | if (serr->ee_errno != ENOMSG || | ||
| 198 | serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { | ||
| 199 | fprintf(stderr, "unknown ip error %d %d\n", | ||
| 200 | serr->ee_errno, | ||
| 201 | serr->ee_origin); | ||
| 202 | serr = NULL; | ||
| 203 | } | ||
| 204 | } else if (cm->cmsg_level == SOL_IP && | ||
| 205 | cm->cmsg_type == IP_PKTINFO) { | ||
| 206 | struct in_pktinfo *info = (void *) CMSG_DATA(cm); | ||
| 207 | print_pktinfo(AF_INET, info->ipi_ifindex, | ||
| 208 | &info->ipi_spec_dst, &info->ipi_addr); | ||
| 209 | } else if (cm->cmsg_level == SOL_IPV6 && | ||
| 210 | cm->cmsg_type == IPV6_PKTINFO) { | ||
| 211 | struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm); | ||
| 212 | print_pktinfo(AF_INET6, info6->ipi6_ifindex, | ||
| 213 | NULL, &info6->ipi6_addr); | ||
| 214 | } else | ||
| 215 | fprintf(stderr, "unknown cmsg %d,%d\n", | ||
| 216 | cm->cmsg_level, cm->cmsg_type); | ||
| 217 | |||
| 218 | if (serr && tss) { | ||
| 219 | print_timestamp(tss, serr->ee_info, serr->ee_data, | ||
| 220 | payload_len); | ||
| 221 | serr = NULL; | ||
| 222 | tss = NULL; | ||
| 223 | batch++; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | if (batch > 1) | ||
| 228 | fprintf(stderr, "batched %d timestamps\n", batch); | ||
| 229 | } | ||
| 230 | |||
| 231 | static int recv_errmsg(int fd) | ||
| 232 | { | ||
| 233 | static char ctrl[1024 /* overprovision*/]; | ||
| 234 | static struct msghdr msg; | ||
| 235 | struct iovec entry; | ||
| 236 | static char *data; | ||
| 237 | int ret = 0; | ||
| 238 | |||
| 239 | data = malloc(cfg_payload_len); | ||
| 240 | if (!data) | ||
| 241 | error(1, 0, "malloc"); | ||
| 242 | |||
| 243 | memset(&msg, 0, sizeof(msg)); | ||
| 244 | memset(&entry, 0, sizeof(entry)); | ||
| 245 | memset(ctrl, 0, sizeof(ctrl)); | ||
| 246 | |||
| 247 | entry.iov_base = data; | ||
| 248 | entry.iov_len = cfg_payload_len; | ||
| 249 | msg.msg_iov = &entry; | ||
| 250 | msg.msg_iovlen = 1; | ||
| 251 | msg.msg_name = NULL; | ||
| 252 | msg.msg_namelen = 0; | ||
| 253 | msg.msg_control = ctrl; | ||
| 254 | msg.msg_controllen = sizeof(ctrl); | ||
| 255 | |||
| 256 | ret = recvmsg(fd, &msg, MSG_ERRQUEUE); | ||
| 257 | if (ret == -1 && errno != EAGAIN) | ||
| 258 | error(1, errno, "recvmsg"); | ||
| 259 | |||
| 260 | if (ret >= 0) { | ||
| 261 | __recv_errmsg_cmsg(&msg, ret); | ||
| 262 | if (cfg_show_payload) | ||
| 263 | print_payload(data, cfg_payload_len); | ||
| 264 | } | ||
| 265 | |||
| 266 | free(data); | ||
| 267 | return ret == -1; | ||
| 268 | } | ||
| 269 | |||
| 270 | static void do_test(int family, unsigned int opt) | ||
| 271 | { | ||
| 272 | char *buf; | ||
| 273 | int fd, i, val = 1, total_len; | ||
| 274 | |||
| 275 | if (family == AF_INET6 && cfg_proto != SOCK_STREAM) { | ||
| 276 | /* due to lack of checksum generation code */ | ||
| 277 | fprintf(stderr, "test: skipping datagram over IPv6\n"); | ||
| 278 | return; | ||
| 279 | } | ||
| 280 | |||
| 281 | total_len = cfg_payload_len; | ||
| 282 | if (cfg_proto == SOCK_RAW) { | ||
| 283 | total_len += sizeof(struct udphdr); | ||
| 284 | if (cfg_ipproto == IPPROTO_RAW) | ||
| 285 | total_len += sizeof(struct iphdr); | ||
| 286 | } | ||
| 287 | |||
| 288 | buf = malloc(total_len); | ||
| 289 | if (!buf) | ||
| 290 | error(1, 0, "malloc"); | ||
| 291 | |||
| 292 | fd = socket(family, cfg_proto, cfg_ipproto); | ||
| 293 | if (fd < 0) | ||
| 294 | error(1, errno, "socket"); | ||
| 295 | |||
| 296 | if (cfg_proto == SOCK_STREAM) { | ||
| 297 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, | ||
| 298 | (char*) &val, sizeof(val))) | ||
| 299 | error(1, 0, "setsockopt no nagle"); | ||
| 300 | |||
| 301 | if (family == PF_INET) { | ||
| 302 | if (connect(fd, (void *) &daddr, sizeof(daddr))) | ||
| 303 | error(1, errno, "connect ipv4"); | ||
| 304 | } else { | ||
| 305 | if (connect(fd, (void *) &daddr6, sizeof(daddr6))) | ||
| 306 | error(1, errno, "connect ipv6"); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | if (cfg_do_pktinfo) { | ||
| 311 | if (family == AF_INET6) { | ||
| 312 | if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO, | ||
| 313 | &val, sizeof(val))) | ||
| 314 | error(1, errno, "setsockopt pktinfo ipv6"); | ||
| 315 | } else { | ||
| 316 | if (setsockopt(fd, SOL_IP, IP_PKTINFO, | ||
| 317 | &val, sizeof(val))) | ||
| 318 | error(1, errno, "setsockopt pktinfo ipv4"); | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | opt |= SOF_TIMESTAMPING_SOFTWARE | | ||
| 323 | SOF_TIMESTAMPING_OPT_CMSG | | ||
| 324 | SOF_TIMESTAMPING_OPT_ID; | ||
| 325 | if (cfg_loop_nodata) | ||
| 326 | opt |= SOF_TIMESTAMPING_OPT_TSONLY; | ||
| 327 | |||
| 328 | if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, | ||
| 329 | (char *) &opt, sizeof(opt))) | ||
| 330 | error(1, 0, "setsockopt timestamping"); | ||
| 331 | |||
| 332 | for (i = 0; i < cfg_num_pkts; i++) { | ||
| 333 | memset(&ts_prev, 0, sizeof(ts_prev)); | ||
| 334 | memset(buf, 'a' + i, total_len); | ||
| 335 | |||
| 336 | if (cfg_proto == SOCK_RAW) { | ||
| 337 | struct udphdr *udph; | ||
| 338 | int off = 0; | ||
| 339 | |||
| 340 | if (cfg_ipproto == IPPROTO_RAW) { | ||
| 341 | struct iphdr *iph = (void *) buf; | ||
| 342 | |||
| 343 | memset(iph, 0, sizeof(*iph)); | ||
| 344 | iph->ihl = 5; | ||
| 345 | iph->version = 4; | ||
| 346 | iph->ttl = 2; | ||
| 347 | iph->daddr = daddr.sin_addr.s_addr; | ||
| 348 | iph->protocol = IPPROTO_UDP; | ||
| 349 | /* kernel writes saddr, csum, len */ | ||
| 350 | |||
| 351 | off = sizeof(*iph); | ||
| 352 | } | ||
| 353 | |||
| 354 | udph = (void *) buf + off; | ||
| 355 | udph->source = ntohs(9000); /* random spoof */ | ||
| 356 | udph->dest = ntohs(dest_port); | ||
| 357 | udph->len = ntohs(sizeof(*udph) + cfg_payload_len); | ||
| 358 | udph->check = 0; /* not allowed for IPv6 */ | ||
| 359 | } | ||
| 360 | |||
| 361 | print_timestamp_usr(); | ||
| 362 | if (cfg_proto != SOCK_STREAM) { | ||
| 363 | if (family == PF_INET) | ||
| 364 | val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr)); | ||
| 365 | else | ||
| 366 | val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6)); | ||
| 367 | } else { | ||
| 368 | val = send(fd, buf, cfg_payload_len, 0); | ||
| 369 | } | ||
| 370 | if (val != total_len) | ||
| 371 | error(1, errno, "send"); | ||
| 372 | |||
| 373 | /* wait for all errors to be queued, else ACKs arrive OOO */ | ||
| 374 | usleep(50 * 1000); | ||
| 375 | |||
| 376 | __poll(fd); | ||
| 377 | |||
| 378 | while (!recv_errmsg(fd)) {} | ||
| 379 | } | ||
| 380 | |||
| 381 | if (close(fd)) | ||
| 382 | error(1, errno, "close"); | ||
| 383 | |||
| 384 | free(buf); | ||
| 385 | usleep(400 * 1000); | ||
| 386 | } | ||
| 387 | |||
| 388 | static void __attribute__((noreturn)) usage(const char *filepath) | ||
| 389 | { | ||
| 390 | fprintf(stderr, "\nUsage: %s [options] hostname\n" | ||
| 391 | "\nwhere options are:\n" | ||
| 392 | " -4: only IPv4\n" | ||
| 393 | " -6: only IPv6\n" | ||
| 394 | " -h: show this message\n" | ||
| 395 | " -I: request PKTINFO\n" | ||
| 396 | " -l N: send N bytes at a time\n" | ||
| 397 | " -n: set no-payload option\n" | ||
| 398 | " -r: use raw\n" | ||
| 399 | " -R: use raw (IP_HDRINCL)\n" | ||
| 400 | " -p N: connect to port N\n" | ||
| 401 | " -u: use udp\n" | ||
| 402 | " -x: show payload (up to 70 bytes)\n", | ||
| 403 | filepath); | ||
| 404 | exit(1); | ||
| 405 | } | ||
| 406 | |||
| 407 | static void parse_opt(int argc, char **argv) | ||
| 408 | { | ||
| 409 | int proto_count = 0; | ||
| 410 | char c; | ||
| 411 | |||
| 412 | while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) { | ||
| 413 | switch (c) { | ||
| 414 | case '4': | ||
| 415 | do_ipv6 = 0; | ||
| 416 | break; | ||
| 417 | case '6': | ||
| 418 | do_ipv4 = 0; | ||
| 419 | break; | ||
| 420 | case 'I': | ||
| 421 | cfg_do_pktinfo = true; | ||
| 422 | break; | ||
| 423 | case 'n': | ||
| 424 | cfg_loop_nodata = true; | ||
| 425 | break; | ||
| 426 | case 'r': | ||
| 427 | proto_count++; | ||
| 428 | cfg_proto = SOCK_RAW; | ||
| 429 | cfg_ipproto = IPPROTO_UDP; | ||
| 430 | break; | ||
| 431 | case 'R': | ||
| 432 | proto_count++; | ||
| 433 | cfg_proto = SOCK_RAW; | ||
| 434 | cfg_ipproto = IPPROTO_RAW; | ||
| 435 | break; | ||
| 436 | case 'u': | ||
| 437 | proto_count++; | ||
| 438 | cfg_proto = SOCK_DGRAM; | ||
| 439 | cfg_ipproto = IPPROTO_UDP; | ||
| 440 | break; | ||
| 441 | case 'l': | ||
| 442 | cfg_payload_len = strtoul(optarg, NULL, 10); | ||
| 443 | break; | ||
| 444 | case 'p': | ||
| 445 | dest_port = strtoul(optarg, NULL, 10); | ||
| 446 | break; | ||
| 447 | case 'x': | ||
| 448 | cfg_show_payload = true; | ||
| 449 | break; | ||
| 450 | case 'h': | ||
| 451 | default: | ||
| 452 | usage(argv[0]); | ||
| 453 | } | ||
| 454 | } | ||
| 455 | |||
| 456 | if (!cfg_payload_len) | ||
| 457 | error(1, 0, "payload may not be nonzero"); | ||
| 458 | if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472) | ||
| 459 | error(1, 0, "udp packet might exceed expected MTU"); | ||
| 460 | if (!do_ipv4 && !do_ipv6) | ||
| 461 | error(1, 0, "pass -4 or -6, not both"); | ||
| 462 | if (proto_count > 1) | ||
| 463 | error(1, 0, "pass -r, -R or -u, not multiple"); | ||
| 464 | |||
| 465 | if (optind != argc - 1) | ||
| 466 | error(1, 0, "missing required hostname argument"); | ||
| 467 | } | ||
| 468 | |||
| 469 | static void resolve_hostname(const char *hostname) | ||
| 470 | { | ||
| 471 | struct addrinfo *addrs, *cur; | ||
| 472 | int have_ipv4 = 0, have_ipv6 = 0; | ||
| 473 | |||
| 474 | if (getaddrinfo(hostname, NULL, NULL, &addrs)) | ||
| 475 | error(1, errno, "getaddrinfo"); | ||
| 476 | |||
| 477 | cur = addrs; | ||
| 478 | while (cur && !have_ipv4 && !have_ipv6) { | ||
| 479 | if (!have_ipv4 && cur->ai_family == AF_INET) { | ||
| 480 | memcpy(&daddr, cur->ai_addr, sizeof(daddr)); | ||
| 481 | daddr.sin_port = htons(dest_port); | ||
| 482 | have_ipv4 = 1; | ||
| 483 | } | ||
| 484 | else if (!have_ipv6 && cur->ai_family == AF_INET6) { | ||
| 485 | memcpy(&daddr6, cur->ai_addr, sizeof(daddr6)); | ||
| 486 | daddr6.sin6_port = htons(dest_port); | ||
| 487 | have_ipv6 = 1; | ||
| 488 | } | ||
| 489 | cur = cur->ai_next; | ||
| 490 | } | ||
| 491 | if (addrs) | ||
| 492 | freeaddrinfo(addrs); | ||
| 493 | |||
| 494 | do_ipv4 &= have_ipv4; | ||
| 495 | do_ipv6 &= have_ipv6; | ||
| 496 | } | ||
| 497 | |||
| 498 | static void do_main(int family) | ||
| 499 | { | ||
| 500 | fprintf(stderr, "family: %s\n", | ||
| 501 | family == PF_INET ? "INET" : "INET6"); | ||
| 502 | |||
| 503 | fprintf(stderr, "test SND\n"); | ||
| 504 | do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE); | ||
| 505 | |||
| 506 | fprintf(stderr, "test ENQ\n"); | ||
| 507 | do_test(family, SOF_TIMESTAMPING_TX_SCHED); | ||
| 508 | |||
| 509 | fprintf(stderr, "test ENQ + SND\n"); | ||
| 510 | do_test(family, SOF_TIMESTAMPING_TX_SCHED | | ||
| 511 | SOF_TIMESTAMPING_TX_SOFTWARE); | ||
| 512 | |||
| 513 | if (cfg_proto == SOCK_STREAM) { | ||
| 514 | fprintf(stderr, "\ntest ACK\n"); | ||
| 515 | do_test(family, SOF_TIMESTAMPING_TX_ACK); | ||
| 516 | |||
| 517 | fprintf(stderr, "\ntest SND + ACK\n"); | ||
| 518 | do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE | | ||
| 519 | SOF_TIMESTAMPING_TX_ACK); | ||
| 520 | |||
| 521 | fprintf(stderr, "\ntest ENQ + SND + ACK\n"); | ||
| 522 | do_test(family, SOF_TIMESTAMPING_TX_SCHED | | ||
| 523 | SOF_TIMESTAMPING_TX_SOFTWARE | | ||
| 524 | SOF_TIMESTAMPING_TX_ACK); | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" }; | ||
| 529 | |||
| 530 | int main(int argc, char **argv) | ||
| 531 | { | ||
| 532 | if (argc == 1) | ||
| 533 | usage(argv[0]); | ||
| 534 | |||
| 535 | parse_opt(argc, argv); | ||
| 536 | resolve_hostname(argv[argc - 1]); | ||
| 537 | |||
| 538 | fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]); | ||
| 539 | fprintf(stderr, "payload: %u\n", cfg_payload_len); | ||
| 540 | fprintf(stderr, "server port: %u\n", dest_port); | ||
| 541 | fprintf(stderr, "\n"); | ||
| 542 | |||
| 543 | if (do_ipv4) | ||
| 544 | do_main(PF_INET); | ||
| 545 | if (do_ipv6) | ||
| 546 | do_main(PF_INET6); | ||
| 547 | |||
| 548 | return 0; | ||
| 549 | } | ||
diff --git a/tools/testing/selftests/prctl/.gitignore b/tools/testing/selftests/prctl/.gitignore new file mode 100644 index 000000000000..0b5c27447bf6 --- /dev/null +++ b/tools/testing/selftests/prctl/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | disable-tsc-ctxt-sw-stress-test | ||
| 2 | disable-tsc-on-off-stress-test | ||
| 3 | disable-tsc-test | ||
diff --git a/tools/testing/selftests/prctl/Makefile b/tools/testing/selftests/prctl/Makefile new file mode 100644 index 000000000000..35aa1c8f2df2 --- /dev/null +++ b/tools/testing/selftests/prctl/Makefile | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | ifndef CROSS_COMPILE | ||
| 2 | uname_M := $(shell uname -m 2>/dev/null || echo not) | ||
| 3 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) | ||
| 4 | |||
| 5 | ifeq ($(ARCH),x86) | ||
| 6 | TEST_PROGS := disable-tsc-ctxt-sw-stress-test disable-tsc-on-off-stress-test \ | ||
| 7 | disable-tsc-test | ||
| 8 | all: $(TEST_PROGS) | ||
| 9 | |||
| 10 | include ../lib.mk | ||
| 11 | |||
| 12 | clean: | ||
| 13 | rm -fr $(TEST_PROGS) | ||
| 14 | endif | ||
| 15 | endif | ||
diff --git a/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c b/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c new file mode 100644 index 000000000000..f7499d1c0415 --- /dev/null +++ b/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | /* | ||
| 2 | * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...) | ||
| 3 | * | ||
| 4 | * Tests if the control register is updated correctly | ||
| 5 | * at context switches | ||
| 6 | * | ||
| 7 | * Warning: this test will cause a very high load for a few seconds | ||
| 8 | * | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <unistd.h> | ||
| 14 | #include <signal.h> | ||
| 15 | #include <inttypes.h> | ||
| 16 | #include <wait.h> | ||
| 17 | |||
| 18 | |||
| 19 | #include <sys/prctl.h> | ||
| 20 | #include <linux/prctl.h> | ||
| 21 | |||
| 22 | /* Get/set the process' ability to use the timestamp counter instruction */ | ||
| 23 | #ifndef PR_GET_TSC | ||
| 24 | #define PR_GET_TSC 25 | ||
| 25 | #define PR_SET_TSC 26 | ||
| 26 | # define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */ | ||
| 27 | # define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */ | ||
| 28 | #endif | ||
| 29 | |||
| 30 | static uint64_t rdtsc(void) | ||
| 31 | { | ||
| 32 | uint32_t lo, hi; | ||
| 33 | /* We cannot use "=A", since this would use %rax on x86_64 */ | ||
| 34 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | ||
| 35 | return (uint64_t)hi << 32 | lo; | ||
| 36 | } | ||
| 37 | |||
| 38 | static void sigsegv_expect(int sig) | ||
| 39 | { | ||
| 40 | /* */ | ||
| 41 | } | ||
| 42 | |||
| 43 | static void segvtask(void) | ||
| 44 | { | ||
| 45 | if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0) | ||
| 46 | { | ||
| 47 | perror("prctl"); | ||
| 48 | exit(0); | ||
| 49 | } | ||
| 50 | signal(SIGSEGV, sigsegv_expect); | ||
| 51 | alarm(10); | ||
| 52 | rdtsc(); | ||
| 53 | fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n"); | ||
| 54 | exit(0); | ||
| 55 | } | ||
| 56 | |||
| 57 | |||
| 58 | static void sigsegv_fail(int sig) | ||
| 59 | { | ||
| 60 | fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n"); | ||
| 61 | exit(0); | ||
| 62 | } | ||
| 63 | |||
| 64 | static void rdtsctask(void) | ||
| 65 | { | ||
| 66 | if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0) | ||
| 67 | { | ||
| 68 | perror("prctl"); | ||
| 69 | exit(0); | ||
| 70 | } | ||
| 71 | signal(SIGSEGV, sigsegv_fail); | ||
| 72 | alarm(10); | ||
| 73 | for(;;) rdtsc(); | ||
| 74 | } | ||
| 75 | |||
| 76 | |||
| 77 | int main(void) | ||
| 78 | { | ||
| 79 | int n_tasks = 100, i; | ||
| 80 | |||
| 81 | fprintf(stderr, "[No further output means we're allright]\n"); | ||
| 82 | |||
| 83 | for (i=0; i<n_tasks; i++) | ||
| 84 | if (fork() == 0) | ||
| 85 | { | ||
| 86 | if (i & 1) | ||
| 87 | segvtask(); | ||
| 88 | else | ||
| 89 | rdtsctask(); | ||
| 90 | } | ||
| 91 | |||
| 92 | for (i=0; i<n_tasks; i++) | ||
| 93 | wait(NULL); | ||
| 94 | |||
| 95 | exit(0); | ||
| 96 | } | ||
| 97 | |||
diff --git a/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c b/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c new file mode 100644 index 000000000000..a06f027e9d16 --- /dev/null +++ b/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | /* | ||
| 2 | * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...) | ||
| 3 | * | ||
| 4 | * Tests if the control register is updated correctly | ||
| 5 | * when set with prctl() | ||
| 6 | * | ||
| 7 | * Warning: this test will cause a very high load for a few seconds | ||
| 8 | * | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <unistd.h> | ||
| 14 | #include <signal.h> | ||
| 15 | #include <inttypes.h> | ||
| 16 | #include <wait.h> | ||
| 17 | |||
| 18 | |||
| 19 | #include <sys/prctl.h> | ||
| 20 | #include <linux/prctl.h> | ||
| 21 | |||
| 22 | /* Get/set the process' ability to use the timestamp counter instruction */ | ||
| 23 | #ifndef PR_GET_TSC | ||
| 24 | #define PR_GET_TSC 25 | ||
| 25 | #define PR_SET_TSC 26 | ||
| 26 | # define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */ | ||
| 27 | # define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */ | ||
| 28 | #endif | ||
| 29 | |||
| 30 | /* snippet from wikipedia :-) */ | ||
| 31 | |||
| 32 | static uint64_t rdtsc(void) | ||
| 33 | { | ||
| 34 | uint32_t lo, hi; | ||
| 35 | /* We cannot use "=A", since this would use %rax on x86_64 */ | ||
| 36 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | ||
| 37 | return (uint64_t)hi << 32 | lo; | ||
| 38 | } | ||
| 39 | |||
| 40 | int should_segv = 0; | ||
| 41 | |||
| 42 | static void sigsegv_cb(int sig) | ||
| 43 | { | ||
| 44 | if (!should_segv) | ||
| 45 | { | ||
| 46 | fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n"); | ||
| 47 | exit(0); | ||
| 48 | } | ||
| 49 | if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0) | ||
| 50 | { | ||
| 51 | perror("prctl"); | ||
| 52 | exit(0); | ||
| 53 | } | ||
| 54 | should_segv = 0; | ||
| 55 | |||
| 56 | rdtsc(); | ||
| 57 | } | ||
| 58 | |||
| 59 | static void task(void) | ||
| 60 | { | ||
| 61 | signal(SIGSEGV, sigsegv_cb); | ||
| 62 | alarm(10); | ||
| 63 | for(;;) | ||
| 64 | { | ||
| 65 | rdtsc(); | ||
| 66 | if (should_segv) | ||
| 67 | { | ||
| 68 | fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n"); | ||
| 69 | exit(0); | ||
| 70 | } | ||
| 71 | if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0) | ||
| 72 | { | ||
| 73 | perror("prctl"); | ||
| 74 | exit(0); | ||
| 75 | } | ||
| 76 | should_segv = 1; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | |||
| 81 | int main(void) | ||
| 82 | { | ||
| 83 | int n_tasks = 100, i; | ||
| 84 | |||
| 85 | fprintf(stderr, "[No further output means we're allright]\n"); | ||
| 86 | |||
| 87 | for (i=0; i<n_tasks; i++) | ||
| 88 | if (fork() == 0) | ||
| 89 | task(); | ||
| 90 | |||
| 91 | for (i=0; i<n_tasks; i++) | ||
| 92 | wait(NULL); | ||
| 93 | |||
| 94 | exit(0); | ||
| 95 | } | ||
| 96 | |||
diff --git a/tools/testing/selftests/prctl/disable-tsc-test.c b/tools/testing/selftests/prctl/disable-tsc-test.c new file mode 100644 index 000000000000..8d494f7bebdb --- /dev/null +++ b/tools/testing/selftests/prctl/disable-tsc-test.c | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | /* | ||
| 2 | * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...) | ||
| 3 | * | ||
| 4 | * Basic test to test behaviour of PR_GET_TSC and PR_SET_TSC | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <stdio.h> | ||
| 8 | #include <stdlib.h> | ||
| 9 | #include <unistd.h> | ||
| 10 | #include <signal.h> | ||
| 11 | #include <inttypes.h> | ||
| 12 | |||
| 13 | |||
| 14 | #include <sys/prctl.h> | ||
| 15 | #include <linux/prctl.h> | ||
| 16 | |||
| 17 | /* Get/set the process' ability to use the timestamp counter instruction */ | ||
| 18 | #ifndef PR_GET_TSC | ||
| 19 | #define PR_GET_TSC 25 | ||
| 20 | #define PR_SET_TSC 26 | ||
| 21 | # define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */ | ||
| 22 | # define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */ | ||
| 23 | #endif | ||
| 24 | |||
| 25 | const char *tsc_names[] = | ||
| 26 | { | ||
| 27 | [0] = "[not set]", | ||
| 28 | [PR_TSC_ENABLE] = "PR_TSC_ENABLE", | ||
| 29 | [PR_TSC_SIGSEGV] = "PR_TSC_SIGSEGV", | ||
| 30 | }; | ||
| 31 | |||
| 32 | static uint64_t rdtsc(void) | ||
| 33 | { | ||
| 34 | uint32_t lo, hi; | ||
| 35 | /* We cannot use "=A", since this would use %rax on x86_64 */ | ||
| 36 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | ||
| 37 | return (uint64_t)hi << 32 | lo; | ||
| 38 | } | ||
| 39 | |||
| 40 | static void sigsegv_cb(int sig) | ||
| 41 | { | ||
| 42 | int tsc_val = 0; | ||
| 43 | |||
| 44 | printf("[ SIG_SEGV ]\n"); | ||
| 45 | printf("prctl(PR_GET_TSC, &tsc_val); "); | ||
| 46 | fflush(stdout); | ||
| 47 | |||
| 48 | if ( prctl(PR_GET_TSC, &tsc_val) == -1) | ||
| 49 | perror("prctl"); | ||
| 50 | |||
| 51 | printf("tsc_val == %s\n", tsc_names[tsc_val]); | ||
| 52 | printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n"); | ||
| 53 | fflush(stdout); | ||
| 54 | if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1) | ||
| 55 | perror("prctl"); | ||
| 56 | |||
| 57 | printf("rdtsc() == "); | ||
| 58 | } | ||
| 59 | |||
| 60 | int main(void) | ||
| 61 | { | ||
| 62 | int tsc_val = 0; | ||
| 63 | |||
| 64 | signal(SIGSEGV, sigsegv_cb); | ||
| 65 | |||
| 66 | printf("rdtsc() == %llu\n", (unsigned long long)rdtsc()); | ||
| 67 | printf("prctl(PR_GET_TSC, &tsc_val); "); | ||
| 68 | fflush(stdout); | ||
| 69 | |||
| 70 | if ( prctl(PR_GET_TSC, &tsc_val) == -1) | ||
| 71 | perror("prctl"); | ||
| 72 | |||
| 73 | printf("tsc_val == %s\n", tsc_names[tsc_val]); | ||
| 74 | printf("rdtsc() == %llu\n", (unsigned long long)rdtsc()); | ||
| 75 | printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n"); | ||
| 76 | fflush(stdout); | ||
| 77 | |||
| 78 | if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1) | ||
| 79 | perror("prctl"); | ||
| 80 | |||
| 81 | printf("rdtsc() == %llu\n", (unsigned long long)rdtsc()); | ||
| 82 | printf("prctl(PR_SET_TSC, PR_TSC_SIGSEGV)\n"); | ||
| 83 | fflush(stdout); | ||
| 84 | |||
| 85 | if ( prctl(PR_SET_TSC, PR_TSC_SIGSEGV) == -1) | ||
| 86 | perror("prctl"); | ||
| 87 | |||
| 88 | printf("rdtsc() == "); | ||
| 89 | fflush(stdout); | ||
| 90 | printf("%llu\n", (unsigned long long)rdtsc()); | ||
| 91 | fflush(stdout); | ||
| 92 | |||
| 93 | exit(EXIT_SUCCESS); | ||
| 94 | } | ||
| 95 | |||
diff --git a/tools/testing/selftests/ptp/.gitignore b/tools/testing/selftests/ptp/.gitignore new file mode 100644 index 000000000000..f562e49d6917 --- /dev/null +++ b/tools/testing/selftests/ptp/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| testptp | |||
diff --git a/tools/testing/selftests/ptp/Makefile b/tools/testing/selftests/ptp/Makefile new file mode 100644 index 000000000000..83dd42b2129e --- /dev/null +++ b/tools/testing/selftests/ptp/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | TEST_PROGS := testptp | ||
| 2 | LDLIBS += -lrt | ||
| 3 | all: $(TEST_PROGS) | ||
| 4 | |||
| 5 | include ../lib.mk | ||
| 6 | |||
| 7 | clean: | ||
| 8 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c new file mode 100644 index 000000000000..5d2eae16f7ee --- /dev/null +++ b/tools/testing/selftests/ptp/testptp.c | |||
| @@ -0,0 +1,523 @@ | |||
| 1 | /* | ||
| 2 | * PTP 1588 clock support - User space test program | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 OMICRON electronics GmbH | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | */ | ||
| 20 | #define _GNU_SOURCE | ||
| 21 | #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ | ||
| 22 | #include <errno.h> | ||
| 23 | #include <fcntl.h> | ||
| 24 | #include <inttypes.h> | ||
| 25 | #include <math.h> | ||
| 26 | #include <signal.h> | ||
| 27 | #include <stdio.h> | ||
| 28 | #include <stdlib.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <sys/ioctl.h> | ||
| 31 | #include <sys/mman.h> | ||
| 32 | #include <sys/stat.h> | ||
| 33 | #include <sys/time.h> | ||
| 34 | #include <sys/timex.h> | ||
| 35 | #include <sys/types.h> | ||
| 36 | #include <time.h> | ||
| 37 | #include <unistd.h> | ||
| 38 | |||
| 39 | #include <linux/ptp_clock.h> | ||
| 40 | |||
| 41 | #define DEVICE "/dev/ptp0" | ||
| 42 | |||
| 43 | #ifndef ADJ_SETOFFSET | ||
| 44 | #define ADJ_SETOFFSET 0x0100 | ||
| 45 | #endif | ||
| 46 | |||
| 47 | #ifndef CLOCK_INVALID | ||
| 48 | #define CLOCK_INVALID -1 | ||
| 49 | #endif | ||
| 50 | |||
| 51 | /* clock_adjtime is not available in GLIBC < 2.14 */ | ||
| 52 | #if !__GLIBC_PREREQ(2, 14) | ||
| 53 | #include <sys/syscall.h> | ||
| 54 | static int clock_adjtime(clockid_t id, struct timex *tx) | ||
| 55 | { | ||
| 56 | return syscall(__NR_clock_adjtime, id, tx); | ||
| 57 | } | ||
| 58 | #endif | ||
| 59 | |||
| 60 | static clockid_t get_clockid(int fd) | ||
| 61 | { | ||
| 62 | #define CLOCKFD 3 | ||
| 63 | #define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) | ||
| 64 | |||
| 65 | return FD_TO_CLOCKID(fd); | ||
| 66 | } | ||
| 67 | |||
| 68 | static void handle_alarm(int s) | ||
| 69 | { | ||
| 70 | printf("received signal %d\n", s); | ||
| 71 | } | ||
| 72 | |||
| 73 | static int install_handler(int signum, void (*handler)(int)) | ||
| 74 | { | ||
| 75 | struct sigaction action; | ||
| 76 | sigset_t mask; | ||
| 77 | |||
| 78 | /* Unblock the signal. */ | ||
| 79 | sigemptyset(&mask); | ||
| 80 | sigaddset(&mask, signum); | ||
| 81 | sigprocmask(SIG_UNBLOCK, &mask, NULL); | ||
| 82 | |||
| 83 | /* Install the signal handler. */ | ||
| 84 | action.sa_handler = handler; | ||
| 85 | action.sa_flags = 0; | ||
| 86 | sigemptyset(&action.sa_mask); | ||
| 87 | sigaction(signum, &action, NULL); | ||
| 88 | |||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | static long ppb_to_scaled_ppm(int ppb) | ||
| 93 | { | ||
| 94 | /* | ||
| 95 | * The 'freq' field in the 'struct timex' is in parts per | ||
| 96 | * million, but with a 16 bit binary fractional field. | ||
| 97 | * Instead of calculating either one of | ||
| 98 | * | ||
| 99 | * scaled_ppm = (ppb / 1000) << 16 [1] | ||
| 100 | * scaled_ppm = (ppb << 16) / 1000 [2] | ||
| 101 | * | ||
| 102 | * we simply use double precision math, in order to avoid the | ||
| 103 | * truncation in [1] and the possible overflow in [2]. | ||
| 104 | */ | ||
| 105 | return (long) (ppb * 65.536); | ||
| 106 | } | ||
| 107 | |||
| 108 | static int64_t pctns(struct ptp_clock_time *t) | ||
| 109 | { | ||
| 110 | return t->sec * 1000000000LL + t->nsec; | ||
| 111 | } | ||
| 112 | |||
| 113 | static void usage(char *progname) | ||
| 114 | { | ||
| 115 | fprintf(stderr, | ||
| 116 | "usage: %s [options]\n" | ||
| 117 | " -a val request a one-shot alarm after 'val' seconds\n" | ||
| 118 | " -A val request a periodic alarm every 'val' seconds\n" | ||
| 119 | " -c query the ptp clock's capabilities\n" | ||
| 120 | " -d name device to open\n" | ||
| 121 | " -e val read 'val' external time stamp events\n" | ||
| 122 | " -f val adjust the ptp clock frequency by 'val' ppb\n" | ||
| 123 | " -g get the ptp clock time\n" | ||
| 124 | " -h prints this message\n" | ||
| 125 | " -i val index for event/trigger\n" | ||
| 126 | " -k val measure the time offset between system and phc clock\n" | ||
| 127 | " for 'val' times (Maximum 25)\n" | ||
| 128 | " -l list the current pin configuration\n" | ||
| 129 | " -L pin,val configure pin index 'pin' with function 'val'\n" | ||
| 130 | " the channel index is taken from the '-i' option\n" | ||
| 131 | " 'val' specifies the auxiliary function:\n" | ||
| 132 | " 0 - none\n" | ||
| 133 | " 1 - external time stamp\n" | ||
| 134 | " 2 - periodic output\n" | ||
| 135 | " -p val enable output with a period of 'val' nanoseconds\n" | ||
| 136 | " -P val enable or disable (val=1|0) the system clock PPS\n" | ||
| 137 | " -s set the ptp clock time from the system time\n" | ||
| 138 | " -S set the system time from the ptp clock time\n" | ||
| 139 | " -t val shift the ptp clock time by 'val' seconds\n" | ||
| 140 | " -T val set the ptp clock time to 'val' seconds\n", | ||
| 141 | progname); | ||
| 142 | } | ||
| 143 | |||
| 144 | int main(int argc, char *argv[]) | ||
| 145 | { | ||
| 146 | struct ptp_clock_caps caps; | ||
| 147 | struct ptp_extts_event event; | ||
| 148 | struct ptp_extts_request extts_request; | ||
| 149 | struct ptp_perout_request perout_request; | ||
| 150 | struct ptp_pin_desc desc; | ||
| 151 | struct timespec ts; | ||
| 152 | struct timex tx; | ||
| 153 | |||
| 154 | static timer_t timerid; | ||
| 155 | struct itimerspec timeout; | ||
| 156 | struct sigevent sigevent; | ||
| 157 | |||
| 158 | struct ptp_clock_time *pct; | ||
| 159 | struct ptp_sys_offset *sysoff; | ||
| 160 | |||
| 161 | |||
| 162 | char *progname; | ||
| 163 | unsigned int i; | ||
| 164 | int c, cnt, fd; | ||
| 165 | |||
| 166 | char *device = DEVICE; | ||
| 167 | clockid_t clkid; | ||
| 168 | int adjfreq = 0x7fffffff; | ||
| 169 | int adjtime = 0; | ||
| 170 | int capabilities = 0; | ||
| 171 | int extts = 0; | ||
| 172 | int gettime = 0; | ||
| 173 | int index = 0; | ||
| 174 | int list_pins = 0; | ||
| 175 | int oneshot = 0; | ||
| 176 | int pct_offset = 0; | ||
| 177 | int n_samples = 0; | ||
| 178 | int periodic = 0; | ||
| 179 | int perout = -1; | ||
| 180 | int pin_index = -1, pin_func; | ||
| 181 | int pps = -1; | ||
| 182 | int seconds = 0; | ||
| 183 | int settime = 0; | ||
| 184 | |||
| 185 | int64_t t1, t2, tp; | ||
| 186 | int64_t interval, offset; | ||
| 187 | |||
| 188 | progname = strrchr(argv[0], '/'); | ||
| 189 | progname = progname ? 1+progname : argv[0]; | ||
| 190 | while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) { | ||
| 191 | switch (c) { | ||
| 192 | case 'a': | ||
| 193 | oneshot = atoi(optarg); | ||
| 194 | break; | ||
| 195 | case 'A': | ||
| 196 | periodic = atoi(optarg); | ||
| 197 | break; | ||
| 198 | case 'c': | ||
| 199 | capabilities = 1; | ||
| 200 | break; | ||
| 201 | case 'd': | ||
| 202 | device = optarg; | ||
| 203 | break; | ||
| 204 | case 'e': | ||
| 205 | extts = atoi(optarg); | ||
| 206 | break; | ||
| 207 | case 'f': | ||
| 208 | adjfreq = atoi(optarg); | ||
| 209 | break; | ||
| 210 | case 'g': | ||
| 211 | gettime = 1; | ||
| 212 | break; | ||
| 213 | case 'i': | ||
| 214 | index = atoi(optarg); | ||
| 215 | break; | ||
| 216 | case 'k': | ||
| 217 | pct_offset = 1; | ||
| 218 | n_samples = atoi(optarg); | ||
| 219 | break; | ||
| 220 | case 'l': | ||
| 221 | list_pins = 1; | ||
| 222 | break; | ||
| 223 | case 'L': | ||
| 224 | cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func); | ||
| 225 | if (cnt != 2) { | ||
| 226 | usage(progname); | ||
| 227 | return -1; | ||
| 228 | } | ||
| 229 | break; | ||
| 230 | case 'p': | ||
| 231 | perout = atoi(optarg); | ||
| 232 | break; | ||
| 233 | case 'P': | ||
| 234 | pps = atoi(optarg); | ||
| 235 | break; | ||
| 236 | case 's': | ||
| 237 | settime = 1; | ||
| 238 | break; | ||
| 239 | case 'S': | ||
| 240 | settime = 2; | ||
| 241 | break; | ||
| 242 | case 't': | ||
| 243 | adjtime = atoi(optarg); | ||
| 244 | break; | ||
| 245 | case 'T': | ||
| 246 | settime = 3; | ||
| 247 | seconds = atoi(optarg); | ||
| 248 | break; | ||
| 249 | case 'h': | ||
| 250 | usage(progname); | ||
| 251 | return 0; | ||
| 252 | case '?': | ||
| 253 | default: | ||
| 254 | usage(progname); | ||
| 255 | return -1; | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | fd = open(device, O_RDWR); | ||
| 260 | if (fd < 0) { | ||
| 261 | fprintf(stderr, "opening %s: %s\n", device, strerror(errno)); | ||
| 262 | return -1; | ||
| 263 | } | ||
| 264 | |||
| 265 | clkid = get_clockid(fd); | ||
| 266 | if (CLOCK_INVALID == clkid) { | ||
| 267 | fprintf(stderr, "failed to read clock id\n"); | ||
| 268 | return -1; | ||
| 269 | } | ||
| 270 | |||
| 271 | if (capabilities) { | ||
| 272 | if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) { | ||
| 273 | perror("PTP_CLOCK_GETCAPS"); | ||
| 274 | } else { | ||
| 275 | printf("capabilities:\n" | ||
| 276 | " %d maximum frequency adjustment (ppb)\n" | ||
| 277 | " %d programmable alarms\n" | ||
| 278 | " %d external time stamp channels\n" | ||
| 279 | " %d programmable periodic signals\n" | ||
| 280 | " %d pulse per second\n" | ||
| 281 | " %d programmable pins\n" | ||
| 282 | " %d cross timestamping\n", | ||
| 283 | caps.max_adj, | ||
| 284 | caps.n_alarm, | ||
| 285 | caps.n_ext_ts, | ||
| 286 | caps.n_per_out, | ||
| 287 | caps.pps, | ||
| 288 | caps.n_pins, | ||
| 289 | caps.cross_timestamping); | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | if (0x7fffffff != adjfreq) { | ||
| 294 | memset(&tx, 0, sizeof(tx)); | ||
| 295 | tx.modes = ADJ_FREQUENCY; | ||
| 296 | tx.freq = ppb_to_scaled_ppm(adjfreq); | ||
| 297 | if (clock_adjtime(clkid, &tx)) { | ||
| 298 | perror("clock_adjtime"); | ||
| 299 | } else { | ||
| 300 | puts("frequency adjustment okay"); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | if (adjtime) { | ||
| 305 | memset(&tx, 0, sizeof(tx)); | ||
| 306 | tx.modes = ADJ_SETOFFSET; | ||
| 307 | tx.time.tv_sec = adjtime; | ||
| 308 | tx.time.tv_usec = 0; | ||
| 309 | if (clock_adjtime(clkid, &tx) < 0) { | ||
| 310 | perror("clock_adjtime"); | ||
| 311 | } else { | ||
| 312 | puts("time shift okay"); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | if (gettime) { | ||
| 317 | if (clock_gettime(clkid, &ts)) { | ||
| 318 | perror("clock_gettime"); | ||
| 319 | } else { | ||
| 320 | printf("clock time: %ld.%09ld or %s", | ||
| 321 | ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec)); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | if (settime == 1) { | ||
| 326 | clock_gettime(CLOCK_REALTIME, &ts); | ||
| 327 | if (clock_settime(clkid, &ts)) { | ||
| 328 | perror("clock_settime"); | ||
| 329 | } else { | ||
| 330 | puts("set time okay"); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | if (settime == 2) { | ||
| 335 | clock_gettime(clkid, &ts); | ||
| 336 | if (clock_settime(CLOCK_REALTIME, &ts)) { | ||
| 337 | perror("clock_settime"); | ||
| 338 | } else { | ||
| 339 | puts("set time okay"); | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | if (settime == 3) { | ||
| 344 | ts.tv_sec = seconds; | ||
| 345 | ts.tv_nsec = 0; | ||
| 346 | if (clock_settime(clkid, &ts)) { | ||
| 347 | perror("clock_settime"); | ||
| 348 | } else { | ||
| 349 | puts("set time okay"); | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | if (extts) { | ||
| 354 | memset(&extts_request, 0, sizeof(extts_request)); | ||
| 355 | extts_request.index = index; | ||
| 356 | extts_request.flags = PTP_ENABLE_FEATURE; | ||
| 357 | if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) { | ||
| 358 | perror("PTP_EXTTS_REQUEST"); | ||
| 359 | extts = 0; | ||
| 360 | } else { | ||
| 361 | puts("external time stamp request okay"); | ||
| 362 | } | ||
| 363 | for (; extts; extts--) { | ||
| 364 | cnt = read(fd, &event, sizeof(event)); | ||
| 365 | if (cnt != sizeof(event)) { | ||
| 366 | perror("read"); | ||
| 367 | break; | ||
| 368 | } | ||
| 369 | printf("event index %u at %lld.%09u\n", event.index, | ||
| 370 | event.t.sec, event.t.nsec); | ||
| 371 | fflush(stdout); | ||
| 372 | } | ||
| 373 | /* Disable the feature again. */ | ||
| 374 | extts_request.flags = 0; | ||
| 375 | if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) { | ||
| 376 | perror("PTP_EXTTS_REQUEST"); | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | if (list_pins) { | ||
| 381 | int n_pins = 0; | ||
| 382 | if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) { | ||
| 383 | perror("PTP_CLOCK_GETCAPS"); | ||
| 384 | } else { | ||
| 385 | n_pins = caps.n_pins; | ||
| 386 | } | ||
| 387 | for (i = 0; i < n_pins; i++) { | ||
| 388 | desc.index = i; | ||
| 389 | if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) { | ||
| 390 | perror("PTP_PIN_GETFUNC"); | ||
| 391 | break; | ||
| 392 | } | ||
| 393 | printf("name %s index %u func %u chan %u\n", | ||
| 394 | desc.name, desc.index, desc.func, desc.chan); | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | if (oneshot) { | ||
| 399 | install_handler(SIGALRM, handle_alarm); | ||
| 400 | /* Create a timer. */ | ||
| 401 | sigevent.sigev_notify = SIGEV_SIGNAL; | ||
| 402 | sigevent.sigev_signo = SIGALRM; | ||
| 403 | if (timer_create(clkid, &sigevent, &timerid)) { | ||
| 404 | perror("timer_create"); | ||
| 405 | return -1; | ||
| 406 | } | ||
| 407 | /* Start the timer. */ | ||
| 408 | memset(&timeout, 0, sizeof(timeout)); | ||
| 409 | timeout.it_value.tv_sec = oneshot; | ||
| 410 | if (timer_settime(timerid, 0, &timeout, NULL)) { | ||
| 411 | perror("timer_settime"); | ||
| 412 | return -1; | ||
| 413 | } | ||
| 414 | pause(); | ||
| 415 | timer_delete(timerid); | ||
| 416 | } | ||
| 417 | |||
| 418 | if (periodic) { | ||
| 419 | install_handler(SIGALRM, handle_alarm); | ||
| 420 | /* Create a timer. */ | ||
| 421 | sigevent.sigev_notify = SIGEV_SIGNAL; | ||
| 422 | sigevent.sigev_signo = SIGALRM; | ||
| 423 | if (timer_create(clkid, &sigevent, &timerid)) { | ||
| 424 | perror("timer_create"); | ||
| 425 | return -1; | ||
| 426 | } | ||
| 427 | /* Start the timer. */ | ||
| 428 | memset(&timeout, 0, sizeof(timeout)); | ||
| 429 | timeout.it_interval.tv_sec = periodic; | ||
| 430 | timeout.it_value.tv_sec = periodic; | ||
| 431 | if (timer_settime(timerid, 0, &timeout, NULL)) { | ||
| 432 | perror("timer_settime"); | ||
| 433 | return -1; | ||
| 434 | } | ||
| 435 | while (1) { | ||
| 436 | pause(); | ||
| 437 | } | ||
| 438 | timer_delete(timerid); | ||
| 439 | } | ||
| 440 | |||
| 441 | if (perout >= 0) { | ||
| 442 | if (clock_gettime(clkid, &ts)) { | ||
| 443 | perror("clock_gettime"); | ||
| 444 | return -1; | ||
| 445 | } | ||
| 446 | memset(&perout_request, 0, sizeof(perout_request)); | ||
| 447 | perout_request.index = index; | ||
| 448 | perout_request.start.sec = ts.tv_sec + 2; | ||
| 449 | perout_request.start.nsec = 0; | ||
| 450 | perout_request.period.sec = 0; | ||
| 451 | perout_request.period.nsec = perout; | ||
| 452 | if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) { | ||
| 453 | perror("PTP_PEROUT_REQUEST"); | ||
| 454 | } else { | ||
| 455 | puts("periodic output request okay"); | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | if (pin_index >= 0) { | ||
| 460 | memset(&desc, 0, sizeof(desc)); | ||
| 461 | desc.index = pin_index; | ||
| 462 | desc.func = pin_func; | ||
| 463 | desc.chan = index; | ||
| 464 | if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) { | ||
| 465 | perror("PTP_PIN_SETFUNC"); | ||
| 466 | } else { | ||
| 467 | puts("set pin function okay"); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | if (pps != -1) { | ||
| 472 | int enable = pps ? 1 : 0; | ||
| 473 | if (ioctl(fd, PTP_ENABLE_PPS, enable)) { | ||
| 474 | perror("PTP_ENABLE_PPS"); | ||
| 475 | } else { | ||
| 476 | puts("pps for system time request okay"); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | if (pct_offset) { | ||
| 481 | if (n_samples <= 0 || n_samples > 25) { | ||
| 482 | puts("n_samples should be between 1 and 25"); | ||
| 483 | usage(progname); | ||
| 484 | return -1; | ||
| 485 | } | ||
| 486 | |||
| 487 | sysoff = calloc(1, sizeof(*sysoff)); | ||
| 488 | if (!sysoff) { | ||
| 489 | perror("calloc"); | ||
| 490 | return -1; | ||
| 491 | } | ||
| 492 | sysoff->n_samples = n_samples; | ||
| 493 | |||
| 494 | if (ioctl(fd, PTP_SYS_OFFSET, sysoff)) | ||
| 495 | perror("PTP_SYS_OFFSET"); | ||
| 496 | else | ||
| 497 | puts("system and phc clock time offset request okay"); | ||
| 498 | |||
| 499 | pct = &sysoff->ts[0]; | ||
| 500 | for (i = 0; i < sysoff->n_samples; i++) { | ||
| 501 | t1 = pctns(pct+2*i); | ||
| 502 | tp = pctns(pct+2*i+1); | ||
| 503 | t2 = pctns(pct+2*i+2); | ||
| 504 | interval = t2 - t1; | ||
| 505 | offset = (t2 + t1) / 2 - tp; | ||
| 506 | |||
| 507 | printf("system time: %lld.%u\n", | ||
| 508 | (pct+2*i)->sec, (pct+2*i)->nsec); | ||
| 509 | printf("phc time: %lld.%u\n", | ||
| 510 | (pct+2*i+1)->sec, (pct+2*i+1)->nsec); | ||
| 511 | printf("system time: %lld.%u\n", | ||
| 512 | (pct+2*i+2)->sec, (pct+2*i+2)->nsec); | ||
| 513 | printf("system/phc clock time offset is %" PRId64 " ns\n" | ||
| 514 | "system clock time delay is %" PRId64 " ns\n", | ||
| 515 | offset, interval); | ||
| 516 | } | ||
| 517 | |||
| 518 | free(sysoff); | ||
| 519 | } | ||
| 520 | |||
| 521 | close(fd); | ||
| 522 | return 0; | ||
| 523 | } | ||
diff --git a/tools/testing/selftests/ptp/testptp.mk b/tools/testing/selftests/ptp/testptp.mk new file mode 100644 index 000000000000..4ef2d9755421 --- /dev/null +++ b/tools/testing/selftests/ptp/testptp.mk | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | # PTP 1588 clock support - User space test program | ||
| 2 | # | ||
| 3 | # Copyright (C) 2010 OMICRON electronics GmbH | ||
| 4 | # | ||
| 5 | # This program is free software; you can redistribute it and/or modify | ||
| 6 | # it under the terms of the GNU General Public License as published by | ||
| 7 | # the Free Software Foundation; either version 2 of the License, or | ||
| 8 | # (at your option) any later version. | ||
| 9 | # | ||
| 10 | # This program is distributed in the hope that it will be useful, | ||
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | # GNU General Public License for more details. | ||
| 14 | # | ||
| 15 | # You should have received a copy of the GNU General Public License | ||
| 16 | # along with this program; if not, write to the Free Software | ||
| 17 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 18 | |||
| 19 | CC = $(CROSS_COMPILE)gcc | ||
| 20 | INC = -I$(KBUILD_OUTPUT)/usr/include | ||
| 21 | CFLAGS = -Wall $(INC) | ||
| 22 | LDLIBS = -lrt | ||
| 23 | PROGS = testptp | ||
| 24 | |||
| 25 | all: $(PROGS) | ||
| 26 | |||
| 27 | testptp: testptp.o | ||
| 28 | |||
| 29 | clean: | ||
| 30 | rm -f testptp.o | ||
| 31 | |||
| 32 | distclean: clean | ||
| 33 | rm -f $(PROGS) | ||
diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c index 5a246a02dff3..15cf56d32155 100644 --- a/tools/testing/selftests/timers/posix_timers.c +++ b/tools/testing/selftests/timers/posix_timers.c | |||
| @@ -122,7 +122,7 @@ static int check_itimer(int which) | |||
| 122 | else if (which == ITIMER_REAL) | 122 | else if (which == ITIMER_REAL) |
| 123 | idle_loop(); | 123 | idle_loop(); |
| 124 | 124 | ||
| 125 | gettimeofday(&end, NULL); | 125 | err = gettimeofday(&end, NULL); |
| 126 | if (err < 0) { | 126 | if (err < 0) { |
| 127 | perror("Can't call gettimeofday()\n"); | 127 | perror("Can't call gettimeofday()\n"); |
| 128 | return -1; | 128 | return -1; |
| @@ -175,7 +175,7 @@ static int check_timer_create(int which) | |||
| 175 | 175 | ||
| 176 | user_loop(); | 176 | user_loop(); |
| 177 | 177 | ||
| 178 | gettimeofday(&end, NULL); | 178 | err = gettimeofday(&end, NULL); |
| 179 | if (err < 0) { | 179 | if (err < 0) { |
| 180 | perror("Can't call gettimeofday()\n"); | 180 | perror("Can't call gettimeofday()\n"); |
| 181 | return -1; | 181 | return -1; |
diff --git a/tools/testing/selftests/vDSO/.gitignore b/tools/testing/selftests/vDSO/.gitignore new file mode 100644 index 000000000000..133bf9ee986c --- /dev/null +++ b/tools/testing/selftests/vDSO/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | vdso_test | ||
| 2 | vdso_standalone_test_x86 | ||
diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile new file mode 100644 index 000000000000..706b68b1c372 --- /dev/null +++ b/tools/testing/selftests/vDSO/Makefile | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | ifndef CROSS_COMPILE | ||
| 2 | CFLAGS := -std=gnu99 | ||
| 3 | CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector | ||
| 4 | ifeq ($(CONFIG_X86_32),y) | ||
| 5 | LDLIBS += -lgcc_s | ||
| 6 | endif | ||
| 7 | |||
| 8 | TEST_PROGS := vdso_test vdso_standalone_test_x86 | ||
| 9 | |||
| 10 | all: $(TEST_PROGS) | ||
| 11 | vdso_test: parse_vdso.c vdso_test.c | ||
| 12 | vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c | ||
| 13 | $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \ | ||
| 14 | vdso_standalone_test_x86.c parse_vdso.c \ | ||
| 15 | -o vdso_standalone_test_x86 | ||
| 16 | |||
| 17 | include ../lib.mk | ||
| 18 | clean: | ||
| 19 | rm -fr $(TEST_PROGS) | ||
| 20 | endif | ||
diff --git a/tools/testing/selftests/vDSO/parse_vdso.c b/tools/testing/selftests/vDSO/parse_vdso.c new file mode 100644 index 000000000000..1dbb4b87268f --- /dev/null +++ b/tools/testing/selftests/vDSO/parse_vdso.c | |||
| @@ -0,0 +1,269 @@ | |||
| 1 | /* | ||
| 2 | * parse_vdso.c: Linux reference vDSO parser | ||
| 3 | * Written by Andrew Lutomirski, 2011-2014. | ||
| 4 | * | ||
| 5 | * This code is meant to be linked in to various programs that run on Linux. | ||
| 6 | * As such, it is available with as few restrictions as possible. This file | ||
| 7 | * is licensed under the Creative Commons Zero License, version 1.0, | ||
| 8 | * available at http://creativecommons.org/publicdomain/zero/1.0/legalcode | ||
| 9 | * | ||
| 10 | * The vDSO is a regular ELF DSO that the kernel maps into user space when | ||
| 11 | * it starts a program. It works equally well in statically and dynamically | ||
| 12 | * linked binaries. | ||
| 13 | * | ||
| 14 | * This code is tested on x86. In principle it should work on any | ||
| 15 | * architecture that has a vDSO. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <stdbool.h> | ||
| 19 | #include <stdint.h> | ||
| 20 | #include <string.h> | ||
| 21 | #include <limits.h> | ||
| 22 | #include <elf.h> | ||
| 23 | |||
| 24 | /* | ||
| 25 | * To use this vDSO parser, first call one of the vdso_init_* functions. | ||
| 26 | * If you've already parsed auxv, then pass the value of AT_SYSINFO_EHDR | ||
| 27 | * to vdso_init_from_sysinfo_ehdr. Otherwise pass auxv to vdso_init_from_auxv. | ||
| 28 | * Then call vdso_sym for each symbol you want. For example, to look up | ||
| 29 | * gettimeofday on x86_64, use: | ||
| 30 | * | ||
| 31 | * <some pointer> = vdso_sym("LINUX_2.6", "gettimeofday"); | ||
| 32 | * or | ||
| 33 | * <some pointer> = vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | ||
| 34 | * | ||
| 35 | * vdso_sym will return 0 if the symbol doesn't exist or if the init function | ||
| 36 | * failed or was not called. vdso_sym is a little slow, so its return value | ||
| 37 | * should be cached. | ||
| 38 | * | ||
| 39 | * vdso_sym is threadsafe; the init functions are not. | ||
| 40 | * | ||
| 41 | * These are the prototypes: | ||
| 42 | */ | ||
| 43 | extern void vdso_init_from_auxv(void *auxv); | ||
| 44 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | ||
| 45 | extern void *vdso_sym(const char *version, const char *name); | ||
| 46 | |||
| 47 | |||
| 48 | /* And here's the code. */ | ||
| 49 | #ifndef ELF_BITS | ||
| 50 | # if ULONG_MAX > 0xffffffffUL | ||
| 51 | # define ELF_BITS 64 | ||
| 52 | # else | ||
| 53 | # define ELF_BITS 32 | ||
| 54 | # endif | ||
| 55 | #endif | ||
| 56 | |||
| 57 | #define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x | ||
| 58 | #define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) | ||
| 59 | #define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) | ||
| 60 | |||
| 61 | static struct vdso_info | ||
| 62 | { | ||
| 63 | bool valid; | ||
| 64 | |||
| 65 | /* Load information */ | ||
| 66 | uintptr_t load_addr; | ||
| 67 | uintptr_t load_offset; /* load_addr - recorded vaddr */ | ||
| 68 | |||
| 69 | /* Symbol table */ | ||
| 70 | ELF(Sym) *symtab; | ||
| 71 | const char *symstrings; | ||
| 72 | ELF(Word) *bucket, *chain; | ||
| 73 | ELF(Word) nbucket, nchain; | ||
| 74 | |||
| 75 | /* Version table */ | ||
| 76 | ELF(Versym) *versym; | ||
| 77 | ELF(Verdef) *verdef; | ||
| 78 | } vdso_info; | ||
| 79 | |||
| 80 | /* Straight from the ELF specification. */ | ||
| 81 | static unsigned long elf_hash(const unsigned char *name) | ||
| 82 | { | ||
| 83 | unsigned long h = 0, g; | ||
| 84 | while (*name) | ||
| 85 | { | ||
| 86 | h = (h << 4) + *name++; | ||
| 87 | if (g = h & 0xf0000000) | ||
| 88 | h ^= g >> 24; | ||
| 89 | h &= ~g; | ||
| 90 | } | ||
| 91 | return h; | ||
| 92 | } | ||
| 93 | |||
| 94 | void vdso_init_from_sysinfo_ehdr(uintptr_t base) | ||
| 95 | { | ||
| 96 | size_t i; | ||
| 97 | bool found_vaddr = false; | ||
| 98 | |||
| 99 | vdso_info.valid = false; | ||
| 100 | |||
| 101 | vdso_info.load_addr = base; | ||
| 102 | |||
| 103 | ELF(Ehdr) *hdr = (ELF(Ehdr)*)base; | ||
| 104 | if (hdr->e_ident[EI_CLASS] != | ||
| 105 | (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) { | ||
| 106 | return; /* Wrong ELF class -- check ELF_BITS */ | ||
| 107 | } | ||
| 108 | |||
| 109 | ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff); | ||
| 110 | ELF(Dyn) *dyn = 0; | ||
| 111 | |||
| 112 | /* | ||
| 113 | * We need two things from the segment table: the load offset | ||
| 114 | * and the dynamic table. | ||
| 115 | */ | ||
| 116 | for (i = 0; i < hdr->e_phnum; i++) | ||
| 117 | { | ||
| 118 | if (pt[i].p_type == PT_LOAD && !found_vaddr) { | ||
| 119 | found_vaddr = true; | ||
| 120 | vdso_info.load_offset = base | ||
| 121 | + (uintptr_t)pt[i].p_offset | ||
| 122 | - (uintptr_t)pt[i].p_vaddr; | ||
| 123 | } else if (pt[i].p_type == PT_DYNAMIC) { | ||
| 124 | dyn = (ELF(Dyn)*)(base + pt[i].p_offset); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | if (!found_vaddr || !dyn) | ||
| 129 | return; /* Failed */ | ||
| 130 | |||
| 131 | /* | ||
| 132 | * Fish out the useful bits of the dynamic table. | ||
| 133 | */ | ||
| 134 | ELF(Word) *hash = 0; | ||
| 135 | vdso_info.symstrings = 0; | ||
| 136 | vdso_info.symtab = 0; | ||
| 137 | vdso_info.versym = 0; | ||
| 138 | vdso_info.verdef = 0; | ||
| 139 | for (i = 0; dyn[i].d_tag != DT_NULL; i++) { | ||
| 140 | switch (dyn[i].d_tag) { | ||
| 141 | case DT_STRTAB: | ||
| 142 | vdso_info.symstrings = (const char *) | ||
| 143 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
| 144 | + vdso_info.load_offset); | ||
| 145 | break; | ||
| 146 | case DT_SYMTAB: | ||
| 147 | vdso_info.symtab = (ELF(Sym) *) | ||
| 148 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
| 149 | + vdso_info.load_offset); | ||
| 150 | break; | ||
| 151 | case DT_HASH: | ||
| 152 | hash = (ELF(Word) *) | ||
| 153 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
| 154 | + vdso_info.load_offset); | ||
| 155 | break; | ||
| 156 | case DT_VERSYM: | ||
| 157 | vdso_info.versym = (ELF(Versym) *) | ||
| 158 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
| 159 | + vdso_info.load_offset); | ||
| 160 | break; | ||
| 161 | case DT_VERDEF: | ||
| 162 | vdso_info.verdef = (ELF(Verdef) *) | ||
| 163 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
| 164 | + vdso_info.load_offset); | ||
| 165 | break; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | if (!vdso_info.symstrings || !vdso_info.symtab || !hash) | ||
| 169 | return; /* Failed */ | ||
| 170 | |||
| 171 | if (!vdso_info.verdef) | ||
| 172 | vdso_info.versym = 0; | ||
| 173 | |||
| 174 | /* Parse the hash table header. */ | ||
| 175 | vdso_info.nbucket = hash[0]; | ||
| 176 | vdso_info.nchain = hash[1]; | ||
| 177 | vdso_info.bucket = &hash[2]; | ||
| 178 | vdso_info.chain = &hash[vdso_info.nbucket + 2]; | ||
| 179 | |||
| 180 | /* That's all we need. */ | ||
| 181 | vdso_info.valid = true; | ||
| 182 | } | ||
| 183 | |||
| 184 | static bool vdso_match_version(ELF(Versym) ver, | ||
| 185 | const char *name, ELF(Word) hash) | ||
| 186 | { | ||
| 187 | /* | ||
| 188 | * This is a helper function to check if the version indexed by | ||
| 189 | * ver matches name (which hashes to hash). | ||
| 190 | * | ||
| 191 | * The version definition table is a mess, and I don't know how | ||
| 192 | * to do this in better than linear time without allocating memory | ||
| 193 | * to build an index. I also don't know why the table has | ||
| 194 | * variable size entries in the first place. | ||
| 195 | * | ||
| 196 | * For added fun, I can't find a comprehensible specification of how | ||
| 197 | * to parse all the weird flags in the table. | ||
| 198 | * | ||
| 199 | * So I just parse the whole table every time. | ||
| 200 | */ | ||
| 201 | |||
| 202 | /* First step: find the version definition */ | ||
| 203 | ver &= 0x7fff; /* Apparently bit 15 means "hidden" */ | ||
| 204 | ELF(Verdef) *def = vdso_info.verdef; | ||
| 205 | while(true) { | ||
| 206 | if ((def->vd_flags & VER_FLG_BASE) == 0 | ||
| 207 | && (def->vd_ndx & 0x7fff) == ver) | ||
| 208 | break; | ||
| 209 | |||
| 210 | if (def->vd_next == 0) | ||
| 211 | return false; /* No definition. */ | ||
| 212 | |||
| 213 | def = (ELF(Verdef) *)((char *)def + def->vd_next); | ||
| 214 | } | ||
| 215 | |||
| 216 | /* Now figure out whether it matches. */ | ||
| 217 | ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux); | ||
| 218 | return def->vd_hash == hash | ||
| 219 | && !strcmp(name, vdso_info.symstrings + aux->vda_name); | ||
| 220 | } | ||
| 221 | |||
| 222 | void *vdso_sym(const char *version, const char *name) | ||
| 223 | { | ||
| 224 | unsigned long ver_hash; | ||
| 225 | if (!vdso_info.valid) | ||
| 226 | return 0; | ||
| 227 | |||
| 228 | ver_hash = elf_hash(version); | ||
| 229 | ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; | ||
| 230 | |||
| 231 | for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) { | ||
| 232 | ELF(Sym) *sym = &vdso_info.symtab[chain]; | ||
| 233 | |||
| 234 | /* Check for a defined global or weak function w/ right name. */ | ||
| 235 | if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) | ||
| 236 | continue; | ||
| 237 | if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && | ||
| 238 | ELF64_ST_BIND(sym->st_info) != STB_WEAK) | ||
| 239 | continue; | ||
| 240 | if (sym->st_shndx == SHN_UNDEF) | ||
| 241 | continue; | ||
| 242 | if (strcmp(name, vdso_info.symstrings + sym->st_name)) | ||
| 243 | continue; | ||
| 244 | |||
| 245 | /* Check symbol version. */ | ||
| 246 | if (vdso_info.versym | ||
| 247 | && !vdso_match_version(vdso_info.versym[chain], | ||
| 248 | version, ver_hash)) | ||
| 249 | continue; | ||
| 250 | |||
| 251 | return (void *)(vdso_info.load_offset + sym->st_value); | ||
| 252 | } | ||
| 253 | |||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | |||
| 257 | void vdso_init_from_auxv(void *auxv) | ||
| 258 | { | ||
| 259 | ELF(auxv_t) *elf_auxv = auxv; | ||
| 260 | for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++) | ||
| 261 | { | ||
| 262 | if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) { | ||
| 263 | vdso_init_from_sysinfo_ehdr(elf_auxv[i].a_un.a_val); | ||
| 264 | return; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | vdso_info.valid = false; | ||
| 269 | } | ||
diff --git a/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c new file mode 100644 index 000000000000..93b0ebf8cc38 --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | /* | ||
| 2 | * vdso_test.c: Sample code to test parse_vdso.c on x86 | ||
| 3 | * Copyright (c) 2011-2014 Andy Lutomirski | ||
| 4 | * Subject to the GNU General Public License, version 2 | ||
| 5 | * | ||
| 6 | * You can amuse yourself by compiling with: | ||
| 7 | * gcc -std=gnu99 -nostdlib | ||
| 8 | * -Os -fno-asynchronous-unwind-tables -flto -lgcc_s | ||
| 9 | * vdso_standalone_test_x86.c parse_vdso.c | ||
| 10 | * to generate a small binary. On x86_64, you can omit -lgcc_s | ||
| 11 | * if you want the binary to be completely standalone. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <sys/syscall.h> | ||
| 15 | #include <sys/time.h> | ||
| 16 | #include <unistd.h> | ||
| 17 | #include <stdint.h> | ||
| 18 | |||
| 19 | extern void *vdso_sym(const char *version, const char *name); | ||
| 20 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | ||
| 21 | extern void vdso_init_from_auxv(void *auxv); | ||
| 22 | |||
| 23 | /* We need a libc functions... */ | ||
| 24 | int strcmp(const char *a, const char *b) | ||
| 25 | { | ||
| 26 | /* This implementation is buggy: it never returns -1. */ | ||
| 27 | while (*a || *b) { | ||
| 28 | if (*a != *b) | ||
| 29 | return 1; | ||
| 30 | if (*a == 0 || *b == 0) | ||
| 31 | return 1; | ||
| 32 | a++; | ||
| 33 | b++; | ||
| 34 | } | ||
| 35 | |||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | /* ...and two syscalls. This is x86-specific. */ | ||
| 40 | static inline long x86_syscall3(long nr, long a0, long a1, long a2) | ||
| 41 | { | ||
| 42 | long ret; | ||
| 43 | #ifdef __x86_64__ | ||
| 44 | asm volatile ("syscall" : "=a" (ret) : "a" (nr), | ||
| 45 | "D" (a0), "S" (a1), "d" (a2) : | ||
| 46 | "cc", "memory", "rcx", | ||
| 47 | "r8", "r9", "r10", "r11" ); | ||
| 48 | #else | ||
| 49 | asm volatile ("int $0x80" : "=a" (ret) : "a" (nr), | ||
| 50 | "b" (a0), "c" (a1), "d" (a2) : | ||
| 51 | "cc", "memory" ); | ||
| 52 | #endif | ||
| 53 | return ret; | ||
| 54 | } | ||
| 55 | |||
| 56 | static inline long linux_write(int fd, const void *data, size_t len) | ||
| 57 | { | ||
| 58 | return x86_syscall3(__NR_write, fd, (long)data, (long)len); | ||
| 59 | } | ||
| 60 | |||
| 61 | static inline void linux_exit(int code) | ||
| 62 | { | ||
| 63 | x86_syscall3(__NR_exit, code, 0, 0); | ||
| 64 | } | ||
| 65 | |||
| 66 | void to_base10(char *lastdig, time_t n) | ||
| 67 | { | ||
| 68 | while (n) { | ||
| 69 | *lastdig = (n % 10) + '0'; | ||
| 70 | n /= 10; | ||
| 71 | lastdig--; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | __attribute__((externally_visible)) void c_main(void **stack) | ||
| 76 | { | ||
| 77 | /* Parse the stack */ | ||
| 78 | long argc = (long)*stack; | ||
| 79 | stack += argc + 2; | ||
| 80 | |||
| 81 | /* Now we're pointing at the environment. Skip it. */ | ||
| 82 | while(*stack) | ||
| 83 | stack++; | ||
| 84 | stack++; | ||
| 85 | |||
| 86 | /* Now we're pointing at auxv. Initialize the vDSO parser. */ | ||
| 87 | vdso_init_from_auxv((void *)stack); | ||
| 88 | |||
| 89 | /* Find gettimeofday. */ | ||
| 90 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); | ||
| 91 | gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | ||
| 92 | |||
| 93 | if (!gtod) | ||
| 94 | linux_exit(1); | ||
| 95 | |||
| 96 | struct timeval tv; | ||
| 97 | long ret = gtod(&tv, 0); | ||
| 98 | |||
| 99 | if (ret == 0) { | ||
| 100 | char buf[] = "The time is .000000\n"; | ||
| 101 | to_base10(buf + 31, tv.tv_sec); | ||
| 102 | to_base10(buf + 38, tv.tv_usec); | ||
| 103 | linux_write(1, buf, sizeof(buf) - 1); | ||
| 104 | } else { | ||
| 105 | linux_exit(ret); | ||
| 106 | } | ||
| 107 | |||
| 108 | linux_exit(0); | ||
| 109 | } | ||
| 110 | |||
| 111 | /* | ||
| 112 | * This is the real entry point. It passes the initial stack into | ||
| 113 | * the C entry point. | ||
| 114 | */ | ||
| 115 | asm ( | ||
| 116 | ".text\n" | ||
| 117 | ".global _start\n" | ||
| 118 | ".type _start,@function\n" | ||
| 119 | "_start:\n\t" | ||
| 120 | #ifdef __x86_64__ | ||
| 121 | "mov %rsp,%rdi\n\t" | ||
| 122 | "jmp c_main" | ||
| 123 | #else | ||
| 124 | "push %esp\n\t" | ||
| 125 | "call c_main\n\t" | ||
| 126 | "int $3" | ||
| 127 | #endif | ||
| 128 | ); | ||
diff --git a/tools/testing/selftests/vDSO/vdso_test.c b/tools/testing/selftests/vDSO/vdso_test.c new file mode 100644 index 000000000000..8daeb7d7032c --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_test.c | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* | ||
| 2 | * vdso_test.c: Sample code to test parse_vdso.c | ||
| 3 | * Copyright (c) 2014 Andy Lutomirski | ||
| 4 | * Subject to the GNU General Public License, version 2 | ||
| 5 | * | ||
| 6 | * Compile with: | ||
| 7 | * gcc -std=gnu99 vdso_test.c parse_vdso.c | ||
| 8 | * | ||
| 9 | * Tested on x86, 32-bit and 64-bit. It may work on other architectures, too. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <stdint.h> | ||
| 13 | #include <elf.h> | ||
| 14 | #include <stdio.h> | ||
| 15 | #include <sys/auxv.h> | ||
| 16 | #include <sys/time.h> | ||
| 17 | |||
| 18 | extern void *vdso_sym(const char *version, const char *name); | ||
| 19 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | ||
| 20 | extern void vdso_init_from_auxv(void *auxv); | ||
| 21 | |||
| 22 | int main(int argc, char **argv) | ||
| 23 | { | ||
| 24 | unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); | ||
| 25 | if (!sysinfo_ehdr) { | ||
| 26 | printf("AT_SYSINFO_EHDR is not present!\n"); | ||
| 27 | return 0; | ||
| 28 | } | ||
| 29 | |||
| 30 | vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); | ||
| 31 | |||
| 32 | /* Find gettimeofday. */ | ||
| 33 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); | ||
| 34 | gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | ||
| 35 | |||
| 36 | if (!gtod) { | ||
| 37 | printf("Could not find __vdso_gettimeofday\n"); | ||
| 38 | return 1; | ||
| 39 | } | ||
| 40 | |||
| 41 | struct timeval tv; | ||
| 42 | long ret = gtod(&tv, 0); | ||
| 43 | |||
| 44 | if (ret == 0) { | ||
| 45 | printf("The time is %lld.%06lld\n", | ||
| 46 | (long long)tv.tv_sec, (long long)tv.tv_usec); | ||
| 47 | } else { | ||
| 48 | printf("__vdso_gettimeofday failed\n"); | ||
| 49 | } | ||
| 50 | |||
| 51 | return 0; | ||
| 52 | } | ||
diff --git a/tools/testing/selftests/watchdog/.gitignore b/tools/testing/selftests/watchdog/.gitignore new file mode 100644 index 000000000000..5aac51575c7e --- /dev/null +++ b/tools/testing/selftests/watchdog/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| watchdog-test | |||
diff --git a/tools/testing/selftests/watchdog/Makefile b/tools/testing/selftests/watchdog/Makefile new file mode 100644 index 000000000000..f863c664e3d1 --- /dev/null +++ b/tools/testing/selftests/watchdog/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | TEST_PROGS := watchdog-test | ||
| 2 | |||
| 3 | all: $(TEST_PROGS) | ||
| 4 | |||
| 5 | include ../lib.mk | ||
| 6 | |||
| 7 | clean: | ||
| 8 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/watchdog/watchdog-test.c b/tools/testing/selftests/watchdog/watchdog-test.c new file mode 100644 index 000000000000..6983d05097e2 --- /dev/null +++ b/tools/testing/selftests/watchdog/watchdog-test.c | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | /* | ||
| 2 | * Watchdog Driver Test Program | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include <errno.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <stdlib.h> | ||
| 8 | #include <string.h> | ||
| 9 | #include <unistd.h> | ||
| 10 | #include <fcntl.h> | ||
| 11 | #include <signal.h> | ||
| 12 | #include <sys/ioctl.h> | ||
| 13 | #include <linux/types.h> | ||
| 14 | #include <linux/watchdog.h> | ||
| 15 | |||
| 16 | int fd; | ||
| 17 | const char v = 'V'; | ||
| 18 | |||
| 19 | /* | ||
| 20 | * This function simply sends an IOCTL to the driver, which in turn ticks | ||
| 21 | * the PC Watchdog card to reset its internal timer so it doesn't trigger | ||
| 22 | * a computer reset. | ||
| 23 | */ | ||
| 24 | static void keep_alive(void) | ||
| 25 | { | ||
| 26 | int dummy; | ||
| 27 | |||
| 28 | printf("."); | ||
| 29 | ioctl(fd, WDIOC_KEEPALIVE, &dummy); | ||
| 30 | } | ||
| 31 | |||
| 32 | /* | ||
| 33 | * The main program. Run the program with "-d" to disable the card, | ||
| 34 | * or "-e" to enable the card. | ||
| 35 | */ | ||
| 36 | |||
| 37 | static void term(int sig) | ||
| 38 | { | ||
| 39 | int ret = write(fd, &v, 1); | ||
| 40 | |||
| 41 | close(fd); | ||
| 42 | if (ret < 0) | ||
| 43 | printf("\nStopping watchdog ticks failed (%d)...\n", errno); | ||
| 44 | else | ||
| 45 | printf("\nStopping watchdog ticks...\n"); | ||
| 46 | exit(0); | ||
| 47 | } | ||
| 48 | |||
| 49 | int main(int argc, char *argv[]) | ||
| 50 | { | ||
| 51 | int flags; | ||
| 52 | unsigned int ping_rate = 1; | ||
| 53 | int ret; | ||
| 54 | |||
| 55 | setbuf(stdout, NULL); | ||
| 56 | |||
| 57 | fd = open("/dev/watchdog", O_WRONLY); | ||
| 58 | |||
| 59 | if (fd == -1) { | ||
| 60 | printf("Watchdog device not enabled.\n"); | ||
| 61 | exit(-1); | ||
| 62 | } | ||
| 63 | |||
| 64 | if (argc > 1) { | ||
| 65 | if (!strncasecmp(argv[1], "-d", 2)) { | ||
| 66 | flags = WDIOS_DISABLECARD; | ||
| 67 | ioctl(fd, WDIOC_SETOPTIONS, &flags); | ||
| 68 | printf("Watchdog card disabled.\n"); | ||
| 69 | goto end; | ||
| 70 | } else if (!strncasecmp(argv[1], "-e", 2)) { | ||
| 71 | flags = WDIOS_ENABLECARD; | ||
| 72 | ioctl(fd, WDIOC_SETOPTIONS, &flags); | ||
| 73 | printf("Watchdog card enabled.\n"); | ||
| 74 | goto end; | ||
| 75 | } else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) { | ||
| 76 | flags = atoi(argv[2]); | ||
| 77 | ioctl(fd, WDIOC_SETTIMEOUT, &flags); | ||
| 78 | printf("Watchdog timeout set to %u seconds.\n", flags); | ||
| 79 | goto end; | ||
| 80 | } else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) { | ||
| 81 | ping_rate = strtoul(argv[2], NULL, 0); | ||
| 82 | printf("Watchdog ping rate set to %u seconds.\n", ping_rate); | ||
| 83 | } else { | ||
| 84 | printf("-d to disable, -e to enable, -t <n> to set " \ | ||
| 85 | "the timeout,\n-p <n> to set the ping rate, and \n"); | ||
| 86 | printf("run by itself to tick the card.\n"); | ||
| 87 | goto end; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | printf("Watchdog Ticking Away!\n"); | ||
| 92 | |||
| 93 | signal(SIGINT, term); | ||
| 94 | |||
| 95 | while(1) { | ||
| 96 | keep_alive(); | ||
| 97 | sleep(ping_rate); | ||
| 98 | } | ||
| 99 | end: | ||
| 100 | ret = write(fd, &v, 1); | ||
| 101 | if (ret < 0) | ||
| 102 | printf("Stopping watchdog ticks failed (%d)...\n", errno); | ||
| 103 | close(fd); | ||
| 104 | return 0; | ||
| 105 | } | ||
diff --git a/tools/testing/selftests/zram/README b/tools/testing/selftests/zram/README index eb17917c8a3a..7972cc512408 100644 --- a/tools/testing/selftests/zram/README +++ b/tools/testing/selftests/zram/README | |||
| @@ -13,7 +13,7 @@ Statistics for individual zram devices are exported through sysfs nodes at | |||
| 13 | 13 | ||
| 14 | Kconfig required: | 14 | Kconfig required: |
| 15 | CONFIG_ZRAM=y | 15 | CONFIG_ZRAM=y |
| 16 | CONFIG_ZRAM_LZ4_COMPRESS=y | 16 | CONFIG_CRYPTO_LZ4=y |
| 17 | CONFIG_ZPOOL=y | 17 | CONFIG_ZPOOL=y |
| 18 | CONFIG_ZSMALLOC=y | 18 | CONFIG_ZSMALLOC=y |
| 19 | 19 | ||
