Skip to contentSkip to conversation

Page prefetch ver. 0.1 for 2.5.68-mm2

RFC New Thomas Schlichter 1 participant 1 message Apr 28, 2003

Subsystems (8)
lore
Share & tools
lore
#0 lore raw

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
diff -urP linux-2.5.68-mm2/arch/i386/Kconfig linux-2.5.68_patched/arch/i386/Kconfig
@@ -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
diff -urP linux-2.5.68-mm2/fs/inode.c linux-2.5.68_patched/fs/inode.c
@@ -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
diff -urP linux-2.5.68-mm2/include/linux/fs.h linux-2.5.68_patched/include/linux/fs.h
@@ -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
diff -urP linux-2.5.68-mm2/include/linux/swap.h linux-2.5.68_patched/include/linux/swap.h
@@ -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
diff -urP linux-2.5.68-mm2/kernel/ksyms.c linux-2.5.68_patched/kernel/ksyms.c
@@ -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
diff -urP linux-2.5.68-mm2/mm/Makefile linux-2.5.68_patched/mm/Makefile
@@ -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
diff -urP linux-2.5.68-mm2/mm/filemap.c linux-2.5.68_patched/mm/filemap.c
@@ -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
diff -urP linux-2.5.68-mm2/mm/swap.c linux-2.5.68_patched/mm/swap.c
@@ -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
diff -urP linux-2.5.68-mm2/mm/swap_state.c linux-2.5.68_patched/mm/swap_state.c
@@ -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
diff -urP linux-2.5.68-mm2/mm/vmscan.c linux-2.5.68_patched/mm/vmscan.c
@@ -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;