Skip to content

Commit 0ac8640

Browse files
Thomas Richtergregkh
authored andcommitted
s390/cpum_cf: Fix endless loop in CF_DIAG event stop
[ Upstream commit e6ce1f1 ] Event CF_DIAG reads out complete counter sets using stcctm instruction. This is done at event start time when the process starts execution and at event stop time when the process is removed from the CPU. During removal the difference of each counter in the counter sets is calculated and saved as raw data in the ring buffer. This works fine unless the number of counters in a counter set is zero. This may happen for the extended counter set. This set is machine specific and the size of the counter set can be zero even when extended counter set is authorized for read access. This case is not handled. cfdiag_diffctr() checks authorization of the extended counter set. If true the functions assumes the extended counter set has been saved in a data buffer. However this is not the case, cfdiag_getctrset() does not save a counter set with counter set size of zero. This mismatch causes an endless loop in the counter set readout during event stop handling. The calculation of the difference of the counters in each counter now verifies the size of the counter set is non-zero. A counter set with size zero is skipped. Fixes: a029a4e ("s390/cpumf: Allow concurrent access for CPU Measurement Counter Facility") Signed-off-by: Thomas Richter <tmricht@linux.ibm.com> Acked-by: Sumanth Korikkar <sumanthk@linux.ibm.com> Acked-by: Heiko Carstens <hca@linux.ibm.com> Cc: Heiko Carstens <hca@linux.ibm.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Alexander Gordeev <agordeev@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 6a54c97 commit 0ac8640

1 file changed

Lines changed: 10 additions & 4 deletions

File tree

arch/s390/kernel/perf_cpum_cf.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -556,25 +556,31 @@ static int cfdiag_diffctr(struct cpu_cf_events *cpuhw, unsigned long auth)
556556
struct cf_trailer_entry *trailer_start, *trailer_stop;
557557
struct cf_ctrset_entry *ctrstart, *ctrstop;
558558
size_t offset = 0;
559+
int i;
559560

560-
auth &= (1 << CPUMF_LCCTL_ENABLE_SHIFT) - 1;
561-
do {
561+
for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) {
562562
ctrstart = (struct cf_ctrset_entry *)(cpuhw->start + offset);
563563
ctrstop = (struct cf_ctrset_entry *)(cpuhw->stop + offset);
564564

565+
/* Counter set not authorized */
566+
if (!(auth & cpumf_ctr_ctl[i]))
567+
continue;
568+
/* Counter set size zero was not saved */
569+
if (!cpum_cf_read_setsize(i))
570+
continue;
571+
565572
if (memcmp(ctrstop, ctrstart, sizeof(*ctrstop))) {
566573
pr_err_once("cpum_cf_diag counter set compare error "
567574
"in set %i\n", ctrstart->set);
568575
return 0;
569576
}
570-
auth &= ~cpumf_ctr_ctl[ctrstart->set];
571577
if (ctrstart->def == CF_DIAG_CTRSET_DEF) {
572578
cfdiag_diffctrset((u64 *)(ctrstart + 1),
573579
(u64 *)(ctrstop + 1), ctrstart->ctr);
574580
offset += ctrstart->ctr * sizeof(u64) +
575581
sizeof(*ctrstart);
576582
}
577-
} while (ctrstart->def && auth);
583+
}
578584

579585
/* Save time_stamp from start of event in stop's trailer */
580586
trailer_start = (struct cf_trailer_entry *)(cpuhw->start + offset);

0 commit comments

Comments
 (0)