From nobody Wed Oct 1 20:34:19 2025 Received: from mxct.zte.com.cn (mxct.zte.com.cn [58.251.27.85]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6FE343090CA; Wed, 1 Oct 2025 14:58:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=58.251.27.85 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759330732; cv=none; b=d1kIIAhU6mZtZ6eaHPo8M8k+Aj8uLZxV8hSm5eYCU7Y2JPHO8xBb1v4XdpqoZRJ6d+td+drd6N3FAlB7M6gb2SALnPuKy80xw+XKX3tXA+t367tIXYoAyZInjehIRqfVrCfWxucn3/v7HW3UlUtkzCdP/RkOGE5xxI5k27GvnpE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759330732; c=relaxed/simple; bh=Ko8ha58nvsDBW8Rqrv4TyRT0SHuuRWXpAxpfN3bgFVY=; h=Date:Message-ID:In-Reply-To:References:Mime-Version:From:To:Cc: Subject:Content-Type; b=qaf13BygYCIzqLWKwcXM7SFW4MGwakv3ip+l4iVvPFfvqDr5uOPsNQ7l1lRniGCWBeqMk24m8wXYQE04iAq0zpK8AuDr2D4KF4RjnAm2cDWIslRM3sFlrpehZsZLxlzRNF9Mne56yTeKN+k6Pok9ooDInM7hB4uMJowQeZHFRXM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn; spf=pass smtp.mailfrom=zte.com.cn; arc=none smtp.client-ip=58.251.27.85 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zte.com.cn Received: from mxde.zte.com.cn (unknown [10.35.20.121]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mxct.zte.com.cn (FangMail) with ESMTPS id 4ccJ385WtRz1Dxn; Wed, 01 Oct 2025 22:58:44 +0800 (CST) Received: from mxhk.zte.com.cn (unknown [192.168.250.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mxde.zte.com.cn (FangMail) with ESMTPS id 4ccJ2z1knQzBQkJq; Wed, 01 Oct 2025 22:58:35 +0800 (CST) Received: from mse-fl2.zte.com.cn (unknown [10.5.228.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mxhk.zte.com.cn (FangMail) with ESMTPS id 4ccJ2p3H10z5PM3G; Wed, 01 Oct 2025 22:58:26 +0800 (CST) Received: from xaxapp05.zte.com.cn ([10.99.98.109]) by mse-fl2.zte.com.cn with SMTP id 591EwIHk093105; Wed, 1 Oct 2025 22:58:18 +0800 (+08) (envelope-from xu.xin16@zte.com.cn) Received: from mapi (xaxapp05[null]) by mapi (Zmail) with MAPI id mid32; Wed, 1 Oct 2025 22:58:19 +0800 (CST) Date: Wed, 1 Oct 2025 22:58:19 +0800 (CST) X-Zmail-TransId: 2afc68dd418b3ca-061c6 X-Mailer: Zmail v1.0 Message-ID: <202510012258194755dOoRXl-9afv5zIk0QwO_@zte.com.cn> In-Reply-To: <202510012256278259zrhgATlLA2C510DMD3qI@zte.com.cn> References: 202510012256278259zrhgATlLA2C510DMD3qI@zte.com.cn Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 From: To: , Cc: , , , , , , Subject: =?UTF-8?B?W1BBVENIIGxpbnV4LW5leHQgMS8yXSB0b29sczogYWRkIGtzbS11dGlscyB0b29scw==?= X-MAIL: mse-fl2.zte.com.cn 591EwIHk093105 X-TLS: YES X-SPF-DOMAIN: zte.com.cn X-ENVELOPE-SENDER: xu.xin16@zte.com.cn X-SPF: None X-SOURCE-IP: 10.35.20.121 unknown Wed, 01 Oct 2025 22:58:45 +0800 X-Fangmail-Anti-Spam-Filtered: true X-Fangmail-MID-QID: 68DD41A3.000/4ccJ385WtRz1Dxn Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: xu xin The ksm-utils contains two tools which are ksm-get and ksm-show. The tools are introduced to easily control a process'KSM enabling or display the ksm state of processes by PID. 1) enable/disable a process's all anonymous VMAs to be scanned by KSM ksm-set -s on [...] ksm-set -s off [...] 2) inquiry the ksm_state of a process or all process ksm pages. ksm-get -a -e # Get all processes's KSM status ksm-get -p pid # Get the specified processs' KSM status 3) Other detailed information, please try ksm-set -h or ksm-get -h Signed-off-by: xu xin --- tools/mm/Makefile | 12 +- tools/mm/ksm-utils/Makefile | 10 + tools/mm/ksm-utils/ksm-get.c | 397 +++++++++++++++++++++++++++++++++++ tools/mm/ksm-utils/ksm-set.c | 144 +++++++++++++ 4 files changed, 560 insertions(+), 3 deletions(-) create mode 100644 tools/mm/ksm-utils/Makefile create mode 100644 tools/mm/ksm-utils/ksm-get.c create mode 100644 tools/mm/ksm-utils/ksm-set.c diff --git a/tools/mm/Makefile b/tools/mm/Makefile index f5725b5c23aa..7a9ba08548a3 100644 --- a/tools/mm/Makefile +++ b/tools/mm/Makefile @@ -3,7 +3,8 @@ # include ../scripts/Makefile.include -BUILD_TARGETS=3Dpage-types slabinfo page_owner_sort thp_swap_allocator_test +BUILD_TARGETS=3Dpage-types slabinfo page_owner_sort thp_swap_allocator_tes= t \ + ksm-utils INSTALL_TARGETS =3D $(BUILD_TARGETS) thpmaps LIB_DIR =3D ../lib/api @@ -12,7 +13,7 @@ LIBS =3D $(LIB_DIR)/libapi.a CFLAGS +=3D -Wall -Wextra -I../lib/ -pthread LDFLAGS +=3D $(LIBS) -pthread -all: $(BUILD_TARGETS) +all: $(BUILD_TARGETS) ksm-utils $(BUILD_TARGETS): $(LIBS) @@ -22,10 +23,15 @@ $(LIBS): %: %.c $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -clean: +clean: ksm-utils_clean $(RM) page-types slabinfo page_owner_sort thp_swap_allocator_test make -C $(LIB_DIR) clean +ksm-utils: + $(call descend,ksm-utils) +ksm-utils_clean: + $(call descend,ksm-utils,clean) + sbindir ?=3D /usr/sbin install: all diff --git a/tools/mm/ksm-utils/Makefile b/tools/mm/ksm-utils/Makefile new file mode 100644 index 000000000000..08eb7c38ee99 --- /dev/null +++ b/tools/mm/ksm-utils/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +CC :=3D $(CROSS_COMPILE)gcc +CFLAGS :=3D -I../../../usr/include + +PROGS :=3D ksm-get ksm-set + +all: $(PROGS) + +clean: + rm -fr $(PROGS) diff --git a/tools/mm/ksm-utils/ksm-get.c b/tools/mm/ksm-utils/ksm-get.c new file mode 100644 index 000000000000..6ae4e8e14126 --- /dev/null +++ b/tools/mm/ksm-utils/ksm-get.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ksm-get: Tool for acquire KSM-merging metrics for processes. + * + * Copyright (C) 2025 ZTE corporation + * + * Authors: xu xin + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INVALID_PID -1 +#define MAX_FILE_NAME_SIZE 64 +#define COMM_MAX_SIZE 16 +#define MAX_PROCESSES 65536 + +/* Enum option for sorting*/ +typedef enum { + SORT_MERGING_PAGES, /* Default, by ksm_merging_pages*/ + SORT_PROFIT +} sort_field_t; + +typedef struct { + int pid; + char comm[COMM_MAX_SIZE]; + long ksm_merging_pages; + long ksm_zero_pages; + long ksm_profit; + long ksm_rmap_items; + int KSM_mergeable; + int KSM_merge_any; + int valid; /* indicate if the data is valid */ +} ksm_info_t; + +int pid =3D INVALID_PID; +int lookup_all_pid; +int need_extend_info; +sort_field_t sort_field =3D SORT_MERGING_PAGES; + +static void usage(void) +{ + fprintf(stderr, "Usage: ksm-show [-a] [-p pid] [--sort field] [-e]\n\n"); + printf("Show KSM merging information of processes.\n\n" + "Get KSM merging information of a specific process:\n" + " ksm-show -p pid\n\n" + "Get KSM merging information of all processes:\n" + " ksm-show -a\n\n" + "Options:\n" + "-a, --all show all processes (default sort by merging_page= s)\n" + "-p, --pid [pid] show specific process\n" + "--sort [field] sort field: merging_pages or profit\n" + "-e display extended information\n" + "-h, --help show this help\n\n" + "Default columns: Pid, Comm, Merging_pages, Ksm_zero_pages, Ksm_profit\n" + ); +} + +static inline bool pid_is_set(void) +{ + return (pid !=3D INVALID_PID); +} + +static int check_arguments(void) +{ + if (pid_is_set() && lookup_all_pid) { + fprintf(stderr, "error: Options -a and -p cannot be used together.\n"); + return -EINVAL; + } + + if (!pid_is_set() && !lookup_all_pid) { + fprintf(stderr, "error: Either -a or -p must be specified.\n"); + return -EINVAL; + } + + return 0; +} + +static void print_header(void) +{ + printf("%-12s", "Pid"); + printf("%-20s", "Comm"); + printf("%-15s", "Merging_pages"); + printf("%-18s", "Ksm_zero_pages"); + printf("%-15s", "Ksm_profit"); + if (need_extend_info) { + printf("%-18s", "Ksm_mergeable"); + printf("%-18s", "Ksm_merge_any"); + } + printf("\n"); +} + +static long parse_proc_ksm_stat(char *buf, char *field) +{ + char *substr; + size_t value_pos; + + substr =3D strstr(buf, field); + if (!substr) + return -1; + + if (!strcmp(field, "ksm_mergeable") || + !strcmp(field, "ksm_merge_any")) { + if (!strncmp(substr + strlen(field) + 2, "yes", 3)) + return 1; + else + return 0; + } + + value_pos =3D strcspn(substr, "0123456789"); + return strtol(substr + value_pos, NULL, 10); +} + +static void get_pid_comm(int this_pid, char *comm, int len) +{ + int comm_fd, read_size; + char proc_comm_name[MAX_FILE_NAME_SIZE]; + + snprintf(proc_comm_name, MAX_FILE_NAME_SIZE, "/proc/%d/comm", this_pid); + comm_fd =3D open(proc_comm_name, O_RDONLY); + if (comm_fd < 0) + return; + + read_size =3D pread(comm_fd, comm, len - 1, 0); + close(comm_fd); + + if (read_size <=3D 0) + return; + + /* make sure string end with \0 */ + if (comm[read_size - 1] =3D=3D '\n') + comm[read_size - 1] =3D '\0'; + else if (read_size < len - 1) + comm[read_size] =3D '\0'; + else + comm[len - 1] =3D '\0'; +} + +static int get_pid_ksm_info(int this_pid, ksm_info_t *info) +{ + int proc_fd, read_size; + char proc_name[MAX_FILE_NAME_SIZE]; + char buf[256]; + + memset(info, 0, sizeof(ksm_info_t)); + info->pid =3D this_pid; + info->valid =3D 0; + + get_pid_comm(this_pid, info->comm, COMM_MAX_SIZE); + snprintf(proc_name, MAX_FILE_NAME_SIZE, "/proc/%d/ksm_stat", this_pid); + + proc_fd =3D open(proc_name, O_RDONLY); + /* ksm_stat doesn't exist, maybe kthread or CONFIG_KSM disabled. */ + if (proc_fd < 0) + return -1; + + read_size =3D pread(proc_fd, buf, sizeof(buf) - 1, 0); + close(proc_fd); + + if (read_size <=3D 0) + return -1; + + + buf[read_size] =3D 0; + + info->ksm_merging_pages =3D parse_proc_ksm_stat(buf, "ksm_merging_pages"); + info->ksm_zero_pages =3D parse_proc_ksm_stat(buf, "ksm_zero_pages"); + info->ksm_profit =3D parse_proc_ksm_stat(buf, "ksm_process_profit"); + info->ksm_rmap_items =3D parse_proc_ksm_stat(buf, "ksm_rmap_items"); + info->KSM_mergeable =3D parse_proc_ksm_stat(buf, "ksm_mergeable"); + info->KSM_merge_any =3D parse_proc_ksm_stat(buf, "ksm_merge_any"); + + if (info->ksm_merging_pages < 0 || info->ksm_profit < 0) + return -1; + + info->valid =3D 1; + return 0; +} + +static void print_ksm_info(ksm_info_t *info) +{ + if (!info->valid) { + printf("%-12d", info->pid); + printf("%-20s", info->comm); + printf("%-15s", "N/A"); + printf("%-18s", "N/A"); + printf("%-15s", "N/A"); + printf("\n"); + return; + } + + printf("%-12d", info->pid); + printf("%-20s", info->comm); + printf("%-15ld", info->ksm_merging_pages); + printf("%-18ld", info->ksm_zero_pages); + printf("%-15ld", info->ksm_profit); + if (need_extend_info) { + printf("%-18s", info->KSM_mergeable >=3D 0 ? + (info->KSM_mergeable ? "yes" : "no") : "N/A"); + printf("%-18s", info->KSM_merge_any >=3D 0 ? + (info->KSM_merge_any ? "yes" : "no") : "N/A"); + } + printf("\n"); +} + +/* sort by ksm_merging_pages in descending order */ +static int compare_by_merging_pages(const void *a, const void *b) +{ + const ksm_info_t *info_a =3D (const ksm_info_t *)a; + const ksm_info_t *info_b =3D (const ksm_info_t *)b; + + /* The valid data is put at first */ + if (info_a->valid && !info_b->valid) + return -1; + if (!info_a->valid && info_b->valid) + return 1; + if (!info_a->valid && !info_b->valid) + return 0; + + /* list in descending order */ + if (info_a->ksm_merging_pages > info_b->ksm_merging_pages) + return -1; + if (info_a->ksm_merging_pages < info_b->ksm_merging_pages) + return 1; + + return 0; +} + +/* sort by ksm_profit in descending order */ +static int compare_by_profit(const void *a, const void *b) +{ + const ksm_info_t *info_a =3D (const ksm_info_t *)a; + const ksm_info_t *info_b =3D (const ksm_info_t *)b; + + /* The valid data is put at first */ + if (info_a->valid && !info_b->valid) + return -1; + if (!info_a->valid && info_b->valid) + return 1; + if (!info_a->valid && !info_b->valid) + return 0; + + /* list in descending order */ + if (info_a->ksm_profit > info_b->ksm_profit) + return -1; + if (info_a->ksm_profit < info_b->ksm_profit) + return 1; + + return 0; +} + +static int collect_all_ksm_info(ksm_info_t *infos, int max_infos) +{ + DIR *dir; + struct dirent *entry; + int this_pid; + int count =3D 0; + + dir =3D opendir("/proc"); + if (!dir) { + perror("cannot open /proc"); + return -1; + } + + while ((entry =3D readdir(dir)) !=3D NULL && count < max_infos) { + /* Check if the dir name is digital (process dir) */ + if (isdigit(entry->d_name[0])) + if (sscanf(entry->d_name, "%d", &this_pid) =3D=3D 1) + if (get_pid_ksm_info(this_pid, &infos[count]) =3D=3D 0) + count++; + } + + closedir(dir); + return count; +} + +static void show_sorted_ksm_stat(void) +{ + ksm_info_t *infos; + int count, i; + + infos =3D malloc(MAX_PROCESSES * sizeof(ksm_info_t)); + if (!infos) { + perror("malloc failed"); + return; + } + + count =3D collect_all_ksm_info(infos, MAX_PROCESSES); + if (count < 0) { + free(infos); + return; + } + + /* pick the sort function by sort filed */ + if (sort_field =3D=3D SORT_MERGING_PAGES) + qsort(infos, count, sizeof(ksm_info_t), compare_by_merging_pages); + else if (sort_field =3D=3D SORT_PROFIT) + qsort(infos, count, sizeof(ksm_info_t), compare_by_profit); + + for (i =3D 0; i < count; i++) + print_ksm_info(&infos[i]); + + free(infos); +} + +static void show_single_ksm_stat(void) +{ + ksm_info_t info; + + if (get_pid_ksm_info(pid, &info) =3D=3D 0) + print_ksm_info(&info); + else + fprintf(stderr, "Error: Cannot get KSM info for pid %d\n", pid); +} + +int main(int argc, char **argv) +{ + int err; + int opt; + int option_index =3D 0; + + // Define long-option + static struct option long_options[] =3D { + {"all", no_argument, 0, 'a'}, + {"pid", required_argument, 0, 'p'}, + {"sort", required_argument, 0, 's'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + + if (argc =3D=3D 1) { + usage(); + return 1; + } + + /* Parse the arguments */ + while ((opt =3D getopt_long(argc, argv, "ap:s:eh", long_options, &option_= index)) !=3D -1) { + switch (opt) { + case 'a': + lookup_all_pid =3D 1; + break; + case 'p': + if (sscanf(optarg, "%d", &pid) !=3D 1) { + fprintf(stderr, "Invalid argument for -p: %s\n", optarg); + return 1; + } + break; + case 's': // sort option + if (strcmp(optarg, "merging_pages") =3D=3D 0) { + sort_field =3D SORT_MERGING_PAGES; + } else if (strcmp(optarg, "profit") =3D=3D 0) { + sort_field =3D SORT_PROFIT; + } else { + fprintf(stderr, "Error sort field: %s. Use merging_pages or profit\n", + optarg); + return 1; + } + break; + case 'e': + need_extend_info =3D 1; + break; + case 'h': + usage(); + return 0; + default: + usage(); + return 1; + } + } + + /* Chech if there is unknown argument.*/ + if (optind < argc) { + fprintf(stderr, "Unexpected argument: %s\n", argv[optind]); + usage(); + return 1; + } + + err =3D check_arguments(); + if (err < 0) + return -EINVAL; + + print_header(); + if (lookup_all_pid) + show_sorted_ksm_stat(); + else + show_single_ksm_stat(); + + return 0; +} diff --git a/tools/mm/ksm-utils/ksm-set.c b/tools/mm/ksm-utils/ksm-set.c new file mode 100644 index 000000000000..7ca8e459d256 --- /dev/null +++ b/tools/mm/ksm-utils/ksm-set.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ksm-set: Tool for enabling/disabling KSM-merging for a process. + * + * Copyright (C) 2024 ZTE corporation + * + * Authors: xu xin + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define INVALID_PID -1 +#define KSM_ENABLE_UNSET -1 + +char **command; +int ksm_enable =3D KSM_ENABLE_UNSET; +int pid =3D INVALID_PID; + +static inline bool command_is_set(void) +{ + return (!!command); +} + +static inline bool ksm_enable_is_set(void) +{ + return (ksm_enable !=3D KSM_ENABLE_UNSET); +} + +static void usage(void) +{ + fprintf(stderr, "Usage: ksm-set -s [on|off] [ [...]]\n\n"); + printf("Change the KSM merging attributes of processes.\n\n" + "Enable/disable KSM merging any anonymous VMA when starting a new proc= ess:\n" + " ksm-set -s [on|off] [...]\n\n" + "Options:\n" + "-s [on|off] enable or disable KSM merging\n" + "-h,--help show this help\n\n" + ); +} + +static int check_arguments(void) +{ + if (!ksm_enable_is_set()) { + fprintf(stderr, "error: Option -s is required.\n"); + return -EINVAL; + } + + if (!command_is_set()) { + fprintf(stderr, "error: Command must be specified.\n"); + return -EINVAL; + } + + return 0; +} + +int main(int argc, char **argv) +{ + int index, nr_cmd_args, err; + char *buffer =3D NULL; + + if (argc =3D=3D 1) { + usage(); + return 1; + } + + /* Parsing the argument*/ + for (index =3D 1; index < argc; index++) { + if (argv[index][0] =3D=3D '-') { + switch (argv[index][1]) { + case 'p': + if (index >=3D argc - 1) { + fprintf(stderr, "Invalid argument for -p\n"); + return 1; + } + if (sscanf(argv[index + 1], "%d", &pid) !=3D 1) { + fprintf(stderr, "Invalid argument for -p\n"); + return 1; + } + index++; + break; + case 's': + if (index >=3D argc - 1) { + fprintf(stderr, "Invalid argument for -s\n"); + return -EINVAL; + } + buffer =3D argv[index + 1]; + if (strcmp(buffer, "on") =3D=3D 0) + ksm_enable =3D 1; + else if (strcmp(buffer, "off") =3D=3D 0) + ksm_enable =3D 0; + else { + fprintf(stderr, "Invalid argument for-s: must be 'on' or 'off'\n"); + return -EINVAL; + } + index++; + break; + case 'h': + usage(); + return 0; + default: + fprintf(stderr, "Unknown option: %s\n", argv[index]); + usage(); + return 1; + } + } else { + /* + * The remained arguments is seen as a command + * with arguments. + */ + command =3D argv + index; + nr_cmd_args =3D argc - index; + break; + } + } + + err =3D check_arguments(); + if (err < 0) + return -EINVAL; + + printf("KSM %s: ", ksm_enable ? "enabled" : "disabled"); + for (index =3D 0; index < nr_cmd_args; index++) + printf("%s ", command[index]); + printf("\n"); + + err =3D prctl(PR_SET_MEMORY_MERGE, ksm_enable, 0, 0, 0); + if (err !=3D 0) { + perror("prctl PR_SET_MEMORY_MERGE failed"); + return -errno; + } + + execvp(command[0], command); + perror("execvp failed"); + return -errno; + + return 0; +} --=20 2.25.1 From nobody Wed Oct 1 20:34:19 2025 Received: from mxhk.zte.com.cn (mxhk.zte.com.cn [160.30.148.34]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9AFD1274B2B; Wed, 1 Oct 2025 15:00:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=160.30.148.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759330839; cv=none; b=qII56hyguR64DcHbTDcCXuWGS99Za4rDJt3EQp6wwSrkY6VcoWwMUQO27Dd5fTJLHC3uKIyeYsoDb4edWCHX47CBAH/bj69Ur3+XhrCBOtJr7uRX5/iHuX0+FgTD/7J23H9WBmMuG7kFhDOp+DrkIYAmKsElNulb7O15PfF763Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759330839; c=relaxed/simple; bh=lgPpTuB3gcB4yBO91fExEFcoqC51GI9wHSTCGPHZhSE=; h=Date:Message-ID:In-Reply-To:References:Mime-Version:From:To:Cc: Subject:Content-Type; b=WqE1tljYLwnZfl4bavlqsBkFiarV5A7Ch9vh0+/XEONzSwHa+xOlpemalCE7k4qPeyREHrvE41iOMi9LV/8kjUxSf6Zn+UFO5Ue6/Vg541EmbcNNVY6xRLXB9qOLTIU/aLR59S4Zri56nLxM2SlhMrA6kc2SSfguQOSmvGWFRCQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn; spf=pass smtp.mailfrom=zte.com.cn; arc=none smtp.client-ip=160.30.148.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zte.com.cn Received: from mse-fl1.zte.com.cn (unknown [10.5.228.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mxhk.zte.com.cn (FangMail) with ESMTPS id 4ccJ5G4dFvz5BNRd; Wed, 01 Oct 2025 23:00:34 +0800 (CST) Received: from xaxapp01.zte.com.cn ([10.88.99.176]) by mse-fl1.zte.com.cn with SMTP id 591F0NDx015186; Wed, 1 Oct 2025 23:00:23 +0800 (+08) (envelope-from xu.xin16@zte.com.cn) Received: from mapi (xaxapp01[null]) by mapi (Zmail) with MAPI id mid32; Wed, 1 Oct 2025 23:00:27 +0800 (CST) Date: Wed, 1 Oct 2025 23:00:27 +0800 (CST) X-Zmail-TransId: 2af968dd420b417-08211 X-Mailer: Zmail v1.0 Message-ID: <20251001230027125gluddf7-yGz-nXN3gvN6z@zte.com.cn> In-Reply-To: <202510012256278259zrhgATlLA2C510DMD3qI@zte.com.cn> References: 202510012256278259zrhgATlLA2C510DMD3qI@zte.com.cn Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 From: To: , Cc: , , , , , , , Subject: =?UTF-8?B?W1BBVENIIGxpbnV4LW5leHQgMi8yXSBtbS9rc206IGZpeCBleGVjL2ZvcmsgaW5oZXJpdGFuY2Ugc3VwcG9ydCBmb3IgcHJjdGw=?= X-MAIL: mse-fl1.zte.com.cn 591F0NDx015186 X-TLS: YES X-SPF-DOMAIN: zte.com.cn X-ENVELOPE-SENDER: xu.xin16@zte.com.cn X-SPF: None X-SOURCE-IP: 10.5.228.132 unknown Wed, 01 Oct 2025 23:00:34 +0800 X-Fangmail-Anti-Spam-Filtered: true X-Fangmail-MID-QID: 68DD4212.000/4ccJ5G4dFvz5BNRd Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: xu xin Background =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D The commit d7597f59d1d33 ("mm: add new api to enable ksm per process") intr= oduce MMF_VM_MERGE_ANY for mm->flags, and allow user to set it by prctl() so that= the process's VMAs are forcely scanned by ksmd. Sequently, the commit 3c6f33b72= 73a ("mm/ksm: support fork/exec for prctl") support inheritsingMMF_VM_MERGE_ANY= flag when a task calls execve(). Lastly, The commit 3a9e567ca45fb ("mm/ksm: fix ksm exec support for prctl") fixed the issue that ksmd doesn'= t scan the mm_struct with MMF_VM_MERGE_ANY by adding the mm_slot to ksm_mm_head in __bprm_mm_init(). Problem =3D=3D=3D=3D=3D=3D=3D In some extreme scenarios, however, this inheritance of MMF_VM_MERGE_ANY du= ring exec/fork can fail. For example, when the scanning frequency of ksmd is tun= ed extremely high, a process carrying MMF_VM_MERGE_ANY may still fail to pass = it to the newly exec'd process. This happens because ksm_execve() is executed too= early in the do_execve flow (prematurely adding the new mm_struct to the ksm_mm_s= lot list). As a result, before do_execve completes, ksmd may have already performed a = scan and found that this new mm_struct has no VM_MERGEABLE VMAs, thus clearing its MMF_VM_MERGE_ANY flag. Consequently, when the new program executes, the flag MMF_VM_MERGE_ANY inheritance fails! Reproduce =3D=3D=3D=3D=3D=3D=3D=3D Prepare ksm-utils in the prerequisite PATCH, and simply do as follows echo 1 > /sys/kernel/mm/ksm/run; echo 2000 > /sys/kernel/mm/ksm/pages_to_scan; echo 0 > /sys/kernel/mm/ksm/sleep_millisecs; ksm-set -s on [NEW_PROGRAM_BIN] & ksm-get -a -e you can see like this: Pid Comm Merging_pages Ksm_zero_pages Ksm_profit= Ksm_mergeable Ksm_merge_any 206 NEW_PROGRAM_BIN 7680 0 30965760 = yes no Note: If the first time don't reproduce the issue, pkill NEW_PROGRAM_BIN and try = run it again. Usually, we can reproduce it in 5 times. Root reason =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D The commit d7597f59d1d33 ("mm: add new api to enable ksm per process") clea= r the flag MMF_VM_MERGE_ANY when ksmd found no VM_MERGEABLE VMAs. Solution =3D=3D=3D=3D=3D=3D=3D=3D Remove the action of clearing MMF_VM_MERGE_ANY when ksmd found no VM_MERGEA= BLE VMAs. because perhaps their mm_struct has just been added to ksm_mm_slot list, an= d its process has not yet officially started running or has not yet performed mma= p/brk to allocate anonymous VMAS. Fixes: 3c6f33b7273a ("mm/ksm: support fork/exec for prctl") Fixes: d7597f59d1d3 ("mm: add new api to enable ksm per process") Signed-off-by: xu xin Cc: stable@vger.kernel.org Cc: Stefan Roesch Cc: David Hildenbrand Cc: Jinjiang Tu Cc: Wang Yaxin --- mm/ksm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mm/ksm.c b/mm/ksm.c index 04019a15b25d..17c7ed7df700 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -2617,8 +2617,14 @@ static struct ksm_rmap_item *scan_get_next_rmap_item= (struct page **page) spin_unlock(&ksm_mmlist_lock); mm_slot_free(mm_slot_cache, mm_slot); + /* + * Only clear MMF_VM_MERGEABLE. We must not clear + * MMF_VM_MERGE_ANY, because for those MMF_VM_MERGE_ANY process, + * perhaps their mm_struct has just been added to ksm_mm_slot + * list, and its process has not yet officially started running + * or has not yet performed mmap/brk to allocate anonymous VMAS. + */ mm_flags_clear(MMF_VM_MERGEABLE, mm); - mm_flags_clear(MMF_VM_MERGE_ANY, mm); mmap_read_unlock(mm); mmdrop(mm); } else { --=20 2.25.