Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] mm: tlb_finish_mmu forget rss

zap_pte_range has been counting the pages it frees in tlb->freed, then
tlb_finish_mmu has used that to update the mm's rss. That got stranger when I
added anon_rss, yet updated it by a different route; and stranger when rss and
anon_rss became mm_counters with special access macros. And it would no
longer be viable if we're relying on page_table_lock to stabilize the
mm_counter, but calling tlb_finish_mmu outside that lock.

Remove the mmu_gather's freed field, let tlb_finish_mmu stick to its own
business, just decrement the rss mm_counter in zap_pte_range (yes, there was
some point to batching the update, and a subsequent patch restores that). And
forget the anal paranoia of first reading the counter to avoid going negative
- if rss does go negative, just fix that bug.

Remove the mmu_gather's flushes and avoided_flushes from arm and arm26: no use
was being made of them. But arm26 alone was actually using the freed, in the
way some others use need_flush: give it a need_flush. arm26 seems to prefer
spaces to tabs here: respect that.

Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Hugh Dickins and committed by
Linus Torvalds
fc2acab3 4d6ddfa9

+18 -69
+1 -2
arch/sparc64/mm/tlb.c
··· 18 18 19 19 /* Heavily inspired by the ppc64 code. */ 20 20 21 - DEFINE_PER_CPU(struct mmu_gather, mmu_gathers) = 22 - { NULL, 0, 0, 0, 0, 0, { 0 }, { NULL }, }; 21 + DEFINE_PER_CPU(struct mmu_gather, mmu_gathers) = { 0, }; 23 22 24 23 void flush_tlb_pending(void) 25 24 {
+1 -14
include/asm-arm/tlb.h
··· 27 27 */ 28 28 struct mmu_gather { 29 29 struct mm_struct *mm; 30 - unsigned int freed; 31 30 unsigned int fullmm; 32 - 33 - unsigned int flushes; 34 - unsigned int avoided_flushes; 35 31 }; 36 32 37 33 DECLARE_PER_CPU(struct mmu_gather, mmu_gathers); ··· 38 42 struct mmu_gather *tlb = &get_cpu_var(mmu_gathers); 39 43 40 44 tlb->mm = mm; 41 - tlb->freed = 0; 42 45 tlb->fullmm = full_mm_flush; 43 46 44 47 return tlb; ··· 46 51 static inline void 47 52 tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) 48 53 { 49 - struct mm_struct *mm = tlb->mm; 50 - unsigned long freed = tlb->freed; 51 - int rss = get_mm_counter(mm, rss); 52 - 53 - if (rss < freed) 54 - freed = rss; 55 - add_mm_counter(mm, rss, -freed); 56 - 57 54 if (tlb->fullmm) 58 - flush_tlb_mm(mm); 55 + flush_tlb_mm(tlb->mm); 59 56 60 57 /* keep the page table cache within bounds */ 61 58 check_pgt_cache();
+13 -22
include/asm-arm26/tlb.h
··· 10 10 */ 11 11 struct mmu_gather { 12 12 struct mm_struct *mm; 13 - unsigned int freed; 14 - unsigned int fullmm; 15 - 16 - unsigned int flushes; 17 - unsigned int avoided_flushes; 13 + unsigned int need_flush; 14 + unsigned int fullmm; 18 15 }; 19 16 20 17 DECLARE_PER_CPU(struct mmu_gather, mmu_gathers); ··· 22 25 struct mmu_gather *tlb = &get_cpu_var(mmu_gathers); 23 26 24 27 tlb->mm = mm; 25 - tlb->freed = 0; 26 - tlb->fullmm = full_mm_flush; 28 + tlb->need_flush = 0; 29 + tlb->fullmm = full_mm_flush; 27 30 28 31 return tlb; 29 32 } ··· 31 34 static inline void 32 35 tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) 33 36 { 34 - struct mm_struct *mm = tlb->mm; 35 - unsigned long freed = tlb->freed; 36 - int rss = get_mm_counter(mm, rss); 37 - 38 - if (rss < freed) 39 - freed = rss; 40 - add_mm_counter(mm, rss, -freed); 41 - 42 - if (freed) { 43 - flush_tlb_mm(mm); 44 - tlb->flushes++; 45 - } else { 46 - tlb->avoided_flushes++; 47 - } 37 + if (tlb->need_flush) 38 + flush_tlb_mm(tlb->mm); 48 39 49 40 /* keep the page table cache within bounds */ 50 41 check_pgt_cache(); ··· 50 65 } while (0) 51 66 #define tlb_end_vma(tlb,vma) do { } while (0) 52 67 53 - #define tlb_remove_page(tlb,page) free_page_and_swap_cache(page) 68 + static inline void 69 + tlb_remove_page(struct mmu_gather *tlb, struct page *page) 70 + { 71 + tlb->need_flush = 1; 72 + free_page_and_swap_cache(page); 73 + } 74 + 54 75 #define pte_free_tlb(tlb,ptep) pte_free(ptep) 55 76 #define pmd_free_tlb(tlb,pmdp) pmd_free(pmdp) 56 77
-9
include/asm-generic/tlb.h
··· 42 42 unsigned int nr; /* set to ~0U means fast mode */ 43 43 unsigned int need_flush;/* Really unmapped some ptes? */ 44 44 unsigned int fullmm; /* non-zero means full mm flush */ 45 - unsigned long freed; 46 45 struct page * pages[FREE_PTE_NR]; 47 46 }; 48 47 ··· 62 63 tlb->nr = num_online_cpus() > 1 ? 0U : ~0U; 63 64 64 65 tlb->fullmm = full_mm_flush; 65 - tlb->freed = 0; 66 66 67 67 return tlb; 68 68 } ··· 86 88 static inline void 87 89 tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) 88 90 { 89 - int freed = tlb->freed; 90 - struct mm_struct *mm = tlb->mm; 91 - int rss = get_mm_counter(mm, rss); 92 - 93 - if (rss < freed) 94 - freed = rss; 95 - add_mm_counter(mm, rss, -freed); 96 91 tlb_flush_mmu(tlb, start, end); 97 92 98 93 /* keep the page table cache within bounds */
-9
include/asm-ia64/tlb.h
··· 60 60 unsigned int nr; /* == ~0U => fast mode */ 61 61 unsigned char fullmm; /* non-zero means full mm flush */ 62 62 unsigned char need_flush; /* really unmapped some PTEs? */ 63 - unsigned long freed; /* number of pages freed */ 64 63 unsigned long start_addr; 65 64 unsigned long end_addr; 66 65 struct page *pages[FREE_PTE_NR]; ··· 146 147 */ 147 148 tlb->nr = (num_online_cpus() == 1) ? ~0U : 0; 148 149 tlb->fullmm = full_mm_flush; 149 - tlb->freed = 0; 150 150 tlb->start_addr = ~0UL; 151 151 return tlb; 152 152 } ··· 157 159 static inline void 158 160 tlb_finish_mmu (struct mmu_gather *tlb, unsigned long start, unsigned long end) 159 161 { 160 - unsigned long freed = tlb->freed; 161 - struct mm_struct *mm = tlb->mm; 162 - unsigned long rss = get_mm_counter(mm, rss); 163 - 164 - if (rss < freed) 165 - freed = rss; 166 - add_mm_counter(mm, rss, -freed); 167 162 /* 168 163 * Note: tlb->nr may be 0 at this point, so we can't rely on tlb->start_addr and 169 164 * tlb->end_addr.
+2 -12
include/asm-sparc64/tlb.h
··· 27 27 unsigned int need_flush; 28 28 unsigned int fullmm; 29 29 unsigned int tlb_nr; 30 - unsigned long freed; 31 30 unsigned long vaddrs[TLB_BATCH_NR]; 32 31 struct page *pages[FREE_PTE_NR]; 33 32 }; ··· 50 51 mp->mm = mm; 51 52 mp->pages_nr = num_online_cpus() > 1 ? 0U : ~0U; 52 53 mp->fullmm = full_mm_flush; 53 - mp->freed = 0; 54 54 55 55 return mp; 56 56 } ··· 76 78 77 79 static inline void tlb_finish_mmu(struct mmu_gather *mp, unsigned long start, unsigned long end) 78 80 { 79 - unsigned long freed = mp->freed; 80 - struct mm_struct *mm = mp->mm; 81 - unsigned long rss = get_mm_counter(mm, rss); 82 - 83 - if (rss < freed) 84 - freed = rss; 85 - add_mm_counter(mm, rss, -freed); 86 - 87 81 tlb_flush_mmu(mp); 88 82 89 83 if (mp->fullmm) { 90 - if (CTX_VALID(mm->context)) 91 - do_flush_tlb_mm(mm); 84 + if (CTX_VALID(mp->mm->context)) 85 + do_flush_tlb_mm(mp->mm); 92 86 mp->fullmm = 0; 93 87 } else 94 88 flush_tlb_pending();
+1 -1
mm/memory.c
··· 582 582 if (pte_young(ptent)) 583 583 mark_page_accessed(page); 584 584 } 585 - tlb->freed++; 585 + dec_mm_counter(tlb->mm, rss); 586 586 page_remove_rmap(page); 587 587 tlb_remove_page(tlb, page); 588 588 continue;