Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Ashutosh Dixit <ashutosh.dixit@intel.com>
To: intel-xe@lists.freedesktop.org
Subject: [Intel-xe] [PATCH 08/17] drm/xe/oa: Expose OA stream fd
Date: Thu,  7 Sep 2023 21:23:39 -0700	[thread overview]
Message-ID: <20230908042348.1592535-9-ashutosh.dixit@intel.com> (raw)
In-Reply-To: <20230908042348.1592535-1-ashutosh.dixit@intel.com>

The OA stream open ioctl returns an fd with its own file_operations for the
newly initialized OA stream. These file_operations allow userspace to
enable or disable the stream, as well as apply a different counter
configuration for the OA stream. Userspace can also poll for data
availability. OA stream initialization is completed in this commit by
enabling the OA stream. When sampling is enabled this starts a hrtimer
which periodically checks for data availablility.

Signed-off-by: Ashutosh Dixit <ashutosh.dixit@intel.com>
---
 drivers/gpu/drm/xe/xe_oa.c | 386 +++++++++++++++++++++++++++++++++++++
 1 file changed, 386 insertions(+)

diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
index 696294475ea8b..ba5ae2cc16065 100644
--- a/drivers/gpu/drm/xe/xe_oa.c
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -12,6 +12,7 @@
 #include <drm/drm_drv.h>
 
 #include "regs/xe_engine_regs.h"
+#include "regs/xe_gpu_commands.h"
 #include "regs/xe_gt_regs.h"
 #include "regs/xe_lrc_layout.h"
 #include "regs/xe_oa_regs.h"
@@ -26,6 +27,7 @@
 #include "xe_migrate.h"
 #include "xe_mmio.h"
 #include "xe_oa.h"
+#include "xe_pm.h"
 #include "xe_sched_job.h"
 #include "xe_vm.h"
 
@@ -33,6 +35,7 @@
 #define OA_TAKEN(tail, head)	(((tail) - (head)) & (OA_BUFFER_SIZE - 1))
 #define DEFAULT_POLL_FREQUENCY_HZ 200
 #define DEFAULT_POLL_PERIOD_NS (NSEC_PER_SEC / DEFAULT_POLL_FREQUENCY_HZ)
+#define INVALID_CTX_ID U32_MAX
 
 static u32 xe_oa_stream_paranoid = true;
 static int xe_oa_sample_rate_hard_limit;
@@ -129,6 +132,210 @@ static const struct xe_oa_regs *__oa_regs(struct xe_oa_stream *stream)
 	return &stream->hwe->oa_group->regs;
 }
 
+static u32 gen12_oa_hw_tail_read(struct xe_oa_stream *stream)
+{
+	return xe_mmio_read32(stream->gt, __oa_regs(stream)->oa_tail_ptr) &
+		GEN12_OAG_OATAILPTR_MASK;
+}
+
+#define oa_report_header_64bit(__s) \
+	((__s)->oa_buffer.format->header == HDR_64_BIT)
+
+static u64 oa_report_id(struct xe_oa_stream *stream, void *report)
+{
+	return oa_report_header_64bit(stream) ? *(u64 *)report : *(u32 *)report;
+}
+
+static u64 oa_timestamp(struct xe_oa_stream *stream, void *report)
+{
+	return oa_report_header_64bit(stream) ?
+		*((u64 *)report + 1) :
+		*((u32 *)report + 1);
+}
+
+static bool xe_oa_buffer_check_unlocked(struct xe_oa_stream *stream)
+{
+	u32 gtt_offset = xe_bo_ggtt_addr(stream->oa_buffer.bo);
+	int report_size = stream->oa_buffer.format->size;
+	u32 tail, hw_tail;
+	unsigned long flags;
+	bool pollin;
+	u32 partial_report_size;
+
+	/*
+	 * We have to consider the (unlikely) possibility that read() errors could result
+	 * in an OA buffer reset which might reset the head and tail state.
+	 */
+	spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
+
+	hw_tail = gen12_oa_hw_tail_read(stream);
+	hw_tail -= gtt_offset;
+
+	/*
+	 * The tail pointer increases in 64 byte increments, not in report_size
+	 * steps. Also the report size may not be a power of 2. Compute potentially
+	 * partially landed report in the OA buffer
+	 */
+	partial_report_size = OA_TAKEN(hw_tail, stream->oa_buffer.tail);
+	partial_report_size %= report_size;
+
+	/* Subtract partial amount off the tail */
+	hw_tail = OA_TAKEN(hw_tail, partial_report_size);
+
+	tail = hw_tail;
+
+	/*
+	 * Walk the stream backward until we find a report with report id and timestmap
+	 * not at 0. Since the circular buffer pointers progress by increments of 64 bytes
+	 * and that reports can be up to 256 bytes long, we can't tell whether a report
+	 * has fully landed in memory before the report id and timestamp of the following
+	 * report have effectively landed.
+	 *
+	 * This is assuming that the writes of the OA unit land in memory in the order
+	 * they were written to.  If not : (╯°□°)╯︵ ┻━┻
+	 */
+	while (OA_TAKEN(tail, stream->oa_buffer.tail) >= report_size) {
+		void *report = stream->oa_buffer.vaddr + tail;
+
+		if (oa_report_id(stream, report) ||
+		    oa_timestamp(stream, report))
+			break;
+
+		tail = OA_TAKEN(tail, report_size);
+	}
+
+	if (OA_TAKEN(hw_tail, tail) > report_size)
+		drm_dbg(&stream->oa->xe->drm,
+			"unlanded report(s) head=0x%x tail=0x%x hw_tail=0x%x\n",
+			stream->oa_buffer.head, tail, hw_tail);
+
+	stream->oa_buffer.tail = tail;
+
+	pollin = OA_TAKEN(stream->oa_buffer.tail,
+			  stream->oa_buffer.head) >= report_size;
+
+	spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
+
+	return pollin;
+}
+
+static enum hrtimer_restart xe_oa_poll_check_timer_cb(struct hrtimer *hrtimer)
+{
+	struct xe_oa_stream *stream =
+		container_of(hrtimer, typeof(*stream), poll_check_timer);
+
+	if (xe_oa_buffer_check_unlocked(stream)) {
+		stream->pollin = true;
+		wake_up(&stream->poll_wq);
+	}
+
+	hrtimer_forward_now(hrtimer, ns_to_ktime(stream->poll_oa_period));
+
+	return HRTIMER_RESTART;
+}
+
+static void xe_oa_init_oa_buffer(struct xe_oa_stream *stream)
+{
+	u32 gtt_offset = xe_bo_ggtt_addr(stream->oa_buffer.bo);
+	unsigned long flags;
+
+	spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
+
+	xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_status, 0);
+	xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_head_ptr,
+			gtt_offset & GEN12_OAG_OAHEADPTR_MASK);
+	stream->oa_buffer.head = 0;
+
+	/*
+	 * PRM says: "This MMIO must be set before the OATAILPTR register and after the
+	 * OAHEADPTR register. This is to enable proper functionality of the overflow bit".
+	 */
+	xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_buffer, gtt_offset |
+			OABUFFER_SIZE_16M | GEN12_OAG_OABUFFER_MEMORY_SELECT);
+	xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_tail_ptr,
+			gtt_offset & GEN12_OAG_OATAILPTR_MASK);
+
+	/* Mark that we need updated tail pointers to read from... */
+	stream->oa_buffer.tail = 0;
+
+	/*
+	 * Reset state used to recognise context switches, affecting which reports we will
+	 * forward to userspace while filtering for a single context.
+	 */
+	stream->oa_buffer.last_ctx_id = INVALID_CTX_ID;
+
+	spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
+
+	/* Zero out the OA buffer since we rely on zero report id and timestamp fields */
+	memset(stream->oa_buffer.vaddr, 0, stream->oa_buffer.bo->size);
+}
+
+static void xe_oa_enable(struct xe_oa_stream *stream)
+{
+	const struct xe_oa_regs *regs;
+	u32 val;
+
+	/*
+	 * If we don't want OA reports from the OA buffer, then we don't
+	 * even need to program the OAG unit.
+	 */
+	if (!stream->sample)
+		return;
+
+	xe_oa_init_oa_buffer(stream);
+
+	regs = __oa_regs(stream);
+	val = (stream->oa_buffer.format->format << regs->oa_ctrl_counter_format_shift) |
+	      GEN12_OAG_OACONTROL_OA_COUNTER_ENABLE;
+
+	xe_mmio_write32(stream->gt, regs->oa_ctrl, val);
+}
+
+static void xe_oa_disable(struct xe_oa_stream *stream)
+{
+	xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_ctrl, 0);
+	if (xe_mmio_wait32(stream->gt, __oa_regs(stream)->oa_ctrl,
+			   GEN12_OAG_OACONTROL_OA_COUNTER_ENABLE, 0, 50000, NULL, false))
+		drm_err(&stream->oa->xe->drm,
+			"wait for OA to be disabled timed out\n");
+
+	xe_mmio_write32(stream->gt, GEN12_OA_TLB_INV_CR, 1);
+	if (xe_mmio_wait32(stream->gt, GEN12_OA_TLB_INV_CR, 1, 0, 50000, NULL, false))
+		drm_err(&stream->oa->xe->drm,
+			"wait for OA tlb invalidate timed out\n");
+}
+
+static __poll_t xe_oa_poll_locked(struct xe_oa_stream *stream,
+				  struct file *file, poll_table *wait)
+{
+	__poll_t events = 0;
+
+	poll_wait(file, &stream->poll_wq, wait);
+
+	/*
+	 * We don't explicitly check whether there's something to read here since this
+	 * path may be hot depending on what else userspace is polling, or on the timeout
+	 * in use. We rely on hrtimer/xe_oa_poll_check_timer_cb to notify us when there
+	 * are samples to read.
+	 */
+	if (stream->pollin)
+		events |= EPOLLIN;
+
+	return events;
+}
+
+static __poll_t xe_oa_poll(struct file *file, poll_table *wait)
+{
+	struct xe_oa_stream *stream = file->private_data;
+	__poll_t ret;
+
+	mutex_lock(&stream->lock);
+	ret = xe_oa_poll_locked(stream, file, wait);
+	mutex_unlock(&stream->lock);
+
+	return ret;
+}
+
 static int xe_oa_submit_bb(struct xe_oa_stream *stream, struct xe_bb *bb)
 {
 	struct xe_hw_engine *hwe = stream->hwe;
@@ -327,6 +534,25 @@ static void xe_oa_disable_metric_set(struct xe_oa_stream *stream)
 	xe_mmio_rmw32(stream->gt, GEN12_SQCNT1, sqcnt1, 0);
 }
 
+static void xe_oa_stream_destroy(struct xe_oa_stream *stream)
+{
+	struct xe_oa_group *g = stream->hwe->oa_group;
+	struct xe_gt *gt = stream->hwe->gt;
+
+	if (WARN_ON(stream != g->exclusive_stream))
+		return;
+
+	/* Unset exclusive_stream first */
+	WRITE_ONCE(g->exclusive_stream, NULL);
+	xe_oa_disable_metric_set(stream);
+
+	XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL));
+	xe_device_mem_access_put(stream->oa->xe);
+
+	xe_oa_free_oa_buffer(stream);
+	xe_oa_free_configs(stream);
+}
+
 static int xe_oa_alloc_oa_buffer(struct xe_oa_stream *stream)
 {
 	struct xe_bo *bo;
@@ -508,6 +734,148 @@ static int xe_oa_enable_metric_set(struct xe_oa_stream *stream)
 	return xe_oa_emit_oa_config(stream);
 }
 
+static void xe_oa_stream_enable(struct xe_oa_stream *stream)
+{
+	stream->pollin = false;
+
+	xe_oa_enable(stream);
+
+	if (stream->sample)
+		hrtimer_start(&stream->poll_check_timer,
+			      ns_to_ktime(stream->poll_oa_period),
+			      HRTIMER_MODE_REL_PINNED);
+}
+
+static void xe_oa_stream_disable(struct xe_oa_stream *stream)
+{
+	xe_oa_disable(stream);
+
+	if (stream->sample)
+		hrtimer_cancel(&stream->poll_check_timer);
+}
+
+static void xe_oa_enable_locked(struct xe_oa_stream *stream)
+{
+	if (stream->enabled)
+		return;
+
+	stream->enabled = true;
+
+	xe_oa_stream_enable(stream);
+}
+
+static void xe_oa_disable_locked(struct xe_oa_stream *stream)
+{
+	if (!stream->enabled)
+		return;
+
+	stream->enabled = false;
+
+	xe_oa_stream_disable(stream);
+}
+
+static long xe_oa_config_locked(struct xe_oa_stream *stream,
+				unsigned long metrics_set)
+{
+	struct xe_oa_config *config;
+	long ret = stream->oa_config->id;
+
+	config = xe_oa_get_oa_config(stream->oa, metrics_set);
+	if (!config)
+		return -ENODEV;
+
+	if (config != stream->oa_config) {
+		int err;
+
+		/*
+		 * If OA is bound to a specific engine, emit the reconfiguration
+		 * inline from that engine. The update will then be ordered with
+		 * respect to submission on that engine.
+		 */
+		err = xe_oa_emit_oa_config(stream);
+		if (!err)
+			config = xchg(&stream->oa_config, config);
+		else
+			ret = err;
+	}
+
+	xe_oa_config_put(config);
+
+	return ret;
+}
+
+static long xe_oa_ioctl_locked(struct xe_oa_stream *stream,
+			       unsigned int cmd,
+			       unsigned long arg)
+{
+	switch (cmd) {
+	case XE_OA_IOCTL_ENABLE:
+		xe_oa_enable_locked(stream);
+		return 0;
+	case XE_OA_IOCTL_DISABLE:
+		xe_oa_disable_locked(stream);
+		return 0;
+	case XE_OA_IOCTL_CONFIG:
+		return xe_oa_config_locked(stream, arg);
+	}
+
+	return -EINVAL;
+}
+
+static long xe_oa_ioctl(struct file *file,
+			unsigned int cmd,
+			unsigned long arg)
+{
+	struct xe_oa_stream *stream = file->private_data;
+	long ret;
+
+	mutex_lock(&stream->lock);
+	ret = xe_oa_ioctl_locked(stream, cmd, arg);
+	mutex_unlock(&stream->lock);
+
+	return ret;
+}
+
+static void xe_oa_destroy_locked(struct xe_oa_stream *stream)
+{
+	if (stream->enabled)
+		xe_oa_disable_locked(stream);
+
+	xe_oa_stream_destroy(stream);
+
+	if (stream->exec_q)
+		xe_exec_queue_put(stream->exec_q);
+
+	kfree(stream);
+}
+
+static int xe_oa_release(struct inode *inode, struct file *file)
+{
+	struct xe_oa_stream *stream = file->private_data;
+	struct xe_gt *gt = stream->gt;
+
+	/*
+	 * Within this call, we know that the fd is being closed and we have no other
+	 * user of stream->lock. Use the perf lock to destroy the stream here.
+	 */
+	mutex_lock(&gt->oa.lock);
+	xe_oa_destroy_locked(stream);
+	mutex_unlock(&gt->oa.lock);
+
+	/* Release the reference the perf stream kept on the driver. */
+	drm_dev_put(&gt->tile->xe->drm);
+
+	return 0;
+}
+
+static const struct file_operations xe_oa_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.release	= xe_oa_release,
+	.poll		= xe_oa_poll,
+	.unlocked_ioctl	= xe_oa_ioctl,
+};
+
 static bool engine_supports_mi_query(struct xe_hw_engine *hwe)
 {
 	return hwe->class == XE_ENGINE_CLASS_RENDER;
@@ -636,6 +1004,7 @@ static int xe_oa_stream_init(struct xe_oa_stream *stream,
 	WRITE_ONCE(g->exclusive_stream, stream);
 
 	hrtimer_init(&stream->poll_check_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	stream->poll_check_timer.function = xe_oa_poll_check_timer_cb;
 	init_waitqueue_head(&stream->poll_wq);
 
 	spin_lock_init(&stream->oa_buffer.ptr_lock);
@@ -663,6 +1032,7 @@ xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
 	struct xe_file *xef = to_xe_file(file);
 	struct xe_oa_stream *stream = NULL;
 	struct xe_exec_queue *q = NULL;
+	unsigned long f_flags = 0;
 	bool privileged_op = true;
 	int stream_fd;
 	int ret;
@@ -715,10 +1085,26 @@ xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
 	if (ret)
 		goto err_free;
 
+	if (param->flags & XE_OA_FLAG_FD_CLOEXEC)
+		f_flags |= O_CLOEXEC;
+	if (param->flags & XE_OA_FLAG_FD_NONBLOCK)
+		f_flags |= O_NONBLOCK;
+
+	stream_fd = anon_inode_getfd("[xe_oa]", &xe_oa_fops, stream, f_flags);
+	if (stream_fd < 0) {
+		ret = stream_fd;
+		goto err_destroy;
+	}
+
+	if (!(param->flags & XE_OA_FLAG_DISABLED))
+		xe_oa_enable_locked(stream);
+
 	/* Hold a reference on the drm device till stream_fd is released */
 	drm_dev_get(&oa->xe->drm);
 
 	return stream_fd;
+err_destroy:
+	xe_oa_stream_destroy(stream);
 err_free:
 	kfree(stream);
 err_exec_q:
-- 
2.41.0


  parent reply	other threads:[~2023-09-08  4:24 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-08  4:23 [Intel-xe] [PATCH 00/17] Add OA functionality to Xe Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 01/17] drm/xe/oa: Introduce OA (observability architecture) uapi Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 02/17] drm/xe/oa: Add OA types Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 03/17] drm/xe/oa: Add registers and GPU commands used by OA Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 04/17] drm/xe/oa: Module init/exit and probe/remove Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 05/17] drm/xe/oa: Add/remove config ioctl's Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 06/17] drm/xe/oa: Start implementing OA stream open ioctl Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 07/17] drm/xe/oa: OA stream initialization Ashutosh Dixit
2023-09-08  4:23 ` Ashutosh Dixit [this message]
2023-09-08  4:23 ` [Intel-xe] [PATCH 09/17] drm/xe/oa: Read file_operation Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 10/17] drm/xe/oa: Implement queries Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 11/17] drm/xe/oa: Override GuC RC with OA on PVC Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 12/17] drm/xe/perf: "Perf" layer to support multiple perf counter stream types Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 13/17] drm/xe/oa: Multiplex PERF ops through a single PERF ioctl Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 14/17] drm/xe/oa: Simplify OA configs in uapi Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 15/17] drm/xe/oa: Remove OA format names from OA uapi Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 16/17] drm/xe/oa: Make xe_oa_timestamp_frequency per gt Ashutosh Dixit
2023-09-08  4:23 ` [Intel-xe] [PATCH 17/17] drm/xe/oa: Remove filtering reports on context id Ashutosh Dixit
2023-09-09  1:24   ` Dixit, Ashutosh
2023-11-13 20:22     ` Dixit, Ashutosh
2023-09-08  4:26 ` [Intel-xe] ✓ CI.Patch_applied: success for Add OA functionality to Xe (rev5) Patchwork
2023-09-08  4:27 ` [Intel-xe] ✗ CI.checkpatch: warning " Patchwork
2023-09-08  4:28 ` [Intel-xe] ✓ CI.KUnit: success " Patchwork
2023-09-08  4:35 ` [Intel-xe] ✓ CI.Build: " Patchwork
2023-09-08  4:35 ` [Intel-xe] ✗ CI.Hooks: failure " Patchwork
2023-09-19 21:18   ` Dixit, Ashutosh
2023-09-08  4:36 ` [Intel-xe] ✓ CI.checksparse: success " Patchwork
2023-09-08  5:10 ` [Intel-xe] ✗ CI.BAT: failure " Patchwork
2023-09-19 21:27   ` Dixit, Ashutosh

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230908042348.1592535-9-ashutosh.dixit@intel.com \
    --to=ashutosh.dixit@intel.com \
    --cc=intel-xe@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox