aboutsummaryrefslogtreecommitdiff
path: root/tftf/tests/runtime_services/standard_service/psci/system_tests/test_psci_system_suspend_stress.c
blob: 5b500beb74a9ac8f92dd3ccdcc14a7d8192573f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/*
 * Copyright (c) 2018, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch.h>
#include <arch_helpers.h>
#include <arm_gic.h>
#include <assert.h>
#include <debug.h>
#include <events.h>
#include <gic_v2.h>
#include <irq.h>
#include <plat_topology.h>
#include <platform.h>
#include <platform_def.h>
#include <power_management.h>
#include <psci.h>
#include <sgi.h>
#include <stdlib.h>
#include <test_helpers.h>
#include <tftf.h>
#include <tftf_lib.h>
#include <timer.h>

#define MAX_TEST_ITERATIONS (100 * PLATFORM_CORE_COUNT)

/* Number of iterations of the test */
static int iteration_count;

/* The CPU assigned the baton to drive the test */
static u_register_t baton_cpu;

/* Synchronization event which will be waited on by all the non-baton CPUs */
static event_t sync_event;

/* Global variables to synchronize participating CPUs on wake-up */
static spinlock_t cpu_count_lock;
static volatile int cpu_count;
static volatile int participating_cpu_count;

/* Variable to store the system suspend power state and its statistics */
static int system_susp_pwr_state;
static u_register_t susp_count;

static test_result_t do_sys_susp_on_off_stress(void);

/*
 * Helper function to wait for participating CPUs participating to enter the
 * test function.
 */
static void wait_for_cpus_to_enter_test(void)
{
	assert(participating_cpu_count <= PLATFORM_CORE_COUNT);
	while (cpu_count != participating_cpu_count)
		;
}

/* Helper function to increment the cpu_count */
static void inc_cpu_count(void)
{
	spin_lock(&cpu_count_lock);
	cpu_count++;
	spin_unlock(&cpu_count_lock);
	assert(cpu_count <= PLATFORM_CORE_COUNT);
}

/* Helper function to decrement the cpu_count */
static void dec_cpu_count(void)
{
	spin_lock(&cpu_count_lock);
	cpu_count--;
	spin_unlock(&cpu_count_lock);
	assert(cpu_count >= 0);
}

/* Helper function to turn ON all the CPUs in the platform */
static int try_cpu_on_all(void)
{
	int ret, cpu_node;
	u_register_t cpu_mpid, current_cpu = read_mpidr_el1() & MPID_MASK;

	/* Try to turn on all the non-lead CPUs */
	for_each_cpu(cpu_node) {
		cpu_mpid = tftf_get_mpidr_from_node(cpu_node);

		/* Skip lead CPU, it is already powered on */
		if (cpu_mpid == current_cpu)
			continue;

		do {
			ret = tftf_try_cpu_on(cpu_mpid,
				(uintptr_t) do_sys_susp_on_off_stress, 0);
			if (ret != PSCI_E_SUCCESS && ret != PSCI_E_ON_PENDING &&
					ret != PSCI_E_ALREADY_ON) {
				ERROR("Unexpected return value 0x%x"
						" from PSCI CPU ON\n", ret);
				return -1;
			}
		} while (ret != PSCI_E_SUCCESS);
	}
	return 0;
}

/* Helper function function to get number of CPUs which are OFF in the system */
static int get_off_cpu_count(void)
{
	int aff_off_cpus = 0;
	u_register_t cpu_mpid, current_cpu = read_mpidr_el1() & MPID_MASK;
	int cpu_node;

	/* Query the number of OFF CPUs */
	for_each_cpu(cpu_node) {
		cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
		/* Skip lead CPU, it is already powered on */
		if (cpu_mpid == current_cpu)
			continue;

		if (tftf_psci_affinity_info(cpu_mpid, MPIDR_AFFLVL0) ==
					PSCI_STATE_OFF)
			aff_off_cpus++;
	}

	return aff_off_cpus;
}

/*
 * The main test function which will be executed by all CPUs.
 * 1. The CPU holding the baton will first enter this function and then turns
 *    ON all other CPUs.
 * 2. All the `non-baton` CPUs then wait for the `sync_event` to be signaled
 *    to turn themselves OFF.
 * 3. Number of CPUs which are signaled via `sync_event` by baton CPU is
 *    random.
 * 4. After signaled CPUs have turned themselves OFF, SYSTEM SUSPEND is
 *    issued by the baton CPU.
 * 5. The return value of SYSTEM_SUSPEND is checked by the baton CPU.
 * 6. The next baton CPU is chosen randomly and the test is handed over to this
 *    CPU.
 */
static test_result_t do_sys_susp_on_off_stress(void)
{
	int psci_ret, off_cpu_count;
	u_register_t current_cpu;

	inc_cpu_count();

	current_cpu = read_mpidr_el1() & MPID_MASK;
	if (current_cpu != baton_cpu) {
		tftf_wait_for_event(&sync_event);
		dec_cpu_count();
		return TEST_RESULT_SUCCESS;
	}

	INFO("System suspend test: Baton holder CPU = 0x%llx\n",
			(unsigned long long) current_cpu);
	if (try_cpu_on_all() == -1) {
		tftf_testcase_printf("CPU_ON of secondary CPUs failed.\n");
		return TEST_RESULT_FAIL;
	}

	wait_for_cpus_to_enter_test();

	/* Turn off random number of cores 1 out of 3 times */
	if (rand() % 3)
		off_cpu_count = rand() % participating_cpu_count;
	else
		off_cpu_count = participating_cpu_count - 1;

	/* Signal random number of CPUs to turn OFF */
	tftf_send_event_to(&sync_event, off_cpu_count);

	/* Wait for `off_cpu_count` CPUs to turn OFF */
	while (get_off_cpu_count() != off_cpu_count)
		;

	/* Program timer to fire after delay */
	tftf_program_timer(PLAT_SUSPEND_ENTRY_TIME);

	/* Issue SYSTEM SUSPEND */
	psci_ret = tftf_system_suspend();
	tftf_cancel_timer();

	/* Check return value of SYSTEM SUSPEND API */
	if (off_cpu_count == (participating_cpu_count - 1)) {
		if (psci_ret != PSCI_E_SUCCESS) {
			tftf_testcase_printf("SYSTEM SUSPEND did not succeed "
							"where expected\n");
			return TEST_RESULT_FAIL;
		}
	} else {
		if (psci_ret != PSCI_E_DENIED) {
			tftf_testcase_printf("SYSTEM SUSPEND did not fail "
							"where expected\n");
			return TEST_RESULT_FAIL;
		}
	}

	/* Pass the baton top another CPU */
	baton_cpu = tftf_find_random_cpu_other_than(current_cpu);

	/* Unblock the waiting CPUs */
	tftf_send_event_to(&sync_event,
			(participating_cpu_count - 1) - off_cpu_count);

	/* Wait for all CPUs other than current to turn OFF */
	while (get_off_cpu_count() != (participating_cpu_count - 1))
		;

	dec_cpu_count();

	if (iteration_count++ < MAX_TEST_ITERATIONS) {
		/* Hand over the test execution the new baton CPU */
		psci_ret = tftf_cpu_on(baton_cpu,
				(uintptr_t) do_sys_susp_on_off_stress, 0);
		if (psci_ret != PSCI_E_SUCCESS)
			return TEST_RESULT_FAIL;

		/* Wait for new baton CPU to enter test */
		while (cpu_count == 0)
			;
	} else {
		/*
		 * The test has completed. Print statistics if PSCI STAT COUNT
		 * is supported.
		 */
		if (is_psci_stat_count_supported()) {
			u_register_t count = tftf_psci_stat_count(baton_cpu,
					system_susp_pwr_state);
			tftf_testcase_printf("Iterated %d with %lld system"
				" suspends\n", MAX_TEST_ITERATIONS,
				(unsigned long long)(count - susp_count));
		}
	}

	return TEST_RESULT_SUCCESS;
}

/*
 * @Test_Aim@ Stress test PSCI SYSTEM SUSPEND API.
 * This test iteratively issues PSCI SYSTEM SUSPEND on random cores after
 * issuing turning OFF a random number of CPUs. The PSCI SYSTEM SUSPEND
 * will only succeed if all the CPUs except the calling CPU is OFF.
 */
test_result_t psci_sys_susp_on_off_stress_test(void)
{
	unsigned int pstateid_idx[PLAT_MAX_PWR_LEVEL + 1];
	unsigned int pwrlvl, susp_type, state_id;
	int ret;

	if (!is_psci_sys_susp_supported()) {
		tftf_testcase_printf("System suspend is not supported "
				"by the EL3 firmware\n");
		return TEST_RESULT_SKIPPED;
	}

	SKIP_TEST_IF_LESS_THAN_N_CPUS(2);

	INIT_PWR_LEVEL_INDEX(pstateid_idx);
	tftf_init_event(&sync_event);
	init_spinlock(&cpu_count_lock);

	/* Initialize participating CPU count */
	participating_cpu_count = tftf_get_total_cpus_count();
	cpu_count = 0;

	iteration_count = 0;

	/*
	 * Assign a baton to the current CPU and it is in charge of driving
	 * the test.
	 */
	baton_cpu = read_mpidr_el1() & MPID_MASK;

	/* Print SYSTEM SUSPEND statistics if PSCI STAT is supported */
	if (is_psci_stat_count_supported()) {
		NOTICE("PSCI STAT COUNT supported\n");
		tftf_set_deepest_pstate_idx(PLAT_MAX_PWR_LEVEL, pstateid_idx);

		/* Check if the power state is valid */
		ret = tftf_get_pstate_vars(&pwrlvl,
					&susp_type,
					&state_id,
					pstateid_idx);
		if (ret != PSCI_E_SUCCESS) {
			tftf_testcase_printf("tftf_get_pstate_vars() failed"
					" with ret = %x\n", ret);
			return TEST_RESULT_FAIL;
		}

		assert(pwrlvl == PLAT_MAX_PWR_LEVEL);

		system_susp_pwr_state = tftf_make_psci_pstate(pwrlvl,
				susp_type, state_id);

		susp_count = tftf_psci_stat_count(baton_cpu, system_susp_pwr_state);
	}

	return do_sys_susp_on_off_stress();
}