vm - swap prefetch v14
Subsystems (6)
PATCH 1/1 vm - swap prefetch v14
Here is the latest version of the swap prefetching patch. This code is now considered quite stable.
Changes:
-The longstanding bug which created swapcache pages that were pinned to ram and could only be removed by doing a swapoff was tracked down and fixed. -Rate of free ram allocation is watched as an indirect and cheap method of determining if any significantly sized file reads are in progress. -I/O priority support was added.
-The tunable to determine the amount of data retrieved per prefetch was added /proc/sys/vm/swap_prefetch
is used to decide how many groups of 128kb to prefetch per 1 second interval and is set to 2 by default. It can be disabled by setting it to 0
Incrementals and latest version available here:
http://ck.kolivas.org/patches/swap-prefetch/
(this is the same as the version in 2.6.13-ck7)
Cheers,
Con
This patch implements swap prefetching when the vm is relatively idle and there is free ram available. The code is based on some early work by Thomas Schlichter.
This stores a list of swapped entries in a list ordered most recently used and a radix tree. It generates a low priority kernel thread running at nice 19 to do the prefetching at a later stage.
Once pages have been added to the swapped list, a timer is started, testing for conditions suitable to prefetch swap pages every 5 seconds. Suitable conditions are defined as lack of swapping out or in any pages, and no watermark tests failing. Significant amounts of dirtied ram and changes in free ram representing disk writes or reads also prevent prefetching.
It then checks that we have spare ram looking for at least 3* pages_high free per zone and if it succeeds that will prefetch pages from swap. The pages are prefetched in 128kb groups every 1 second until the vm is busy for the tests above, the watermarks fail to detect adequate free ram or the list is emptied. The pages are copied to swap cache and kept on backing store. This allows pressure on either physical ram or swap to readily find free pages without further I/O.
The amount prefetched in each group is configurable via the tunable in /proc/sys/vm/swap_prefetch . This is set to 2 by default (256kb).
In testing on modern pc hardware this results in wall-clock time activation of the firefox browser to speed up 5 fold after a worst case complete swap-out of the browser on an static web page.
include/linux/swap.h | 33 +++ include/linux/sysctl.h | 1 init/Kconfig | 21 ++ kernel/sysctl.c | 12 + mm/Makefile | 1 mm/page_alloc.c | 14 + mm/swap.c | 3 mm/swap_prefetch.c | 434 +++++++++++++++++++++++++++++++++++++++++++++++++ mm/swap_state.c | 10 + mm/vmscan.c | 5 10 files changed, 529 insertions(+), 5 deletions(-)
include/linux/swap.h +33 -0
@@ -185,6 +185,38 @@ extern int shmem_unuse(swp_entry_t entryextern void swap_unplug_io_fn (struct backing_dev_info *,struct page *); +#ifdef CONFIG_SWAP_PREFETCH +/* only used by prefetch externally */ +/* mm/swap_prefetch.c */ +extern void prepare_prefetch (void ); +extern void add_to_swapped_list (unsigned long index); +extern void remove_from_swapped_list (unsigned long index); +extern void delay_prefetch (void ); +/* linux/mm/page_alloc.c */ +extern struct page * +buffered_rmqueue (struct zone *zone,int order,unsigned int __nocast gfp_flags); +extern void zone_statistics (struct zonelist *zonelist,struct zone *z); +extern int swap_prefetch; + +#else /* CONFIG_SWAP_PREFETCH */ +static inline void add_to_swapped_list (unsigned long index) +{ +} + +static inline void prepare_prefetch (void ) +{ +} + +static inline void remove_from_swapped_list (unsigned long index) +{ +} + +static inline void delay_prefetch (void ) +{ +} + +#endif /* CONFIG_SWAP_PREFETCH */ +#ifdef CONFIG_SWAP /* linux/mm/page_io.c */ extern int swap_readpage (struct file *,struct page *);
@@ -206,6 +238,7 @@ extern void free_pages_and_swap_cache(stextern struct page *lookup_swap_cache (swp_entry_t );extern struct page *read_swap_cache_async (swp_entry_t ,struct vm_area_struct *vma,unsigned long addr); +extern int add_to_swap_cache (struct page *page,swp_entry_t entry);/* linux/mm/swapfile.c */ extern long total_swap_pages;extern unsigned int nr_swapfiles;
include/linux/sysctl.h +1 -0
@@ -180,6 +180,7 @@ enumVM_VFS_CACHE_PRESSURE =26 ,/* dcache/icache reclaim pressure */ VM_LEGACY_VA_LAYOUT =27 ,/* legacy/compatibility virtual address space layout */ VM_SWAP_TOKEN_TIMEOUT =28 ,/* default time for token time out */ + VM_SWAP_PREFETCH=29, /* int: amount to swap prefetch */ };
init/Kconfig +21 -0
@@ -87,6 +87,27 @@ config SWAP used to provide more virtual memory than the actual RAM present in your computer. If unsure say Y. +config SWAP_PREFETCH + bool "Support for prefetching swapped memory" + depends on SWAP + default n + ---help--- + This option will allow the kernel to prefetch swapped memory pages + when idle. The pages will be kept on both swap and in swap_cache + thus avoiding the need for further I/O if either ram or swap space + is required. + + What this will do on workstations is slowly bring back applications + that have swapped out after memory intensive workloads back into + physical ram if you have free ram at a later stage and the machine + is relatively idle. This means that when you come back to your + computer after leaving it idle for a while, applications will come + to life faster. Note that your swap usage will appear to increase + but these are cached pages, can be dropped freely by the vm, and it + should stabilise around 50% swap usage. + + Desktop users will most likely want to say Y. + config SYSVIPC bool "System V IPC" depends on MMU
kernel/sysctl.c +12 -0
@@ -850,6 +850,18 @@ static ctl_table vm_table[] = { .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, +#ifdef CONFIG_SWAP_PREFETCH + { + .ctl_name = VM_SWAP_PREFETCH, + .procname = "swap_prefetch", + .data = &swap_prefetch, + .maxlen = sizeof(swap_prefetch), + .mode = 0644, + .proc_handler = &proc_dointvec, + .strategy = &sysctl_intvec, + .extra1 = &zero, + }, +#endif #endif { .ctl_name = 0 } };
mm/Makefile +1 -0
@@ -13,6 +13,7 @@ obj-y := bootmem.o filemap.o mempool.o prio_tree.o $(mmu-y) obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o +obj-$(CONFIG_SWAP_PREFETCH) += swap_prefetch.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o obj-$(CONFIG_NUMA) += mempolicy.o obj-$(CONFIG_SPARSEMEM) += sparse.o
mm/page_alloc.c +10 -4
@@ -607,7 +607,7 @@ void drain_local_pages(void) }#endif /* CONFIG_PM */ -static void zone_statistics (struct zonelist *zonelist,struct zone *z) +void zone_statistics (struct zonelist *zonelist,struct zone *z) {#ifdef CONFIG_NUMA unsigned long flags;
@@ -684,7 +684,7 @@ static inline void prep_zero_page(struct* we cheat by calling it from here, in the order > 0 path. Saves a branch * or two. */ -static struct page * +struct page *buffered_rmqueue (struct zone *zone,int order,unsigned int __nocast gfp_flags) {unsigned long flags;
@@ -745,7 +745,7 @@ int zone_watermark_ok(struct zone *z, in min -= min /4 ;if (free_pages <= min + z->lowmem_reserve[classzone_idx]) -return 0 ; + goto out_failed;for (o =0 ; o < order; o++) {/* At the next order, this order's pages become unavailable */ free_pages -= z->free_area[o].nr_free << o;
@@ -754,9 +754,15 @@ int zone_watermark_ok(struct zone *z, in min >>=1 ;if (free_pages <= min) -return 0 ; + goto out_failed; } +return 1 ; +out_failed: +/* Swap prefetching is delayed if any watermark is low */ +delay_prefetch (); + +return 0 ; } static inline int
mm/swap.c +3 -0
@@ -481,5 +481,8 @@ void __init swap_setup(void)* Right now other parts of the system means that we * _really_ don't want to cluster much more */ + +prepare_prefetch (); +hotcpu_notifier (cpu_swap_callback,0 ); }
mm/swap_prefetch.c +434 -0
434 lines not on this page · raw message
mm/swap_state.c +9 -1
@@ -80,6 +80,7 @@ static int __add_to_swap_cache(struct pa error =radix_tree_insert (&swapper_space.page_tree, entry.val, page);if (!error) { +remove_from_swapped_list (entry.val);page_cache_get (page);SetPageLocked (page);SetPageSwapCache (page);
@@ -93,11 +94,12 @@ static int __add_to_swap_cache(struct pareturn error; } -static int add_to_swap_cache (struct page *page,swp_entry_t entry) +int add_to_swap_cache (struct page *page,swp_entry_t entry) {int error;if (!swap_duplicate (entry)) { +remove_from_swapped_list (entry.val);INC_CACHE_INFO (noent_race);return -ENOENT ; }
@@ -145,6 +147,9 @@ int add_to_swap(struct page * page)swp_entry_t entry;int err; +/* Swap prefetching is delayed if we're swapping pages */ +delay_prefetch (); +if (!PageLocked (page))BUG ();
@@ -325,6 +330,9 @@ struct page *read_swap_cache_async(swp_estruct page *found_page, *new_page =NULL ;int err; +/* Swap prefetching is delayed if we're already reading from swap */ +delay_prefetch (); + do { /* * First check the swap cache. Since this is normally
mm/vmscan.c +5 -0
@@ -519,6 +519,7 @@ static int shrink_list(struct list_head#ifdef CONFIG_SWAP if (PageSwapCache (page)) {swp_entry_t swap = { .val = page->private }; +add_to_swapped_list (swap.val);__delete_from_swap_cache (page);write_unlock_irq (&mapping->tree_lock);swap_free (swap);
@@ -929,6 +930,8 @@ int try_to_free_pages(struct zone **zoneunsigned long lru_pages =0 ;int i; +delay_prefetch (); + sc.gfp_mask = gfp_mask; sc.may_writepage =0 ; sc.may_swap =1 ;
@@ -1275,6 +1278,8 @@ int shrink_all_memory(int nr_pages) .reclaimed_slab = 0, }; +delay_prefetch (); + current->reclaim_state = &reclaim_state;for_each_pgdat (pgdat) {int freed;
Hi Con,
your patch still contains a serious BUG :-)
Quoted 4 lines · Con Kolivas
-The tunable to determine the amount of data retrieved per prefetch was added
/proc/sys/vm/swap_prefetch
is used to decide how many groups of 128kb to prefetch per 1 second interval and is set to 2 by default. It can be disabled by setting it to 0
swap_prefetch is not documented at Documentation/sysctl/vm.txt
This simple text would suffice I think. Maybe comment about disabling it for laptop usage.
Regards
Ingo Oeser
Quoted 11 lines · Ingo Oeser
Hi Con,
your patch still contains a serious BUG :-)
Quoted 5 lines · Con Kolivas
-The tunable to determine the amount of data retrieved per prefetch was added
/proc/sys/vm/swap_prefetch
is used to decide how many groups of 128kb to prefetch per 1 second interval and is set to 2 by default. It can be disabled by setting it to 0
Quoted 4 lines
swap_prefetchis not documented atDocumentation/sysctl/vm.txtThis simple text would suffice I think. Maybe comment about disabling it for laptop usage.
Thanks for the suggestion. I'll add the Documentation entry and you reminded me to do something specific with laptop mode which I have for v15 I'm about to announce.
Cheers,
Con