[RESEND PATCH 2/2] perf/core: Fix the mask in perf_output_sample_regs

Madhavan Srinivasan maddy at linux.vnet.ibm.com
Wed Aug 3 18:29:07 AEST 2016


When decoding the perf_regs mask in perf_output_sample_regs(),
we loop through the mask using find_first_bit and find_next_bit functions.
While the exisitng code works fine in most of the case,
the logic is broken for 32bit kernel (Big Endian).
When reading u64 mask using (u32 *)(&val)[0], find_*_bit() assumes it gets
lower 32bits of u64 but instead gets upper 32bits which is wrong.
Proposed fix is to swap the words of the u64 to handle this case.
This is _not_ endianness swap.

Suggested-by: Yury Norov <ynorov at caviumnetworks.com>
Reviewed-by: Yury Norov <ynorov at caviumnetworks.com>
Cc: Yury Norov <ynorov at caviumnetworks.com>
Cc: Peter Zijlstra <peterz at infradead.org>
Cc: Ingo Molnar <mingo at redhat.com>
Cc: Arnaldo Carvalho de Melo <acme at kernel.org>
Cc: Alexander Shishkin <alexander.shishkin at linux.intel.com>
Cc: Jiri Olsa <jolsa at kernel.org>
Cc: Michael Ellerman <mpe at ellerman.id.au>
Signed-off-by: Madhavan Srinivasan <maddy at linux.vnet.ibm.com>
---
 include/linux/bitmap.h |  2 ++
 kernel/events/core.c   |  4 +++-
 lib/bitmap.c           | 19 +++++++++++++++++++
 3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 27bfc0b631a9..6f2cc9eb12d9 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -188,6 +188,8 @@ extern int bitmap_print_to_pagebuf(bool list, char *buf,
 #define small_const_nbits(nbits) \
 	(__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
 
+extern void bitmap_from_u64(unsigned long *dst, u64 mask);
+
 static inline void bitmap_zero(unsigned long *dst, unsigned int nbits)
 {
 	if (small_const_nbits(nbits))
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 356a6c7cb52a..f5ed20a63a5e 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5269,8 +5269,10 @@ perf_output_sample_regs(struct perf_output_handle *handle,
 			struct pt_regs *regs, u64 mask)
 {
 	int bit;
+	DECLARE_BITMAP(_mask, 64);
 
-	for_each_set_bit(bit, (const unsigned long *) &mask,
+	bitmap_from_u64(_mask, mask);
+	for_each_set_bit(bit, _mask,
 			 sizeof(mask) * BITS_PER_BYTE) {
 		u64 val;
 
diff --git a/lib/bitmap.c b/lib/bitmap.c
index eca88087fa8a..2b9bda507645 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -1170,3 +1170,22 @@ void bitmap_copy_le(unsigned long *dst, const unsigned long *src, unsigned int n
 }
 EXPORT_SYMBOL(bitmap_copy_le);
 #endif
+
+/*
+ * bitmap_from_u64 - Check and swap words within u64.
+ *  @mask: source bitmap
+ *  @dst:  destination bitmap
+ *
+ * In 32bit Big Endian kernel, when using (u32 *)(&val)[*]
+ * to read u64 mask, we will get wrong word.
+ * That is "(u32 *)(&val)[0]" gets upper 32 bits,
+ * but expected could be lower 32bits of u64.
+ */
+void bitmap_from_u64(unsigned long *dst, u64 mask)
+{
+	dst[0] = mask & ULONG_MAX;
+
+	if (sizeof(mask) > sizeof(unsigned long))
+		dst[1] = mask >> 32;
+}
+EXPORT_SYMBOL(bitmap_from_u64);
-- 
2.7.4



More information about the Linuxppc-dev mailing list