read_zero() hack, turn 2

Ingo Molnar (mingo@pc5829.hil.siemens.at)
Thu, 16 Jan 1997 21:58:06 +0100 (MET)


okay [i warned you :)], here is the hack that messes with memory mappings
instead of setting physical memory to zero when someone is reading
/dev/zero.

It's against 2.1.21, and it hasnt crashed my machine yet. (which means
nothing as it could be the most broken thing on earth).

This patch gives totally unbelievable copying benchmarks when shuffling
/dev/zero into /dev/null using dd :) [I just measured 12 GigaBytes/sec
on a 100 MHz Neptune pentium! =B-) ]

[ joke aside: isnt this the easiest way to zero out large areas of memory?
libc could detect the "memset(addr,0,bignumber)" case? Or is this
already the case. ]

--- linux-2.1.21_orig/drivers/char/mem.c Mon Dec 23 10:12:12 1996
+++ linux/drivers/char/mem.c Thu Jan 16 21:45:36 1997
@@ -231,14 +231,39 @@
}

/*
- * For fun, somebody might look into using the MMU for this.
- * NOTE! It's not trivial: you have to check that the mapping
- * is a private mapping and if so you can just map in the
- * zero page directly. But shared mappings _have_ to use the
- * physical copy.
+ * For fun, we are using the MMU for this.
*/
static inline unsigned long read_zero_pagealigned(char * buf, unsigned long size)
{
+ struct vm_area_struct * curr_vma;
+ unsigned long addr=(unsigned long)buf;
+
+/*
+ * First we take the most obvious case: when we have one VM area to deal with,
+ * and it's privately mapped.
+ */
+ curr_vma = find_vma(current->mm, addr);
+
+ if ( !(curr_vma->vm_flags & VM_SHARED) &&
+ (addr + size-1 <= curr_vma->vm_end) ) {
+
+ flush_cache_range(current->mm, addr, addr + size);
+ zap_page_range(current->mm, addr, size);
+ zeromap_page_range(addr, size, PAGE_COPY);
+ flush_tlb_range(current->mm, addr, addr + size);
+
+ return 0;
+ }
+
+/*
+ * Ooops, the shared case is hard. Lets do the conventional
+ * zeroing.
+ *
+ * FIXME: same for the multiple-vma case, we dont handle it
+ * now for simplicity, although it's much easier than
+ * the shared case. Not that it should happen often ...
+ */
+
do {
if (clear_user(buf, PAGE_SIZE))
break;
@@ -247,6 +272,7 @@
buf += PAGE_SIZE;
size -= PAGE_SIZE;
} while (size);
+
return size;
}