From: Johannes Weiner Subject: mm: reclaim anonymous memory in virtual clusters To reduce disk seeks on swap-in, collect virtual clusters of inactive anonymous pages for reclaim. Signed-off-by: Johannes Weiner --- diff --git a/mm/vmscan.c b/mm/vmscan.c index 3b58602..ba11dee 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1020,6 +1020,101 @@ int isolate_lru_page(struct page *page) return ret; } +static unsigned long cluster_inactive_anon_vma(struct vm_area_struct *vma, + struct page *page, + unsigned long *scanned, + struct list_head *cluster) +{ + pte_t *pte; + spinlock_t *ptl; + unsigned long va, area, start, end, nr_taken = 0, nr_scanned = 0; + + va = page_address_in_vma(page, vma); + if (va == -EFAULT) + return 0; + + pte = page_check_address(page, vma->vm_mm, va, &ptl, 0); + if (!pte) + return 0; + pte_unmap_unlock(pte, ptl); + + area = page_cluster << PAGE_SHIFT; + start = va - area; + if (start < vma->vm_start) + start = vma->vm_start; + end = va + area; + if (end > vma->vm_end) + end = vma->vm_end; + + for (va = start; va < end; va += PAGE_SIZE, nr_scanned++) { + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + struct zone *zone; + struct page *cursor; + + pgd = pgd_offset(vma->vm_mm, va); + if (!pgd_present(*pgd)) + continue; + pud = pud_offset(pgd, va); + if (!pud_present(*pud)) + continue; + pmd = pmd_offset(pud, va); + if (!pmd_present(*pmd)) + continue; + pte = pte_offset_map_lock(vma->vm_mm, pmd, va, &ptl); + if (!pte_present(*pte)) { + pte_unmap_unlock(pte, ptl); + continue; + } + cursor = vm_normal_page(vma, va, *pte); + pte_unmap_unlock(pte, ptl); + + if (!cursor || cursor == page) + continue; + + zone = page_zone(cursor); + if (zone != page_zone(page)) + continue; + + spin_lock_irq(&zone->lru_lock); + if (!__isolate_lru_page(cursor, ISOLATE_INACTIVE, 0)) { + list_move_tail(&cursor->lru, cluster); + nr_taken++; + } + spin_unlock_irq(&zone->lru_lock); + } + *scanned += nr_scanned; + return nr_taken; +} + +static unsigned long cluster_inactive_anon(struct list_head *list, + unsigned long *scanned) +{ + LIST_HEAD(cluster); + unsigned long nr_taken = 0, nr_scanned = 0; + + while (!list_empty(list)) { + struct page *page; + struct anon_vma *anon_vma; + struct vm_area_struct *vma; + + page = list_entry(list->next, struct page, lru); + list_move(&page->lru, &cluster); + + anon_vma = page_lock_anon_vma(page); + if (!anon_vma) + continue; + list_for_each_entry(vma, &anon_vma->head, anon_vma_node) + nr_taken += cluster_inactive_anon_vma(vma, page, + &nr_scanned, &cluster); + page_unlock_anon_vma(anon_vma); + } + list_replace(&cluster, list); + *scanned += nr_scanned; + return nr_taken; +} + /* * shrink_inactive_list() is a helper for shrink_zone(). It returns the number * of reclaimed pages @@ -1061,6 +1156,11 @@ static unsigned long shrink_inactive_list(unsigned long max_scan, nr_taken = sc->isolate_pages(sc->swap_cluster_max, &page_list, &nr_scan, sc->order, mode, zone, sc->mem_cgroup, 0, file); + if (!file && mode == ISOLATE_INACTIVE) { + spin_unlock_irq(&zone->lru_lock); + nr_taken += cluster_inactive_anon(&page_list, &nr_scan); + spin_lock_irq(&zone->lru_lock); + } nr_active = clear_active_flags(&page_list, count); __count_vm_events(PGDEACTIVATE, nr_active);