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>
2014-03-05 19:33:42 +04:00
# include <linux/cputime.h>
2005-04-17 02:20:36 +04:00
static spinlock_t cpufreq_stats_lock ;
struct cpufreq_stats {
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
} ;
2015-01-06 18:39:07 +03:00
static int cpufreq_stats_update ( struct cpufreq_stats * stats )
2005-04-17 02:20:36 +04:00
{
2015-01-06 18:39:03 +03:00
unsigned long long cur_time = get_jiffies_64 ( ) ;
2005-05-26 01:46:50 +04:00
2005-04-17 02:20:36 +04:00
spin_lock ( & cpufreq_stats_lock ) ;
2015-01-06 18:39:12 +03:00
stats - > time_in_state [ stats - > last_index ] + = cur_time - stats - > last_time ;
2015-01-06 18:39:07 +03:00
stats - > 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
{
2015-01-13 09:04:00 +03:00
return sprintf ( buf , " %d \n " , policy - > stats - > 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
{
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats = policy - > stats ;
2005-04-17 02:20:36 +04:00
ssize_t len = 0 ;
int i ;
2015-01-13 09:04:00 +03:00
2015-01-06 18:39:07 +03:00
cpufreq_stats_update ( stats ) ;
for ( i = 0 ; i < stats - > state_num ; i + + ) {
len + = sprintf ( buf + len , " %u %llu \n " , stats - > freq_table [ i ] ,
2009-01-18 09:49:04 +03:00
( unsigned long long )
2015-01-06 18:39:07 +03:00
jiffies_64_to_clock_t ( stats - > 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
{
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats = policy - > stats ;
2005-04-17 02:20:36 +04:00
ssize_t len = 0 ;
int i , j ;
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 , " : " ) ;
2015-01-06 18:39:07 +03:00
for ( i = 0 ; i < stats - > state_num ; i + + ) {
2005-05-26 01:46:50 +04:00
if ( len > = PAGE_SIZE )
break ;
len + = snprintf ( buf + len , PAGE_SIZE - len , " %9u " ,
2015-01-06 18:39:07 +03:00
stats - > freq_table [ i ] ) ;
2005-05-26 01:46:50 +04:00
}
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 " ) ;
2015-01-06 18:39:07 +03:00
for ( i = 0 ; i < stats - > state_num ; i + + ) {
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: " ,
2015-01-06 18:39:07 +03:00
stats - > freq_table [ i ] ) ;
2005-04-17 02:20:36 +04:00
2015-01-06 18:39:07 +03:00
for ( j = 0 ; j < stats - > 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 " ,
2015-01-06 18:39:07 +03:00
stats - > trans_table [ i * stats - > max_state + j ] ) ;
2005-04-17 02:20:36 +04:00
}
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 "
} ;
2015-01-06 18:39:07 +03:00
static int freq_table_get_index ( struct cpufreq_stats * stats , unsigned int freq )
2005-04-17 02:20:36 +04:00
{
int index ;
2015-01-06 18:39:07 +03:00
for ( index = 0 ; index < stats - > max_state ; index + + )
if ( stats - > freq_table [ index ] = = freq )
2005-04-17 02:20:36 +04:00
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
{
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats = policy - > stats ;
2013-01-14 17:23:03 +04:00
2015-01-13 09:04:00 +03:00
/* Already freed */
2015-01-06 18:39:07 +03:00
if ( ! stats )
2014-01-07 05:40:12 +04:00
return ;
2015-01-06 18:39:07 +03:00
pr_debug ( " %s: Free stats table \n " , __func__ ) ;
2014-01-07 05:40:12 +04:00
sysfs_remove_group ( & policy - > kobj , & stats_attr_group ) ;
2015-01-06 18:39:07 +03:00
kfree ( stats - > time_in_state ) ;
kfree ( stats ) ;
2015-01-13 09:04:00 +03:00
policy - > stats = 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 ;
2015-01-06 18:39:04 +03:00
__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-02-28 05:58:36 +04:00
static int __cpufreq_stats_create_table ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
2015-01-06 18:39:11 +03:00
unsigned int i = 0 , count = 0 , ret = - ENOMEM ;
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats ;
2005-04-17 02:20:36 +04:00
unsigned int alloc_size ;
unsigned int cpu = policy - > cpu ;
2014-04-26 00:15:38 +04:00
struct cpufreq_frequency_table * pos , * table ;
2014-02-28 05:58:36 +04:00
2015-01-06 18:39:11 +03:00
/* We need cpufreq table for creating stats table */
2014-02-28 05:58:36 +04:00
table = cpufreq_frequency_get_table ( cpu ) ;
if ( unlikely ( ! table ) )
return 0 ;
2015-01-06 18:39:01 +03:00
/* stats already initialized */
2015-01-13 09:04:00 +03:00
if ( policy - > stats )
2015-01-06 18:39:01 +03:00
return - EEXIST ;
2015-01-06 18:39:07 +03:00
stats = kzalloc ( sizeof ( * stats ) , GFP_KERNEL ) ;
2015-01-06 18:39:11 +03:00
if ( ! stats )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2015-01-06 18:39:11 +03:00
/* Find total allocation size */
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table )
2005-04-17 02:20:36 +04:00
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
2015-01-06 18:39:11 +03:00
/* Allocate memory for time_in_state/freq_table/trans_table in one go */
2015-01-06 18:39:07 +03:00
stats - > time_in_state = kzalloc ( alloc_size , GFP_KERNEL ) ;
2015-01-06 18:39:11 +03:00
if ( ! stats - > time_in_state )
goto free_stat ;
2015-01-06 18:39:07 +03:00
stats - > freq_table = ( unsigned int * ) ( stats - > time_in_state + count ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2015-01-06 18:39:07 +03:00
stats - > trans_table = stats - > freq_table + count ;
2005-04-17 02:20:36 +04:00
# endif
2015-01-06 18:39:11 +03:00
stats - > max_state = count ;
/* Find valid-unique entries */
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table )
2015-01-06 18:39:07 +03:00
if ( freq_table_get_index ( stats , pos - > frequency ) = = - 1 )
stats - > freq_table [ i + + ] = pos - > frequency ;
2015-01-06 18:39:11 +03:00
2015-01-06 18:39:15 +03:00
stats - > state_num = i ;
2015-01-06 18:39:07 +03:00
stats - > last_time = get_jiffies_64 ( ) ;
stats - > last_index = freq_table_get_index ( stats , policy - > cur ) ;
2015-01-06 18:39:11 +03:00
policy - > stats = stats ;
ret = sysfs_create_group ( & policy - > kobj , & stats_attr_group ) ;
if ( ! ret )
return 0 ;
/* We failed, release resources */
2015-01-13 09:04:00 +03:00
policy - > stats = NULL ;
2015-01-06 18:39:11 +03:00
kfree ( stats - > time_in_state ) ;
free_stat :
kfree ( stats ) ;
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 ;
/*
* " likely(!policy) " because normally cpufreq_stats will be registered
* before cpufreq driver
*/
policy = cpufreq_cpu_get ( cpu ) ;
if ( likely ( ! policy ) )
return ;
2014-02-28 05:58:36 +04:00
__cpufreq_stats_create_table ( policy ) ;
2014-01-07 05:40:13 +04:00
cpufreq_cpu_put ( policy ) ;
}
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 ;
2013-01-14 17:23:03 +04:00
2014-01-07 05:40:10 +04:00
if ( val = = CPUFREQ_CREATE_POLICY )
2014-02-28 05:58:36 +04:00
ret = __cpufreq_stats_create_table ( policy ) ;
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 ;
2015-01-13 09:04:00 +03:00
struct cpufreq_policy * policy = cpufreq_cpu_get ( freq - > cpu ) ;
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats ;
2005-04-17 02:20:36 +04:00
int old_index , new_index ;
2015-01-13 09:04:00 +03:00
if ( ! policy ) {
pr_err ( " %s: No policy found \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2015-01-13 09:04:00 +03:00
}
2005-04-17 02:20:36 +04:00
2015-01-13 09:04:00 +03:00
if ( val ! = CPUFREQ_POSTCHANGE )
goto put_policy ;
if ( ! policy - > stats ) {
pr_debug ( " %s: No stats found \n " , __func__ ) ;
goto put_policy ;
}
2015-01-06 18:39:07 +03:00
stats = policy - > stats ;
2006-12-19 23:58:55 +03:00
2015-01-06 18:39:07 +03:00
old_index = stats - > last_index ;
new_index = freq_table_get_index ( stats , freq - > new ) ;
2005-04-17 02:20:36 +04:00
2015-01-06 18:39:07 +03:00
/* We can't do stats->time_in_state[-1]= .. */
[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
if ( old_index = = - 1 | | new_index = = - 1 )
2015-01-13 09:04:00 +03:00
goto put_policy ;
2005-04-17 02:20:36 +04:00
[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
if ( old_index = = new_index )
2015-01-13 09:04:00 +03:00
goto put_policy ;
2006-12-19 23:58:55 +03:00
2015-01-06 18:39:14 +03:00
cpufreq_stats_update ( stats ) ;
2015-01-06 18:39:07 +03:00
stats - > last_index = new_index ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2015-01-06 18:39:07 +03:00
stats - > trans_table [ old_index * stats - > max_state + new_index ] + + ;
2005-04-17 02:20:36 +04:00
# endif
2015-01-06 18:39:07 +03:00
stats - > total_trans + + ;
2015-01-13 09:04:00 +03:00
put_policy :
cpufreq_cpu_put ( policy ) ;
2005-04-17 02:20:36 +04:00
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> " ) ;
2015-01-06 18:39:00 +03:00
MODULE_DESCRIPTION ( " Export cpufreq stats via sysfs " ) ;
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 ) ;