GIT: unionfs2-2.6.27.y: drivers/char/mem.c: avoid OOM lockup during large reads from /dev/zero

Erez Zadok ezk at fsl.cs.sunysb.edu
Thu Aug 12 23:15:09 EDT 2010


commit 77c68f845135db9396b4e227a92b5de10429071a
Author: Salman Qazi <sqazi at google.com>
Date:   Thu Jun 4 15:20:39 2009 -0700

    drivers/char/mem.c: avoid OOM lockup during large reads from /dev/zero
    
    commit 730c586ad5228c339949b2eb4e72b80ae167abc4 upstream.
    
    While running 20 parallel instances of dd as follows:
    
      #!/bin/bash
      for i in `seq 1 20`; do
               dd if=/dev/zero of=/export/hda3/dd_$i bs=1073741824 count=1 &
      done
      wait
    
    on a 16G machine, we noticed that rather than just killing the processes,
    the entire kernel went down.  Stracing dd reveals that it first does an
    mmap2, which makes 1GB worth of zero page mappings.  Then it performs a
    read on those pages from /dev/zero, and finally it performs a write.
    
    The machine died during the reads.  Looking at the code, it was noticed
    that /dev/zero's read operation had been changed by
    557ed1fa2620dc119adb86b34c614e152a629a80 ("remove ZERO_PAGE") from giving
    zero page mappings to actually zeroing the page.
    
    The zeroing of the pages causes physical pages to be allocated to the
    process.  But, when the process exhausts all the memory that it can, the
    kernel cannot kill it, as it is still in the kernel mode allocating more
    memory.  Consequently, the kernel eventually crashes.
    
    To fix this, I propose that when a fatal signal is pending during
    /dev/zero read operation, we simply return and let the user process die.
    
    Signed-off-by: Salman Qazi <sqazi at google.com>
    Cc: Nick Piggin <nickpiggin at yahoo.com.au>
    Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
    [ Modified error return and comment trivially.  - Linus]
    Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>

diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 672b08e..3191fc8 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -724,6 +724,9 @@ static ssize_t read_zero(struct file * file, char __user * buf,
 		written += chunk - unwritten;
 		if (unwritten)
 			break;
+		/* Consider changing this to just 'signal_pending()' with lots of testing */
+		if (fatal_signal_pending(current))
+			return written ? written : -EINTR;
 		buf += chunk;
 		count -= chunk;
 		cond_resched();


More information about the unionfs-cvs mailing list