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