Skip to contentSkip to conversation

kernel BUG at mm/mmap.c:1793

2.6.7-mm4: OOPS New E. Gryaznova 4 participants 5 messages Jun 30, 2004 – Jul 2, 2004

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

Hello,

this is reproducible for me problem:
This wilson mmap test (attached) causes the kernel BUG at mm/mmap.c:1793 immediately after running.

kernel :  2.6.7-mm4 (no extra patches)
test : wison-mmap-test -s 1000
tested filesystem :  ext2
RAM : 256Mb

machine :

# car /proc/cpuinfo
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 6
model           : 6
model name      : AMD Athlon(tm) MP Processor 1700+
stepping        : 2
cpu MHz         : 1460.204
cache size      : 256 KB
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 1
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse syscall mp mmxext 3dnowext 3dnow
bogomips        : 2883.58

processor : 1
vendor_id : AuthenticAMD
cpu family : 6
model : 6
model name : AMD Athlon(tm) Processor
stepping : 2
cpu MHz : 1460.204
cache size : 256 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 1
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse syscall mp mmxext 3dnowext 3dnow
bogomips : 2916.35

------------[ cut here ]------------
kernel BUG at mm/mmap.c:1793!

invalid operand: 0000 [#1]
SMP

Program received signal SIGTRAP, Trace/breakpoint trap. 0xc01441af in exit_mmap (mm=0xcf90a040) at tlb.h:78
78 tlb->nr = 0;
(gdb)

(gdb) bt

#0  0xc01441af in exit_mmap (mm=0xcf90a040) at tlb.h:78
#1  0xc011c137 in mmput (mm=0xcf90a040) at kernel/fork.c:464
#2  0xc011ff95 in do_exit (code=11) at kernel/exit.c:516
#3  0xc012024a in do_group_exit (exit_code=11) at kernel/exit.c:886
#4  0xc0127f3f in get_signal_to_deliver (info=0xccd4df24, regs=0xccd4dfc4,

cookie=0xc12067c8) at kernel/signal.c:1871
#5 0xc01059eb in do_signal (regs=0xccd4dfc4, oldset=0xcf53ec04)
at arch/i386/kernel/signal.c:572
#6 0xc0105acb in do_notify_resume (regs=0xccd4dfc4, oldset=0xffffffd4,
thread_info_flags=3240126408) at arch/i386/kernel/signal.c:619

#7  0xc0105cbe in work_notifysig () at arch/i386/kernel/entry.S:350
#8  0x1ffccfd8 in ?? ()
#9  0x00000400 in ?? ()

(gdb)

(gdb) c
Continuing.

CPU:    0
EIP:    0060:[<c01441af>]    Not tainted VLI
EFLAGS: 00010202   (2.6.7-mm4)

EIP is at exit_mmap+0x13f/0x150
eax: 00000001 ebx: c1204040 ecx: c12067c8 edx: ffffffd4
esi: cf90a040 edi: cf53e6c0 ebp: ccd4dea0 esp: ccd4de80
ds: 007b es: 007b ss: 0068
Process wilson-mmap-tes (pid: 968, threadinfo=ccd4c000 task=cf53e6c0)

Stack: 00000000 ffffffff ccd4de94 00000000 c1204040 0000002c cf90a040 cf90a06c
      ccd4deac c011c137 cf90a040 ccd4ded0 c011ff95 00000000 cf53e6c0 
0000000a
      00000000 cf652b00 ccd4c000 0000000b ccd4deec c012024a 0000000b 
cf53ec04
Call Trace:
[<c0106aa6>] show_stack+0xa6/0xb0
[<c0106c1d>] show_registers+0x14d/0x1c0
[<c0106dca>] die+0xaa/0x130
[<c01071ec>] do_invalid_op+0x9c/0xa0
[<c01066dd>] error_code+0x2d/0x38
[<c011c137>] mmput+0x57/0x90
[<c011ff95>] do_exit+0x155/0x330
[<c012024a>] do_group_exit+0x3a/0xc0
[<c0127f3f>] get_signal_to_deliver+0x26f/0x370
[<c01059eb>] do_signal+0x5b/0x100
[<c0105acb>] do_notify_resume+0x3b/0x3d
[<c0105cbe>] work_notifysig+0x13/0x15
Code: 5b 5e c9 c3 8b 03 c7 43 08 00 00 00 00 e8 fa fa fc ff 8b 53 04 83 fa ff 74 a2 8d 43 14 e8 4a 67 00 00 c7 43 04 00 00 00 00 eb 91 <0f> 0b 01 07 ec c3 41 c0 e9 59 ff ff ff 8d 74 26 00 55 89 e5 56

Thanks,
Lena.

/*
* 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);
         }
     }

}

#1 lore raw
Quoted 4 lines · E. Gryaznova

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?

#2 lore raw
Quoted 10 lines · Andrew Morton
Quoted 4 lines · E. Gryaznova

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

Quoted 3 lines

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

#3 lore raw
In reply to Andrew Morton
Quoted 8 lines · Andrew Morton
Quoted 4 lines · E. Gryaznova

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

mm/mmap.c +1 -1
@@ -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 {
 		/*
#4 lore raw

PATCH 1/1 flexible-mmap-update.patch, 2.6.7-mm5

Quoted 5 lines · Hugh Dickins

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.

Agreed. the attached flexible-mmap-update.patch (against 2.6.7-mm5) implements the following changes:

- fall back to the bottom-up layout if the stack can grow unlimited (if the stack ulimit has been set to RLIM_INFINITY)

- try the bottom-up allocator if the top-down allocator fails - this can utilize the hole between the true bottom of the stack and its ulimit, as a last-resort effort.

i've tested a number of failure scenarios with various ulimits and mmap sizes, and we now successfully allocate a VM area in all cases i tested.

Ingo

- fall back to the bottom-up layout if the stack can grow unlimited (if the stack ulimit has been set to RLIM_INFINITY)

- try the bottom-up allocator if the top-down allocator fails - this can utilize the hole between the true bottom of the stack and its ulimit, as a last-resort effort.

arch/i386/mm/mmap.c renamed from arch/i386/mm/mmap.c.orig +3 -2
@@ -55,9 +55,10 @@ void arch_pick_mmap_layout(struct mm_str
 {
 	/*
 	 * Fall back to the standard layout if the personality
-	 * bit is set:
+	 * bit is set, or if the expected stack growth is unlimited:
 	 */
-	if (current->personality & ADDR_COMPAT_LAYOUT) {
+	if ((current->personality & ADDR_COMPAT_LAYOUT) ||
+			current->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY) {
 		mm->mmap_base = TASK_UNMAPPED_BASE;
 		mm->get_unmapped_area = arch_get_unmapped_area;
 		mm->unmap_area = arch_unmap_area;
mm/mmap.c renamed from mm/mmap.c.orig +18 -5
@@ -1074,13 +1074,13 @@ void arch_unmap_area(struct vm_area_stru
  * stack's low limit (the base):
  */
 unsigned long
-arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
-			  unsigned long len, unsigned long pgoff,
-			  unsigned long flags)
+arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
+			  const unsigned long len, const unsigned long pgoff,
+			  const unsigned long flags)
 {
 	struct vm_area_struct *vma, *prev_vma;
 	struct mm_struct *mm = current->mm;
-	unsigned long base = mm->mmap_base;
+	unsigned long base = mm->mmap_base, addr = addr0;
 	int first_time = 1;
 
 	/* requested length too big for entire address space */
@@ -1142,7 +1142,20 @@ fail:
 		first_time = 0;
 		goto try_again;
 	}
-	return -ENOMEM;
+	/*
+	 * A failed mmap() very likely causes application failure,
+	 * so fall back to the bottom-up function here. This scenario
+	 * can happen with large stack limits and large mmap()
+	 * allocations.
+	 */
+	mm->free_area_cache = TASK_UNMAPPED_BASE;
+	addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
+	/*
+	 * Restore the topdown base:
+	 */
+	mm->free_area_cache = base;
+
+	return addr;
 }
 
 void arch_unmap_area_topdown(struct vm_area_struct *area)