* Any shadow entries of evicted pages, or swap entries from
  * shmem/tmpfs, are included in the returned array.
  *
+ * If it finds a Transparent Huge Page, head or tail, find_get_entries()
+ * stops at that page: the caller is likely to have a better way to handle
+ * the compound page as a whole, and then skip its extent, than repeatedly
+ * calling find_get_entries() to return all its tails.
+ *
  * Return: the number of pages and shadow entries which were found.
  */
 unsigned find_get_entries(struct address_space *mapping,
                /* Has the page moved or been split? */
                if (unlikely(page != xas_reload(&xas)))
                        goto put_page;
-               page = find_subpage(page, xas.xa_index);
 
+               /*
+                * Terminate early on finding a THP, to allow the caller to
+                * handle it all at once; but continue if this is hugetlbfs.
+                */
+               if (PageTransHuge(page) && !PageHuge(page)) {
+                       page = find_subpage(page, xas.xa_index);
+                       nr_entries = ret + 1;
+               }
 export:
                indices[ret] = xas.xa_index;
                entries[ret] = page;
 
        }
 }
 
+/*
+ * 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.
                        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);
                }
 
                        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);
                }