perf/x86/intel/pt: Split ToPA metadata and page layout
PT uses page sized ToPA tables, where the ToPA table resides at the bottom and its driver-specific metadata taking up a few words at the top of the page. The split is currently calculated manually and needs to be redone every time a field is added to or removed from the metadata structure. Also, the 32-bit version can be made smaller. By splitting the table and metadata into separate structures, we are making the compiler figure out the division of the page. Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> 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> Link: http://lkml.kernel.org/r/20190821124727.73310-5-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
539f7c26b4
commit
38bb8d77d0
@ -545,16 +545,8 @@ static void pt_config_buffer(void *buf, unsigned int topa_idx,
|
|||||||
wrmsrl(MSR_IA32_RTIT_OUTPUT_MASK, reg);
|
wrmsrl(MSR_IA32_RTIT_OUTPUT_MASK, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Keep ToPA table-related metadata on the same page as the actual table,
|
|
||||||
* taking up a few words from the top
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define TENTS_PER_PAGE (((PAGE_SIZE - 40) / sizeof(struct topa_entry)) - 1)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct topa - page-sized ToPA table with metadata at the top
|
* struct topa - ToPA metadata
|
||||||
* @table: actual ToPA table entries, as understood by PT hardware
|
|
||||||
* @list: linkage to struct pt_buffer's list of tables
|
* @list: linkage to struct pt_buffer's list of tables
|
||||||
* @phys: physical address of this page
|
* @phys: physical address of this page
|
||||||
* @offset: offset of the first entry in this table in the buffer
|
* @offset: offset of the first entry in this table in the buffer
|
||||||
@ -562,7 +554,6 @@ static void pt_config_buffer(void *buf, unsigned int topa_idx,
|
|||||||
* @last: index of the last initialized entry in this table
|
* @last: index of the last initialized entry in this table
|
||||||
*/
|
*/
|
||||||
struct topa {
|
struct topa {
|
||||||
struct topa_entry table[TENTS_PER_PAGE];
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
u64 phys;
|
u64 phys;
|
||||||
u64 offset;
|
u64 offset;
|
||||||
@ -570,8 +561,39 @@ struct topa {
|
|||||||
int last;
|
int last;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keep ToPA table-related metadata on the same page as the actual table,
|
||||||
|
* taking up a few words from the top
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TENTS_PER_PAGE \
|
||||||
|
((PAGE_SIZE - sizeof(struct topa)) / sizeof(struct topa_entry))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct topa_page - page-sized ToPA table with metadata at the top
|
||||||
|
* @table: actual ToPA table entries, as understood by PT hardware
|
||||||
|
* @topa: metadata
|
||||||
|
*/
|
||||||
|
struct topa_page {
|
||||||
|
struct topa_entry table[TENTS_PER_PAGE];
|
||||||
|
struct topa topa;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct topa_page *topa_to_page(struct topa *topa)
|
||||||
|
{
|
||||||
|
return container_of(topa, struct topa_page, topa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct topa_page *topa_entry_to_page(struct topa_entry *te)
|
||||||
|
{
|
||||||
|
return (struct topa_page *)((unsigned long)te & PAGE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
/* make -1 stand for the last table entry */
|
/* make -1 stand for the last table entry */
|
||||||
#define TOPA_ENTRY(t, i) ((i) == -1 ? &(t)->table[(t)->last] : &(t)->table[(i)])
|
#define TOPA_ENTRY(t, i) \
|
||||||
|
((i) == -1 \
|
||||||
|
? &topa_to_page(t)->table[(t)->last] \
|
||||||
|
: &topa_to_page(t)->table[(i)])
|
||||||
#define TOPA_ENTRY_SIZE(t, i) (sizes(TOPA_ENTRY((t), (i))->size))
|
#define TOPA_ENTRY_SIZE(t, i) (sizes(TOPA_ENTRY((t), (i))->size))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -584,27 +606,27 @@ struct topa {
|
|||||||
static struct topa *topa_alloc(int cpu, gfp_t gfp)
|
static struct topa *topa_alloc(int cpu, gfp_t gfp)
|
||||||
{
|
{
|
||||||
int node = cpu_to_node(cpu);
|
int node = cpu_to_node(cpu);
|
||||||
struct topa *topa;
|
struct topa_page *tp;
|
||||||
struct page *p;
|
struct page *p;
|
||||||
|
|
||||||
p = alloc_pages_node(node, gfp | __GFP_ZERO, 0);
|
p = alloc_pages_node(node, gfp | __GFP_ZERO, 0);
|
||||||
if (!p)
|
if (!p)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
topa = page_address(p);
|
tp = page_address(p);
|
||||||
topa->last = 0;
|
tp->topa.last = 0;
|
||||||
topa->phys = page_to_phys(p);
|
tp->topa.phys = page_to_phys(p);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case of singe-entry ToPA, always put the self-referencing END
|
* In case of singe-entry ToPA, always put the self-referencing END
|
||||||
* link as the 2nd entry in the table
|
* link as the 2nd entry in the table
|
||||||
*/
|
*/
|
||||||
if (!intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries)) {
|
if (!intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries)) {
|
||||||
TOPA_ENTRY(topa, 1)->base = topa->phys >> TOPA_SHIFT;
|
TOPA_ENTRY(&tp->topa, 1)->base = tp->topa.phys;
|
||||||
TOPA_ENTRY(topa, 1)->end = 1;
|
TOPA_ENTRY(&tp->topa, 1)->end = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return topa;
|
return &tp->topa;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -714,22 +736,23 @@ static void pt_topa_dump(struct pt_buffer *buf)
|
|||||||
struct topa *topa;
|
struct topa *topa;
|
||||||
|
|
||||||
list_for_each_entry(topa, &buf->tables, list) {
|
list_for_each_entry(topa, &buf->tables, list) {
|
||||||
|
struct topa_page *tp = topa_to_page(topa);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
pr_debug("# table @%p (%016Lx), off %llx size %zx\n", topa->table,
|
pr_debug("# table @%p (%016Lx), off %llx size %zx\n", tp->table,
|
||||||
topa->phys, topa->offset, topa->size);
|
topa->phys, topa->offset, topa->size);
|
||||||
for (i = 0; i < TENTS_PER_PAGE; i++) {
|
for (i = 0; i < TENTS_PER_PAGE; i++) {
|
||||||
pr_debug("# entry @%p (%lx sz %u %c%c%c) raw=%16llx\n",
|
pr_debug("# entry @%p (%lx sz %u %c%c%c) raw=%16llx\n",
|
||||||
&topa->table[i],
|
&tp->table[i],
|
||||||
(unsigned long)topa->table[i].base << TOPA_SHIFT,
|
(unsigned long)tp->table[i].base << TOPA_SHIFT,
|
||||||
sizes(topa->table[i].size),
|
sizes(tp->table[i].size),
|
||||||
topa->table[i].end ? 'E' : ' ',
|
tp->table[i].end ? 'E' : ' ',
|
||||||
topa->table[i].intr ? 'I' : ' ',
|
tp->table[i].intr ? 'I' : ' ',
|
||||||
topa->table[i].stop ? 'S' : ' ',
|
tp->table[i].stop ? 'S' : ' ',
|
||||||
*(u64 *)&topa->table[i]);
|
*(u64 *)&tp->table[i]);
|
||||||
if ((intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries) &&
|
if ((intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries) &&
|
||||||
topa->table[i].stop) ||
|
tp->table[i].stop) ||
|
||||||
topa->table[i].end)
|
tp->table[i].end)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -792,7 +815,7 @@ static void pt_update_head(struct pt *pt)
|
|||||||
*/
|
*/
|
||||||
static void *pt_buffer_region(struct pt_buffer *buf)
|
static void *pt_buffer_region(struct pt_buffer *buf)
|
||||||
{
|
{
|
||||||
return phys_to_virt(buf->cur->table[buf->cur_idx].base << TOPA_SHIFT);
|
return phys_to_virt(TOPA_ENTRY(buf->cur, buf->cur_idx)->base << TOPA_SHIFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -869,9 +892,11 @@ static void pt_handle_status(struct pt *pt)
|
|||||||
static void pt_read_offset(struct pt_buffer *buf)
|
static void pt_read_offset(struct pt_buffer *buf)
|
||||||
{
|
{
|
||||||
u64 offset, base_topa;
|
u64 offset, base_topa;
|
||||||
|
struct topa_page *tp;
|
||||||
|
|
||||||
rdmsrl(MSR_IA32_RTIT_OUTPUT_BASE, base_topa);
|
rdmsrl(MSR_IA32_RTIT_OUTPUT_BASE, base_topa);
|
||||||
buf->cur = phys_to_virt(base_topa);
|
tp = phys_to_virt(base_topa);
|
||||||
|
buf->cur = &tp->topa;
|
||||||
|
|
||||||
rdmsrl(MSR_IA32_RTIT_OUTPUT_MASK, offset);
|
rdmsrl(MSR_IA32_RTIT_OUTPUT_MASK, offset);
|
||||||
/* offset within current output region */
|
/* offset within current output region */
|
||||||
@ -1021,6 +1046,7 @@ static void pt_buffer_setup_topa_index(struct pt_buffer *buf)
|
|||||||
*/
|
*/
|
||||||
static void pt_buffer_reset_offsets(struct pt_buffer *buf, unsigned long head)
|
static void pt_buffer_reset_offsets(struct pt_buffer *buf, unsigned long head)
|
||||||
{
|
{
|
||||||
|
struct topa_page *cur_tp;
|
||||||
int pg;
|
int pg;
|
||||||
|
|
||||||
if (buf->snapshot)
|
if (buf->snapshot)
|
||||||
@ -1029,7 +1055,8 @@ static void pt_buffer_reset_offsets(struct pt_buffer *buf, unsigned long head)
|
|||||||
pg = (head >> PAGE_SHIFT) & (buf->nr_pages - 1);
|
pg = (head >> PAGE_SHIFT) & (buf->nr_pages - 1);
|
||||||
pg = pt_topa_next_entry(buf, pg);
|
pg = pt_topa_next_entry(buf, pg);
|
||||||
|
|
||||||
buf->cur = (struct topa *)((unsigned long)buf->topa_index[pg] & PAGE_MASK);
|
cur_tp = topa_entry_to_page(buf->topa_index[pg]);
|
||||||
|
buf->cur = &cur_tp->topa;
|
||||||
buf->cur_idx = buf->topa_index[pg] - TOPA_ENTRY(buf->cur, 0);
|
buf->cur_idx = buf->topa_index[pg] - TOPA_ENTRY(buf->cur, 0);
|
||||||
buf->output_off = head & (pt_buffer_region_size(buf) - 1);
|
buf->output_off = head & (pt_buffer_region_size(buf) - 1);
|
||||||
|
|
||||||
@ -1294,7 +1321,7 @@ void intel_pt_interrupt(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pt_config_buffer(buf->cur->table, buf->cur_idx,
|
pt_config_buffer(topa_to_page(buf->cur)->table, buf->cur_idx,
|
||||||
buf->output_off);
|
buf->output_off);
|
||||||
pt_config(event);
|
pt_config(event);
|
||||||
}
|
}
|
||||||
@ -1359,7 +1386,7 @@ static void pt_event_start(struct perf_event *event, int mode)
|
|||||||
WRITE_ONCE(pt->handle_nmi, 1);
|
WRITE_ONCE(pt->handle_nmi, 1);
|
||||||
hwc->state = 0;
|
hwc->state = 0;
|
||||||
|
|
||||||
pt_config_buffer(buf->cur->table, buf->cur_idx,
|
pt_config_buffer(topa_to_page(buf->cur)->table, buf->cur_idx,
|
||||||
buf->output_off);
|
buf->output_off);
|
||||||
pt_config(event);
|
pt_config(event);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user