Project Home
Project Home
Documents
Documents
Wiki
Wiki
Discussion Forums
Discussions
Project Information
Project Info
Forum Topic - mmap with PROT_WRITE appears to force an unnecessary flush to disk even if msync isn’t called: (2 Items)
   
mmap with PROT_WRITE appears to force an unnecessary flush to disk even if msync isn’t called  
I’m posting this here (instead of the filesystem forum) since it happens on more than one filesystem type (which leads 
me to believe it’s a bug with mmap, not with the filesystems).

In my application, I open a file as read/write, mmap it in, compare it with new data, and only if the new data is 
different do I copy the changed data into the mmap’d buffer and call msync. The goal is to avoid unnecessary writes to 
our NOR flash filesystem.

However, on both our devf-generic NOR flash FFS3 and devb-eide compact flash IDE QNX4 filesystems, it appears that 
simply mmap’ing the file with PROT_WRITE is enough to force some kind of a flush out to the physical storage on munmap 
(even if msync was never called because the data didn’t change). If all I do is mmap with PROT_WRITE and munmap, the 
flash filesystem ends up reclaiming blocks (and periodically getting delayed for a couple seconds because of this).

The workaround is pretty straightforward: If I do my initial mmap with PROT_READ only, compare my contents, and then do 
an mprotect with PROT_WRITE only if the contents have changed, the unnecessary filesystem flushes don’t occur.

Interestingly, if I do my initial mmap with PROT_READ only, then do an mprotect with PROT_READ | PROT_WRITE, and then 
munmap (no msync), the filesystem data doesn’t appear to be flushed. Only if the initial mmap has the PROT_WRITE flag 
is the physical storage unnecessarily touched.

I’m attaching a sample program that runs through several different sets of mmap and mprotect flags (runs forever with 
100 runs in each set). You need to pass in a filename that it will write to, and optionally the string values that it 
will write. Here is the example output from our NOR flash filesystem (as previously mentioned, I was able to reproduce 
the relative differences on a compact flash IDE filesystem as well, though the absolute delays are much shorter):

# /tmp/z_test_mmap /persist/test_file.txt
Toggling between writing hello and hello to /persist/test_file.txt
Elapsed time 11339 msec for 100 cycles of mmap PROT_READ | PROT_WRITE, no additional mprotect
Elapsed time    63 msec for 100 cycles of mmap PROT_READ, mprotect PROT_READ | PROT_WRITE
Elapsed time    62 msec for 100 cycles of mmap PROT_READ, no additional mprotect (file is not actually updated even on a
 change)
Elapsed time 11495 msec for 100 cycles of mmap PROT_READ | PROT_WRITE, mprotect PROT_READ (file is not actually updated 
even on a change)
Elapsed time    66 msec for 100 cycles of mmap PROT_READ, mprotect PROT_READ | PROT_WRITE, but no mysnc (file is not 
actually updated even on a change)

# /tmp/z_test_mmap /persist/test_file.txt hello world
Toggling between writing hello and world to /persist/test_file.txt
Elapsed time 11594 msec for 100 cycles of mmap PROT_READ | PROT_WRITE, no additional mprotect
Elapsed time 11438 msec for 100 cycles of mmap PROT_READ, mprotect PROT_READ | PROT_WRITE
Elapsed time    78 msec for 100 cycles of mmap PROT_READ, no additional mprotect (file is not actually updated even on a
 change)
Elapsed time 13454 msec for 100 cycles of mmap PROT_READ | PROT_WRITE, mprotect PROT_READ (file is not actually updated 
even on a change)
Elapsed time    33 msec for 100 cycles of mmap PROT_READ, mprotect PROT_READ | PROT_WRITE, but no mysnc (file is not 
actually updated even on a change)
Attachment: Text z_test_mmap.c 4.79 KB
Re: mmap with PROT_WRITE appears to force an unnecessary flush to disk even if msync isn’t called  
Hi,

I've provided the same information to your support case and our bug tracking system.  This is a bug in the memmgr for 
sure.  The crux is that code in the OS (memory_reference() ) can turn on both NEEDSSYNC and MODIFIED to the mapping even
 if the underlying file backing mapping hasn't been modified itself.  The logic is simply to check if PROT_WRITE is on, 
and then turn on MR_WRITE.  The behaviour is likely not intended as doing you've shown that PROT_READ mapping avoids 
this and you can then do an mprotect() to turn on the PROT_WRITE post mapping (and still not get flushed).

While I'm not completely familiar with _why_ the unconditional PROT_WRITE=MR_WRITE logic exists (I'm sure there is a 
wierd corner case), I've provided a patch that undoes any pending 'NEEDSSYNC" after setup since there isn't a way for 
the user to have modified the mapping before we've returned from mmap().

You can use the patch if you're blocked while the OS team reviews the issue.

Thanks!
-Adam


Here is a patch against 6.4.1:

Index: memmgr/vmm_mmap.c
===================================================================
--- memmgr/vmm_mmap.c   (revision 274448)
+++ memmgr/vmm_mmap.c   (working copy)
@@ -380,6 +380,18 @@
                        if(mm->end >= end_vaddr) break;
                        mm = mm->next;
                }
+               /* ADAM */ 
+               {
+                       /* Memory reference might have set the NEEDSSYNC even
though it really shouldn't since
+                               we haven't returned control to the user and we
don't modify the underlying object
+                               at any point on their behalf.  The last end of
the mappings (zero fill) could be
+                               due to the OS but it's neither part of the file
nor should it result in a sync.
+                       */
+                       if(mm->obj_ref && mm->obj_ref->obp &&
mm->obj_ref->obp->hdr.type == OBJECT_MEM_FD) {
+                       obp->mem.mm.flags &= ~MM_FDMEM_NEEDSSYNC;
+            }
+        }
+
        }
        return r;
 }