/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define TLOG_TAG "memlatency" #include #include #include #include #include #include #include #include #define BLOCK_SIZE_BYTES (CACHE_LINE * 4) #define STRUCT_NPAD (BLOCK_SIZE_BYTES) / sizeof(uintptr_t) #define MAX_WORKING_SET_SZ 16777216 static const uint64_t working_set_sizes[] = { BLOCK_SIZE_BYTES, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, MAX_WORKING_SET_SZ, }; typedef union memlatency_state_t { union memlatency_state_t* next; uintptr_t pad[STRUCT_NPAD]; } memlatency_state_t; static memlatency_state_t* memlatency_state_start; static size_t nb_blocks = MAX_WORKING_SET_SZ / sizeof(memlatency_state_t); static void get_param_name_cb_fixed(char* buf, size_t buf_size, size_t param_idx) { snprintf(buf, buf_size, "%" PRIu64 " Bytes working size in blocks of %zu Bytes", working_set_sizes[param_idx], sizeof(memlatency_state_t)); } static void get_formatted_value_cb(char* buf, size_t buf_size, int64_t value, const char* metric_name) { if (strcmp("time_micro_seconds", metric_name) == 0) { int64_t mic_sec = value / 1000; int64_t n_sec = value % 1000; snprintf(buf, buf_size, "%" PRId64 ".%03" PRId64 "", mic_sec, n_sec); } else { snprintf(buf, buf_size, "%" PRId64, value); } } static uint64_t mem_lat_pmu_evt_arr[] = {PMU_EV_CPU_CYCLES, PMU_EV_INST_RETIRED}; BENCH_SETUP(memlatency) { BENCH_INIT_PMU(mem_lat_pmu_evt_arr); trusty_bench_get_param_name_cb = &get_param_name_cb_fixed; trusty_bench_get_formatted_value_cb = &get_formatted_value_cb; memlatency_state_start = memalign(CACHE_LINE, nb_blocks * sizeof(memlatency_state_t)); if (memlatency_state_start == NULL) { TLOGE("Failed to Allocate memory for memlatency_state!"); return ERR_NO_MEMORY; } memset((uint8_t*)memlatency_state_start, 0, nb_blocks * sizeof(memlatency_state_t)); for (size_t idx = 0; idx < nb_blocks - 1; ++idx) { memlatency_state_start[idx].next = &memlatency_state_start[idx + 1]; } static_assert(sizeof(memlatency_state_t) == BLOCK_SIZE_BYTES); return NO_ERROR; } BENCH_TEARDOWN(memlatency) { free(memlatency_state_start); memlatency_state_start = NULL; } BENCH(memlatency, latency_read, 20, working_set_sizes) { uint64_t sz = working_set_sizes[bench_get_param_idx()]; uint64_t nb_blocks = sz / BLOCK_SIZE_BYTES; uint64_t loops = 10 * (MAX_WORKING_SET_SZ / sz); ASSERT_GT(nb_blocks, 0); while (loops > 0) { --loops; volatile union memlatency_state_t* block = memlatency_state_start; for (size_t idx = 0; idx < nb_blocks; idx++) { /* To make sure we are not overwriting next block Address */ static_assert(sizeof(uintptr_t) == __SIZEOF_POINTER__); block = block->next; } } return NO_ERROR; test_abort: return ERR_GENERIC; } BENCH(memlatency, latency_write, 20, working_set_sizes) { uint64_t sz = working_set_sizes[bench_get_param_idx()]; uint64_t nb_blocks = sz / BLOCK_SIZE_BYTES; uint64_t loops = 10 * (MAX_WORKING_SET_SZ / sz); ASSERT_GT(nb_blocks, 0); while (loops > 0) { --loops; union memlatency_state_t* block = memlatency_state_start; for (size_t idx = 0; idx < nb_blocks; idx++) { /* To make sure we are not overwriting next block Address */ static_assert(sizeof(uintptr_t) == __SIZEOF_POINTER__); (block + idx)->pad[1] = idx + sz; } } return NO_ERROR; test_abort: return ERR_GENERIC; } BENCH_RESULT(memlatency, latency_read, time_micro_seconds) { return bench_get_duration_ns(); } BENCH_RESULT(memlatency, latency_write, time_micro_seconds) { return bench_get_duration_ns(); } BENCH_RESULT(memlatency, latency_read, cycle_counter_0) { return bench_get_pmu_cnt(0); } BENCH_RESULT(memlatency, latency_read, cycle_counter) { return bench_get_pmu_cnt(1); } BENCH_RESULT(memlatency, latency_read, inst_retired) { return bench_get_pmu_cnt(2); } PORT_TEST(memlatency, "com.android.kernel.memorylatency.bench");