@@ -44,6 +44,16 @@ static DEFINE_PER_CPU(u64 *, trace_imc_mem);
44
44
static struct imc_pmu_ref * trace_imc_refc ;
45
45
static int trace_imc_mem_size ;
46
46
47
+ /*
48
+ * Global data structure used to avoid races between thread,
49
+ * core and trace-imc
50
+ */
51
+ static struct imc_pmu_ref imc_global_refc = {
52
+ .lock = __MUTEX_INITIALIZER (imc_global_refc .lock ),
53
+ .id = 0 ,
54
+ .refc = 0 ,
55
+ };
56
+
47
57
static struct imc_pmu * imc_event_to_pmu (struct perf_event * event )
48
58
{
49
59
return container_of (event -> pmu , struct imc_pmu , pmu );
@@ -698,6 +708,16 @@ static int ppc_core_imc_cpu_offline(unsigned int cpu)
698
708
return - EINVAL ;
699
709
700
710
ref -> refc = 0 ;
711
+ /*
712
+ * Reduce the global reference count, if this is the
713
+ * last cpu in this core and core-imc event running
714
+ * in this cpu.
715
+ */
716
+ mutex_lock (& imc_global_refc .lock );
717
+ if (imc_global_refc .id == IMC_DOMAIN_CORE )
718
+ imc_global_refc .refc -- ;
719
+
720
+ mutex_unlock (& imc_global_refc .lock );
701
721
}
702
722
return 0 ;
703
723
}
@@ -710,6 +730,23 @@ static int core_imc_pmu_cpumask_init(void)
710
730
ppc_core_imc_cpu_offline );
711
731
}
712
732
733
+ static void reset_global_refc (struct perf_event * event )
734
+ {
735
+ mutex_lock (& imc_global_refc .lock );
736
+ imc_global_refc .refc -- ;
737
+
738
+ /*
739
+ * If no other thread is running any
740
+ * event for this domain(thread/core/trace),
741
+ * set the global id to zero.
742
+ */
743
+ if (imc_global_refc .refc <= 0 ) {
744
+ imc_global_refc .refc = 0 ;
745
+ imc_global_refc .id = 0 ;
746
+ }
747
+ mutex_unlock (& imc_global_refc .lock );
748
+ }
749
+
713
750
static void core_imc_counters_release (struct perf_event * event )
714
751
{
715
752
int rc , core_id ;
@@ -759,6 +796,8 @@ static void core_imc_counters_release(struct perf_event *event)
759
796
ref -> refc = 0 ;
760
797
}
761
798
mutex_unlock (& ref -> lock );
799
+
800
+ reset_global_refc (event );
762
801
}
763
802
764
803
static int core_imc_event_init (struct perf_event * event )
@@ -819,6 +858,29 @@ static int core_imc_event_init(struct perf_event *event)
819
858
++ ref -> refc ;
820
859
mutex_unlock (& ref -> lock );
821
860
861
+ /*
862
+ * Since the system can run either in accumulation or trace-mode
863
+ * of IMC at a time, core-imc events are allowed only if no other
864
+ * trace/thread imc events are enabled/monitored.
865
+ *
866
+ * Take the global lock, and check the refc.id
867
+ * to know whether any other trace/thread imc
868
+ * events are running.
869
+ */
870
+ mutex_lock (& imc_global_refc .lock );
871
+ if (imc_global_refc .id == 0 || imc_global_refc .id == IMC_DOMAIN_CORE ) {
872
+ /*
873
+ * No other trace/thread imc events are running in
874
+ * the system, so set the refc.id to core-imc.
875
+ */
876
+ imc_global_refc .id = IMC_DOMAIN_CORE ;
877
+ imc_global_refc .refc ++ ;
878
+ } else {
879
+ mutex_unlock (& imc_global_refc .lock );
880
+ return - EBUSY ;
881
+ }
882
+ mutex_unlock (& imc_global_refc .lock );
883
+
822
884
event -> hw .event_base = (u64 )pcmi -> vbase + (config & IMC_EVENT_OFFSET_MASK );
823
885
event -> destroy = core_imc_counters_release ;
824
886
return 0 ;
@@ -877,7 +939,23 @@ static int ppc_thread_imc_cpu_online(unsigned int cpu)
877
939
878
940
static int ppc_thread_imc_cpu_offline (unsigned int cpu )
879
941
{
880
- mtspr (SPRN_LDBAR , 0 );
942
+ /*
943
+ * Set the bit 0 of LDBAR to zero.
944
+ *
945
+ * If bit 0 of LDBAR is unset, it will stop posting
946
+ * the counter data to memory.
947
+ * For thread-imc, bit 0 of LDBAR will be set to 1 in the
948
+ * event_add function. So reset this bit here, to stop the updates
949
+ * to memory in the cpu_offline path.
950
+ */
951
+ mtspr (SPRN_LDBAR , (mfspr (SPRN_LDBAR ) & (~(1UL << 63 ))));
952
+
953
+ /* Reduce the refc if thread-imc event running on this cpu */
954
+ mutex_lock (& imc_global_refc .lock );
955
+ if (imc_global_refc .id == IMC_DOMAIN_THREAD )
956
+ imc_global_refc .refc -- ;
957
+ mutex_unlock (& imc_global_refc .lock );
958
+
881
959
return 0 ;
882
960
}
883
961
@@ -916,7 +994,22 @@ static int thread_imc_event_init(struct perf_event *event)
916
994
if (!target )
917
995
return - EINVAL ;
918
996
997
+ mutex_lock (& imc_global_refc .lock );
998
+ /*
999
+ * Check if any other trace/core imc events are running in the
1000
+ * system, if not set the global id to thread-imc.
1001
+ */
1002
+ if (imc_global_refc .id == 0 || imc_global_refc .id == IMC_DOMAIN_THREAD ) {
1003
+ imc_global_refc .id = IMC_DOMAIN_THREAD ;
1004
+ imc_global_refc .refc ++ ;
1005
+ } else {
1006
+ mutex_unlock (& imc_global_refc .lock );
1007
+ return - EBUSY ;
1008
+ }
1009
+ mutex_unlock (& imc_global_refc .lock );
1010
+
919
1011
event -> pmu -> task_ctx_nr = perf_sw_context ;
1012
+ event -> destroy = reset_global_refc ;
920
1013
return 0 ;
921
1014
}
922
1015
@@ -1063,10 +1156,12 @@ static void thread_imc_event_del(struct perf_event *event, int flags)
1063
1156
int core_id ;
1064
1157
struct imc_pmu_ref * ref ;
1065
1158
1066
- mtspr (SPRN_LDBAR , 0 );
1067
-
1068
1159
core_id = smp_processor_id () / threads_per_core ;
1069
1160
ref = & core_imc_refc [core_id ];
1161
+ if (!ref ) {
1162
+ pr_debug ("imc: Failed to get event reference count\n" );
1163
+ return ;
1164
+ }
1070
1165
1071
1166
mutex_lock (& ref -> lock );
1072
1167
ref -> refc -- ;
@@ -1082,6 +1177,10 @@ static void thread_imc_event_del(struct perf_event *event, int flags)
1082
1177
ref -> refc = 0 ;
1083
1178
}
1084
1179
mutex_unlock (& ref -> lock );
1180
+
1181
+ /* Set bit 0 of LDBAR to zero, to stop posting updates to memory */
1182
+ mtspr (SPRN_LDBAR , (mfspr (SPRN_LDBAR ) & (~(1UL << 63 ))));
1183
+
1085
1184
/*
1086
1185
* Take a snapshot and calculate the delta and update
1087
1186
* the event counter values.
@@ -1133,7 +1232,18 @@ static int ppc_trace_imc_cpu_online(unsigned int cpu)
1133
1232
1134
1233
static int ppc_trace_imc_cpu_offline (unsigned int cpu )
1135
1234
{
1136
- mtspr (SPRN_LDBAR , 0 );
1235
+ /*
1236
+ * No need to set bit 0 of LDBAR to zero, as
1237
+ * it is set to zero for imc trace-mode
1238
+ *
1239
+ * Reduce the refc if any trace-imc event running
1240
+ * on this cpu.
1241
+ */
1242
+ mutex_lock (& imc_global_refc .lock );
1243
+ if (imc_global_refc .id == IMC_DOMAIN_TRACE )
1244
+ imc_global_refc .refc -- ;
1245
+ mutex_unlock (& imc_global_refc .lock );
1246
+
1137
1247
return 0 ;
1138
1248
}
1139
1249
@@ -1226,29 +1336,26 @@ static int trace_imc_event_add(struct perf_event *event, int flags)
1226
1336
local_mem = get_trace_imc_event_base_addr ();
1227
1337
ldbar_value = ((u64 )local_mem & THREAD_IMC_LDBAR_MASK ) | TRACE_IMC_ENABLE ;
1228
1338
1229
- if (core_imc_refc )
1230
- ref = & core_imc_refc [core_id ];
1339
+ /* trace-imc reference count */
1340
+ if (trace_imc_refc )
1341
+ ref = & trace_imc_refc [core_id ];
1231
1342
if (!ref ) {
1232
- /* If core-imc is not enabled, use trace-imc reference count */
1233
- if (trace_imc_refc )
1234
- ref = & trace_imc_refc [core_id ];
1235
- if (!ref )
1236
- return - EINVAL ;
1343
+ pr_debug ("imc: Failed to get the event reference count\n" );
1344
+ return - EINVAL ;
1237
1345
}
1346
+
1238
1347
mtspr (SPRN_LDBAR , ldbar_value );
1239
1348
mutex_lock (& ref -> lock );
1240
1349
if (ref -> refc == 0 ) {
1241
1350
if (opal_imc_counters_start (OPAL_IMC_COUNTERS_TRACE ,
1242
1351
get_hard_smp_processor_id (smp_processor_id ()))) {
1243
1352
mutex_unlock (& ref -> lock );
1244
1353
pr_err ("trace-imc: Unable to start the counters for core %d\n" , core_id );
1245
- mtspr (SPRN_LDBAR , 0 );
1246
1354
return - EINVAL ;
1247
1355
}
1248
1356
}
1249
1357
++ ref -> refc ;
1250
1358
mutex_unlock (& ref -> lock );
1251
-
1252
1359
return 0 ;
1253
1360
}
1254
1361
@@ -1274,16 +1381,13 @@ static void trace_imc_event_del(struct perf_event *event, int flags)
1274
1381
int core_id = smp_processor_id () / threads_per_core ;
1275
1382
struct imc_pmu_ref * ref = NULL ;
1276
1383
1277
- if (core_imc_refc )
1278
- ref = & core_imc_refc [core_id ];
1384
+ if (trace_imc_refc )
1385
+ ref = & trace_imc_refc [core_id ];
1279
1386
if (!ref ) {
1280
- /* If core-imc is not enabled, use trace-imc reference count */
1281
- if (trace_imc_refc )
1282
- ref = & trace_imc_refc [core_id ];
1283
- if (!ref )
1284
- return ;
1387
+ pr_debug ("imc: Failed to get event reference count\n" );
1388
+ return ;
1285
1389
}
1286
- mtspr ( SPRN_LDBAR , 0 );
1390
+
1287
1391
mutex_lock (& ref -> lock );
1288
1392
ref -> refc -- ;
1289
1393
if (ref -> refc == 0 ) {
@@ -1297,6 +1401,7 @@ static void trace_imc_event_del(struct perf_event *event, int flags)
1297
1401
ref -> refc = 0 ;
1298
1402
}
1299
1403
mutex_unlock (& ref -> lock );
1404
+
1300
1405
trace_imc_event_stop (event , flags );
1301
1406
}
1302
1407
@@ -1314,10 +1419,30 @@ static int trace_imc_event_init(struct perf_event *event)
1314
1419
if (event -> attr .sample_period == 0 )
1315
1420
return - ENOENT ;
1316
1421
1422
+ /*
1423
+ * Take the global lock, and make sure
1424
+ * no other thread is running any core/thread imc
1425
+ * events
1426
+ */
1427
+ mutex_lock (& imc_global_refc .lock );
1428
+ if (imc_global_refc .id == 0 || imc_global_refc .id == IMC_DOMAIN_TRACE ) {
1429
+ /*
1430
+ * No core/thread imc events are running in the
1431
+ * system, so set the refc.id to trace-imc.
1432
+ */
1433
+ imc_global_refc .id = IMC_DOMAIN_TRACE ;
1434
+ imc_global_refc .refc ++ ;
1435
+ } else {
1436
+ mutex_unlock (& imc_global_refc .lock );
1437
+ return - EBUSY ;
1438
+ }
1439
+ mutex_unlock (& imc_global_refc .lock );
1440
+
1317
1441
event -> hw .idx = -1 ;
1318
1442
target = event -> hw .target ;
1319
1443
1320
1444
event -> pmu -> task_ctx_nr = perf_hw_context ;
1445
+ event -> destroy = reset_global_refc ;
1321
1446
return 0 ;
1322
1447
}
1323
1448
@@ -1429,10 +1554,10 @@ static void cleanup_all_core_imc_memory(void)
1429
1554
static void thread_imc_ldbar_disable (void * dummy )
1430
1555
{
1431
1556
/*
1432
- * By Zeroing LDBAR, we disable thread-imc
1433
- * updates.
1557
+ * By setting 0th bit of LDBAR to zero , we disable thread-imc
1558
+ * updates to memory .
1434
1559
*/
1435
- mtspr (SPRN_LDBAR , 0 );
1560
+ mtspr (SPRN_LDBAR , ( mfspr ( SPRN_LDBAR ) & (~( 1UL << 63 ))) );
1436
1561
}
1437
1562
1438
1563
void thread_imc_disable (void )
0 commit comments