diff mbox

[3/3] selftests/powerpc: Add transactional syscall test

Message ID b4a4ea23b20098512cf350b97ce98e0d1ea58531.1426740212.git.sam.bobroff@au1.ibm.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

Sam Bobroff March 19, 2015, 4:43 a.m. UTC
Check that a syscall made during an active transaction will fail with
the correct failure code and that one made during a suspended
transaction will succeed.

Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>
---
 tools/testing/selftests/powerpc/tm/Makefile     |    3 +-
 tools/testing/selftests/powerpc/tm/tm-syscall.c |  113 +++++++++++++++++++++++
 2 files changed, 115 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/powerpc/tm/tm-syscall.c

Comments

Anshuman Khandual March 20, 2015, 9:25 a.m. UTC | #1
On 03/19/2015 10:13 AM, Sam Bobroff wrote:
> Check that a syscall made during an active transaction will fail with
> the correct failure code and that one made during a suspended
> transaction will succeed.
> 
> Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>

The test works.

> +
> +int tm_syscall(void)
> +{
> +	SKIP_IF(!((long)get_auxv_entry(AT_HWCAP2) & PPC_FEATURE2_HTM));
> +	setbuf(stdout, 0);
> +	FAIL_IF(!t_active_getppid_test());
> +	printf("%d active transactions correctly aborted.\n", TM_TEST_RUNS);
> +	FAIL_IF(!t_suspended_getppid_test());
> +	printf("%d suspended transactions succeeded.\n", TM_TEST_RUNS);
> +	return 0;
> +}
> +
> +int main(void)
> +{
> +	return test_harness(tm_syscall, "tm_syscall");
> +}
> +

There is an extra blank line at the end of this file. Interchanging return
codes of 0 and 1 for various functions make it very confusing along with
negative FAIL_IF checks in the primary test function. Control flow structures
like these can use some in-code documentation for readability.

+	for (i = 0; i < TM_RETRIES; i++) {
+		if (__builtin_tbegin(0)) {
+			getppid();
+			__builtin_tend(0);
+			return 1;
+		}
+		if (t_failure_persistent())
+			return 0;

or

+		if (__builtin_tbegin(0)) {
+			__builtin_tsuspend();
+			getppid();
+			__builtin_tresume();
+			__builtin_tend(0);
+			return 1;
+		}
+		if (t_failure_persistent())
+			return 0;
Sam Bobroff March 24, 2015, 1:52 a.m. UTC | #2
On 20/03/15 20:25, Anshuman Khandual wrote:
> On 03/19/2015 10:13 AM, Sam Bobroff wrote:
>> Check that a syscall made during an active transaction will fail with
>> the correct failure code and that one made during a suspended
>> transaction will succeed.
>>
>> Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>
> 
> The test works.

Great :-)

>> +
>> +int tm_syscall(void)
>> +{
>> +	SKIP_IF(!((long)get_auxv_entry(AT_HWCAP2) & PPC_FEATURE2_HTM));
>> +	setbuf(stdout, 0);
>> +	FAIL_IF(!t_active_getppid_test());
>> +	printf("%d active transactions correctly aborted.\n", TM_TEST_RUNS);
>> +	FAIL_IF(!t_suspended_getppid_test());
>> +	printf("%d suspended transactions succeeded.\n", TM_TEST_RUNS);
>> +	return 0;
>> +}
>> +
>> +int main(void)
>> +{
>> +	return test_harness(tm_syscall, "tm_syscall");
>> +}
>> +
> 
> There is an extra blank line at the end of this file. Interchanging return
> codes of 0 and 1 for various functions make it very confusing along with
> negative FAIL_IF checks in the primary test function. Control flow structures
> like these can use some in-code documentation for readability.
> 
> +	for (i = 0; i < TM_RETRIES; i++) {
> +		if (__builtin_tbegin(0)) {
> +			getppid();
> +			__builtin_tend(0);
> +			return 1;
> +		}
> +		if (t_failure_persistent())
> +			return 0;
> 
> or
> 
> +		if (__builtin_tbegin(0)) {
> +			__builtin_tsuspend();
> +			getppid();
> +			__builtin_tresume();
> +			__builtin_tend(0);
> +			return 1;
> +		}
> +		if (t_failure_persistent())
> +			return 0;
> 

Good points. I'll remove the blank line and comment the code.

I'm not sure I can do any better with the FAIL_IF() macro: I wanted it
to read "fail if the test failed", but I can see what you mean about a
double negative. Maybe it would be better to introduce a different
macro, more like a standard assert: TEST(XXX) which fails if XXX is
false. However, I think "TEST" would be too generic a name and I'm not
should what would be better. Any comments/suggestions?

Thanks for the review!

Cheers,
Sam.
Michael Ellerman March 24, 2015, 2:02 a.m. UTC | #3
On Tue, 2015-03-24 at 12:52 +1100, Sam Bobroff wrote:
> On 20/03/15 20:25, Anshuman Khandual wrote:
> > On 03/19/2015 10:13 AM, Sam Bobroff wrote:
> >> Check that a syscall made during an active transaction will fail with
> >> the correct failure code and that one made during a suspended
> >> transaction will succeed.
> >>
> >> Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>
> > 
> > The test works.
> 
> Great :-)
> 
> >> +
> >> +int tm_syscall(void)
> >> +{
> >> +	SKIP_IF(!((long)get_auxv_entry(AT_HWCAP2) & PPC_FEATURE2_HTM));
> >> +	setbuf(stdout, 0);
> >> +	FAIL_IF(!t_active_getppid_test());
> >> +	printf("%d active transactions correctly aborted.\n", TM_TEST_RUNS);
> >> +	FAIL_IF(!t_suspended_getppid_test());
> >> +	printf("%d suspended transactions succeeded.\n", TM_TEST_RUNS);
> >> +	return 0;
> >> +}
> >> +
> >> +int main(void)
> >> +{
> >> +	return test_harness(tm_syscall, "tm_syscall");
> >> +}
> >> +
> > 
> > There is an extra blank line at the end of this file. Interchanging return
> > codes of 0 and 1 for various functions make it very confusing along with
> > negative FAIL_IF checks in the primary test function. Control flow structures
> > like these can use some in-code documentation for readability.
> > 
> > +	for (i = 0; i < TM_RETRIES; i++) {
> > +		if (__builtin_tbegin(0)) {
> > +			getppid();
> > +			__builtin_tend(0);
> > +			return 1;
> > +		}
> > +		if (t_failure_persistent())
> > +			return 0;
> > 
> > or
> > 
> > +		if (__builtin_tbegin(0)) {
> > +			__builtin_tsuspend();
> > +			getppid();
> > +			__builtin_tresume();
> > +			__builtin_tend(0);
> > +			return 1;
> > +		}
> > +		if (t_failure_persistent())
> > +			return 0;
> > 
> 
> Good points. I'll remove the blank line and comment the code.
> 
> I'm not sure I can do any better with the FAIL_IF() macro: I wanted it
> to read "fail if the test failed", but I can see what you mean about a
> double negative. Maybe it would be better to introduce a different
> macro, more like a standard assert: TEST(XXX) which fails if XXX is
> false. However, I think "TEST" would be too generic a name and I'm not
> should what would be better. Any comments/suggestions?

FAIL_IF() is designed for things that return 0 for OK and !0 for failure. Like
most things in C.

So I think it would be improved if you inverted your return codes in your test
routines.

Even better to return ESOMETHING in the error cases, and zero otherwise.

cheers
Sam Bobroff March 30, 2015, 12:06 a.m. UTC | #4
On 24/03/15 13:02, Michael Ellerman wrote:
> On Tue, 2015-03-24 at 12:52 +1100, Sam Bobroff wrote:
>> On 20/03/15 20:25, Anshuman Khandual wrote:
>>> On 03/19/2015 10:13 AM, Sam Bobroff wrote:
>>>> Check that a syscall made during an active transaction will fail with
>>>> the correct failure code and that one made during a suspended
>>>> transaction will succeed.
>>>>
>>>> Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>
>>>
>>> The test works.
>>
>> Great :-)
>>
>>>> +
>>>> +int tm_syscall(void)
>>>> +{
>>>> +	SKIP_IF(!((long)get_auxv_entry(AT_HWCAP2) & PPC_FEATURE2_HTM));
>>>> +	setbuf(stdout, 0);
>>>> +	FAIL_IF(!t_active_getppid_test());
>>>> +	printf("%d active transactions correctly aborted.\n", TM_TEST_RUNS);
>>>> +	FAIL_IF(!t_suspended_getppid_test());
>>>> +	printf("%d suspended transactions succeeded.\n", TM_TEST_RUNS);
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +int main(void)
>>>> +{
>>>> +	return test_harness(tm_syscall, "tm_syscall");
>>>> +}
>>>> +
>>>
>>> There is an extra blank line at the end of this file. Interchanging return
>>> codes of 0 and 1 for various functions make it very confusing along with
>>> negative FAIL_IF checks in the primary test function. Control flow structures
>>> like these can use some in-code documentation for readability.
>>>
>>> +	for (i = 0; i < TM_RETRIES; i++) {
>>> +		if (__builtin_tbegin(0)) {
>>> +			getppid();
>>> +			__builtin_tend(0);
>>> +			return 1;
>>> +		}
>>> +		if (t_failure_persistent())
>>> +			return 0;
>>>
>>> or
>>>
>>> +		if (__builtin_tbegin(0)) {
>>> +			__builtin_tsuspend();
>>> +			getppid();
>>> +			__builtin_tresume();
>>> +			__builtin_tend(0);
>>> +			return 1;
>>> +		}
>>> +		if (t_failure_persistent())
>>> +			return 0;
>>>
>>
>> Good points. I'll remove the blank line and comment the code.
>>
>> I'm not sure I can do any better with the FAIL_IF() macro: I wanted it
>> to read "fail if the test failed", but I can see what you mean about a
>> double negative. Maybe it would be better to introduce a different
>> macro, more like a standard assert: TEST(XXX) which fails if XXX is
>> false. However, I think "TEST" would be too generic a name and I'm not
>> should what would be better. Any comments/suggestions?
> 
> FAIL_IF() is designed for things that return 0 for OK and !0 for failure. Like
> most things in C.
> 
> So I think it would be improved if you inverted your return codes in your test
> routines.
> 
> Even better to return ESOMETHING in the error cases, and zero otherwise.
> 
> cheers

Fair enough. I think the *_test() functions I added for "clarity" were
just making it more confusing, so I've dropped them.

Moving the code around, even a little, has also exposed the fact that
transactions are very sensitive to how the code is compiled so I'm going
to move the transaction blocks out into a separate assembly file where I
can control exactly what instructions get used. This will also mean that
it's no longer dependent on using linker magic (or some other trick) to
avoid lazy symbol loading.

I'll repost the series.

Thanks for the review!

Cheers,
Sam.
diff mbox

Patch

diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
index 2cede23..d8dab0d 100644
--- a/tools/testing/selftests/powerpc/tm/Makefile
+++ b/tools/testing/selftests/powerpc/tm/Makefile
@@ -1,4 +1,5 @@ 
-PROGS := tm-resched-dscr
+PROGS := tm-resched-dscr tm-syscall
+CFLAGS:=$(CFLAGS) -mhtm -Wl,-z,now
 
 all: $(PROGS)
 
diff --git a/tools/testing/selftests/powerpc/tm/tm-syscall.c b/tools/testing/selftests/powerpc/tm/tm-syscall.c
new file mode 100644
index 0000000..7c60e53
--- /dev/null
+++ b/tools/testing/selftests/powerpc/tm/tm-syscall.c
@@ -0,0 +1,113 @@ 
+/* 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.
+ *
+ * It is important to compile with -Wl,-z,now to prevent
+ * lazy symbol resolution from affecting the results.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <asm/tm.h>
+#include <asm/cputable.h>
+#include <linux/auxvec.h>
+
+#include "utils.h"
+
+#define TM_RETRIES 10
+#define TM_TEST_RUNS 1000
+
+int t_failure_persistent(void)
+{
+	long texasr = __builtin_get_texasr();
+	long failure_code = (texasr >> 56) & 0xff;
+
+	return failure_code & TM_CAUSE_PERSISTENT;
+}
+
+int t_failure_code_syscall(void)
+{
+	long texasr = __builtin_get_texasr();
+	long failure_code = (texasr >> 56) & 0xff;
+
+	return (failure_code & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL;
+}
+
+int t_active_getppid(void)
+{
+	int i;
+
+	for (i = 0; i < TM_RETRIES; i++) {
+		if (__builtin_tbegin(0)) {
+			getppid();
+			__builtin_tend(0);
+			return 1;
+		}
+		if (t_failure_persistent())
+			return 0;
+	}
+	return 0;
+}
+
+int t_active_getppid_test(void)
+{
+	int i;
+
+	for (i = 0; i < TM_TEST_RUNS; i++) {
+		if (t_active_getppid())
+			return 0;
+		if (!t_failure_persistent())
+			return 0;
+		if (!t_failure_code_syscall())
+			return 0;
+	}
+	return 1;
+}
+
+int t_suspended_getppid(void)
+{
+	int i;
+
+	for (i = 0; i < TM_RETRIES; i++) {
+		if (__builtin_tbegin(0)) {
+			__builtin_tsuspend();
+			getppid();
+			__builtin_tresume();
+			__builtin_tend(0);
+			return 1;
+		}
+		if (t_failure_persistent())
+			return 0;
+	}
+	return 0;
+}
+
+int t_suspended_getppid_test(void)
+{
+	int i;
+
+	for (i = 0; i < TM_TEST_RUNS; i++) {
+		if (!t_suspended_getppid())
+			return 0;
+	}
+	return 1;
+}
+
+int tm_syscall(void)
+{
+	SKIP_IF(!((long)get_auxv_entry(AT_HWCAP2) & PPC_FEATURE2_HTM));
+	setbuf(stdout, 0);
+	FAIL_IF(!t_active_getppid_test());
+	printf("%d active transactions correctly aborted.\n", TM_TEST_RUNS);
+	FAIL_IF(!t_suspended_getppid_test());
+	printf("%d suspended transactions succeeded.\n", TM_TEST_RUNS);
+	return 0;
+}
+
+int main(void)
+{
+	return test_harness(tm_syscall, "tm_syscall");
+}
+