huge tmpfs: fix split_huge_page() after FALLOC_FL_KEEP_SIZE
[sfrench/cifs-2.6.git] / mm / shmem.c
index 9ef579f6cab334dec0637bb974be1657e9db21de..e391325dfc21440bd1c7e9f968518cf4190d618f 100644 (file)
@@ -902,6 +902,9 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
        if (lend == -1)
                end = -1;       /* unsigned, so actually very big */
 
+       if (info->fallocend > start && info->fallocend <= end && !unfalloc)
+               info->fallocend = start;
+
        pagevec_init(&pvec);
        index = start;
        while (index < end && find_lock_entries(mapping, index, end - 1,
@@ -2650,7 +2653,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
        struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
        struct shmem_inode_info *info = SHMEM_I(inode);
        struct shmem_falloc shmem_falloc;
-       pgoff_t start, index, end;
+       pgoff_t start, index, end, undo_fallocend;
        int error;
 
        if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
@@ -2719,6 +2722,15 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
        inode->i_private = &shmem_falloc;
        spin_unlock(&inode->i_lock);
 
+       /*
+        * info->fallocend is only relevant when huge pages might be
+        * involved: to prevent split_huge_page() freeing fallocated
+        * pages when FALLOC_FL_KEEP_SIZE committed beyond i_size.
+        */
+       undo_fallocend = info->fallocend;
+       if (info->fallocend < end)
+               info->fallocend = end;
+
        for (index = start; index < end; ) {
                struct page *page;
 
@@ -2733,6 +2745,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
                else
                        error = shmem_getpage(inode, index, &page, SGP_FALLOC);
                if (error) {
+                       info->fallocend = undo_fallocend;
                        /* Remove the !PageUptodate pages we added */
                        if (index > start) {
                                shmem_undo_range(inode,