net/mlx4_en: Fix mixed PFC and Global pause user control requests
[pandora-kernel.git] / mm / truncate.c
index 632b15e..143883a 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/buffer_head.h> /* grr. try_to_release_page,
                                   do_invalidatepage */
 #include <linux/cleancache.h>
+#include <linux/rmap.h>
 #include "internal.h"
 
 
@@ -394,11 +395,12 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page)
        if (page_has_private(page) && !try_to_release_page(page, GFP_KERNEL))
                return 0;
 
+       clear_page_mlock(page);
+
        spin_lock_irq(&mapping->tree_lock);
        if (PageDirty(page))
                goto failed;
 
-       clear_page_mlock(page);
        BUG_ON(page_has_private(page));
        __delete_from_page_cache(page);
        spin_unlock_irq(&mapping->tree_lock);
@@ -574,11 +576,63 @@ void truncate_setsize(struct inode *inode, loff_t newsize)
 
        oldsize = inode->i_size;
        i_size_write(inode, newsize);
-
+       if (newsize > oldsize)
+               pagecache_isize_extended(inode, oldsize, newsize);
        truncate_pagecache(inode, oldsize, newsize);
 }
 EXPORT_SYMBOL(truncate_setsize);
 
+/**
+ * pagecache_isize_extended - update pagecache after extension of i_size
+ * @inode:     inode for which i_size was extended
+ * @from:      original inode size
+ * @to:                new inode size
+ *
+ * Handle extension of inode size either caused by extending truncate or by
+ * write starting after current i_size. We mark the page straddling current
+ * i_size RO so that page_mkwrite() is called on the nearest write access to
+ * the page.  This way filesystem can be sure that page_mkwrite() is called on
+ * the page before user writes to the page via mmap after the i_size has been
+ * changed.
+ *
+ * The function must be called after i_size is updated so that page fault
+ * coming after we unlock the page will already see the new i_size.
+ * The function must be called while we still hold i_mutex - this not only
+ * makes sure i_size is stable but also that userspace cannot observe new
+ * i_size value before we are prepared to store mmap writes at new inode size.
+ */
+void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to)
+{
+       int bsize = 1 << inode->i_blkbits;
+       loff_t rounded_from;
+       struct page *page;
+       pgoff_t index;
+
+       WARN_ON(to > inode->i_size);
+
+       if (from >= to || bsize == PAGE_CACHE_SIZE)
+               return;
+       /* Page straddling @from will not have any hole block created? */
+       rounded_from = round_up(from, bsize);
+       if (to <= rounded_from || !(rounded_from & (PAGE_CACHE_SIZE - 1)))
+               return;
+
+       index = from >> PAGE_CACHE_SHIFT;
+       page = find_lock_page(inode->i_mapping, index);
+       /* Page not cached? Nothing to do */
+       if (!page)
+               return;
+       /*
+        * See clear_page_dirty_for_io() for details why set_page_dirty()
+        * is needed.
+        */
+       if (page_mkclean(page))
+               set_page_dirty(page);
+       unlock_page(page);
+       page_cache_release(page);
+}
+EXPORT_SYMBOL(pagecache_isize_extended);
+
 /**
  * vmtruncate - unmap mappings "freed" by truncate() syscall
  * @inode: inode of the file used
@@ -601,28 +655,3 @@ int vmtruncate(struct inode *inode, loff_t newsize)
        return 0;
 }
 EXPORT_SYMBOL(vmtruncate);
-
-int vmtruncate_range(struct inode *inode, loff_t lstart, loff_t lend)
-{
-       struct address_space *mapping = inode->i_mapping;
-       loff_t holebegin = round_up(lstart, PAGE_SIZE);
-       loff_t holelen = 1 + lend - holebegin;
-
-       /*
-        * If the underlying filesystem is not going to provide
-        * a way to truncate a range of blocks (punch a hole) -
-        * we should return failure right now.
-        */
-       if (!inode->i_op->truncate_range)
-               return -ENOSYS;
-
-       mutex_lock(&inode->i_mutex);
-       inode_dio_wait(inode);
-       unmap_mapping_range(mapping, holebegin, holelen, 1);
-       inode->i_op->truncate_range(inode, lstart, lend);
-       /* unmap again to remove racily COWed private pages */
-       unmap_mapping_range(mapping, holebegin, holelen, 1);
-       mutex_unlock(&inode->i_mutex);
-
-       return 0;
-}