Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bitmap.h, perf/core: Fix the mask in perf_output_sample_regs()

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 exisiting code works fine in most of the case, the logic
is broken for big-endian 32-bit kernels.

When reading a u64 mask using (u32 *)(&val)[0], find_*_bit() assumes
that it gets the lower 32 bits of u64, but instead it gets the upper
32 bits - which is wrong.

The fix is to swap the words of the u64 to handle this case.
This is _not_ a regular endianness swap.

Suggested-by: Yury Norov <ynorov@caviumnetworks.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Yury Norov <ynorov@caviumnetworks.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Cc: linuxppc-dev@lists.ozlabs.org
Link: http://lkml.kernel.org/r/1471426568-31051-2-git-send-email-maddy@linux.vnet.ibm.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Madhavan Srinivasan and committed by
Ingo Molnar
29dd3288 8942c2b7

+21 -2
+18
include/linux/bitmap.h
··· 339 339 return __bitmap_parse(buf, buflen, 0, maskp, nmaskbits); 340 340 } 341 341 342 + /* 343 + * bitmap_from_u64 - Check and swap words within u64. 344 + * @mask: source bitmap 345 + * @dst: destination bitmap 346 + * 347 + * In 32-bit Big Endian kernel, when using (u32 *)(&val)[*] 348 + * to read u64 mask, we will get the wrong word. 349 + * That is "(u32 *)(&val)[0]" gets the upper 32 bits, 350 + * but we expect the lower 32-bits of u64. 351 + */ 352 + static inline void bitmap_from_u64(unsigned long *dst, u64 mask) 353 + { 354 + dst[0] = mask & ULONG_MAX; 355 + 356 + if (sizeof(mask) > sizeof(unsigned long)) 357 + dst[1] = mask >> 32; 358 + } 359 + 342 360 #endif /* __ASSEMBLY__ */ 343 361 344 362 #endif /* __LINUX_BITMAP_H */
+3 -2
kernel/events/core.c
··· 5340 5340 struct pt_regs *regs, u64 mask) 5341 5341 { 5342 5342 int bit; 5343 + DECLARE_BITMAP(_mask, 64); 5343 5344 5344 - for_each_set_bit(bit, (const unsigned long *) &mask, 5345 - sizeof(mask) * BITS_PER_BYTE) { 5345 + bitmap_from_u64(_mask, mask); 5346 + for_each_set_bit(bit, _mask, sizeof(mask) * BITS_PER_BYTE) { 5346 5347 u64 val; 5347 5348 5348 5349 val = perf_reg_value(regs, bit);