2004-06-30 15:58:39

by E.Gryaznova

[permalink] [raw]
Subject: [2.6.7-mm4: OOPS] kernel BUG at mm/mmap.c:1793

/*
* This program was originaly from W.Wilson Ho.
*/

/*
* @(#)bigfile.c 1.2 98/12/19 Connectathon Testsuite
*/

/*
* Write and reread a large file. This potentially covers a few problems
* that have appeared in the past:
* - inability of server to commit a large file range with one RPC
* - client's dirtying memory faster than it can clean it
* - server's returning bogus file attributes, confusing the client
* - client and server not propagating "filesystem full" errors back to the
* application
*/

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>

static char usage[] = "usage: bigfile [-s size_in_MB] filename";

static off_t file_size = 30 * 1024 * 1024;

static char *filename; /* name of test file */
static int buffer_size = 8192; /* size of read/write buffer */

static long pagesize;

static void write_read_mmap (int fd);

int main(argc, argv)
int argc;
char **argv;
{
int c;
off_t size;
int fd;
extern int optind;
extern char *optarg;

pagesize = sysconf(_SC_PAGESIZE);
if (pagesize < 0) {
fprintf(stderr, "can't get page size\n");
exit(2);
}

while ((c = getopt(argc, argv, "s:")) != EOF)
switch (c) {
case 's':
size = atol(optarg) * 1024 * 1024;
if (size > 0)
file_size = size;
break;
case '?':
fprintf(stderr, "%s\n", usage);
exit(2);
break;
}
if (optind != argc - 1) {
fprintf(stderr, "%s\n", usage);
exit(2);
}
filename = argv[optind];
printf ("file: %s, size = %lu\n", filename, file_size);
fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
fprintf(stderr, "can't create %s: %s\n",
filename, strerror(errno));
exit(2);
}

write_read_mmap(fd);

if (unlink(filename) < 0) {
fprintf(stderr, "can't unlink %s: %s\n",
filename, strerror(errno));
exit(2);
}

printf ("OK. Passed.\n");
exit(0);
}

/*
* Return non-zero if the given buffer is full of the given value.
* Otherwise, return zero.
*/

static int
verify(buf, bufsize, val)
char *buf;
long bufsize;
unsigned char val;
{
int i;

for (i = 0; i < bufsize; i++) {
if ((unsigned char)(buf[i]) != val)
return (0);
}

return (1);
}

/*
* Print the contents of the buffer in hex to stderr.
*/

static void
dump_buf(buf, bufsize)
char *buf;
int bufsize;
{
int i;

for (i = 0; i < bufsize; i++) {
fprintf(stderr, "%x ", buf[i]);
if ((i + 1) % 40 == 0)
fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
}

/*
* Write out the given error message and exit. If the error is because
* there is no more space, flag it as a warning, and delete the file.
* Otherwise, flag it as an error and leave the file alone.
*/

static void
io_error(error, errmsg)
int error; /* errno value */
char *errmsg;
{
if (error == EDQUOT || error == ENOSPC)
fprintf(stderr, "Warning: can't complete test: ");
else
fprintf(stderr, "Error: ");
fprintf(stderr, "%s\n", errmsg);

if (error == EDQUOT || error == ENOSPC)
unlink(filename);

exit(2);
}

/*
* Return the test value for the given offset.
*/

static inline unsigned char
testval(off_t offset)
{
return 'a' + (offset % 26);
}

/*
* Write and then randomly reread the file, by mapping it. Same error
* handling as write_read().
*/

static void
write_read_mmap (int fd)
{
long numpages = file_size / pagesize;
char *buf;
int i;

/*
* Truncate the file and then map it in (the entire file). Then
* fill it with unsigned chars, the same as write_read().
*/

if (ftruncate(fd, 0) < 0) {
fprintf(stderr, "can't truncate %s: %s\n",
filename, strerror(errno));
exit(2);
}
if (ftruncate(fd, file_size) < 0) {
int error = errno;
char errmsg[1024];

sprintf(errmsg, "write to %s failed: %s",
filename, strerror(errno));
io_error(error, errmsg);
}
buf = mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == (char *)MAP_FAILED) {
fprintf(stderr, "can't map %s for writing: %s\n",
filename, strerror(errno));
exit(2);
}

for (i = 0; i < numpages; i++) {
unsigned char val = testval(i);

memset(buf + i * pagesize, val, pagesize);
}

if (msync(buf, file_size, MS_SYNC | MS_INVALIDATE) < 0) {
char errmsg[1024];
int error = errno;

sprintf(errmsg, "can't msync %s: %s", filename,
strerror(error));
io_error(error, errmsg);
}
if (munmap(buf, file_size) < 0) {
char errmsg[1024];
int error = errno;

sprintf(errmsg, "can't munmap %s: %s", filename,
strerror(error));
io_error(error, errmsg);
}

/*
* Reread the file, a page at a time, and make sure it has correct
* bits.
*/

for (i = 0; i < numpages; i++) {
unsigned char val = testval(i);

buf = mmap(0, pagesize, PROT_READ, MAP_SHARED, fd,
i * pagesize);
if (buf == (char *)MAP_FAILED) {
fprintf(stderr, "can't map %s for reading: %s\n",
filename, strerror(errno));
exit(2);
}
if (!verify(buf, pagesize, val)) {
fprintf(stderr,
"verify of mapped file failed, offset %ld; ",
(long)i * pagesize);
fprintf(stderr, "expected %x, got \n",
val);
dump_buf(buf, pagesize);
exit(1);
}

if (munmap(buf, pagesize) < 0) {
fprintf(stderr,
"can't unmap file after verifying: %s\n",
strerror(errno));
exit(2);
}
}
}




Attachments:
wilson-mmap-test.c (6.68 kB)

2004-06-30 18:43:06

by Andrew Morton

[permalink] [raw]
Subject: Re: [2.6.7-mm4: OOPS] kernel BUG at mm/mmap.c:1793

"E. Gryaznova" <[email protected]> wrote:
>
> this is reproducible for me problem:
> This wilson mmap test (attached) causes the kernel BUG at mm/mmap.c:1793
> immediately after running.

I cannot trigger it here. Does it happen every time? How much memory does
that machine have?

2004-06-30 18:56:09

by E.Gryaznova

[permalink] [raw]
Subject: Re: [2.6.7-mm4: OOPS] kernel BUG at mm/mmap.c:1793

Andrew Morton wrote:

>"E. Gryaznova" <[email protected]> wrote:
>
>
>>this is reproducible for me problem:
>> This wilson mmap test (attached) causes the kernel BUG at mm/mmap.c:1793
>> immediately after running.
>>
>>
>
>I cannot trigger it here. Does it happen every time?
>
Yes

> How much memory does
>that machine have?
>
# cat /proc/meminfo
MemTotal: 254396 kB
MemFree: 195224 kB
Buffers: 12648 kB
Cached: 19244 kB
SwapCached: 0 kB
Active: 25000 kB
Inactive: 14668 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 254396 kB
LowFree: 195224 kB
SwapTotal: 1052216 kB
SwapFree: 1052216 kB
Dirty: 20 kB
Writeback: 0 kB
Mapped: 11596 kB
Slab: 16236 kB
Committed_AS: 24996 kB
PageTables: 424 kB
VmallocTotal: 770040 kB
VmallocUsed: 6868 kB
VmallocChunk: 763152 kB

Thanks,
Lena


2004-06-30 20:08:45

by Hugh Dickins

[permalink] [raw]
Subject: Re: [2.6.7-mm4: OOPS] kernel BUG at mm/mmap.c:1793

On Wed, 30 Jun 2004, Andrew Morton wrote:
> "E. Gryaznova" <[email protected]> wrote:
> >
> > this is reproducible for me problem:
> > This wilson mmap test (attached) causes the kernel BUG at mm/mmap.c:1793
> > immediately after running.
>
> I cannot trigger it here. Does it happen every time? How much memory does
> that machine have?

I get it as easily as Lena does, perhaps you've a smallish stack rlimit.

The problem is in the flexible mmap patch: arch_get_unmapped_area_topdown
is liable to give your mmap vm_start above TASK_SIZE with vm_end wrapped;
which is confusing, and ends up as that BUG_ON(mm->map_count).

The patch below stops that behaviour, but it's not the full solution:
wilson_mmap_test -s 1000 then simply cannot allocate memory for the
large mmap, whereas it works fine non-top-down.

I think it's wrong to interpret a large or rlim_infinite stack rlimit
as an inviolable request to reserve that much for the stack: it makes
much less VM available than bottom up, not what was intended. Perhaps
top down should go bottom up (instead of belly up) when it fails -
but I'd probably better leave that to Ingo.

Or perhaps the default should place stack below text (as WLI suggested
and ELF intended, with its text defaulting to 0x08048000, small progs
sharing page table between stack and text and data); with a further
personality for those needing bigger stack.

Hugh

--- 2.6.7-mm4/mm/mmap.c 2004-06-29 12:18:55.000000000 +0100
+++ linux/mm/mmap.c 2004-06-30 20:20:50.026826472 +0100
@@ -1100,12 +1100,12 @@ arch_get_unmapped_area_topdown(struct fi
return addr;
}

+try_again:
/* make sure it can fit in the remaining address space */
if (mm->free_area_cache < len)
goto fail;

/* either no address requested or cant fit in requested address hole */
-try_again:
addr = (mm->free_area_cache - len) & PAGE_MASK;
do {
/*