shmem: fix splicing from a hole while it's punched
[pandora-kernel.git] / mm / shmem.c
index ec7244b..1371021 100644 (file)
@@ -499,22 +499,19 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
        }
 
        index = start;
-       for ( ; ; ) {
+       while (index <= end) {
                cond_resched();
                pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
                        min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
                                                        pvec.pages, indices);
                if (!pvec.nr) {
-                       if (index == start)
+                       /* If all gone or hole-punch, we're done */
+                       if (index == start || end != -1)
                                break;
+                       /* But if truncating, restart to make sure all gone */
                        index = start;
                        continue;
                }
-               if (index == start && indices[0] > end) {
-                       shmem_deswap_pagevec(&pvec);
-                       pagevec_release(&pvec);
-                       break;
-               }
                mem_cgroup_uncharge_start();
                for (i = 0; i < pagevec_count(&pvec); i++) {
                        struct page *page = pvec.pages[i];
@@ -524,8 +521,12 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
                                break;
 
                        if (radix_tree_exceptional_entry(page)) {
-                               nr_swaps_freed += !shmem_free_swap(mapping,
-                                                               index, page);
+                               if (shmem_free_swap(mapping, index, page)) {
+                                       /* Swap was replaced by page: retry */
+                                       index--;
+                                       break;
+                               }
+                               nr_swaps_freed++;
                                continue;
                        }
 
@@ -533,6 +534,11 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
                        if (page->mapping == mapping) {
                                VM_BUG_ON(PageWriteback(page));
                                truncate_inode_page(mapping, page);
+                       } else {
+                               /* Page was replaced by swap: retry */
+                               unlock_page(page);
+                               index--;
+                               break;
                        }
                        unlock_page(page);
                }