From ab5d57c45835566f84b0f40269fa73e69c5f62de Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 29 Apr 2009 22:19:21 +0200 Subject: [patch] swap: unfrighten swap-in readahead Swap-in is clustered by looking below and above the target slot for other slots in use to be read in the hope they will be needed soon. At the moment, the resulting cluster is described by an offset into swap space and a number of slots to be read, which is obviously only useful for contiguous ranges of slots. But there may be holes in the cluster: unused or bad swap slots. We don't want to read them in but we still want to read slots beyond these holes within the cluster. The current code scans the swap map from the target slot and just stops walking in one direction when it encounters a hole. Use a bitmap to properly represent a full cluster of slots and then read the pages for which the bits are set. --- include/linux/swap.h | 2 +- mm/swap_state.c | 20 ++++++++++------- mm/swapfile.c | 55 ++++++++++++++++++------------------------------- 3 files changed, 33 insertions(+), 44 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 62d8143..36edf5e 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -301,7 +301,7 @@ extern void si_swapinfo(struct sysinfo *); extern swp_entry_t get_swap_page(void); extern swp_entry_t get_swap_page_of_type(int); extern int swap_duplicate(swp_entry_t); -extern int valid_swaphandles(swp_entry_t, unsigned long *); +extern pgoff_t valid_swaphandles(swp_entry_t, unsigned long *, unsigned long); extern void swap_free(swp_entry_t); extern int free_swap_and_cache(swp_entry_t); extern int swap_type_of(dev_t, sector_t, struct block_device **); diff --git a/mm/swap_state.c b/mm/swap_state.c index 3ecea98..7c3e9c7 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -349,10 +349,10 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask, struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask, struct vm_area_struct *vma, unsigned long addr) { - int nr_pages; - struct page *page; + unsigned long nr_slots = 1 << page_cluster; + unsigned long slots[nr_slots]; unsigned long offset; - unsigned long end_offset; + pgoff_t base; /* * Get starting offset for readaround, and number of pages to read. @@ -361,11 +361,15 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask, * more likely that neighbouring swap pages came from the same node: * so use the same "addr" to choose the same node for each swap read. */ - nr_pages = valid_swaphandles(entry, &offset); - for (end_offset = offset + nr_pages; offset < end_offset; offset++) { - /* Ok, do the async read-ahead now */ - page = read_swap_cache_async(swp_entry(swp_type(entry), offset), - gfp_mask, vma, addr); + base = valid_swaphandles(entry, slots, nr_slots); + for (offset = find_first_bit(slots, nr_slots); + offset < nr_slots; + offset = find_next_bit(slots, nr_slots, offset + 1)) { + struct page *page; + swp_entry_t tmp; + + tmp = swp_entry(swp_type(entry), base + offset); + page = read_swap_cache_async(tmp, gfp_mask, vma, addr); if (!page) break; page_cache_release(page); diff --git a/mm/swapfile.c b/mm/swapfile.c index 312fafe..576bc90 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1985,25 +1985,22 @@ get_swap_info_struct(unsigned type) return &swap_info[type]; } -/* - * swap_lock prevents swap_map being freed. Don't grab an extra - * reference on the swaphandle, it doesn't matter if it becomes unused. - */ -int valid_swaphandles(swp_entry_t entry, unsigned long *offset) +static int swap_inuse(unsigned long swapcount) { - struct swap_info_struct *si; - int our_page_cluster = page_cluster; - pgoff_t target, toff; - pgoff_t base, end; - int nr_pages = 0; + return swap_count(swapcount) && swap_count(swapcount) != SWAP_MAP_BAD; +} - if (!our_page_cluster) /* no readahead */ - return 0; +pgoff_t valid_swaphandles(swp_entry_t entry, unsigned long *slots, + unsigned long nr_slots) +{ + struct swap_info_struct *si; + pgoff_t target, base, end; + bitmap_zero(slots, nr_slots); si = &swap_info[swp_type(entry)]; target = swp_offset(entry); - base = (target >> our_page_cluster) << our_page_cluster; - end = base + (1 << our_page_cluster); + base = target & ~(nr_slots - 1); + end = base + nr_slots; if (!base) /* first page is swap header */ base++; @@ -2011,29 +2008,16 @@ int valid_swaphandles(swp_entry_t entry, unsigned long *offset) if (end > si->max) /* don't go beyond end of map */ end = si->max; - /* Count contiguous allocated slots above our target */ - for (toff = target; ++toff < end; nr_pages++) { - /* Don't read in free or bad pages */ - if (!si->swap_map[toff]) - break; - if (swap_count(si->swap_map[toff]) == SWAP_MAP_BAD) - break; - } - /* Count contiguous allocated slots below our target */ - for (toff = target; --toff >= base; nr_pages++) { - /* Don't read in free or bad pages */ - if (!si->swap_map[toff]) - break; - if (swap_count(si->swap_map[toff]) == SWAP_MAP_BAD) - break; + while (end-- > base) { + /* + * swap_lock prevents swap_map being freed. Don't grab + * an extra reference on the swaphandle, it doesn't + * matter if it becomes unused. + */ + if (end == target || swap_inuse(si->swap_map[end])) + set_bit(end - base, slots); } spin_unlock(&swap_lock); - /* - * Indicate starting offset, and return number of pages to get: - * if only 1, say 0, since there's then no readahead to be done. - */ - *offset = ++toff; - return nr_pages? ++nr_pages: 0; + return base; } -- 1.6.3