.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
---|
2 | 2 | #define _GNU_SOURCE |
---|
3 | 3 | #include <assert.h> |
---|
| 4 | +#include <linux/membarrier.h> |
---|
4 | 5 | #include <pthread.h> |
---|
5 | 6 | #include <sched.h> |
---|
| 7 | +#include <stdatomic.h> |
---|
6 | 8 | #include <stdint.h> |
---|
7 | 9 | #include <stdio.h> |
---|
8 | 10 | #include <stdlib.h> |
---|
.. | .. |
---|
159 | 161 | " cbnz " INJECT_ASM_REG ", 222b\n" \ |
---|
160 | 162 | "333:\n" |
---|
161 | 163 | |
---|
162 | | -#elif __PPC__ |
---|
| 164 | +#elif defined(__PPC__) |
---|
163 | 165 | |
---|
164 | 166 | #define RSEQ_INJECT_INPUT \ |
---|
165 | 167 | , [loop_cnt_1]"m"(loop_cnt[1]) \ |
---|
.. | .. |
---|
366 | 368 | abort(); |
---|
367 | 369 | reps = thread_data->reps; |
---|
368 | 370 | for (i = 0; i < reps; i++) { |
---|
369 | | - int cpu = rseq_cpu_start(); |
---|
370 | | - |
---|
371 | | - cpu = rseq_this_cpu_lock(&data->lock); |
---|
| 371 | + int cpu = rseq_this_cpu_lock(&data->lock); |
---|
372 | 372 | data->c[cpu].count++; |
---|
373 | 373 | rseq_percpu_unlock(&data->lock, cpu); |
---|
374 | 374 | #ifndef BENCHMARK |
---|
.. | .. |
---|
549 | 549 | for (;;) { |
---|
550 | 550 | struct percpu_list_node *head; |
---|
551 | 551 | intptr_t *targetptr, expectnot, *load; |
---|
552 | | - off_t offset; |
---|
| 552 | + long offset; |
---|
553 | 553 | int ret; |
---|
554 | 554 | |
---|
555 | 555 | cpu = rseq_cpu_start(); |
---|
.. | .. |
---|
1131 | 1131 | return ret; |
---|
1132 | 1132 | } |
---|
1133 | 1133 | |
---|
| 1134 | +struct test_membarrier_thread_args { |
---|
| 1135 | + int stop; |
---|
| 1136 | + intptr_t percpu_list_ptr; |
---|
| 1137 | +}; |
---|
| 1138 | + |
---|
| 1139 | +/* Worker threads modify data in their "active" percpu lists. */ |
---|
| 1140 | +void *test_membarrier_worker_thread(void *arg) |
---|
| 1141 | +{ |
---|
| 1142 | + struct test_membarrier_thread_args *args = |
---|
| 1143 | + (struct test_membarrier_thread_args *)arg; |
---|
| 1144 | + const int iters = opt_reps; |
---|
| 1145 | + int i; |
---|
| 1146 | + |
---|
| 1147 | + if (rseq_register_current_thread()) { |
---|
| 1148 | + fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", |
---|
| 1149 | + errno, strerror(errno)); |
---|
| 1150 | + abort(); |
---|
| 1151 | + } |
---|
| 1152 | + |
---|
| 1153 | + /* Wait for initialization. */ |
---|
| 1154 | + while (!atomic_load(&args->percpu_list_ptr)) {} |
---|
| 1155 | + |
---|
| 1156 | + for (i = 0; i < iters; ++i) { |
---|
| 1157 | + int ret; |
---|
| 1158 | + |
---|
| 1159 | + do { |
---|
| 1160 | + int cpu = rseq_cpu_start(); |
---|
| 1161 | + |
---|
| 1162 | + ret = rseq_offset_deref_addv(&args->percpu_list_ptr, |
---|
| 1163 | + sizeof(struct percpu_list_entry) * cpu, 1, cpu); |
---|
| 1164 | + } while (rseq_unlikely(ret)); |
---|
| 1165 | + } |
---|
| 1166 | + |
---|
| 1167 | + if (rseq_unregister_current_thread()) { |
---|
| 1168 | + fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", |
---|
| 1169 | + errno, strerror(errno)); |
---|
| 1170 | + abort(); |
---|
| 1171 | + } |
---|
| 1172 | + return NULL; |
---|
| 1173 | +} |
---|
| 1174 | + |
---|
| 1175 | +void test_membarrier_init_percpu_list(struct percpu_list *list) |
---|
| 1176 | +{ |
---|
| 1177 | + int i; |
---|
| 1178 | + |
---|
| 1179 | + memset(list, 0, sizeof(*list)); |
---|
| 1180 | + for (i = 0; i < CPU_SETSIZE; i++) { |
---|
| 1181 | + struct percpu_list_node *node; |
---|
| 1182 | + |
---|
| 1183 | + node = malloc(sizeof(*node)); |
---|
| 1184 | + assert(node); |
---|
| 1185 | + node->data = 0; |
---|
| 1186 | + node->next = NULL; |
---|
| 1187 | + list->c[i].head = node; |
---|
| 1188 | + } |
---|
| 1189 | +} |
---|
| 1190 | + |
---|
| 1191 | +void test_membarrier_free_percpu_list(struct percpu_list *list) |
---|
| 1192 | +{ |
---|
| 1193 | + int i; |
---|
| 1194 | + |
---|
| 1195 | + for (i = 0; i < CPU_SETSIZE; i++) |
---|
| 1196 | + free(list->c[i].head); |
---|
| 1197 | +} |
---|
| 1198 | + |
---|
| 1199 | +static int sys_membarrier(int cmd, int flags, int cpu_id) |
---|
| 1200 | +{ |
---|
| 1201 | + return syscall(__NR_membarrier, cmd, flags, cpu_id); |
---|
| 1202 | +} |
---|
| 1203 | + |
---|
| 1204 | +/* |
---|
| 1205 | + * The manager thread swaps per-cpu lists that worker threads see, |
---|
| 1206 | + * and validates that there are no unexpected modifications. |
---|
| 1207 | + */ |
---|
| 1208 | +void *test_membarrier_manager_thread(void *arg) |
---|
| 1209 | +{ |
---|
| 1210 | + struct test_membarrier_thread_args *args = |
---|
| 1211 | + (struct test_membarrier_thread_args *)arg; |
---|
| 1212 | + struct percpu_list list_a, list_b; |
---|
| 1213 | + intptr_t expect_a = 0, expect_b = 0; |
---|
| 1214 | + int cpu_a = 0, cpu_b = 0; |
---|
| 1215 | + |
---|
| 1216 | + if (rseq_register_current_thread()) { |
---|
| 1217 | + fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", |
---|
| 1218 | + errno, strerror(errno)); |
---|
| 1219 | + abort(); |
---|
| 1220 | + } |
---|
| 1221 | + |
---|
| 1222 | + /* Init lists. */ |
---|
| 1223 | + test_membarrier_init_percpu_list(&list_a); |
---|
| 1224 | + test_membarrier_init_percpu_list(&list_b); |
---|
| 1225 | + |
---|
| 1226 | + atomic_store(&args->percpu_list_ptr, (intptr_t)&list_a); |
---|
| 1227 | + |
---|
| 1228 | + while (!atomic_load(&args->stop)) { |
---|
| 1229 | + /* list_a is "active". */ |
---|
| 1230 | + cpu_a = rand() % CPU_SETSIZE; |
---|
| 1231 | + /* |
---|
| 1232 | + * As list_b is "inactive", we should never see changes |
---|
| 1233 | + * to list_b. |
---|
| 1234 | + */ |
---|
| 1235 | + if (expect_b != atomic_load(&list_b.c[cpu_b].head->data)) { |
---|
| 1236 | + fprintf(stderr, "Membarrier test failed\n"); |
---|
| 1237 | + abort(); |
---|
| 1238 | + } |
---|
| 1239 | + |
---|
| 1240 | + /* Make list_b "active". */ |
---|
| 1241 | + atomic_store(&args->percpu_list_ptr, (intptr_t)&list_b); |
---|
| 1242 | + if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, |
---|
| 1243 | + MEMBARRIER_CMD_FLAG_CPU, cpu_a) && |
---|
| 1244 | + errno != ENXIO /* missing CPU */) { |
---|
| 1245 | + perror("sys_membarrier"); |
---|
| 1246 | + abort(); |
---|
| 1247 | + } |
---|
| 1248 | + /* |
---|
| 1249 | + * Cpu A should now only modify list_b, so the values |
---|
| 1250 | + * in list_a should be stable. |
---|
| 1251 | + */ |
---|
| 1252 | + expect_a = atomic_load(&list_a.c[cpu_a].head->data); |
---|
| 1253 | + |
---|
| 1254 | + cpu_b = rand() % CPU_SETSIZE; |
---|
| 1255 | + /* |
---|
| 1256 | + * As list_a is "inactive", we should never see changes |
---|
| 1257 | + * to list_a. |
---|
| 1258 | + */ |
---|
| 1259 | + if (expect_a != atomic_load(&list_a.c[cpu_a].head->data)) { |
---|
| 1260 | + fprintf(stderr, "Membarrier test failed\n"); |
---|
| 1261 | + abort(); |
---|
| 1262 | + } |
---|
| 1263 | + |
---|
| 1264 | + /* Make list_a "active". */ |
---|
| 1265 | + atomic_store(&args->percpu_list_ptr, (intptr_t)&list_a); |
---|
| 1266 | + if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, |
---|
| 1267 | + MEMBARRIER_CMD_FLAG_CPU, cpu_b) && |
---|
| 1268 | + errno != ENXIO /* missing CPU*/) { |
---|
| 1269 | + perror("sys_membarrier"); |
---|
| 1270 | + abort(); |
---|
| 1271 | + } |
---|
| 1272 | + /* Remember a value from list_b. */ |
---|
| 1273 | + expect_b = atomic_load(&list_b.c[cpu_b].head->data); |
---|
| 1274 | + } |
---|
| 1275 | + |
---|
| 1276 | + test_membarrier_free_percpu_list(&list_a); |
---|
| 1277 | + test_membarrier_free_percpu_list(&list_b); |
---|
| 1278 | + |
---|
| 1279 | + if (rseq_unregister_current_thread()) { |
---|
| 1280 | + fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", |
---|
| 1281 | + errno, strerror(errno)); |
---|
| 1282 | + abort(); |
---|
| 1283 | + } |
---|
| 1284 | + return NULL; |
---|
| 1285 | +} |
---|
| 1286 | + |
---|
| 1287 | +/* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */ |
---|
| 1288 | +#ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV |
---|
| 1289 | +void test_membarrier(void) |
---|
| 1290 | +{ |
---|
| 1291 | + const int num_threads = opt_threads; |
---|
| 1292 | + struct test_membarrier_thread_args thread_args; |
---|
| 1293 | + pthread_t worker_threads[num_threads]; |
---|
| 1294 | + pthread_t manager_thread; |
---|
| 1295 | + int i, ret; |
---|
| 1296 | + |
---|
| 1297 | + if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, 0, 0)) { |
---|
| 1298 | + perror("sys_membarrier"); |
---|
| 1299 | + abort(); |
---|
| 1300 | + } |
---|
| 1301 | + |
---|
| 1302 | + thread_args.stop = 0; |
---|
| 1303 | + thread_args.percpu_list_ptr = 0; |
---|
| 1304 | + ret = pthread_create(&manager_thread, NULL, |
---|
| 1305 | + test_membarrier_manager_thread, &thread_args); |
---|
| 1306 | + if (ret) { |
---|
| 1307 | + errno = ret; |
---|
| 1308 | + perror("pthread_create"); |
---|
| 1309 | + abort(); |
---|
| 1310 | + } |
---|
| 1311 | + |
---|
| 1312 | + for (i = 0; i < num_threads; i++) { |
---|
| 1313 | + ret = pthread_create(&worker_threads[i], NULL, |
---|
| 1314 | + test_membarrier_worker_thread, &thread_args); |
---|
| 1315 | + if (ret) { |
---|
| 1316 | + errno = ret; |
---|
| 1317 | + perror("pthread_create"); |
---|
| 1318 | + abort(); |
---|
| 1319 | + } |
---|
| 1320 | + } |
---|
| 1321 | + |
---|
| 1322 | + |
---|
| 1323 | + for (i = 0; i < num_threads; i++) { |
---|
| 1324 | + ret = pthread_join(worker_threads[i], NULL); |
---|
| 1325 | + if (ret) { |
---|
| 1326 | + errno = ret; |
---|
| 1327 | + perror("pthread_join"); |
---|
| 1328 | + abort(); |
---|
| 1329 | + } |
---|
| 1330 | + } |
---|
| 1331 | + |
---|
| 1332 | + atomic_store(&thread_args.stop, 1); |
---|
| 1333 | + ret = pthread_join(manager_thread, NULL); |
---|
| 1334 | + if (ret) { |
---|
| 1335 | + errno = ret; |
---|
| 1336 | + perror("pthread_join"); |
---|
| 1337 | + abort(); |
---|
| 1338 | + } |
---|
| 1339 | +} |
---|
| 1340 | +#else /* RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV */ |
---|
| 1341 | +void test_membarrier(void) |
---|
| 1342 | +{ |
---|
| 1343 | + fprintf(stderr, "rseq_offset_deref_addv is not implemented on this architecture. " |
---|
| 1344 | + "Skipping membarrier test.\n"); |
---|
| 1345 | +} |
---|
| 1346 | +#endif |
---|
| 1347 | + |
---|
1134 | 1348 | static void show_usage(int argc, char **argv) |
---|
1135 | 1349 | { |
---|
1136 | 1350 | printf("Usage : %s <OPTIONS>\n", |
---|
.. | .. |
---|
1153 | 1367 | printf(" [-r N] Number of repetitions per thread (default 5000)\n"); |
---|
1154 | 1368 | printf(" [-d] Disable rseq system call (no initialization)\n"); |
---|
1155 | 1369 | printf(" [-D M] Disable rseq for each M threads\n"); |
---|
1156 | | - printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement\n"); |
---|
| 1370 | + printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n"); |
---|
1157 | 1371 | printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n"); |
---|
1158 | 1372 | printf(" [-v] Verbose output.\n"); |
---|
1159 | 1373 | printf(" [-h] Show this help.\n"); |
---|
.. | .. |
---|
1268 | 1482 | case 'i': |
---|
1269 | 1483 | case 'b': |
---|
1270 | 1484 | case 'm': |
---|
| 1485 | + case 'r': |
---|
1271 | 1486 | break; |
---|
1272 | 1487 | default: |
---|
1273 | 1488 | show_usage(argc, argv); |
---|
.. | .. |
---|
1320 | 1535 | printf_verbose("counter increment\n"); |
---|
1321 | 1536 | test_percpu_inc(); |
---|
1322 | 1537 | break; |
---|
| 1538 | + case 'r': |
---|
| 1539 | + printf_verbose("membarrier\n"); |
---|
| 1540 | + test_membarrier(); |
---|
| 1541 | + break; |
---|
1323 | 1542 | } |
---|
1324 | 1543 | if (!opt_disable_rseq && rseq_unregister_current_thread()) |
---|
1325 | 1544 | abort(); |
---|