diff -urNp x-ref/arch/sparc/kernel/sys_sunos.c x/arch/sparc/kernel/sys_sunos.c --- x-ref/arch/sparc/kernel/sys_sunos.c Tue Oct 8 22:26:08 2002 +++ x/arch/sparc/kernel/sys_sunos.c Thu Oct 10 05:05:57 2002 @@ -193,7 +193,7 @@ asmlinkage int sunos_brk(unsigned long b * fool it, but this should catch most mistakes. */ freepages = atomic_read(&buffermem_pages) >> PAGE_SHIFT; - freepages += atomic_read(&page_cache_size); + freepages += page_cache_size; freepages >>= 1; freepages += nr_free_pages(); freepages += nr_swap_pages; diff -urNp x-ref/arch/sparc64/kernel/sys_sunos32.c x/arch/sparc64/kernel/sys_sunos32.c --- x-ref/arch/sparc64/kernel/sys_sunos32.c Tue Oct 8 22:26:08 2002 +++ x/arch/sparc64/kernel/sys_sunos32.c Thu Oct 10 05:05:57 2002 @@ -157,7 +157,7 @@ asmlinkage int sunos_brk(u32 baddr) * fool it, but this should catch most mistakes. */ freepages = atomic_read(&buffermem_pages) >> PAGE_SHIFT; - freepages += atomic_read(&page_cache_size); + freepages += page_cache_size; freepages >>= 1; freepages += nr_free_pages(); freepages += nr_swap_pages; diff -urNp x-ref/fs/buffer.c x/fs/buffer.c --- x-ref/fs/buffer.c Thu Oct 10 05:05:55 2002 +++ x/fs/buffer.c Thu Oct 10 05:05:57 2002 @@ -2721,10 +2721,10 @@ void show_buffers(void) #endif printk("Buffer memory: %6dkB\n", - atomic_read(&buffermem_pages) << (PAGE_SHIFT-10)); + atomic_read(&buffermem_pages) << (PAGE_SHIFT-10)); - printk("Cache memory: %6dkB\n", - (atomic_read(&page_cache_size)- atomic_read(&buffermem_pages)) << (PAGE_SHIFT-10)); + printk("Cache memory: %6ldkB\n", + (page_cache_size - atomic_read(&buffermem_pages)) << (PAGE_SHIFT-10)); #ifdef CONFIG_SMP /* trylock does nothing on UP and so we could deadlock */ if (!spin_trylock(&lru_list_lock)) diff -urNp x-ref/fs/proc/proc_misc.c x/fs/proc/proc_misc.c --- x-ref/fs/proc/proc_misc.c Thu Oct 10 05:05:49 2002 +++ x/fs/proc/proc_misc.c Thu Oct 10 05:05:57 2002 @@ -163,7 +163,7 @@ static int meminfo_read_proc(char *page, #define B(x) ((unsigned long long)(x) << PAGE_SHIFT) si_meminfo(&i); si_swapinfo(&i); - pg_size = atomic_read(&page_cache_size) - i.bufferram ; + pg_size = page_cache_size - i.bufferram; len = sprintf(page, " total: used: free: shared: buffers: cached:\n" "Mem: %8Lu %8Lu %8Lu %8Lu %8Lu %8Lu\n" diff -urNp x-ref/include/linux/mmzone.h x/include/linux/mmzone.h --- x-ref/include/linux/mmzone.h Tue Oct 8 22:26:27 2002 +++ x/include/linux/mmzone.h Thu Oct 10 05:05:57 2002 @@ -41,7 +41,18 @@ typedef struct zone_struct { spinlock_t lock; unsigned long free_pages; unsigned long pages_min, pages_low, pages_high; - int need_balance; + + /* + * The below fields are protected by different locks (or by + * no lock at all like need_balance), so they're longs to + * provide an atomic granularity against each other on + * all architectures. + */ + unsigned long need_balance; + /* protected by the pagemap_lru_lock */ + unsigned long nr_active_pages, nr_inactive_pages; + /* protected by the pagecache_lock */ + unsigned long nr_cache_pages; /* * free areas of different sizes diff -urNp x-ref/include/linux/pagemap.h x/include/linux/pagemap.h --- x-ref/include/linux/pagemap.h Tue Oct 8 22:26:27 2002 +++ x/include/linux/pagemap.h Thu Oct 10 05:05:57 2002 @@ -45,7 +45,7 @@ extern unsigned int page_hash_bits; #define PAGE_HASH_BITS (page_hash_bits) #define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS) -extern atomic_t page_cache_size; /* # of pages currently in the hash table */ +extern unsigned long page_cache_size; /* # of pages currently in the hash table */ extern struct page **page_hash_table; extern void page_cache_init(unsigned long); diff -urNp x-ref/include/linux/swap.h x/include/linux/swap.h --- x-ref/include/linux/swap.h Thu Oct 10 05:05:55 2002 +++ x/include/linux/swap.h Thu Oct 10 05:06:12 2002 @@ -87,7 +87,7 @@ extern unsigned int nr_free_pages(void); extern unsigned int nr_free_buffer_pages(void); extern int nr_active_pages; extern int nr_inactive_pages; -extern atomic_t page_cache_size; +extern unsigned long page_cache_size; extern atomic_t buffermem_pages; extern spinlock_cacheline_t pagecache_lock_cacheline; @@ -177,34 +177,46 @@ do { \ BUG(); \ } while (0) +extern void delta_nr_active_pages(struct page *page, long delta); +#define inc_nr_active_pages(page) delta_nr_active_pages(page, 1) +#define dec_nr_active_pages(page) delta_nr_active_pages(page, -1) + +extern void delta_nr_inactive_pages(struct page *page, long delta); +#define inc_nr_inactive_pages(page) delta_nr_inactive_pages(page, 1) +#define dec_nr_inactive_pages(page) delta_nr_inactive_pages(page, -1) + #define add_page_to_active_list(page) \ do { \ DEBUG_LRU_PAGE(page); \ SetPageActive(page); \ list_add(&(page)->lru, &active_list); \ - nr_active_pages++; \ + inc_nr_active_pages(page); \ } while (0) #define add_page_to_inactive_list(page) \ do { \ DEBUG_LRU_PAGE(page); \ list_add(&(page)->lru, &inactive_list); \ - nr_inactive_pages++; \ + inc_nr_inactive_pages(page); \ } while (0) #define del_page_from_active_list(page) \ do { \ list_del(&(page)->lru); \ ClearPageActive(page); \ - nr_active_pages--; \ + dec_nr_active_pages(page); \ } while (0) #define del_page_from_inactive_list(page) \ do { \ list_del(&(page)->lru); \ - nr_inactive_pages--; \ + dec_nr_inactive_pages(page); \ } while (0) +extern void delta_nr_cache_pages(struct page *page, long delta); +#define inc_nr_cache_pages(page) delta_nr_cache_pages(page, 1) +#define dec_nr_cache_pages(page) delta_nr_cache_pages(page, -1) + extern spinlock_t swaplock; #define swap_list_lock() spin_lock(&swaplock) diff -urNp x-ref/mm/filemap.c x/mm/filemap.c --- x-ref/mm/filemap.c Thu Oct 10 05:05:53 2002 +++ x/mm/filemap.c Thu Oct 10 05:05:57 2002 @@ -42,7 +42,7 @@ * SMP-threaded pagemap-LRU 1999, Andrea Arcangeli */ -atomic_t page_cache_size = ATOMIC_INIT(0); +unsigned long page_cache_size; unsigned int page_hash_bits; struct page **page_hash_table; @@ -79,7 +79,7 @@ static void add_page_to_hash_queue(struc next->pprev_hash = &page->next_hash; if (page->buffers) PAGE_BUG(page); - atomic_inc(&page_cache_size); + inc_nr_cache_pages(page); } static inline void add_page_to_inode_queue(struct address_space *mapping, struct page * page) @@ -109,7 +109,7 @@ static inline void remove_page_from_hash next->pprev_hash = pprev; *pprev = next; page->pprev_hash = NULL; - atomic_dec(&page_cache_size); + dec_nr_cache_pages(page); } /* diff -urNp x-ref/mm/mmap.c x/mm/mmap.c --- x-ref/mm/mmap.c Thu Oct 10 05:05:51 2002 +++ x/mm/mmap.c Thu Oct 10 05:05:57 2002 @@ -70,7 +70,7 @@ int vm_enough_memory(long pages) return 1; /* The page cache contains buffer pages these days.. */ - free = atomic_read(&page_cache_size); + free = page_cache_size; free += nr_free_pages(); free += nr_swap_pages; diff -urNp x-ref/mm/swap.c x/mm/swap.c --- x-ref/mm/swap.c Tue Oct 8 22:26:27 2002 +++ x/mm/swap.c Thu Oct 10 05:05:57 2002 @@ -94,6 +94,78 @@ void lru_cache_del(struct page * page) spin_unlock(&pagemap_lru_lock); } +/** + * delta_nr_active_pages: alter the number of active pages. + * + * @page: the page which is being activated/deactivated + * @delta: +1 for activation, -1 for deactivation + * + * Called under pagecache_lock + */ +void delta_nr_active_pages(struct page *page, long delta) +{ + pg_data_t *pgdat; + zone_t *classzone, *overflow; + + classzone = page_zone(page); + pgdat = classzone->zone_pgdat; + overflow = pgdat->node_zones + pgdat->nr_zones; + + while (classzone < overflow) { + classzone->nr_active_pages += delta; + classzone++; + } + nr_active_pages += delta; +} + +/** + * delta_nr_inactive_pages: alter the number of inactive pages. + * + * @page: the page which is being deactivated/activated + * @delta: +1 for deactivation, -1 for activation + * + * Called under pagecache_lock + */ +void delta_nr_inactive_pages(struct page *page, long delta) +{ + pg_data_t *pgdat; + zone_t *classzone, *overflow; + + classzone = page_zone(page); + pgdat = classzone->zone_pgdat; + overflow = pgdat->node_zones + pgdat->nr_zones; + + while (classzone < overflow) { + classzone->nr_inactive_pages += delta; + classzone++; + } + nr_inactive_pages += delta; +} + +/** + * delta_nr_cache_pages: alter the number of pages in the pagecache + * + * @page: the page which is being added/removed + * @delta: +1 for addition, -1 for removal + * + * Called under pagecache_lock + */ +void delta_nr_cache_pages(struct page *page, long delta) +{ + pg_data_t *pgdat; + zone_t *classzone, *overflow; + + classzone = page_zone(page); + pgdat = classzone->zone_pgdat; + overflow = pgdat->node_zones + pgdat->nr_zones; + + while (classzone < overflow) { + classzone->nr_cache_pages += delta; + classzone++; + } + page_cache_size += delta; +} + /* * Perform any setup for the swap system */