1// SPDX-License-Identifier: GPL-2.0+
2
3/*
4 * Copyright 2018 IBM Corporation.
5 */
6
7#define __SANE_USERSPACE_TYPES__
8
9#include <sys/types.h>
10#include <stdint.h>
11#include <malloc.h>
12#include <unistd.h>
13#include <signal.h>
14#include <stdlib.h>
15#include <string.h>
16#include <stdio.h>
17#include "utils.h"
18#include "flush_utils.h"
19
20int entry_flush_test(void)
21{
22	char *p;
23	int repetitions = 10;
24	int fd, passes = 0, iter, rc = 0;
25	struct perf_event_read v;
26	__u64 l1d_misses_total = 0;
27	unsigned long iterations = 100000, zero_size = 24 * 1024;
28	unsigned long l1d_misses_expected;
29	int rfi_flush_orig;
30	int entry_flush, entry_flush_orig;
31
32	SKIP_IF(geteuid() != 0);
33
34	// The PMU event we use only works on Power7 or later
35	SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
36
37	if (read_debugfs_int("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
38		perror("Unable to read powerpc/rfi_flush debugfs file");
39		SKIP_IF(1);
40	}
41
42	if (read_debugfs_int("powerpc/entry_flush", &entry_flush_orig) < 0) {
43		perror("Unable to read powerpc/entry_flush debugfs file");
44		SKIP_IF(1);
45	}
46
47	if (rfi_flush_orig != 0) {
48		if (write_debugfs_int("powerpc/rfi_flush", 0) < 0) {
49			perror("error writing to powerpc/rfi_flush debugfs file");
50			FAIL_IF(1);
51		}
52	}
53
54	entry_flush = entry_flush_orig;
55
56	fd = perf_event_open_counter(PERF_TYPE_HW_CACHE, PERF_L1D_READ_MISS_CONFIG, -1);
57	FAIL_IF(fd < 0);
58
59	p = (char *)memalign(zero_size, CACHELINE_SIZE);
60
61	FAIL_IF(perf_event_enable(fd));
62
63	// disable L1 prefetching
64	set_dscr(1);
65
66	iter = repetitions;
67
68	/*
69	 * We expect to see l1d miss for each cacheline access when entry_flush
70	 * is set. Allow a small variation on this.
71	 */
72	l1d_misses_expected = iterations * (zero_size / CACHELINE_SIZE - 2);
73
74again:
75	FAIL_IF(perf_event_reset(fd));
76
77	syscall_loop(p, iterations, zero_size);
78
79	FAIL_IF(read(fd, &v, sizeof(v)) != sizeof(v));
80
81	if (entry_flush && v.l1d_misses >= l1d_misses_expected)
82		passes++;
83	else if (!entry_flush && v.l1d_misses < (l1d_misses_expected / 2))
84		passes++;
85
86	l1d_misses_total += v.l1d_misses;
87
88	while (--iter)
89		goto again;
90
91	if (passes < repetitions) {
92		printf("FAIL (L1D misses with entry_flush=%d: %llu %c %lu) [%d/%d failures]\n",
93		       entry_flush, l1d_misses_total, entry_flush ? '<' : '>',
94		       entry_flush ? repetitions * l1d_misses_expected :
95		       repetitions * l1d_misses_expected / 2,
96		       repetitions - passes, repetitions);
97		rc = 1;
98	} else {
99		printf("PASS (L1D misses with entry_flush=%d: %llu %c %lu) [%d/%d pass]\n",
100		       entry_flush, l1d_misses_total, entry_flush ? '>' : '<',
101		       entry_flush ? repetitions * l1d_misses_expected :
102		       repetitions * l1d_misses_expected / 2,
103		       passes, repetitions);
104	}
105
106	if (entry_flush == entry_flush_orig) {
107		entry_flush = !entry_flush_orig;
108		if (write_debugfs_int("powerpc/entry_flush", entry_flush) < 0) {
109			perror("error writing to powerpc/entry_flush debugfs file");
110			return 1;
111		}
112		iter = repetitions;
113		l1d_misses_total = 0;
114		passes = 0;
115		goto again;
116	}
117
118	perf_event_disable(fd);
119	close(fd);
120
121	set_dscr(0);
122
123	if (write_debugfs_int("powerpc/rfi_flush", rfi_flush_orig) < 0) {
124		perror("unable to restore original value of powerpc/rfi_flush debugfs file");
125		return 1;
126	}
127
128	if (write_debugfs_int("powerpc/entry_flush", entry_flush_orig) < 0) {
129		perror("unable to restore original value of powerpc/entry_flush debugfs file");
130		return 1;
131	}
132
133	return rc;
134}
135
136int main(int argc, char *argv[])
137{
138	return test_harness(entry_flush_test, "entry_flush_test");
139}
140