Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 66d21901 authored by Alexander Shishkin's avatar Alexander Shishkin Committed by Ingo Molnar
Browse files

perf/x86/intel/pt: Move transaction start/stop to PMU start/stop callbacks



As per AUX buffer management requirement, AUX output has to happen between
pmu::start and pmu::stop calls so that perf_event_stop() actually stops it
and therefore perf can free the AUX data after it has called pmu::stop.

This patch moves perf_aux_output_{begin,end} from pt_event_{add,del} to
pt_event_{start,stop}. As a bonus, we get rid of pt_buffer_is_full(),
which is already taken care of by perf_aux_output_begin() anyway.

Signed-off-by: default avatarAlexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
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>
Cc: vince@deater.net
Link: http://lkml.kernel.org/r/1457098969-21595-5-git-send-email-alexander.shishkin@linux.intel.com


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent af5bb4ed
Loading
Loading
Loading
Loading
+27 −58
Original line number Original line Diff line number Diff line
@@ -905,26 +905,6 @@ static void pt_buffer_free_aux(void *data)
	kfree(buf);
	kfree(buf);
}
}


/**
 * pt_buffer_is_full() - check if the buffer is full
 * @buf:	PT buffer.
 * @pt:		Per-cpu pt handle.
 *
 * If the user hasn't read data from the output region that aux_head
 * points to, the buffer is considered full: the user needs to read at
 * least this region and update aux_tail to point past it.
 */
static bool pt_buffer_is_full(struct pt_buffer *buf, struct pt *pt)
{
	if (buf->snapshot)
		return false;

	if (local_read(&buf->data_size) >= pt->handle.size)
		return true;

	return false;
}

/**
/**
 * intel_pt_interrupt() - PT PMI handler
 * intel_pt_interrupt() - PT PMI handler
 */
 */
@@ -989,20 +969,33 @@ void intel_pt_interrupt(void)


static void pt_event_start(struct perf_event *event, int mode)
static void pt_event_start(struct perf_event *event, int mode)
{
{
	struct hw_perf_event *hwc = &event->hw;
	struct pt *pt = this_cpu_ptr(&pt_ctx);
	struct pt *pt = this_cpu_ptr(&pt_ctx);
	struct pt_buffer *buf = perf_get_aux(&pt->handle);
	struct pt_buffer *buf;


	if (!buf || pt_buffer_is_full(buf, pt)) {
	buf = perf_aux_output_begin(&pt->handle, event);
		event->hw.state = PERF_HES_STOPPED;
	if (!buf)
		return;
		goto fail_stop;

	pt_buffer_reset_offsets(buf, pt->handle.head);
	if (!buf->snapshot) {
		if (pt_buffer_reset_markers(buf, &pt->handle))
			goto fail_end_stop;
	}
	}


	ACCESS_ONCE(pt->handle_nmi) = 1;
	ACCESS_ONCE(pt->handle_nmi) = 1;
	event->hw.state = 0;
	hwc->state = 0;


	pt_config_buffer(buf->cur->table, buf->cur_idx,
	pt_config_buffer(buf->cur->table, buf->cur_idx,
			 buf->output_off);
			 buf->output_off);
	pt_config(event);
	pt_config(event);

	return;

fail_end_stop:
	perf_aux_output_end(&pt->handle, 0, true);
fail_stop:
	hwc->state = PERF_HES_STOPPED;
}
}


static void pt_event_stop(struct perf_event *event, int mode)
static void pt_event_stop(struct perf_event *event, int mode)
@@ -1035,19 +1028,7 @@ static void pt_event_stop(struct perf_event *event, int mode)
		pt_handle_status(pt);
		pt_handle_status(pt);


		pt_update_head(pt);
		pt_update_head(pt);
	}
}


static void pt_event_del(struct perf_event *event, int mode)
{
	struct pt *pt = this_cpu_ptr(&pt_ctx);
	struct pt_buffer *buf;

	pt_event_stop(event, PERF_EF_UPDATE);

	buf = perf_get_aux(&pt->handle);

	if (buf) {
		if (buf->snapshot)
		if (buf->snapshot)
			pt->handle.head =
			pt->handle.head =
				local_xchg(&buf->data_size,
				local_xchg(&buf->data_size,
@@ -1057,9 +1038,13 @@ static void pt_event_del(struct perf_event *event, int mode)
	}
	}
}
}


static void pt_event_del(struct perf_event *event, int mode)
{
	pt_event_stop(event, PERF_EF_UPDATE);
}

static int pt_event_add(struct perf_event *event, int mode)
static int pt_event_add(struct perf_event *event, int mode)
{
{
	struct pt_buffer *buf;
	struct pt *pt = this_cpu_ptr(&pt_ctx);
	struct pt *pt = this_cpu_ptr(&pt_ctx);
	struct hw_perf_event *hwc = &event->hw;
	struct hw_perf_event *hwc = &event->hw;
	int ret = -EBUSY;
	int ret = -EBUSY;
@@ -1067,34 +1052,18 @@ static int pt_event_add(struct perf_event *event, int mode)
	if (pt->handle.event)
	if (pt->handle.event)
		goto fail;
		goto fail;


	buf = perf_aux_output_begin(&pt->handle, event);
	ret = -EINVAL;
	if (!buf)
		goto fail_stop;

	pt_buffer_reset_offsets(buf, pt->handle.head);
	if (!buf->snapshot) {
		ret = pt_buffer_reset_markers(buf, &pt->handle);
		if (ret)
			goto fail_end_stop;
	}

	if (mode & PERF_EF_START) {
	if (mode & PERF_EF_START) {
		pt_event_start(event, 0);
		pt_event_start(event, 0);
		ret = -EBUSY;
		ret = -EINVAL;
		if (hwc->state == PERF_HES_STOPPED)
		if (hwc->state == PERF_HES_STOPPED)
			goto fail_end_stop;
			goto fail;
	} else {
	} else {
		hwc->state = PERF_HES_STOPPED;
		hwc->state = PERF_HES_STOPPED;
	}
	}


	return 0;
	ret = 0;

fail_end_stop:
	perf_aux_output_end(&pt->handle, 0, true);
fail_stop:
	hwc->state = PERF_HES_STOPPED;
fail:
fail:

	return ret;
	return ret;
}
}