@@ -3778,50 +3778,75 @@ u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
37783778}
37793779EXPORT_SYMBOL_GPL (perf_event_read_value );
37803780
3781- static int perf_read_group (struct perf_event * event ,
3782- u64 read_format , char __user * buf )
3781+ static void __perf_read_group_add (struct perf_event * leader ,
3782+ u64 read_format , u64 * values )
37833783{
3784- struct perf_event * leader = event -> group_leader , * sub ;
3785- struct perf_event_context * ctx = leader -> ctx ;
3786- int n = 0 , size = 0 , ret ;
3787- u64 count , enabled , running ;
3788- u64 values [5 ];
3784+ struct perf_event * sub ;
3785+ int n = 1 ; /* skip @nr */
37893786
3790- lockdep_assert_held (& ctx -> mutex );
3787+ perf_event_read (leader , true);
3788+
3789+ /*
3790+ * Since we co-schedule groups, {enabled,running} times of siblings
3791+ * will be identical to those of the leader, so we only publish one
3792+ * set.
3793+ */
3794+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED ) {
3795+ values [n ++ ] += leader -> total_time_enabled +
3796+ atomic64_read (& leader -> child_total_time_enabled );
3797+ }
37913798
3792- count = perf_event_read_value (leader , & enabled , & running );
3799+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING ) {
3800+ values [n ++ ] += leader -> total_time_running +
3801+ atomic64_read (& leader -> child_total_time_running );
3802+ }
37933803
3794- values [n ++ ] = 1 + leader -> nr_siblings ;
3795- if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED )
3796- values [n ++ ] = enabled ;
3797- if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING )
3798- values [n ++ ] = running ;
3799- values [n ++ ] = count ;
3804+ /*
3805+ * Write {count,id} tuples for every sibling.
3806+ */
3807+ values [n ++ ] += perf_event_count (leader );
38003808 if (read_format & PERF_FORMAT_ID )
38013809 values [n ++ ] = primary_event_id (leader );
38023810
3803- size = n * sizeof (u64 );
3811+ list_for_each_entry (sub , & leader -> sibling_list , group_entry ) {
3812+ values [n ++ ] += perf_event_count (sub );
3813+ if (read_format & PERF_FORMAT_ID )
3814+ values [n ++ ] = primary_event_id (sub );
3815+ }
3816+ }
38043817
3805- if (copy_to_user (buf , values , size ))
3806- return - EFAULT ;
3818+ static int perf_read_group (struct perf_event * event ,
3819+ u64 read_format , char __user * buf )
3820+ {
3821+ struct perf_event * leader = event -> group_leader , * child ;
3822+ struct perf_event_context * ctx = leader -> ctx ;
3823+ int ret = event -> read_size ;
3824+ u64 * values ;
38073825
3808- ret = size ;
3826+ lockdep_assert_held ( & ctx -> mutex ) ;
38093827
3810- list_for_each_entry (sub , & leader -> sibling_list , group_entry ) {
3811- n = 0 ;
3828+ values = kzalloc (event -> read_size , GFP_KERNEL );
3829+ if (!values )
3830+ return - ENOMEM ;
38123831
3813- values [n ++ ] = perf_event_read_value (sub , & enabled , & running );
3814- if (read_format & PERF_FORMAT_ID )
3815- values [n ++ ] = primary_event_id (sub );
3832+ values [0 ] = 1 + leader -> nr_siblings ;
3833+
3834+ /*
3835+ * By locking the child_mutex of the leader we effectively
3836+ * lock the child list of all siblings.. XXX explain how.
3837+ */
3838+ mutex_lock (& leader -> child_mutex );
38163839
3817- size = n * sizeof (u64 );
3840+ __perf_read_group_add (leader , read_format , values );
3841+ list_for_each_entry (child , & leader -> child_list , child_list )
3842+ __perf_read_group_add (child , read_format , values );
38183843
3819- if (copy_to_user (buf + ret , values , size )) {
3820- return - EFAULT ;
3821- }
3844+ mutex_unlock (& leader -> child_mutex );
38223845
3823- ret += size ;
3824- }
3846+ if (copy_to_user (buf , values , event -> read_size ))
3847+ ret = - EFAULT ;
3848+
3849+ kfree (values );
38253850
38263851 return ret ;
38273852}
0 commit comments