Merge branch 'x86-cpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / arch / x86 / events / intel / pt.c
index 26af1f19f153f704eb10a09deab76c2a3fbc1c5a..74e80ed9c6c474551b57c6d8b4d548aeb3ea8e4d 100644 (file)
@@ -545,33 +545,62 @@ static void pt_config_buffer(void *buf, unsigned int topa_idx,
        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
- * @table:     actual ToPA table entries, as understood by PT hardware
+ * struct topa - ToPA metadata
  * @list:      linkage to struct pt_buffer's list of tables
- * @phys:      physical address of this page
  * @offset:    offset of the first entry in this table in the buffer
  * @size:      total size of all entries in this table
  * @last:      index of the last initialized entry in this table
+ * @z_count:   how many times the first entry repeats
  */
 struct topa {
-       struct topa_entry       table[TENTS_PER_PAGE];
        struct list_head        list;
-       u64                     phys;
        u64                     offset;
        size_t                  size;
        int                     last;
+       unsigned int            z_count;
 };
 
+/*
+ * 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);
+}
+
+static inline phys_addr_t topa_pfn(struct topa *topa)
+{
+       return PFN_DOWN(virt_to_phys(topa_to_page(topa)));
+}
+
 /* 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_PAGES(t, i) (1 << TOPA_ENTRY((t), (i))->size)
 
 /**
  * topa_alloc() - allocate page-sized ToPA table
@@ -583,27 +612,26 @@ struct topa {
 static struct topa *topa_alloc(int cpu, gfp_t gfp)
 {
        int node = cpu_to_node(cpu);
-       struct topa *topa;
+       struct topa_page *tp;
        struct page *p;
 
        p = alloc_pages_node(node, gfp | __GFP_ZERO, 0);
        if (!p)
                return NULL;
 
-       topa = page_address(p);
-       topa->last = 0;
-       topa->phys = page_to_phys(p);
+       tp = page_address(p);
+       tp->topa.last = 0;
 
        /*
         * In case of singe-entry ToPA, always put the self-referencing END
         * link as the 2nd entry in the table
         */
        if (!intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries)) {
-               TOPA_ENTRY(topa, 1)->base = topa->phys >> TOPA_SHIFT;
-               TOPA_ENTRY(topa, 1)->end = 1;
+               TOPA_ENTRY(&tp->topa, 1)->base = page_to_phys(p);
+               TOPA_ENTRY(&tp->topa, 1)->end = 1;
        }
 
-       return topa;
+       return &tp->topa;
 }
 
 /**
@@ -643,7 +671,7 @@ static void topa_insert_table(struct pt_buffer *buf, struct topa *topa)
 
        BUG_ON(last->last != TENTS_PER_PAGE - 1);
 
-       TOPA_ENTRY(last, -1)->base = topa->phys >> TOPA_SHIFT;
+       TOPA_ENTRY(last, -1)->base = topa_pfn(topa);
        TOPA_ENTRY(last, -1)->end = 1;
 }
 
@@ -670,7 +698,7 @@ static bool topa_table_full(struct topa *topa)
  *
  * Return:     0 on success or error code.
  */
-static int topa_insert_pages(struct pt_buffer *buf, gfp_t gfp)
+static int topa_insert_pages(struct pt_buffer *buf, int cpu, gfp_t gfp)
 {
        struct topa *topa = buf->last;
        int order = 0;
@@ -681,13 +709,18 @@ static int topa_insert_pages(struct pt_buffer *buf, gfp_t gfp)
                order = page_private(p);
 
        if (topa_table_full(topa)) {
-               topa = topa_alloc(buf->cpu, gfp);
+               topa = topa_alloc(cpu, gfp);
                if (!topa)
                        return -ENOMEM;
 
                topa_insert_table(buf, topa);
        }
 
+       if (topa->z_count == topa->last - 1) {
+               if (order == TOPA_ENTRY(topa, topa->last - 1)->size)
+                       topa->z_count++;
+       }
+
        TOPA_ENTRY(topa, -1)->base = page_to_phys(p) >> TOPA_SHIFT;
        TOPA_ENTRY(topa, -1)->size = order;
        if (!buf->snapshot &&
@@ -713,23 +746,26 @@ static void pt_topa_dump(struct pt_buffer *buf)
        struct topa *topa;
 
        list_for_each_entry(topa, &buf->tables, list) {
+               struct topa_page *tp = topa_to_page(topa);
                int i;
 
-               pr_debug("# table @%p (%016Lx), off %llx size %zx\n", topa->table,
-                        topa->phys, topa->offset, topa->size);
+               pr_debug("# table @%p, off %llx size %zx\n", tp->table,
+                        topa->offset, topa->size);
                for (i = 0; i < TENTS_PER_PAGE; i++) {
                        pr_debug("# entry @%p (%lx sz %u %c%c%c) raw=%16llx\n",
-                                &topa->table[i],
-                                (unsigned long)topa->table[i].base << TOPA_SHIFT,
-                                sizes(topa->table[i].size),
-                                topa->table[i].end ?  'E' : ' ',
-                                topa->table[i].intr ? 'I' : ' ',
-                                topa->table[i].stop ? 'S' : ' ',
-                                *(u64 *)&topa->table[i]);
+                                &tp->table[i],
+                                (unsigned long)tp->table[i].base << TOPA_SHIFT,
+                                sizes(tp->table[i].size),
+                                tp->table[i].end ?  'E' : ' ',
+                                tp->table[i].intr ? 'I' : ' ',
+                                tp->table[i].stop ? 'S' : ' ',
+                                *(u64 *)&tp->table[i]);
                        if ((intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries) &&
-                            topa->table[i].stop) ||
-                           topa->table[i].end)
+                            tp->table[i].stop) ||
+                           tp->table[i].end)
                                break;
+                       if (!i && topa->z_count)
+                               i += topa->z_count;
                }
        }
 }
@@ -771,7 +807,7 @@ static void pt_update_head(struct pt *pt)
 
        /* offset of the current output region within this table */
        for (topa_idx = 0; topa_idx < buf->cur_idx; topa_idx++)
-               base += sizes(buf->cur->table[topa_idx].size);
+               base += TOPA_ENTRY_SIZE(buf->cur, topa_idx);
 
        if (buf->snapshot) {
                local_set(&buf->data_size, base);
@@ -791,7 +827,7 @@ static void pt_update_head(struct pt *pt)
  */
 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);
 }
 
 /**
@@ -800,7 +836,7 @@ static void *pt_buffer_region(struct pt_buffer *buf)
  */
 static size_t pt_buffer_region_size(struct pt_buffer *buf)
 {
-       return sizes(buf->cur->table[buf->cur_idx].size);
+       return TOPA_ENTRY_SIZE(buf->cur, buf->cur_idx);
 }
 
 /**
@@ -830,7 +866,7 @@ static void pt_handle_status(struct pt *pt)
                 * know.
                 */
                if (!intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries) ||
-                   buf->output_off == sizes(TOPA_ENTRY(buf->cur, buf->cur_idx)->size)) {
+                   buf->output_off == pt_buffer_region_size(buf)) {
                        perf_aux_output_flag(&pt->handle,
                                             PERF_AUX_FLAG_TRUNCATED);
                        advance++;
@@ -868,9 +904,11 @@ static void pt_handle_status(struct pt *pt)
 static void pt_read_offset(struct pt_buffer *buf)
 {
        u64 offset, base_topa;
+       struct topa_page *tp;
 
        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);
        /* offset within current output region */
@@ -879,29 +917,97 @@ static void pt_read_offset(struct pt_buffer *buf)
        buf->cur_idx = (offset & 0xffffff80) >> 7;
 }
 
-/**
- * pt_topa_next_entry() - obtain index of the first page in the next ToPA entry
- * @buf:       PT buffer.
- * @pg:                Page offset in the buffer.
- *
- * When advancing to the next output region (ToPA entry), given a page offset
- * into the buffer, we need to find the offset of the first page in the next
- * region.
- */
-static unsigned int pt_topa_next_entry(struct pt_buffer *buf, unsigned int pg)
+static struct topa_entry *
+pt_topa_entry_for_page(struct pt_buffer *buf, unsigned int pg)
 {
-       struct topa_entry *te = buf->topa_index[pg];
+       struct topa_page *tp;
+       struct topa *topa;
+       unsigned int idx, cur_pg = 0, z_pg = 0, start_idx = 0;
 
-       /* one region */
-       if (buf->first == buf->last && buf->first->last == 1)
-               return pg;
+       /*
+        * Indicates a bug in the caller.
+        */
+       if (WARN_ON_ONCE(pg >= buf->nr_pages))
+               return NULL;
+
+       /*
+        * First, find the ToPA table where @pg fits. With high
+        * order allocations, there shouldn't be many of these.
+        */
+       list_for_each_entry(topa, &buf->tables, list) {
+               if (topa->offset + topa->size > pg << PAGE_SHIFT)
+                       goto found;
+       }
+
+       /*
+        * Hitting this means we have a problem in the ToPA
+        * allocation code.
+        */
+       WARN_ON_ONCE(1);
 
-       do {
-               pg++;
-               pg &= buf->nr_pages - 1;
-       } while (buf->topa_index[pg] == te);
+       return NULL;
 
-       return pg;
+found:
+       /*
+        * Indicates a problem in the ToPA allocation code.
+        */
+       if (WARN_ON_ONCE(topa->last == -1))
+               return NULL;
+
+       tp = topa_to_page(topa);
+       cur_pg = PFN_DOWN(topa->offset);
+       if (topa->z_count) {
+               z_pg = TOPA_ENTRY_PAGES(topa, 0) * (topa->z_count + 1);
+               start_idx = topa->z_count + 1;
+       }
+
+       /*
+        * Multiple entries at the beginning of the table have the same size,
+        * ideally all of them; if @pg falls there, the search is done.
+        */
+       if (pg >= cur_pg && pg < cur_pg + z_pg) {
+               idx = (pg - cur_pg) / TOPA_ENTRY_PAGES(topa, 0);
+               return &tp->table[idx];
+       }
+
+       /*
+        * Otherwise, slow path: iterate through the remaining entries.
+        */
+       for (idx = start_idx, cur_pg += z_pg; idx < topa->last; idx++) {
+               if (cur_pg + TOPA_ENTRY_PAGES(topa, idx) > pg)
+                       return &tp->table[idx];
+
+               cur_pg += TOPA_ENTRY_PAGES(topa, idx);
+       }
+
+       /*
+        * Means we couldn't find a ToPA entry in the table that does match.
+        */
+       WARN_ON_ONCE(1);
+
+       return NULL;
+}
+
+static struct topa_entry *
+pt_topa_prev_entry(struct pt_buffer *buf, struct topa_entry *te)
+{
+       unsigned long table = (unsigned long)te & ~(PAGE_SIZE - 1);
+       struct topa_page *tp;
+       struct topa *topa;
+
+       tp = (struct topa_page *)table;
+       if (tp->table != te)
+               return --te;
+
+       topa = &tp->topa;
+       if (topa == buf->first)
+               topa = buf->last;
+       else
+               topa = list_prev_entry(topa, list);
+
+       tp = topa_to_page(topa);
+
+       return &tp->table[topa->last - 1];
 }
 
 /**
@@ -925,8 +1031,7 @@ static int pt_buffer_reset_markers(struct pt_buffer *buf,
        unsigned long idx, npages, wakeup;
 
        /* can't stop in the middle of an output region */
-       if (buf->output_off + handle->size + 1 <
-           sizes(TOPA_ENTRY(buf->cur, buf->cur_idx)->size)) {
+       if (buf->output_off + handle->size + 1 < pt_buffer_region_size(buf)) {
                perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
                return -EINVAL;
        }
@@ -937,9 +1042,13 @@ static int pt_buffer_reset_markers(struct pt_buffer *buf,
                return 0;
 
        /* clear STOP and INT from current entry */
-       buf->topa_index[buf->stop_pos]->stop = 0;
-       buf->topa_index[buf->stop_pos]->intr = 0;
-       buf->topa_index[buf->intr_pos]->intr = 0;
+       if (buf->stop_te) {
+               buf->stop_te->stop = 0;
+               buf->stop_te->intr = 0;
+       }
+
+       if (buf->intr_te)
+               buf->intr_te->intr = 0;
 
        /* how many pages till the STOP marker */
        npages = handle->size >> PAGE_SHIFT;
@@ -950,7 +1059,12 @@ static int pt_buffer_reset_markers(struct pt_buffer *buf,
 
        idx = (head >> PAGE_SHIFT) + npages;
        idx &= buf->nr_pages - 1;
-       buf->stop_pos = idx;
+
+       if (idx != buf->stop_pos) {
+               buf->stop_pos = idx;
+               buf->stop_te = pt_topa_entry_for_page(buf, idx);
+               buf->stop_te = pt_topa_prev_entry(buf, buf->stop_te);
+       }
 
        wakeup = handle->wakeup >> PAGE_SHIFT;
 
@@ -960,50 +1074,19 @@ static int pt_buffer_reset_markers(struct pt_buffer *buf,
                idx = wakeup;
 
        idx &= buf->nr_pages - 1;
-       buf->intr_pos = idx;
+       if (idx != buf->intr_pos) {
+               buf->intr_pos = idx;
+               buf->intr_te = pt_topa_entry_for_page(buf, idx);
+               buf->intr_te = pt_topa_prev_entry(buf, buf->intr_te);
+       }
 
-       buf->topa_index[buf->stop_pos]->stop = 1;
-       buf->topa_index[buf->stop_pos]->intr = 1;
-       buf->topa_index[buf->intr_pos]->intr = 1;
+       buf->stop_te->stop = 1;
+       buf->stop_te->intr = 1;
+       buf->intr_te->intr = 1;
 
        return 0;
 }
 
-/**
- * pt_buffer_setup_topa_index() - build topa_index[] table of regions
- * @buf:       PT buffer.
- *
- * topa_index[] references output regions indexed by offset into the
- * buffer for purposes of quick reverse lookup.
- */
-static void pt_buffer_setup_topa_index(struct pt_buffer *buf)
-{
-       struct topa *cur = buf->first, *prev = buf->last;
-       struct topa_entry *te_cur = TOPA_ENTRY(cur, 0),
-               *te_prev = TOPA_ENTRY(prev, prev->last - 1);
-       int pg = 0, idx = 0;
-
-       while (pg < buf->nr_pages) {
-               int tidx;
-
-               /* pages within one topa entry */
-               for (tidx = 0; tidx < 1 << te_cur->size; tidx++, pg++)
-                       buf->topa_index[pg] = te_prev;
-
-               te_prev = te_cur;
-
-               if (idx == cur->last - 1) {
-                       /* advance to next topa table */
-                       idx = 0;
-                       cur = list_entry(cur->list.next, struct topa, list);
-               } else {
-                       idx++;
-               }
-               te_cur = TOPA_ENTRY(cur, idx);
-       }
-
-}
-
 /**
  * pt_buffer_reset_offsets() - adjust buffer's write pointers from aux_head
  * @buf:       PT buffer.
@@ -1021,18 +1104,20 @@ static void pt_buffer_setup_topa_index(struct pt_buffer *buf)
  */
 static void pt_buffer_reset_offsets(struct pt_buffer *buf, unsigned long head)
 {
+       struct topa_page *cur_tp;
+       struct topa_entry *te;
        int pg;
 
        if (buf->snapshot)
                head &= (buf->nr_pages << PAGE_SHIFT) - 1;
 
        pg = (head >> PAGE_SHIFT) & (buf->nr_pages - 1);
-       pg = pt_topa_next_entry(buf, pg);
+       te = pt_topa_entry_for_page(buf, pg);
 
-       buf->cur = (struct topa *)((unsigned long)buf->topa_index[pg] & PAGE_MASK);
-       buf->cur_idx = ((unsigned long)buf->topa_index[pg] -
-                       (unsigned long)buf->cur) / sizeof(struct topa_entry);
-       buf->output_off = head & (sizes(buf->cur->table[buf->cur_idx].size) - 1);
+       cur_tp = topa_entry_to_page(te);
+       buf->cur = &cur_tp->topa;
+       buf->cur_idx = te - TOPA_ENTRY(buf->cur, 0);
+       buf->output_off = head & (pt_buffer_region_size(buf) - 1);
 
        local64_set(&buf->head, head);
        local_set(&buf->data_size, 0);
@@ -1061,31 +1146,29 @@ static void pt_buffer_fini_topa(struct pt_buffer *buf)
  * @size:      Total size of all regions within this ToPA.
  * @gfp:       Allocation flags.
  */
-static int pt_buffer_init_topa(struct pt_buffer *buf, unsigned long nr_pages,
-                              gfp_t gfp)
+static int pt_buffer_init_topa(struct pt_buffer *buf, int cpu,
+                              unsigned long nr_pages, gfp_t gfp)
 {
        struct topa *topa;
        int err;
 
-       topa = topa_alloc(buf->cpu, gfp);
+       topa = topa_alloc(cpu, gfp);
        if (!topa)
                return -ENOMEM;
 
        topa_insert_table(buf, topa);
 
        while (buf->nr_pages < nr_pages) {
-               err = topa_insert_pages(buf, gfp);
+               err = topa_insert_pages(buf, cpu, gfp);
                if (err) {
                        pt_buffer_fini_topa(buf);
                        return -ENOMEM;
                }
        }
 
-       pt_buffer_setup_topa_index(buf);
-
        /* link last table to the first one, unless we're double buffering */
        if (intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries)) {
-               TOPA_ENTRY(buf->last, -1)->base = buf->first->phys >> TOPA_SHIFT;
+               TOPA_ENTRY(buf->last, -1)->base = topa_pfn(buf->first);
                TOPA_ENTRY(buf->last, -1)->end = 1;
        }
 
@@ -1119,18 +1202,18 @@ pt_buffer_setup_aux(struct perf_event *event, void **pages,
                cpu = raw_smp_processor_id();
        node = cpu_to_node(cpu);
 
-       buf = kzalloc_node(offsetof(struct pt_buffer, topa_index[nr_pages]),
-                          GFP_KERNEL, node);
+       buf = kzalloc_node(sizeof(struct pt_buffer), GFP_KERNEL, node);
        if (!buf)
                return NULL;
 
-       buf->cpu = cpu;
        buf->snapshot = snapshot;
        buf->data_pages = pages;
+       buf->stop_pos = -1;
+       buf->intr_pos = -1;
 
        INIT_LIST_HEAD(&buf->tables);
 
-       ret = pt_buffer_init_topa(buf, nr_pages, GFP_KERNEL);
+       ret = pt_buffer_init_topa(buf, cpu, nr_pages, GFP_KERNEL);
        if (ret) {
                kfree(buf);
                return NULL;
@@ -1296,7 +1379,7 @@ void intel_pt_interrupt(void)
                        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);
                pt_config(event);
        }
@@ -1361,7 +1444,7 @@ static void pt_event_start(struct perf_event *event, int mode)
        WRITE_ONCE(pt->handle_nmi, 1);
        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);
        pt_config(event);
 
@@ -1481,6 +1564,11 @@ void cpu_emergency_stop_pt(void)
                pt_event_stop(pt->handle.event, PERF_EF_UPDATE);
 }
 
+int is_intel_pt_event(struct perf_event *event)
+{
+       return event->pmu == &pt_pmu.pmu;
+}
+
 static __init int pt_init(void)
 {
        int ret, cpu, prior_warn = 0;