1 /* SPDX-License-Identifier: LGPL-2.0-only */ 2 /* 3 * SMP and IPI Tests 4 * 5 * Copyright 2024 Nicholas Piggin, IBM Corp. 6 */ 7 #include <libcflat.h> 8 #include <asm/atomic.h> 9 #include <asm/barrier.h> 10 #include <asm/processor.h> 11 #include <asm/time.h> 12 #include <asm/smp.h> 13 #include <asm/setup.h> 14 #include <asm/ppc_asm.h> 15 #include <devicetree.h> 16 17 static volatile bool start_test_running = true; 18 static volatile int nr_cpus_started; 19 20 static void start_fn(int cpu_id) 21 { 22 atomic_fetch_inc(&nr_cpus_started); 23 while (start_test_running) 24 cpu_relax(); 25 atomic_fetch_dec(&nr_cpus_started); 26 } 27 28 static void test_start_cpus(int argc, char **argv) 29 { 30 uint64_t tb; 31 32 if (argc > 2) 33 report_abort("Unsupported argument: '%s'", argv[2]); 34 35 nr_cpus_started = 1; 36 if (!start_all_cpus(start_fn)) 37 report_abort("Failed to start secondary cpus"); 38 39 tb = get_tb(); 40 while (nr_cpus_started < nr_cpus_present) { 41 cpu_relax(); 42 if (get_tb() - tb > tb_hz * 5) 43 report_abort("Failed to start all secondaries"); 44 } 45 46 if (nr_cpus_started != nr_cpus_online) 47 report_abort("Started CPUs does not match online"); 48 49 barrier(); 50 start_test_running = false; 51 barrier(); 52 53 tb = get_tb(); 54 while (nr_cpus_started > 1) { 55 cpu_relax(); 56 if (get_tb() - tb > tb_hz * 5) 57 report_abort("Failed to stop all secondaries"); 58 } 59 60 stop_all_cpus(); 61 62 report(true, "start cpus"); 63 } 64 65 static volatile int nr_cpus_ipi = 0; 66 67 static void ipi_handler(struct pt_regs *regs, void *data) 68 { 69 atomic_fetch_inc(&nr_cpus_ipi); 70 } 71 72 static volatile bool ipi_test_running = true; 73 74 static void ipi_fn(int cpu_id) 75 { 76 local_ipi_enable(); 77 78 mtspr(SPR_DEC, 0x7fffffff); 79 local_irq_enable(); 80 while (ipi_test_running) 81 cpu_relax(); 82 local_irq_disable(); 83 84 local_ipi_disable(); 85 } 86 87 static void test_ipi_cpus(int argc, char **argv) 88 { 89 uint64_t tb; 90 int i; 91 92 if (argc > 2) 93 report_abort("Unsupported argument: '%s'", argv[2]); 94 95 if (nr_cpus_present < 2) { 96 report_skip("Requires SMP (2 or more CPUs)"); 97 return; 98 } 99 100 register_ipi(ipi_handler, NULL); 101 102 if (!start_all_cpus(ipi_fn)) 103 report_abort("Failed to start secondary cpus"); 104 105 for (i = 1; i < nr_cpus_online; i++) 106 send_ipi(cpus[i].server_no); 107 108 tb = get_tb(); 109 while (nr_cpus_ipi < nr_cpus_online - 1) { 110 cpu_relax(); 111 if (get_tb() - tb > tb_hz * 5) 112 report_abort("Secondaries failed to respond to IPIs"); 113 } 114 115 send_ipi(cpus[1].server_no); 116 117 tb = get_tb(); 118 while (nr_cpus_ipi < nr_cpus_online) { 119 cpu_relax(); 120 if (get_tb() - tb > tb_hz * 5) 121 report_abort("Secondaries failed to respond to IPIs"); 122 } 123 124 ipi_test_running = false; 125 126 stop_all_cpus(); 127 128 assert(nr_cpus_ipi == nr_cpus_present); 129 130 unregister_ipi(); 131 132 report(true, "IPI cpus"); 133 } 134 135 static uint64_t time; 136 static bool time_went_backward; 137 138 static void check_and_record_time(void) 139 { 140 uint64_t tb; 141 uint64_t t; 142 uint64_t old; 143 144 t = time; 145 again: 146 barrier(); 147 tb = get_tb(); 148 asm volatile("1: ldarx %0,0,%1 ; cmpd %0,%2 ; bne 2f ; stdcx. %3,0,%1 ; bne- 1b; 2:" : "=&r"(old) : "r"(&time), "r"(t), "r"(tb) : "memory", "cr0"); 149 assert(tb >= t); 150 if (old != t) { 151 t = old; 152 goto again; 153 } 154 if (old > tb) 155 time_went_backward = true; 156 } 157 158 static void update_time(int64_t tb_offset) 159 { 160 uint64_t new_tb; 161 162 new_tb = get_tb() + tb_offset; 163 mtspr(SPR_TBU40, new_tb); 164 if ((get_tb() & 0xFFFFFF) < (new_tb & 0xFFFFFF)) { 165 new_tb += 0x1000000; 166 mtspr(SPR_TBU40, new_tb); 167 } 168 } 169 170 static void time_sync_fn(int cpu_id) 171 { 172 uint64_t start = get_tb(); 173 174 while (!time_went_backward && get_tb() - start < tb_hz*2) { 175 check_and_record_time(); 176 cpu_relax(); 177 } 178 179 while (!time_went_backward && get_tb() - start < tb_hz*2) { 180 check_and_record_time(); 181 udelay(1); 182 } 183 184 if (machine_is_powernv()) { 185 while (!time_went_backward && get_tb() - start < tb_hz*2) { 186 check_and_record_time(); 187 update_time(0x1234000000); 188 cpu_relax(); 189 update_time(-0x1234000000); 190 } 191 } 192 } 193 194 static void test_time_sync(int argc, char **argv) 195 { 196 if (argc > 2) 197 report_abort("Unsupported argument: '%s'", argv[2]); 198 199 if (nr_cpus_present < 2) { 200 report_skip("Requires SMP (2 or more CPUs)"); 201 return; 202 } 203 204 time_went_backward = false; 205 206 if (!start_all_cpus(time_sync_fn)) 207 report_abort("Failed to start secondary cpus"); 208 209 time_sync_fn(-1); 210 211 stop_all_cpus(); 212 213 report(!time_went_backward, "time sync"); 214 } 215 216 static volatile bool relax_test_running = true; 217 218 static int relax_loop_count[NR_CPUS]; 219 220 static void relax_fn(int cpu_id) 221 { 222 volatile int i = 0; 223 224 while (relax_test_running) { 225 cpu_relax(); 226 i++; 227 } 228 229 relax_loop_count[cpu_id] = i; 230 } 231 232 #define ITERS 1000000 233 234 static void test_relax(int argc, char **argv) 235 { 236 volatile int i; 237 int count; 238 239 if (argc > 2) 240 report_abort("Unsupported argument: '%s'", argv[2]); 241 242 if (nr_cpus_present < 2) { 243 report_skip("Requires SMP (2 or more CPUs)"); 244 return; 245 } 246 247 if (!start_all_cpus(relax_fn)) 248 report_abort("Failed to start secondary cpus"); 249 250 for (i = 0; i < ITERS; i++) 251 ; 252 253 relax_test_running = false; 254 255 stop_all_cpus(); 256 257 count = 0; 258 for (i = 0; i < NR_CPUS; i++) 259 count += relax_loop_count[i]; 260 if (count == 0) 261 count = 1; 262 263 report(true, "busy-loops on CPU:%d vs cpu_relax-loops on others %ld%%", smp_processor_id(), (long)ITERS * 100 / count); 264 } 265 266 static volatile bool pause_test_running = true; 267 268 static int pause_loop_count[NR_CPUS]; 269 270 static void pause_fn(int cpu_id) 271 { 272 volatile int i = 0; 273 274 while (pause_test_running) { 275 pause_short(); 276 i++; 277 } 278 279 pause_loop_count[cpu_id] = i; 280 } 281 282 #define ITERS 1000000 283 284 static void test_pause(int argc, char **argv) 285 { 286 volatile int i; 287 int count; 288 289 if (argc > 2) 290 report_abort("Unsupported argument: '%s'", argv[2]); 291 292 if (!cpu_has_pause_short) 293 return; 294 295 if (nr_cpus_present < 2) { 296 report_skip("Requires SMP (2 or more CPUs)"); 297 return; 298 } 299 300 if (!start_all_cpus(pause_fn)) 301 report_abort("Failed to start secondary cpus"); 302 303 for (i = 0; i < ITERS; i++) 304 ; 305 306 pause_test_running = false; 307 308 stop_all_cpus(); 309 310 count = 0; 311 for (i = 0; i < NR_CPUS; i++) 312 count += pause_loop_count[i]; 313 314 report(true, "busy-loops on CPU:%d vs pause_short-loops on others %ld%%", smp_processor_id(), (long)ITERS * 100 / count); 315 } 316 317 struct { 318 const char *name; 319 void (*func)(int argc, char **argv); 320 } hctests[] = { 321 { "start_cpus", test_start_cpus }, 322 { "ipi_cpus", test_ipi_cpus }, 323 { "time_sync", test_time_sync }, 324 { "cpu_relax", test_relax }, 325 { "pause", test_pause }, 326 { NULL, NULL } 327 }; 328 329 int main(int argc, char **argv) 330 { 331 bool all; 332 int i; 333 334 all = argc == 1 || !strcmp(argv[1], "all"); 335 336 report_prefix_push("smp"); 337 338 for (i = 0; hctests[i].name != NULL; i++) { 339 if (all || strcmp(argv[1], hctests[i].name) == 0) { 340 report_prefix_push(hctests[i].name); 341 hctests[i].func(argc, argv); 342 report_prefix_pop(); 343 } 344 } 345 346 report_prefix_pop(); 347 return report_summary(); 348 } 349