|
@@ -604,6 +604,13 @@ static void set_tlb_ubc_flush_pending(struct mm_struct *mm, bool writable)
|
|
|
arch_tlbbatch_add_mm(&tlb_ubc->arch, mm);
|
|
|
tlb_ubc->flush_required = true;
|
|
|
|
|
|
+ /*
|
|
|
+ * Ensure compiler does not re-order the setting of tlb_flush_batched
|
|
|
+ * before the PTE is cleared.
|
|
|
+ */
|
|
|
+ barrier();
|
|
|
+ mm->tlb_flush_batched = true;
|
|
|
+
|
|
|
/*
|
|
|
* If the PTE was dirty then it's best to assume it's writable. The
|
|
|
* caller must use try_to_unmap_flush_dirty() or try_to_unmap_flush()
|
|
@@ -631,6 +638,35 @@ static bool should_defer_flush(struct mm_struct *mm, enum ttu_flags flags)
|
|
|
|
|
|
return should_defer;
|
|
|
}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Reclaim unmaps pages under the PTL but do not flush the TLB prior to
|
|
|
+ * releasing the PTL if TLB flushes are batched. It's possible for a parallel
|
|
|
+ * operation such as mprotect or munmap to race between reclaim unmapping
|
|
|
+ * the page and flushing the page. If this race occurs, it potentially allows
|
|
|
+ * access to data via a stale TLB entry. Tracking all mm's that have TLB
|
|
|
+ * batching in flight would be expensive during reclaim so instead track
|
|
|
+ * whether TLB batching occurred in the past and if so then do a flush here
|
|
|
+ * if required. This will cost one additional flush per reclaim cycle paid
|
|
|
+ * by the first operation at risk such as mprotect and mumap.
|
|
|
+ *
|
|
|
+ * This must be called under the PTL so that an access to tlb_flush_batched
|
|
|
+ * that is potentially a "reclaim vs mprotect/munmap/etc" race will synchronise
|
|
|
+ * via the PTL.
|
|
|
+ */
|
|
|
+void flush_tlb_batched_pending(struct mm_struct *mm)
|
|
|
+{
|
|
|
+ if (mm->tlb_flush_batched) {
|
|
|
+ flush_tlb_mm(mm);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Do not allow the compiler to re-order the clearing of
|
|
|
+ * tlb_flush_batched before the tlb is flushed.
|
|
|
+ */
|
|
|
+ barrier();
|
|
|
+ mm->tlb_flush_batched = false;
|
|
|
+ }
|
|
|
+}
|
|
|
#else
|
|
|
static void set_tlb_ubc_flush_pending(struct mm_struct *mm, bool writable)
|
|
|
{
|