dirty pages in the page cache.
Subsystems (8)
RFC PATCH 1/1 dirty pages in the page cache.
The following patch implements dirty pages in the page cache, using it's own version of kflushd, kpgflushd so the support is not confined to just block based filesystems, and allows fun things like allocate of space on write.
The code still has a ways to go before it reaches optimum tuning but the important part of the interal API looks solid.
As far as space consumption in struct page, one more set of list pointers has been added (to keep the dirty page list, and whatever), and 12 bits of flags has been used, allowing a crude but space efficient timer on when a page should be next written.
Some important functions are:mark_page_dirty, mark_page_clean, unlock_page, write_page.
Eric
fs/buffer.c +3 -23
@@ -1103,23 +1103,6 @@ goto try_again; } -/* Run the hooks that have to be done when a page I/O has completed. */ -static inline void after_unlock_page (struct page * page) -{ -if (test_and_clear_bit (PG_decr_after, &page->flags)) { -atomic_dec (&nr_async_pages); -#ifdef DEBUG_SWAP -printk ("DebugVM: Finished IO on page %p, nr_async_pages %d\n" , - (char *)page_address (page), -atomic_read (&nr_async_pages)); -#endif - } -if (test_and_clear_bit (PG_swap_unlock_after, &page->flags)) -swap_after_unlock_page (page->key); -if (test_and_clear_bit (PG_free_after, &page->flags)) -__free_page (page); -} -/* * Free all temporary buffers belonging to a page. * This needs to be called with interrupts disabled.
@@ -1190,9 +1173,7 @@/* OK, the async IO on this page is complete. */ free_async_buffers (bh);restore_flags (flags); -clear_bit (PG_locked, &page->flags); -wake_up (&page->wait); -after_unlock_page (page); +unlock_page (page);return ; still_busy:
@@ -1285,14 +1266,12 @@* and unlock_buffer(). */ }else {unsigned long flags; -clear_bit (PG_locked, &page->flags);set_bit (PG_uptodate, &page->flags); -wake_up (&page->wait);save_flags (flags);cli ();free_async_buffers (bh);restore_flags (flags); -after_unlock_page (page); +unlock_page (page); } ++current->maj_flt;return 0 ;
@@ -1593,6 +1572,7 @@sync_supers (0 );sync_inodes (0 ); +sync_pcache (1 ,0 ); ncount =0 ;#ifdef DEBUG
include/linux/mm.h +16 -2
@@ -141,6 +141,7 @@wait_queue_head_t wait;struct page **pprev_hash;void *generic_pp;/* This is page buffers iff PageBuffer(page) is true. */ +struct list_head lru;/* dirty page queue */ } mem_map_t;/* Page flag bit values */
@@ -157,8 +158,15 @@#define PG_swap_cache 10#define PG_skip 11#define PG_buffer 12 +#define PG_wcycle_low 13 +#define PG_wcycle_high 24#define PG_reserved 31 +/* Which cycle of page_flush which will write out the pages */ +#define PageWCycle (page) (((page)->flags & ((2 << PG_wcycle_high) -1)) >> PG_wcycle_low) +#define PageSetWCycle (page, cycle) \ + ((page)->flags |= ((cycle & ((PG_wcycle_high - PG_wcycle_low) -1)) << PG_wcycle_low)) +/* Make it prettier to test the above... */ #define PageLocked (page) (test_bit(PG_locked, &(page)->flags))#define PageError (page) (test_bit(PG_error, &(page)->flags))
@@ -225,6 +233,12 @@* page->inode is the pointer to the inode, and page->key is the * offset into the file (divided by PAGE_CACHE_SIZE). * +* If an inode page wants to use the generic dirty page management, +* mark_page_dirty() is called, which sets page->dirty. Either +* mark_page_clean() or write_page() can be called to remove this +* condition. Though usually this will happen automatically after the +* page has aged appropriately. +* * A page may have buffers allocated to it. In this case, * PageBuffer(page) is true and page->generic_pp is a circular list of * these buffer heads. Else, PageBuffer(page) is false.
@@ -332,12 +346,12 @@extern int do_munmap (unsigned long ,size_t );/* filemap.c */ -extern unsigned long page_unuse (struct page *); -extern int shrink_mmap (int ,int );extern void truncate_inode_pages (struct inode *,loff_t );extern void invalidate_inode_pages (struct inode *); +extern int sync_inode_pages (struct inode *inode,int wait);extern void zap_inode_pages (struct inode *);extern void update_vm_cache (struct inode *,loff_t ,const char *,int ); +extern struct page *get_inode_page (struct inode *,loff_t ,unsigned long *);extern unsigned long get_cached_page (struct inode *,unsigned long ,int );extern void put_cached_page (unsigned long );
include/linux/pagemap.h +21 -0
@@ -155,4 +155,25 @@ struct vm_store *store, unsigned long key, struct page **hash); +extern int sync_pcache (int old,int max_write); +extern int sync_pcache_dev (kdev_t dev); + +extern atomic_t nr_dirty_pages; +extern void wakeup_pgflush (int wait); +/* more thought needs to go into mark_page_dirty, +* +* It looks like an excellent place to require functionality to be present +* if there is a configurable function under it. +* +* Also I need to figure out how the delay for writing a page needs to be set. +*/ +extern void mark_page_dirty (struct page *page); +extern void mark_page_clean (struct page *page); +extern void unlock_page (struct page *page); +extern int generic_writepage ( +struct vm_store *store,struct page *page,unsigned long index,void **p); +extern int generic_updatepage (struct file *file,struct page *page, +const char *buf,unsigned int offset,unsigned int count,int sync); +extern int write_page (struct page *page);/* do I need to export this one? */ +#endif
include/linux/vm_store.h +2 -0
@@ -47,6 +47,8 @@extern int shrink_mmap (int priority,int gfp_mask);extern void update_vm_store_cache (struct vm_store *store,unsigned long index,unsigned long offset,const char * buf,int count); +extern int sync_store_pages (struct vm_store *store); +extern int wait_on_store_pages (struct vm_store *store);#endif /* KERNEL */ #endif /* _LINUX_VM_STORE_H */
init/main.c +5 -0
@@ -67,6 +67,8 @@static int init (void *);extern int bdflush (void *); +extern int pgflush (void *); +extern void pgflush_init (void );extern int kswapd (void *);extern int kpiod (void *);extern void kswapd_setup (void );
@@ -1299,6 +1301,9 @@/* Launch bdflush from here, instead of the old syscall way. */ kernel_thread (bdflush,NULL ,CLONE_FS |CLONE_FILES |CLONE_SIGHAND ); +/* Launch pgflush from here, it's a clone of bdflush... */ +pgflush_init (); +kernel_thread (pgflush,NULL ,CLONE_FS |CLONE_FILES |CLONE_SIGHAND );/* Start the background pageout daemon. */ kswapd_setup ();kernel_thread (kpiod,NULL ,CLONE_FS |CLONE_FILES |CLONE_SIGHAND );
ipc/shm.c +1 -0
@@ -515,6 +515,7 @@ shmd->vm_file =NULL ; shmd->vm_store =NULL ; shmd->vm_index =0 ; + shmd->vm_store =NULL ; shmd->vm_ops = &shm_vm_ops; shp->u.shm_nattch++;/* prevent destruction */
kernel/ksyms.c +0 -3
@@ -105,7 +105,6 @@EXPORT_SYMBOL (remap_page_range);EXPORT_SYMBOL (max_mapnr);EXPORT_SYMBOL (high_memory); -EXPORT_SYMBOL (update_vm_cache);EXPORT_SYMBOL (vmtruncate);EXPORT_SYMBOL (find_vma);EXPORT_SYMBOL (get_unmapped_area);
@@ -145,8 +144,6 @@EXPORT_SYMBOL (check_disk_change);EXPORT_SYMBOL (invalidate_buffers);EXPORT_SYMBOL (invalidate_inodes); -EXPORT_SYMBOL (invalidate_inode_pages); -EXPORT_SYMBOL (truncate_inode_pages);EXPORT_SYMBOL (fsync_dev);EXPORT_SYMBOL (permission);EXPORT_SYMBOL (inode_setattr);
mm/Makefile +2 -2
@@ -11,8 +11,8 @@ O_OBJS := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ vmalloc.o slab.o \ swap.o vmscan.o page_io.o page_alloc.o swap_state.o swapfile.o \ - vm_store.o + vm_store.o page_flush.o -OX_OBJS := swap_syms.o +OX_OBJS := swap_syms.o page_syms.o include $(TOPDIR)/Rules.make
mm/filemap.c +33 -5
@@ -58,6 +58,9 @@* locked down (those are sure to be up-to-date anyway, so we shouldn't * invalidate them). */ +/* +* Should we invalidate dirty pages? --EWB 19 June 1998 +*/ void invalidate_inode_pages (struct inode * inode) {invalidate_store_pages (inode->vm_store);
@@ -84,6 +87,11 @@if (keep_bytes) {/* partial truncate, clear end of page */ page =find_page (inode->vm_store, partial_keep); +/* Wait in case we are reading the page and truncating it simultaneously */ +while (PageLocked (page) && !PageUptodate (page)) { +wait_on_page (page); + page =find_page (inode->vm_store, partial_keep); + }if (page) {unsigned long address =page_address (page);memset ((void *) (keep_bytes + address),0 ,PAGE_CACHE_SIZE - keep_bytes);
@@ -118,6 +126,24 @@update_vm_store_cache (inode->vm_store, index, offset, buf, count); } +/* Sync all of the pages associated with an inode */ +int sync_inode_pages (struct inode *inode,int wait) +{ +int error =0 ; +struct vm_store *store, *next; +for (store = inode->vm_store; store !=NULL ; store = next) { + next = store->st_next; + error |=sync_store_pages (store); + } +if (wait) { +for (store = inode->vm_store; store !=NULL ; store = next) { + next = store->st_next; + error |=wait_on_store_pages (store); + } + } +return error? -EIO :0 ; +} +struct page *get_inode_page (struct inode *inode,loff_t page,unsigned long *page_cache_ptr) {
@@ -300,14 +326,14 @@ max_ahead =0 ;/* -* The current page is locked. +* The current page is not uptodate. * If the current position is inside the previous read IO request, do not * try to reread previously read ahead pages. * Otherwise decide or not to read ahead some pages synchronously. * If we are not going to read ahead, set the read ahead context for this * page only. */ -if (PageLocked (page)) { +if (!PageUptodate (page)) {if (!filp->f_ralen || index >= raend || index + filp->f_ralen < raend) { raend = index;if (((loff_t )raend <<PAGE_CACHE_SHIFT ) < inode->i_size)
@@ -321,7 +347,7 @@ } }/* -* The current page is not locked. +* The current page is uptodate * If we were reading ahead and, * if the current max read ahead size is not zero and, * if the current position is inside the last read-ahead IO request,
@@ -507,7 +533,9 @@else if (reada_ok && filp->f_ramax > (MIN_READAHEAD >>PAGE_CACHE_SHIFT )) filp->f_ramax = (MIN_READAHEAD >>PAGE_CACHE_SHIFT ); -wait_on_page (page); +if (!PageUptodate (page) &&PageLocked (page)) { +wait_on_page (page); + }if (!PageUptodate (page)) goto page_read_error;
@@ -798,7 +826,7 @@ goto failure; } -if (PageLocked (page)) +if (PageLocked (page) && !PageUptodate (page)) goto page_locked_wait;if (!PageUptodate (page)) goto page_read_error;
mm/mmap.c +2 -1
@@ -71,7 +71,7 @@ }/* Remove one vm structure from the inode's i_mmap ring. */ -static inline void remove_shared_vm_struct (struct vm_area_struct *vma) +static void remove_shared_vm_struct (struct vm_area_struct *vma) {struct file * file = vma->vm_file;struct vm_store *store = vma->vm_store;
@@ -536,6 +536,7 @@ mpnt->vm_file = area->vm_file; mpnt->vm_store = area->vm_store; mpnt->vm_pte = area->vm_pte; + mpnt->vm_store = area->vm_store;if (mpnt->vm_file) mpnt->vm_file->f_count++;if (mpnt->vm_ops && mpnt->vm_ops->open)
mm/page_alloc.c +4 -0
@@ -124,6 +124,8 @@if (!PageReserved (page) &&atomic_dec_and_test (&page->count)) {if (PageSwapCache (page))panic ("Freeing swap cache page" ); +if (PageDirty (page)) +panic ("Freeing dirty page" ); page->flags &= ~(1 << PG_referenced);free_pages_ok (page - mem_map,0 );return ;
@@ -141,6 +143,8 @@if (atomic_dec_and_test (&map->count)) {if (PageSwapCache (map))panic ("Freeing swap cache pages" ); +if (PageDirty (map)) +panic ("Freeing dirty page" ); map->flags &= ~(1 << PG_referenced);free_pages_ok (map_nr, order);return ;
mm/page_flush.c +566 -0
566 lines not on this page · raw message
mm/page_syms.c +38 -0
@@ -0,0 +1,38 @@ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/pagemap.h> +#include <linux/mm.h> +#include <linux/vm_store.h> + +/* store functions */ +EXPORT_SYMBOL (get_store_page); +EXPORT_SYMBOL (invalidate_store_pages); +EXPORT_SYMBOL (zap_store_pages); +EXPORT_SYMBOL (remove_store_page); +EXPORT_SYMBOL (update_vm_store_cache); +EXPORT_SYMBOL (sync_store_pages); +EXPORT_SYMBOL (wait_on_store_pages); + +/* filemap functions */ +EXPORT_SYMBOL (invalidate_inode_pages); +EXPORT_SYMBOL (truncate_inode_pages); +EXPORT_SYMBOL (sync_inode_pages); +EXPORT_SYMBOL (zap_inode_pages); +EXPORT_SYMBOL (update_vm_cache); +EXPORT_SYMBOL (get_inode_page); + +/* pagemap functions */ + +EXPORT_SYMBOL (add_to_page_cache); +EXPORT_SYMBOL (page_cache_size); +EXPORT_SYMBOL (page_hash_table); +EXPORT_SYMBOL (__wait_on_page); + +EXPORT_SYMBOL (mark_page_dirty); +EXPORT_SYMBOL (mark_page_clean); +EXPORT_SYMBOL (unlock_page); + +EXPORT_SYMBOL (write_page); +EXPORT_SYMBOL (generic_writepage); +EXPORT_SYMBOL (generic_updatepage);
mm/vm_store.c +52 -2
84 lines not on this page · raw message
mm/vmscan.c +7 -0
@@ -385,6 +385,13 @@/* Always trim SLAB caches when memory gets low. */ kmem_cache_reap (gfp_mask); +/* Write out dirty pages when memory gets low. +* Eventually they will unlock and we can free them if needed. +*/ +if (atomic_read (&nr_dirty_pages)) { +wakeup_pgflush (0 ); + } + priority =6 ; do { while (shrink_mmap(priority, gfp_mask)) {
--
To unsubscribe, send a message with 'unsubscribe linux-mm my@address' in the body to [email protected]. For more info on Linux MM,
see: http://humbolt.geo.uu.nl/Linux-MM/