diff -u mm1-2.6.0-2/arch/i386/mm/pgtable.c mm1-2.6.0-2/arch/i386/mm/pgtable.c --- mm1-2.6.0-2/arch/i386/mm/pgtable.c 2003-12-24 04:40:50.000000000 -0800 +++ mm1-2.6.0-2/arch/i386/mm/pgtable.c 2003-12-24 06:02:29.000000000 -0800 @@ -374,13 +374,18 @@ kmem_cache_free(pgd_cache, pgd); } -static void shrink_cpu_pagetable_cache(void *__gfp_mask) +struct pagetable_shrink { + int gfp_mask; + atomic_t nr; +}; + +static void shrink_cpu_pagetable_cache(void *__shrink) { - int zone, high, gfp_mask = (int)__gfp_mask; - unsigned long flags; + struct pagetable_shrink *shrink = (struct pagetable_shrink *)__shrink; struct mmu_gather *tlb = &per_cpu(mmu_gathers, get_cpu()); + int zone, high = !!(shrink->gfp_mask & __GFP_HIGHMEM); + unsigned long flags; - high = !!(gfp_mask & __GFP_HIGHMEM); local_irq_save(flags); if (!tlb->nr_pte_ready) goto out; @@ -396,17 +401,55 @@ &tlb->ready_list[zone], 0); tlb->nr_pte_ready -= tlb->ready_count[zone]; + atomic_sub(tlb->ready_count[zone], &shrink->nr); tlb->ready_count[zone] = 0; + if (atomic_read(&shrink->nr) <= 0) + goto out; } out: local_irq_restore(flags); put_cpu(); } +static int pagetable_shrinkage_possible(int gfp_mask) +{ + int cpu, total = 0; + + for (cpu = 0; cpu < NR_CPUS; ++cpu) { + struct mmu_gather *tlb; + if (!cpu_online(cpu)) + continue; + tlb = &per_cpu(mmu_gathers, cpu); + if ((gfp_mask & __GFP_HIGHMEM) || !(GFP_PTE & __GFP_HIGHMEM)) + total += tlb->nr_pte_ready; + else { + int zone; + for (zone = 0; zone < MAX_ZONE_ID; ++zone) { + if (!zone_high(zone_table[zone])) + total += tlb->ready_count[zone]; + } + } + } + return total; +} + static int shrink_pagetable_cache(int nr_to_scan, unsigned int gfp_mask) { - on_each_cpu(shrink_cpu_pagetable_cache, (void *)gfp_mask, 1, 1); - return 1; + int possible; + if ((gfp_mask & __GFP_HIGHMEM) && !(GFP_PTE & __GFP_HIGHMEM)) + return 0; + possible = pagetable_shrinkage_possible(gfp_mask); + if (!nr_to_scan) + return possible; + else if (!possible) + return 0; + else { + struct pagetable_shrink shrink; + shrink.gfp_mask = gfp_mask; + atomic_set(&shrink.nr, nr_to_scan); + on_each_cpu(shrink_cpu_pagetable_cache, &shrink, 1, 1); + return pagetable_shrinkage_possible(gfp_mask); + } } static __init int init_pagetable_cache_shrinker(void)