tux3: Support mmap write: Fix race of mmap write with write(2) on delta boundary

Clear writable to protect oldpage from following mmap write race.

       cpu0                          cpu1                   cpu2
                                                          [mmap write]
                                                          mmap write(old)
                                                              page fault
                                    [backend]                 dirty old
                                    delta++
   [page_fault]
   page fork
       [*A]
                                                          mmap write(old)
                                                              no page fault
       copy_page(new, old)                                    modify page
       replace_pte(new, old)
                                    flusher
                                    page_mkclean(old)

There is delay between delta++ and page_mkclean() for I/O. So,
while cpu0 copying data on page by pagefork, another cpu (cpu2)
can change data on the same page. If this race happens, new and old
page can have different data.

To fix this race, we should make PTE read-only before start pagefork
at [*A] place.

Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
diff --git a/fs/tux3/buffer_fork.c b/fs/tux3/buffer_fork.c
index fd3508e..c1530c2 100644
--- a/fs/tux3/buffer_fork.c
+++ b/fs/tux3/buffer_fork.c
@@ -198,6 +198,42 @@
 #include "mmap_builtin_hack.h"
 
 /*
+ * Clear writable to protect oldpage from following mmap write race.
+ *
+ *        cpu0                          cpu1                   cpu2
+ *                                                           [mmap write]
+ *                                                           mmap write(old)
+ *                                                               page fault
+ *                                     [backend]                 dirty old
+ *                                     delta++
+ *    [page_fault]
+ *    page fork
+ *                                                           mmap write(old)
+ *                                                               no page fault
+ *        copy_page(new, old)                                    modify page
+ *        replace_pte(new, old)
+ *                                     flusher
+ *                                     page_mkclean(old)
+ *
+ * There is delay between delta++ and page_mkclean() for I/O. So,
+ * while cpu0 copying data on page by page fork, another cpu (cpu2)
+ * can change data on the same page. If this race happens, new and old
+ * page can have different data.
+ */
+static void prepare_clone_page(struct page *page)
+{
+	assert(PageLocked(page));
+
+	/*
+	 * If backend flusher is still not clearing the dirty flag and
+	 * (not call page_mkclean()) for I/O. Call it here to prevent
+	 * above race, instead.
+	 */
+	if (PageDirty(page))
+		page_mkclean(page);
+}
+
+/*
  * This replaces the oldpage on radix-tree with newpage atomically.
  *
  * Similar to migrate_pages(), but the oldpage is for writeout.
@@ -601,6 +637,9 @@
 		goto out;
 	}
 
+	/* Clear writable to protect oldpage from mmap write race */
+	prepare_clone_page(oldpage);
+
 	/*
 	 * We need to buffer fork. Start to clone the oldpage.
 	 */