Skip to content

Commit a36e8ba

Browse files
anjutsudhakarmpe
authored andcommitted
powerpc/perf: Implement a global lock to avoid races between trace, core and thread imc events.
IMC(In-memory Collection Counters) does performance monitoring in two different modes, i.e accumulation mode(core-imc and thread-imc events), and trace mode(trace-imc events). A cpu thread can either be in accumulation-mode or trace-mode at a time and this is done via the LDBAR register in POWER architecture. The current design does not address the races between thread-imc and trace-imc events. Patch implements a global id and lock to avoid the races between core, trace and thread imc events. With this global id-lock implementation, the system can either run core, thread or trace imc events at a time. i.e. to run any core-imc events, thread/trace imc events should not be enabled/monitored. Signed-off-by: Anju T Sudhakar <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent a95a0a1 commit a36e8ba

File tree

1 file changed

+149
-24
lines changed

1 file changed

+149
-24
lines changed

arch/powerpc/perf/imc-pmu.c

Lines changed: 149 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ static DEFINE_PER_CPU(u64 *, trace_imc_mem);
4444
static struct imc_pmu_ref *trace_imc_refc;
4545
static int trace_imc_mem_size;
4646

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+
4757
static struct imc_pmu *imc_event_to_pmu(struct perf_event *event)
4858
{
4959
return container_of(event->pmu, struct imc_pmu, pmu);
@@ -698,6 +708,16 @@ static int ppc_core_imc_cpu_offline(unsigned int cpu)
698708
return -EINVAL;
699709

700710
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);
701721
}
702722
return 0;
703723
}
@@ -710,6 +730,23 @@ static int core_imc_pmu_cpumask_init(void)
710730
ppc_core_imc_cpu_offline);
711731
}
712732

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+
713750
static void core_imc_counters_release(struct perf_event *event)
714751
{
715752
int rc, core_id;
@@ -759,6 +796,8 @@ static void core_imc_counters_release(struct perf_event *event)
759796
ref->refc = 0;
760797
}
761798
mutex_unlock(&ref->lock);
799+
800+
reset_global_refc(event);
762801
}
763802

764803
static int core_imc_event_init(struct perf_event *event)
@@ -819,6 +858,29 @@ static int core_imc_event_init(struct perf_event *event)
819858
++ref->refc;
820859
mutex_unlock(&ref->lock);
821860

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+
822884
event->hw.event_base = (u64)pcmi->vbase + (config & IMC_EVENT_OFFSET_MASK);
823885
event->destroy = core_imc_counters_release;
824886
return 0;
@@ -877,7 +939,23 @@ static int ppc_thread_imc_cpu_online(unsigned int cpu)
877939

878940
static int ppc_thread_imc_cpu_offline(unsigned int cpu)
879941
{
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+
881959
return 0;
882960
}
883961

@@ -916,7 +994,22 @@ static int thread_imc_event_init(struct perf_event *event)
916994
if (!target)
917995
return -EINVAL;
918996

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+
9191011
event->pmu->task_ctx_nr = perf_sw_context;
1012+
event->destroy = reset_global_refc;
9201013
return 0;
9211014
}
9221015

@@ -1063,10 +1156,12 @@ static void thread_imc_event_del(struct perf_event *event, int flags)
10631156
int core_id;
10641157
struct imc_pmu_ref *ref;
10651158

1066-
mtspr(SPRN_LDBAR, 0);
1067-
10681159
core_id = smp_processor_id() / threads_per_core;
10691160
ref = &core_imc_refc[core_id];
1161+
if (!ref) {
1162+
pr_debug("imc: Failed to get event reference count\n");
1163+
return;
1164+
}
10701165

10711166
mutex_lock(&ref->lock);
10721167
ref->refc--;
@@ -1082,6 +1177,10 @@ static void thread_imc_event_del(struct perf_event *event, int flags)
10821177
ref->refc = 0;
10831178
}
10841179
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+
10851184
/*
10861185
* Take a snapshot and calculate the delta and update
10871186
* the event counter values.
@@ -1133,7 +1232,18 @@ static int ppc_trace_imc_cpu_online(unsigned int cpu)
11331232

11341233
static int ppc_trace_imc_cpu_offline(unsigned int cpu)
11351234
{
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+
11371247
return 0;
11381248
}
11391249

@@ -1226,29 +1336,26 @@ static int trace_imc_event_add(struct perf_event *event, int flags)
12261336
local_mem = get_trace_imc_event_base_addr();
12271337
ldbar_value = ((u64)local_mem & THREAD_IMC_LDBAR_MASK) | TRACE_IMC_ENABLE;
12281338

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];
12311342
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;
12371345
}
1346+
12381347
mtspr(SPRN_LDBAR, ldbar_value);
12391348
mutex_lock(&ref->lock);
12401349
if (ref->refc == 0) {
12411350
if (opal_imc_counters_start(OPAL_IMC_COUNTERS_TRACE,
12421351
get_hard_smp_processor_id(smp_processor_id()))) {
12431352
mutex_unlock(&ref->lock);
12441353
pr_err("trace-imc: Unable to start the counters for core %d\n", core_id);
1245-
mtspr(SPRN_LDBAR, 0);
12461354
return -EINVAL;
12471355
}
12481356
}
12491357
++ref->refc;
12501358
mutex_unlock(&ref->lock);
1251-
12521359
return 0;
12531360
}
12541361

@@ -1274,16 +1381,13 @@ static void trace_imc_event_del(struct perf_event *event, int flags)
12741381
int core_id = smp_processor_id() / threads_per_core;
12751382
struct imc_pmu_ref *ref = NULL;
12761383

1277-
if (core_imc_refc)
1278-
ref = &core_imc_refc[core_id];
1384+
if (trace_imc_refc)
1385+
ref = &trace_imc_refc[core_id];
12791386
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;
12851389
}
1286-
mtspr(SPRN_LDBAR, 0);
1390+
12871391
mutex_lock(&ref->lock);
12881392
ref->refc--;
12891393
if (ref->refc == 0) {
@@ -1297,6 +1401,7 @@ static void trace_imc_event_del(struct perf_event *event, int flags)
12971401
ref->refc = 0;
12981402
}
12991403
mutex_unlock(&ref->lock);
1404+
13001405
trace_imc_event_stop(event, flags);
13011406
}
13021407

@@ -1314,10 +1419,30 @@ static int trace_imc_event_init(struct perf_event *event)
13141419
if (event->attr.sample_period == 0)
13151420
return -ENOENT;
13161421

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+
13171441
event->hw.idx = -1;
13181442
target = event->hw.target;
13191443

13201444
event->pmu->task_ctx_nr = perf_hw_context;
1445+
event->destroy = reset_global_refc;
13211446
return 0;
13221447
}
13231448

@@ -1429,10 +1554,10 @@ static void cleanup_all_core_imc_memory(void)
14291554
static void thread_imc_ldbar_disable(void *dummy)
14301555
{
14311556
/*
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.
14341559
*/
1435-
mtspr(SPRN_LDBAR, 0);
1560+
mtspr(SPRN_LDBAR, (mfspr(SPRN_LDBAR) & (~(1UL << 63))));
14361561
}
14371562

14381563
void thread_imc_disable(void)

0 commit comments

Comments
 (0)