perf: Factor __output_copy to be usable with specific copy function
Adding a generic way to use __output_copy function with specific copy function via DEFINE_PERF_OUTPUT_COPY macro. Using this to add new __output_copy_user function, that provides output copy from user pointers. For x86 the copy_from_user_nmi function is used and __copy_from_user_inatomic for the rest of the architectures. This new function will be used in user stack dump on sample, coming in next patches. Signed-off-by: Jiri Olsa <jolsa@redhat.com> Cc: "Frank Ch. Eigler" <fche@redhat.com> Cc: Arun Sharma <asharma@fb.com> Cc: Benjamin Redelings <benjamin.redelings@nescent.org> Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com> Cc: Cyrill Gorcunov <gorcunov@openvz.org> Cc: Frank Ch. Eigler <fche@redhat.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Robert Richter <robert.richter@amd.com> Cc: Stephane Eranian <eranian@google.com> Cc: Tom Zanussi <tzanussi@gmail.com> Cc: Ulrich Drepper <drepper@gmail.com> Link: http://lkml.kernel.org/r/1344345647-11536-4-git-send-email-jolsa@redhat.com Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
4018994f3d
commit
91d7753a45
4 changed files with 47 additions and 25 deletions
|
@ -262,4 +262,6 @@ static inline void perf_check_microcode(void) { }
|
|||
static inline void amd_pmu_disable_virt(void) { }
|
||||
#endif
|
||||
|
||||
#define arch_perf_out_copy_user copy_from_user_nmi
|
||||
|
||||
#endif /* _ASM_X86_PERF_EVENT_H */
|
||||
|
|
|
@ -1319,7 +1319,7 @@ static inline bool has_branch_stack(struct perf_event *event)
|
|||
extern int perf_output_begin(struct perf_output_handle *handle,
|
||||
struct perf_event *event, unsigned int size);
|
||||
extern void perf_output_end(struct perf_output_handle *handle);
|
||||
extern void perf_output_copy(struct perf_output_handle *handle,
|
||||
extern unsigned int perf_output_copy(struct perf_output_handle *handle,
|
||||
const void *buf, unsigned int len);
|
||||
extern int perf_swevent_get_recursion_context(void);
|
||||
extern void perf_swevent_put_recursion_context(int rctx);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define _KERNEL_EVENTS_INTERNAL_H
|
||||
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
/* Buffer handling */
|
||||
|
||||
|
@ -76,30 +77,49 @@ static inline unsigned long perf_data_size(struct ring_buffer *rb)
|
|||
return rb->nr_pages << (PAGE_SHIFT + page_order(rb));
|
||||
}
|
||||
|
||||
static inline void
|
||||
__output_copy(struct perf_output_handle *handle,
|
||||
const void *buf, unsigned int len)
|
||||
{
|
||||
do {
|
||||
unsigned long size = min_t(unsigned long, handle->size, len);
|
||||
|
||||
memcpy(handle->addr, buf, size);
|
||||
|
||||
len -= size;
|
||||
handle->addr += size;
|
||||
buf += size;
|
||||
handle->size -= size;
|
||||
if (!handle->size) {
|
||||
struct ring_buffer *rb = handle->rb;
|
||||
|
||||
handle->page++;
|
||||
handle->page &= rb->nr_pages - 1;
|
||||
handle->addr = rb->data_pages[handle->page];
|
||||
handle->size = PAGE_SIZE << page_order(rb);
|
||||
}
|
||||
} while (len);
|
||||
#define DEFINE_OUTPUT_COPY(func_name, memcpy_func) \
|
||||
static inline unsigned int \
|
||||
func_name(struct perf_output_handle *handle, \
|
||||
const void *buf, unsigned int len) \
|
||||
{ \
|
||||
unsigned long size, written; \
|
||||
\
|
||||
do { \
|
||||
size = min_t(unsigned long, handle->size, len); \
|
||||
\
|
||||
written = memcpy_func(handle->addr, buf, size); \
|
||||
\
|
||||
len -= written; \
|
||||
handle->addr += written; \
|
||||
buf += written; \
|
||||
handle->size -= written; \
|
||||
if (!handle->size) { \
|
||||
struct ring_buffer *rb = handle->rb; \
|
||||
\
|
||||
handle->page++; \
|
||||
handle->page &= rb->nr_pages - 1; \
|
||||
handle->addr = rb->data_pages[handle->page]; \
|
||||
handle->size = PAGE_SIZE << page_order(rb); \
|
||||
} \
|
||||
} while (len && written == size); \
|
||||
\
|
||||
return len; \
|
||||
}
|
||||
|
||||
static inline int memcpy_common(void *dst, const void *src, size_t n)
|
||||
{
|
||||
memcpy(dst, src, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
DEFINE_OUTPUT_COPY(__output_copy, memcpy_common)
|
||||
|
||||
#ifndef arch_perf_out_copy_user
|
||||
#define arch_perf_out_copy_user __copy_from_user_inatomic
|
||||
#endif
|
||||
|
||||
DEFINE_OUTPUT_COPY(__output_copy_user, arch_perf_out_copy_user)
|
||||
|
||||
/* Callchain handling */
|
||||
extern struct perf_callchain_entry *
|
||||
perf_callchain(struct perf_event *event, struct pt_regs *regs);
|
||||
|
|
|
@ -182,10 +182,10 @@ int perf_output_begin(struct perf_output_handle *handle,
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
void perf_output_copy(struct perf_output_handle *handle,
|
||||
unsigned int perf_output_copy(struct perf_output_handle *handle,
|
||||
const void *buf, unsigned int len)
|
||||
{
|
||||
__output_copy(handle, buf, len);
|
||||
return __output_copy(handle, buf, len);
|
||||
}
|
||||
|
||||
void perf_output_end(struct perf_output_handle *handle)
|
||||
|
|
Loading…
Reference in a new issue