1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 static const stress_help_t help[] = {
28 	{ NULL,	"nop N",		"start N workers that burn cycles with no-ops" },
29 	{ NULL,	"nop-ops N",		"stop after N nop bogo no-op operations" },
30 	{ NULL, "nop-instr INSTR",	"specify nop instruction to use" },
31 	{ NULL,	NULL,		NULL }
32 };
33 
34 #if defined(HAVE_ASM_NOP)
35 
36 static sigjmp_buf jmpbuf;
37 
38 typedef struct {
39 	const char *name;
40 	void (*func)(const stress_args_t *args);
41 } stress_nop_instr_t;
42 
43 #define OPx1(op)	op();
44 #define OPx4(op)	OPx1(op) OPx1(op) OPx1(op) OPx1(op)
45 #define OPx16(op)	OPx4(op) OPx4(op) OPx4(op) OPx4(op)
46 #define OPx64(op)	do { OPx16(op) OPx16(op) OPx16(op) OPx16(op) } while (0)
47 
48 #define STRESS_NOP_SPIN_OP(name, op)		\
49 static void stress_nop_spin_ ## name(const stress_args_t *args)	\
50 {						\
51 	do {					\
52 		register int i = 1024;		\
53 						\
54 		while (i--)			\
55 			OPx64(op); 		\
56 						\
57 		inc_counter(args);		\
58 	} while (keep_stressing(args));		\
59 }
60 
stress_op_nop(void)61 static inline void stress_op_nop(void)
62 {
63 #if defined(STRESS_ARCH_KVX)
64 	/*
65 	 * Extra ;; required for KVX to indicate end of
66 	 * a VLIW instruction bundle
67 	 */
68 	__asm__ __volatile__("nop;;\n");
69 #else
70 	__asm__ __volatile__("nop;\n");
71 #endif
72 }
73 
STRESS_NOP_SPIN_OP(nop,stress_op_nop)74 STRESS_NOP_SPIN_OP(nop, stress_op_nop)
75 
76 #if defined(HAVE_ASM_X86_PAUSE)
77 static inline void stress_op_x86_pause(void)
78 {
79 	__asm__ __volatile__("pause;\n" ::: "memory");
80 }
81 
82 STRESS_NOP_SPIN_OP(x86_pause, stress_op_x86_pause);
83 #endif
84 
85 #if defined(HAVE_ASM_ARM_YIELD)
stress_op_arm_yield(void)86 static inline void stress_op_arm_yield(void)
87 {
88 	__asm__ __volatile__("yield;\n");
89 }
90 
91 STRESS_NOP_SPIN_OP(arm_yield, stress_op_arm_yield);
92 #endif
93 
94 #if defined(STRESS_ARCH_X86)
stress_op_x86_nop2(void)95 static inline void stress_op_x86_nop2(void)
96 {
97 	__asm__ __volatile__(".byte 0x66, 0x90;\n");
98 }
99 
stress_op_x86_nop3(void)100 static inline void stress_op_x86_nop3(void)
101 {
102 	__asm__ __volatile__(".byte 0x0f, 0x1f, 0x00;\n");
103 }
104 
stress_op_x86_nop4(void)105 static inline void stress_op_x86_nop4(void)
106 {
107 	__asm__ __volatile__(".byte 0x0f, 0x1f, 0x40, 0x00;\n");
108 }
109 
stress_op_x86_nop5(void)110 static inline void stress_op_x86_nop5(void)
111 {
112 	__asm__ __volatile__(".byte 0x0f, 0x1f, 0x44, 0x00, 0x00;\n");
113 }
114 
stress_op_x86_nop6(void)115 static inline void stress_op_x86_nop6(void)
116 {
117 	__asm__ __volatile__(".byte 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00;\n");
118 }
119 
stress_op_x86_nop7(void)120 static inline void stress_op_x86_nop7(void)
121 {
122 	__asm__ __volatile__(".byte 0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00;\n");
123 }
124 
stress_op_x86_nop8(void)125 static inline void stress_op_x86_nop8(void)
126 {
127 	__asm__ __volatile__(".byte 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00;\n");
128 }
129 
stress_op_x86_nop9(void)130 static inline void stress_op_x86_nop9(void)
131 {
132 	__asm__ __volatile__(".byte 0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00;\n");
133 }
134 
stress_op_x86_nop10(void)135 static inline void stress_op_x86_nop10(void)
136 {
137 	__asm__ __volatile__(".byte 0x66, 0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00;\n");
138 }
139 
stress_op_x86_nop11(void)140 static inline void stress_op_x86_nop11(void)
141 {
142 	__asm__ __volatile__(".byte 0x66, 0x66, 0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00;\n");
143 }
144 
145 STRESS_NOP_SPIN_OP(x86_nop2, stress_op_x86_nop2);
146 STRESS_NOP_SPIN_OP(x86_nop3, stress_op_x86_nop3);
147 STRESS_NOP_SPIN_OP(x86_nop4, stress_op_x86_nop4);
148 STRESS_NOP_SPIN_OP(x86_nop5, stress_op_x86_nop5);
149 STRESS_NOP_SPIN_OP(x86_nop6, stress_op_x86_nop6);
150 STRESS_NOP_SPIN_OP(x86_nop7, stress_op_x86_nop7);
151 STRESS_NOP_SPIN_OP(x86_nop8, stress_op_x86_nop8);
152 STRESS_NOP_SPIN_OP(x86_nop9, stress_op_x86_nop9);
153 STRESS_NOP_SPIN_OP(x86_nop10, stress_op_x86_nop10);
154 STRESS_NOP_SPIN_OP(x86_nop11, stress_op_x86_nop11);
155 #endif
156 
157 stress_nop_instr_t nop_instr[] = {
158 	{ "nop",	stress_nop_spin_nop },
159 #if defined(STRESS_ARCH_X86)
160 	{ "nop2",	stress_nop_spin_x86_nop2 },
161 	{ "nop3",	stress_nop_spin_x86_nop3 },
162 	{ "nop4",	stress_nop_spin_x86_nop4 },
163 	{ "nop5",	stress_nop_spin_x86_nop5 },
164 	{ "nop6",	stress_nop_spin_x86_nop6 },
165 	{ "nop7",	stress_nop_spin_x86_nop7 },
166 	{ "nop8",	stress_nop_spin_x86_nop8 },
167 	{ "nop9",	stress_nop_spin_x86_nop9 },
168 	{ "nop10",	stress_nop_spin_x86_nop10 },
169 	{ "nop11",	stress_nop_spin_x86_nop11 },
170 #endif
171 #if defined(HAVE_ASM_X86_PAUSE)
172 	{ "pause",	stress_nop_spin_x86_pause },
173 #endif
174 #if defined(HAVE_ASM_ARM_YIELD)
175 	{ "yield",	stress_nop_spin_arm_yield },
176 #endif
177 	{ NULL,		NULL },
178 };
179 
stress_set_nop_instr(const char * opt)180 static int stress_set_nop_instr(const char *opt)
181 {
182 	stress_nop_instr_t const *instr;
183 
184 	for (instr = nop_instr; instr->func; instr++) {
185 		if (!strcmp(instr->name, opt)) {
186 			stress_set_setting("nop-instr", TYPE_ID_UINTPTR_T, &instr);
187 			return 0;
188 		}
189 	}
190 
191 	(void)fprintf(stderr, "nop-instr must be one of:");
192 	for (instr = nop_instr; instr->func; instr++) {
193 		(void)fprintf(stderr, " %s", instr->name);
194 	}
195 	(void)fprintf(stderr, "\n");
196 
197 	return -1;
198 }
199 
stress_sigill_nop_handler(int signum)200 static void stress_sigill_nop_handler(int signum)
201 {
202 	(void)signum;
203 
204 	siglongjmp(jmpbuf, 1);
205 }
206 
207 /*
208  *  stress_nop()
209  *	stress that does lots of not a lot
210  */
stress_nop(const stress_args_t * args)211 static int stress_nop(const stress_args_t *args)
212 {
213 	stress_nop_instr_t const *instr = &nop_instr[0];
214 
215 	(void)stress_get_setting("nop-instr", &instr);
216 
217 	if (stress_sighandler(args->name, SIGILL, stress_sigill_nop_handler, NULL) < 0)
218 		return EXIT_NO_RESOURCE;
219 
220 	if (sigsetjmp(jmpbuf, 1) != 0) {
221 		/* We reach here on an SIGILL trap */
222 		if (instr != &nop_instr[0]) {
223 			pr_inf("%s '%s' instruction was illegal, falling back to nop\n",
224 				args->name, instr->name);
225 			instr = &nop_instr[0];
226 		} else {
227 			/* Really should be able to do nop, skip */
228 			pr_inf("%s 'nop' instruction was illegal, skipping stressor\n",
229 				args->name);
230 			return EXIT_NO_RESOURCE;
231 		}
232 	}
233 
234 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
235 	instr->func(args);
236 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
237 
238 	return EXIT_SUCCESS;
239 }
240 
241 static const stress_opt_set_func_t opt_set_funcs[] = {
242 	{ OPT_nop_instr,	stress_set_nop_instr },
243 	{ 0,                    NULL }
244 };
245 
246 stressor_info_t stress_nop_info = {
247 	.stressor = stress_nop,
248 	.class = CLASS_CPU,
249 	.opt_set_funcs = opt_set_funcs,
250 	.help = help
251 };
252 #else
253 stressor_info_t stress_nop_info = {
254 	.stressor = stress_not_implemented,
255 	.class = CLASS_CPU,
256 	.help = help
257 };
258 #endif
259