2006-06-30 12:55:32 +04:00
/*
* linux / mm / vmstat . c
*
* Manages VM statistics
* Copyright ( C ) 1991 , 1992 , 1993 , 1994 Linus Torvalds
2006-06-30 12:55:33 +04:00
*
* zoned VM statistics
* Copyright ( C ) 2006 Silicon Graphics , Inc . ,
* Christoph Lameter < christoph @ lameter . com >
2006-06-30 12:55:32 +04:00
*/
# include <linux/mm.h>
2006-06-30 12:55:33 +04:00
# include <linux/module.h>
2006-09-01 08:27:35 +04:00
# include <linux/cpu.h>
2006-06-30 12:55:32 +04:00
2006-06-30 12:55:45 +04:00
# ifdef CONFIG_VM_EVENT_COUNTERS
DEFINE_PER_CPU ( struct vm_event_state , vm_event_states ) = { { 0 } } ;
EXPORT_PER_CPU_SYMBOL ( vm_event_states ) ;
static void sum_vm_events ( unsigned long * ret , cpumask_t * cpumask )
{
int cpu = 0 ;
int i ;
memset ( ret , 0 , NR_VM_EVENT_ITEMS * sizeof ( unsigned long ) ) ;
cpu = first_cpu ( * cpumask ) ;
while ( cpu < NR_CPUS ) {
struct vm_event_state * this = & per_cpu ( vm_event_states , cpu ) ;
cpu = next_cpu ( cpu , * cpumask ) ;
if ( cpu < NR_CPUS )
prefetch ( & per_cpu ( vm_event_states , cpu ) ) ;
for ( i = 0 ; i < NR_VM_EVENT_ITEMS ; i + + )
ret [ i ] + = this - > event [ i ] ;
}
}
/*
* Accumulate the vm event counters across all CPUs .
* The result is unavoidably approximate - it can change
* during and after execution of this function .
*/
void all_vm_events ( unsigned long * ret )
{
sum_vm_events ( ret , & cpu_online_map ) ;
}
2006-07-10 15:44:31 +04:00
EXPORT_SYMBOL_GPL ( all_vm_events ) ;
2006-06-30 12:55:45 +04:00
# ifdef CONFIG_HOTPLUG
/*
* Fold the foreign cpu events into our own .
*
* This is adding to the events on one processor
* but keeps the global counts constant .
*/
void vm_events_fold_cpu ( int cpu )
{
struct vm_event_state * fold_state = & per_cpu ( vm_event_states , cpu ) ;
int i ;
for ( i = 0 ; i < NR_VM_EVENT_ITEMS ; i + + ) {
count_vm_events ( i , fold_state - > event [ i ] ) ;
fold_state - > event [ i ] = 0 ;
}
}
# endif /* CONFIG_HOTPLUG */
# endif /* CONFIG_VM_EVENT_COUNTERS */
2006-06-30 12:55:33 +04:00
/*
* Manage combined zone based / global counters
*
* vm_stat contains the global counters
*/
atomic_long_t vm_stat [ NR_VM_ZONE_STAT_ITEMS ] ;
EXPORT_SYMBOL ( vm_stat ) ;
# ifdef CONFIG_SMP
2006-09-01 08:27:35 +04:00
static int calculate_threshold ( struct zone * zone )
{
int threshold ;
int mem ; /* memory in 128 MB units */
/*
* The threshold scales with the number of processors and the amount
* of memory per zone . More memory means that we can defer updates for
* longer , more processors could lead to more contention .
* fls ( ) is used to have a cheap way of logarithmic scaling .
*
* Some sample thresholds :
*
* Threshold Processors ( fls ) Zonesize fls ( mem + 1 )
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* 8 1 1 0.9 - 1 GB 4
* 16 2 2 0.9 - 1 GB 4
* 20 2 2 1 - 2 GB 5
* 24 2 2 2 - 4 GB 6
* 28 2 2 4 - 8 GB 7
* 32 2 2 8 - 16 GB 8
* 4 2 2 < 128 M 1
* 30 4 3 2 - 4 GB 5
* 48 4 3 8 - 16 GB 8
* 32 8 4 1 - 2 GB 4
* 32 8 4 0.9 - 1 GB 4
* 10 16 5 < 128 M 1
* 40 16 5 900 M 4
* 70 64 7 2 - 4 GB 5
* 84 64 7 4 - 8 GB 6
* 108 512 9 4 - 8 GB 6
* 125 1024 10 8 - 16 GB 8
* 125 1024 10 16 - 32 GB 9
*/
mem = zone - > present_pages > > ( 27 - PAGE_SHIFT ) ;
threshold = 2 * fls ( num_online_cpus ( ) ) * ( 1 + fls ( mem ) ) ;
/*
* Maximum threshold is 125
*/
threshold = min ( 125 , threshold ) ;
return threshold ;
}
2006-06-30 12:55:33 +04:00
/*
2006-09-01 08:27:35 +04:00
* Refresh the thresholds for each zone .
2006-06-30 12:55:33 +04:00
*/
2006-09-01 08:27:35 +04:00
static void refresh_zone_stat_thresholds ( void )
2006-06-30 12:55:33 +04:00
{
2006-09-01 08:27:35 +04:00
struct zone * zone ;
int cpu ;
int threshold ;
for_each_zone ( zone ) {
if ( ! zone - > present_pages )
continue ;
threshold = calculate_threshold ( zone ) ;
for_each_online_cpu ( cpu )
zone_pcp ( zone , cpu ) - > stat_threshold = threshold ;
}
2006-06-30 12:55:33 +04:00
}
/*
* For use when we know that interrupts are disabled .
*/
void __mod_zone_page_state ( struct zone * zone , enum zone_stat_item item ,
int delta )
{
2006-09-01 08:27:35 +04:00
struct per_cpu_pageset * pcp = zone_pcp ( zone , smp_processor_id ( ) ) ;
s8 * p = pcp - > vm_stat_diff + item ;
2006-06-30 12:55:33 +04:00
long x ;
x = delta + * p ;
2006-09-01 08:27:35 +04:00
if ( unlikely ( x > pcp - > stat_threshold | | x < - pcp - > stat_threshold ) ) {
2006-06-30 12:55:33 +04:00
zone_page_state_add ( x , zone , item ) ;
x = 0 ;
}
* p = x ;
}
EXPORT_SYMBOL ( __mod_zone_page_state ) ;
/*
* For an unknown interrupt state
*/
void mod_zone_page_state ( struct zone * zone , enum zone_stat_item item ,
int delta )
{
unsigned long flags ;
local_irq_save ( flags ) ;
__mod_zone_page_state ( zone , item , delta ) ;
local_irq_restore ( flags ) ;
}
EXPORT_SYMBOL ( mod_zone_page_state ) ;
/*
* Optimized increment and decrement functions .
*
* These are only for a single page and therefore can take a struct page *
* argument instead of struct zone * . This allows the inclusion of the code
* generated for page_zone ( page ) into the optimized functions .
*
* No overflow check is necessary and therefore the differential can be
* incremented or decremented in place which may allow the compilers to
* generate better code .
* The increment or decrement is known and therefore one boundary check can
* be omitted .
*
2006-09-01 08:27:35 +04:00
* NOTE : These functions are very performance sensitive . Change only
* with care .
*
2006-06-30 12:55:33 +04:00
* Some processors have inc / dec instructions that are atomic vs an interrupt .
* However , the code must first determine the differential location in a zone
* based on the processor number and then inc / dec the counter . There is no
* guarantee without disabling preemption that the processor will not change
* in between and therefore the atomicity vs . interrupt cannot be exploited
* in a useful way here .
*/
2007-02-10 12:43:01 +03:00
void __inc_zone_state ( struct zone * zone , enum zone_stat_item item )
2006-06-30 12:55:33 +04:00
{
2006-09-01 08:27:35 +04:00
struct per_cpu_pageset * pcp = zone_pcp ( zone , smp_processor_id ( ) ) ;
s8 * p = pcp - > vm_stat_diff + item ;
2006-06-30 12:55:33 +04:00
( * p ) + + ;
2006-09-01 08:27:35 +04:00
if ( unlikely ( * p > pcp - > stat_threshold ) ) {
int overstep = pcp - > stat_threshold / 2 ;
zone_page_state_add ( * p + overstep , zone , item ) ;
* p = - overstep ;
2006-06-30 12:55:33 +04:00
}
}
2006-06-30 12:55:44 +04:00
void __inc_zone_page_state ( struct page * page , enum zone_stat_item item )
{
__inc_zone_state ( page_zone ( page ) , item ) ;
}
2006-06-30 12:55:33 +04:00
EXPORT_SYMBOL ( __inc_zone_page_state ) ;
2007-02-10 12:43:01 +03:00
void __dec_zone_state ( struct zone * zone , enum zone_stat_item item )
2006-06-30 12:55:33 +04:00
{
2006-09-01 08:27:35 +04:00
struct per_cpu_pageset * pcp = zone_pcp ( zone , smp_processor_id ( ) ) ;
s8 * p = pcp - > vm_stat_diff + item ;
2006-06-30 12:55:33 +04:00
( * p ) - - ;
2006-09-01 08:27:35 +04:00
if ( unlikely ( * p < - pcp - > stat_threshold ) ) {
int overstep = pcp - > stat_threshold / 2 ;
zone_page_state_add ( * p - overstep , zone , item ) ;
* p = overstep ;
2006-06-30 12:55:33 +04:00
}
}
2007-02-10 12:43:01 +03:00
void __dec_zone_page_state ( struct page * page , enum zone_stat_item item )
{
__dec_zone_state ( page_zone ( page ) , item ) ;
}
2006-06-30 12:55:33 +04:00
EXPORT_SYMBOL ( __dec_zone_page_state ) ;
2006-06-30 12:55:44 +04:00
void inc_zone_state ( struct zone * zone , enum zone_stat_item item )
{
unsigned long flags ;
local_irq_save ( flags ) ;
__inc_zone_state ( zone , item ) ;
local_irq_restore ( flags ) ;
}
2006-06-30 12:55:33 +04:00
void inc_zone_page_state ( struct page * page , enum zone_stat_item item )
{
unsigned long flags ;
struct zone * zone ;
zone = page_zone ( page ) ;
local_irq_save ( flags ) ;
2006-06-30 12:55:44 +04:00
__inc_zone_state ( zone , item ) ;
2006-06-30 12:55:33 +04:00
local_irq_restore ( flags ) ;
}
EXPORT_SYMBOL ( inc_zone_page_state ) ;
void dec_zone_page_state ( struct page * page , enum zone_stat_item item )
{
unsigned long flags ;
local_irq_save ( flags ) ;
2006-09-01 08:27:34 +04:00
__dec_zone_page_state ( page , item ) ;
2006-06-30 12:55:33 +04:00
local_irq_restore ( flags ) ;
}
EXPORT_SYMBOL ( dec_zone_page_state ) ;
/*
* Update the zone counters for one cpu .
*/
void refresh_cpu_vm_stats ( int cpu )
{
struct zone * zone ;
int i ;
unsigned long flags ;
for_each_zone ( zone ) {
struct per_cpu_pageset * pcp ;
2006-09-26 10:31:49 +04:00
if ( ! populated_zone ( zone ) )
continue ;
2006-06-30 12:55:33 +04:00
pcp = zone_pcp ( zone , cpu ) ;
for ( i = 0 ; i < NR_VM_ZONE_STAT_ITEMS ; i + + )
if ( pcp - > vm_stat_diff [ i ] ) {
local_irq_save ( flags ) ;
zone_page_state_add ( pcp - > vm_stat_diff [ i ] ,
zone , i ) ;
pcp - > vm_stat_diff [ i ] = 0 ;
local_irq_restore ( flags ) ;
}
}
}
static void __refresh_cpu_vm_stats ( void * dummy )
{
refresh_cpu_vm_stats ( smp_processor_id ( ) ) ;
}
/*
* Consolidate all counters .
*
* Note that the result is less inaccurate but still inaccurate
* if concurrent processes are allowed to run .
*/
void refresh_vm_stats ( void )
{
on_each_cpu ( __refresh_cpu_vm_stats , NULL , 0 , 1 ) ;
}
EXPORT_SYMBOL ( refresh_vm_stats ) ;
# endif
2006-06-30 12:55:44 +04:00
# ifdef CONFIG_NUMA
/*
* zonelist = the list of zones passed to the allocator
* z = the zone from which the allocation occurred .
*
* Must be called with interrupts disabled .
*/
void zone_statistics ( struct zonelist * zonelist , struct zone * z )
{
if ( z - > zone_pgdat = = zonelist - > zones [ 0 ] - > zone_pgdat ) {
__inc_zone_state ( z , NUMA_HIT ) ;
} else {
__inc_zone_state ( z , NUMA_MISS ) ;
__inc_zone_state ( zonelist - > zones [ 0 ] , NUMA_FOREIGN ) ;
}
2006-09-27 12:50:10 +04:00
if ( z - > node = = numa_node_id ( ) )
2006-06-30 12:55:44 +04:00
__inc_zone_state ( z , NUMA_LOCAL ) ;
else
__inc_zone_state ( z , NUMA_OTHER ) ;
}
# endif
2006-06-30 12:55:32 +04:00
# ifdef CONFIG_PROC_FS
# include <linux/seq_file.h>
static void * frag_start ( struct seq_file * m , loff_t * pos )
{
pg_data_t * pgdat ;
loff_t node = * pos ;
for ( pgdat = first_online_pgdat ( ) ;
pgdat & & node ;
pgdat = next_online_pgdat ( pgdat ) )
- - node ;
return pgdat ;
}
static void * frag_next ( struct seq_file * m , void * arg , loff_t * pos )
{
pg_data_t * pgdat = ( pg_data_t * ) arg ;
( * pos ) + + ;
return next_online_pgdat ( pgdat ) ;
}
static void frag_stop ( struct seq_file * m , void * arg )
{
}
/*
* This walks the free areas for each zone .
*/
static int frag_show ( struct seq_file * m , void * arg )
{
pg_data_t * pgdat = ( pg_data_t * ) arg ;
struct zone * zone ;
struct zone * node_zones = pgdat - > node_zones ;
unsigned long flags ;
int order ;
for ( zone = node_zones ; zone - node_zones < MAX_NR_ZONES ; + + zone ) {
if ( ! populated_zone ( zone ) )
continue ;
spin_lock_irqsave ( & zone - > lock , flags ) ;
seq_printf ( m , " Node %d, zone %8s " , pgdat - > node_id , zone - > name ) ;
for ( order = 0 ; order < MAX_ORDER ; + + order )
seq_printf ( m , " %6lu " , zone - > free_area [ order ] . nr_free ) ;
spin_unlock_irqrestore ( & zone - > lock , flags ) ;
seq_putc ( m , ' \n ' ) ;
}
return 0 ;
}
2006-12-07 07:40:36 +03:00
const struct seq_operations fragmentation_op = {
2006-06-30 12:55:32 +04:00
. start = frag_start ,
. next = frag_next ,
. stop = frag_stop ,
. show = frag_show ,
} ;
2007-02-10 12:43:10 +03:00
# ifdef CONFIG_ZONE_DMA
# define TEXT_FOR_DMA(xx) xx "_dma",
# else
# define TEXT_FOR_DMA(xx)
# endif
2006-09-26 10:31:15 +04:00
# ifdef CONFIG_ZONE_DMA32
# define TEXT_FOR_DMA32(xx) xx "_dma32",
# else
# define TEXT_FOR_DMA32(xx)
# endif
# ifdef CONFIG_HIGHMEM
# define TEXT_FOR_HIGHMEM(xx) xx "_high",
# else
# define TEXT_FOR_HIGHMEM(xx)
# endif
2007-02-10 12:43:10 +03:00
# define TEXTS_FOR_ZONES(xx) TEXT_FOR_DMA(xx) TEXT_FOR_DMA32(xx) xx "_normal", \
2006-09-26 10:31:15 +04:00
TEXT_FOR_HIGHMEM ( xx )
2006-12-07 07:40:36 +03:00
static const char * const vmstat_text [ ] = {
2006-06-30 12:55:33 +04:00
/* Zoned VM counters */
2007-02-10 12:43:02 +03:00
" nr_free_pages " ,
2007-02-10 12:43:01 +03:00
" nr_active " ,
" nr_inactive " ,
2006-06-30 12:55:36 +04:00
" nr_anon_pages " ,
2006-06-30 12:55:34 +04:00
" nr_mapped " ,
2006-06-30 12:55:35 +04:00
" nr_file_pages " ,
2007-02-10 12:43:02 +03:00
" nr_dirty " ,
" nr_writeback " ,
2006-09-26 10:31:51 +04:00
" nr_slab_reclaimable " ,
" nr_slab_unreclaimable " ,
2006-06-30 12:55:38 +04:00
" nr_page_table_pages " ,
2006-06-30 12:55:32 +04:00
" nr_unstable " ,
2006-06-30 12:55:41 +04:00
" nr_bounce " ,
2006-09-27 12:50:00 +04:00
" nr_vmscan_write " ,
2006-06-30 12:55:32 +04:00
2006-06-30 12:55:44 +04:00
# ifdef CONFIG_NUMA
" numa_hit " ,
" numa_miss " ,
" numa_foreign " ,
" numa_interleave " ,
" numa_local " ,
" numa_other " ,
# endif
2006-06-30 12:55:45 +04:00
# ifdef CONFIG_VM_EVENT_COUNTERS
2006-06-30 12:55:32 +04:00
" pgpgin " ,
" pgpgout " ,
" pswpin " ,
" pswpout " ,
2006-09-26 10:31:15 +04:00
TEXTS_FOR_ZONES ( " pgalloc " )
2006-06-30 12:55:32 +04:00
" pgfree " ,
" pgactivate " ,
" pgdeactivate " ,
" pgfault " ,
" pgmajfault " ,
2006-09-26 10:31:15 +04:00
TEXTS_FOR_ZONES ( " pgrefill " )
TEXTS_FOR_ZONES ( " pgsteal " )
TEXTS_FOR_ZONES ( " pgscan_kswapd " )
TEXTS_FOR_ZONES ( " pgscan_direct " )
2006-06-30 12:55:32 +04:00
" pginodesteal " ,
" slabs_scanned " ,
" kswapd_steal " ,
" kswapd_inodesteal " ,
" pageoutrun " ,
" allocstall " ,
" pgrotated " ,
2006-06-30 12:55:45 +04:00
# endif
2006-06-30 12:55:32 +04:00
} ;
/*
* Output information about zones in @ pgdat .
*/
static int zoneinfo_show ( struct seq_file * m , void * arg )
{
pg_data_t * pgdat = arg ;
struct zone * zone ;
struct zone * node_zones = pgdat - > node_zones ;
unsigned long flags ;
for ( zone = node_zones ; zone - node_zones < MAX_NR_ZONES ; zone + + ) {
int i ;
if ( ! populated_zone ( zone ) )
continue ;
spin_lock_irqsave ( & zone - > lock , flags ) ;
seq_printf ( m , " Node %d, zone %8s " , pgdat - > node_id , zone - > name ) ;
seq_printf ( m ,
" \n pages free %lu "
" \n min %lu "
" \n low %lu "
" \n high %lu "
" \n scanned %lu (a: %lu i: %lu) "
" \n spanned %lu "
" \n present %lu " ,
2007-02-10 12:43:02 +03:00
zone_page_state ( zone , NR_FREE_PAGES ) ,
2006-06-30 12:55:32 +04:00
zone - > pages_min ,
zone - > pages_low ,
zone - > pages_high ,
zone - > pages_scanned ,
zone - > nr_scan_active , zone - > nr_scan_inactive ,
zone - > spanned_pages ,
zone - > present_pages ) ;
2006-06-30 12:55:33 +04:00
for ( i = 0 ; i < NR_VM_ZONE_STAT_ITEMS ; i + + )
seq_printf ( m , " \n %-12s %lu " , vmstat_text [ i ] ,
zone_page_state ( zone , i ) ) ;
2006-06-30 12:55:32 +04:00
seq_printf ( m ,
" \n protection: (%lu " ,
zone - > lowmem_reserve [ 0 ] ) ;
for ( i = 1 ; i < ARRAY_SIZE ( zone - > lowmem_reserve ) ; i + + )
seq_printf ( m , " , %lu " , zone - > lowmem_reserve [ i ] ) ;
seq_printf ( m ,
" ) "
" \n pagesets " ) ;
for_each_online_cpu ( i ) {
struct per_cpu_pageset * pageset ;
int j ;
pageset = zone_pcp ( zone , i ) ;
for ( j = 0 ; j < ARRAY_SIZE ( pageset - > pcp ) ; j + + ) {
seq_printf ( m ,
" \n cpu: %i pcp: %i "
" \n count: %i "
" \n high: %i "
" \n batch: %i " ,
i , j ,
pageset - > pcp [ j ] . count ,
pageset - > pcp [ j ] . high ,
pageset - > pcp [ j ] . batch ) ;
}
2006-09-01 08:27:35 +04:00
# ifdef CONFIG_SMP
seq_printf ( m , " \n vm stats threshold: %d " ,
pageset - > stat_threshold ) ;
# endif
2006-06-30 12:55:32 +04:00
}
seq_printf ( m ,
" \n all_unreclaimable: %u "
" \n prev_priority: %i "
" \n start_pfn: %lu " ,
zone - > all_unreclaimable ,
zone - > prev_priority ,
zone - > zone_start_pfn ) ;
spin_unlock_irqrestore ( & zone - > lock , flags ) ;
seq_putc ( m , ' \n ' ) ;
}
return 0 ;
}
2006-12-07 07:40:36 +03:00
const struct seq_operations zoneinfo_op = {
2006-06-30 12:55:32 +04:00
. start = frag_start , /* iterate over all zones. The same as in
* fragmentation . */
. next = frag_next ,
. stop = frag_stop ,
. show = zoneinfo_show ,
} ;
static void * vmstat_start ( struct seq_file * m , loff_t * pos )
{
2006-06-30 12:55:33 +04:00
unsigned long * v ;
2006-06-30 12:55:45 +04:00
# ifdef CONFIG_VM_EVENT_COUNTERS
unsigned long * e ;
# endif
2006-06-30 12:55:33 +04:00
int i ;
2006-06-30 12:55:32 +04:00
if ( * pos > = ARRAY_SIZE ( vmstat_text ) )
return NULL ;
2006-06-30 12:55:45 +04:00
# ifdef CONFIG_VM_EVENT_COUNTERS
2006-06-30 12:55:33 +04:00
v = kmalloc ( NR_VM_ZONE_STAT_ITEMS * sizeof ( unsigned long )
2006-06-30 12:55:45 +04:00
+ sizeof ( struct vm_event_state ) , GFP_KERNEL ) ;
# else
v = kmalloc ( NR_VM_ZONE_STAT_ITEMS * sizeof ( unsigned long ) ,
GFP_KERNEL ) ;
# endif
2006-06-30 12:55:33 +04:00
m - > private = v ;
if ( ! v )
2006-06-30 12:55:32 +04:00
return ERR_PTR ( - ENOMEM ) ;
2006-06-30 12:55:33 +04:00
for ( i = 0 ; i < NR_VM_ZONE_STAT_ITEMS ; i + + )
v [ i ] = global_page_state ( i ) ;
2006-06-30 12:55:45 +04:00
# ifdef CONFIG_VM_EVENT_COUNTERS
e = v + NR_VM_ZONE_STAT_ITEMS ;
all_vm_events ( e ) ;
e [ PGPGIN ] / = 2 ; /* sectors -> kbytes */
e [ PGPGOUT ] / = 2 ;
# endif
2006-06-30 12:55:33 +04:00
return v + * pos ;
2006-06-30 12:55:32 +04:00
}
static void * vmstat_next ( struct seq_file * m , void * arg , loff_t * pos )
{
( * pos ) + + ;
if ( * pos > = ARRAY_SIZE ( vmstat_text ) )
return NULL ;
return ( unsigned long * ) m - > private + * pos ;
}
static int vmstat_show ( struct seq_file * m , void * arg )
{
unsigned long * l = arg ;
unsigned long off = l - ( unsigned long * ) m - > private ;
seq_printf ( m , " %s %lu \n " , vmstat_text [ off ] , * l ) ;
return 0 ;
}
static void vmstat_stop ( struct seq_file * m , void * arg )
{
kfree ( m - > private ) ;
m - > private = NULL ;
}
2006-12-07 07:40:36 +03:00
const struct seq_operations vmstat_op = {
2006-06-30 12:55:32 +04:00
. start = vmstat_start ,
. next = vmstat_next ,
. stop = vmstat_stop ,
. show = vmstat_show ,
} ;
# endif /* CONFIG_PROC_FS */
2006-09-01 08:27:35 +04:00
# ifdef CONFIG_SMP
2007-05-09 13:35:12 +04:00
static DEFINE_PER_CPU ( struct delayed_work , vmstat_work ) ;
2007-05-09 13:35:13 +04:00
int sysctl_stat_interval __read_mostly = HZ ;
2007-05-09 13:35:12 +04:00
static void vmstat_update ( struct work_struct * w )
{
refresh_cpu_vm_stats ( smp_processor_id ( ) ) ;
2007-05-09 13:35:13 +04:00
schedule_delayed_work ( & __get_cpu_var ( vmstat_work ) ,
sysctl_stat_interval ) ;
2007-05-09 13:35:12 +04:00
}
static void __devinit start_cpu_timer ( int cpu )
{
struct delayed_work * vmstat_work = & per_cpu ( vmstat_work , cpu ) ;
INIT_DELAYED_WORK ( vmstat_work , vmstat_update ) ;
schedule_delayed_work_on ( cpu , vmstat_work , HZ + cpu ) ;
}
2006-09-01 08:27:35 +04:00
/*
* Use the cpu notifier to insure that the thresholds are recalculated
* when necessary .
*/
static int __cpuinit vmstat_cpuup_callback ( struct notifier_block * nfb ,
unsigned long action ,
void * hcpu )
{
2007-05-09 13:35:12 +04:00
long cpu = ( long ) hcpu ;
2006-09-01 08:27:35 +04:00
switch ( action ) {
2007-05-09 13:35:12 +04:00
case CPU_ONLINE :
case CPU_ONLINE_FROZEN :
start_cpu_timer ( cpu ) ;
break ;
case CPU_DOWN_PREPARE :
case CPU_DOWN_PREPARE_FROZEN :
cancel_rearming_delayed_work ( & per_cpu ( vmstat_work , cpu ) ) ;
per_cpu ( vmstat_work , cpu ) . work . func = NULL ;
break ;
case CPU_DOWN_FAILED :
case CPU_DOWN_FAILED_FROZEN :
start_cpu_timer ( cpu ) ;
break ;
2006-12-07 07:33:08 +03:00
case CPU_DEAD :
2007-05-09 13:35:10 +04:00
case CPU_DEAD_FROZEN :
2006-12-07 07:33:08 +03:00
refresh_zone_stat_thresholds ( ) ;
break ;
default :
break ;
2006-09-01 08:27:35 +04:00
}
return NOTIFY_OK ;
}
static struct notifier_block __cpuinitdata vmstat_notifier =
{ & vmstat_cpuup_callback , NULL , 0 } ;
int __init setup_vmstat ( void )
{
2007-05-09 13:35:12 +04:00
int cpu ;
2006-09-01 08:27:35 +04:00
refresh_zone_stat_thresholds ( ) ;
register_cpu_notifier ( & vmstat_notifier ) ;
2007-05-09 13:35:12 +04:00
for_each_online_cpu ( cpu )
start_cpu_timer ( cpu ) ;
2006-09-01 08:27:35 +04:00
return 0 ;
}
module_init ( setup_vmstat )
# endif