diff --git a/arch/powerpc/include/asm/tlb.h b/arch/powerpc/include/asm/tlb.h
index 9138baccebb0..5d3107f2b014 100644
--- a/arch/powerpc/include/asm/tlb.h
+++ b/arch/powerpc/include/asm/tlb.h
@@ -30,6 +30,40 @@
#define __tlb_remove_tlb_entry __tlb_remove_tlb_entry
#define tlb_remove_check_page_size_change tlb_remove_check_page_size_change
+#ifdef CONFIG_PPC_BOOK3S_64
+/*
+ * powerpc book3s hash does not have page table structure caches, and
+ * radix requires explicit management with PWC invalidate tlb type, so
+ * there is no need to expand the mmu_gather range over invalidated page
+ * table pages like the generic code does.
+ */
+
+#define pte_free_tlb(tlb, ptep, address) \
+ do { \
+ __pte_free_tlb(tlb, ptep, address); \
+ } while (0)
+
+#define pmd_free_tlb(tlb, pmdp, address) \
+ do { \
+ __pmd_free_tlb(tlb, pmdp, address); \
+ } while (0)
+
+#define pud_free_tlb(tlb, pudp, address) \
+ do { \
+ __pud_free_tlb(tlb, pudp, address); \
+ } while (0)
+
+/*
+ * Radix sets need_flush_all when page table pages have been unmapped
+ * and the PWC needs flushing. Generic code must call our tlb_flush
+ * even on empty ranges in this case.
+ *
+ * This will always be false for hash.
+ */
+#define arch_tlb_mustflush(tlb) (tlb->need_flush_all)
+
+#endif
+
extern void tlb_flush(struct mmu_gather *tlb);
/* Get the generic bits... */diff --git a/arch/powerpc/mm/tlb-radix.c b/arch/powerpc/mm/tlb-radix.c
index 1135b43a597c..238b20a513e7 100644
--- a/arch/powerpc/mm/tlb-radix.c
+++ b/arch/powerpc/mm/tlb-radix.c
@@ -862,6 +862,16 @@ void radix__tlb_flush(struct mmu_gather *tlb)
unsigned long start = tlb->start;
unsigned long end = tlb->end;
+ /*
+ * This can happen if need_flush_all is set due to a page table
+ * invalidate, but no ptes under it freed (see arch_tlb_mustflush).
+ * Set end = start to prevent any TLB flushing here (only PWC).
+ */
+ if (!end) {
+ WARN_ON_ONCE(!tlb->need_flush_all);
+ end = start;
+ }
+
/*
* if page size is not something we understand, do a full mm flush
*diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h
index b320c0cc8996..a55ef1425f0d 100644
--- a/include/asm-generic/tlb.h
+++ b/include/asm-generic/tlb.h
@@ -285,6 +285,11 @@ static inline void tlb_remove_check_page_size_change(struct mmu_gather *tlb,
* http://lkml.kernel.org/r/CA+55aFzBggoXtNXQeng5d_mRoDnaMBE5Y+URs+PHR67nUpMtaw@mail.gmail.com
*
* For now w.r.t page table cache, mark the range_size as PAGE_SIZE
+ *
+ * Update: powerpc (Book3S 64-bit, radix MMU) has an architected page table
+ * cache (called PWC), and invalidates it specifically. It sets the
+ * need_flush_all flag to indicate the PWC requires flushing, so it defines
+ * its own p??_free_tlb functions which do not expand the TLB range.
*/
#ifndef pte_free_tlb
--
2.17.0