clk: zynqmp: fix memory leak in zynqmp_register_clocks
[linux-2.6-microblaze.git] / mm / shmem.c
index 97448a4..d722eb8 100644 (file)
@@ -788,6 +788,32 @@ void shmem_unlock_mapping(struct address_space *mapping)
        }
 }
 
+/*
+ * Check whether a hole-punch or truncation needs to split a huge page,
+ * returning true if no split was required, or the split has been successful.
+ *
+ * Eviction (or truncation to 0 size) should never need to split a huge page;
+ * but in rare cases might do so, if shmem_undo_range() failed to trylock on
+ * head, and then succeeded to trylock on tail.
+ *
+ * A split can only succeed when there are no additional references on the
+ * huge page: so the split below relies upon find_get_entries() having stopped
+ * when it found a subpage of the huge page, without getting further references.
+ */
+static bool shmem_punch_compound(struct page *page, pgoff_t start, pgoff_t end)
+{
+       if (!PageTransCompound(page))
+               return true;
+
+       /* Just proceed to delete a huge page wholly within the range punched */
+       if (PageHead(page) &&
+           page->index >= start && page->index + HPAGE_PMD_NR <= end)
+               return true;
+
+       /* Try to split huge page, so we can truly punch the hole or truncate */
+       return split_huge_page(page) >= 0;
+}
+
 /*
  * Remove range of pages and swap entries from page cache, and free them.
  * If !unfalloc, truncate or punch hole; if unfalloc, undo failed fallocate.
@@ -838,31 +864,11 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
                        if (!trylock_page(page))
                                continue;
 
-                       if (PageTransTail(page)) {
-                               /* Middle of THP: zero out the page */
-                               clear_highpage(page);
-                               unlock_page(page);
-                               continue;
-                       } else if (PageTransHuge(page)) {
-                               if (index == round_down(end, HPAGE_PMD_NR)) {
-                                       /*
-                                        * Range ends in the middle of THP:
-                                        * zero out the page
-                                        */
-                                       clear_highpage(page);
-                                       unlock_page(page);
-                                       continue;
-                               }
-                               index += HPAGE_PMD_NR - 1;
-                               i += HPAGE_PMD_NR - 1;
-                       }
-
-                       if (!unfalloc || !PageUptodate(page)) {
-                               VM_BUG_ON_PAGE(PageTail(page), page);
-                               if (page_mapping(page) == mapping) {
-                                       VM_BUG_ON_PAGE(PageWriteback(page), page);
+                       if ((!unfalloc || !PageUptodate(page)) &&
+                           page_mapping(page) == mapping) {
+                               VM_BUG_ON_PAGE(PageWriteback(page), page);
+                               if (shmem_punch_compound(page, start, end))
                                        truncate_inode_page(mapping, page);
-                               }
                        }
                        unlock_page(page);
                }
@@ -936,43 +942,25 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
 
                        lock_page(page);
 
-                       if (PageTransTail(page)) {
-                               /* Middle of THP: zero out the page */
-                               clear_highpage(page);
-                               unlock_page(page);
-                               /*
-                                * Partial thp truncate due 'start' in middle
-                                * of THP: don't need to look on these pages
-                                * again on !pvec.nr restart.
-                                */
-                               if (index != round_down(end, HPAGE_PMD_NR))
-                                       start++;
-                               continue;
-                       } else if (PageTransHuge(page)) {
-                               if (index == round_down(end, HPAGE_PMD_NR)) {
-                                       /*
-                                        * Range ends in the middle of THP:
-                                        * zero out the page
-                                        */
-                                       clear_highpage(page);
-                                       unlock_page(page);
-                                       continue;
-                               }
-                               index += HPAGE_PMD_NR - 1;
-                               i += HPAGE_PMD_NR - 1;
-                       }
-
                        if (!unfalloc || !PageUptodate(page)) {
-                               VM_BUG_ON_PAGE(PageTail(page), page);
-                               if (page_mapping(page) == mapping) {
-                                       VM_BUG_ON_PAGE(PageWriteback(page), page);
-                                       truncate_inode_page(mapping, page);
-                               } else {
+                               if (page_mapping(page) != mapping) {
                                        /* Page was replaced by swap: retry */
                                        unlock_page(page);
                                        index--;
                                        break;
                                }
+                               VM_BUG_ON_PAGE(PageWriteback(page), page);
+                               if (shmem_punch_compound(page, start, end))
+                                       truncate_inode_page(mapping, page);
+                               else {
+                                       /* Wipe the page and don't get stuck */
+                                       clear_highpage(page);
+                                       flush_dcache_page(page);
+                                       set_page_dirty(page);
+                                       if (index <
+                                           round_up(start, HPAGE_PMD_NR))
+                                               start = index + 1;
+                               }
                        }
                        unlock_page(page);
                }
@@ -3120,12 +3108,9 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
 
        error = security_inode_init_security(inode, dir, &dentry->d_name,
                                             shmem_initxattrs, NULL);
-       if (error) {
-               if (error != -EOPNOTSUPP) {
-                       iput(inode);
-                       return error;
-               }
-               error = 0;
+       if (error && error != -EOPNOTSUPP) {
+               iput(inode);
+               return error;
        }
 
        inode->i_size = len-1;
@@ -4011,7 +3996,7 @@ bool shmem_huge_enabled(struct vm_area_struct *vma)
                        if (i_size >= HPAGE_PMD_SIZE &&
                                        i_size >> PAGE_SHIFT >= off)
                                return true;
-                       /* fall through */
+                       fallthrough;
                case SHMEM_HUGE_ADVISE:
                        /* TODO: implement fadvise() hints */
                        return (vma->vm_flags & VM_HUGEPAGE);