Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 20273941 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Linus Torvalds
Browse files

mm: fix race in kunmap_atomic()



Christoph reported a nice splat which illustrated a race in the new stack
based kmap_atomic implementation.

The problem is that we pop our stack slot before we're completely done
resetting its state -- in particular clearing the PTE (sometimes that's
CONFIG_DEBUG_HIGHMEM).  If an interrupt happens before we actually clear
the PTE used for the last slot, that interrupt can reuse the slot in a
dirty state, which triggers a BUG in kmap_atomic().

Fix this by introducing kmap_atomic_idx() which reports the current slot
index without actually releasing it and use that to find the PTE and delay
the _pop() until after we're completely done.

Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Reported-by: default avatarChristoph Hellwig <hch@infradead.org>
Acked-by: default avatarRik van Riel <riel@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent a8e23a29
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ void __kunmap_atomic(void *kvaddr)
	int idx, type;

	if (kvaddr >= (void *)FIXADDR_START) {
		type = kmap_atomic_idx_pop();
		type = kmap_atomic_idx();
		idx = type + KM_TYPE_NR * smp_processor_id();

		if (cache_is_vivt())
@@ -101,6 +101,7 @@ void __kunmap_atomic(void *kvaddr)
#else
		(void) idx;  /* to kill a warning */
#endif
		kmap_atomic_idx_pop();
	} else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
		/* this address was obtained through kmap_high_get() */
		kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
+2 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ EXPORT_SYMBOL(__kmap_atomic);

void __kunmap_atomic(void *kvaddr)
{
	int type = kmap_atomic_idx_pop();
	int type = kmap_atomic_idx();
	switch (type) {
	case 0:		__kunmap_atomic_primary(4, 6);	break;
	case 1:		__kunmap_atomic_primary(5, 7);	break;
@@ -83,6 +83,7 @@ void __kunmap_atomic(void *kvaddr)
	default:
		BUG();
	}
	kmap_atomic_idx_pop();
	pagefault_enable();
}
EXPORT_SYMBOL(__kunmap_atomic);
+2 −1
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@ void __kunmap_atomic(void *kvaddr)
		return;
	}

	type = kmap_atomic_idx_pop();
	type = kmap_atomic_idx();
#ifdef CONFIG_DEBUG_HIGHMEM
	{
		int idx = type + KM_TYPE_NR * smp_processor_id();
@@ -89,6 +89,7 @@ void __kunmap_atomic(void *kvaddr)
		local_flush_tlb_one(vaddr);
	}
#endif
	kmap_atomic_idx_pop();
	pagefault_enable();
}
EXPORT_SYMBOL(__kunmap_atomic);
+3 −1
Original line number Diff line number Diff line
@@ -101,7 +101,7 @@ static inline void __kunmap_atomic(unsigned long vaddr)
		return;
	}

	type = kmap_atomic_idx_pop();
	type = kmap_atomic_idx();

#if HIGHMEM_DEBUG
	{
@@ -119,6 +119,8 @@ static inline void __kunmap_atomic(unsigned long vaddr)
		__flush_tlb_one(vaddr);
	}
#endif

	kmap_atomic_idx_pop();
	pagefault_enable();
}
#endif /* __KERNEL__ */
+3 −1
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ void __kunmap_atomic(void *kvaddr)
		return;
	}

	type = kmap_atomic_idx_pop();
	type = kmap_atomic_idx();

#ifdef CONFIG_DEBUG_HIGHMEM
	{
@@ -79,6 +79,8 @@ void __kunmap_atomic(void *kvaddr)
		local_flush_tlb_page(NULL, vaddr);
	}
#endif

	kmap_atomic_idx_pop();
	pagefault_enable();
}
EXPORT_SYMBOL(__kunmap_atomic);
Loading