[PATCH V5 01/14] perf/core: Add PERF_SAMPLE_DATA_PAGE_SIZE

kan.liang
2019-02-08T17:56:34Z

Current perf can report both virtual address and physical address, but
it doesn't report page size. Users have no idea how large the utilized
page is. They cannot promote/demote large pages to optimize memory use.

Add a new sample type for data page size.

Current perf already has a facility to collect data virtual address.
A __weak function, aim to retrieve page size via a given virtual
address, is introduced in the generic code.
Now it always returns 0. All architectures can implement their own
functions later separately. The real page size must be returned. The
function must be IRQ-safe.

A u64 type is claimed for page_size. Because struct perf_sample_data
requires cacheline_aligned.

Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---

Changes since V4
- Split patch 1 of V4 into two patches.
This patch add the core stuff including the weak function

include/linux/perf_event.h | 1 +
include/uapi/linux/perf_event.h | 4 +++-
kernel/events/core.c | 15 +++++++++++++++
3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 6cb5d48..46908cf 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -938,6 +938,7 @@ struct perf_sample_data {
u64 stack_user_size;

u64 phys_addr;
+ u64 data_page_size;
} ____cacheline_aligned;

/* default value for data source */
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index 7198ddd..0e8d222 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -141,8 +141,9 @@ enum perf_event_sample_format {
PERF_SAMPLE_TRANSACTION = 1U << 17,
PERF_SAMPLE_REGS_INTR = 1U << 18,
PERF_SAMPLE_PHYS_ADDR = 1U << 19,
+ PERF_SAMPLE_DATA_PAGE_SIZE = 1U << 20,

- PERF_SAMPLE_MAX = 1U << 20, /* non-ABI */
+ PERF_SAMPLE_MAX = 1U << 21, /* non-ABI */

__PERF_SAMPLE_CALLCHAIN_EARLY = 1ULL << 63, /* non-ABI; internal use */
};
@@ -863,6 +864,7 @@ enum perf_event_type {
* { u64 abi; # enum perf_sample_regs_abi
* u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
* { u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR
+ * { u64 data_page_size;} && PERF_SAMPLE_DATA_PAGE_SIZE
* };
*/
PERF_RECORD_SAMPLE = 9,
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 5aeb4c7..fe6f601 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1752,6 +1752,9 @@ static void __perf_event_header_size(struct perf_event *event, u64 sample_type)
if (sample_type & PERF_SAMPLE_PHYS_ADDR)
size += sizeof(data->phys_addr);

+ if (sample_type & PERF_SAMPLE_DATA_PAGE_SIZE)
+ size += sizeof(data->data_page_size);
+
event->header_size = size;
}

@@ -6304,6 +6307,9 @@ void perf_output_sample(struct perf_output_handle *handle,
if (sample_type & PERF_SAMPLE_PHYS_ADDR)
perf_output_put(handle, data->phys_addr);

+ if (sample_type & PERF_SAMPLE_DATA_PAGE_SIZE)
+ perf_output_put(handle, data->data_page_size);
+
if (!event->attr.watermark) {
int wakeup_events = event->attr.wakeup_events;

@@ -6351,6 +6357,12 @@ static u64 perf_virt_to_phys(u64 virt)
return phys_addr;
}

+/* Return page size of given virtual address. IRQ-safe required. */
+u64 __weak perf_get_page_size(u64 virt)
+{
+ return 0;
+}
+
static struct perf_callchain_entry __empty_callchain = { .nr = 0, };

struct perf_callchain_entry *
@@ -6492,6 +6504,9 @@ void perf_prepare_sample(struct perf_event_header *header,

if (sample_type & PERF_SAMPLE_PHYS_ADDR)
data->phys_addr = perf_virt_to_phys(data->addr);
+
+ if (sample_type & PERF_SAMPLE_DATA_PAGE_SIZE)
+ data->data_page_size = perf_get_page_size(data->addr);
}

static __always_inline int
--
2.7.4