linux/tools/testing/selftests/arm64/mte/check_ksm_options.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (C) 2020 ARM Limited
   3
   4#define _GNU_SOURCE
   5
   6#include <errno.h>
   7#include <fcntl.h>
   8#include <signal.h>
   9#include <stdio.h>
  10#include <stdlib.h>
  11#include <string.h>
  12#include <ucontext.h>
  13#include <sys/mman.h>
  14
  15#include "kselftest.h"
  16#include "mte_common_util.h"
  17#include "mte_def.h"
  18
  19#define TEST_UNIT       10
  20#define PATH_KSM        "/sys/kernel/mm/ksm/"
  21#define MAX_LOOP        4
  22
  23static size_t page_sz;
  24static unsigned long ksm_sysfs[5];
  25
  26static unsigned long read_sysfs(char *str)
  27{
  28        FILE *f;
  29        unsigned long val = 0;
  30
  31        f = fopen(str, "r");
  32        if (!f) {
  33                ksft_print_msg("ERR: missing %s\n", str);
  34                return 0;
  35        }
  36        if (fscanf(f, "%lu", &val) != 1) {
  37                ksft_print_msg("ERR: parsing %s\n", str);
  38                val = 0;
  39        }
  40        fclose(f);
  41        return val;
  42}
  43
  44static void write_sysfs(char *str, unsigned long val)
  45{
  46        FILE *f;
  47
  48        f = fopen(str, "w");
  49        if (!f) {
  50                ksft_print_msg("ERR: missing %s\n", str);
  51                return;
  52        }
  53        fprintf(f, "%lu", val);
  54        fclose(f);
  55}
  56
  57static void mte_ksm_setup(void)
  58{
  59        ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes");
  60        write_sysfs(PATH_KSM "merge_across_nodes", 1);
  61        ksm_sysfs[1] = read_sysfs(PATH_KSM "sleep_millisecs");
  62        write_sysfs(PATH_KSM "sleep_millisecs", 0);
  63        ksm_sysfs[2] = read_sysfs(PATH_KSM "run");
  64        write_sysfs(PATH_KSM "run", 1);
  65        ksm_sysfs[3] = read_sysfs(PATH_KSM "max_page_sharing");
  66        write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3] + TEST_UNIT);
  67        ksm_sysfs[4] = read_sysfs(PATH_KSM "pages_to_scan");
  68        write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4] + TEST_UNIT);
  69}
  70
  71static void mte_ksm_restore(void)
  72{
  73        write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]);
  74        write_sysfs(PATH_KSM "sleep_millisecs", ksm_sysfs[1]);
  75        write_sysfs(PATH_KSM "run", ksm_sysfs[2]);
  76        write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3]);
  77        write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4]);
  78}
  79
  80static void mte_ksm_scan(void)
  81{
  82        int cur_count = read_sysfs(PATH_KSM "full_scans");
  83        int scan_count = cur_count + 1;
  84        int max_loop_count = MAX_LOOP;
  85
  86        while ((cur_count < scan_count) && max_loop_count) {
  87                sleep(1);
  88                cur_count = read_sysfs(PATH_KSM "full_scans");
  89                max_loop_count--;
  90        }
  91#ifdef DEBUG
  92        ksft_print_msg("INFO: pages_shared=%lu pages_sharing=%lu\n",
  93                        read_sysfs(PATH_KSM "pages_shared"),
  94                        read_sysfs(PATH_KSM "pages_sharing"));
  95#endif
  96}
  97
  98static int check_madvise_options(int mem_type, int mode, int mapping)
  99{
 100        char *ptr;
 101        int err, ret;
 102
 103        err = KSFT_FAIL;
 104        if (access(PATH_KSM, F_OK) == -1) {
 105                ksft_print_msg("ERR: Kernel KSM config not enabled\n");
 106                return err;
 107        }
 108
 109        mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
 110        ptr = mte_allocate_memory(TEST_UNIT * page_sz, mem_type, mapping, true);
 111        if (check_allocated_memory(ptr, TEST_UNIT * page_sz, mem_type, false) != KSFT_PASS)
 112                return KSFT_FAIL;
 113
 114        /* Insert same data in all the pages */
 115        memset(ptr, 'A', TEST_UNIT * page_sz);
 116        ret = madvise(ptr, TEST_UNIT * page_sz, MADV_MERGEABLE);
 117        if (ret) {
 118                ksft_print_msg("ERR: madvise failed to set MADV_UNMERGEABLE\n");
 119                goto madvise_err;
 120        }
 121        mte_ksm_scan();
 122        /* Tagged pages should not merge */
 123        if ((read_sysfs(PATH_KSM "pages_shared") < 1) ||
 124            (read_sysfs(PATH_KSM "pages_sharing") < (TEST_UNIT - 1)))
 125                err = KSFT_PASS;
 126madvise_err:
 127        mte_free_memory(ptr, TEST_UNIT * page_sz, mem_type, true);
 128        return err;
 129}
 130
 131int main(int argc, char *argv[])
 132{
 133        int err;
 134
 135        err = mte_default_setup();
 136        if (err)
 137                return err;
 138        page_sz = getpagesize();
 139        if (!page_sz) {
 140                ksft_print_msg("ERR: Unable to get page size\n");
 141                return KSFT_FAIL;
 142        }
 143        /* Register signal handlers */
 144        mte_register_signal(SIGBUS, mte_default_handler);
 145        mte_register_signal(SIGSEGV, mte_default_handler);
 146
 147        /* Set test plan */
 148        ksft_set_plan(4);
 149
 150        /* Enable KSM */
 151        mte_ksm_setup();
 152
 153        evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE),
 154                "Check KSM mte page merge for private mapping, sync mode and mmap memory\n");
 155        evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE),
 156                "Check KSM mte page merge for private mapping, async mode and mmap memory\n");
 157        evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED),
 158                "Check KSM mte page merge for shared mapping, sync mode and mmap memory\n");
 159        evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED),
 160                "Check KSM mte page merge for shared mapping, async mode and mmap memory\n");
 161
 162        mte_ksm_restore();
 163        mte_restore_setup();
 164        ksft_print_cnts();
 165        return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
 166}
 167