All of lore.kernel.org
 help / color / mirror / Atom feed
From: Li Wang <liwang@redhat.com>
To: ltp@lists.linux.it
Subject: [LTP] [RFC PATCH] mm: rewrite mtest01 with new API
Date: Thu, 28 Feb 2019 15:40:02 +0800	[thread overview]
Message-ID: <20190228074002.14351-1-liwang@redhat.com> (raw)

 Test issue:
   mtest01 start many children to alloc chunck of memory and do write
   page(with -w option), but occasionally some children were killed by
   oom-killer and exit with SIGCHLD signal sending. After the parent
   reciving this SIGCHLD signal it will report FAIL as a test result.

   It seems not a real kernel bug if something just like that, it's
   trying to use 80% of memory and swap. Once it uses most of memory,
   system starts swapping, but the test is likely consuming memory at
   greater rate than kswapd can provide, which eventually triggers OOM.

   ---- FAIL LOG ----
   mtest01     0  TINFO  :  Total memory already used on system = 1027392 kbytes
   mtest01     0  TINFO  :  Total memory used needed to reach maximum = 12715520 kbytes
   mtest01     0  TINFO  :  Filling up 80% of ram which is 11688128 kbytes
   mtest01     1  TFAIL  :  mtest01.c:314: child process exited unexpectedly
   -------------------

 Rewrite changes:
   To make mtest01 more easier to understand, I just rewrite it into
   LTP new API and make a little changes in children behavior.

   * drop the signal SIGCHLD action becasue new API help to check_child_status
   * make child pause itself after finishing their memory allocating/writing
   * parent sends SIGCONT to make children continue and exit
   * decrease the pressure to 50% total ram+swap for testing

Signed-off-by: Li Wang <liwang@redhat.com>
---
 runtest/mm                             |   4 +-
 testcases/kernel/mem/mtest01/mtest01.c | 430 ++++++++++++-------------
 2 files changed, 204 insertions(+), 230 deletions(-)

diff --git a/runtest/mm b/runtest/mm
index a09f39c1e..93180e834 100644
--- a/runtest/mm
+++ b/runtest/mm
@@ -9,8 +9,8 @@ mm02 mmap001
 # repetitive mmapping test.
 # Creates a one page map repetitively for one minute.
 
-mtest01 mtest01 -p80
-mtest01w mtest01 -p80 -w
+mtest01 mtest01 -p50
+mtest01w mtest01 -p50 -w
 
 #test for race conditions
 mtest05   mmstress
diff --git a/testcases/kernel/mem/mtest01/mtest01.c b/testcases/kernel/mem/mtest01/mtest01.c
index ca9073a8e..1bda92499 100644
--- a/testcases/kernel/mem/mtest01/mtest01.c
+++ b/testcases/kernel/mem/mtest01/mtest01.c
@@ -1,36 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
+ *  Copyright (c) International Business Machines Corp., 2001
+ *  Copyright (c) Linux Test Project., 2019
  *
- *   Copyright (c) International Business Machines  Corp., 2001
+ *  DESCRIPTION:
  *
- *   This program is free software;  you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
+ *  mtest01 mallocs memory <chunksize> at a time until malloc fails.
  *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
- *   the GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program;  if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- *  FILE		: mtest01.c
- *  DESCRIPTION : mallocs memory <chunksize> at a time until malloc fails.
- *  HISTORY:
- *	04/10/2001 Paul Larson (plars@us.ibm.com)
- *	  written
- *	11/09/2001 Manoj Iyer (manjo@austin.ibm.com)
- *	  Modified.
- *	  - Removed compile warnings.
- *	  - Added header file #include <unistd.h> definition for getopt()
- *	05/13/2003 Robbie Williamson (robbiew@us.ibm.com)
- *	  Modified.
- *	  - Rewrote the test to be able to execute on large memory machines.
+ *  Parent process starts several child processes (each child process is
+ *  tasked with allocating some amount of memory), it waits until all child
+ *  processes send SIGRTMIN signal or until the amount of free memory is
+ *  decreased by the amount that all child tasks together should allocate,
+ *  it resumes all children by sending the SIGCONT signal.
  *
+ *  Child process allocates certain amount of memory and fills it with some
+ *  data (the '-w' option) so the pages are actually allocated when the desired
+ *  amount of memory is allocated then it sends SIGRTMIN signal to the parent
+ *  process, it pauses itself by raise SIGSTOP until get parent SIGCONT signal
+ *  to continue and exit.
  */
 
 #include <sys/types.h>
@@ -42,111 +29,73 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-#include "test.h"
+#include "tst_test.h"
 
-#define FIVE_HUNDRED_MB (unsigned long long)(500*1024*1024)
-#define ONE_GB	(unsigned long long)(1024*1024*1024)
-#define THREE_GB (unsigned long long)(3*ONE_GB)
+#define FIVE_HUNDRED_MB	(unsigned long long)(500*1024*1024)
+#define ONE_GB		(unsigned long long)(1024*1024*1024)
+#define THREE_GB	(unsigned long long)(3*ONE_GB)
 
-char *TCID = "mtest01";
-int TST_TOTAL = 1;
-static sig_atomic_t pid_count;
-static sig_atomic_t sigchld_count;
 static pid_t *pid_list;
+static sig_atomic_t pid_count;
+static struct sysinfo sstats;
+static int max_pids;
+static unsigned long long alloc_bytes;
+static unsigned long long alloc_maxbytes;
+static unsigned long long total_ram;
+static unsigned long long total_free;
+static unsigned long long D, C;
+static unsigned long long original_maxbytes;
+static unsigned long long pre_mem = 0, post_mem = 0;
 
-static void handler(int signo)
-{
-	if (signo == SIGCHLD)
-		sigchld_count++;
-	pid_count++;
-}
-
-static void cleanup(void)
-{
-	int i = 0;
-
-	while (pid_list[i] > 0) {
-		kill(pid_list[i], SIGKILL);
-		i++;
-	}
+static int chunksize = 1024*1024;
+static int maxpercent = 20;
+static long maxbytes = 0;
+static char *dowrite;
+static char *verbose;
 
-	free(pid_list);
-}
+static char *opt_chunksize, *opt_maxbytes, *opt_maxpercent;
+static struct tst_option mtest_options[] = {
+	{"c:", &opt_chunksize,	"-c  size of chunk in bytes to malloc on each pass"},
+	{"b:", &opt_maxbytes,	"-b  maximum number of bytes to allocate before stopping"},
+	{"p:", &opt_maxpercent, "-u  percent of total memory used at which the program stops"},
+	{"w",  &dowrite,   	"-w  write to the memory after allocating"},
+	{"v",  &verbose,     	"-v  verbose"},
+	{NULL, NULL, 		NULL}
+};
 
-int main(int argc, char *argv[])
+static void parse_mtest_options(char *str_chunksize, int *chunksize,
+		char *str_maxbytes, long *maxbytes,
+		char *str_maxpercent, int *maxpercent)
 {
-	int c;
-	char *mem;
-	float percent;
-	unsigned int maxpercent = 0, dowrite = 0, verbose = 0, j;
-	unsigned long bytecount, alloc_bytes, max_pids;
-	unsigned long long original_maxbytes, maxbytes = 0;
-	unsigned long long pre_mem = 0, post_mem = 0;
-	unsigned long long total_ram, total_free, D, C;
-	int chunksize = 1024 * 1024;	/* one meg at a time by default */
-	struct sysinfo sstats;
-	int i, pid_cntr;
-	pid_t pid;
-	struct sigaction act;
+	if (str_chunksize)
+		if (tst_parse_int(str_chunksize, chunksize, 1, INT_MAX))
+			tst_brk(TBROK, "Invalid chunksize '%s'", str_chunksize);
 
-	act.sa_handler = handler;
-	act.sa_flags = 0;
-	sigemptyset(&act.sa_mask);
-	sigaction(SIGRTMIN, &act, 0);
-	sigaction(SIGCHLD, &act, 0);
+	if (str_maxbytes) {
+		if (tst_parse_long(str_maxbytes, maxbytes, 1, LONG_MAX)) {
+			tst_brk(TBROK, "Invalid maxbytes '%s'", str_maxbytes);
+		} else if (str_maxpercent) {
+			tst_brk(TBROK, "ERROR: -b option cannot be used with -p "
+					"option at the same time");
+		}
+		alloc_maxbytes = (unsigned long long)maxbytes;
+	}
 
-	while ((c = getopt(argc, argv, "c:b:p:wvh")) != -1) {
-		switch (c) {
-		case 'c':
-			chunksize = atoi(optarg);
-			break;
-		case 'b':
-			if (maxpercent != 0)
-				tst_brkm(TBROK, NULL,
-					 "ERROR: -b option cannot be used with -p "
-					 "option at the same time");
-			maxbytes = atoll(optarg);
-			break;
-		case 'p':
-			if (maxbytes != 0)
-				tst_brkm(TBROK, NULL,
-					 "ERROR: -p option cannot be used with -b "
-					 "option at the same time");
-			maxpercent = atoi(optarg);
-			if (maxpercent <= 0)
-				tst_brkm(TBROK, NULL,
-					 "ERROR: -p option requires number greater "
-					 "than 0");
-			if (maxpercent > 99)
-				tst_brkm(TBROK, NULL,
-					 "ERROR: -p option cannot be greater than "
-					 "99");
-			break;
-		case 'w':
-			dowrite = 1;
-			break;
-		case 'v':
-			verbose = 1;
-			break;
-		case 'h':
-		default:
-			printf
-			    ("usage: %s [-c <bytes>] [-b <bytes>|-p <percent>] [-v]\n",
-			     argv[0]);
-			printf
-			    ("\t-c <num>\tsize of chunk in bytes to malloc on each pass\n");
-			printf
-			    ("\t-b <bytes>\tmaximum number of bytes to allocate before stopping\n");
-			printf
-			    ("\t-p <bytes>\tpercent of total memory used at which the program stops\n");
-			printf
-			    ("\t-w\t\twrite to the memory after allocating\n");
-			printf("\t-v\t\tverbose\n");
-			printf("\t-h\t\tdisplay usage\n");
-			exit(1);
+	if (str_maxpercent) {
+		if (tst_parse_int(str_maxpercent, maxpercent, 1, 99)) {
+			tst_brk(TBROK, "Invalid maxpercent '%s'", str_maxpercent);
+		} else if (str_maxbytes) {
+			tst_brk(TBROK, "ERROR: -p option cannot be used with -b "
+					"option at the same time");
 		}
 	}
+}
 
+static void setup(void)
+{
+	parse_mtest_options(opt_chunksize, &chunksize,
+			opt_maxbytes, &maxbytes,
+			opt_maxpercent, &maxpercent);
 	sysinfo(&sstats);
 	total_ram = sstats.totalram + sstats.totalswap;
 	total_free = sstats.freeram + sstats.freeswap;
@@ -154,173 +103,198 @@ int main(int argc, char *argv[])
 	pre_mem = sstats.mem_unit * total_free;
 	max_pids = total_ram * sstats.mem_unit
 		/ (unsigned long)FIVE_HUNDRED_MB + 10;
-
 	if ((pid_list = malloc(max_pids * sizeof(pid_t))) == NULL)
-		tst_brkm(TBROK | TERRNO, NULL, "malloc failed.");
+		tst_brk(TBROK | TERRNO, "malloc failed.");
 	memset(pid_list, 0, max_pids * sizeof(pid_t));
 
 	/* Currently used memory */
 	C = sstats.mem_unit * (total_ram - total_free);
-	tst_resm(TINFO, "Total memory already used on system = %llu kbytes",
+	tst_res(TINFO, "Total memory already used on system = %llu kbytes",
 		 C / 1024);
 
 	if (maxpercent) {
-		percent = (float)maxpercent / 100.00;
-
 		/* Desired memory needed to reach maxpercent */
-		D = percent * (sstats.mem_unit * total_ram);
-		tst_resm(TINFO,
+		D = ((float)maxpercent / 100.00) * (sstats.mem_unit * total_ram);
+		tst_res(TINFO,
 			 "Total memory used needed to reach maximum = %llu kbytes",
 			 D / 1024);
 
 		/* Are we already using more than maxpercent? */
 		if (C > D) {
-			tst_resm(TFAIL,
+			tst_res(TINFO,
 				 "More memory than the maximum amount you specified "
 				 " is already being used");
 			free(pid_list);
-			tst_exit();
+			exit(0);
 		}
 
-		/* set maxbytes to the extra amount we want to allocate */
-		maxbytes = D - C;
-		tst_resm(TINFO, "Filling up %d%% of ram which is %llu kbytes",
-			 maxpercent, maxbytes / 1024);
+		/* set alloc_maxbytes to the extra amount we want to allocate */
+		alloc_maxbytes = D - C;
+		tst_res(TINFO, "Filling up %d%% of ram which is %llu kbytes",
+			 maxpercent, alloc_maxbytes / 1024);
+	}
+	original_maxbytes = alloc_maxbytes;
+}
+
+static void cleanup(void)
+{
+	int i = 0;
+
+	while (pid_list[i] > 0) {
+		kill(pid_list[i], SIGCONT);
+		i++;
+	}
+
+	if(pid_list)
+		free(pid_list);
+}
+
+static void handler(int sig LTP_ATTRIBUTE_UNUSED)
+{
+        pid_count++;
+}
+
+static void do_write_page(char *mem, int chunksize)
+{
+	int i;
+
+	for (i = 0; i < chunksize; i++)
+		*(mem + i) = 'a';
+}
+
+static void child_loop_alloc(void)
+{
+	unsigned long bytecount = (unsigned long)chunksize;
+	char *mem;
+
+	while (1) {
+		mem = SAFE_MALLOC(chunksize);
+		if (dowrite)
+			do_write_page(mem, chunksize);
+
+		if (verbose)
+			tst_res(TINFO,
+				"child %d allocated %lu bytes chunksize is %d",
+				getpid(), bytecount, chunksize);
+		bytecount += chunksize;
+		if (alloc_bytes && bytecount >= alloc_bytes)
+			break;
 	}
-	original_maxbytes = maxbytes;
+	if (dowrite)
+		tst_res(TINFO, "... %lu bytes allocated and used in child %d",
+				bytecount, getpid());
+	else
+		tst_res(TINFO, "... %lu bytes allocated only in child %d",
+				bytecount, getpid());
+
+	kill(getppid(), SIGRTMIN);
+	raise(SIGSTOP);
+	exit(0);
+}
+
+static void mem_test(void)
+{
+	int i, pid_cntr;
+	pid_t pid;
+	struct sigaction act;
+
+	act.sa_handler = handler;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+	sigaction(SIGRTMIN, &act, 0);
+
 	i = 0;
 	pid_cntr = 0;
-	pid = fork();
-	if (pid < 0)
-		tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
+	pid = SAFE_FORK();
 	if (pid != 0) {
 		pid_cntr++;
 		pid_list[i] = pid;
 	}
 
-#if defined (_s390_)		/* s390's 31bit addressing requires smaller chunks */
-	while (pid != 0 && maxbytes > FIVE_HUNDRED_MB) {
+#if defined (_s390_)	/* s390's 31bit addressing requires smaller chunks */
+	while (pid != 0 && alloc_maxbytes > FIVE_HUNDRED_MB) {
 		i++;
 		if (i >= max_pids)
-			tst_brkm(TBROK, cleanup, "max_pids needs to be increased");
-		maxbytes -= FIVE_HUNDRED_MB;
-		pid = fork();
-		if (pid < 0)
-			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
+			tst_brk(TBROK, "max_pids needs to be increased");
+		alloc_maxbytes -= FIVE_HUNDRED_MB;
+		pid = SAFE_FORK();
 		if (pid != 0) {
 			pid_cntr++;
 			pid_list[i] = pid;
 		}
 	}
-	if (maxbytes > FIVE_HUNDRED_MB)
+	if (alloc_maxbytes > FIVE_HUNDRED_MB)
 		alloc_bytes = FIVE_HUNDRED_MB;
 	else
-		alloc_bytes = (unsigned long)maxbytes;
+		alloc_bytes = alloc_maxbytes;
 
 #elif __WORDSIZE == 32
-	while (pid != 0 && maxbytes > ONE_GB) {
+	while (pid != 0 && alloc_maxbytes > ONE_GB) {
 		i++;
 		if (i >= max_pids)
-			tst_brkm(TBROK, cleanup, "max_pids needs to be increased");
-		maxbytes -= ONE_GB;
-		pid = fork();
-		if (pid < 0)
-		    tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
+			tst_brk(TBROK, "max_pids needs to be increased");
+		allocmaxbytes -= ONE_GB;
+		pid = SAFE_FORK();
 		if (pid != 0) {
 			pid_cntr++;
 			pid_list[i] = pid;
 		}
 	}
-	if (maxbytes > ONE_GB)
+	if (alloc_maxbytes > ONE_GB)
 		alloc_bytes = ONE_GB;
 	else
-		alloc_bytes = (unsigned long)maxbytes;
+		alloc_bytes = allocmaxbytes;
 
 #elif __WORDSIZE == 64
-	while (pid != 0 && maxbytes > THREE_GB) {
+	while (pid != 0 && alloc_maxbytes > THREE_GB) {
 		i++;
 		if (i >= max_pids)
-			tst_brkm(TBROK, cleanup, "max_pids needs to be increased");
-		maxbytes -= THREE_GB;
-		pid = fork();
-		if (pid < 0)
-			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
+			tst_brk(TBROK, "max_pids needs to be increased");
+		alloc_maxbytes -= THREE_GB;
+		pid = SAFE_FORK();
 		if (pid != 0) {
 			pid_cntr++;
 			pid_list[i] = pid;
 		}
 	}
-	alloc_bytes = MIN(THREE_GB, maxbytes);
+	alloc_bytes = MIN(THREE_GB, alloc_maxbytes);
 #endif
 
-	if (pid == 0) {
-		bytecount = chunksize;
-		while (1) {
-			if ((mem = malloc(chunksize)) == NULL) {
-				tst_resm(TBROK | TERRNO,
-					 "stopped@%lu bytes", bytecount);
-				free(pid_list);
-				tst_exit();
-			}
-			if (dowrite)
-				for (j = 0; j < chunksize; j++)
-					*(mem + j) = 'a';
-			if (verbose)
-				tst_resm(TINFO,
-					 "allocated %lu bytes chunksize is %d",
-					 bytecount, chunksize);
-			bytecount += chunksize;
-			if (alloc_bytes && bytecount >= alloc_bytes)
-				break;
-		}
-		if (dowrite)
-			tst_resm(TINFO, "... %lu bytes allocated and used.",
-				 bytecount);
-		else
-			tst_resm(TINFO, "... %lu bytes allocated only.",
-				 bytecount);
-		kill(getppid(), SIGRTMIN);
-		while (1)
-			sleep(1);
-	} else {
-		sysinfo(&sstats);
+	if (pid == 0)
+		child_loop_alloc();
 
-		if (dowrite) {
-			/* Total Free Post-Test RAM */
-			post_mem =
-			    (unsigned long long)sstats.mem_unit *
-			    sstats.freeram;
-			post_mem =
-			    post_mem +
-			    (unsigned long long)sstats.mem_unit *
-			    sstats.freeswap;
-
-			while ((((unsigned long long)pre_mem - post_mem) <
-				(unsigned long long)original_maxbytes) &&
-			       pid_count < pid_cntr && !sigchld_count) {
-				sleep(1);
-				sysinfo(&sstats);
-				post_mem =
-				    (unsigned long long)sstats.mem_unit *
-				    sstats.freeram;
-				post_mem =
-				    post_mem +
-				    (unsigned long long)sstats.mem_unit *
-				    sstats.freeswap;
-			}
-		}
+	/* waits in the loop for all children finish allocating*/
+	while(pid_count < pid_cntr)
+		sleep(1);
 
-		if (sigchld_count) {
-			tst_resm(TFAIL, "child process exited unexpectedly");
-		} else if (dowrite) {
-			tst_resm(TPASS, "%llu kbytes allocated and used.",
-				 original_maxbytes / 1024);
-		} else {
-			tst_resm(TPASS, "%llu kbytes allocated only.",
-				 original_maxbytes / 1024);
-		}
+	if (dowrite) {
+		sysinfo(&sstats);
+		/* Total Free Post-Test RAM */
+		post_mem = (unsigned long long)sstats.mem_unit * sstats.freeram;
+		post_mem = post_mem + (unsigned long long)sstats.mem_unit * sstats.freeswap;
 
+		if (((pre_mem - post_mem) < original_maxbytes))
+			tst_res(TFAIL, "kbytes allocated and used less than expected %llu",
+					original_maxbytes / 1024);
+		else
+			tst_res(TPASS, "%llu kbytes allocated and used",
+					original_maxbytes / 1024);
+	} else {
+		tst_res(TPASS, "%llu kbytes allocated only",
+				original_maxbytes / 1024);
+	}
+
+	i = 0;
+	while (pid_list[i] > 0) {
+		kill(pid_list[i], SIGCONT);
+		i++;
 	}
-	cleanup();
-	tst_exit();
 }
+
+static struct tst_test test = {
+	.forks_child = 1,
+	.options = mtest_options,
+	.setup = setup,
+	.cleanup = cleanup,
+	.test_all = mem_test,
+};
-- 
2.20.1


             reply	other threads:[~2019-02-28  7:40 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-28  7:40 Li Wang [this message]
2019-02-28 22:08 ` [LTP] [RFC PATCH] mm: rewrite mtest01 with new API Jan Stancek
2019-03-01  6:05   ` Li Wang
2019-03-01  8:03     ` Jan Stancek
2019-03-01  8:26       ` Li Wang
2019-03-01  8:44         ` Jan Stancek
2019-03-05  7:04           ` Li Wang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190228074002.14351-1-liwang@redhat.com \
    --to=liwang@redhat.com \
    --cc=ltp@lists.linux.it \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.