1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  * Copyright (C)      2021 Colin Ian King.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20 #include "stress-ng.h"
21 
22 static const stress_help_t help[] = {
23 	{ NULL,	"tsc N",	"start N workers reading the time stamp counter" },
24 	{ NULL,	"tsc-ops N",	"stop after N TSC bogo operations" },
25 	{ NULL,	NULL,		NULL }
26 };
27 
28 
29 #if defined(STRESS_ARCH_RISCV) &&	\
30     defined(SIGILL)
31 
32 #define HAVE_STRESS_TSC_CAPABILITY
33 
34 static sigjmp_buf jmpbuf;
35 static bool tsc_supported = false;
36 
rdtsc(void)37 static inline unsigned long rdtsc(void)
38 {
39 	register unsigned long ticks;
40 
41         __asm__ __volatile__("rdtime %0"
42                               : "=r" (ticks)
43 			      :
44                               : "memory");
45 	return ticks;
46 }
47 
stress_sigill_handler(int signum)48 static void stress_sigill_handler(int signum)
49 {
50 	(void)signum;
51 
52 	siglongjmp(jmpbuf, 1);
53 }
54 
55 /*
56  *  stress_tsc_supported()
57  *	check if tsc is supported, riscv variant
58  */
stress_tsc_supported(const char * name)59 static int stress_tsc_supported(const char *name)
60 {
61 	unsigned long cycles;
62 
63 	if (stress_sighandler(name, SIGILL, stress_sigill_handler, NULL) < 0)
64 		return -1;
65 
66 	/*
67 	 *  We get here with non-zero return if SIGILL occurs
68 	 */
69 	if (sigsetjmp(jmpbuf, 1) != 0) {
70 		pr_inf_skip("%s stressor will be skipped, "
71 			"rdcycle not allowed\n", name);
72 		return -1;
73 	}
74 
75 	cycles = rdtsc();
76 	(void)cycles;
77 	tsc_supported = true;
78 
79 	return 0;
80 }
81 #endif
82 
83 #if defined(STRESS_ARCH_X86)
84 
85 #define HAVE_STRESS_TSC_CAPABILITY
86 
87 static bool tsc_supported = false;
88 
89 /*
90  *  stress_tsc_supported()
91  *	check if tsc is supported, x86 variant
92  */
stress_tsc_supported(const char * name)93 static int stress_tsc_supported(const char *name)
94 {
95 	/* Intel CPU? */
96 	if (!stress_cpu_is_x86()) {
97 		pr_inf_skip("%s stressor will be skipped, "
98 			"not a recognised Intel CPU\n", name);
99 		return -1;
100 	}
101 	/* ..and supports tsc? */
102 	if (!stress_cpu_x86_has_tsc()) {
103 		pr_inf_skip("%s stressor will be skipped, CPU "
104 			"does not support the tsc instruction\n", name);
105 		return -1;
106 	}
107 	tsc_supported = true;
108 	return 0;
109 }
110 
111 /*
112  *  read tsc
113  */
rdtsc(void)114 static inline void rdtsc(void)
115 {
116 #if STRESS_TSC_SERIALIZED
117 	asm volatile("cpuid\nrdtsc\n" : : : "%edx", "%eax");
118 #else
119 	asm volatile("rdtsc\n" : : : "%edx", "%eax");
120 #endif
121 }
122 
123 #elif defined(STRESS_ARCH_PPC64) &&		\
124       defined(HAVE_SYS_PLATFORM_PPC_H) &&	\
125       defined(HAVE_PPC_GET_TIMEBASE)
126 
127 #define HAVE_STRESS_TSC_CAPABILITY
128 
129 static bool tsc_supported = true;
130 
131 /*
132  *  stress_tsc_supported()
133  *	check if tsc is supported, ppc variant
134  */
stress_tsc_supported(const char * name)135 static int stress_tsc_supported(const char *name)
136 {
137 	(void)name;
138 
139 	return 0;
140 }
141 
rdtsc(void)142 static inline void rdtsc(void)
143 {
144 	(void)__ppc_get_timebase();
145 }
146 
147 #elif defined(STRESS_ARCH_S390)
148 
149 #define HAVE_STRESS_TSC_CAPABILITY
150 
151 static bool tsc_supported = true;
152 
153 /*
154  *  stress_tsc_supported()
155  *	check if tsc is supported, s390x variant
156  */
stress_tsc_supported(const char * name)157 static int stress_tsc_supported(const char *name)
158 {
159 	(void)name;
160 
161 	return 0;
162 }
163 
rdtsc(void)164 static inline void rdtsc(void)
165 {
166 	uint64_t tick;
167 
168 	asm("\tstck\t%0\n" : "=Q" (tick) : : "cc");
169 }
170 #endif
171 
172 #if defined(HAVE_STRESS_TSC_CAPABILITY)
173 /*
174  *  Unrolled 32 times
175  */
176 #define TSCx32()	\
177 {			\
178 	rdtsc();	\
179 	rdtsc();	\
180 	rdtsc();	\
181 	rdtsc();	\
182 	rdtsc();	\
183 	rdtsc();	\
184 	rdtsc();	\
185 	rdtsc();	\
186 	rdtsc();	\
187 	rdtsc();	\
188 	rdtsc();	\
189 	rdtsc();	\
190 	rdtsc();	\
191 	rdtsc();	\
192 	rdtsc();	\
193 	rdtsc();	\
194 	rdtsc();	\
195 	rdtsc();	\
196 	rdtsc();	\
197 	rdtsc();	\
198 	rdtsc();	\
199 	rdtsc();	\
200 	rdtsc();	\
201 	rdtsc();	\
202 	rdtsc();	\
203 	rdtsc();	\
204 	rdtsc();	\
205 	rdtsc();	\
206 	rdtsc();	\
207 	rdtsc();	\
208 	rdtsc();	\
209 	rdtsc();	\
210 }
211 
212 /*
213  *  stress_tsc()
214  *      stress Intel tsc instruction
215  */
stress_tsc(const stress_args_t * args)216 static int stress_tsc(const stress_args_t *args)
217 {
218 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
219 
220 	if (tsc_supported) {
221 		do {
222 			TSCx32();
223 			TSCx32();
224 			TSCx32();
225 			TSCx32();
226 			inc_counter(args);
227 		} while (keep_stressing(args));
228 	}
229 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
230 
231 	return EXIT_SUCCESS;
232 }
233 
234 stressor_info_t stress_tsc_info = {
235 	.stressor = stress_tsc,
236 	.supported = stress_tsc_supported,
237 	.class = CLASS_CPU,
238 	.help = help
239 };
240 #else
241 
stress_tsc_supported(const char * name)242 static int stress_tsc_supported(const char *name)
243 {
244 	pr_inf_skip("%s stressor will be skipped, CPU "
245 		"does not support the rdtsc instruction.\n", name);
246 	return -1;
247 }
248 
249 stressor_info_t stress_tsc_info = {
250 	.stressor = stress_not_implemented,
251 	.supported = stress_tsc_supported,
252 	.class = CLASS_CPU,
253 	.help = help
254 };
255 #endif
256