Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Mostly simple overlapping changes. For example, David Ahern's adjacency list revamp in 'net-next' conflicted with an adjacency list traversal bug fix in 'net'. Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
1
tools/testing/selftests/filesystems/.gitignore
vendored
Normal file
1
tools/testing/selftests/filesystems/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
dnotify_test
|
7
tools/testing/selftests/filesystems/Makefile
Normal file
7
tools/testing/selftests/filesystems/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
TEST_PROGS := dnotify_test
|
||||
all: $(TEST_PROGS)
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
clean:
|
||||
rm -fr $(TEST_PROGS)
|
34
tools/testing/selftests/filesystems/dnotify_test.c
Normal file
34
tools/testing/selftests/filesystems/dnotify_test.c
Normal file
@@ -0,0 +1,34 @@
|
||||
#define _GNU_SOURCE /* needed to get the defines */
|
||||
#include <fcntl.h> /* in glibc 2.2 this has the needed
|
||||
values defined */
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static volatile int event_fd;
|
||||
|
||||
static void handler(int sig, siginfo_t *si, void *data)
|
||||
{
|
||||
event_fd = si->si_fd;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
int fd;
|
||||
|
||||
act.sa_sigaction = handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGRTMIN + 1, &act, NULL);
|
||||
|
||||
fd = open(".", O_RDONLY);
|
||||
fcntl(fd, F_SETSIG, SIGRTMIN + 1);
|
||||
fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT);
|
||||
/* we will now be notified if any of the files
|
||||
in "." is modified or new files are created */
|
||||
while (1) {
|
||||
pause();
|
||||
printf("Got event on fd=%d\n", event_fd);
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@
|
||||
|
||||
# Test for a color capable console
|
||||
if [ -z "$USE_COLOR" ]; then
|
||||
tput setf 7
|
||||
tput setf 7 || tput setaf 7
|
||||
if [ $? -eq 0 ]; then
|
||||
USE_COLOR=1
|
||||
tput sgr0
|
||||
|
@@ -23,7 +23,7 @@
|
||||
|
||||
# Test for a color capable shell and pass the result to the subdir scripts
|
||||
USE_COLOR=0
|
||||
tput setf 7
|
||||
tput setf 7 || tput setaf 7
|
||||
if [ $? -eq 0 ]; then
|
||||
USE_COLOR=1
|
||||
tput sgr0
|
||||
|
1
tools/testing/selftests/ia64/.gitignore
vendored
Normal file
1
tools/testing/selftests/ia64/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
aliasing-test
|
8
tools/testing/selftests/ia64/Makefile
Normal file
8
tools/testing/selftests/ia64/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
TEST_PROGS := aliasing-test
|
||||
|
||||
all: $(TEST_PROGS)
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
clean:
|
||||
rm -fr $(TEST_PROGS)
|
263
tools/testing/selftests/ia64/aliasing-test.c
Normal file
263
tools/testing/selftests/ia64/aliasing-test.c
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Exercise /dev/mem mmap cases that have been troublesome in the past
|
||||
*
|
||||
* (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
|
||||
* Bjorn Helgaas <bjorn.helgaas@hp.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
int sum;
|
||||
|
||||
static int map_mem(char *path, off_t offset, size_t length, int touch)
|
||||
{
|
||||
int fd, rc;
|
||||
void *addr;
|
||||
int *c;
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd == -1) {
|
||||
perror(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fnmatch("/proc/bus/pci/*", path, 0) == 0) {
|
||||
rc = ioctl(fd, PCIIOC_MMAP_IS_MEM);
|
||||
if (rc == -1)
|
||||
perror("PCIIOC_MMAP_IS_MEM ioctl");
|
||||
}
|
||||
|
||||
addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
|
||||
if (addr == MAP_FAILED)
|
||||
return 1;
|
||||
|
||||
if (touch) {
|
||||
c = (int *) addr;
|
||||
while (c < (int *) (addr + length))
|
||||
sum += *c++;
|
||||
}
|
||||
|
||||
rc = munmap(addr, length);
|
||||
if (rc == -1) {
|
||||
perror("munmap");
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_tree(char *path, char *file, off_t offset, size_t length, int touch)
|
||||
{
|
||||
struct dirent **namelist;
|
||||
char *name, *path2;
|
||||
int i, n, r, rc = 0, result = 0;
|
||||
struct stat buf;
|
||||
|
||||
n = scandir(path, &namelist, 0, alphasort);
|
||||
if (n < 0) {
|
||||
perror("scandir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
name = namelist[i]->d_name;
|
||||
|
||||
if (fnmatch(".", name, 0) == 0)
|
||||
goto skip;
|
||||
if (fnmatch("..", name, 0) == 0)
|
||||
goto skip;
|
||||
|
||||
path2 = malloc(strlen(path) + strlen(name) + 3);
|
||||
strcpy(path2, path);
|
||||
strcat(path2, "/");
|
||||
strcat(path2, name);
|
||||
|
||||
if (fnmatch(file, name, 0) == 0) {
|
||||
rc = map_mem(path2, offset, length, touch);
|
||||
if (rc == 0)
|
||||
fprintf(stderr, "PASS: %s 0x%lx-0x%lx is %s\n", path2, offset, offset + length, touch ? "readable" : "mappable");
|
||||
else if (rc > 0)
|
||||
fprintf(stderr, "PASS: %s 0x%lx-0x%lx not mappable\n", path2, offset, offset + length);
|
||||
else {
|
||||
fprintf(stderr, "FAIL: %s 0x%lx-0x%lx not accessible\n", path2, offset, offset + length);
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
r = lstat(path2, &buf);
|
||||
if (r == 0 && S_ISDIR(buf.st_mode)) {
|
||||
rc = scan_tree(path2, file, offset, length, touch);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
result |= rc;
|
||||
free(path2);
|
||||
|
||||
skip:
|
||||
free(namelist[i]);
|
||||
}
|
||||
free(namelist);
|
||||
return result;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
|
||||
static int read_rom(char *path)
|
||||
{
|
||||
int fd, rc;
|
||||
size_t size = 0;
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd == -1) {
|
||||
perror(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = write(fd, "1", 2);
|
||||
if (rc <= 0) {
|
||||
close(fd);
|
||||
perror("write");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
rc = read(fd, buf, sizeof(buf));
|
||||
if (rc > 0)
|
||||
size += rc;
|
||||
} while (rc > 0);
|
||||
|
||||
close(fd);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int scan_rom(char *path, char *file)
|
||||
{
|
||||
struct dirent **namelist;
|
||||
char *name, *path2;
|
||||
int i, n, r, rc = 0, result = 0;
|
||||
struct stat buf;
|
||||
|
||||
n = scandir(path, &namelist, 0, alphasort);
|
||||
if (n < 0) {
|
||||
perror("scandir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
name = namelist[i]->d_name;
|
||||
|
||||
if (fnmatch(".", name, 0) == 0)
|
||||
goto skip;
|
||||
if (fnmatch("..", name, 0) == 0)
|
||||
goto skip;
|
||||
|
||||
path2 = malloc(strlen(path) + strlen(name) + 3);
|
||||
strcpy(path2, path);
|
||||
strcat(path2, "/");
|
||||
strcat(path2, name);
|
||||
|
||||
if (fnmatch(file, name, 0) == 0) {
|
||||
rc = read_rom(path2);
|
||||
|
||||
/*
|
||||
* It's OK if the ROM is unreadable. Maybe there
|
||||
* is no ROM, or some other error occurred. The
|
||||
* important thing is that no MCA happened.
|
||||
*/
|
||||
if (rc > 0)
|
||||
fprintf(stderr, "PASS: %s read %d bytes\n", path2, rc);
|
||||
else {
|
||||
fprintf(stderr, "PASS: %s not readable\n", path2);
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
r = lstat(path2, &buf);
|
||||
if (r == 0 && S_ISDIR(buf.st_mode)) {
|
||||
rc = scan_rom(path2, file);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
result |= rc;
|
||||
free(path2);
|
||||
|
||||
skip:
|
||||
free(namelist[i]);
|
||||
}
|
||||
free(namelist);
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (map_mem("/dev/mem", 0, 0xA0000, 1) == 0)
|
||||
fprintf(stderr, "PASS: /dev/mem 0x0-0xa0000 is readable\n");
|
||||
else
|
||||
fprintf(stderr, "FAIL: /dev/mem 0x0-0xa0000 not accessible\n");
|
||||
|
||||
/*
|
||||
* It's not safe to blindly read the VGA frame buffer. If you know
|
||||
* how to poke the card the right way, it should respond, but it's
|
||||
* not safe in general. Many machines, e.g., Intel chipsets, cover
|
||||
* up a non-responding card by just returning -1, but others will
|
||||
* report the failure as a machine check.
|
||||
*/
|
||||
if (map_mem("/dev/mem", 0xA0000, 0x20000, 0) == 0)
|
||||
fprintf(stderr, "PASS: /dev/mem 0xa0000-0xc0000 is mappable\n");
|
||||
else
|
||||
fprintf(stderr, "FAIL: /dev/mem 0xa0000-0xc0000 not accessible\n");
|
||||
|
||||
if (map_mem("/dev/mem", 0xC0000, 0x40000, 1) == 0)
|
||||
fprintf(stderr, "PASS: /dev/mem 0xc0000-0x100000 is readable\n");
|
||||
else
|
||||
fprintf(stderr, "FAIL: /dev/mem 0xc0000-0x100000 not accessible\n");
|
||||
|
||||
/*
|
||||
* Often you can map all the individual pieces above (0-0xA0000,
|
||||
* 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole
|
||||
* thing at once. This is because the individual pieces use different
|
||||
* attributes, and there's no single attribute supported over the
|
||||
* whole region.
|
||||
*/
|
||||
rc = map_mem("/dev/mem", 0, 1024*1024, 0);
|
||||
if (rc == 0)
|
||||
fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 is mappable\n");
|
||||
else if (rc > 0)
|
||||
fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 not mappable\n");
|
||||
else
|
||||
fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n");
|
||||
|
||||
scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1);
|
||||
scan_tree("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0);
|
||||
scan_tree("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1);
|
||||
scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0);
|
||||
|
||||
scan_rom("/sys/devices", "rom");
|
||||
|
||||
scan_tree("/proc/bus/pci", "??.?", 0, 0xA0000, 1);
|
||||
scan_tree("/proc/bus/pci", "??.?", 0xA0000, 0x20000, 0);
|
||||
scan_tree("/proc/bus/pci", "??.?", 0xC0000, 0x40000, 1);
|
||||
scan_tree("/proc/bus/pci", "??.?", 0, 1024*1024, 0);
|
||||
|
||||
return rc;
|
||||
}
|
3
tools/testing/selftests/networking/timestamping/.gitignore
vendored
Normal file
3
tools/testing/selftests/networking/timestamping/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
timestamping
|
||||
txtimestamp
|
||||
hwtstamp_config
|
8
tools/testing/selftests/networking/timestamping/Makefile
Normal file
8
tools/testing/selftests/networking/timestamping/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
TEST_PROGS := hwtstamp_config timestamping txtimestamp
|
||||
|
||||
all: $(TEST_PROGS)
|
||||
|
||||
include ../../lib.mk
|
||||
|
||||
clean:
|
||||
rm -fr $(TEST_PROGS)
|
@@ -0,0 +1,134 @@
|
||||
/* Test program for SIOC{G,S}HWTSTAMP
|
||||
* Copyright 2013 Solarflare Communications
|
||||
* Author: Ben Hutchings
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <linux/if.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/sockios.h>
|
||||
|
||||
static int
|
||||
lookup_value(const char **names, int size, const char *name)
|
||||
{
|
||||
int value;
|
||||
|
||||
for (value = 0; value < size; value++)
|
||||
if (names[value] && strcasecmp(names[value], name) == 0)
|
||||
return value;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *
|
||||
lookup_name(const char **names, int size, int value)
|
||||
{
|
||||
return (value >= 0 && value < size) ? names[value] : NULL;
|
||||
}
|
||||
|
||||
static void list_names(FILE *f, const char **names, int size)
|
||||
{
|
||||
int value;
|
||||
|
||||
for (value = 0; value < size; value++)
|
||||
if (names[value])
|
||||
fprintf(f, " %s\n", names[value]);
|
||||
}
|
||||
|
||||
static const char *tx_types[] = {
|
||||
#define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name
|
||||
TX_TYPE(OFF),
|
||||
TX_TYPE(ON),
|
||||
TX_TYPE(ONESTEP_SYNC)
|
||||
#undef TX_TYPE
|
||||
};
|
||||
#define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0])))
|
||||
|
||||
static const char *rx_filters[] = {
|
||||
#define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name
|
||||
RX_FILTER(NONE),
|
||||
RX_FILTER(ALL),
|
||||
RX_FILTER(SOME),
|
||||
RX_FILTER(PTP_V1_L4_EVENT),
|
||||
RX_FILTER(PTP_V1_L4_SYNC),
|
||||
RX_FILTER(PTP_V1_L4_DELAY_REQ),
|
||||
RX_FILTER(PTP_V2_L4_EVENT),
|
||||
RX_FILTER(PTP_V2_L4_SYNC),
|
||||
RX_FILTER(PTP_V2_L4_DELAY_REQ),
|
||||
RX_FILTER(PTP_V2_L2_EVENT),
|
||||
RX_FILTER(PTP_V2_L2_SYNC),
|
||||
RX_FILTER(PTP_V2_L2_DELAY_REQ),
|
||||
RX_FILTER(PTP_V2_EVENT),
|
||||
RX_FILTER(PTP_V2_SYNC),
|
||||
RX_FILTER(PTP_V2_DELAY_REQ),
|
||||
#undef RX_FILTER
|
||||
};
|
||||
#define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0])))
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n"
|
||||
"tx_type is any of (case-insensitive):\n",
|
||||
stderr);
|
||||
list_names(stderr, tx_types, N_TX_TYPES);
|
||||
fputs("rx_filter is any of (case-insensitive):\n", stderr);
|
||||
list_names(stderr, rx_filters, N_RX_FILTERS);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
struct hwtstamp_config config;
|
||||
const char *name;
|
||||
int sock;
|
||||
|
||||
if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) {
|
||||
usage();
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (argc == 4) {
|
||||
config.flags = 0;
|
||||
config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]);
|
||||
config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]);
|
||||
if (config.tx_type < 0 || config.rx_filter < 0) {
|
||||
usage();
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock < 0) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
strcpy(ifr.ifr_name, argv[1]);
|
||||
ifr.ifr_data = (caddr_t)&config;
|
||||
|
||||
if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) {
|
||||
perror("ioctl");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("flags = %#x\n", config.flags);
|
||||
name = lookup_name(tx_types, N_TX_TYPES, config.tx_type);
|
||||
if (name)
|
||||
printf("tx_type = %s\n", name);
|
||||
else
|
||||
printf("tx_type = %d\n", config.tx_type);
|
||||
name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter);
|
||||
if (name)
|
||||
printf("rx_filter = %s\n", name);
|
||||
else
|
||||
printf("rx_filter = %d\n", config.rx_filter);
|
||||
|
||||
return 0;
|
||||
}
|
528
tools/testing/selftests/networking/timestamping/timestamping.c
Normal file
528
tools/testing/selftests/networking/timestamping/timestamping.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* This program demonstrates how the various time stamping features in
|
||||
* the Linux kernel work. It emulates the behavior of a PTP
|
||||
* implementation in stand-alone master mode by sending PTPv1 Sync
|
||||
* multicasts once every second. It looks for similar packets, but
|
||||
* beyond that doesn't actually implement PTP.
|
||||
*
|
||||
* Outgoing packets are time stamped with SO_TIMESTAMPING with or
|
||||
* without hardware support.
|
||||
*
|
||||
* Incoming packets are time stamped with SO_TIMESTAMPING with or
|
||||
* without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
|
||||
* SO_TIMESTAMP[NS].
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
* Author: Patrick Ohly <patrick.ohly@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/errqueue.h>
|
||||
|
||||
#ifndef SO_TIMESTAMPING
|
||||
# define SO_TIMESTAMPING 37
|
||||
# define SCM_TIMESTAMPING SO_TIMESTAMPING
|
||||
#endif
|
||||
|
||||
#ifndef SO_TIMESTAMPNS
|
||||
# define SO_TIMESTAMPNS 35
|
||||
#endif
|
||||
|
||||
#ifndef SIOCGSTAMPNS
|
||||
# define SIOCGSTAMPNS 0x8907
|
||||
#endif
|
||||
|
||||
#ifndef SIOCSHWTSTAMP
|
||||
# define SIOCSHWTSTAMP 0x89b0
|
||||
#endif
|
||||
|
||||
static void usage(const char *error)
|
||||
{
|
||||
if (error)
|
||||
printf("invalid option: %s\n", error);
|
||||
printf("timestamping interface option*\n\n"
|
||||
"Options:\n"
|
||||
" IP_MULTICAST_LOOP - looping outgoing multicasts\n"
|
||||
" SO_TIMESTAMP - normal software time stamping, ms resolution\n"
|
||||
" SO_TIMESTAMPNS - more accurate software time stamping\n"
|
||||
" SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
|
||||
" SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
|
||||
" SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
|
||||
" SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
|
||||
" SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
|
||||
" SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
|
||||
" SIOCGSTAMP - check last socket time stamp\n"
|
||||
" SIOCGSTAMPNS - more accurate socket time stamp\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void bail(const char *error)
|
||||
{
|
||||
printf("%s: %s\n", error, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static const unsigned char sync[] = {
|
||||
0x00, 0x01, 0x00, 0x01,
|
||||
0x5f, 0x44, 0x46, 0x4c,
|
||||
0x54, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x01,
|
||||
|
||||
/* fake uuid */
|
||||
0x00, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05,
|
||||
|
||||
0x00, 0x01, 0x00, 0x37,
|
||||
0x00, 0x00, 0x00, 0x08,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x49, 0x05, 0xcd, 0x01,
|
||||
0x29, 0xb1, 0x8d, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
|
||||
/* fake uuid */
|
||||
0x00, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05,
|
||||
|
||||
0x00, 0x00, 0x00, 0x37,
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x44, 0x46, 0x4c, 0x54,
|
||||
0x00, 0x00, 0xf0, 0x60,
|
||||
0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0xf0, 0x60,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x44, 0x46, 0x4c, 0x54,
|
||||
0x00, 0x01,
|
||||
|
||||
/* fake uuid */
|
||||
0x00, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
|
||||
{
|
||||
struct timeval now;
|
||||
int res;
|
||||
|
||||
res = sendto(sock, sync, sizeof(sync), 0,
|
||||
addr, addr_len);
|
||||
gettimeofday(&now, 0);
|
||||
if (res < 0)
|
||||
printf("%s: %s\n", "send", strerror(errno));
|
||||
else
|
||||
printf("%ld.%06ld: sent %d bytes\n",
|
||||
(long)now.tv_sec, (long)now.tv_usec,
|
||||
res);
|
||||
}
|
||||
|
||||
static void printpacket(struct msghdr *msg, int res,
|
||||
char *data,
|
||||
int sock, int recvmsg_flags,
|
||||
int siocgstamp, int siocgstampns)
|
||||
{
|
||||
struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
|
||||
struct cmsghdr *cmsg;
|
||||
struct timeval tv;
|
||||
struct timespec ts;
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, 0);
|
||||
|
||||
printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
|
||||
(long)now.tv_sec, (long)now.tv_usec,
|
||||
(recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
|
||||
res,
|
||||
inet_ntoa(from_addr->sin_addr),
|
||||
msg->msg_controllen);
|
||||
for (cmsg = CMSG_FIRSTHDR(msg);
|
||||
cmsg;
|
||||
cmsg = CMSG_NXTHDR(msg, cmsg)) {
|
||||
printf(" cmsg len %zu: ", cmsg->cmsg_len);
|
||||
switch (cmsg->cmsg_level) {
|
||||
case SOL_SOCKET:
|
||||
printf("SOL_SOCKET ");
|
||||
switch (cmsg->cmsg_type) {
|
||||
case SO_TIMESTAMP: {
|
||||
struct timeval *stamp =
|
||||
(struct timeval *)CMSG_DATA(cmsg);
|
||||
printf("SO_TIMESTAMP %ld.%06ld",
|
||||
(long)stamp->tv_sec,
|
||||
(long)stamp->tv_usec);
|
||||
break;
|
||||
}
|
||||
case SO_TIMESTAMPNS: {
|
||||
struct timespec *stamp =
|
||||
(struct timespec *)CMSG_DATA(cmsg);
|
||||
printf("SO_TIMESTAMPNS %ld.%09ld",
|
||||
(long)stamp->tv_sec,
|
||||
(long)stamp->tv_nsec);
|
||||
break;
|
||||
}
|
||||
case SO_TIMESTAMPING: {
|
||||
struct timespec *stamp =
|
||||
(struct timespec *)CMSG_DATA(cmsg);
|
||||
printf("SO_TIMESTAMPING ");
|
||||
printf("SW %ld.%09ld ",
|
||||
(long)stamp->tv_sec,
|
||||
(long)stamp->tv_nsec);
|
||||
stamp++;
|
||||
/* skip deprecated HW transformed */
|
||||
stamp++;
|
||||
printf("HW raw %ld.%09ld",
|
||||
(long)stamp->tv_sec,
|
||||
(long)stamp->tv_nsec);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("type %d", cmsg->cmsg_type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IPPROTO_IP:
|
||||
printf("IPPROTO_IP ");
|
||||
switch (cmsg->cmsg_type) {
|
||||
case IP_RECVERR: {
|
||||
struct sock_extended_err *err =
|
||||
(struct sock_extended_err *)CMSG_DATA(cmsg);
|
||||
printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
|
||||
strerror(err->ee_errno),
|
||||
err->ee_origin,
|
||||
#ifdef SO_EE_ORIGIN_TIMESTAMPING
|
||||
err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
|
||||
"bounced packet" : "unexpected origin"
|
||||
#else
|
||||
"probably SO_EE_ORIGIN_TIMESTAMPING"
|
||||
#endif
|
||||
);
|
||||
if (res < sizeof(sync))
|
||||
printf(" => truncated data?!");
|
||||
else if (!memcmp(sync, data + res - sizeof(sync),
|
||||
sizeof(sync)))
|
||||
printf(" => GOT OUR DATA BACK (HURRAY!)");
|
||||
break;
|
||||
}
|
||||
case IP_PKTINFO: {
|
||||
struct in_pktinfo *pktinfo =
|
||||
(struct in_pktinfo *)CMSG_DATA(cmsg);
|
||||
printf("IP_PKTINFO interface index %u",
|
||||
pktinfo->ipi_ifindex);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("type %d", cmsg->cmsg_type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("level %d type %d",
|
||||
cmsg->cmsg_level,
|
||||
cmsg->cmsg_type);
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (siocgstamp) {
|
||||
if (ioctl(sock, SIOCGSTAMP, &tv))
|
||||
printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno));
|
||||
else
|
||||
printf("SIOCGSTAMP %ld.%06ld\n",
|
||||
(long)tv.tv_sec,
|
||||
(long)tv.tv_usec);
|
||||
}
|
||||
if (siocgstampns) {
|
||||
if (ioctl(sock, SIOCGSTAMPNS, &ts))
|
||||
printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
|
||||
else
|
||||
printf("SIOCGSTAMPNS %ld.%09ld\n",
|
||||
(long)ts.tv_sec,
|
||||
(long)ts.tv_nsec);
|
||||
}
|
||||
}
|
||||
|
||||
static void recvpacket(int sock, int recvmsg_flags,
|
||||
int siocgstamp, int siocgstampns)
|
||||
{
|
||||
char data[256];
|
||||
struct msghdr msg;
|
||||
struct iovec entry;
|
||||
struct sockaddr_in from_addr;
|
||||
struct {
|
||||
struct cmsghdr cm;
|
||||
char control[512];
|
||||
} control;
|
||||
int res;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_iov = &entry;
|
||||
msg.msg_iovlen = 1;
|
||||
entry.iov_base = data;
|
||||
entry.iov_len = sizeof(data);
|
||||
msg.msg_name = (caddr_t)&from_addr;
|
||||
msg.msg_namelen = sizeof(from_addr);
|
||||
msg.msg_control = &control;
|
||||
msg.msg_controllen = sizeof(control);
|
||||
|
||||
res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
|
||||
if (res < 0) {
|
||||
printf("%s %s: %s\n",
|
||||
"recvmsg",
|
||||
(recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
|
||||
strerror(errno));
|
||||
} else {
|
||||
printpacket(&msg, res, data,
|
||||
sock, recvmsg_flags,
|
||||
siocgstamp, siocgstampns);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int so_timestamping_flags = 0;
|
||||
int so_timestamp = 0;
|
||||
int so_timestampns = 0;
|
||||
int siocgstamp = 0;
|
||||
int siocgstampns = 0;
|
||||
int ip_multicast_loop = 0;
|
||||
char *interface;
|
||||
int i;
|
||||
int enabled = 1;
|
||||
int sock;
|
||||
struct ifreq device;
|
||||
struct ifreq hwtstamp;
|
||||
struct hwtstamp_config hwconfig, hwconfig_requested;
|
||||
struct sockaddr_in addr;
|
||||
struct ip_mreq imr;
|
||||
struct in_addr iaddr;
|
||||
int val;
|
||||
socklen_t len;
|
||||
struct timeval next;
|
||||
|
||||
if (argc < 2)
|
||||
usage(0);
|
||||
interface = argv[1];
|
||||
|
||||
for (i = 2; i < argc; i++) {
|
||||
if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
|
||||
so_timestamp = 1;
|
||||
else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
|
||||
so_timestampns = 1;
|
||||
else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
|
||||
siocgstamp = 1;
|
||||
else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
|
||||
siocgstampns = 1;
|
||||
else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
|
||||
ip_multicast_loop = 1;
|
||||
else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
|
||||
so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
|
||||
else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
|
||||
so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
|
||||
else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
|
||||
so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
|
||||
else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
|
||||
so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
|
||||
else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
|
||||
so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
|
||||
else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
|
||||
so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
|
||||
else
|
||||
usage(argv[i]);
|
||||
}
|
||||
|
||||
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock < 0)
|
||||
bail("socket");
|
||||
|
||||
memset(&device, 0, sizeof(device));
|
||||
strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
|
||||
if (ioctl(sock, SIOCGIFADDR, &device) < 0)
|
||||
bail("getting interface IP address");
|
||||
|
||||
memset(&hwtstamp, 0, sizeof(hwtstamp));
|
||||
strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
|
||||
hwtstamp.ifr_data = (void *)&hwconfig;
|
||||
memset(&hwconfig, 0, sizeof(hwconfig));
|
||||
hwconfig.tx_type =
|
||||
(so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
|
||||
HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
|
||||
hwconfig.rx_filter =
|
||||
(so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
|
||||
HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
|
||||
hwconfig_requested = hwconfig;
|
||||
if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
|
||||
if ((errno == EINVAL || errno == ENOTSUP) &&
|
||||
hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
|
||||
hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
|
||||
printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
|
||||
else
|
||||
bail("SIOCSHWTSTAMP");
|
||||
}
|
||||
printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
|
||||
hwconfig_requested.tx_type, hwconfig.tx_type,
|
||||
hwconfig_requested.rx_filter, hwconfig.rx_filter);
|
||||
|
||||
/* bind to PTP port */
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(319 /* PTP event port */);
|
||||
if (bind(sock,
|
||||
(struct sockaddr *)&addr,
|
||||
sizeof(struct sockaddr_in)) < 0)
|
||||
bail("bind");
|
||||
|
||||
/* set multicast group for outgoing packets */
|
||||
inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
|
||||
addr.sin_addr = iaddr;
|
||||
imr.imr_multiaddr.s_addr = iaddr.s_addr;
|
||||
imr.imr_interface.s_addr =
|
||||
((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
|
||||
&imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
|
||||
bail("set multicast");
|
||||
|
||||
/* join multicast group, loop our own packet */
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||
&imr, sizeof(struct ip_mreq)) < 0)
|
||||
bail("join multicast group");
|
||||
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&ip_multicast_loop, sizeof(enabled)) < 0) {
|
||||
bail("loop multicast");
|
||||
}
|
||||
|
||||
/* set socket options for time stamping */
|
||||
if (so_timestamp &&
|
||||
setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
|
||||
&enabled, sizeof(enabled)) < 0)
|
||||
bail("setsockopt SO_TIMESTAMP");
|
||||
|
||||
if (so_timestampns &&
|
||||
setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
|
||||
&enabled, sizeof(enabled)) < 0)
|
||||
bail("setsockopt SO_TIMESTAMPNS");
|
||||
|
||||
if (so_timestamping_flags &&
|
||||
setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
|
||||
&so_timestamping_flags,
|
||||
sizeof(so_timestamping_flags)) < 0)
|
||||
bail("setsockopt SO_TIMESTAMPING");
|
||||
|
||||
/* request IP_PKTINFO for debugging purposes */
|
||||
if (setsockopt(sock, SOL_IP, IP_PKTINFO,
|
||||
&enabled, sizeof(enabled)) < 0)
|
||||
printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
|
||||
|
||||
/* verify socket options */
|
||||
len = sizeof(val);
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
|
||||
printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
|
||||
else
|
||||
printf("SO_TIMESTAMP %d\n", val);
|
||||
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
|
||||
printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
|
||||
strerror(errno));
|
||||
else
|
||||
printf("SO_TIMESTAMPNS %d\n", val);
|
||||
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
|
||||
printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
|
||||
strerror(errno));
|
||||
} else {
|
||||
printf("SO_TIMESTAMPING %d\n", val);
|
||||
if (val != so_timestamping_flags)
|
||||
printf(" not the expected value %d\n",
|
||||
so_timestamping_flags);
|
||||
}
|
||||
|
||||
/* send packets forever every five seconds */
|
||||
gettimeofday(&next, 0);
|
||||
next.tv_sec = (next.tv_sec + 1) / 5 * 5;
|
||||
next.tv_usec = 0;
|
||||
while (1) {
|
||||
struct timeval now;
|
||||
struct timeval delta;
|
||||
long delta_us;
|
||||
int res;
|
||||
fd_set readfs, errorfs;
|
||||
|
||||
gettimeofday(&now, 0);
|
||||
delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
|
||||
(long)(next.tv_usec - now.tv_usec);
|
||||
if (delta_us > 0) {
|
||||
/* continue waiting for timeout or data */
|
||||
delta.tv_sec = delta_us / 1000000;
|
||||
delta.tv_usec = delta_us % 1000000;
|
||||
|
||||
FD_ZERO(&readfs);
|
||||
FD_ZERO(&errorfs);
|
||||
FD_SET(sock, &readfs);
|
||||
FD_SET(sock, &errorfs);
|
||||
printf("%ld.%06ld: select %ldus\n",
|
||||
(long)now.tv_sec, (long)now.tv_usec,
|
||||
delta_us);
|
||||
res = select(sock + 1, &readfs, 0, &errorfs, &delta);
|
||||
gettimeofday(&now, 0);
|
||||
printf("%ld.%06ld: select returned: %d, %s\n",
|
||||
(long)now.tv_sec, (long)now.tv_usec,
|
||||
res,
|
||||
res < 0 ? strerror(errno) : "success");
|
||||
if (res > 0) {
|
||||
if (FD_ISSET(sock, &readfs))
|
||||
printf("ready for reading\n");
|
||||
if (FD_ISSET(sock, &errorfs))
|
||||
printf("has error\n");
|
||||
recvpacket(sock, 0,
|
||||
siocgstamp,
|
||||
siocgstampns);
|
||||
recvpacket(sock, MSG_ERRQUEUE,
|
||||
siocgstamp,
|
||||
siocgstampns);
|
||||
}
|
||||
} else {
|
||||
/* write one packet */
|
||||
sendpacket(sock,
|
||||
(struct sockaddr *)&addr,
|
||||
sizeof(addr));
|
||||
next.tv_sec += 5;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
549
tools/testing/selftests/networking/timestamping/txtimestamp.c
Normal file
549
tools/testing/selftests/networking/timestamping/txtimestamp.c
Normal file
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
* Author: willemb@google.com (Willem de Bruijn)
|
||||
*
|
||||
* Test software tx timestamping, including
|
||||
*
|
||||
* - SCHED, SND and ACK timestamps
|
||||
* - RAW, UDP and TCP
|
||||
* - IPv4 and IPv6
|
||||
* - various packet sizes (to test GSO and TSO)
|
||||
*
|
||||
* Consult the command line arguments for help on running
|
||||
* the various testcases.
|
||||
*
|
||||
* This test requires a dummy TCP server.
|
||||
* A simple `nc6 [-u] -l -p $DESTPORT` will do
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <asm/types.h>
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <poll.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* command line parameters */
|
||||
static int cfg_proto = SOCK_STREAM;
|
||||
static int cfg_ipproto = IPPROTO_TCP;
|
||||
static int cfg_num_pkts = 4;
|
||||
static int do_ipv4 = 1;
|
||||
static int do_ipv6 = 1;
|
||||
static int cfg_payload_len = 10;
|
||||
static bool cfg_show_payload;
|
||||
static bool cfg_do_pktinfo;
|
||||
static bool cfg_loop_nodata;
|
||||
static uint16_t dest_port = 9000;
|
||||
|
||||
static struct sockaddr_in daddr;
|
||||
static struct sockaddr_in6 daddr6;
|
||||
static struct timespec ts_prev;
|
||||
|
||||
static void __print_timestamp(const char *name, struct timespec *cur,
|
||||
uint32_t key, int payload_len)
|
||||
{
|
||||
if (!(cur->tv_sec | cur->tv_nsec))
|
||||
return;
|
||||
|
||||
fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)",
|
||||
name, cur->tv_sec, cur->tv_nsec / 1000,
|
||||
key, payload_len);
|
||||
|
||||
if ((ts_prev.tv_sec | ts_prev.tv_nsec)) {
|
||||
int64_t cur_ms, prev_ms;
|
||||
|
||||
cur_ms = (long) cur->tv_sec * 1000 * 1000;
|
||||
cur_ms += cur->tv_nsec / 1000;
|
||||
|
||||
prev_ms = (long) ts_prev.tv_sec * 1000 * 1000;
|
||||
prev_ms += ts_prev.tv_nsec / 1000;
|
||||
|
||||
fprintf(stderr, " (%+" PRId64 " us)", cur_ms - prev_ms);
|
||||
}
|
||||
|
||||
ts_prev = *cur;
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static void print_timestamp_usr(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timeval tv; /* avoid dependency on -lrt */
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
ts.tv_sec = tv.tv_sec;
|
||||
ts.tv_nsec = tv.tv_usec * 1000;
|
||||
|
||||
__print_timestamp(" USR", &ts, 0, 0);
|
||||
}
|
||||
|
||||
static void print_timestamp(struct scm_timestamping *tss, int tstype,
|
||||
int tskey, int payload_len)
|
||||
{
|
||||
const char *tsname;
|
||||
|
||||
switch (tstype) {
|
||||
case SCM_TSTAMP_SCHED:
|
||||
tsname = " ENQ";
|
||||
break;
|
||||
case SCM_TSTAMP_SND:
|
||||
tsname = " SND";
|
||||
break;
|
||||
case SCM_TSTAMP_ACK:
|
||||
tsname = " ACK";
|
||||
break;
|
||||
default:
|
||||
error(1, 0, "unknown timestamp type: %u",
|
||||
tstype);
|
||||
}
|
||||
__print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
|
||||
}
|
||||
|
||||
/* TODO: convert to check_and_print payload once API is stable */
|
||||
static void print_payload(char *data, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
if (len > 70)
|
||||
len = 70;
|
||||
|
||||
fprintf(stderr, "payload: ");
|
||||
for (i = 0; i < len; i++)
|
||||
fprintf(stderr, "%02hhx ", data[i]);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)
|
||||
{
|
||||
char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN];
|
||||
|
||||
fprintf(stderr, " pktinfo: ifindex=%u src=%s dst=%s\n",
|
||||
ifindex,
|
||||
saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown",
|
||||
daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown");
|
||||
}
|
||||
|
||||
static void __poll(int fd)
|
||||
{
|
||||
struct pollfd pollfd;
|
||||
int ret;
|
||||
|
||||
memset(&pollfd, 0, sizeof(pollfd));
|
||||
pollfd.fd = fd;
|
||||
ret = poll(&pollfd, 1, 100);
|
||||
if (ret != 1)
|
||||
error(1, errno, "poll");
|
||||
}
|
||||
|
||||
static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
|
||||
{
|
||||
struct sock_extended_err *serr = NULL;
|
||||
struct scm_timestamping *tss = NULL;
|
||||
struct cmsghdr *cm;
|
||||
int batch = 0;
|
||||
|
||||
for (cm = CMSG_FIRSTHDR(msg);
|
||||
cm && cm->cmsg_len;
|
||||
cm = CMSG_NXTHDR(msg, cm)) {
|
||||
if (cm->cmsg_level == SOL_SOCKET &&
|
||||
cm->cmsg_type == SCM_TIMESTAMPING) {
|
||||
tss = (void *) CMSG_DATA(cm);
|
||||
} else if ((cm->cmsg_level == SOL_IP &&
|
||||
cm->cmsg_type == IP_RECVERR) ||
|
||||
(cm->cmsg_level == SOL_IPV6 &&
|
||||
cm->cmsg_type == IPV6_RECVERR)) {
|
||||
serr = (void *) CMSG_DATA(cm);
|
||||
if (serr->ee_errno != ENOMSG ||
|
||||
serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
|
||||
fprintf(stderr, "unknown ip error %d %d\n",
|
||||
serr->ee_errno,
|
||||
serr->ee_origin);
|
||||
serr = NULL;
|
||||
}
|
||||
} else if (cm->cmsg_level == SOL_IP &&
|
||||
cm->cmsg_type == IP_PKTINFO) {
|
||||
struct in_pktinfo *info = (void *) CMSG_DATA(cm);
|
||||
print_pktinfo(AF_INET, info->ipi_ifindex,
|
||||
&info->ipi_spec_dst, &info->ipi_addr);
|
||||
} else if (cm->cmsg_level == SOL_IPV6 &&
|
||||
cm->cmsg_type == IPV6_PKTINFO) {
|
||||
struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm);
|
||||
print_pktinfo(AF_INET6, info6->ipi6_ifindex,
|
||||
NULL, &info6->ipi6_addr);
|
||||
} else
|
||||
fprintf(stderr, "unknown cmsg %d,%d\n",
|
||||
cm->cmsg_level, cm->cmsg_type);
|
||||
|
||||
if (serr && tss) {
|
||||
print_timestamp(tss, serr->ee_info, serr->ee_data,
|
||||
payload_len);
|
||||
serr = NULL;
|
||||
tss = NULL;
|
||||
batch++;
|
||||
}
|
||||
}
|
||||
|
||||
if (batch > 1)
|
||||
fprintf(stderr, "batched %d timestamps\n", batch);
|
||||
}
|
||||
|
||||
static int recv_errmsg(int fd)
|
||||
{
|
||||
static char ctrl[1024 /* overprovision*/];
|
||||
static struct msghdr msg;
|
||||
struct iovec entry;
|
||||
static char *data;
|
||||
int ret = 0;
|
||||
|
||||
data = malloc(cfg_payload_len);
|
||||
if (!data)
|
||||
error(1, 0, "malloc");
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
memset(ctrl, 0, sizeof(ctrl));
|
||||
|
||||
entry.iov_base = data;
|
||||
entry.iov_len = cfg_payload_len;
|
||||
msg.msg_iov = &entry;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_control = ctrl;
|
||||
msg.msg_controllen = sizeof(ctrl);
|
||||
|
||||
ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
|
||||
if (ret == -1 && errno != EAGAIN)
|
||||
error(1, errno, "recvmsg");
|
||||
|
||||
if (ret >= 0) {
|
||||
__recv_errmsg_cmsg(&msg, ret);
|
||||
if (cfg_show_payload)
|
||||
print_payload(data, cfg_payload_len);
|
||||
}
|
||||
|
||||
free(data);
|
||||
return ret == -1;
|
||||
}
|
||||
|
||||
static void do_test(int family, unsigned int opt)
|
||||
{
|
||||
char *buf;
|
||||
int fd, i, val = 1, total_len;
|
||||
|
||||
if (family == AF_INET6 && cfg_proto != SOCK_STREAM) {
|
||||
/* due to lack of checksum generation code */
|
||||
fprintf(stderr, "test: skipping datagram over IPv6\n");
|
||||
return;
|
||||
}
|
||||
|
||||
total_len = cfg_payload_len;
|
||||
if (cfg_proto == SOCK_RAW) {
|
||||
total_len += sizeof(struct udphdr);
|
||||
if (cfg_ipproto == IPPROTO_RAW)
|
||||
total_len += sizeof(struct iphdr);
|
||||
}
|
||||
|
||||
buf = malloc(total_len);
|
||||
if (!buf)
|
||||
error(1, 0, "malloc");
|
||||
|
||||
fd = socket(family, cfg_proto, cfg_ipproto);
|
||||
if (fd < 0)
|
||||
error(1, errno, "socket");
|
||||
|
||||
if (cfg_proto == SOCK_STREAM) {
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char*) &val, sizeof(val)))
|
||||
error(1, 0, "setsockopt no nagle");
|
||||
|
||||
if (family == PF_INET) {
|
||||
if (connect(fd, (void *) &daddr, sizeof(daddr)))
|
||||
error(1, errno, "connect ipv4");
|
||||
} else {
|
||||
if (connect(fd, (void *) &daddr6, sizeof(daddr6)))
|
||||
error(1, errno, "connect ipv6");
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg_do_pktinfo) {
|
||||
if (family == AF_INET6) {
|
||||
if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,
|
||||
&val, sizeof(val)))
|
||||
error(1, errno, "setsockopt pktinfo ipv6");
|
||||
} else {
|
||||
if (setsockopt(fd, SOL_IP, IP_PKTINFO,
|
||||
&val, sizeof(val)))
|
||||
error(1, errno, "setsockopt pktinfo ipv4");
|
||||
}
|
||||
}
|
||||
|
||||
opt |= SOF_TIMESTAMPING_SOFTWARE |
|
||||
SOF_TIMESTAMPING_OPT_CMSG |
|
||||
SOF_TIMESTAMPING_OPT_ID;
|
||||
if (cfg_loop_nodata)
|
||||
opt |= SOF_TIMESTAMPING_OPT_TSONLY;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
|
||||
(char *) &opt, sizeof(opt)))
|
||||
error(1, 0, "setsockopt timestamping");
|
||||
|
||||
for (i = 0; i < cfg_num_pkts; i++) {
|
||||
memset(&ts_prev, 0, sizeof(ts_prev));
|
||||
memset(buf, 'a' + i, total_len);
|
||||
|
||||
if (cfg_proto == SOCK_RAW) {
|
||||
struct udphdr *udph;
|
||||
int off = 0;
|
||||
|
||||
if (cfg_ipproto == IPPROTO_RAW) {
|
||||
struct iphdr *iph = (void *) buf;
|
||||
|
||||
memset(iph, 0, sizeof(*iph));
|
||||
iph->ihl = 5;
|
||||
iph->version = 4;
|
||||
iph->ttl = 2;
|
||||
iph->daddr = daddr.sin_addr.s_addr;
|
||||
iph->protocol = IPPROTO_UDP;
|
||||
/* kernel writes saddr, csum, len */
|
||||
|
||||
off = sizeof(*iph);
|
||||
}
|
||||
|
||||
udph = (void *) buf + off;
|
||||
udph->source = ntohs(9000); /* random spoof */
|
||||
udph->dest = ntohs(dest_port);
|
||||
udph->len = ntohs(sizeof(*udph) + cfg_payload_len);
|
||||
udph->check = 0; /* not allowed for IPv6 */
|
||||
}
|
||||
|
||||
print_timestamp_usr();
|
||||
if (cfg_proto != SOCK_STREAM) {
|
||||
if (family == PF_INET)
|
||||
val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr));
|
||||
else
|
||||
val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6));
|
||||
} else {
|
||||
val = send(fd, buf, cfg_payload_len, 0);
|
||||
}
|
||||
if (val != total_len)
|
||||
error(1, errno, "send");
|
||||
|
||||
/* wait for all errors to be queued, else ACKs arrive OOO */
|
||||
usleep(50 * 1000);
|
||||
|
||||
__poll(fd);
|
||||
|
||||
while (!recv_errmsg(fd)) {}
|
||||
}
|
||||
|
||||
if (close(fd))
|
||||
error(1, errno, "close");
|
||||
|
||||
free(buf);
|
||||
usleep(400 * 1000);
|
||||
}
|
||||
|
||||
static void __attribute__((noreturn)) usage(const char *filepath)
|
||||
{
|
||||
fprintf(stderr, "\nUsage: %s [options] hostname\n"
|
||||
"\nwhere options are:\n"
|
||||
" -4: only IPv4\n"
|
||||
" -6: only IPv6\n"
|
||||
" -h: show this message\n"
|
||||
" -I: request PKTINFO\n"
|
||||
" -l N: send N bytes at a time\n"
|
||||
" -n: set no-payload option\n"
|
||||
" -r: use raw\n"
|
||||
" -R: use raw (IP_HDRINCL)\n"
|
||||
" -p N: connect to port N\n"
|
||||
" -u: use udp\n"
|
||||
" -x: show payload (up to 70 bytes)\n",
|
||||
filepath);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void parse_opt(int argc, char **argv)
|
||||
{
|
||||
int proto_count = 0;
|
||||
char c;
|
||||
|
||||
while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) {
|
||||
switch (c) {
|
||||
case '4':
|
||||
do_ipv6 = 0;
|
||||
break;
|
||||
case '6':
|
||||
do_ipv4 = 0;
|
||||
break;
|
||||
case 'I':
|
||||
cfg_do_pktinfo = true;
|
||||
break;
|
||||
case 'n':
|
||||
cfg_loop_nodata = true;
|
||||
break;
|
||||
case 'r':
|
||||
proto_count++;
|
||||
cfg_proto = SOCK_RAW;
|
||||
cfg_ipproto = IPPROTO_UDP;
|
||||
break;
|
||||
case 'R':
|
||||
proto_count++;
|
||||
cfg_proto = SOCK_RAW;
|
||||
cfg_ipproto = IPPROTO_RAW;
|
||||
break;
|
||||
case 'u':
|
||||
proto_count++;
|
||||
cfg_proto = SOCK_DGRAM;
|
||||
cfg_ipproto = IPPROTO_UDP;
|
||||
break;
|
||||
case 'l':
|
||||
cfg_payload_len = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'p':
|
||||
dest_port = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'x':
|
||||
cfg_show_payload = true;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cfg_payload_len)
|
||||
error(1, 0, "payload may not be nonzero");
|
||||
if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)
|
||||
error(1, 0, "udp packet might exceed expected MTU");
|
||||
if (!do_ipv4 && !do_ipv6)
|
||||
error(1, 0, "pass -4 or -6, not both");
|
||||
if (proto_count > 1)
|
||||
error(1, 0, "pass -r, -R or -u, not multiple");
|
||||
|
||||
if (optind != argc - 1)
|
||||
error(1, 0, "missing required hostname argument");
|
||||
}
|
||||
|
||||
static void resolve_hostname(const char *hostname)
|
||||
{
|
||||
struct addrinfo *addrs, *cur;
|
||||
int have_ipv4 = 0, have_ipv6 = 0;
|
||||
|
||||
if (getaddrinfo(hostname, NULL, NULL, &addrs))
|
||||
error(1, errno, "getaddrinfo");
|
||||
|
||||
cur = addrs;
|
||||
while (cur && !have_ipv4 && !have_ipv6) {
|
||||
if (!have_ipv4 && cur->ai_family == AF_INET) {
|
||||
memcpy(&daddr, cur->ai_addr, sizeof(daddr));
|
||||
daddr.sin_port = htons(dest_port);
|
||||
have_ipv4 = 1;
|
||||
}
|
||||
else if (!have_ipv6 && cur->ai_family == AF_INET6) {
|
||||
memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));
|
||||
daddr6.sin6_port = htons(dest_port);
|
||||
have_ipv6 = 1;
|
||||
}
|
||||
cur = cur->ai_next;
|
||||
}
|
||||
if (addrs)
|
||||
freeaddrinfo(addrs);
|
||||
|
||||
do_ipv4 &= have_ipv4;
|
||||
do_ipv6 &= have_ipv6;
|
||||
}
|
||||
|
||||
static void do_main(int family)
|
||||
{
|
||||
fprintf(stderr, "family: %s\n",
|
||||
family == PF_INET ? "INET" : "INET6");
|
||||
|
||||
fprintf(stderr, "test SND\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
|
||||
|
||||
fprintf(stderr, "test ENQ\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SCHED);
|
||||
|
||||
fprintf(stderr, "test ENQ + SND\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SCHED |
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE);
|
||||
|
||||
if (cfg_proto == SOCK_STREAM) {
|
||||
fprintf(stderr, "\ntest ACK\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_ACK);
|
||||
|
||||
fprintf(stderr, "\ntest SND + ACK\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_TX_ACK);
|
||||
|
||||
fprintf(stderr, "\ntest ENQ + SND + ACK\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SCHED |
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_TX_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc == 1)
|
||||
usage(argv[0]);
|
||||
|
||||
parse_opt(argc, argv);
|
||||
resolve_hostname(argv[argc - 1]);
|
||||
|
||||
fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]);
|
||||
fprintf(stderr, "payload: %u\n", cfg_payload_len);
|
||||
fprintf(stderr, "server port: %u\n", dest_port);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
if (do_ipv4)
|
||||
do_main(PF_INET);
|
||||
if (do_ipv6)
|
||||
do_main(PF_INET6);
|
||||
|
||||
return 0;
|
||||
}
|
1
tools/testing/selftests/powerpc/copyloops/asm/export.h
Normal file
1
tools/testing/selftests/powerpc/copyloops/asm/export.h
Normal file
@@ -0,0 +1 @@
|
||||
#define EXPORT_SYMBOL(x)
|
@@ -4,3 +4,4 @@ fpu_preempt
|
||||
vmx_preempt
|
||||
fpu_signal
|
||||
vmx_signal
|
||||
vsx_preempt
|
||||
|
2
tools/testing/selftests/powerpc/signal/.gitignore
vendored
Normal file
2
tools/testing/selftests/powerpc/signal/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
signal
|
||||
signal_tm
|
1
tools/testing/selftests/powerpc/stringloops/asm/export.h
Normal file
1
tools/testing/selftests/powerpc/stringloops/asm/export.h
Normal file
@@ -0,0 +1 @@
|
||||
#define EXPORT_SYMBOL(x)
|
@@ -7,3 +7,7 @@ tm-fork
|
||||
tm-tar
|
||||
tm-tmspr
|
||||
tm-exec
|
||||
tm-signal-context-chk-fpu
|
||||
tm-signal-context-chk-gpr
|
||||
tm-signal-context-chk-vmx
|
||||
tm-signal-context-chk-vsx
|
||||
|
3
tools/testing/selftests/prctl/.gitignore
vendored
Normal file
3
tools/testing/selftests/prctl/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
disable-tsc-ctxt-sw-stress-test
|
||||
disable-tsc-on-off-stress-test
|
||||
disable-tsc-test
|
15
tools/testing/selftests/prctl/Makefile
Normal file
15
tools/testing/selftests/prctl/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
ifndef CROSS_COMPILE
|
||||
uname_M := $(shell uname -m 2>/dev/null || echo not)
|
||||
ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
|
||||
|
||||
ifeq ($(ARCH),x86)
|
||||
TEST_PROGS := disable-tsc-ctxt-sw-stress-test disable-tsc-on-off-stress-test \
|
||||
disable-tsc-test
|
||||
all: $(TEST_PROGS)
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
clean:
|
||||
rm -fr $(TEST_PROGS)
|
||||
endif
|
||||
endif
|
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
|
||||
*
|
||||
* Tests if the control register is updated correctly
|
||||
* at context switches
|
||||
*
|
||||
* Warning: this test will cause a very high load for a few seconds
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <inttypes.h>
|
||||
#include <wait.h>
|
||||
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <linux/prctl.h>
|
||||
|
||||
/* Get/set the process' ability to use the timestamp counter instruction */
|
||||
#ifndef PR_GET_TSC
|
||||
#define PR_GET_TSC 25
|
||||
#define PR_SET_TSC 26
|
||||
# define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */
|
||||
# define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */
|
||||
#endif
|
||||
|
||||
static uint64_t rdtsc(void)
|
||||
{
|
||||
uint32_t lo, hi;
|
||||
/* We cannot use "=A", since this would use %rax on x86_64 */
|
||||
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
|
||||
return (uint64_t)hi << 32 | lo;
|
||||
}
|
||||
|
||||
static void sigsegv_expect(int sig)
|
||||
{
|
||||
/* */
|
||||
}
|
||||
|
||||
static void segvtask(void)
|
||||
{
|
||||
if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0)
|
||||
{
|
||||
perror("prctl");
|
||||
exit(0);
|
||||
}
|
||||
signal(SIGSEGV, sigsegv_expect);
|
||||
alarm(10);
|
||||
rdtsc();
|
||||
fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
static void sigsegv_fail(int sig)
|
||||
{
|
||||
fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void rdtsctask(void)
|
||||
{
|
||||
if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0)
|
||||
{
|
||||
perror("prctl");
|
||||
exit(0);
|
||||
}
|
||||
signal(SIGSEGV, sigsegv_fail);
|
||||
alarm(10);
|
||||
for(;;) rdtsc();
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int n_tasks = 100, i;
|
||||
|
||||
fprintf(stderr, "[No further output means we're allright]\n");
|
||||
|
||||
for (i=0; i<n_tasks; i++)
|
||||
if (fork() == 0)
|
||||
{
|
||||
if (i & 1)
|
||||
segvtask();
|
||||
else
|
||||
rdtsctask();
|
||||
}
|
||||
|
||||
for (i=0; i<n_tasks; i++)
|
||||
wait(NULL);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
|
||||
*
|
||||
* Tests if the control register is updated correctly
|
||||
* when set with prctl()
|
||||
*
|
||||
* Warning: this test will cause a very high load for a few seconds
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <inttypes.h>
|
||||
#include <wait.h>
|
||||
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <linux/prctl.h>
|
||||
|
||||
/* Get/set the process' ability to use the timestamp counter instruction */
|
||||
#ifndef PR_GET_TSC
|
||||
#define PR_GET_TSC 25
|
||||
#define PR_SET_TSC 26
|
||||
# define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */
|
||||
# define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */
|
||||
#endif
|
||||
|
||||
/* snippet from wikipedia :-) */
|
||||
|
||||
static uint64_t rdtsc(void)
|
||||
{
|
||||
uint32_t lo, hi;
|
||||
/* We cannot use "=A", since this would use %rax on x86_64 */
|
||||
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
|
||||
return (uint64_t)hi << 32 | lo;
|
||||
}
|
||||
|
||||
int should_segv = 0;
|
||||
|
||||
static void sigsegv_cb(int sig)
|
||||
{
|
||||
if (!should_segv)
|
||||
{
|
||||
fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n");
|
||||
exit(0);
|
||||
}
|
||||
if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0)
|
||||
{
|
||||
perror("prctl");
|
||||
exit(0);
|
||||
}
|
||||
should_segv = 0;
|
||||
|
||||
rdtsc();
|
||||
}
|
||||
|
||||
static void task(void)
|
||||
{
|
||||
signal(SIGSEGV, sigsegv_cb);
|
||||
alarm(10);
|
||||
for(;;)
|
||||
{
|
||||
rdtsc();
|
||||
if (should_segv)
|
||||
{
|
||||
fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n");
|
||||
exit(0);
|
||||
}
|
||||
if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0)
|
||||
{
|
||||
perror("prctl");
|
||||
exit(0);
|
||||
}
|
||||
should_segv = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int n_tasks = 100, i;
|
||||
|
||||
fprintf(stderr, "[No further output means we're allright]\n");
|
||||
|
||||
for (i=0; i<n_tasks; i++)
|
||||
if (fork() == 0)
|
||||
task();
|
||||
|
||||
for (i=0; i<n_tasks; i++)
|
||||
wait(NULL);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
95
tools/testing/selftests/prctl/disable-tsc-test.c
Normal file
95
tools/testing/selftests/prctl/disable-tsc-test.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
|
||||
*
|
||||
* Basic test to test behaviour of PR_GET_TSC and PR_SET_TSC
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <linux/prctl.h>
|
||||
|
||||
/* Get/set the process' ability to use the timestamp counter instruction */
|
||||
#ifndef PR_GET_TSC
|
||||
#define PR_GET_TSC 25
|
||||
#define PR_SET_TSC 26
|
||||
# define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */
|
||||
# define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */
|
||||
#endif
|
||||
|
||||
const char *tsc_names[] =
|
||||
{
|
||||
[0] = "[not set]",
|
||||
[PR_TSC_ENABLE] = "PR_TSC_ENABLE",
|
||||
[PR_TSC_SIGSEGV] = "PR_TSC_SIGSEGV",
|
||||
};
|
||||
|
||||
static uint64_t rdtsc(void)
|
||||
{
|
||||
uint32_t lo, hi;
|
||||
/* We cannot use "=A", since this would use %rax on x86_64 */
|
||||
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
|
||||
return (uint64_t)hi << 32 | lo;
|
||||
}
|
||||
|
||||
static void sigsegv_cb(int sig)
|
||||
{
|
||||
int tsc_val = 0;
|
||||
|
||||
printf("[ SIG_SEGV ]\n");
|
||||
printf("prctl(PR_GET_TSC, &tsc_val); ");
|
||||
fflush(stdout);
|
||||
|
||||
if ( prctl(PR_GET_TSC, &tsc_val) == -1)
|
||||
perror("prctl");
|
||||
|
||||
printf("tsc_val == %s\n", tsc_names[tsc_val]);
|
||||
printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n");
|
||||
fflush(stdout);
|
||||
if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1)
|
||||
perror("prctl");
|
||||
|
||||
printf("rdtsc() == ");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int tsc_val = 0;
|
||||
|
||||
signal(SIGSEGV, sigsegv_cb);
|
||||
|
||||
printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
|
||||
printf("prctl(PR_GET_TSC, &tsc_val); ");
|
||||
fflush(stdout);
|
||||
|
||||
if ( prctl(PR_GET_TSC, &tsc_val) == -1)
|
||||
perror("prctl");
|
||||
|
||||
printf("tsc_val == %s\n", tsc_names[tsc_val]);
|
||||
printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
|
||||
printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n");
|
||||
fflush(stdout);
|
||||
|
||||
if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1)
|
||||
perror("prctl");
|
||||
|
||||
printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
|
||||
printf("prctl(PR_SET_TSC, PR_TSC_SIGSEGV)\n");
|
||||
fflush(stdout);
|
||||
|
||||
if ( prctl(PR_SET_TSC, PR_TSC_SIGSEGV) == -1)
|
||||
perror("prctl");
|
||||
|
||||
printf("rdtsc() == ");
|
||||
fflush(stdout);
|
||||
printf("%llu\n", (unsigned long long)rdtsc());
|
||||
fflush(stdout);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
1
tools/testing/selftests/ptp/.gitignore
vendored
Normal file
1
tools/testing/selftests/ptp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
testptp
|
8
tools/testing/selftests/ptp/Makefile
Normal file
8
tools/testing/selftests/ptp/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
TEST_PROGS := testptp
|
||||
LDLIBS += -lrt
|
||||
all: $(TEST_PROGS)
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
clean:
|
||||
rm -fr $(TEST_PROGS)
|
523
tools/testing/selftests/ptp/testptp.c
Normal file
523
tools/testing/selftests/ptp/testptp.c
Normal file
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* PTP 1588 clock support - User space test program
|
||||
*
|
||||
* Copyright (C) 2010 OMICRON electronics GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timex.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/ptp_clock.h>
|
||||
|
||||
#define DEVICE "/dev/ptp0"
|
||||
|
||||
#ifndef ADJ_SETOFFSET
|
||||
#define ADJ_SETOFFSET 0x0100
|
||||
#endif
|
||||
|
||||
#ifndef CLOCK_INVALID
|
||||
#define CLOCK_INVALID -1
|
||||
#endif
|
||||
|
||||
/* clock_adjtime is not available in GLIBC < 2.14 */
|
||||
#if !__GLIBC_PREREQ(2, 14)
|
||||
#include <sys/syscall.h>
|
||||
static int clock_adjtime(clockid_t id, struct timex *tx)
|
||||
{
|
||||
return syscall(__NR_clock_adjtime, id, tx);
|
||||
}
|
||||
#endif
|
||||
|
||||
static clockid_t get_clockid(int fd)
|
||||
{
|
||||
#define CLOCKFD 3
|
||||
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
|
||||
|
||||
return FD_TO_CLOCKID(fd);
|
||||
}
|
||||
|
||||
static void handle_alarm(int s)
|
||||
{
|
||||
printf("received signal %d\n", s);
|
||||
}
|
||||
|
||||
static int install_handler(int signum, void (*handler)(int))
|
||||
{
|
||||
struct sigaction action;
|
||||
sigset_t mask;
|
||||
|
||||
/* Unblock the signal. */
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, signum);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
|
||||
/* Install the signal handler. */
|
||||
action.sa_handler = handler;
|
||||
action.sa_flags = 0;
|
||||
sigemptyset(&action.sa_mask);
|
||||
sigaction(signum, &action, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long ppb_to_scaled_ppm(int ppb)
|
||||
{
|
||||
/*
|
||||
* The 'freq' field in the 'struct timex' is in parts per
|
||||
* million, but with a 16 bit binary fractional field.
|
||||
* Instead of calculating either one of
|
||||
*
|
||||
* scaled_ppm = (ppb / 1000) << 16 [1]
|
||||
* scaled_ppm = (ppb << 16) / 1000 [2]
|
||||
*
|
||||
* we simply use double precision math, in order to avoid the
|
||||
* truncation in [1] and the possible overflow in [2].
|
||||
*/
|
||||
return (long) (ppb * 65.536);
|
||||
}
|
||||
|
||||
static int64_t pctns(struct ptp_clock_time *t)
|
||||
{
|
||||
return t->sec * 1000000000LL + t->nsec;
|
||||
}
|
||||
|
||||
static void usage(char *progname)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [options]\n"
|
||||
" -a val request a one-shot alarm after 'val' seconds\n"
|
||||
" -A val request a periodic alarm every 'val' seconds\n"
|
||||
" -c query the ptp clock's capabilities\n"
|
||||
" -d name device to open\n"
|
||||
" -e val read 'val' external time stamp events\n"
|
||||
" -f val adjust the ptp clock frequency by 'val' ppb\n"
|
||||
" -g get the ptp clock time\n"
|
||||
" -h prints this message\n"
|
||||
" -i val index for event/trigger\n"
|
||||
" -k val measure the time offset between system and phc clock\n"
|
||||
" for 'val' times (Maximum 25)\n"
|
||||
" -l list the current pin configuration\n"
|
||||
" -L pin,val configure pin index 'pin' with function 'val'\n"
|
||||
" the channel index is taken from the '-i' option\n"
|
||||
" 'val' specifies the auxiliary function:\n"
|
||||
" 0 - none\n"
|
||||
" 1 - external time stamp\n"
|
||||
" 2 - periodic output\n"
|
||||
" -p val enable output with a period of 'val' nanoseconds\n"
|
||||
" -P val enable or disable (val=1|0) the system clock PPS\n"
|
||||
" -s set the ptp clock time from the system time\n"
|
||||
" -S set the system time from the ptp clock time\n"
|
||||
" -t val shift the ptp clock time by 'val' seconds\n"
|
||||
" -T val set the ptp clock time to 'val' seconds\n",
|
||||
progname);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct ptp_clock_caps caps;
|
||||
struct ptp_extts_event event;
|
||||
struct ptp_extts_request extts_request;
|
||||
struct ptp_perout_request perout_request;
|
||||
struct ptp_pin_desc desc;
|
||||
struct timespec ts;
|
||||
struct timex tx;
|
||||
|
||||
static timer_t timerid;
|
||||
struct itimerspec timeout;
|
||||
struct sigevent sigevent;
|
||||
|
||||
struct ptp_clock_time *pct;
|
||||
struct ptp_sys_offset *sysoff;
|
||||
|
||||
|
||||
char *progname;
|
||||
unsigned int i;
|
||||
int c, cnt, fd;
|
||||
|
||||
char *device = DEVICE;
|
||||
clockid_t clkid;
|
||||
int adjfreq = 0x7fffffff;
|
||||
int adjtime = 0;
|
||||
int capabilities = 0;
|
||||
int extts = 0;
|
||||
int gettime = 0;
|
||||
int index = 0;
|
||||
int list_pins = 0;
|
||||
int oneshot = 0;
|
||||
int pct_offset = 0;
|
||||
int n_samples = 0;
|
||||
int periodic = 0;
|
||||
int perout = -1;
|
||||
int pin_index = -1, pin_func;
|
||||
int pps = -1;
|
||||
int seconds = 0;
|
||||
int settime = 0;
|
||||
|
||||
int64_t t1, t2, tp;
|
||||
int64_t interval, offset;
|
||||
|
||||
progname = strrchr(argv[0], '/');
|
||||
progname = progname ? 1+progname : argv[0];
|
||||
while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
oneshot = atoi(optarg);
|
||||
break;
|
||||
case 'A':
|
||||
periodic = atoi(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
capabilities = 1;
|
||||
break;
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
extts = atoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
adjfreq = atoi(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
gettime = 1;
|
||||
break;
|
||||
case 'i':
|
||||
index = atoi(optarg);
|
||||
break;
|
||||
case 'k':
|
||||
pct_offset = 1;
|
||||
n_samples = atoi(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
list_pins = 1;
|
||||
break;
|
||||
case 'L':
|
||||
cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func);
|
||||
if (cnt != 2) {
|
||||
usage(progname);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
perout = atoi(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
pps = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
settime = 1;
|
||||
break;
|
||||
case 'S':
|
||||
settime = 2;
|
||||
break;
|
||||
case 't':
|
||||
adjtime = atoi(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
settime = 3;
|
||||
seconds = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
usage(progname);
|
||||
return 0;
|
||||
case '?':
|
||||
default:
|
||||
usage(progname);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fd = open(device, O_RDWR);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "opening %s: %s\n", device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
clkid = get_clockid(fd);
|
||||
if (CLOCK_INVALID == clkid) {
|
||||
fprintf(stderr, "failed to read clock id\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (capabilities) {
|
||||
if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
|
||||
perror("PTP_CLOCK_GETCAPS");
|
||||
} else {
|
||||
printf("capabilities:\n"
|
||||
" %d maximum frequency adjustment (ppb)\n"
|
||||
" %d programmable alarms\n"
|
||||
" %d external time stamp channels\n"
|
||||
" %d programmable periodic signals\n"
|
||||
" %d pulse per second\n"
|
||||
" %d programmable pins\n"
|
||||
" %d cross timestamping\n",
|
||||
caps.max_adj,
|
||||
caps.n_alarm,
|
||||
caps.n_ext_ts,
|
||||
caps.n_per_out,
|
||||
caps.pps,
|
||||
caps.n_pins,
|
||||
caps.cross_timestamping);
|
||||
}
|
||||
}
|
||||
|
||||
if (0x7fffffff != adjfreq) {
|
||||
memset(&tx, 0, sizeof(tx));
|
||||
tx.modes = ADJ_FREQUENCY;
|
||||
tx.freq = ppb_to_scaled_ppm(adjfreq);
|
||||
if (clock_adjtime(clkid, &tx)) {
|
||||
perror("clock_adjtime");
|
||||
} else {
|
||||
puts("frequency adjustment okay");
|
||||
}
|
||||
}
|
||||
|
||||
if (adjtime) {
|
||||
memset(&tx, 0, sizeof(tx));
|
||||
tx.modes = ADJ_SETOFFSET;
|
||||
tx.time.tv_sec = adjtime;
|
||||
tx.time.tv_usec = 0;
|
||||
if (clock_adjtime(clkid, &tx) < 0) {
|
||||
perror("clock_adjtime");
|
||||
} else {
|
||||
puts("time shift okay");
|
||||
}
|
||||
}
|
||||
|
||||
if (gettime) {
|
||||
if (clock_gettime(clkid, &ts)) {
|
||||
perror("clock_gettime");
|
||||
} else {
|
||||
printf("clock time: %ld.%09ld or %s",
|
||||
ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
|
||||
}
|
||||
}
|
||||
|
||||
if (settime == 1) {
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
if (clock_settime(clkid, &ts)) {
|
||||
perror("clock_settime");
|
||||
} else {
|
||||
puts("set time okay");
|
||||
}
|
||||
}
|
||||
|
||||
if (settime == 2) {
|
||||
clock_gettime(clkid, &ts);
|
||||
if (clock_settime(CLOCK_REALTIME, &ts)) {
|
||||
perror("clock_settime");
|
||||
} else {
|
||||
puts("set time okay");
|
||||
}
|
||||
}
|
||||
|
||||
if (settime == 3) {
|
||||
ts.tv_sec = seconds;
|
||||
ts.tv_nsec = 0;
|
||||
if (clock_settime(clkid, &ts)) {
|
||||
perror("clock_settime");
|
||||
} else {
|
||||
puts("set time okay");
|
||||
}
|
||||
}
|
||||
|
||||
if (extts) {
|
||||
memset(&extts_request, 0, sizeof(extts_request));
|
||||
extts_request.index = index;
|
||||
extts_request.flags = PTP_ENABLE_FEATURE;
|
||||
if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
|
||||
perror("PTP_EXTTS_REQUEST");
|
||||
extts = 0;
|
||||
} else {
|
||||
puts("external time stamp request okay");
|
||||
}
|
||||
for (; extts; extts--) {
|
||||
cnt = read(fd, &event, sizeof(event));
|
||||
if (cnt != sizeof(event)) {
|
||||
perror("read");
|
||||
break;
|
||||
}
|
||||
printf("event index %u at %lld.%09u\n", event.index,
|
||||
event.t.sec, event.t.nsec);
|
||||
fflush(stdout);
|
||||
}
|
||||
/* Disable the feature again. */
|
||||
extts_request.flags = 0;
|
||||
if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
|
||||
perror("PTP_EXTTS_REQUEST");
|
||||
}
|
||||
}
|
||||
|
||||
if (list_pins) {
|
||||
int n_pins = 0;
|
||||
if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
|
||||
perror("PTP_CLOCK_GETCAPS");
|
||||
} else {
|
||||
n_pins = caps.n_pins;
|
||||
}
|
||||
for (i = 0; i < n_pins; i++) {
|
||||
desc.index = i;
|
||||
if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) {
|
||||
perror("PTP_PIN_GETFUNC");
|
||||
break;
|
||||
}
|
||||
printf("name %s index %u func %u chan %u\n",
|
||||
desc.name, desc.index, desc.func, desc.chan);
|
||||
}
|
||||
}
|
||||
|
||||
if (oneshot) {
|
||||
install_handler(SIGALRM, handle_alarm);
|
||||
/* Create a timer. */
|
||||
sigevent.sigev_notify = SIGEV_SIGNAL;
|
||||
sigevent.sigev_signo = SIGALRM;
|
||||
if (timer_create(clkid, &sigevent, &timerid)) {
|
||||
perror("timer_create");
|
||||
return -1;
|
||||
}
|
||||
/* Start the timer. */
|
||||
memset(&timeout, 0, sizeof(timeout));
|
||||
timeout.it_value.tv_sec = oneshot;
|
||||
if (timer_settime(timerid, 0, &timeout, NULL)) {
|
||||
perror("timer_settime");
|
||||
return -1;
|
||||
}
|
||||
pause();
|
||||
timer_delete(timerid);
|
||||
}
|
||||
|
||||
if (periodic) {
|
||||
install_handler(SIGALRM, handle_alarm);
|
||||
/* Create a timer. */
|
||||
sigevent.sigev_notify = SIGEV_SIGNAL;
|
||||
sigevent.sigev_signo = SIGALRM;
|
||||
if (timer_create(clkid, &sigevent, &timerid)) {
|
||||
perror("timer_create");
|
||||
return -1;
|
||||
}
|
||||
/* Start the timer. */
|
||||
memset(&timeout, 0, sizeof(timeout));
|
||||
timeout.it_interval.tv_sec = periodic;
|
||||
timeout.it_value.tv_sec = periodic;
|
||||
if (timer_settime(timerid, 0, &timeout, NULL)) {
|
||||
perror("timer_settime");
|
||||
return -1;
|
||||
}
|
||||
while (1) {
|
||||
pause();
|
||||
}
|
||||
timer_delete(timerid);
|
||||
}
|
||||
|
||||
if (perout >= 0) {
|
||||
if (clock_gettime(clkid, &ts)) {
|
||||
perror("clock_gettime");
|
||||
return -1;
|
||||
}
|
||||
memset(&perout_request, 0, sizeof(perout_request));
|
||||
perout_request.index = index;
|
||||
perout_request.start.sec = ts.tv_sec + 2;
|
||||
perout_request.start.nsec = 0;
|
||||
perout_request.period.sec = 0;
|
||||
perout_request.period.nsec = perout;
|
||||
if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
|
||||
perror("PTP_PEROUT_REQUEST");
|
||||
} else {
|
||||
puts("periodic output request okay");
|
||||
}
|
||||
}
|
||||
|
||||
if (pin_index >= 0) {
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
desc.index = pin_index;
|
||||
desc.func = pin_func;
|
||||
desc.chan = index;
|
||||
if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {
|
||||
perror("PTP_PIN_SETFUNC");
|
||||
} else {
|
||||
puts("set pin function okay");
|
||||
}
|
||||
}
|
||||
|
||||
if (pps != -1) {
|
||||
int enable = pps ? 1 : 0;
|
||||
if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
|
||||
perror("PTP_ENABLE_PPS");
|
||||
} else {
|
||||
puts("pps for system time request okay");
|
||||
}
|
||||
}
|
||||
|
||||
if (pct_offset) {
|
||||
if (n_samples <= 0 || n_samples > 25) {
|
||||
puts("n_samples should be between 1 and 25");
|
||||
usage(progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sysoff = calloc(1, sizeof(*sysoff));
|
||||
if (!sysoff) {
|
||||
perror("calloc");
|
||||
return -1;
|
||||
}
|
||||
sysoff->n_samples = n_samples;
|
||||
|
||||
if (ioctl(fd, PTP_SYS_OFFSET, sysoff))
|
||||
perror("PTP_SYS_OFFSET");
|
||||
else
|
||||
puts("system and phc clock time offset request okay");
|
||||
|
||||
pct = &sysoff->ts[0];
|
||||
for (i = 0; i < sysoff->n_samples; i++) {
|
||||
t1 = pctns(pct+2*i);
|
||||
tp = pctns(pct+2*i+1);
|
||||
t2 = pctns(pct+2*i+2);
|
||||
interval = t2 - t1;
|
||||
offset = (t2 + t1) / 2 - tp;
|
||||
|
||||
printf("system time: %lld.%u\n",
|
||||
(pct+2*i)->sec, (pct+2*i)->nsec);
|
||||
printf("phc time: %lld.%u\n",
|
||||
(pct+2*i+1)->sec, (pct+2*i+1)->nsec);
|
||||
printf("system time: %lld.%u\n",
|
||||
(pct+2*i+2)->sec, (pct+2*i+2)->nsec);
|
||||
printf("system/phc clock time offset is %" PRId64 " ns\n"
|
||||
"system clock time delay is %" PRId64 " ns\n",
|
||||
offset, interval);
|
||||
}
|
||||
|
||||
free(sysoff);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
33
tools/testing/selftests/ptp/testptp.mk
Normal file
33
tools/testing/selftests/ptp/testptp.mk
Normal file
@@ -0,0 +1,33 @@
|
||||
# PTP 1588 clock support - User space test program
|
||||
#
|
||||
# Copyright (C) 2010 OMICRON electronics GmbH
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
INC = -I$(KBUILD_OUTPUT)/usr/include
|
||||
CFLAGS = -Wall $(INC)
|
||||
LDLIBS = -lrt
|
||||
PROGS = testptp
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
testptp: testptp.o
|
||||
|
||||
clean:
|
||||
rm -f testptp.o
|
||||
|
||||
distclean: clean
|
||||
rm -f $(PROGS)
|
@@ -122,7 +122,7 @@ static int check_itimer(int which)
|
||||
else if (which == ITIMER_REAL)
|
||||
idle_loop();
|
||||
|
||||
gettimeofday(&end, NULL);
|
||||
err = gettimeofday(&end, NULL);
|
||||
if (err < 0) {
|
||||
perror("Can't call gettimeofday()\n");
|
||||
return -1;
|
||||
@@ -175,7 +175,7 @@ static int check_timer_create(int which)
|
||||
|
||||
user_loop();
|
||||
|
||||
gettimeofday(&end, NULL);
|
||||
err = gettimeofday(&end, NULL);
|
||||
if (err < 0) {
|
||||
perror("Can't call gettimeofday()\n");
|
||||
return -1;
|
||||
|
2
tools/testing/selftests/vDSO/.gitignore
vendored
Normal file
2
tools/testing/selftests/vDSO/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
vdso_test
|
||||
vdso_standalone_test_x86
|
20
tools/testing/selftests/vDSO/Makefile
Normal file
20
tools/testing/selftests/vDSO/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
ifndef CROSS_COMPILE
|
||||
CFLAGS := -std=gnu99
|
||||
CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector
|
||||
ifeq ($(CONFIG_X86_32),y)
|
||||
LDLIBS += -lgcc_s
|
||||
endif
|
||||
|
||||
TEST_PROGS := vdso_test vdso_standalone_test_x86
|
||||
|
||||
all: $(TEST_PROGS)
|
||||
vdso_test: parse_vdso.c vdso_test.c
|
||||
vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c
|
||||
$(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \
|
||||
vdso_standalone_test_x86.c parse_vdso.c \
|
||||
-o vdso_standalone_test_x86
|
||||
|
||||
include ../lib.mk
|
||||
clean:
|
||||
rm -fr $(TEST_PROGS)
|
||||
endif
|
269
tools/testing/selftests/vDSO/parse_vdso.c
Normal file
269
tools/testing/selftests/vDSO/parse_vdso.c
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* parse_vdso.c: Linux reference vDSO parser
|
||||
* Written by Andrew Lutomirski, 2011-2014.
|
||||
*
|
||||
* This code is meant to be linked in to various programs that run on Linux.
|
||||
* As such, it is available with as few restrictions as possible. This file
|
||||
* is licensed under the Creative Commons Zero License, version 1.0,
|
||||
* available at http://creativecommons.org/publicdomain/zero/1.0/legalcode
|
||||
*
|
||||
* The vDSO is a regular ELF DSO that the kernel maps into user space when
|
||||
* it starts a program. It works equally well in statically and dynamically
|
||||
* linked binaries.
|
||||
*
|
||||
* This code is tested on x86. In principle it should work on any
|
||||
* architecture that has a vDSO.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <elf.h>
|
||||
|
||||
/*
|
||||
* To use this vDSO parser, first call one of the vdso_init_* functions.
|
||||
* If you've already parsed auxv, then pass the value of AT_SYSINFO_EHDR
|
||||
* to vdso_init_from_sysinfo_ehdr. Otherwise pass auxv to vdso_init_from_auxv.
|
||||
* Then call vdso_sym for each symbol you want. For example, to look up
|
||||
* gettimeofday on x86_64, use:
|
||||
*
|
||||
* <some pointer> = vdso_sym("LINUX_2.6", "gettimeofday");
|
||||
* or
|
||||
* <some pointer> = vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
|
||||
*
|
||||
* vdso_sym will return 0 if the symbol doesn't exist or if the init function
|
||||
* failed or was not called. vdso_sym is a little slow, so its return value
|
||||
* should be cached.
|
||||
*
|
||||
* vdso_sym is threadsafe; the init functions are not.
|
||||
*
|
||||
* These are the prototypes:
|
||||
*/
|
||||
extern void vdso_init_from_auxv(void *auxv);
|
||||
extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
|
||||
extern void *vdso_sym(const char *version, const char *name);
|
||||
|
||||
|
||||
/* And here's the code. */
|
||||
#ifndef ELF_BITS
|
||||
# if ULONG_MAX > 0xffffffffUL
|
||||
# define ELF_BITS 64
|
||||
# else
|
||||
# define ELF_BITS 32
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
|
||||
#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
|
||||
#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)
|
||||
|
||||
static struct vdso_info
|
||||
{
|
||||
bool valid;
|
||||
|
||||
/* Load information */
|
||||
uintptr_t load_addr;
|
||||
uintptr_t load_offset; /* load_addr - recorded vaddr */
|
||||
|
||||
/* Symbol table */
|
||||
ELF(Sym) *symtab;
|
||||
const char *symstrings;
|
||||
ELF(Word) *bucket, *chain;
|
||||
ELF(Word) nbucket, nchain;
|
||||
|
||||
/* Version table */
|
||||
ELF(Versym) *versym;
|
||||
ELF(Verdef) *verdef;
|
||||
} vdso_info;
|
||||
|
||||
/* Straight from the ELF specification. */
|
||||
static unsigned long elf_hash(const unsigned char *name)
|
||||
{
|
||||
unsigned long h = 0, g;
|
||||
while (*name)
|
||||
{
|
||||
h = (h << 4) + *name++;
|
||||
if (g = h & 0xf0000000)
|
||||
h ^= g >> 24;
|
||||
h &= ~g;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
void vdso_init_from_sysinfo_ehdr(uintptr_t base)
|
||||
{
|
||||
size_t i;
|
||||
bool found_vaddr = false;
|
||||
|
||||
vdso_info.valid = false;
|
||||
|
||||
vdso_info.load_addr = base;
|
||||
|
||||
ELF(Ehdr) *hdr = (ELF(Ehdr)*)base;
|
||||
if (hdr->e_ident[EI_CLASS] !=
|
||||
(ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) {
|
||||
return; /* Wrong ELF class -- check ELF_BITS */
|
||||
}
|
||||
|
||||
ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff);
|
||||
ELF(Dyn) *dyn = 0;
|
||||
|
||||
/*
|
||||
* We need two things from the segment table: the load offset
|
||||
* and the dynamic table.
|
||||
*/
|
||||
for (i = 0; i < hdr->e_phnum; i++)
|
||||
{
|
||||
if (pt[i].p_type == PT_LOAD && !found_vaddr) {
|
||||
found_vaddr = true;
|
||||
vdso_info.load_offset = base
|
||||
+ (uintptr_t)pt[i].p_offset
|
||||
- (uintptr_t)pt[i].p_vaddr;
|
||||
} else if (pt[i].p_type == PT_DYNAMIC) {
|
||||
dyn = (ELF(Dyn)*)(base + pt[i].p_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_vaddr || !dyn)
|
||||
return; /* Failed */
|
||||
|
||||
/*
|
||||
* Fish out the useful bits of the dynamic table.
|
||||
*/
|
||||
ELF(Word) *hash = 0;
|
||||
vdso_info.symstrings = 0;
|
||||
vdso_info.symtab = 0;
|
||||
vdso_info.versym = 0;
|
||||
vdso_info.verdef = 0;
|
||||
for (i = 0; dyn[i].d_tag != DT_NULL; i++) {
|
||||
switch (dyn[i].d_tag) {
|
||||
case DT_STRTAB:
|
||||
vdso_info.symstrings = (const char *)
|
||||
((uintptr_t)dyn[i].d_un.d_ptr
|
||||
+ vdso_info.load_offset);
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
vdso_info.symtab = (ELF(Sym) *)
|
||||
((uintptr_t)dyn[i].d_un.d_ptr
|
||||
+ vdso_info.load_offset);
|
||||
break;
|
||||
case DT_HASH:
|
||||
hash = (ELF(Word) *)
|
||||
((uintptr_t)dyn[i].d_un.d_ptr
|
||||
+ vdso_info.load_offset);
|
||||
break;
|
||||
case DT_VERSYM:
|
||||
vdso_info.versym = (ELF(Versym) *)
|
||||
((uintptr_t)dyn[i].d_un.d_ptr
|
||||
+ vdso_info.load_offset);
|
||||
break;
|
||||
case DT_VERDEF:
|
||||
vdso_info.verdef = (ELF(Verdef) *)
|
||||
((uintptr_t)dyn[i].d_un.d_ptr
|
||||
+ vdso_info.load_offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!vdso_info.symstrings || !vdso_info.symtab || !hash)
|
||||
return; /* Failed */
|
||||
|
||||
if (!vdso_info.verdef)
|
||||
vdso_info.versym = 0;
|
||||
|
||||
/* Parse the hash table header. */
|
||||
vdso_info.nbucket = hash[0];
|
||||
vdso_info.nchain = hash[1];
|
||||
vdso_info.bucket = &hash[2];
|
||||
vdso_info.chain = &hash[vdso_info.nbucket + 2];
|
||||
|
||||
/* That's all we need. */
|
||||
vdso_info.valid = true;
|
||||
}
|
||||
|
||||
static bool vdso_match_version(ELF(Versym) ver,
|
||||
const char *name, ELF(Word) hash)
|
||||
{
|
||||
/*
|
||||
* This is a helper function to check if the version indexed by
|
||||
* ver matches name (which hashes to hash).
|
||||
*
|
||||
* The version definition table is a mess, and I don't know how
|
||||
* to do this in better than linear time without allocating memory
|
||||
* to build an index. I also don't know why the table has
|
||||
* variable size entries in the first place.
|
||||
*
|
||||
* For added fun, I can't find a comprehensible specification of how
|
||||
* to parse all the weird flags in the table.
|
||||
*
|
||||
* So I just parse the whole table every time.
|
||||
*/
|
||||
|
||||
/* First step: find the version definition */
|
||||
ver &= 0x7fff; /* Apparently bit 15 means "hidden" */
|
||||
ELF(Verdef) *def = vdso_info.verdef;
|
||||
while(true) {
|
||||
if ((def->vd_flags & VER_FLG_BASE) == 0
|
||||
&& (def->vd_ndx & 0x7fff) == ver)
|
||||
break;
|
||||
|
||||
if (def->vd_next == 0)
|
||||
return false; /* No definition. */
|
||||
|
||||
def = (ELF(Verdef) *)((char *)def + def->vd_next);
|
||||
}
|
||||
|
||||
/* Now figure out whether it matches. */
|
||||
ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux);
|
||||
return def->vd_hash == hash
|
||||
&& !strcmp(name, vdso_info.symstrings + aux->vda_name);
|
||||
}
|
||||
|
||||
void *vdso_sym(const char *version, const char *name)
|
||||
{
|
||||
unsigned long ver_hash;
|
||||
if (!vdso_info.valid)
|
||||
return 0;
|
||||
|
||||
ver_hash = elf_hash(version);
|
||||
ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
|
||||
|
||||
for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) {
|
||||
ELF(Sym) *sym = &vdso_info.symtab[chain];
|
||||
|
||||
/* Check for a defined global or weak function w/ right name. */
|
||||
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
|
||||
continue;
|
||||
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
|
||||
ELF64_ST_BIND(sym->st_info) != STB_WEAK)
|
||||
continue;
|
||||
if (sym->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
if (strcmp(name, vdso_info.symstrings + sym->st_name))
|
||||
continue;
|
||||
|
||||
/* Check symbol version. */
|
||||
if (vdso_info.versym
|
||||
&& !vdso_match_version(vdso_info.versym[chain],
|
||||
version, ver_hash))
|
||||
continue;
|
||||
|
||||
return (void *)(vdso_info.load_offset + sym->st_value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vdso_init_from_auxv(void *auxv)
|
||||
{
|
||||
ELF(auxv_t) *elf_auxv = auxv;
|
||||
for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++)
|
||||
{
|
||||
if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) {
|
||||
vdso_init_from_sysinfo_ehdr(elf_auxv[i].a_un.a_val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vdso_info.valid = false;
|
||||
}
|
128
tools/testing/selftests/vDSO/vdso_standalone_test_x86.c
Normal file
128
tools/testing/selftests/vDSO/vdso_standalone_test_x86.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* vdso_test.c: Sample code to test parse_vdso.c on x86
|
||||
* Copyright (c) 2011-2014 Andy Lutomirski
|
||||
* Subject to the GNU General Public License, version 2
|
||||
*
|
||||
* You can amuse yourself by compiling with:
|
||||
* gcc -std=gnu99 -nostdlib
|
||||
* -Os -fno-asynchronous-unwind-tables -flto -lgcc_s
|
||||
* vdso_standalone_test_x86.c parse_vdso.c
|
||||
* to generate a small binary. On x86_64, you can omit -lgcc_s
|
||||
* if you want the binary to be completely standalone.
|
||||
*/
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern void *vdso_sym(const char *version, const char *name);
|
||||
extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
|
||||
extern void vdso_init_from_auxv(void *auxv);
|
||||
|
||||
/* We need a libc functions... */
|
||||
int strcmp(const char *a, const char *b)
|
||||
{
|
||||
/* This implementation is buggy: it never returns -1. */
|
||||
while (*a || *b) {
|
||||
if (*a != *b)
|
||||
return 1;
|
||||
if (*a == 0 || *b == 0)
|
||||
return 1;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ...and two syscalls. This is x86-specific. */
|
||||
static inline long x86_syscall3(long nr, long a0, long a1, long a2)
|
||||
{
|
||||
long ret;
|
||||
#ifdef __x86_64__
|
||||
asm volatile ("syscall" : "=a" (ret) : "a" (nr),
|
||||
"D" (a0), "S" (a1), "d" (a2) :
|
||||
"cc", "memory", "rcx",
|
||||
"r8", "r9", "r10", "r11" );
|
||||
#else
|
||||
asm volatile ("int $0x80" : "=a" (ret) : "a" (nr),
|
||||
"b" (a0), "c" (a1), "d" (a2) :
|
||||
"cc", "memory" );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline long linux_write(int fd, const void *data, size_t len)
|
||||
{
|
||||
return x86_syscall3(__NR_write, fd, (long)data, (long)len);
|
||||
}
|
||||
|
||||
static inline void linux_exit(int code)
|
||||
{
|
||||
x86_syscall3(__NR_exit, code, 0, 0);
|
||||
}
|
||||
|
||||
void to_base10(char *lastdig, time_t n)
|
||||
{
|
||||
while (n) {
|
||||
*lastdig = (n % 10) + '0';
|
||||
n /= 10;
|
||||
lastdig--;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((externally_visible)) void c_main(void **stack)
|
||||
{
|
||||
/* Parse the stack */
|
||||
long argc = (long)*stack;
|
||||
stack += argc + 2;
|
||||
|
||||
/* Now we're pointing at the environment. Skip it. */
|
||||
while(*stack)
|
||||
stack++;
|
||||
stack++;
|
||||
|
||||
/* Now we're pointing at auxv. Initialize the vDSO parser. */
|
||||
vdso_init_from_auxv((void *)stack);
|
||||
|
||||
/* Find gettimeofday. */
|
||||
typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
|
||||
gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
|
||||
|
||||
if (!gtod)
|
||||
linux_exit(1);
|
||||
|
||||
struct timeval tv;
|
||||
long ret = gtod(&tv, 0);
|
||||
|
||||
if (ret == 0) {
|
||||
char buf[] = "The time is .000000\n";
|
||||
to_base10(buf + 31, tv.tv_sec);
|
||||
to_base10(buf + 38, tv.tv_usec);
|
||||
linux_write(1, buf, sizeof(buf) - 1);
|
||||
} else {
|
||||
linux_exit(ret);
|
||||
}
|
||||
|
||||
linux_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the real entry point. It passes the initial stack into
|
||||
* the C entry point.
|
||||
*/
|
||||
asm (
|
||||
".text\n"
|
||||
".global _start\n"
|
||||
".type _start,@function\n"
|
||||
"_start:\n\t"
|
||||
#ifdef __x86_64__
|
||||
"mov %rsp,%rdi\n\t"
|
||||
"jmp c_main"
|
||||
#else
|
||||
"push %esp\n\t"
|
||||
"call c_main\n\t"
|
||||
"int $3"
|
||||
#endif
|
||||
);
|
52
tools/testing/selftests/vDSO/vdso_test.c
Normal file
52
tools/testing/selftests/vDSO/vdso_test.c
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* vdso_test.c: Sample code to test parse_vdso.c
|
||||
* Copyright (c) 2014 Andy Lutomirski
|
||||
* Subject to the GNU General Public License, version 2
|
||||
*
|
||||
* Compile with:
|
||||
* gcc -std=gnu99 vdso_test.c parse_vdso.c
|
||||
*
|
||||
* Tested on x86, 32-bit and 64-bit. It may work on other architectures, too.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <elf.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
extern void *vdso_sym(const char *version, const char *name);
|
||||
extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
|
||||
extern void vdso_init_from_auxv(void *auxv);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
|
||||
if (!sysinfo_ehdr) {
|
||||
printf("AT_SYSINFO_EHDR is not present!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
|
||||
|
||||
/* Find gettimeofday. */
|
||||
typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
|
||||
gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
|
||||
|
||||
if (!gtod) {
|
||||
printf("Could not find __vdso_gettimeofday\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
long ret = gtod(&tv, 0);
|
||||
|
||||
if (ret == 0) {
|
||||
printf("The time is %lld.%06lld\n",
|
||||
(long long)tv.tv_sec, (long long)tv.tv_usec);
|
||||
} else {
|
||||
printf("__vdso_gettimeofday failed\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
1
tools/testing/selftests/watchdog/.gitignore
vendored
Normal file
1
tools/testing/selftests/watchdog/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
watchdog-test
|
8
tools/testing/selftests/watchdog/Makefile
Normal file
8
tools/testing/selftests/watchdog/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
TEST_PROGS := watchdog-test
|
||||
|
||||
all: $(TEST_PROGS)
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
clean:
|
||||
rm -fr $(TEST_PROGS)
|
105
tools/testing/selftests/watchdog/watchdog-test.c
Normal file
105
tools/testing/selftests/watchdog/watchdog-test.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Watchdog Driver Test Program
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
int fd;
|
||||
const char v = 'V';
|
||||
|
||||
/*
|
||||
* This function simply sends an IOCTL to the driver, which in turn ticks
|
||||
* the PC Watchdog card to reset its internal timer so it doesn't trigger
|
||||
* a computer reset.
|
||||
*/
|
||||
static void keep_alive(void)
|
||||
{
|
||||
int dummy;
|
||||
|
||||
printf(".");
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
}
|
||||
|
||||
/*
|
||||
* The main program. Run the program with "-d" to disable the card,
|
||||
* or "-e" to enable the card.
|
||||
*/
|
||||
|
||||
static void term(int sig)
|
||||
{
|
||||
int ret = write(fd, &v, 1);
|
||||
|
||||
close(fd);
|
||||
if (ret < 0)
|
||||
printf("\nStopping watchdog ticks failed (%d)...\n", errno);
|
||||
else
|
||||
printf("\nStopping watchdog ticks...\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int flags;
|
||||
unsigned int ping_rate = 1;
|
||||
int ret;
|
||||
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
fd = open("/dev/watchdog", O_WRONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
printf("Watchdog device not enabled.\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (argc > 1) {
|
||||
if (!strncasecmp(argv[1], "-d", 2)) {
|
||||
flags = WDIOS_DISABLECARD;
|
||||
ioctl(fd, WDIOC_SETOPTIONS, &flags);
|
||||
printf("Watchdog card disabled.\n");
|
||||
goto end;
|
||||
} else if (!strncasecmp(argv[1], "-e", 2)) {
|
||||
flags = WDIOS_ENABLECARD;
|
||||
ioctl(fd, WDIOC_SETOPTIONS, &flags);
|
||||
printf("Watchdog card enabled.\n");
|
||||
goto end;
|
||||
} else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) {
|
||||
flags = atoi(argv[2]);
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &flags);
|
||||
printf("Watchdog timeout set to %u seconds.\n", flags);
|
||||
goto end;
|
||||
} else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) {
|
||||
ping_rate = strtoul(argv[2], NULL, 0);
|
||||
printf("Watchdog ping rate set to %u seconds.\n", ping_rate);
|
||||
} else {
|
||||
printf("-d to disable, -e to enable, -t <n> to set " \
|
||||
"the timeout,\n-p <n> to set the ping rate, and \n");
|
||||
printf("run by itself to tick the card.\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Watchdog Ticking Away!\n");
|
||||
|
||||
signal(SIGINT, term);
|
||||
|
||||
while(1) {
|
||||
keep_alive();
|
||||
sleep(ping_rate);
|
||||
}
|
||||
end:
|
||||
ret = write(fd, &v, 1);
|
||||
if (ret < 0)
|
||||
printf("Stopping watchdog ticks failed (%d)...\n", errno);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
@@ -13,7 +13,7 @@ Statistics for individual zram devices are exported through sysfs nodes at
|
||||
|
||||
Kconfig required:
|
||||
CONFIG_ZRAM=y
|
||||
CONFIG_ZRAM_LZ4_COMPRESS=y
|
||||
CONFIG_CRYPTO_LZ4=y
|
||||
CONFIG_ZPOOL=y
|
||||
CONFIG_ZSMALLOC=y
|
||||
|
||||
|
Reference in New Issue
Block a user