Page prefetch ver. 0.1 for 2.5.68-mm2
Subsystems (8)
RFC 1/1 Page prefetch ver. 0.1 for 2.5.68-mm2
This is a working version of the idea posted on the LKML a few weeks ago...
Currently it only works when loaded as a module. Then a kernel thread 'kprefetchd' is started which prefetches swap and buffer pages when there is free buffer memory. When the module is unloaded the kernel thread is stopped again.
For me it works as expected and has no noticeable negative impact. But some benchmarks should be performed to ensure this...
Besides from the problem that this works only as a module, yet, there are a few points which could be improved when I've got some spare time... For example I should take care of the disk I/O usage before prefetching. The list of swapped pages is growing too much, too. It can be seen be doing a
grep swapped_entry /proc/slabinfo
An other point is that it doesn't work on NFS file systems as I set the file pointer to NULL for do_page_chache_readahead().
And I'm open for any other further improvements.
I hope someone else likes that patch as much as I do... ;-)
Best regards
Thomas Schlichter
P.S.: For those who want it I've got a patch that applies on vanilla 2.5.68, too...
arch/i386/Kconfig +8 -0
@@ -373,6 +373,14 @@ depends on MK8 || MPENTIUM4 default y +config PAGE_PREFETCH + tristate "Prefetch swapped memory pages (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + This option enables the kernel to prefetch swapped memory pages + when idle. This feature is experimental and known not to work + with the NFS filesystem! + config HUGETLB_PAGE bool "Huge TLB Page Support" help
fs/inode.c +4 -0
@@ -18,6 +18,7 @@#include <linux/hash.h> #include <linux/swap.h> #include <linux/security.h> +#include <linux/page_prefetch.h> /* * This is needed for the following functions:
@@ -176,10 +177,12 @@INIT_LIST_HEAD (&inode->i_data.clean_pages);INIT_LIST_HEAD (&inode->i_data.dirty_pages);INIT_LIST_HEAD (&inode->i_data.locked_pages); +INIT_LIST_HEAD (&inode->i_data.swapped_pages);INIT_LIST_HEAD (&inode->i_data.io_pages);INIT_LIST_HEAD (&inode->i_dentry);INIT_LIST_HEAD (&inode->i_devices);sema_init (&inode->i_sem,1 ); +INIT_RADIX_TREE (&inode->i_data.swap_tree,GFP_ATOMIC );INIT_RADIX_TREE (&inode->i_data.page_tree,GFP_ATOMIC );spin_lock_init (&inode->i_data.page_lock);init_MUTEX (&inode->i_data.i_shared_sem);
@@ -227,6 +230,7 @@void clear_inode (struct inode *inode) { +invalidate_swapped_list (&inode->i_data);invalidate_inode_buffers (inode);if (inode->i_data.nrpages)
include/linux/fs.h +2 -0
@@ -312,11 +312,13 @@struct backing_dev_info ;struct address_space {struct inode *host;/* owner: inode, block_device */ +struct radix_tree_root swap_tree;/* radix tree of swapped pages */ struct radix_tree_root page_tree;/* radix tree of all pages */ spinlock_t page_lock;/* and rwlock protecting it */ struct list_head clean_pages;/* list of clean pages */ struct list_head dirty_pages;/* list of dirty pages */ struct list_head locked_pages;/* list of locked pages */ +struct list_head swapped_pages;/* list of swapped pages */ struct list_head io_pages;/* being prepared for I/O */ unsigned long nrpages;/* number of total pages */ struct address_space_operations *a_ops;/* methods */
include/linux/page_prefetch.h +96 -0
96 lines not on this page · raw message
include/linux/swap.h +2 -0
@@ -155,6 +155,8 @@extern unsigned int nr_free_pages_pgdat (pg_data_t *pgdat);extern unsigned int nr_free_buffer_pages (void );extern unsigned int nr_free_pagecache_pages (void ); +extern unsigned int nr_avail_buffer_pages (void ); +extern unsigned int nr_avail_pagecache_pages (void );/* linux/mm/swap.c */ extern void FASTCALL (lru_cache_add (struct page *));
kernel/ksyms.c +8 -0
@@ -59,6 +59,7 @@#include <linux/time.h> #include <linux/backing-dev.h> #include <linux/percpu_counter.h> +#include <linux/page_prefetch.h> #include <asm/checksum.h> #if defined(CONFIG_PROC_FS )
@@ -71,6 +72,13 @@extern struct timezone sys_tz;extern int panic_timeout; + +/* needed for page prefetch support */ +EXPORT_SYMBOL (swapped_root); +EXPORT_SYMBOL (swapper_space); +EXPORT_SYMBOL (swapin_readahead); +EXPORT_SYMBOL (do_page_cache_readahead); +EXPORT_SYMBOL (nr_avail_pagecache_pages);/* process memory management */ EXPORT_SYMBOL (do_mmap_pgoff);
mm/Makefile +2 -0
@@ -12,3 +12,5 @@ slab.o swap.o truncate.o vcache.o vmscan.o $(mmu-y) obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o + +obj-$(CONFIG_PAGE_PREFETCH) += page_prefetch.o
mm/filemap.c +6 -1
@@ -36,6 +36,7 @@* FIXME: remove all knowledge of the buffer layer from the core VM */ #include <linux/buffer_head.h> /* for generic_osync_inode */ +#include <linux/page_prefetch.h> #include <asm/uaccess.h> #include <asm/mman.h>
@@ -83,6 +84,7 @@BUG_ON (PageDirty (page) && !PageSwapCache (page)); +remove_from_swapped_list (mapping, page->index);radix_tree_delete (&mapping->page_tree, page->index);list_del (&page->list); page->mapping =NULL ;
@@ -222,8 +224,11 @@int add_to_page_cache (struct page *page,struct address_space *mapping,pgoff_t offset,int gfp_mask) { -int error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); +int error; +remove_from_swapped_list (mapping, offset); + + error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM);if (error ==0 ) {page_cache_get (page);spin_lock (&mapping->page_lock);
mm/page_alloc.c +42 -0
48 lines not on this page · raw message
mm/page_prefetch.c +114 -0
114 lines not on this page · raw message
mm/swap.c +20 -0
@@ -23,6 +23,13 @@#include <linux/mm_inline.h> #include <linux/buffer_head.h> /* for try_to_release_page() */ #include <linux/percpu.h> +#include <linux/page_prefetch.h> + +struct swapped_root_t swapped_root = { + .lock =SPIN_LOCK_UNLOCKED , + .count =0 , + .list =LIST_HEAD_INIT (swapped_root.list), +};/* How many pages do we try to swap or page in/out together? */ int page_cluster;
@@ -390,4 +397,17 @@* Right now other parts of the system means that we * _really_ don't want to cluster much more */ + +/* +* Create kmem cache for swapped entries +*/ + swapped_root.cache =kmem_cache_create ("swapped_entry" , +sizeof (struct swapped_entry_t ),0 ,0 ,NULL ,NULL ); +if (!swapped_root.cache) +panic ("swap_setup(): cannot create swapped_entry SLAB cache" ); + +/* +* Set max count of swapped entries +*/ + swapped_root.maxcount =nr_free_pagecache_pages (); }
mm/swap_state.c +2 -0
@@ -32,12 +32,14 @@extern struct address_space_operations swap_aops;struct address_space swapper_space = { + .swap_tree =RADIX_TREE_INIT (GFP_ATOMIC ), .page_tree =RADIX_TREE_INIT (GFP_ATOMIC ), .page_lock =SPIN_LOCK_UNLOCKED , .clean_pages =LIST_HEAD_INIT (swapper_space.clean_pages), .dirty_pages =LIST_HEAD_INIT (swapper_space.dirty_pages), .io_pages =LIST_HEAD_INIT (swapper_space.io_pages), .locked_pages =LIST_HEAD_INIT (swapper_space.locked_pages), + .swapped_pages =LIST_HEAD_INIT (swapper_space.swapped_pages), .host = &swapper_inode, .a_ops = &swap_aops, .backing_dev_info = &swap_backing_dev_info,
mm/vmscan.c +3 -0
@@ -28,6 +28,7 @@#include <linux/pagevec.h> #include <linux/backing-dev.h> #include <linux/rmap-locking.h> +#include <linux/page_prefetch.h> #include <asm/pgalloc.h> #include <asm/tlbflush.h>
@@ -414,6 +415,8 @@ free_it:unlock_page (page); ret++; +if (mapping) +add_to_swapped_list (mapping, page->index);if (!pagevec_add (&freed_pvec, page))__pagevec_release_nonlru (&freed_pvec);continue ;