All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [RFC PATCH] mm: rewrite mtest01 with new API
@ 2019-02-28  7:40 Li Wang
  2019-02-28 22:08 ` Jan Stancek
  0 siblings, 1 reply; 7+ messages in thread
From: Li Wang @ 2019-02-28  7:40 UTC (permalink / raw)
  To: ltp

 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


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [LTP] [RFC PATCH] mm: rewrite mtest01 with new API
  2019-02-28  7:40 [LTP] [RFC PATCH] mm: rewrite mtest01 with new API Li Wang
@ 2019-02-28 22:08 ` Jan Stancek
  2019-03-01  6:05   ` Li Wang
  0 siblings, 1 reply; 7+ messages in thread
From: Jan Stancek @ 2019-02-28 22:08 UTC (permalink / raw)
  To: ltp

Hi,

----- Original Message -----
> 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.

This seems to be quite common on ppc systems (64k pages with slow I/O),
so I do welcome fix/rewrite.

> 
>    ---- 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

Current behaviour varies a lot depending on system. I'm thinking if we should
just set it to 80% of free RAM. We already have number of OOM tests,
so maybe we don't need to worry about memory pressure here too.

> 
> 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(-)
> 

<snip>

> +
> +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 was thinking if we can't "abuse" tst_futexes a bit. It's a piece of
shared memory we already have and could use for an atomic counter.

<snip>

> +	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);

What happens if one child hits OOM?

>  
> -		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,

Is default timeout going to work on large boxes (256GB+ RAM)?


Thinking loud, what if...
- we define at the start of test how much memory we want to allocate (target == 80% of free RAM)
- we allocate a shared memory for counter, that each child increases
  as it allocates memory (progress)
  (or we abuse tst_futexes)
  we could use tst_atomic_add_return() to count allocated chunks globally
- once child finishes allocation it will pause()
- we set timeout to ~3 minutes
- main process runs in loop, sleeps, and periodically checks
  - if progress reached target, PASS, break
  - if progress hasn't increased in last 15 seconds, FAIL, break
  - if we are 15 seconds away from timeout, end test early, PASS, break
    (reason is to avoid running too long on big boxes)
- kill all children, exit

Regards,
Jan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [LTP] [RFC PATCH] mm: rewrite mtest01 with new API
  2019-02-28 22:08 ` Jan Stancek
@ 2019-03-01  6:05   ` Li Wang
  2019-03-01  8:03     ` Jan Stancek
  0 siblings, 1 reply; 7+ messages in thread
From: Li Wang @ 2019-03-01  6:05 UTC (permalink / raw)
  To: ltp

On Fri, Mar 1, 2019 at 6:09 AM Jan Stancek <jstancek@redhat.com> wrote:

Current behaviour varies a lot depending on system. I'm thinking if we
> should
> just set it to 80% of free RAM. We already have number of OOM tests,
> so maybe we don't need to worry about memory pressure here too.
>

Yes, I'm ok with that change. Since if we decrease the allocated
consumption to 50% mem+swap, that probably only do allocating in the part
of free mem too.


> > +
> > +     act.sa_handler = handler;
> > +     act.sa_flags = 0;
> > +     sigemptyset(&act.sa_mask);
> > +     sigaction(SIGRTMIN, &act, 0);
>
> I was thinking if we can't "abuse" tst_futexes a bit. It's a piece of
> shared memory we already have and could use for an atomic counter.
>
> <snip>
>
> > +     /* waits in the loop for all children finish allocating*/
> > +     while(pid_count < pid_cntr)
> > +             sleep(1);
>
> What happens if one child hits OOM?
>

LTP new API does wait and check child status for the test, if one
child_A(allocating finished and status paused) hits OOM, it will just break
and report status, but that's ok for this event, because other children
which still allocating will keep running after system reclaiming memory
from child_A. So parent process will recieve all of children's SIGRTMIN
signal and break from the while loop correctly.

Anthoer situation(I haven't hit), is one child_B(still allocating and not
finishes) was killed by OOM. that will make parent fall into an infinite
loop here. From OOM mechanism, oom-killer likes to choose high score
process, so this situation maybe not easy to reproduce. But that not mean
it will not, since oom-killer is not perfect.

Anyway, to avoid the second situation occuring, I'd like to take you advice
to make parent exiting loop safly with many check actions.


> >
> > -             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,
>
> Is default timeout going to work on large boxes (256GB+ RAM)?
>

No.

I had the same worries before, but in this test, the number of
children(max_pids) will be increased dynamically with the system total
memory size. And each child allocating won't beyond the 'alloc_bytes'
(alloc_bytes = MIN(THREE_GB, alloc_maxbytes)) limitaion, so an extra time
consumption part is just by forking, but from my evaluation on a 4T ram
system, mtest01 finishes very faster(99% mem+swap, 2m22sec) than I
expected. So the default timeout is not trigger at all.

# cat /proc/meminfo  | grep Mem
MemTotal:       4227087524 kB
MemFree:        4223159948 kB
MemAvailable:   4213257308 kB

# time ./mtest01 -p99 -w
tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s
mtest01.c:113: INFO: Total memory already used on system = 3880348 kbytes
mtest01.c:120: INFO: Total memory used needed to reach maximum = 4188969005
kbytes
mtest01.c:134: INFO: Filling up 99% of ram which is 4185088657 kbytes
...
mtest01.c:185: INFO: ... 3221225472 bytes allocated and used in child 41779
mtest01.c:281: PASS: 4185132681 kbytes allocated and used
...

real 2m22.213s
user 79m52.390s
sys 351m56.059s


>
> Thinking loud, what if...
> - we define at the start of test how much memory we want to allocate
> (target == 80% of free RAM)
> - we allocate a shared memory for counter, that each child increases
>   as it allocates memory (progress)
>   (or we abuse tst_futexes)
>   we could use tst_atomic_add_return() to count allocated chunks globally
> - once child finishes allocation it will pause()
> - we set timeout to ~3 minutes
> - main process runs in loop, sleeps, and periodically checks
>   - if progress reached target, PASS, break
>   - if progress hasn't increased in last 15 seconds, FAIL, break
>   - if we are 15 seconds away from timeout, end test early, PASS, break
>     (reason is to avoid running too long on big boxes)
> - kill all children, exit
>
>
Real good suggestions, I will try to take some of them in V2.


> Regards,
> Jan
>


-- 
Regards,
Li Wang
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.linux.it/pipermail/ltp/attachments/20190301/65522666/attachment.html>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [LTP] [RFC PATCH] mm: rewrite mtest01 with new API
  2019-03-01  6:05   ` Li Wang
@ 2019-03-01  8:03     ` Jan Stancek
  2019-03-01  8:26       ` Li Wang
  0 siblings, 1 reply; 7+ messages in thread
From: Jan Stancek @ 2019-03-01  8:03 UTC (permalink / raw)
  To: ltp



----- Original Message -----
> On Fri, Mar 1, 2019 at 6:09 AM Jan Stancek <jstancek@redhat.com> wrote:
> 
> Current behaviour varies a lot depending on system. I'm thinking if we
> > should
> > just set it to 80% of free RAM. We already have number of OOM tests,
> > so maybe we don't need to worry about memory pressure here too.
> >
> 
> Yes, I'm ok with that change. Since if we decrease the allocated
> consumption to 50% mem+swap, that probably only do allocating in the part
> of free mem too.
> 
> 
> > > +
> > > +     act.sa_handler = handler;
> > > +     act.sa_flags = 0;
> > > +     sigemptyset(&act.sa_mask);
> > > +     sigaction(SIGRTMIN, &act, 0);
> >
> > I was thinking if we can't "abuse" tst_futexes a bit. It's a piece of
> > shared memory we already have and could use for an atomic counter.
> >
> > <snip>
> >
> > > +     /* waits in the loop for all children finish allocating*/
> > > +     while(pid_count < pid_cntr)
> > > +             sleep(1);
> >
> > What happens if one child hits OOM?
> >
> 
> LTP new API does wait and check child status for the test, if one
> child_A(allocating finished and status paused) hits OOM, it will just break
> and report status, but that's ok for this event, because other children
> which still allocating will keep running after system reclaiming memory
> from child_A. So parent process will recieve all of children's SIGRTMIN
> signal and break from the while loop correctly.
> 
> Anthoer situation(I haven't hit), is one child_B(still allocating and not
> finishes) was killed by OOM. that will make parent fall into an infinite
> loop here. From OOM mechanism, oom-killer likes to choose high score
> process, so this situation maybe not easy to reproduce. But that not mean
> it will not, since oom-killer is not perfect.
> 
> Anyway, to avoid the second situation occuring, I'd like to take you advice
> to make parent exiting loop safly with many check actions.
> 
> 
> > >
> > > -             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,
> >
> > Is default timeout going to work on large boxes (256GB+ RAM)?
> >
> 
> No.
> 
> I had the same worries before, but in this test, the number of
> children(max_pids) will be increased dynamically with the system total
> memory size. And each child allocating won't beyond the 'alloc_bytes'
> (alloc_bytes = MIN(THREE_GB, alloc_maxbytes)) limitaion, so an extra time
> consumption part is just by forking, but from my evaluation on a 4T ram
> system, mtest01 finishes very faster(99% mem+swap, 2m22sec) than I
> expected. So the default timeout is not trigger at all.
> 
> # cat /proc/meminfo  | grep Mem
> MemTotal:       4227087524 kB
> MemFree:        4223159948 kB
> MemAvailable:   4213257308 kB
> 
> # time ./mtest01 -p99 -w
> tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s
> mtest01.c:113: INFO: Total memory already used on system = 3880348 kbytes
> mtest01.c:120: INFO: Total memory used needed to reach maximum = 4188969005
> kbytes
> mtest01.c:134: INFO: Filling up 99% of ram which is 4185088657 kbytes
> ...
> mtest01.c:185: INFO: ... 3221225472 bytes allocated and used in child 41779
> mtest01.c:281: PASS: 4185132681 kbytes allocated and used
> ...
> 
> real 2m22.213s
> user 79m52.390s
> sys 351m56.059s
> 
> 
> >
> > Thinking loud, what if...
> > - we define at the start of test how much memory we want to allocate
> > (target == 80% of free RAM)
> > - we allocate a shared memory for counter, that each child increases
> >   as it allocates memory (progress)
> >   (or we abuse tst_futexes)
> >   we could use tst_atomic_add_return() to count allocated chunks globally
> > - once child finishes allocation it will pause()
> > - we set timeout to ~3 minutes
> > - main process runs in loop, sleeps, and periodically checks
> >   - if progress reached target, PASS, break
> >   - if progress hasn't increased in last 15 seconds, FAIL, break
> >   - if we are 15 seconds away from timeout, end test early, PASS, break
> >     (reason is to avoid running too long on big boxes)
> > - kill all children, exit
> >
> >
> Real good suggestions, I will try to take some of them in V2.

Maybe give it few days, so other people can respond, if they like/don't like
going in this direction.

> 
> 
> > Regards,
> > Jan
> >
> 
> 
> --
> Regards,
> Li Wang
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [LTP] [RFC PATCH] mm: rewrite mtest01 with new API
  2019-03-01  8:03     ` Jan Stancek
@ 2019-03-01  8:26       ` Li Wang
  2019-03-01  8:44         ` Jan Stancek
  0 siblings, 1 reply; 7+ messages in thread
From: Li Wang @ 2019-03-01  8:26 UTC (permalink / raw)
  To: ltp

On Fri, Mar 1, 2019 at 4:03 PM Jan Stancek <jstancek@redhat.com> wrote:

Maybe give it few days, so other people can respond, if they like/don't like
> going in this direction.
>

Sure.

Btw, I'm not going to use shared memory here since that probably make
things more complicated.
I guess just do remaining time check in the parent while loop is enough for
the two OOM situations
which I was mentioned in last mail.

==== mem_test() ======
        ...

if (pid == 0)
child_loop_alloc();

/* waits in the loop for all children finish allocating*/
while(pid_count < pid_cntr) {
if (tst_timeout_remaining() < 15)
break;

sleep(1);
}

if (dowrite) {
                pre_free - post_free check...
} else {
...
}

       kill children here..

-- 
Regards,
Li Wang
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.linux.it/pipermail/ltp/attachments/20190301/6952d779/attachment-0001.html>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [LTP] [RFC PATCH] mm: rewrite mtest01 with new API
  2019-03-01  8:26       ` Li Wang
@ 2019-03-01  8:44         ` Jan Stancek
  2019-03-05  7:04           ` Li Wang
  0 siblings, 1 reply; 7+ messages in thread
From: Jan Stancek @ 2019-03-01  8:44 UTC (permalink / raw)
  To: ltp




----- Original Message -----
> On Fri, Mar 1, 2019 at 4:03 PM Jan Stancek <jstancek@redhat.com> wrote:
> 
> Maybe give it few days, so other people can respond, if they like/don't like
> > going in this direction.
> >
> 
> Sure.
> 
> Btw, I'm not going to use shared memory here since that probably make
> things more complicated.
> I guess just do remaining time check in the parent while loop is enough for
> the two OOM situations
> which I was mentioned in last mail.
> 
> ==== mem_test() ======
>         ...
> 
> if (pid == 0)
> child_loop_alloc();
> 
> /* waits in the loop for all children finish allocating*/
> while(pid_count < pid_cntr) {
> if (tst_timeout_remaining() < 15)
> break;
> 
> sleep(1);
> }
> 
> if (dowrite) {
>                 pre_free - post_free check...

This doesn't look very accurate. Other system processes can influence this.

Do we need to check it at all? If a child didn't complete allocation
it's not going to signal parent.

> } else {
> ...
> }
> 
>        kill children here..
> 
> --
> Regards,
> Li Wang
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [LTP] [RFC PATCH] mm: rewrite mtest01 with new API
  2019-03-01  8:44         ` Jan Stancek
@ 2019-03-05  7:04           ` Li Wang
  0 siblings, 0 replies; 7+ messages in thread
From: Li Wang @ 2019-03-05  7:04 UTC (permalink / raw)
  To: ltp

On Fri, Mar 1, 2019 at 4:44 PM Jan Stancek <jstancek@redhat.com> wrote:

>
> > if (dowrite) {
> >                 pre_free - post_free check...
>
> This doesn't look very accurate. Other system processes can influence this.
>
> Do we need to check it at all? If a child didn't complete allocation
> it's not going to signal parent.
>

Sounds reasonable, if the parent exit from the loop correctly that means
children finished their memory allocation.

-- 
Regards,
Li Wang
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.linux.it/pipermail/ltp/attachments/20190305/c97248e5/attachment.html>

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-03-05  7:04 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-28  7:40 [LTP] [RFC PATCH] mm: rewrite mtest01 with new API Li Wang
2019-02-28 22:08 ` 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

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.