Skip to contentSkip to conversation

linux: allow use of split page table locks

PATCH New Jan Beulich 1 participant 1 message Mar 26, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 linux: allow use of split page table locks

This fixes the race condition previously experienced between (un)pinning and vmscan.

arch/i386/mm/pgtable-xen.c +62 -4
Index: head-2007-03-19/arch/i386/mm/pgtable-xen.c
===================================================================
@@ -574,6 +574,64 @@ void make_pages_writable(void *va, unsig
 	}
 }
 
+static void _pin_lock(struct mm_struct *mm, int lock) {
+	if (lock)
+		spin_lock(&mm->page_table_lock);
+#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+	/* While mm->page_table_lock protects us against insertions and
+	 * removals of higher level page table pages, it doesn't protect
+	 * against updates of pte-s. Such updates, however, require the
+	 * pte pages to be in consistent state (unpinned+writable or
+	 * pinned+readonly). The pinning and attribute changes, however
+	 * cannot be done atomically, which is why such updates must be
+	 * prevented from happening concurrently.
+	 * Note that no pte lock can ever elsewhere be acquired nesting
+	 * with an already acquired one in the same mm, or with the mm's
+	 * page_table_lock already acquired, as that would break in the
+	 * non-split case (where all these are actually resolving to the
+	 * one page_table_lock). Thus acquiring all of them here is not
+	 * going to result in dead locks, and the order of acquires
+	 * doesn't matter.
+	 */
+	{
+		pgd_t *pgd = mm->pgd;
+		unsigned g;
+
+		for (g = 0; g < USER_PTRS_PER_PGD; g++, pgd++) {
+			pud_t *pud;
+			unsigned u;
+
+			if (pgd_none(*pgd))
+				continue;
+			pud = pud_offset(pgd, 0);
+			for (u = 0; u < PTRS_PER_PUD; u++, pud++) {
+				pmd_t *pmd;
+				unsigned m;
+
+				if (pud_none(*pud))
+					continue;
+				pmd = pmd_offset(pud, 0);
+				for (m = 0; m < PTRS_PER_PMD; m++, pmd++) {
+					spinlock_t *ptl;
+
+					if (pmd_none(*pmd))
+						continue;
+					ptl = pte_lockptr(0, pmd);
+					if (lock)
+						spin_lock(ptl);
+					else
+						spin_unlock(ptl);
+				}
+			}
+		}
+	}
+#endif
+	if (!lock)
+		spin_unlock(&mm->page_table_lock);
+}
+#define pin_lock(mm) _pin_lock(mm, 1)
+#define pin_unlock(mm) _pin_lock(mm, 0)
+
 static inline void pgd_walk_set_prot(struct page *page, pgprot_t flags)
 {
 	unsigned long pfn = page_to_pfn(page);
@@ -656,18 +714,18 @@ void mm_pin(struct mm_struct *mm)
 {
 	if (xen_feature(XENFEAT_writable_page_tables))
 		return;
-	spin_lock(&mm->page_table_lock);
+	pin_lock(mm);
 	__pgd_pin(mm->pgd);
-	spin_unlock(&mm->page_table_lock);
+	pin_unlock(mm);
 }
 
 void mm_unpin(struct mm_struct *mm)
 {
 	if (xen_feature(XENFEAT_writable_page_tables))
 		return;
-	spin_lock(&mm->page_table_lock);
+	pin_lock(mm);
 	__pgd_unpin(mm->pgd);
-	spin_unlock(&mm->page_table_lock);
+	pin_unlock(mm);
 }
 
 void mm_pin_all(void)
arch/x86_64/mm/pageattr-xen.c +62 -4

96 lines not on this page · raw message

mm/Kconfig +0 -3
Index: head-2007-03-19/mm/Kconfig
===================================================================
@@ -127,14 +127,11 @@ config MEMORY_HOTPLUG_SPARSE
 # Default to 4 for wider testing, though 8 might be more appropriate.
 # ARM's adjust_pte (unused if VIPT) depends on mm-wide page_table_lock.
 # PA-RISC 7xxx's spinlock_t would enlarge struct page from 32 to 44 bytes.
-# XEN on x86 architecture uses the mapping field on pagetable pages to store a
-# pointer to the destructor. This conflicts with pte_lock_deinit().
 #
 config SPLIT_PTLOCK_CPUS
 	int
 	default "4096" if ARM && !CPU_CACHE_VIPT
 	default "4096" if PARISC && !PA20
-	default "4096" if X86_XEN || X86_64_XEN
 	default "4"
 
 #