Radix tree page cache
Subsystems (10)
PATCH 1/1 Radix tree page cache
Linus,
Please, consider for inclusion in 2.5.3 series the following radix tree page cache patch.
Regards,
-velco
drivers/block/rd.c +1 -3
@@ -156,7 +156,6 @@ do { int count; - struct page ** hash; struct page * page; char * src, * dst; int unlock = 0;
@@ -166,8 +165,7 @@ count = size; size -= count; - hash =page_hash (mapping, index); - page =__find_get_page (mapping, index, hash); + page =find_get_page (mapping, index);if (!page) { page =grab_cache_page (mapping, index); err = -ENOMEM ;
fs/inode.c +1 -0
@@ -144,6 +144,7 @@INIT_LIST_HEAD (&inode->i_devices);sema_init (&inode->i_sem,1 );sema_init (&inode->i_zombie,1 ); +INIT_RADIX_TREE_ROOT (&inode->i_data.page_tree,GFP_ATOMIC );spin_lock_init (&inode->i_data.i_shared_lock); }
include/linux/fs.h +2 -0
@@ -21,6 +21,7 @@#include <linux/cache.h> #include <linux/stddef.h> #include <linux/string.h> +#include <linux/radix-tree.h> #include <asm/atomic.h> #include <asm/bitops.h>
@@ -370,6 +371,7 @@ };struct address_space { +struct radix_tree_root page_tree;/* radix tree of all pages */ 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 */
include/linux/mm.h +20 -6
@@ -149,15 +149,12 @@struct list_head list;/* ->mapping has some page lists. */ struct address_space *mapping;/* The inode (or ...) we belong to. */ unsigned long index;/* Our offset within mapping. */ -struct page *next_hash;/* Next page sharing our hash bucket in -the pagecache hash table. */ atomic_t count;/* Usage count, see below. */ unsigned long flags;/* atomic flags, some possibly updated asynchronously */ struct list_head lru;/* Pageout list, eg. active_list; protected by pagemap_lru_lock !! */ wait_queue_head_t wait;/* Page locked? Stand in line... */ -struct page **pprev_hash;/* Complement to *next_hash. */ struct buffer_head * buffers;/* Buffer maps us to a disk block. */ void *virtual;/* Kernel virtual address (NULL if not kmapped, ie. highmem) */
@@ -225,9 +222,8 @@* using the page->list list_head. These fields are also used for * freelist managemet (when page->count==0). * -* There is also a hash table mapping (mapping,index) to the page -* in memory if present. The lists for this hash table use the fields -* page->next_hash and page->pprev_hash. +* There is also a per-mapping radix tree mapping index to the page +* in memory if present. The tree is rooted at mapping->root. * * All process pages can do I/O: * - inode pages may need to be read from disk,
@@ -459,6 +455,24 @@return 1 ; else return 0; +} + +static inline void add_page_to_inode_queue (struct address_space *mapping,struct page * page) +{ +struct list_head *head = &mapping->clean_pages; + + mapping->nrpages++; +list_add (&page->list, head); + page->mapping = mapping; +} + +static inline void remove_page_from_inode_queue (struct page * page) +{ +struct address_space * mapping = page->mapping; + + mapping->nrpages--; +list_del (&page->list); + page->mapping =NULL ; }struct zone_t ;
include/linux/pagemap.h +10 -41
63 lines not on this page · raw message
include/linux/radix-tree.h new file +45 -0
45 lines not on this page · raw message
include/linux/swap.h +4 -1
@@ -96,7 +96,7 @@struct task_struct ;struct vm_area_struct ;struct sysinfo ; - +struct address_space ;struct zone_t ;/* linux/mm/swap.c */
@@ -126,6 +126,9 @@extern int add_to_swap_cache (struct page *,swp_entry_t );extern void __delete_from_swap_cache (struct page *page);extern void delete_from_swap_cache (struct page *page); +extern int move_to_swap_cache (struct page *page,swp_entry_t entry); +extern int move_from_swap_cache (struct page *page,unsigned long index, +struct address_space *mapping);extern void free_page_and_swap_cache (struct page *page);extern struct page *lookup_swap_cache (swp_entry_t );extern struct page *read_swap_cache_async (swp_entry_t );
init/main.c +2 -1
@@ -69,6 +69,7 @@extern void sbus_init (void );extern void sysctl_init (void );extern void signals_init (void ); +extern void radix_tree_init (void )__init ;extern void free_initmem (void );
@@ -364,7 +365,7 @@proc_caches_init ();vfs_caches_init (mempages);buffer_init (mempages); -page_cache_init (mempages); +radix_tree_init ();#if defined(CONFIG_ARCH_S390 )ccwcache_init ();#endif
kernel/ksyms.c +2 -4
@@ -218,8 +218,6 @@EXPORT_SYMBOL (generic_file_mmap);EXPORT_SYMBOL (generic_ro_fops);EXPORT_SYMBOL (generic_buffer_fdatasync); -EXPORT_SYMBOL (page_hash_bits); -EXPORT_SYMBOL (page_hash_table);EXPORT_SYMBOL (file_lock_list);EXPORT_SYMBOL (locks_init_lock);EXPORT_SYMBOL (locks_copy_lock);
@@ -254,8 +252,8 @@EXPORT_SYMBOL (__pollwait);EXPORT_SYMBOL (poll_freewait);EXPORT_SYMBOL (ROOT_DEV ); -EXPORT_SYMBOL (__find_get_page); -EXPORT_SYMBOL (__find_lock_page); +EXPORT_SYMBOL (find_get_page); +EXPORT_SYMBOL (find_lock_page);EXPORT_SYMBOL (grab_cache_page);EXPORT_SYMBOL (grab_cache_page_nowait);EXPORT_SYMBOL (read_cache_page);
lib/Makefile +3 -2
@@ -8,9 +8,10 @@ L_TARGET := lib.a -export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o crc32.o +export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o crc32.o radix-tree.o -obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o bust_spinlocks.o rbtree.o +obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \ + bust_spinlocks.o rbtree.o radix-tree.o obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
lib/radix-tree.c new file +292 -0
292 lines not on this page · raw message
mm/filemap.c +170 -270
847 lines not on this page · raw message
mm/shmem.c +27 -28
116 lines not on this page · raw message
mm/swap_state.c +101 -10
147 lines not on this page · raw message
mm/swapfile.c +4 -4
@@ -239,10 +239,10 @@/* Is the only swap cache user the cache itself? */ if (p->swap_map[SWP_OFFSET (entry)] ==1 ) {/* Recheck the page count with the pagecache lock held.. */ -spin_lock (&pagecache_lock); +spin_lock (&swapper_space.i_shared_lock);if (page_count (page) - !!page->buffers ==2 ) retval =1 ; -spin_unlock (&pagecache_lock); +spin_unlock (&swapper_space.i_shared_lock); }swap_info_put (p); }
@@ -307,13 +307,13 @@ retval =0 ;if (p->swap_map[SWP_OFFSET (entry)] ==1 ) {/* Recheck the page count with the pagecache lock held.. */ -spin_lock (&pagecache_lock); +spin_lock (&swapper_space.i_shared_lock);if (page_count (page) - !!page->buffers ==2 ) {__delete_from_swap_cache (page);SetPageDirty (page); retval =1 ; } -spin_unlock (&pagecache_lock); +spin_unlock (&swapper_space.i_shared_lock); }swap_info_put (p);
mm/vmscan.c +28 -10
@@ -137,10 +137,16 @@* (adding to the page cache will clear the dirty * and uptodate bits, so we need to do it again) */ -if (add_to_swap_cache (page, entry) ==0 ) { +switch (add_to_swap_cache (page, entry)) { +case 0 :SetPageUptodate (page);set_page_dirty (page); goto set_swap_pte; +case -ENOMEM : +swap_free (entry); + goto preserve; +default : +break ; }/* Raced with "speculative" read_swap_cache_async */ swap_free (entry);
@@ -338,6 +344,7 @@static int shrink_cache (int nr_pages,zone_t * classzone,unsigned int gfp_mask,int priority) {struct list_head * entry; +struct address_space *mapping;int max_scan = nr_inactive_pages / priority;int max_mapped = nr_pages << (9 - priority);
@@ -392,7 +399,9 @@continue ; } -if (PageDirty (page) &&is_page_cache_freeable (page) && page->mapping) { + mapping = page->mapping; + +if (PageDirty (page) &&is_page_cache_freeable (page) && mapping) {/* * It is not critical here to write it only if * the page is unmapped beause any direct writer
@@ -403,7 +412,7 @@*/ int (*writepage)(struct page *); - writepage = page->mapping->a_ops->writepage; + writepage = mapping->a_ops->writepage;if ((gfp_mask & __GFP_FS) && writepage) {ClearPageDirty (page);SetPageLaunder (page);
@@ -430,7 +439,7 @@page_cache_get (page);if (try_to_release_page (page, gfp_mask)) { -if (!page->mapping) { +if (!mapping) {/* * We must not allow an anon page * with no buffers to be visible on
@@ -467,13 +476,22 @@ } } -spin_lock (&pagecache_lock); +/* +* Page is locked, so mapping can't change under our +* feet. +*/ +if (!mapping) { +UnlockPage (page); + goto page_mapped; + } + +spin_lock (&mapping->i_shared_lock);/* * this is the non-racy check for busy page. */ -if (!page->mapping || !is_page_cache_freeable (page)) { -spin_unlock (&pagecache_lock); +if (!is_page_cache_freeable (page)) { +spin_unlock (&mapping->i_shared_lock);UnlockPage (page); page_mapped:if (--max_mapped >=0 )
@@ -493,7 +511,7 @@* the page is freeable* so not in use by anybody. */ if (PageDirty (page)) { -spin_unlock (&pagecache_lock); +spin_unlock (&mapping->i_shared_lock);UnlockPage (page);continue ; }
@@ -501,12 +519,12 @@/* point of no return */ if (likely (!PageSwapCache (page))) {__remove_inode_page (page); -spin_unlock (&pagecache_lock); +spin_unlock (&mapping->i_shared_lock); }else {swp_entry_t swap; swap.val = page->index;__delete_from_swap_cache (page); -spin_unlock (&pagecache_lock); +spin_unlock (&mapping->i_shared_lock);swap_free (swap); }
Quoted 4 lines · Momchil Velikov
Linus,
Please, consider for inclusion in 2.5.3 series the following radix tree page cache patch.
Please benchmark on files 10giga large, only do cached I/O (reads are fine) between 9G and 10G offset for example.
dbench or others handling not very huge files are not interesting benchmarks for this, for them per-inode rbtree was faster too. the only problem are the very huge files, the hashtable guarantees the same performance on small and huge files.
Andrea
Quoted 8 lines · Andrea Arcangeli
Quoted 4 lines · Momchil Velikov
Linus,
Please, consider for inclusion in 2.5.3 series the following radix tree page cache patch.
Please benchmark on files 10giga large, only do cached I/O (reads are fine) between 9G and 10G offset for example.
All published data I've seen on file size distribution on Unix show that the overwhelming mayority of files is a few KiB long, so this is a corner case. Why benchmark it expressly?
Signature · 1 line
Horst von Brand http://counter.li.org # 22616