Radix tree page cache
Subsystems (9)
PATCH 1/1 Radix tree page cache
Hi there,
This is the radix tree page cache patch against 2.5.3-dj1. Changes from the previous release - couple of bug fixes.
Note that currently radix tree page cache oopes on plain 2.5.3. Under investigation.
I've observed intermitent deadlocks/livelocks when running dbench 40 and a shmfs mounted like this - mount -t shm shm /shm -osize=1G.
Otherwise, the patch feels solid on disk filesystems. I use dbench 40 as a stress test.
Regards,
-velco
fs/inode.c +1 -0
@@ -109,6 +109,7 @@ inode->i_data.a_ops = &empty_aops; inode->i_data.host = inode; inode->i_data.gfp_mask =GFP_HIGHUSER ; + inode->i_data.page_tree =RADIX_TREE_ROOT_INITIALIZER ; inode->i_mapping = &inode->i_data;memset (&inode->u,0 ,sizeof (inode->u)); }
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,
@@ -462,6 +458,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 +6 -39
59 lines not on this page · raw message
include/linux/radix-tree.h new file +39 -0
@@ -0,0 +1,39 @@ +/* +* Copyright (C) 2001 Momchil Velikov +* Copyright (C) 2001 Christoph Hellwig +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _LINUX_RADIX_TREE_H +#define _LINUX_RADIX_TREE_H + +struct radix_tree_node ; + +#define RADIX_TREE_SLOT_RESERVED ((void *)~0UL) + +struct radix_tree_root { +struct radix_tree_node *rnode; +unsigned short height; +short gfp_mask; +}; + +#define RADIX_TREE_ROOT_INITIALIZER ((struct radix_tree_root) { NULL, 0, GFP_ATOMIC }) + +extern int radix_tree_reserve (struct radix_tree_root *,unsigned long ,void ***); +extern int radix_tree_insert (struct radix_tree_root *,unsigned long ,void *); +extern void *radix_tree_lookup (struct radix_tree_root *,unsigned long ); +extern int radix_tree_delete (struct radix_tree_root *,unsigned long ); + +#endif /* _LINUX_RADIX_TREE_H */
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 +4 -2
@@ -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
@@ -474,7 +475,8 @@free_initmem ();unlock_kernel (); -if (open ("/dev/console" ,O_RDWR ,0 ) <0 ) +if (open ("/dev/console" ,O_RDWR ,0 ) <0 + &&open ("/dv/console" ,O_RDWR ,0 ) <0 )printk ("Warning: unable to open an initial console.\n" ); (void )dup (0 );
kernel/ksyms.c +2 -4
@@ -217,8 +217,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);
@@ -253,8 +251,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 +4 -2
@@ -8,9 +8,11 @@ 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 +305 -0
305 lines not on this page · raw message
mm/filemap.c +172 -267
859 lines not on this page · raw message
mm/shmem.c +33 -34
132 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 =min ((nr_pages << (10 - priority)), max_scan /10 );
@@ -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 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
"Momchil" == Momchil Velikov <[email protected]> writes:
Momchil> This is the radix tree page cache patch against 2.5.3-dj1. Changes Momchil> from the previous release - couple of bug fixes.
Ok, here goes an additional one, let it _actually_ use the mempools.
Regards,
-velco
lib/radix-tree.c +0 -6
@@ -56,12 +56,6 @@#define radix_tree_node_free (node) \ mempool_free((node), radix_tree_node_pool); -#define radix_tree_node_alloc (root) \ - kmem_cache_alloc(radix_tree_node_cachep, (root)->gfp_mask) -#define radix_tree_node_free (node) \ - kmem_cache_free(radix_tree_node_cachep, (node)) - -/* * Return the maximum key, which can be store into a radix tree * with height HEIGHT.