clk: zynqmp: fix memory leak in zynqmp_register_clocks
[linux-2.6-microblaze.git] / mm / shmem.c
index f47347c..d722eb8 100644 (file)
@@ -410,7 +410,7 @@ static bool shmem_confirm_swap(struct address_space *mapping,
 #define SHMEM_HUGE_DENY                (-1)
 #define SHMEM_HUGE_FORCE       (-2)
 
-#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 /* ifdef here to avoid bloating shmem.o when not necessary */
 
 static int shmem_huge __read_mostly;
@@ -580,7 +580,7 @@ static long shmem_unused_huge_count(struct super_block *sb,
        struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
        return READ_ONCE(sbinfo->shrinklist_len);
 }
-#else /* !CONFIG_TRANSPARENT_HUGE_PAGECACHE */
+#else /* !CONFIG_TRANSPARENT_HUGEPAGE */
 
 #define shmem_huge SHMEM_HUGE_DENY
 
@@ -589,11 +589,11 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
 {
        return 0;
 }
-#endif /* CONFIG_TRANSPARENT_HUGE_PAGECACHE */
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 static inline bool is_huge_enabled(struct shmem_sb_info *sbinfo)
 {
-       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) &&
+       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
            (shmem_huge == SHMEM_HUGE_FORCE || sbinfo->huge) &&
            shmem_huge != SHMEM_HUGE_DENY)
                return true;
@@ -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);
                }
@@ -1059,7 +1047,7 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
                         * Part of the huge page can be beyond i_size: subject
                         * to shrink under memory pressure.
                         */
-                       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE)) {
+                       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
                                spin_lock(&sbinfo->shrinklist_lock);
                                /*
                                 * _careful to defend against unlocked access to
@@ -1472,9 +1460,6 @@ static struct page *shmem_alloc_hugepage(gfp_t gfp,
        pgoff_t hindex;
        struct page *page;
 
-       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE))
-               return NULL;
-
        hindex = round_down(index, HPAGE_PMD_NR);
        if (xa_find(&mapping->i_pages, &hindex, hindex + HPAGE_PMD_NR - 1,
                                                                XA_PRESENT))
@@ -1486,6 +1471,8 @@ static struct page *shmem_alloc_hugepage(gfp_t gfp,
        shmem_pseudo_vma_destroy(&pvma);
        if (page)
                prep_transhuge_page(page);
+       else
+               count_vm_event(THP_FILE_FALLBACK);
        return page;
 }
 
@@ -1511,7 +1498,7 @@ static struct page *shmem_alloc_and_acct_page(gfp_t gfp,
        int nr;
        int err = -ENOSPC;
 
-       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE))
+       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
                huge = false;
        nr = huge ? HPAGE_PMD_NR : 1;
 
@@ -1813,17 +1800,20 @@ repeat:
        if (shmem_huge == SHMEM_HUGE_FORCE)
                goto alloc_huge;
        switch (sbinfo->huge) {
-               loff_t i_size;
-               pgoff_t off;
        case SHMEM_HUGE_NEVER:
                goto alloc_nohuge;
-       case SHMEM_HUGE_WITHIN_SIZE:
+       case SHMEM_HUGE_WITHIN_SIZE: {
+               loff_t i_size;
+               pgoff_t off;
+
                off = round_up(index, HPAGE_PMD_NR);
                i_size = round_up(i_size_read(inode), PAGE_SIZE);
                if (i_size >= HPAGE_PMD_SIZE &&
                    i_size >> PAGE_SHIFT >= off)
                        goto alloc_huge;
-               /* fallthrough */
+
+               fallthrough;
+       }
        case SHMEM_HUGE_ADVISE:
                if (sgp_huge == SGP_HUGE)
                        goto alloc_huge;
@@ -1871,8 +1861,13 @@ alloc_nohuge:
 
        error = mem_cgroup_try_charge_delay(page, charge_mm, gfp, &memcg,
                                            PageTransHuge(page));
-       if (error)
+       if (error) {
+               if (PageTransHuge(page)) {
+                       count_vm_event(THP_FILE_FALLBACK);
+                       count_vm_event(THP_FILE_FALLBACK_CHARGE);
+               }
                goto unacct;
+       }
        error = shmem_add_to_page_cache(page, mapping, hindex,
                                        NULL, gfp & GFP_RECLAIM_MASK);
        if (error) {
@@ -2089,7 +2084,7 @@ unsigned long shmem_get_unmapped_area(struct file *file,
        get_area = current->mm->get_unmapped_area;
        addr = get_area(file, uaddr, len, pgoff, flags);
 
-       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE))
+       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
                return addr;
        if (IS_ERR_VALUE(addr))
                return addr;
@@ -2228,7 +2223,7 @@ static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
 
        file_accessed(file);
        vma->vm_ops = &shmem_vm_ops;
-       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) &&
+       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
                        ((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
                        (vma->vm_end & HPAGE_PMD_MASK)) {
                khugepaged_enter(vma, vma->vm_flags);
@@ -3113,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;
@@ -3455,7 +3447,7 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
        case Opt_huge:
                ctx->huge = result.uint_32;
                if (ctx->huge != SHMEM_HUGE_NEVER &&
-                   !(IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) &&
+                   !(IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
                      has_transparent_hugepage()))
                        goto unsupported_parameter;
                ctx->seen |= SHMEM_SEEN_HUGE;
@@ -3601,7 +3593,7 @@ static int shmem_show_options(struct seq_file *seq, struct dentry *root)
        if (!gid_eq(sbinfo->gid, GLOBAL_ROOT_GID))
                seq_printf(seq, ",gid=%u",
                                from_kgid_munged(&init_user_ns, sbinfo->gid));
-#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
        /* Rightly or wrongly, show huge mount option unmasked by shmem_huge */
        if (sbinfo->huge)
                seq_printf(seq, ",huge=%s", shmem_format_huge(sbinfo->huge));
@@ -3846,7 +3838,7 @@ static const struct super_operations shmem_ops = {
        .evict_inode    = shmem_evict_inode,
        .drop_inode     = generic_delete_inode,
        .put_super      = shmem_put_super,
-#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
        .nr_cached_objects      = shmem_unused_huge_count,
        .free_cached_objects    = shmem_unused_huge_scan,
 #endif
@@ -3908,7 +3900,7 @@ int __init shmem_init(void)
                goto out1;
        }
 
-#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
        if (has_transparent_hugepage() && shmem_huge > SHMEM_HUGE_DENY)
                SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge;
        else
@@ -3924,7 +3916,7 @@ out2:
        return error;
 }
 
-#if defined(CONFIG_TRANSPARENT_HUGE_PAGECACHE) && defined(CONFIG_SYSFS)
+#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && defined(CONFIG_SYSFS)
 static ssize_t shmem_enabled_show(struct kobject *kobj,
                struct kobj_attribute *attr, char *buf)
 {
@@ -3976,9 +3968,9 @@ static ssize_t shmem_enabled_store(struct kobject *kobj,
 
 struct kobj_attribute shmem_enabled_attr =
        __ATTR(shmem_enabled, 0644, shmem_enabled_show, shmem_enabled_store);
-#endif /* CONFIG_TRANSPARENT_HUGE_PAGECACHE && CONFIG_SYSFS */
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE && CONFIG_SYSFS */
 
-#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 bool shmem_huge_enabled(struct vm_area_struct *vma)
 {
        struct inode *inode = file_inode(vma->vm_file);
@@ -4004,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);
@@ -4013,7 +4005,7 @@ bool shmem_huge_enabled(struct vm_area_struct *vma)
                        return false;
        }
 }
-#endif /* CONFIG_TRANSPARENT_HUGE_PAGECACHE */
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #else /* !CONFIG_SHMEM */
 
@@ -4182,7 +4174,7 @@ int shmem_zero_setup(struct vm_area_struct *vma)
        vma->vm_file = file;
        vma->vm_ops = &shmem_vm_ops;
 
-       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) &&
+       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
                        ((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
                        (vma->vm_end & HPAGE_PMD_MASK)) {
                khugepaged_enter(vma, vma->vm_flags);