* [LTP] [PATCH v2] mm: rewrite mtest01 with new API
@ 2019-03-06 9:51 Li Wang
2019-03-07 9:57 ` Jan Stancek
0 siblings, 1 reply; 2+ messages in thread
From: Li Wang @ 2019-03-06 9:51 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.
* decrease the pressure to 80% of free memory for testing
* 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
* use TST_PROCESS_STATE_WAIT to wait child changes to 'T' state
Signed-off-by: Li Wang <liwang@redhat.com>
---
Notes:
v1 --> v2
* allocate 80% of free RAM for testing
* remove the pre_free - post_free check
* involve tst_time_remaining for safe exit from loop
* use TST_PROCESS_STATE_WAIT to wait child state change
testcases/kernel/mem/mtest01/mtest01.c | 435 +++++++++++--------------
1 file changed, 198 insertions(+), 237 deletions(-)
diff --git a/testcases/kernel/mem/mtest01/mtest01.c b/testcases/kernel/mem/mtest01/mtest01.c
index ca9073a8e..0f1743c64 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,23 +29,87 @@
#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)
+#define STOP_THRESHOLD 15 /* seconds remaining before reaching timeout */
-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 total_free;
+static unsigned long long alloc_bytes;
+static unsigned long long alloc_maxbytes;
+static unsigned long long original_maxbytes;
+
+static int chunksize = 1024*1024;
+static int maxpercent = 20;
+static long maxbytes = 0;
+static char *dowrite;
+static char *verbose;
+
+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}
+};
-static void handler(int signo)
+static void parse_mtest_options(char *str_chunksize, int *chunksize,
+ char *str_maxbytes, long *maxbytes,
+ char *str_maxpercent, int *maxpercent)
{
- if (signo == SIGCHLD)
- sigchld_count++;
- pid_count++;
+ if (str_chunksize)
+ if (tst_parse_int(str_chunksize, chunksize, 1, INT_MAX))
+ tst_brk(TBROK, "Invalid chunksize '%s'", str_chunksize);
+
+ 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;
+ }
+
+ 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_free = sstats.freeram;
+
+ max_pids = total_free * sstats.mem_unit
+ / (unsigned long)FIVE_HUNDRED_MB + 10;
+ pid_list = SAFE_MALLOC(max_pids * sizeof(pid_t));
+ memset(pid_list, 0, max_pids * sizeof(pid_t));
+
+ if (maxpercent) {
+ /* set alloc_maxbytes to the extra amount we want to allocate */
+ alloc_maxbytes = ((float)maxpercent / 100.00)
+ * (sstats.mem_unit * total_free);
+ tst_res(TINFO, "Filling up %d%% of free ram which is %llu kbytes",
+ maxpercent, alloc_maxbytes / 1024);
+ }
+ original_maxbytes = alloc_maxbytes;
}
static void cleanup(void)
@@ -66,25 +117,59 @@ static void cleanup(void)
int i = 0;
while (pid_list[i] > 0) {
- kill(pid_list[i], SIGKILL);
+ kill(pid_list[i], SIGCONT);
i++;
}
- free(pid_list);
+ if(pid_list)
+ free(pid_list);
}
-int main(int argc, char *argv[])
+static void handler(int sig LTP_ATTRIBUTE_UNUSED)
{
- int c;
+ 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;
- 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@a time by default */
- struct sysinfo sstats;
+
+ 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;
+ }
+ 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;
@@ -93,234 +178,110 @@ int main(int argc, char *argv[])
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGRTMIN, &act, 0);
- sigaction(SIGCHLD, &act, 0);
-
- 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);
- }
- }
-
- sysinfo(&sstats);
- total_ram = sstats.totalram + sstats.totalswap;
- total_free = sstats.freeram + sstats.freeswap;
- /* Total Free Pre-Test RAM */
- 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.");
- 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",
- 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,
- "Total memory used needed to reach maximum = %llu kbytes",
- D / 1024);
-
- /* Are we already using more than maxpercent? */
- if (C > D) {
- tst_resm(TFAIL,
- "More memory than the maximum amount you specified "
- " is already being used");
- free(pid_list);
- tst_exit();
- }
- /* 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);
- }
- original_maxbytes = maxbytes;
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 = alloc_maxbytes;
#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 (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;
- }
+ if (pid == 0)
+ child_loop_alloc();
+
+ /* waits in the loop for all children finish allocating*/
+ while(pid_count < pid_cntr) {
+ if (tst_timeout_remaining() < STOP_THRESHOLD) {
+ tst_res(TWARN,
+ "the remaininig time is not enough for testing");
+
+ break;
}
- if (sigchld_count) {
- tst_resm(TFAIL, "child process exited unexpectedly");
- } else if (dowrite) {
- tst_resm(TPASS, "%llu kbytes allocated and used.",
- original_maxbytes / 1024);
+ sleep(1);
+ }
+
+ if (dowrite) {
+ if(pid_count < pid_cntr) {
+ tst_res(TFAIL, "kbytes allocated and used less than expected %llu",
+ original_maxbytes / 1024);
+
} else {
- tst_resm(TPASS, "%llu kbytes allocated only.",
- original_maxbytes / 1024);
+ tst_res(TPASS, "%llu kbytes allocated and used",
+ original_maxbytes / 1024);
}
+ } else {
+ if(pid_count < pid_cntr) {
+ tst_res(TFAIL, "kbytes allocated less than expected %llu",
+ original_maxbytes / 1024);
+ } else {
+ tst_res(TPASS, "%llu kbytes allocated only",
+ original_maxbytes / 1024);
+ }
+ }
+ i = 0;
+ while (pid_list[i] > 0) {
+ TST_PROCESS_STATE_WAIT(pid_list[i], 'T');
+ 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] 2+ messages in thread
* [LTP] [PATCH v2] mm: rewrite mtest01 with new API
2019-03-06 9:51 [LTP] [PATCH v2] mm: rewrite mtest01 with new API Li Wang
@ 2019-03-07 9:57 ` Jan Stancek
0 siblings, 0 replies; 2+ messages in thread
From: Jan Stancek @ 2019-03-07 9:57 UTC (permalink / raw)
To: ltp
----- 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.
>
> ---- 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.
>
> * decrease the pressure to 80% of free memory for testing
> * 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
> * use TST_PROCESS_STATE_WAIT to wait child changes to 'T' state
>
> Signed-off-by: Li Wang <liwang@redhat.com>
> ---
>
> Notes:
> v1 --> v2
> * allocate 80% of free RAM for testing
> * remove the pre_free - post_free check
> * involve tst_time_remaining for safe exit from loop
> * use TST_PROCESS_STATE_WAIT to wait child state change
>
> testcases/kernel/mem/mtest01/mtest01.c | 435 +++++++++++--------------
> 1 file changed, 198 insertions(+), 237 deletions(-)
>
> diff --git a/testcases/kernel/mem/mtest01/mtest01.c
> b/testcases/kernel/mem/mtest01/mtest01.c
> index ca9073a8e..0f1743c64 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,
Hi,
This part doesn't look accurate for v2, it doesn't check free memory.
> + * 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,23 +29,87 @@
> #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)
> +#define STOP_THRESHOLD 15 /* seconds remaining before reaching timeout */
>
> -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 total_free;
> +static unsigned long long alloc_bytes;
> +static unsigned long long alloc_maxbytes;
> +static unsigned long long original_maxbytes;
I wonder which of these need to be global.
> +
> +static int chunksize = 1024*1024;
> +static int maxpercent = 20;
> +static long maxbytes = 0;
> +static char *dowrite;
> +static char *verbose;
> +
> +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}
> +};
>
> -static void handler(int signo)
> +static void parse_mtest_options(char *str_chunksize, int *chunksize,
> + char *str_maxbytes, long *maxbytes,
> + char *str_maxpercent, int *maxpercent)
> {
> - if (signo == SIGCHLD)
> - sigchld_count++;
> - pid_count++;
> + if (str_chunksize)
> + if (tst_parse_int(str_chunksize, chunksize, 1, INT_MAX))
> + tst_brk(TBROK, "Invalid chunksize '%s'", str_chunksize);
> +
> + 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;
> + }
> +
> + 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_free = sstats.freeram;
> +
> + max_pids = total_free * sstats.mem_unit
> + / (unsigned long)FIVE_HUNDRED_MB + 10;
> + pid_list = SAFE_MALLOC(max_pids * sizeof(pid_t));
> + memset(pid_list, 0, max_pids * sizeof(pid_t));
> +
> + if (maxpercent) {
> + /* set alloc_maxbytes to the extra amount we want to allocate */
> + alloc_maxbytes = ((float)maxpercent / 100.00)
> + * (sstats.mem_unit * total_free);
> + tst_res(TINFO, "Filling up %d%% of free ram which is %llu kbytes",
> + maxpercent, alloc_maxbytes / 1024);
> + }
> + original_maxbytes = alloc_maxbytes;
> }
>
> static void cleanup(void)
> @@ -66,25 +117,59 @@ static void cleanup(void)
> int i = 0;
>
> while (pid_list[i] > 0) {
> - kill(pid_list[i], SIGKILL);
> + kill(pid_list[i], SIGCONT);
Is SIGCONT needed here? If everything goes OK, it's sent by mem_test().
If things go wrong, library kills process group with SIGKILL.
> i++;
> }
>
> - free(pid_list);
> + if(pid_list)
> + free(pid_list);
> }
>
> -int main(int argc, char *argv[])
> +static void handler(int sig LTP_ATTRIBUTE_UNUSED)
> {
> - int c;
> + 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;
> - 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;
> +
> + 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;
> + }
> + 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;
> @@ -93,234 +178,110 @@ int main(int argc, char *argv[])
> act.sa_flags = 0;
> sigemptyset(&act.sa_mask);
> sigaction(SIGRTMIN, &act, 0);
> - sigaction(SIGCHLD, &act, 0);
> -
> - 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);
> - }
> - }
> -
> - sysinfo(&sstats);
> - total_ram = sstats.totalram + sstats.totalswap;
> - total_free = sstats.freeram + sstats.freeswap;
> - /* Total Free Pre-Test RAM */
> - 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.");
> - 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",
> - 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,
> - "Total memory used needed to reach maximum = %llu kbytes",
> - D / 1024);
> -
> - /* Are we already using more than maxpercent? */
> - if (C > D) {
> - tst_resm(TFAIL,
> - "More memory than the maximum amount you specified "
> - " is already being used");
> - free(pid_list);
> - tst_exit();
> - }
>
> - /* 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);
> - }
> - original_maxbytes = maxbytes;
> 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 */
Can you also rework these ifdefs? It's appears, the same code is same
in each of them. The only difference is how much child allocates.
"alloc_bytes" could be a parameter for child_loop_alloc(), it doesn't
look like it needs to be global.
Regards,
Jan
> + 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 = alloc_maxbytes;
>
> #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 at %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 (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;
> - }
> + if (pid == 0)
> + child_loop_alloc();
> +
> + /* waits in the loop for all children finish allocating*/
> + while(pid_count < pid_cntr) {
> + if (tst_timeout_remaining() < STOP_THRESHOLD) {
> + tst_res(TWARN,
> + "the remaininig time is not enough for testing");
> +
> + break;
> }
>
> - if (sigchld_count) {
> - tst_resm(TFAIL, "child process exited unexpectedly");
> - } else if (dowrite) {
> - tst_resm(TPASS, "%llu kbytes allocated and used.",
> - original_maxbytes / 1024);
> + sleep(1);
> + }
> +
> + if (dowrite) {
> + if(pid_count < pid_cntr) {
> + tst_res(TFAIL, "kbytes allocated and used less than expected %llu",
> + original_maxbytes / 1024);
> +
> } else {
> - tst_resm(TPASS, "%llu kbytes allocated only.",
> - original_maxbytes / 1024);
> + tst_res(TPASS, "%llu kbytes allocated and used",
> + original_maxbytes / 1024);
> }
> + } else {
> + if(pid_count < pid_cntr) {
> + tst_res(TFAIL, "kbytes allocated less than expected %llu",
> + original_maxbytes / 1024);
> + } else {
> + tst_res(TPASS, "%llu kbytes allocated only",
> + original_maxbytes / 1024);
> + }
> + }
>
> + i = 0;
> + while (pid_list[i] > 0) {
> + TST_PROCESS_STATE_WAIT(pid_list[i], 'T');
> + 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 [flat|nested] 2+ messages in thread
end of thread, other threads:[~2019-03-07 9:57 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-06 9:51 [LTP] [PATCH v2] mm: rewrite mtest01 with new API Li Wang
2019-03-07 9:57 ` Jan Stancek
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.