20
20
#include <linux/kobject.h>
21
21
#include <linux/spinlock.h>
22
22
#include <linux/notifier.h>
23
+ #include <linux/sort.h>
24
+ #include <linux/err.h>
23
25
#include <asm/cputime.h>
24
26
25
27
static spinlock_t cpufreq_stats_lock ;
@@ -38,6 +40,20 @@ struct cpufreq_stats {
38
40
#endif
39
41
};
40
42
43
+ struct all_cpufreq_stats {
44
+ unsigned int state_num ;
45
+ cputime64_t * time_in_state ;
46
+ unsigned int * freq_table ;
47
+ };
48
+
49
+ struct all_freq_table {
50
+ unsigned int * freq_table ;
51
+ unsigned int table_size ;
52
+ };
53
+
54
+ static struct all_freq_table * all_freq_table ;
55
+
56
+ static DEFINE_PER_CPU (struct all_cpufreq_stats * , all_cpufreq_stats ) ;
41
57
static DEFINE_PER_CPU (struct cpufreq_stats * , cpufreq_stats_table ) ;
42
58
43
59
struct cpufreq_stats_attribute {
@@ -48,14 +64,24 @@ struct cpufreq_stats_attribute {
48
64
static int cpufreq_stats_update (unsigned int cpu )
49
65
{
50
66
struct cpufreq_stats * stat ;
67
+ struct all_cpufreq_stats * all_stat ;
51
68
unsigned long long cur_time ;
52
69
53
70
cur_time = get_jiffies_64 ();
54
71
spin_lock (& cpufreq_stats_lock );
55
72
stat = per_cpu (cpufreq_stats_table , cpu );
56
- if (stat -> time_in_state )
73
+ all_stat = per_cpu (all_cpufreq_stats , cpu );
74
+ if (!stat ) {
75
+ spin_unlock (& cpufreq_stats_lock );
76
+ return 0 ;
77
+ }
78
+ if (stat -> time_in_state ) {
57
79
stat -> time_in_state [stat -> last_index ] +=
58
80
cur_time - stat -> last_time ;
81
+ if (all_stat )
82
+ all_stat -> time_in_state [stat -> last_index ] +=
83
+ cur_time - stat -> last_time ;
84
+ }
59
85
stat -> last_time = cur_time ;
60
86
spin_unlock (& cpufreq_stats_lock );
61
87
return 0 ;
@@ -86,6 +112,62 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
86
112
return len ;
87
113
}
88
114
115
+ static int get_index_all_cpufreq_stat (struct all_cpufreq_stats * all_stat ,
116
+ unsigned int freq )
117
+ {
118
+ int i ;
119
+ if (!all_stat )
120
+ return -1 ;
121
+ for (i = 0 ; i < all_stat -> state_num ; i ++ ) {
122
+ if (all_stat -> freq_table [i ] == freq )
123
+ return i ;
124
+ }
125
+ return -1 ;
126
+ }
127
+
128
+ static ssize_t show_all_time_in_state (struct kobject * kobj ,
129
+ struct kobj_attribute * attr , char * buf )
130
+ {
131
+ ssize_t len = 0 ;
132
+ unsigned int i , cpu , freq , index ;
133
+ struct all_cpufreq_stats * all_stat ;
134
+ struct cpufreq_policy * policy ;
135
+
136
+ len += scnprintf (buf + len , PAGE_SIZE - len , "freq\t\t" );
137
+ for_each_possible_cpu (cpu ) {
138
+ len += scnprintf (buf + len , PAGE_SIZE - len , "cpu%d\t\t" , cpu );
139
+ if (cpu_online (cpu ))
140
+ cpufreq_stats_update (cpu );
141
+ }
142
+
143
+ if (!all_freq_table )
144
+ goto out ;
145
+ for (i = 0 ; i < all_freq_table -> table_size ; i ++ ) {
146
+ freq = all_freq_table -> freq_table [i ];
147
+ len += scnprintf (buf + len , PAGE_SIZE - len , "\n%u\t\t" , freq );
148
+ for_each_possible_cpu (cpu ) {
149
+ policy = cpufreq_cpu_get (cpu );
150
+ if (policy == NULL )
151
+ continue ;
152
+ all_stat = per_cpu (all_cpufreq_stats , policy -> cpu );
153
+ index = get_index_all_cpufreq_stat (all_stat , freq );
154
+ if (index != -1 ) {
155
+ len += scnprintf (buf + len , PAGE_SIZE - len ,
156
+ "%llu\t\t" , (unsigned long long )
157
+ cputime64_to_clock_t (all_stat -> time_in_state [index ]));
158
+ } else {
159
+ len += scnprintf (buf + len , PAGE_SIZE - len ,
160
+ "N/A\t\t" );
161
+ }
162
+ cpufreq_cpu_put (policy );
163
+ }
164
+ }
165
+
166
+ out :
167
+ len += scnprintf (buf + len , PAGE_SIZE - len , "\n" );
168
+ return len ;
169
+ }
170
+
89
171
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
90
172
static ssize_t show_trans_table (struct cpufreq_policy * policy , char * buf )
91
173
{
@@ -149,6 +231,9 @@ static struct attribute_group stats_attr_group = {
149
231
.name = "stats"
150
232
};
151
233
234
+ static struct kobj_attribute _attr_all_time_in_state = __ATTR (all_time_in_state ,
235
+ 0444 , show_all_time_in_state , NULL );
236
+
152
237
static int freq_table_get_index (struct cpufreq_stats * stat , unsigned int freq )
153
238
{
154
239
int index ;
@@ -195,6 +280,29 @@ static void cpufreq_stats_free_sysfs(unsigned int cpu)
195
280
cpufreq_cpu_put (policy );
196
281
}
197
282
283
+ static void cpufreq_allstats_free (void )
284
+ {
285
+ int i ;
286
+ struct all_cpufreq_stats * all_stat ;
287
+
288
+ sysfs_remove_file (cpufreq_global_kobject ,
289
+ & _attr_all_time_in_state .attr );
290
+
291
+ for (i = 0 ; i < total_cpus ; i ++ ) {
292
+ all_stat = per_cpu (all_cpufreq_stats , i );
293
+ if (!all_stat )
294
+ continue ;
295
+ kfree (all_stat -> time_in_state );
296
+ kfree (all_stat );
297
+ per_cpu (all_cpufreq_stats , i ) = NULL ;
298
+ }
299
+ if (all_freq_table ) {
300
+ kfree (all_freq_table -> freq_table );
301
+ kfree (all_freq_table );
302
+ all_freq_table = NULL ;
303
+ }
304
+ }
305
+
198
306
static int cpufreq_stats_create_table (struct cpufreq_policy * policy ,
199
307
struct cpufreq_frequency_table * table )
200
308
{
@@ -281,6 +389,106 @@ static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy)
281
389
stat -> cpu = policy -> cpu ;
282
390
}
283
391
392
+ static int compare_for_sort (const void * lhs_ptr , const void * rhs_ptr )
393
+ {
394
+ unsigned int lhs = * (const unsigned int * )(lhs_ptr );
395
+ unsigned int rhs = * (const unsigned int * )(rhs_ptr );
396
+ if (lhs < rhs )
397
+ return -1 ;
398
+ if (lhs > rhs )
399
+ return 1 ;
400
+ return 0 ;
401
+ }
402
+
403
+ static bool check_all_freq_table (unsigned int freq )
404
+ {
405
+ int i ;
406
+ for (i = 0 ; i < all_freq_table -> table_size ; i ++ ) {
407
+ if (freq == all_freq_table -> freq_table [i ])
408
+ return true;
409
+ }
410
+ return false;
411
+ }
412
+
413
+ static void create_all_freq_table (void )
414
+ {
415
+ all_freq_table = kzalloc (sizeof (struct all_freq_table ),
416
+ GFP_KERNEL );
417
+ if (!all_freq_table )
418
+ pr_warn ("could not allocate memory for all_freq_table\n" );
419
+ return ;
420
+ }
421
+
422
+ static void add_all_freq_table (unsigned int freq )
423
+ {
424
+ unsigned int size ;
425
+ size = sizeof (unsigned int ) * (all_freq_table -> table_size + 1 );
426
+ all_freq_table -> freq_table = krealloc (all_freq_table -> freq_table ,
427
+ size , GFP_KERNEL );
428
+ if (IS_ERR (all_freq_table -> freq_table )) {
429
+ pr_warn ("Could not reallocate memory for freq_table\n" );
430
+ all_freq_table -> freq_table = NULL ;
431
+ return ;
432
+ }
433
+ all_freq_table -> freq_table [all_freq_table -> table_size ++ ] = freq ;
434
+ }
435
+
436
+ static void cpufreq_allstats_create (unsigned int cpu )
437
+ {
438
+ int i , j = 0 ;
439
+ unsigned int alloc_size , count = 0 ;
440
+ struct cpufreq_frequency_table * table = cpufreq_frequency_get_table (cpu );
441
+ struct all_cpufreq_stats * all_stat ;
442
+ bool sort_needed = false;
443
+
444
+ if (!table )
445
+ return ;
446
+
447
+ for (i = 0 ; table [i ].frequency != CPUFREQ_TABLE_END ; i ++ ) {
448
+ unsigned int freq = table [i ].frequency ;
449
+ if (freq == CPUFREQ_ENTRY_INVALID )
450
+ continue ;
451
+ count ++ ;
452
+ }
453
+
454
+ all_stat = kzalloc (sizeof (struct all_cpufreq_stats ),
455
+ GFP_KERNEL );
456
+ if (!all_stat ) {
457
+ pr_warn ("Cannot allocate memory for cpufreq stats\n" );
458
+ return ;
459
+ }
460
+
461
+ /*Allocate memory for freq table per cpu as well as clockticks per freq*/
462
+ alloc_size = count * sizeof (int ) + count * sizeof (cputime64_t );
463
+ all_stat -> time_in_state = kzalloc (alloc_size , GFP_KERNEL );
464
+ if (!all_stat -> time_in_state ) {
465
+ pr_warn ("Cannot allocate memory for cpufreq time_in_state\n" );
466
+ kfree (all_stat );
467
+ all_stat = NULL ;
468
+ return ;
469
+ }
470
+ all_stat -> freq_table = (unsigned int * )
471
+ (all_stat -> time_in_state + count );
472
+
473
+ spin_lock (& cpufreq_stats_lock );
474
+ for (i = 0 ; table [i ].frequency != CPUFREQ_TABLE_END ; i ++ ) {
475
+ unsigned int freq = table [i ].frequency ;
476
+ if (freq == CPUFREQ_ENTRY_INVALID )
477
+ continue ;
478
+ all_stat -> freq_table [j ++ ] = freq ;
479
+ if (all_freq_table && !check_all_freq_table (freq )) {
480
+ add_all_freq_table (freq );
481
+ sort_needed = true;
482
+ }
483
+ }
484
+ if (sort_needed )
485
+ sort (all_freq_table -> freq_table , all_freq_table -> table_size ,
486
+ sizeof (unsigned int ), & compare_for_sort , NULL );
487
+ all_stat -> state_num = j ;
488
+ per_cpu (all_cpufreq_stats , cpu ) = all_stat ;
489
+ spin_unlock (& cpufreq_stats_lock );
490
+ }
491
+
284
492
static int cpufreq_stat_notifier_policy (struct notifier_block * nb ,
285
493
unsigned long val , void * data )
286
494
{
@@ -299,6 +507,10 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
299
507
table = cpufreq_frequency_get_table (cpu );
300
508
if (!table )
301
509
return 0 ;
510
+
511
+ if (!per_cpu (all_cpufreq_stats , cpu ))
512
+ cpufreq_allstats_create (cpu );
513
+
302
514
ret = cpufreq_stats_create_table (policy , table );
303
515
if (ret )
304
516
return ret ;
@@ -355,6 +567,9 @@ static int cpufreq_stats_create_table_cpu(unsigned int cpu)
355
567
if (!table )
356
568
goto out ;
357
569
570
+ if (!per_cpu (all_cpufreq_stats , cpu ))
571
+ cpufreq_allstats_create (cpu );
572
+
358
573
ret = cpufreq_stats_create_table (policy , table );
359
574
360
575
out :
@@ -430,6 +645,12 @@ static int __init cpufreq_stats_init(void)
430
645
return ret ;
431
646
}
432
647
648
+ create_all_freq_table ();
649
+ ret = sysfs_create_file (cpufreq_global_kobject ,
650
+ & _attr_all_time_in_state .attr );
651
+ if (ret )
652
+ pr_warn ("Error creating sysfs file for cpufreq stats\n" );
653
+
433
654
return 0 ;
434
655
}
435
656
static void __exit cpufreq_stats_exit (void )
@@ -445,6 +666,7 @@ static void __exit cpufreq_stats_exit(void)
445
666
cpufreq_stats_free_table (cpu );
446
667
cpufreq_stats_free_sysfs (cpu );
447
668
}
669
+ cpufreq_allstats_free ();
448
670
}
449
671
450
672
MODULE_AUTHOR (
"Zou Nan hai <[email protected] >" );
0 commit comments