2005-04-17 02:20:36 +04:00
/*
* drivers / cpufreq / cpufreq_stats . c
*
* Copyright ( C ) 2003 - 2004 Venkatesh Pallipadi < venkatesh . pallipadi @ intel . com > .
2009-01-18 09:49:04 +03:00
* ( C ) 2004 Zou Nan hai < nanhai . zou @ intel . com > .
2005-04-17 02:20:36 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/cpu.h>
# include <linux/cpufreq.h>
2011-05-27 21:23:32 +04:00
# include <linux/module.h>
2013-08-06 21:23:03 +04:00
# include <linux/slab.h>
2005-05-26 01:46:50 +04:00
# include <asm/cputime.h>
2005-04-17 02:20:36 +04:00
static spinlock_t cpufreq_stats_lock ;
struct cpufreq_stats {
unsigned int cpu ;
unsigned int total_trans ;
2013-06-19 12:49:33 +04:00
unsigned long long last_time ;
2005-04-17 02:20:36 +04:00
unsigned int max_state ;
unsigned int state_num ;
unsigned int last_index ;
2012-10-26 02:51:21 +04:00
u64 * time_in_state ;
2005-04-17 02:20:36 +04:00
unsigned int * freq_table ;
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
unsigned int * trans_table ;
# endif
} ;
2008-03-26 01:06:53 +03:00
static DEFINE_PER_CPU ( struct cpufreq_stats * , cpufreq_stats_table ) ;
2005-04-17 02:20:36 +04:00
struct cpufreq_stats_attribute {
struct attribute attr ;
ssize_t ( * show ) ( struct cpufreq_stats * , char * ) ;
} ;
2009-01-18 09:49:04 +03:00
static int cpufreq_stats_update ( unsigned int cpu )
2005-04-17 02:20:36 +04:00
{
struct cpufreq_stats * stat ;
2005-05-26 01:46:50 +04:00
unsigned long long cur_time ;
cur_time = get_jiffies_64 ( ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & cpufreq_stats_lock ) ;
2008-03-26 01:06:53 +03:00
stat = per_cpu ( cpufreq_stats_table , cpu ) ;
2005-04-17 02:20:36 +04:00
if ( stat - > time_in_state )
2011-12-15 17:56:09 +04:00
stat - > time_in_state [ stat - > last_index ] + =
cur_time - stat - > last_time ;
2005-05-26 01:46:50 +04:00
stat - > last_time = cur_time ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & cpufreq_stats_lock ) ;
return 0 ;
}
2009-01-18 09:49:04 +03:00
static ssize_t show_total_trans ( struct cpufreq_policy * policy , char * buf )
2005-04-17 02:20:36 +04:00
{
2008-03-26 01:06:53 +03:00
struct cpufreq_stats * stat = per_cpu ( cpufreq_stats_table , policy - > cpu ) ;
2006-05-31 01:57:14 +04:00
if ( ! stat )
2005-04-17 02:20:36 +04:00
return 0 ;
return sprintf ( buf , " %d \n " ,
2008-03-26 01:06:53 +03:00
per_cpu ( cpufreq_stats_table , stat - > cpu ) - > total_trans ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-18 09:49:04 +03:00
static ssize_t show_time_in_state ( struct cpufreq_policy * policy , char * buf )
2005-04-17 02:20:36 +04:00
{
ssize_t len = 0 ;
int i ;
2008-03-26 01:06:53 +03:00
struct cpufreq_stats * stat = per_cpu ( cpufreq_stats_table , policy - > cpu ) ;
2006-05-31 01:57:14 +04:00
if ( ! stat )
2005-04-17 02:20:36 +04:00
return 0 ;
cpufreq_stats_update ( stat - > cpu ) ;
for ( i = 0 ; i < stat - > state_num ; i + + ) {
2006-02-28 08:43:23 +03:00
len + = sprintf ( buf + len , " %u %llu \n " , stat - > freq_table [ i ] ,
2009-01-18 09:49:04 +03:00
( unsigned long long )
2013-09-07 20:35:08 +04:00
jiffies_64_to_clock_t ( stat - > time_in_state [ i ] ) ) ;
2005-04-17 02:20:36 +04:00
}
return len ;
}
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2009-01-18 09:49:04 +03:00
static ssize_t show_trans_table ( struct cpufreq_policy * policy , char * buf )
2005-04-17 02:20:36 +04:00
{
ssize_t len = 0 ;
int i , j ;
2008-03-26 01:06:53 +03:00
struct cpufreq_stats * stat = per_cpu ( cpufreq_stats_table , policy - > cpu ) ;
2006-05-31 01:57:14 +04:00
if ( ! stat )
2005-04-17 02:20:36 +04:00
return 0 ;
cpufreq_stats_update ( stat - > cpu ) ;
2005-05-26 01:46:50 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " From : To \n " ) ;
len + = snprintf ( buf + len , PAGE_SIZE - len , " : " ) ;
for ( i = 0 ; i < stat - > state_num ; i + + ) {
if ( len > = PAGE_SIZE )
break ;
len + = snprintf ( buf + len , PAGE_SIZE - len , " %9u " ,
stat - > freq_table [ i ] ) ;
}
if ( len > = PAGE_SIZE )
2008-02-16 13:41:25 +03:00
return PAGE_SIZE ;
2005-05-26 01:46:50 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " \n " ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < stat - > state_num ; i + + ) {
if ( len > = PAGE_SIZE )
break ;
2005-05-26 01:46:50 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " %9u: " ,
2005-04-17 02:20:36 +04:00
stat - > freq_table [ i ] ) ;
2013-06-19 12:49:33 +04:00
for ( j = 0 ; j < stat - > state_num ; j + + ) {
2005-04-17 02:20:36 +04:00
if ( len > = PAGE_SIZE )
break ;
2005-05-26 01:46:50 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " %9u " ,
2005-04-17 02:20:36 +04:00
stat - > trans_table [ i * stat - > max_state + j ] ) ;
}
2008-02-16 13:41:25 +03:00
if ( len > = PAGE_SIZE )
break ;
2005-04-17 02:20:36 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " \n " ) ;
}
2008-02-16 13:41:25 +03:00
if ( len > = PAGE_SIZE )
return PAGE_SIZE ;
2005-04-17 02:20:36 +04:00
return len ;
}
2013-02-04 15:38:52 +04:00
cpufreq_freq_attr_ro ( trans_table ) ;
2005-04-17 02:20:36 +04:00
# endif
2013-02-04 15:38:52 +04:00
cpufreq_freq_attr_ro ( total_trans ) ;
cpufreq_freq_attr_ro ( time_in_state ) ;
2005-04-17 02:20:36 +04:00
static struct attribute * default_attrs [ ] = {
2013-02-04 15:38:52 +04:00
& total_trans . attr ,
& time_in_state . attr ,
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2013-02-04 15:38:52 +04:00
& trans_table . attr ,
2005-04-17 02:20:36 +04:00
# endif
NULL
} ;
static struct attribute_group stats_attr_group = {
. attrs = default_attrs ,
. name = " stats "
} ;
2009-01-18 09:49:04 +03:00
static int freq_table_get_index ( struct cpufreq_stats * stat , unsigned int freq )
2005-04-17 02:20:36 +04:00
{
int index ;
for ( index = 0 ; index < stat - > max_state ; index + + )
if ( stat - > freq_table [ index ] = = freq )
return index ;
return - 1 ;
}
2014-01-07 05:40:12 +04:00
static void __cpufreq_stats_free_table ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
2014-01-07 05:40:12 +04:00
struct cpufreq_stats * stat = per_cpu ( cpufreq_stats_table , policy - > cpu ) ;
2013-01-14 17:23:03 +04:00
2014-01-07 05:40:12 +04:00
if ( ! stat )
return ;
pr_debug ( " %s: Free stat table \n " , __func__ ) ;
sysfs_remove_group ( & policy - > kobj , & stats_attr_group ) ;
kfree ( stat - > time_in_state ) ;
kfree ( stat ) ;
per_cpu ( cpufreq_stats_table , policy - > cpu ) = NULL ;
2011-05-02 22:29:17 +04:00
}
2014-01-07 05:40:12 +04:00
static void cpufreq_stats_free_table ( unsigned int cpu )
2011-05-02 22:29:17 +04:00
{
2014-01-07 05:40:12 +04:00
struct cpufreq_policy * policy ;
2013-02-06 21:02:12 +04:00
2014-01-07 05:40:12 +04:00
policy = cpufreq_cpu_get ( cpu ) ;
2013-03-22 14:13:52 +04:00
if ( ! policy )
2013-02-06 21:02:12 +04:00
return ;
2014-01-07 05:40:12 +04:00
if ( cpufreq_frequency_get_table ( policy - > cpu ) )
__cpufreq_stats_free_table ( policy ) ;
2013-03-22 14:13:52 +04:00
cpufreq_cpu_put ( policy ) ;
2005-04-17 02:20:36 +04:00
}
2014-01-07 05:40:13 +04:00
static int __cpufreq_stats_create_table ( struct cpufreq_policy * policy ,
2005-04-17 02:20:36 +04:00
struct cpufreq_frequency_table * table )
{
unsigned int i , j , count = 0 , ret = 0 ;
struct cpufreq_stats * stat ;
unsigned int alloc_size ;
unsigned int cpu = policy - > cpu ;
2008-03-26 01:06:53 +03:00
if ( per_cpu ( cpufreq_stats_table , cpu ) )
2005-04-17 02:20:36 +04:00
return - EBUSY ;
2013-08-06 21:23:06 +04:00
stat = kzalloc ( sizeof ( * stat ) , GFP_KERNEL ) ;
2009-01-18 09:49:04 +03:00
if ( ( stat ) = = NULL )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2014-02-28 05:58:34 +04:00
ret = sysfs_create_group ( & policy - > kobj , & stats_attr_group ) ;
2009-01-18 09:49:04 +03:00
if ( ret )
2005-04-17 02:20:36 +04:00
goto error_out ;
stat - > cpu = cpu ;
2008-03-26 01:06:53 +03:00
per_cpu ( cpufreq_stats_table , cpu ) = stat ;
2005-04-17 02:20:36 +04:00
2009-01-18 09:49:04 +03:00
for ( i = 0 ; table [ i ] . frequency ! = CPUFREQ_TABLE_END ; i + + ) {
2005-04-17 02:20:36 +04:00
unsigned int freq = table [ i ] . frequency ;
if ( freq = = CPUFREQ_ENTRY_INVALID )
continue ;
count + + ;
}
2012-10-26 02:51:21 +04:00
alloc_size = count * sizeof ( int ) + count * sizeof ( u64 ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
alloc_size + = count * count * sizeof ( int ) ;
# endif
stat - > max_state = count ;
2005-10-21 02:17:43 +04:00
stat - > time_in_state = kzalloc ( alloc_size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! stat - > time_in_state ) {
ret = - ENOMEM ;
2014-02-28 05:58:35 +04:00
goto error_alloc ;
2005-04-17 02:20:36 +04:00
}
stat - > freq_table = ( unsigned int * ) ( stat - > time_in_state + count ) ;
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
stat - > trans_table = stat - > freq_table + count ;
# endif
j = 0 ;
for ( i = 0 ; table [ i ] . frequency ! = CPUFREQ_TABLE_END ; i + + ) {
unsigned int freq = table [ i ] . frequency ;
if ( freq = = CPUFREQ_ENTRY_INVALID )
continue ;
if ( freq_table_get_index ( stat , freq ) = = - 1 )
stat - > freq_table [ j + + ] = freq ;
}
stat - > state_num = j ;
spin_lock ( & cpufreq_stats_lock ) ;
2005-05-26 01:46:50 +04:00
stat - > last_time = get_jiffies_64 ( ) ;
2005-04-17 02:20:36 +04:00
stat - > last_index = freq_table_get_index ( stat , policy - > cur ) ;
spin_unlock ( & cpufreq_stats_lock ) ;
return 0 ;
2014-02-28 05:58:35 +04:00
error_alloc :
sysfs_remove_group ( & policy - > kobj , & stats_attr_group ) ;
2005-04-17 02:20:36 +04:00
error_out :
kfree ( stat ) ;
2008-03-26 01:06:53 +03:00
per_cpu ( cpufreq_stats_table , cpu ) = NULL ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2014-01-07 05:40:13 +04:00
static void cpufreq_stats_create_table ( unsigned int cpu )
{
struct cpufreq_policy * policy ;
struct cpufreq_frequency_table * table ;
/*
* " likely(!policy) " because normally cpufreq_stats will be registered
* before cpufreq driver
*/
policy = cpufreq_cpu_get ( cpu ) ;
if ( likely ( ! policy ) )
return ;
table = cpufreq_frequency_get_table ( policy - > cpu ) ;
if ( likely ( table ) )
__cpufreq_stats_create_table ( policy , table ) ;
cpufreq_cpu_put ( policy ) ;
}
2013-01-14 17:23:03 +04:00
static void cpufreq_stats_update_policy_cpu ( struct cpufreq_policy * policy )
{
struct cpufreq_stats * stat = per_cpu ( cpufreq_stats_table ,
policy - > last_cpu ) ;
pr_debug ( " Updating stats_table for new_cpu %u from last_cpu %u \n " ,
policy - > cpu , policy - > last_cpu ) ;
per_cpu ( cpufreq_stats_table , policy - > cpu ) = per_cpu ( cpufreq_stats_table ,
policy - > last_cpu ) ;
per_cpu ( cpufreq_stats_table , policy - > last_cpu ) = NULL ;
stat - > cpu = policy - > cpu ;
}
2009-01-18 09:49:04 +03:00
static int cpufreq_stat_notifier_policy ( struct notifier_block * nb ,
unsigned long val , void * data )
2005-04-17 02:20:36 +04:00
{
2014-01-07 05:40:10 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
struct cpufreq_policy * policy = data ;
struct cpufreq_frequency_table * table ;
unsigned int cpu = policy - > cpu ;
2013-01-14 17:23:03 +04:00
if ( val = = CPUFREQ_UPDATE_POLICY_CPU ) {
cpufreq_stats_update_policy_cpu ( policy ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
table = cpufreq_frequency_get_table ( cpu ) ;
if ( ! table )
return 0 ;
2014-01-07 05:40:10 +04:00
if ( val = = CPUFREQ_CREATE_POLICY )
2014-01-07 05:40:13 +04:00
ret = __cpufreq_stats_create_table ( policy , table ) ;
2014-01-07 05:40:12 +04:00
else if ( val = = CPUFREQ_REMOVE_POLICY )
__cpufreq_stats_free_table ( policy ) ;
2014-01-07 05:40:10 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2009-01-18 09:49:04 +03:00
static int cpufreq_stat_notifier_trans ( struct notifier_block * nb ,
unsigned long val , void * data )
2005-04-17 02:20:36 +04:00
{
struct cpufreq_freqs * freq = data ;
struct cpufreq_stats * stat ;
int old_index , new_index ;
if ( val ! = CPUFREQ_POSTCHANGE )
return 0 ;
2008-03-26 01:06:53 +03:00
stat = per_cpu ( cpufreq_stats_table , freq - > cpu ) ;
2005-04-17 02:20:36 +04:00
if ( ! stat )
return 0 ;
2006-12-19 23:58:55 +03:00
2008-04-28 00:46:56 +04:00
old_index = stat - > last_index ;
2005-04-17 02:20:36 +04:00
new_index = freq_table_get_index ( stat , freq - > new ) ;
[CPUFREQ] Don't set stat->last_index to -1 if the pol->cur has incorrect value.
If the driver submitted an non-existing pol>cur value (say it
used the default initialized value of zero), when the cpufreq
stats tries to setup its initial values it incorrectly sets
stat->last_index to -1 (or 0xfffff...). And cpufreq_stats_update
tries to update at that index location and fails.
This can be caused by:
stat->last_index = freq_table_get_index(stat, policy->cur);
not finding the appropiate frequency in the table (b/c the policy->cur
is wrong) and we end up crashing. The fix however is
concentrated in the 'cpufreq_stats_update' as the last_index
(and old_index) are updated there. Which means it can reset
the last_index to -1 again and on the next iteration cause a crash.
Without this patch, the following crash is observed:
powernow-k8: Found 1 AMD Athlon(tm) 64 Processor 3700+ (1 cpu cores) (version 2.20.00)
powernow-k8: fid 0x2 (1000 MHz), vid 0x12
powernow-k8: fid 0xa (1800 MHz), vid 0xa
powernow-k8: fid 0xc (2000 MHz), vid 0x8
powernow-k8: fid 0xe (2200 MHz), vid 0x8
Marking TSC unstable due to cpufreq changes
powernow-k8: fid trans failed, fid 0x2, curr 0x0
BUG: unable to handle kernel paging request at ffff880807e07b78
IP: [<ffffffff81479163>] cpufreq_stats_update+0x46/0x5b
.. snip..
Pid: 1, comm: swapper Not tainted 3.0.0-rc2 #45 MICRO-STAR INTERNATIONAL CO., LTD MS-7094/MS-7094
..snip..
Call Trace:
[<ffffffff81479248>] cpufreq_stat_notifier_trans+0x48/0x7c
[<ffffffff81095d68>] notifier_call_chain+0x32/0x5e
[<ffffffff81095e6b>] __srcu_notifier_call_chain+0x47/0x63
[<ffffffff81095e96>] srcu_notifier_call_chain+0xf/0x11
[<ffffffff81477e7a>] cpufreq_notify_transition+0x111/0x134
[<ffffffff8147b0d4>] powernowk8_target+0x53b/0x617
[<ffffffff8147723a>] __cpufreq_driver_target+0x2e/0x30
[<ffffffff8147a127>] cpufreq_governor_dbs+0x339/0x356
[<ffffffff81477394>] __cpufreq_governor+0xa8/0xe9
[<ffffffff81477525>] __cpufreq_set_policy+0x132/0x13e
[<ffffffff8147848d>] cpufreq_add_dev_interface+0x272/0x28c
Reported-by: Tobias Diedrich <ranma+xen@tdiedrich.de>
Tested-by: Tobias Diedrich <ranma+xen@tdiedrich.de>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Dave Jones <davej@redhat.com>
2011-06-16 23:36:38 +04:00
/* We can't do stat->time_in_state[-1]= .. */
if ( old_index = = - 1 | | new_index = = - 1 )
2005-04-17 02:20:36 +04:00
return 0 ;
[CPUFREQ] Don't set stat->last_index to -1 if the pol->cur has incorrect value.
If the driver submitted an non-existing pol>cur value (say it
used the default initialized value of zero), when the cpufreq
stats tries to setup its initial values it incorrectly sets
stat->last_index to -1 (or 0xfffff...). And cpufreq_stats_update
tries to update at that index location and fails.
This can be caused by:
stat->last_index = freq_table_get_index(stat, policy->cur);
not finding the appropiate frequency in the table (b/c the policy->cur
is wrong) and we end up crashing. The fix however is
concentrated in the 'cpufreq_stats_update' as the last_index
(and old_index) are updated there. Which means it can reset
the last_index to -1 again and on the next iteration cause a crash.
Without this patch, the following crash is observed:
powernow-k8: Found 1 AMD Athlon(tm) 64 Processor 3700+ (1 cpu cores) (version 2.20.00)
powernow-k8: fid 0x2 (1000 MHz), vid 0x12
powernow-k8: fid 0xa (1800 MHz), vid 0xa
powernow-k8: fid 0xc (2000 MHz), vid 0x8
powernow-k8: fid 0xe (2200 MHz), vid 0x8
Marking TSC unstable due to cpufreq changes
powernow-k8: fid trans failed, fid 0x2, curr 0x0
BUG: unable to handle kernel paging request at ffff880807e07b78
IP: [<ffffffff81479163>] cpufreq_stats_update+0x46/0x5b
.. snip..
Pid: 1, comm: swapper Not tainted 3.0.0-rc2 #45 MICRO-STAR INTERNATIONAL CO., LTD MS-7094/MS-7094
..snip..
Call Trace:
[<ffffffff81479248>] cpufreq_stat_notifier_trans+0x48/0x7c
[<ffffffff81095d68>] notifier_call_chain+0x32/0x5e
[<ffffffff81095e6b>] __srcu_notifier_call_chain+0x47/0x63
[<ffffffff81095e96>] srcu_notifier_call_chain+0xf/0x11
[<ffffffff81477e7a>] cpufreq_notify_transition+0x111/0x134
[<ffffffff8147b0d4>] powernowk8_target+0x53b/0x617
[<ffffffff8147723a>] __cpufreq_driver_target+0x2e/0x30
[<ffffffff8147a127>] cpufreq_governor_dbs+0x339/0x356
[<ffffffff81477394>] __cpufreq_governor+0xa8/0xe9
[<ffffffff81477525>] __cpufreq_set_policy+0x132/0x13e
[<ffffffff8147848d>] cpufreq_add_dev_interface+0x272/0x28c
Reported-by: Tobias Diedrich <ranma+xen@tdiedrich.de>
Tested-by: Tobias Diedrich <ranma+xen@tdiedrich.de>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Dave Jones <davej@redhat.com>
2011-06-16 23:36:38 +04:00
cpufreq_stats_update ( freq - > cpu ) ;
if ( old_index = = new_index )
2006-12-19 23:58:55 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
spin_lock ( & cpufreq_stats_lock ) ;
stat - > last_index = new_index ;
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
stat - > trans_table [ old_index * stat - > max_state + new_index ] + + ;
# endif
stat - > total_trans + + ;
spin_unlock ( & cpufreq_stats_lock ) ;
return 0 ;
}
static struct notifier_block notifier_policy_block = {
. notifier_call = cpufreq_stat_notifier_policy
} ;
static struct notifier_block notifier_trans_block = {
. notifier_call = cpufreq_stat_notifier_trans
} ;
2009-01-18 09:49:04 +03:00
static int __init cpufreq_stats_init ( void )
2005-04-17 02:20:36 +04:00
{
int ret ;
unsigned int cpu ;
2005-10-31 01:59:54 +03:00
2005-04-17 02:20:36 +04:00
spin_lock_init ( & cpufreq_stats_lock ) ;
2009-01-18 09:49:04 +03:00
ret = cpufreq_register_notifier ( & notifier_policy_block ,
CPUFREQ_POLICY_NOTIFIER ) ;
if ( ret )
2005-04-17 02:20:36 +04:00
return ret ;
2014-01-07 05:40:13 +04:00
for_each_online_cpu ( cpu )
cpufreq_stats_create_table ( cpu ) ;
2009-01-18 09:49:04 +03:00
ret = cpufreq_register_notifier ( & notifier_trans_block ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
if ( ret ) {
2005-04-17 02:20:36 +04:00
cpufreq_unregister_notifier ( & notifier_policy_block ,
CPUFREQ_POLICY_NOTIFIER ) ;
2012-12-15 03:22:02 +04:00
for_each_online_cpu ( cpu )
cpufreq_stats_free_table ( cpu ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
return 0 ;
}
2009-01-18 09:49:04 +03:00
static void __exit cpufreq_stats_exit ( void )
2005-04-17 02:20:36 +04:00
{
unsigned int cpu ;
2005-10-31 01:59:54 +03:00
2005-04-17 02:20:36 +04:00
cpufreq_unregister_notifier ( & notifier_policy_block ,
CPUFREQ_POLICY_NOTIFIER ) ;
cpufreq_unregister_notifier ( & notifier_trans_block ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
2014-01-07 05:40:12 +04:00
for_each_online_cpu ( cpu )
2007-10-03 00:28:15 +04:00
cpufreq_stats_free_table ( cpu ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-18 09:49:04 +03:00
MODULE_AUTHOR ( " Zou Nan hai <nanhai.zou@intel.com> " ) ;
MODULE_DESCRIPTION ( " 'cpufreq_stats' - A driver to export cpufreq stats "
2006-10-26 14:50:58 +04:00
" through sysfs filesystem " ) ;
2009-01-18 09:49:04 +03:00
MODULE_LICENSE ( " GPL " ) ;
2005-04-17 02:20:36 +04:00
module_init ( cpufreq_stats_init ) ;
module_exit ( cpufreq_stats_exit ) ;