1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define TLOG_TAG "memlatency"
18
19 #include <inttypes.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <arch/defines.h>
26 #include <trusty_benchmark.h>
27 #include <uapi/err.h>
28
29 #define BLOCK_SIZE_BYTES (CACHE_LINE * 4)
30 #define STRUCT_NPAD (BLOCK_SIZE_BYTES) / sizeof(uintptr_t)
31 #define MAX_WORKING_SET_SZ 16777216
32
33 static const uint64_t working_set_sizes[] = {
34 BLOCK_SIZE_BYTES,
35 512,
36 1024,
37 2048,
38 4096,
39 8192,
40 16384,
41 32768,
42 65536,
43 131072,
44 262144,
45 524288,
46 1048576,
47 2097152,
48 4194304,
49 8388608,
50 MAX_WORKING_SET_SZ,
51 };
52
53 typedef union memlatency_state_t {
54 union memlatency_state_t* next;
55 uintptr_t pad[STRUCT_NPAD];
56 } memlatency_state_t;
57
58 static memlatency_state_t* memlatency_state_start;
59
60 static size_t nb_blocks = MAX_WORKING_SET_SZ / sizeof(memlatency_state_t);
61
get_param_name_cb_fixed(char * buf,size_t buf_size,size_t param_idx)62 static void get_param_name_cb_fixed(char* buf,
63 size_t buf_size,
64 size_t param_idx) {
65 snprintf(buf, buf_size,
66 "%" PRIu64 " Bytes working size in blocks of %zu Bytes",
67 working_set_sizes[param_idx], sizeof(memlatency_state_t));
68 }
69
get_formatted_value_cb(char * buf,size_t buf_size,int64_t value,const char * metric_name)70 static void get_formatted_value_cb(char* buf,
71 size_t buf_size,
72 int64_t value,
73 const char* metric_name) {
74 if (strcmp("time_micro_seconds", metric_name) == 0) {
75 int64_t mic_sec = value / 1000;
76 int64_t n_sec = value % 1000;
77
78 snprintf(buf, buf_size, "%" PRId64 ".%03" PRId64 "", mic_sec, n_sec);
79 } else {
80 snprintf(buf, buf_size, "%" PRId64, value);
81 }
82 }
83
BENCH_SETUP(memlatency)84 BENCH_SETUP(memlatency) {
85 trusty_bench_get_param_name_cb = &get_param_name_cb_fixed;
86 trusty_bench_get_formatted_value_cb = &get_formatted_value_cb;
87 memlatency_state_start =
88 memalign(CACHE_LINE, nb_blocks * sizeof(memlatency_state_t));
89
90 if (memlatency_state_start == NULL) {
91 TLOGE("Failed to Allocate memory for memlatency_state!");
92 return ERR_NO_MEMORY;
93 }
94
95 memset((uint8_t*)memlatency_state_start, 0,
96 nb_blocks * sizeof(memlatency_state_t));
97
98 for (size_t idx = 0; idx < nb_blocks - 1; ++idx) {
99 memlatency_state_start[idx].next = &memlatency_state_start[idx + 1];
100 }
101
102 static_assert(sizeof(memlatency_state_t) == BLOCK_SIZE_BYTES);
103
104 return NO_ERROR;
105 }
106
BENCH_TEARDOWN(memlatency)107 BENCH_TEARDOWN(memlatency) {
108 free(memlatency_state_start);
109 memlatency_state_start = NULL;
110 }
111
112 BENCH(memlatency, latency_read, 20, working_set_sizes) {
113 uint64_t sz = working_set_sizes[bench_get_param_idx()];
114 uint64_t nb_blocks = sz / BLOCK_SIZE_BYTES;
115 uint64_t loops = 10 * (MAX_WORKING_SET_SZ / sz);
116
117 ASSERT_GT(nb_blocks, 0);
118
119 while (loops > 0) {
120 --loops;
121 volatile union memlatency_state_t* block = memlatency_state_start;
122
123 for (size_t idx = 0; idx < nb_blocks; idx++) {
124 /* To make sure we are not overwriting next block Address */
125 static_assert(sizeof(uintptr_t) == __SIZEOF_POINTER__);
126 block = block->next;
127 }
128 }
129
130 return NO_ERROR;
131 test_abort:
132 return ERR_GENERIC;
133 }
134
135 BENCH(memlatency, latency_write, 20, working_set_sizes) {
136 uint64_t sz = working_set_sizes[bench_get_param_idx()];
137 uint64_t nb_blocks = sz / BLOCK_SIZE_BYTES;
138 uint64_t loops = 10 * (MAX_WORKING_SET_SZ / sz);
139
140 ASSERT_GT(nb_blocks, 0);
141
142 while (loops > 0) {
143 --loops;
144 union memlatency_state_t* block = memlatency_state_start;
145
146 for (size_t idx = 0; idx < nb_blocks; idx++) {
147 /* To make sure we are not overwriting next block Address */
148 static_assert(sizeof(uintptr_t) == __SIZEOF_POINTER__);
149 (block + idx)->pad[1] = idx + sz;
150 }
151 }
152
153 return NO_ERROR;
154 test_abort:
155 return ERR_GENERIC;
156 }
157
BENCH_RESULT(memlatency,latency_read,time_micro_seconds)158 BENCH_RESULT(memlatency, latency_read, time_micro_seconds) {
159 return bench_get_duration_ns();
160 }
161
BENCH_RESULT(memlatency,latency_write,time_micro_seconds)162 BENCH_RESULT(memlatency, latency_write, time_micro_seconds) {
163 return bench_get_duration_ns();
164 }
165
166 PORT_TEST(memlatency, "com.android.kernel.memorylatency.bench");
167