Patch from Trond Myklebust Here's another patch to demonstrate how the NFS client could be added to the generic 'dirty page' accounting. As you can see, I've had to add an extra field to the 'page_state' struct, in order to distinguish between genuinely dirty pages (that have not been written to the server), and pages that have been written into the page-cache on the server, but have not yet been synced to disk (so-called 'unstable writes'). This ought to ensure that NFS indeed get called back by the VM in a timely fashion (via writeback_inodes()) when it is responsible for hogging the memory. nfs/write.c | 4 ++++ linux/page-flags.h | 2 ++ page-writeback.c | 6 +++--- page_alloc.c | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff -puN fs/nfs/write.c~nfs-unstable-pages fs/nfs/write.c --- 25/fs/nfs/write.c~nfs-unstable-pages 2003-02-20 02:41:51.000000000 -0800 +++ 25-akpm/fs/nfs/write.c 2003-02-20 03:09:48.000000000 -0800 @@ -362,6 +362,7 @@ nfs_mark_request_dirty(struct nfs_page * spin_lock(&nfs_wreq_lock); nfs_list_add_request(req, &nfsi->dirty); nfsi->ndirty++; + inc_page_state(nr_dirty); spin_unlock(&nfs_wreq_lock); mark_inode_dirty(inode); } @@ -389,6 +390,7 @@ nfs_mark_request_commit(struct nfs_page spin_lock(&nfs_wreq_lock); nfs_list_add_request(req, &nfsi->commit); nfsi->ncommit++; + inc_page_state(nr_unstable); spin_unlock(&nfs_wreq_lock); mark_inode_dirty(inode); } @@ -457,6 +459,7 @@ nfs_scan_dirty(struct inode *inode, stru int res; res = nfs_scan_list(&nfsi->dirty, dst, file, idx_start, npages); nfsi->ndirty -= res; + sub_page_state(nr_dirty,res); if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); return res; @@ -481,6 +484,7 @@ nfs_scan_commit(struct inode *inode, str int res; res = nfs_scan_list(&nfsi->commit, dst, file, idx_start, npages); nfsi->ncommit -= res; + sub_page_state(nr_unstable,res); if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); return res; diff -puN include/linux/page-flags.h~nfs-unstable-pages include/linux/page-flags.h --- 25/include/linux/page-flags.h~nfs-unstable-pages 2003-02-20 02:41:51.000000000 -0800 +++ 25-akpm/include/linux/page-flags.h 2003-02-20 02:41:51.000000000 -0800 @@ -82,6 +82,7 @@ struct page_state { unsigned long nr_dirty; /* Dirty writeable pages */ unsigned long nr_writeback; /* Pages under writeback */ + unsigned long nr_unstable; /* NFS unstable pages */ unsigned long nr_pagecache; /* Pages in pagecache */ unsigned long nr_page_table_pages;/* Pages used for pagetables */ unsigned long nr_reverse_maps; /* includes PageDirect */ @@ -132,6 +133,7 @@ extern void get_full_page_state(struct p #define inc_page_state(member) mod_page_state(member, 1UL) #define dec_page_state(member) mod_page_state(member, 0UL - 1) +#define sub_page_state(member,delta) mod_page_state(member, 0UL - (delta)) /* diff -puN mm/page_alloc.c~nfs-unstable-pages mm/page_alloc.c --- 25/mm/page_alloc.c~nfs-unstable-pages 2003-02-20 02:41:51.000000000 -0800 +++ 25-akpm/mm/page_alloc.c 2003-02-20 03:09:48.000000000 -0800 @@ -939,11 +939,12 @@ void show_free_areas(void) K(nr_free_pages()), K(nr_free_highpages())); - printk("Active:%lu inactive:%lu dirty:%lu writeback:%lu free:%u\n", + printk("Active:%lu inactive:%lu dirty:%lu writeback:%lu unstable:%lu free:%u\n", active, inactive, ps.nr_dirty, ps.nr_writeback, + ps.nr_unstable, nr_free_pages()); for_each_zone(zone) { @@ -1434,6 +1435,7 @@ struct seq_operations fragmentation_op = static char *vmstat_text[] = { "nr_dirty", "nr_writeback", + "nr_unstable", "nr_pagecache", "nr_page_table_pages", "nr_reverse_maps", diff -puN mm/page-writeback.c~nfs-unstable-pages mm/page-writeback.c --- 25/mm/page-writeback.c~nfs-unstable-pages 2003-02-20 02:41:51.000000000 -0800 +++ 25-akpm/mm/page-writeback.c 2003-02-20 02:41:51.000000000 -0800 @@ -152,7 +152,7 @@ void balance_dirty_pages(struct address_ dirty_exceeded = 1; - if (ps.nr_dirty) + if (ps.nr_dirty + ps.nr_unstable ) writeback_inodes(&wbc); get_dirty_limits(&ps, &background_thresh, &dirty_thresh); @@ -223,7 +223,7 @@ static void background_writeout(unsigned long dirty_thresh; get_dirty_limits(&ps, &background_thresh, &dirty_thresh); - if (ps.nr_dirty < background_thresh && min_pages <= 0) + if (ps.nr_dirty + ps.nr_unstable < background_thresh && min_pages <= 0) break; wbc.encountered_congestion = 0; wbc.nr_to_write = MAX_WRITEBACK_PAGES; @@ -294,7 +294,7 @@ static void wb_kupdate(unsigned long arg oldest_jif = jiffies - (dirty_expire_centisecs * HZ) / 100; start_jif = jiffies; next_jif = start_jif + (dirty_writeback_centisecs * HZ) / 100; - nr_to_write = ps.nr_dirty; + nr_to_write = ps.nr_dirty + ps.nr_unstable; while (nr_to_write > 0) { wbc.encountered_congestion = 0; wbc.nr_to_write = MAX_WRITEBACK_PAGES; _