Re: Linux-2.5.16

From: Paul Mackerras (paulus@samba.org)
Date: Mon May 20 2002 - 07:43:17 EST


Linus,

This patch splits up the existing tlb_remove_page into
tlb_remove_tlb_entry (for pages that are/were mapped into userspace)
and tlb_remove_page, as you suggested. It also adds the necessary
stuff for PPC, which has its own include/asm-ppc/tlb.h now. This
works on at least one PPC machine. :)

Thanks,
Paul.

diff -urN linux-2.5/include/asm-generic/tlb.h pmac-2.5/include/asm-generic/tlb.h
--- linux-2.5/include/asm-generic/tlb.h Sun May 19 21:04:28 2002
+++ pmac-2.5/include/asm-generic/tlb.h Mon May 20 11:46:18 2002
@@ -83,11 +83,10 @@
         tlb_flush_mmu(tlb, start, end);
 }
 
-
-/* void tlb_remove_page(mmu_gather_t *tlb, pte_t *ptep, unsigned long addr)
- * Must perform the equivalent to __free_pte(pte_get_and_clear(ptep)), while
- * handling the additional races in SMP caused by other CPUs caching valid
- * mappings in their TLBs.
+/* void tlb_remove_page(mmu_gather_t *tlb, struct page *page)
+ * This should free the page given after flushing any reference
+ * to it from the TLB. This should be done no later than the
+ * next call to tlb_finish_mmu for this tlb.
  */
 static inline void tlb_remove_page(mmu_gather_t *tlb, struct page *page)
 {
@@ -99,6 +98,18 @@
         tlb->pages[tlb->nr++] = page;
         if (tlb->nr >= FREE_PTE_NR)
                 tlb_flush_mmu(tlb, 0, 0);
+}
+
+/* void tlb_remove_tlb_entry(mmu_gather_t *tlb, struct page *page, unsigned long address)
+ * This is similar to tlb_remove_page, except that we are given the
+ * virtual address at which the page was mapped. The address parameter
+ * is unused here but is used on some architectures.
+ */
+static inline void tlb_remove_tlb_entry(mmu_gather_t *tlb, struct page *page,
+ unsigned long address)
+{
+ tlb->freed++;
+ tlb_remove_page(tlb, page);
 }
 
 #endif /* _ASM_GENERIC__TLB_H */
diff -urN linux-2.5/mm/memory.c pmac-2.5/mm/memory.c
--- linux-2.5/mm/memory.c Thu May 16 20:31:42 2002
+++ pmac-2.5/mm/memory.c Mon May 20 14:06:58 2002
@@ -353,7 +353,7 @@
                                 if (!PageReserved(page)) {
                                         if (pte_dirty(pte))
                                                 set_page_dirty(page);
- tlb_remove_page(tlb, page);
+ tlb_remove_tlb_entry(tlb, page, address+offset);
                                 }
                         }
                 } else {
diff -urN linux-2.5/include/asm-ppc/tlb.h pmac-2.5/include/asm-ppc/tlb.h
--- linux-2.5/include/asm-ppc/tlb.h Tue Feb 5 18:40:23 2002
+++ pmac-2.5/include/asm-ppc/tlb.h Mon May 20 20:14:20 2002
@@ -1,4 +1,141 @@
 /*
- * BK Id: SCCS/s.tlb.h 1.5 05/17/01 18:14:26 cort
+ * include/asm-ppc/tlb.h
+ *
+ * TLB shootdown code for PPC
+ *
+ * Based on include/asm-generic/tlb.h, which is
+ * Copyright 2001 Red Hat, Inc.
+ * Based on code from mm/memory.c Copyright Linus Torvalds and others.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  */
-#include <asm-generic/tlb.h>
+#ifndef _ASM_PPC__TLB_H
+#define _ASM_PPC__TLB_H
+
+#include <linux/config.h>
+#include <asm/tlbflush.h>
+#include <asm/page.h>
+
+/*
+ * This makes sizeof(mmu_gather_t) a power of 2.
+ * We assume we get some advantage from batching up the invalidations.
+ * It would be nice to measure how much ...
+ */
+#define FREE_PTE_NR 507
+
+/* mmu_gather_t is an opaque type used by the mm code for passing around any
+ * data needed by arch specific code for tlb_remove_page. This structure can
+ * be per-CPU or per-MM as the page table lock is held for the duration of TLB
+ * shootdown.
+ */
+typedef struct free_pte_ctx {
+ struct mm_struct *mm;
+ unsigned long nr; /* set to ~0UL means fast mode */
+ unsigned long freed;
+ unsigned long start;
+ unsigned long end;
+ struct page * pages[FREE_PTE_NR];
+} mmu_gather_t;
+
+/* Declared in arch/ppc/mm/init.c. */
+extern mmu_gather_t mmu_gathers[NR_CPUS];
+
+/*
+ * Actually do the flushes that we have gathered up, and
+ * then free the corresponding pages.
+ */
+static inline void tlb_flush_mmu(mmu_gather_t *tlb)
+{
+ unsigned long nr;
+
+ nr = tlb->nr;
+ if (nr != 0) {
+ unsigned long i;
+ flush_tlb_mm_range(tlb->mm, tlb->start, tlb->end + PAGE_SIZE);
+ tlb->nr = 0;
+ for (i = 0; i < nr; i++)
+ free_page_and_swap_cache(tlb->pages[i]);
+ }
+}
+
+/* tlb_gather_mmu
+ * Return a pointer to an initialized mmu_gather_t.
+ */
+static inline mmu_gather_t *tlb_gather_mmu(struct mm_struct *mm)
+{
+ mmu_gather_t *tlb = &mmu_gathers[smp_processor_id()];
+
+ tlb->mm = mm;
+ tlb->freed = 0;
+ tlb->nr = 0;
+ return tlb;
+}
+
+/* tlb_finish_mmu
+ * Called at the end of the shootdown operation to free up any resources
+ * that were required. The page table lock is still held at this point.
+ */
+static inline void tlb_finish_mmu(mmu_gather_t *tlb, unsigned long start, unsigned long end)
+{
+ int freed = tlb->freed;
+ struct mm_struct *mm = tlb->mm;
+ int rss = mm->rss;
+
+ if (rss < freed)
+ freed = rss;
+ mm->rss = rss - freed;
+}
+
+/* Nothing needed here in fact... */
+#define tlb_start_vma(tlb, vma) do { } while (0)
+
+/*
+ * flush_tlb_mm_range looks at the pte pages for the range of addresses
+ * in order to check the _PAGE_HASHPTE bit. Thus we can't defer
+ * the tlb_flush_mmu call to tlb_finish_mmu time, since by then the
+ * pointers to the pte pages in the pgdir have been zeroed.
+ * Instead we do the tlb_flush_mmu here. In future we could possibly
+ * do something cleverer, like keeping our own pointer(s) to the pte
+ * page(s) that we are interested in.
+ */
+static inline void tlb_end_vma(mmu_gather_t *tlb, struct vm_area_struct *vma)
+{
+ tlb_flush_mmu(tlb);
+}
+
+/* void tlb_remove_page(mmu_gather_t *tlb, struct page *page)
+ *
+ * On PPC this should never be called.
+ */
+static inline void tlb_remove_page(mmu_gather_t *tlb, struct page *page)
+{
+ BUG();
+}
+
+/* void tlb_remove_tlb_entry(mmu_gather_t *tlb, struct page *page, unsigned long address)
+ * This should free the page given after flushing any reference
+ * to it from the MMU hash table and TLB. This should be done no
+ * later than the next call to tlb_finish_mmu for this tlb.
+ * We get given the virtual address at which the page was mapped.
+ */
+static inline void tlb_remove_tlb_entry(mmu_gather_t *tlb, struct page *page,
+ unsigned long address)
+{
+ tlb->freed++;
+
+ if (tlb->nr == 0)
+ tlb->start = address;
+ else if (address - tlb->end > 32 * PAGE_SIZE) {
+ tlb_flush_mmu(tlb);
+ tlb->start = address;
+ }
+ tlb->end = address;
+ tlb->pages[tlb->nr++] = page;
+ if (tlb->nr >= FREE_PTE_NR)
+ tlb_flush_mmu(tlb);
+}
+
+#endif /* _ASM_PPC__TLB_H */
diff -urN linux-2.5/include/asm-ppc/tlbflush.h pmac-2.5/include/asm-ppc/tlbflush.h
--- linux-2.5/include/asm-ppc/tlbflush.h Fri May 10 10:14:59 2002
+++ pmac-2.5/include/asm-ppc/tlbflush.h Mon May 20 12:00:24 2002
@@ -27,6 +27,9 @@
         { __tlbia(); }
 static inline void flush_tlb_mm(struct mm_struct *mm)
         { __tlbia(); }
+static inline void flush_tlb_mm_range(struct mm_struct *mm,
+ unsigned long start, unsigned long end);
+ { __tlbia(); }
 static inline void flush_tlb_page(struct vm_area_struct *vma,
                                 unsigned long vmaddr)
         { _tlbie(vmaddr); }
@@ -45,6 +48,9 @@
         { __tlbia(); }
 static inline void flush_tlb_mm(struct mm_struct *mm)
         { __tlbia(); }
+static inline void flush_tlb_mm_range(struct mm_struct *mm,
+ unsigned long start, unsigned long end)
+ { __tlbia(); }
 static inline void flush_tlb_page(struct vm_area_struct *vma,
                                 unsigned long vmaddr)
         { _tlbie(vmaddr); }
@@ -61,6 +67,8 @@
 struct vm_area_struct;
 extern void flush_tlb_all(void);
 extern void flush_tlb_mm(struct mm_struct *mm);
+extern void flush_tlb_mm_range(struct mm_struct *mm,
+ unsigned long start, unsigned long end);
 extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
 extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
                             unsigned long end);
diff -urN linux-2.5/include/asm-ppc/pgalloc.h pmac-2.5/include/asm-ppc/pgalloc.h
--- linux-2.5/include/asm-ppc/pgalloc.h Wed Apr 10 17:56:21 2002
+++ pmac-2.5/include/asm-ppc/pgalloc.h Mon May 20 11:18:03 2002
@@ -20,6 +20,7 @@
  */
 #define pmd_alloc_one(mm,address) ({ BUG(); ((pmd_t *)2); })
 #define pmd_free(x) do { } while (0)
+#define pmd_free_tlb(tlb,x) do { } while (0)
 #define pgd_populate(mm, pmd, pte) BUG()
 
 #define pmd_populate_kernel(mm, pmd, pte) \
@@ -31,6 +32,8 @@
 extern struct page *pte_alloc_one(struct mm_struct *mm, unsigned long addr);
 extern void pte_free_kernel(pte_t *pte);
 extern void pte_free(struct page *pte);
+
+#define pte_free_tlb(tlb, pte) pte_free((pte))
 
 #define check_pgt_cache() do { } while (0)
 
diff -urN linux-2.5/arch/ppc/mm/tlb.c pmac-2.5/arch/ppc/mm/tlb.c
--- linux-2.5/arch/ppc/mm/tlb.c Mon Apr 15 09:48:49 2002
+++ pmac-2.5/arch/ppc/mm/tlb.c Mon May 20 22:21:40 2002
@@ -59,7 +59,7 @@
 #define FINISH_FLUSH do { } while (0)
 #endif
 
-static void flush_range(struct mm_struct *mm, unsigned long start,
+void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
                         unsigned long end)
 {
         pmd_t *pmd;
@@ -110,7 +110,7 @@
          */
         printk(KERN_ERR "flush_tlb_all called from %p\n",
                __builtin_return_address(0));
- flush_range(&init_mm, TASK_SIZE, ~0UL);
+ flush_tlb_mm_range(&init_mm, TASK_SIZE, ~0UL);
         FINISH_FLUSH;
 }
 
@@ -119,7 +119,7 @@
  */
 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 {
- flush_range(&init_mm, start, end);
+ flush_tlb_mm_range(&init_mm, start, end);
         FINISH_FLUSH;
 }
 
@@ -130,18 +130,15 @@
  */
 void flush_tlb_mm(struct mm_struct *mm)
 {
+ struct vm_area_struct *mp;
+
         if (Hash == 0) {
                 _tlbia();
                 return;
         }
 
- if (mm->map_count) {
- struct vm_area_struct *mp;
- for (mp = mm->mmap; mp != NULL; mp = mp->vm_next)
- flush_range(mp->vm_mm, mp->vm_start, mp->vm_end);
- } else {
- flush_range(mm, 0, TASK_SIZE);
- }
+ for (mp = mm->mmap; mp != NULL; mp = mp->vm_next)
+ flush_tlb_mm_range(mp->vm_mm, mp->vm_start, mp->vm_end);
         FINISH_FLUSH;
 }
 
@@ -170,6 +167,6 @@
 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
                      unsigned long end)
 {
- flush_range(vma->vm_mm, start, end);
+ flush_tlb_mm_range(vma->vm_mm, start, end);
         FINISH_FLUSH;
 }
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Thu May 23 2002 - 22:00:19 EST