--- include/linux/vmstat.h | 17 ++++-- mm/vmstat.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 127 insertions(+), 12 deletions(-) Index: linux-2.6/include/linux/vmstat.h =================================================================== --- linux-2.6.orig/include/linux/vmstat.h 2007-11-07 16:49:39.985701332 -0800 +++ linux-2.6/include/linux/vmstat.h 2007-11-07 19:55:52.431617269 -0800 @@ -202,15 +202,22 @@ extern void inc_zone_state(struct zone * void __mod_zone_page_state(struct zone *, enum zone_stat_item item, int); void __inc_zone_page_state(struct page *, enum zone_stat_item); void __dec_zone_page_state(struct page *, enum zone_stat_item); +void __inc_zone_state(struct zone *, enum zone_stat_item); +void __dec_zone_state(struct zone *, enum zone_stat_item); +#ifdef CONFIG_FAST_CMPXCHG_LOCAL +#define inc_zone_page_state __inc_zone_page_state +#define dec_zone_page_state __dec_zone_page_state +#define mod_zone_page_state __mod_zone_page_state +#define inc_zone_state __inc_zone_state +#define dec_zone_state __dec_zone_state +#else void mod_zone_page_state(struct zone *, enum zone_stat_item, int); void inc_zone_page_state(struct page *, enum zone_stat_item); void dec_zone_page_state(struct page *, enum zone_stat_item); - -extern void inc_zone_state(struct zone *, enum zone_stat_item); -extern void __inc_zone_state(struct zone *, enum zone_stat_item); -extern void dec_zone_state(struct zone *, enum zone_stat_item); -extern void __dec_zone_state(struct zone *, enum zone_stat_item); +void inc_zone_state(struct zone *, enum zone_stat_item); +void dec_zone_state(struct zone *, enum zone_stat_item); +#endif void refresh_cpu_vm_stats(int); #else /* CONFIG_SMP */ Index: linux-2.6/mm/vmstat.c =================================================================== --- linux-2.6.orig/mm/vmstat.c 2007-11-07 16:49:40.097701253 -0800 +++ linux-2.6/mm/vmstat.c 2007-11-07 19:59:07.391081633 -0800 @@ -153,8 +153,109 @@ static void refresh_zone_stat_thresholds } } +#ifdef CONFIG_FAST_CMPXCHG_LOCAL +void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item, + int delta) +{ + struct per_cpu_pageset *pcp = THIS_CPU(zone->pageset); + s8 *p = pcp->vm_stat_diff + item; + s8 old; + unsigned long new; + unsigned long add; + + do { + add = 0; + old = *p; + new = old + delta; + + if (unlikely(new > pcp->stat_threshold || + new < -pcp->stat_threshold)) { + add = new; + new = 0; + } + + } while (cmpxchg_local(p, old, new) != old); + + if (add) + zone_page_state_add(add, zone, item); +} +EXPORT_SYMBOL(__mod_zone_page_state); + +/* + * Optimized increment and decrement functions implemented using + * cmpxchg_local. These do not require interrupts to be disabled + * and enabled. + */ +void __inc_zone_state(struct zone *zone, enum zone_stat_item item) +{ + struct per_cpu_pageset *pcp = THIS_CPU(zone->pageset); + s8 *p = pcp->vm_stat_diff + item; + int add; + unsigned long old; + unsigned long new; + + do { + add = 0; + old = *p; + new = old + 1; + + if (unlikely(new > pcp->stat_threshold)) { + add = new + pcp->stat_threshold / 2; + new = -(pcp->stat_threshold / 2); + } + } while (cmpxchg_local(p, old, new) != old); + + if (add) + zone_page_state_add(add, zone, item); +} + +void __inc_zone_page_state(struct page *page, enum zone_stat_item item) +{ + __inc_zone_state(page_zone(page), item); +} +EXPORT_SYMBOL(__inc_zone_page_state); + +void __dec_zone_state(struct zone *zone, enum zone_stat_item item) +{ + struct per_cpu_pageset *pcp = THIS_CPU(zone->pageset); + s8 *p = pcp->vm_stat_diff + item; + int sub; + unsigned long old; + unsigned long new; + + do { + sub = 0; + old = *p; + new = old - 1; + + if (unlikely(new < - pcp->stat_threshold)) { + sub = new - pcp->stat_threshold / 2; + new = pcp->stat_threshold / 2; + } + } while (cmpxchg_local(p, old, new) != old); + + if (sub) + zone_page_state_add(sub, zone, item); +} + +void __dec_zone_page_state(struct page *page, enum zone_stat_item item) +{ + __dec_zone_state(page_zone(page), item); +} +EXPORT_SYMBOL(__dec_zone_page_state); + +static inline void sync_diff(struct zone *zone, struct per_cpu_pageset *p, int i) +{ + /* + * xchg_local() would be useful here but that does not exist. + */ + zone_page_state_add(xchg(&p->vm_stat_diff[i], 0), zone, i); +} + +#else /* CONFIG_FAST_CMPXCHG_LOCAL */ + /* - * For use when we know that interrupts are disabled. + * Functions that do not rely on cmpxchg_local */ void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item, int delta) @@ -284,6 +385,17 @@ void dec_zone_page_state(struct page *pa } EXPORT_SYMBOL(dec_zone_page_state); +static inline void sync_diff(struct zone *zone, struct per_cpu_pageset *p, int i) +{ + unsigned long flags; + + local_irq_save(flags); + zone_page_state_add(p->vm_stat_diff[i], zone, i); + p->vm_stat_diff[i] = 0; + local_irq_restore(flags); +} +#endif /* !CONFIG_FAST_CMPXCHG_LOCAL */ + /* * Update the zone counters for one cpu. * @@ -302,7 +414,6 @@ void refresh_cpu_vm_stats(int cpu) { struct zone *zone; int i; - unsigned long flags; for_each_zone(zone) { struct per_cpu_pageset *p; @@ -314,15 +425,12 @@ void refresh_cpu_vm_stats(int cpu) for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) if (p->vm_stat_diff[i]) { - local_irq_save(flags); - zone_page_state_add(p->vm_stat_diff[i], - zone, i); - p->vm_stat_diff[i] = 0; + sync_diff(zone, p, i); + #ifdef CONFIG_NUMA /* 3 seconds idle till flush */ p->expire = 3; #endif - local_irq_restore(flags); } #ifdef CONFIG_NUMA /*