4
4
#include <stdbool.h>
5
5
#include <time.h>
6
6
#include <string.h>
7
+ #include <numa.h>
7
8
8
9
#include "../kselftest.h"
9
10
13
14
#define KSM_PAGE_COUNT_DEFAULT 10l
14
15
#define KSM_PROT_STR_DEFAULT "rw"
15
16
#define KSM_USE_ZERO_PAGES_DEFAULT false
17
+ #define KSM_MERGE_ACROSS_NODES_DEFAULT true
16
18
17
19
struct ksm_sysfs {
18
20
unsigned long max_page_sharing ;
@@ -27,7 +29,8 @@ struct ksm_sysfs {
27
29
enum ksm_test_name {
28
30
CHECK_KSM_MERGE ,
29
31
CHECK_KSM_UNMERGE ,
30
- CHECK_KSM_ZERO_PAGE_MERGE
32
+ CHECK_KSM_ZERO_PAGE_MERGE ,
33
+ CHECK_KSM_NUMA_MERGE
31
34
};
32
35
33
36
static int ksm_write_sysfs (const char * file_path , unsigned long val )
@@ -83,11 +86,12 @@ static int str_to_prot(char *prot_str)
83
86
static void print_help (void )
84
87
{
85
88
printf ("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n"
86
- "[-z use_zero_pages]\n" );
89
+ "[-z use_zero_pages] [-m merge_across_nodes] \n" );
87
90
88
91
printf ("Supported <test type>:\n"
89
92
" -M (page merging)\n"
90
93
" -Z (zero pages merging)\n"
94
+ " -N (merging of pages in different NUMA nodes)\n"
91
95
" -U (page unmerging)\n\n" );
92
96
93
97
printf (" -a: specify the access protections of pages.\n"
@@ -99,6 +103,8 @@ static void print_help(void)
99
103
" Default: %d seconds\n" , KSM_SCAN_LIMIT_SEC_DEFAULT );
100
104
printf (" -z: change use_zero_pages tunable\n"
101
105
" Default: %d\n" , KSM_USE_ZERO_PAGES_DEFAULT );
106
+ printf (" -m: change merge_across_nodes tunable\n"
107
+ " Default: %d\n" , KSM_MERGE_ACROSS_NODES_DEFAULT );
102
108
103
109
exit (0 );
104
110
}
@@ -339,6 +345,68 @@ static int check_ksm_zero_page_merge(int mapping, int prot, long page_count, int
339
345
return KSFT_FAIL ;
340
346
}
341
347
348
+ static int check_ksm_numa_merge (int mapping , int prot , int timeout , bool merge_across_nodes ,
349
+ size_t page_size )
350
+ {
351
+ void * numa1_map_ptr , * numa2_map_ptr ;
352
+ struct timespec start_time ;
353
+ int page_count = 2 ;
354
+
355
+ if (clock_gettime (CLOCK_MONOTONIC_RAW , & start_time )) {
356
+ perror ("clock_gettime" );
357
+ return KSFT_FAIL ;
358
+ }
359
+
360
+ if (numa_available () < 0 ) {
361
+ perror ("NUMA support not enabled" );
362
+ return KSFT_SKIP ;
363
+ }
364
+ if (numa_max_node () < 1 ) {
365
+ printf ("At least 2 NUMA nodes must be available\n" );
366
+ return KSFT_SKIP ;
367
+ }
368
+ if (ksm_write_sysfs (KSM_FP ("merge_across_nodes" ), merge_across_nodes ))
369
+ return KSFT_FAIL ;
370
+
371
+ /* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */
372
+ numa1_map_ptr = numa_alloc_onnode (page_size , 0 );
373
+ numa2_map_ptr = numa_alloc_onnode (page_size , 1 );
374
+ if (!numa1_map_ptr || !numa2_map_ptr ) {
375
+ perror ("numa_alloc_onnode" );
376
+ return KSFT_FAIL ;
377
+ }
378
+
379
+ memset (numa1_map_ptr , '*' , page_size );
380
+ memset (numa2_map_ptr , '*' , page_size );
381
+
382
+ /* try to merge the pages */
383
+ if (ksm_merge_pages (numa1_map_ptr , page_size , start_time , timeout ) ||
384
+ ksm_merge_pages (numa2_map_ptr , page_size , start_time , timeout ))
385
+ goto err_out ;
386
+
387
+ /*
388
+ * verify that the right number of pages are merged:
389
+ * 1) if merge_across_nodes was enabled, 2 duplicate pages will be merged;
390
+ * 2) if merge_across_nodes = 0, there must be 0 merged pages, since there is
391
+ * only 1 unique page in each node and they can't be shared.
392
+ */
393
+ if (merge_across_nodes && !assert_ksm_pages_count (page_count ))
394
+ goto err_out ;
395
+ else if (!merge_across_nodes && !assert_ksm_pages_count (0 ))
396
+ goto err_out ;
397
+
398
+ numa_free (numa1_map_ptr , page_size );
399
+ numa_free (numa2_map_ptr , page_size );
400
+ printf ("OK\n" );
401
+ return KSFT_PASS ;
402
+
403
+ err_out :
404
+ numa_free (numa1_map_ptr , page_size );
405
+ numa_free (numa2_map_ptr , page_size );
406
+ printf ("Not OK\n" );
407
+ return KSFT_FAIL ;
408
+ }
409
+
342
410
int main (int argc , char * argv [])
343
411
{
344
412
int ret , opt ;
@@ -349,8 +417,9 @@ int main(int argc, char *argv[])
349
417
struct ksm_sysfs ksm_sysfs_old ;
350
418
int test_name = CHECK_KSM_MERGE ;
351
419
bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT ;
420
+ bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT ;
352
421
353
- while ((opt = getopt (argc , argv , "ha:p:l:z:MUZ " )) != -1 ) {
422
+ while ((opt = getopt (argc , argv , "ha:p:l:z:m:MUZN " )) != -1 ) {
354
423
switch (opt ) {
355
424
case 'a' :
356
425
prot = str_to_prot (optarg );
@@ -378,6 +447,12 @@ int main(int argc, char *argv[])
378
447
else
379
448
use_zero_pages = 1 ;
380
449
break ;
450
+ case 'm' :
451
+ if (strcmp (optarg , "0" ) == 0 )
452
+ merge_across_nodes = 0 ;
453
+ else
454
+ merge_across_nodes = 1 ;
455
+ break ;
381
456
case 'M' :
382
457
break ;
383
458
case 'U' :
@@ -386,6 +461,9 @@ int main(int argc, char *argv[])
386
461
case 'Z' :
387
462
test_name = CHECK_KSM_ZERO_PAGE_MERGE ;
388
463
break ;
464
+ case 'N' :
465
+ test_name = CHECK_KSM_NUMA_MERGE ;
466
+ break ;
389
467
default :
390
468
return KSFT_FAIL ;
391
469
}
@@ -423,6 +501,10 @@ int main(int argc, char *argv[])
423
501
ret = check_ksm_zero_page_merge (MAP_PRIVATE | MAP_ANONYMOUS , prot , page_count ,
424
502
ksm_scan_limit_sec , use_zero_pages , page_size );
425
503
break ;
504
+ case CHECK_KSM_NUMA_MERGE :
505
+ ret = check_ksm_numa_merge (MAP_PRIVATE | MAP_ANONYMOUS , prot , ksm_scan_limit_sec ,
506
+ merge_across_nodes , page_size );
507
+ break ;
426
508
}
427
509
428
510
if (ksm_restore (& ksm_sysfs_old )) {
0 commit comments