aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/powerpc/tm/tm-syscall.c
blob: d7256b79ec4c9a0928339ac49c70a2d4a81b3cc4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
 * Copyright 2015, Sam Bobroff, IBM Corp.
 * Licensed under GPLv2.
 *
 * Test the kernel's system call code to ensure that a system call
 * made from within an active HTM transaction is aborted with the
 * correct failure code.
 * Conversely, ensure that a system call made from within a
 * suspended transaction can succeed.
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <asm/tm.h>
#include <asm/cputable.h>
#include <sys/time.h>
#include <stdlib.h>

#include "utils.h"

extern int getppid_tm_active(void);
extern int getppid_tm_suspended(void);

unsigned retries = 0;

#define TEST_DURATION 10 /* seconds */
#define TM_RETRIES 100

long failure_code(void)
{
	return __builtin_get_texasru() >> 24;
}

bool failure_is_persistent(void)
{
	return (failure_code() & TM_CAUSE_PERSISTENT) == TM_CAUSE_PERSISTENT;
}

bool failure_is_syscall(void)
{
	return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL;
}

pid_t getppid_tm(bool suspend)
{
	int i;
	pid_t pid;

	for (i = 0; i < TM_RETRIES; i++) {
		if (suspend)
			pid = getppid_tm_suspended();
		else
			pid = getppid_tm_active();

		if (pid >= 0)
			return pid;

		if (failure_is_persistent()) {
			if (failure_is_syscall())
				return -1;

			printf("Unexpected persistent transaction failure.\n");
			printf("TEXASR 0x%016lx, TFIAR 0x%016lx.\n",
			       __builtin_get_texasr(), __builtin_get_tfiar());
			exit(-1);
		}

		retries++;
	}

	printf("Exceeded limit of %d temporary transaction failures.\n", TM_RETRIES);
	printf("TEXASR 0x%016lx, TFIAR 0x%016lx.\n",
	       __builtin_get_texasr(), __builtin_get_tfiar());

	exit(-1);
}

static inline bool have_htm_nosc(void)
{
#ifdef PPC_FEATURE2_HTM_NOSC
	return have_hwcap2(PPC_FEATURE2_HTM_NOSC);
#else
	printf("PPC_FEATURE2_HTM_NOSC not defined, can't check AT_HWCAP2\n");
	return false;
#endif
}

int tm_syscall(void)
{
	unsigned count = 0;
	struct timeval end, now;

	SKIP_IF(!have_htm_nosc());

	setbuf(stdout, NULL);

	printf("Testing transactional syscalls for %d seconds...\n", TEST_DURATION);

	gettimeofday(&end, NULL);
	now.tv_sec = TEST_DURATION;
	now.tv_usec = 0;
	timeradd(&end, &now, &end);

	for (count = 0; timercmp(&now, &end, <); count++) {
		/*
		 * Test a syscall within a suspended transaction and verify
		 * that it succeeds.
		 */
		FAIL_IF(getppid_tm(true) == -1); /* Should succeed. */

		/*
		 * Test a syscall within an active transaction and verify that
		 * it fails with the correct failure code.
		 */
		FAIL_IF(getppid_tm(false) != -1);  /* Should fail... */
		FAIL_IF(!failure_is_persistent()); /* ...persistently... */
		FAIL_IF(!failure_is_syscall());    /* ...with code syscall. */
		gettimeofday(&now, 0);
	}

	printf("%d active and suspended transactions behaved correctly.\n", count);
	printf("(There were %d transaction retries.)\n", retries);

	return 0;
}

int main(void)
{
	return test_harness(tm_syscall, "tm_syscall");
}