mm,shmem,thp: limit shmem THP allocations to requested zones
[linux-2.6-microblaze.git] / mm / shmem.c
index 5ea1fa5..b2db4ed 100644 (file)
@@ -907,18 +907,12 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
 
        pagevec_init(&pvec);
        index = start;
-       while (index < end) {
-               pvec.nr = find_get_entries(mapping, index,
-                       min(end - index, (pgoff_t)PAGEVEC_SIZE),
-                       pvec.pages, indices);
-               if (!pvec.nr)
-                       break;
+       while (index < end && find_lock_entries(mapping, index, end - 1,
+                       &pvec, indices)) {
                for (i = 0; i < pagevec_count(&pvec); i++) {
                        struct page *page = pvec.pages[i];
 
                        index = indices[i];
-                       if (index >= end)
-                               break;
 
                        if (xa_is_value(page)) {
                                if (unfalloc)
@@ -927,18 +921,10 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
                                                                index, page);
                                continue;
                        }
+                       index += thp_nr_pages(page) - 1;
 
-                       VM_BUG_ON_PAGE(page_to_pgoff(page) != index, page);
-
-                       if (!trylock_page(page))
-                               continue;
-
-                       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);
-                       }
+                       if (!unfalloc || !PageUptodate(page))
+                               truncate_inode_page(mapping, page);
                        unlock_page(page);
                }
                pagevec_remove_exceptionals(&pvec);
@@ -979,10 +965,8 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
        while (index < end) {
                cond_resched();
 
-               pvec.nr = find_get_entries(mapping, index,
-                               min(end - index, (pgoff_t)PAGEVEC_SIZE),
-                               pvec.pages, indices);
-               if (!pvec.nr) {
+               if (!find_get_entries(mapping, index, end - 1, &pvec,
+                               indices)) {
                        /* If all gone or hole-punch or unfalloc, we're done */
                        if (index == start || end != -1)
                                break;
@@ -994,9 +978,6 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
                        struct page *page = pvec.pages[i];
 
                        index = indices[i];
-                       if (index >= end)
-                               break;
-
                        if (xa_is_value(page)) {
                                if (unfalloc)
                                        continue;
@@ -1524,6 +1505,30 @@ static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
        return page;
 }
 
+/*
+ * Make sure huge_gfp is always more limited than limit_gfp.
+ * Some of the flags set permissions, while others set limitations.
+ */
+static gfp_t limit_gfp_mask(gfp_t huge_gfp, gfp_t limit_gfp)
+{
+       gfp_t allowflags = __GFP_IO | __GFP_FS | __GFP_RECLAIM;
+       gfp_t denyflags = __GFP_NOWARN | __GFP_NORETRY;
+       gfp_t zoneflags = limit_gfp & GFP_ZONEMASK;
+       gfp_t result = huge_gfp & ~(allowflags | GFP_ZONEMASK);
+
+       /* Allow allocations only from the originally specified zones. */
+       result |= zoneflags;
+
+       /*
+        * Minimize the result gfp by taking the union with the deny flags,
+        * and the intersection of the allow flags.
+        */
+       result |= (limit_gfp & denyflags);
+       result |= (huge_gfp & limit_gfp) & allowflags;
+
+       return result;
+}
+
 static struct page *shmem_alloc_hugepage(gfp_t gfp,
                struct shmem_inode_info *info, pgoff_t index)
 {
@@ -1538,8 +1543,8 @@ static struct page *shmem_alloc_hugepage(gfp_t gfp,
                return NULL;
 
        shmem_pseudo_vma_init(&pvma, info, hindex);
-       page = alloc_pages_vma(gfp | __GFP_COMP | __GFP_NORETRY | __GFP_NOWARN,
-                       HPAGE_PMD_ORDER, &pvma, 0, numa_node_id(), true);
+       page = alloc_pages_vma(gfp, HPAGE_PMD_ORDER, &pvma, 0, numa_node_id(),
+                              true);
        shmem_pseudo_vma_destroy(&pvma);
        if (page)
                prep_transhuge_page(page);
@@ -1795,6 +1800,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
        struct page *page;
        enum sgp_type sgp_huge = sgp;
        pgoff_t hindex = index;
+       gfp_t huge_gfp;
        int error;
        int once = 0;
        int alloced = 0;
@@ -1812,7 +1818,8 @@ repeat:
        sbinfo = SHMEM_SB(inode->i_sb);
        charge_mm = vma ? vma->vm_mm : current->mm;
 
-       page = find_lock_entry(mapping, index);
+       page = pagecache_get_page(mapping, index,
+                                       FGP_ENTRY | FGP_HEAD | FGP_LOCK, 0);
        if (xa_is_value(page)) {
                error = shmem_swapin_page(inode, index, &page,
                                          sgp, gfp, vma, fault_type);
@@ -1880,7 +1887,9 @@ repeat:
        }
 
 alloc_huge:
-       page = shmem_alloc_and_acct_page(gfp, inode, index, true);
+       huge_gfp = vma_thp_gfp_mask(vma);
+       huge_gfp = limit_gfp_mask(huge_gfp, gfp);
+       page = shmem_alloc_and_acct_page(huge_gfp, inode, index, true);
        if (IS_ERR(page)) {
 alloc_nohuge:
                page = shmem_alloc_and_acct_page(gfp, inode,
@@ -2667,86 +2676,20 @@ static ssize_t shmem_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
        return retval ? retval : error;
 }
 
-/*
- * llseek SEEK_DATA or SEEK_HOLE through the page cache.
- */
-static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
-                                   pgoff_t index, pgoff_t end, int whence)
-{
-       struct page *page;
-       struct pagevec pvec;
-       pgoff_t indices[PAGEVEC_SIZE];
-       bool done = false;
-       int i;
-
-       pagevec_init(&pvec);
-       pvec.nr = 1;            /* start small: we may be there already */
-       while (!done) {
-               pvec.nr = find_get_entries(mapping, index,
-                                       pvec.nr, pvec.pages, indices);
-               if (!pvec.nr) {
-                       if (whence == SEEK_DATA)
-                               index = end;
-                       break;
-               }
-               for (i = 0; i < pvec.nr; i++, index++) {
-                       if (index < indices[i]) {
-                               if (whence == SEEK_HOLE) {
-                                       done = true;
-                                       break;
-                               }
-                               index = indices[i];
-                       }
-                       page = pvec.pages[i];
-                       if (page && !xa_is_value(page)) {
-                               if (!PageUptodate(page))
-                                       page = NULL;
-                       }
-                       if (index >= end ||
-                           (page && whence == SEEK_DATA) ||
-                           (!page && whence == SEEK_HOLE)) {
-                               done = true;
-                               break;
-                       }
-               }
-               pagevec_remove_exceptionals(&pvec);
-               pagevec_release(&pvec);
-               pvec.nr = PAGEVEC_SIZE;
-               cond_resched();
-       }
-       return index;
-}
-
 static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence)
 {
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = mapping->host;
-       pgoff_t start, end;
-       loff_t new_offset;
 
        if (whence != SEEK_DATA && whence != SEEK_HOLE)
                return generic_file_llseek_size(file, offset, whence,
                                        MAX_LFS_FILESIZE, i_size_read(inode));
+       if (offset < 0)
+               return -ENXIO;
+
        inode_lock(inode);
        /* We're holding i_mutex so we can access i_size directly */
-
-       if (offset < 0 || offset >= inode->i_size)
-               offset = -ENXIO;
-       else {
-               start = offset >> PAGE_SHIFT;
-               end = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-               new_offset = shmem_seek_hole_data(mapping, start, end, whence);
-               new_offset <<= PAGE_SHIFT;
-               if (new_offset > offset) {
-                       if (new_offset < inode->i_size)
-                               offset = new_offset;
-                       else if (whence == SEEK_DATA)
-                               offset = -ENXIO;
-                       else
-                               offset = inode->i_size;
-               }
-       }
-
+       offset = mapping_seek_hole_data(mapping, offset, inode->i_size, whence);
        if (offset >= 0)
                offset = vfs_setpos(file, offset, MAX_LFS_FILESIZE);
        inode_unlock(inode);