Skip to content

Commit 6706384

Browse files
virtuosoIngo Molnar
authored and
Ingo Molnar
committed
perf/x86/intel/pt: Opportunistically use single range output mode
Most of PT implementations support Single Range Output mode, which is an alternative to ToPA that can be used for a single contiguous buffer and if we don't require an interrupt, that is, in AUX snapshot mode. Now that perf core will use high order allocations for the AUX buffer, in many cases the first condition will also be satisfied. The two most obvious benefits of the Single Range Output mode over the ToPA are: * not having to allocate the ToPA table(s), * not using the ToPA walk hardware. Make use of this functionality where available and appropriate. Signed-off-by: Alexander Shishkin <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: David Ahern <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Stephane Eranian <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Vince Weaver <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 25e8920 commit 6706384

File tree

2 files changed

+92
-28
lines changed

2 files changed

+92
-28
lines changed

arch/x86/events/intel/pt.c

Lines changed: 90 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ static u64 pt_config_filters(struct perf_event *event)
482482

483483
static void pt_config(struct perf_event *event)
484484
{
485+
struct pt *pt = this_cpu_ptr(&pt_ctx);
486+
struct pt_buffer *buf = perf_get_aux(&pt->handle);
485487
u64 reg;
486488

487489
/* First round: clear STATUS, in particular the PSB byte counter. */
@@ -491,7 +493,9 @@ static void pt_config(struct perf_event *event)
491493
}
492494

493495
reg = pt_config_filters(event);
494-
reg |= RTIT_CTL_TOPA | RTIT_CTL_TRACEEN;
496+
reg |= RTIT_CTL_TRACEEN;
497+
if (!buf->single)
498+
reg |= RTIT_CTL_TOPA;
495499

496500
/*
497501
* Previously, we had BRANCH_EN on by default, but now that PT has
@@ -543,18 +547,6 @@ static void pt_config_stop(struct perf_event *event)
543547
wmb();
544548
}
545549

546-
static void pt_config_buffer(void *buf, unsigned int topa_idx,
547-
unsigned int output_off)
548-
{
549-
u64 reg;
550-
551-
wrmsrl(MSR_IA32_RTIT_OUTPUT_BASE, virt_to_phys(buf));
552-
553-
reg = 0x7f | ((u64)topa_idx << 7) | ((u64)output_off << 32);
554-
555-
wrmsrl(MSR_IA32_RTIT_OUTPUT_MASK, reg);
556-
}
557-
558550
/**
559551
* struct topa - ToPA metadata
560552
* @list: linkage to struct pt_buffer's list of tables
@@ -612,6 +604,26 @@ static inline phys_addr_t topa_pfn(struct topa *topa)
612604
#define TOPA_ENTRY_SIZE(t, i) (sizes(TOPA_ENTRY((t), (i))->size))
613605
#define TOPA_ENTRY_PAGES(t, i) (1 << TOPA_ENTRY((t), (i))->size)
614606

607+
static void pt_config_buffer(struct pt_buffer *buf)
608+
{
609+
u64 reg, mask;
610+
void *base;
611+
612+
if (buf->single) {
613+
base = buf->data_pages[0];
614+
mask = (buf->nr_pages * PAGE_SIZE - 1) >> 7;
615+
} else {
616+
base = topa_to_page(buf->cur)->table;
617+
mask = (u64)buf->cur_idx;
618+
}
619+
620+
wrmsrl(MSR_IA32_RTIT_OUTPUT_BASE, virt_to_phys(base));
621+
622+
reg = 0x7f | (mask << 7) | ((u64)buf->output_off << 32);
623+
624+
wrmsrl(MSR_IA32_RTIT_OUTPUT_MASK, reg);
625+
}
626+
615627
/**
616628
* topa_alloc() - allocate page-sized ToPA table
617629
* @cpu: CPU on which to allocate.
@@ -812,6 +824,11 @@ static void pt_update_head(struct pt *pt)
812824
struct pt_buffer *buf = perf_get_aux(&pt->handle);
813825
u64 topa_idx, base, old;
814826

827+
if (buf->single) {
828+
local_set(&buf->data_size, buf->output_off);
829+
return;
830+
}
831+
815832
/* offset of the first region in this table from the beginning of buf */
816833
base = buf->cur->offset + buf->output_off;
817834

@@ -913,18 +930,21 @@ static void pt_handle_status(struct pt *pt)
913930
*/
914931
static void pt_read_offset(struct pt_buffer *buf)
915932
{
916-
u64 offset, base_topa;
933+
u64 offset, base;
917934
struct topa_page *tp;
918935

919-
rdmsrl(MSR_IA32_RTIT_OUTPUT_BASE, base_topa);
920-
tp = phys_to_virt(base_topa);
921-
buf->cur = &tp->topa;
936+
if (!buf->single) {
937+
rdmsrl(MSR_IA32_RTIT_OUTPUT_BASE, base);
938+
tp = phys_to_virt(base);
939+
buf->cur = &tp->topa;
940+
}
922941

923942
rdmsrl(MSR_IA32_RTIT_OUTPUT_MASK, offset);
924943
/* offset within current output region */
925944
buf->output_off = offset >> 32;
926945
/* index of current output region within this table */
927-
buf->cur_idx = (offset & 0xffffff80) >> 7;
946+
if (!buf->single)
947+
buf->cur_idx = (offset & 0xffffff80) >> 7;
928948
}
929949

930950
static struct topa_entry *
@@ -1040,6 +1060,9 @@ static int pt_buffer_reset_markers(struct pt_buffer *buf,
10401060
unsigned long head = local64_read(&buf->head);
10411061
unsigned long idx, npages, wakeup;
10421062

1063+
if (buf->single)
1064+
return 0;
1065+
10431066
/* can't stop in the middle of an output region */
10441067
if (buf->output_off + handle->size + 1 < pt_buffer_region_size(buf)) {
10451068
perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
@@ -1121,13 +1144,17 @@ static void pt_buffer_reset_offsets(struct pt_buffer *buf, unsigned long head)
11211144
if (buf->snapshot)
11221145
head &= (buf->nr_pages << PAGE_SHIFT) - 1;
11231146

1124-
pg = (head >> PAGE_SHIFT) & (buf->nr_pages - 1);
1125-
te = pt_topa_entry_for_page(buf, pg);
1147+
if (!buf->single) {
1148+
pg = (head >> PAGE_SHIFT) & (buf->nr_pages - 1);
1149+
te = pt_topa_entry_for_page(buf, pg);
11261150

1127-
cur_tp = topa_entry_to_page(te);
1128-
buf->cur = &cur_tp->topa;
1129-
buf->cur_idx = te - TOPA_ENTRY(buf->cur, 0);
1130-
buf->output_off = head & (pt_buffer_region_size(buf) - 1);
1151+
cur_tp = topa_entry_to_page(te);
1152+
buf->cur = &cur_tp->topa;
1153+
buf->cur_idx = te - TOPA_ENTRY(buf->cur, 0);
1154+
buf->output_off = head & (pt_buffer_region_size(buf) - 1);
1155+
} else {
1156+
buf->output_off = head;
1157+
}
11311158

11321159
local64_set(&buf->head, head);
11331160
local_set(&buf->data_size, 0);
@@ -1141,6 +1168,9 @@ static void pt_buffer_fini_topa(struct pt_buffer *buf)
11411168
{
11421169
struct topa *topa, *iter;
11431170

1171+
if (buf->single)
1172+
return;
1173+
11441174
list_for_each_entry_safe(topa, iter, &buf->tables, list) {
11451175
/*
11461176
* right now, this is in free_aux() path only, so
@@ -1186,6 +1216,36 @@ static int pt_buffer_init_topa(struct pt_buffer *buf, int cpu,
11861216
return 0;
11871217
}
11881218

1219+
static int pt_buffer_try_single(struct pt_buffer *buf, int nr_pages)
1220+
{
1221+
struct page *p = virt_to_page(buf->data_pages[0]);
1222+
int ret = -ENOTSUPP, order = 0;
1223+
1224+
/*
1225+
* We can use single range output mode
1226+
* + in snapshot mode, where we don't need interrupts;
1227+
* + if the hardware supports it;
1228+
* + if the entire buffer is one contiguous allocation.
1229+
*/
1230+
if (!buf->snapshot)
1231+
goto out;
1232+
1233+
if (!intel_pt_validate_hw_cap(PT_CAP_single_range_output))
1234+
goto out;
1235+
1236+
if (PagePrivate(p))
1237+
order = page_private(p);
1238+
1239+
if (1 << order != nr_pages)
1240+
goto out;
1241+
1242+
buf->single = true;
1243+
buf->nr_pages = nr_pages;
1244+
ret = 0;
1245+
out:
1246+
return ret;
1247+
}
1248+
11891249
/**
11901250
* pt_buffer_setup_aux() - set up topa tables for a PT buffer
11911251
* @cpu: Cpu on which to allocate, -1 means current.
@@ -1230,6 +1290,10 @@ pt_buffer_setup_aux(struct perf_event *event, void **pages,
12301290

12311291
INIT_LIST_HEAD(&buf->tables);
12321292

1293+
ret = pt_buffer_try_single(buf, nr_pages);
1294+
if (!ret)
1295+
return buf;
1296+
12331297
ret = pt_buffer_init_topa(buf, cpu, nr_pages, GFP_KERNEL);
12341298
if (ret) {
12351299
kfree(buf);
@@ -1396,8 +1460,7 @@ void intel_pt_interrupt(void)
13961460
return;
13971461
}
13981462

1399-
pt_config_buffer(topa_to_page(buf->cur)->table, buf->cur_idx,
1400-
buf->output_off);
1463+
pt_config_buffer(buf);
14011464
pt_config_start(event);
14021465
}
14031466
}
@@ -1461,8 +1524,7 @@ static void pt_event_start(struct perf_event *event, int mode)
14611524
WRITE_ONCE(pt->handle_nmi, 1);
14621525
hwc->state = 0;
14631526

1464-
pt_config_buffer(topa_to_page(buf->cur)->table, buf->cur_idx,
1465-
buf->output_off);
1527+
pt_config_buffer(buf);
14661528
pt_config(event);
14671529

14681530
return;

arch/x86/events/intel/pt.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct pt_pmu {
6464
* @lost: if data was lost/truncated
6565
* @head: logical write offset inside the buffer
6666
* @snapshot: if this is for a snapshot/overwrite counter
67+
* @single: use Single Range Output instead of ToPA
6768
* @stop_pos: STOP topa entry index
6869
* @intr_pos: INT topa entry index
6970
* @stop_te: STOP topa entry pointer
@@ -80,6 +81,7 @@ struct pt_buffer {
8081
local_t data_size;
8182
local64_t head;
8283
bool snapshot;
84+
bool single;
8385
long stop_pos, intr_pos;
8486
struct topa_entry *stop_te, *intr_te;
8587
void **data_pages;

0 commit comments

Comments
 (0)