linux/tools/testing/selftests/powerpc/tm/tm-syscall.c
Sam bobroff b4b56f9eca powerpc/tm: Abort syscalls in active transactions
This patch changes the syscall handler to doom (tabort) active
transactions when a syscall is made and return very early without
performing the syscall and keeping side effects to a minimum (no CPU
accounting or system call tracing is performed). Also included is a
new HWCAP2 bit, PPC_FEATURE2_HTM_NOSC, to indicate this
behaviour to userspace.

Currently, the system call instruction automatically suspends an
active transaction which causes side effects to persist when an active
transaction fails.

This does change the kernel's behaviour, but in a way that was
documented as unsupported.  It doesn't reduce functionality as
syscalls will still be performed after tsuspend; it just requires that
the transaction be explicitly suspended.  It also provides a
consistent interface and makes the behaviour of user code
substantially the same across powerpc and platforms that do not
support suspended transactions (e.g. x86 and s390).

Performance measurements using
http://ozlabs.org/~anton/junkcode/null_syscall.c indicate the cost of
a normal (non-aborted) system call increases by about 0.25%.

Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2015-06-19 17:10:28 +10:00

123 lines
2.7 KiB
C

/*
* 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 <linux/auxvec.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);
}
int tm_syscall(void)
{
unsigned count = 0;
struct timeval end, now;
SKIP_IF(!((long)get_auxv_entry(AT_HWCAP2)
& PPC_FEATURE2_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");
}