blob: b20fe887b58ce1002bdccc99cdac064899cd7a80 [file] [log] [blame]
/*
* Copyright (c) 2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* This file contains tests that measure the latencies for PSCI power
* down sequences.
*/
#include <arch.h>
#include <arch_helpers.h>
#include <debug.h>
#include <events.h>
#include <irq.h>
#include <mmio.h>
#include <plat_topology.h>
#include <platform.h>
#include <platform_def.h>
#include <power_management.h>
#include <psci.h>
#include <stdlib.h>
#include <test_helpers.h>
#include <tftf.h>
#include <tftf_lib.h>
static event_t target_booted, target_keep_on_booted, target_keep_on;
/*
* Define the variance allowed from baseline number. This is the divisor
* to be used. For example, for 10% define this macro to 10 and for
* 20% define to 5.
*/
#define BASELINE_VARIANCE 10
static test_result_t test_target_function(void)
{
tftf_send_event(&target_booted);
return TEST_RESULT_SUCCESS;
}
static test_result_t test_target_keep_on_function(void)
{
tftf_send_event(&target_keep_on_booted);
tftf_wait_for_event(&target_keep_on);
return TEST_RESULT_SUCCESS;
}
/*
* The helper routine for psci_trigger_peer_cluster_cache_coh() test. Turn ON and turn OFF
* the target CPU while flooding it with CPU ON requests.
*/
static test_result_t get_target_cpu_on_stats(unsigned int target_mpid,
uint64_t *count_diff, unsigned int *cpu_on_hits_on_target)
{
int ret;
uint64_t start_time;
ret = tftf_try_cpu_on(target_mpid, (uintptr_t) test_target_function, 0);
if (ret != PSCI_E_SUCCESS) {
tftf_testcase_printf("Failed to turn ON target CPU %x\n", target_mpid);
return TEST_RESULT_FAIL;
}
tftf_wait_for_event(&target_booted);
/* The target CPU is now turning OFF */
start_time = syscounter_read();
/* Flood the target CPU with CPU ON requests */
do {
ret = tftf_try_cpu_on(target_mpid, (uintptr_t) test_target_function, 0);
if (ret == PSCI_E_ALREADY_ON)
(*cpu_on_hits_on_target)++;
} while ((ret == PSCI_E_ALREADY_ON) && (syscounter_read() < (start_time + read_cntfrq_el0())));
if (ret != PSCI_E_SUCCESS) {
tftf_testcase_printf("The target failed to turn ON within 1000ms\n");
return TEST_RESULT_FAIL;
}
*count_diff = (uint64_t)(syscounter_read() - start_time);
tftf_wait_for_event(&target_booted);
return TEST_RESULT_SUCCESS;
}
/*
* @Test_Aim@ Measure the difference in latencies in waking up a CPU when it is
* the last one to powerdown in a cluster vs when the cluster is kept `alive`.
* This test tries to bring up a CPU on a different cluster and then turn it
* OFF. As it is being turned OFF, flood it with PSCI_CPU_ON requests from lead
* CPU and see if there is any delay in detecting that the target CPU is OFF.
*
* This test also tries to trigger bug fixed by this patch SHA 71341d23 in TF-A
* repo. The theory is that when the last CPU in the cluster powers down, it
* plugs itself out of coherent interconnect and the cache maintenance that was
* performed as part of the target CPU shutdown may not seen by the current
* CPU cluster. Hence depending on the cached value of the aff_info state
* of the target CPU may not be correct. Hence the TF-A patch does a cache line
* flush before reading the aff_info state in the CPU_ON code path. The test
* tries to detect whether there is a delay in the aff_info state of the target
* CPU as seen from the current CPU.
*
* The test itself did not manage to trigger the issue on ARM internal
* platforms but only measures the difference in delay when the
* last CPU in the cluster is powered down versus `a CPU` in the cluster is
* powered down.
*
* There are 2 parts to this test. In the first part, the target CPU is turned
* ON and the test is conducted as described above but with another CPU
* in the target cluster kept running (referred to as the keep ON CPU).
* The baseline numbers are collected in this configuration.
*
* For the second part of the test, the sequence is repeated, but without the
* `keep on` CPU. The test numbers are collected. If there is a variation of
* more than BASELINE_VARIANCE from the baseline numbers, then a message
* indicating the same is printed out. This is a bit subjective test and
* depends on the platform. Hence this test is not recommended to be run on
* Models.
*/
test_result_t psci_trigger_peer_cluster_cache_coh(void)
{
unsigned int target_idx, target_keep_on_idx, cluster_1, cluster_2,
target_mpid, target_keep_on_mpid, hits_baseline = 0,
hits_test = 0;
int ret;
uint64_t diff_baseline = 0, diff_test = 0;
SKIP_TEST_IF_LESS_THAN_N_CLUSTERS(2);
tftf_init_event(&target_booted);
tftf_init_event(&target_keep_on_booted);
tftf_init_event(&target_keep_on);
/* Identify the cluster node corresponding to the lead CPU */
cluster_1 = tftf_get_parent_node_from_mpidr(read_mpidr_el1(),
PLAT_MAX_PWR_LEVEL - 1);
assert(cluster_1 != PWR_DOMAIN_INIT);
/* Identify 2nd cluster node for the test */
for_each_power_domain_idx(cluster_2, PLAT_MAX_PWR_LEVEL - 1) {
if (cluster_2 != cluster_1)
break;
}
assert(cluster_2 != PWR_DOMAIN_INIT);
/* First lets try to get baseline time data */
/* Identify a target CPU and `keep_on` CPU nodes on cluster_2 */
target_idx = tftf_get_next_cpu_in_pwr_domain(cluster_2, PWR_DOMAIN_INIT);
target_keep_on_idx = tftf_get_next_cpu_in_pwr_domain(cluster_2, target_idx);
assert(target_idx != PWR_DOMAIN_INIT);
if (target_keep_on_idx == PWR_DOMAIN_INIT) {
tftf_testcase_printf("Need at least 2 CPUs on target test cluster\n");
return TEST_RESULT_SKIPPED;
}
/* Get the MPIDR for the target and `keep_on` CPUs */
target_mpid = tftf_get_mpidr_from_node(target_idx);
target_keep_on_mpid = tftf_get_mpidr_from_node(target_keep_on_idx);
assert(target_mpid != INVALID_MPID);
assert(target_keep_on_mpid != INVALID_MPID);
/* Turn On the `keep_on CPU` and keep it ON for the baseline data */
ret = tftf_try_cpu_on(target_keep_on_mpid, (uintptr_t) test_target_keep_on_function, 0);
if (ret != PSCI_E_SUCCESS) {
tftf_testcase_printf("Failed to turn ON target CPU %x\n", target_mpid);
return TEST_RESULT_FAIL;
}
tftf_wait_for_event(&target_keep_on_booted);
ret = get_target_cpu_on_stats(target_mpid, &diff_baseline, &hits_baseline);
/* Allow `Keep-on` CPU to power OFF */
tftf_send_event(&target_keep_on);
if (ret != TEST_RESULT_SUCCESS)
return TEST_RESULT_FAIL;
tftf_testcase_printf("\t\tFinished in ticks \tCPU_ON requests prior to success\n");
tftf_testcase_printf("Baseline data: \t%lld \t\t\t%d\n", diff_baseline,
hits_baseline);
wait_for_non_lead_cpus();
/*
* Now we have baseline data. Try to test the same case but without a
* `keep on` CPU.
*/
ret = get_target_cpu_on_stats(target_mpid, &diff_test, &hits_test);
if (ret != TEST_RESULT_SUCCESS)
return TEST_RESULT_FAIL;
tftf_testcase_printf("Test data: \t%lld \t\t\t%d\n", diff_test,
hits_test);
int variance = ((diff_test - diff_baseline) * 100) / (diff_baseline);
tftf_testcase_printf("Variance of %d per-cent from baseline detected\n",
variance);
wait_for_non_lead_cpus();
return TEST_RESULT_SUCCESS;
}