Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rseq/selftests: Test MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ

Based on Google-internal RSEQ work done by Paul Turner and Andrew
Hunter.

This patch adds a selftest for MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ.
The test quite often fails without the previous patch in this
patchset, but consistently passes with it.

Signed-off-by: Peter Oskolkov <posk@google.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Link: https://lkml.kernel.org/r/20200923233618.2572849-3-posk@google.com

authored by

Peter Oskolkov and committed by
Peter Zijlstra
f166b111 ea366dd7

+224 -1
+222 -1
tools/testing/selftests/rseq/param_test.c
··· 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> ··· 1133 1131 return ret; 1134 1132 } 1135 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 + 1136 1348 static void show_usage(int argc, char **argv) 1137 1349 { 1138 1350 printf("Usage : %s <OPTIONS>\n", ··· 1369 1153 printf(" [-r N] Number of repetitions per thread (default 5000)\n"); 1370 1154 printf(" [-d] Disable rseq system call (no initialization)\n"); 1371 1155 printf(" [-D M] Disable rseq for each M threads\n"); 1372 - printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement\n"); 1156 + printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n"); 1373 1157 printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n"); 1374 1158 printf(" [-v] Verbose output.\n"); 1375 1159 printf(" [-h] Show this help.\n"); ··· 1484 1268 case 'i': 1485 1269 case 'b': 1486 1270 case 'm': 1271 + case 'r': 1487 1272 break; 1488 1273 default: 1489 1274 show_usage(argc, argv); ··· 1536 1319 case 'i': 1537 1320 printf_verbose("counter increment\n"); 1538 1321 test_percpu_inc(); 1322 + break; 1323 + case 'r': 1324 + printf_verbose("membarrier\n"); 1325 + test_membarrier(); 1539 1326 break; 1540 1327 } 1541 1328 if (!opt_disable_rseq && rseq_unregister_current_thread())
+2
tools/testing/selftests/rseq/run_param_test.sh
··· 15 15 "-T m" 16 16 "-T m -M" 17 17 "-T i" 18 + "-T r" 18 19 ) 19 20 20 21 TEST_NAME=( ··· 26 25 "memcpy" 27 26 "memcpy with barrier" 28 27 "increment" 28 + "membarrier" 29 29 ) 30 30 IFS="$OLDIFS" 31 31