diff options
Diffstat (limited to 'mm/rmap.c')
| -rw-r--r-- | mm/rmap.c | 43 | 
1 files changed, 36 insertions, 7 deletions
| diff --git a/mm/rmap.c b/mm/rmap.c index 163ac4e6bcee..6a1e8c7f6213 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -621,9 +621,20 @@ void try_to_unmap_flush_dirty(void)  		try_to_unmap_flush();  } +/* + * Bits 0-14 of mm->tlb_flush_batched record pending generations. + * Bits 16-30 of mm->tlb_flush_batched bit record flushed generations. + */ +#define TLB_FLUSH_BATCH_FLUSHED_SHIFT	16 +#define TLB_FLUSH_BATCH_PENDING_MASK			\ +	((1 << (TLB_FLUSH_BATCH_FLUSHED_SHIFT - 1)) - 1) +#define TLB_FLUSH_BATCH_PENDING_LARGE			\ +	(TLB_FLUSH_BATCH_PENDING_MASK / 2) +  static void set_tlb_ubc_flush_pending(struct mm_struct *mm, bool writable)  {  	struct tlbflush_unmap_batch *tlb_ubc = ¤t->tlb_ubc; +	int batch, nbatch;  	arch_tlbbatch_add_mm(&tlb_ubc->arch, mm);  	tlb_ubc->flush_required = true; @@ -633,7 +644,22 @@ static void set_tlb_ubc_flush_pending(struct mm_struct *mm, bool writable)  	 * before the PTE is cleared.  	 */  	barrier(); -	mm->tlb_flush_batched = true; +	batch = atomic_read(&mm->tlb_flush_batched); +retry: +	if ((batch & TLB_FLUSH_BATCH_PENDING_MASK) > TLB_FLUSH_BATCH_PENDING_LARGE) { +		/* +		 * Prevent `pending' from catching up with `flushed' because of +		 * overflow.  Reset `pending' and `flushed' to be 1 and 0 if +		 * `pending' becomes large. +		 */ +		nbatch = atomic_cmpxchg(&mm->tlb_flush_batched, batch, 1); +		if (nbatch != batch) { +			batch = nbatch; +			goto retry; +		} +	} else { +		atomic_inc(&mm->tlb_flush_batched); +	}  	/*  	 * If the PTE was dirty then it's best to assume it's writable. The @@ -680,15 +706,18 @@ static bool should_defer_flush(struct mm_struct *mm, enum ttu_flags flags)   */  void flush_tlb_batched_pending(struct mm_struct *mm)  { -	if (data_race(mm->tlb_flush_batched)) { -		flush_tlb_mm(mm); +	int batch = atomic_read(&mm->tlb_flush_batched); +	int pending = batch & TLB_FLUSH_BATCH_PENDING_MASK; +	int flushed = batch >> TLB_FLUSH_BATCH_FLUSHED_SHIFT; +	if (pending != flushed) { +		flush_tlb_mm(mm);  		/* -		 * Do not allow the compiler to re-order the clearing of -		 * tlb_flush_batched before the tlb is flushed. +		 * If the new TLB flushing is pending during flushing, leave +		 * mm->tlb_flush_batched as is, to avoid losing flushing.  		 */ -		barrier(); -		mm->tlb_flush_batched = false; +		atomic_cmpxchg(&mm->tlb_flush_batched, batch, +			       pending | (pending << TLB_FLUSH_BATCH_FLUSHED_SHIFT));  	}  }  #else |