Subject: kern/2755: Touching virtual memory==physical memory causes pathological
To: None <gnats-bugs@gnats.netbsd.org>
From: Jonathan Stone <jonathan@DSG.Stanford.EDU>
List: netbsd-bugs
Date: 09/14/1996 16:05:15
>Number:         2755
>Category:       kern
>Synopsis:       Exhausting memory causes a machine to freeze for up to a minute
>Confidential:   yes
>Severity:       critical
>Priority:       high
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Sep 15 21:50:03 1996
>Last-Modified:
>Originator:     Jonathan Stone
>Organization:
	
>Release:        1.2_BETA
>Environment:
	
System: NetBSD Cup.DSG.Stanford.EDU 1.2_BETA NetBSD 1.2_BETA (DSG) #47: Mon Jul 29 12:05:57 PDT 1996 jonathan@Cup.DSG.Stanford.EDU:/aga/n1/src/NetBSD/IP-PLUS/src/sys/arch/i386/compile/DSG i386


>Description:

NetBSD exhibits pathological behaviour somewhere in the VM system,
under at least some circumstances when the active memory (desired
working-set size) is greater than physical memory.  I have replicated
the behaviour several times on a 64Mbyte machine.  I describe the
bebaviour as "pathological" because (as observed from either local X
applications or remote logins from another machine) *all* processes on
the afflicted machine are blocked for 60 to 65 seconds.

During this time, X applications on the thrashing machine do not
repaint when iconified and de-iconified; the kernel answers TCP
connects to listening ports, but inetd can't seem to fork new daemons
(e.g., telnet gets a "connect" message but no login prompt, and a
telnet interrupt or "are you there" are not answered.  system monitoring
tools also seem to get stopped.

Listening to the swap disk suggests that not much disk I/O activity
is taking place during the one-minute hiatus.


This is

>How-To-Repeat:

Compile and run the following program on a NetBSD/i386 machine, with
arguments "-v N", where N is the amount of physical memory, in megabytes,
installed in the machine.   I find that on a 64Mbyte machine,  the
pathological behaviour starts approximately when touching the 50th Mbyte.
Re-running the program on a previously-idle system forces idle processes
to get swapped out, and increases the amount of memory that can be touched
before the pathological regime is entered.



>Fix:

I dont have a clue.


>Audit-Trail:
>Unformatted:
>Sharfile of Example Program:


# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	memtest.c
#
echo x - memtest.c
sed 's/^X//' >memtest.c << 'END-of-memtest.c'
X/*
X * Write test pattern into N megabytes of virtual memory.
X * May also induce thrashing or demonstrate VM performance problems
X * when the size tested is at or near physical memory size.
X */
X
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <unistd.h>
X
X#define ONE_MBYTE (1024* 1024)
X
X/*
X *  Flags
X */
Xint verboseflag = 0;
Xint foreverflag = 0;
X
X
X/*
X * Fill a hunk of memory with a test pattern, then do a read-after-write
X * comparison.
X */
Xint
Xmemtest (int *mem, int size, int pattern)
X{
X	register int i;
X	
X	for (i = 0; i < size; i++) {
X		mem[i] = pattern ;
X	}
X
X	for (i = 0; i < size; i++) {
X		if (mem[i] != pattern) {
X			printf("failed at %p, should be 0x%x, is 0x%x\n",
X			       mem+i, pattern, mem[i]);
X			((volatile int*)mem)[i] = pattern ;
X			if (mem[i] != pattern) 
X			printf("failed again at %p, should be 0x%x, is 0x%x\n",
X			       mem+i, pattern, mem[i]);
X		}
X	}
X}
X
Xvoid
Xusage()
X{
X	extern char *__progname;
X	printf("usage: %s [-v] [-f] <megabytes>\n", __progname);
X	exit(2);
X}
X
X
Xint
Xparseopts(int argc, char *argv[])
X{
X	extern int optind;
X	int ch;
X
X	while ((ch = getopt(argc, argv, "vf")) != -1) {
X		switch (ch) {
X		case 'v':
X			verboseflag = 1;
X			break;
X		case 'f':
X			foreverflag = 1;
X			break;
X		default:
X			usage();
X		}
X	}
X	
X	argc -= optind;
X	argv += optind;
X
X	if (argc != 1) usage();
X
X	return (atoi (argv[0]));
X}
X
X
X
Xint
Xmain(int argc, char *argv[])
X{
X	void  *p;
X	int i, megs;
X
X	megs = parseopts(argc, argv);
X	p = (void*)malloc(ONE_MBYTE * megs);
X
Xtest:
X	for (i = 0; i < megs; i++) {
X		int *thishunk = p+(ONE_MBYTE*i);
X		if (verboseflag)
X			printf("testing: %d Mbytes\n", i);
X		memtest(thishunk, (ONE_MBYTE / sizeof(int)), 0);
X		memtest(thishunk, (ONE_MBYTE / sizeof(int)), 0xffffffff);
X		memtest(thishunk, (ONE_MBYTE / sizeof(int)), 0xa5a5a5a5);
X		memtest(thishunk, (ONE_MBYTE / sizeof(int)), 0x5a5a5a5a);
X		memtest(thishunk, (ONE_MBYTE / sizeof(int)), 0);
X	}
X	if (foreverflag) goto test;
X}
END-of-memtest.c
exit