diff mbox series

[2/2] drm/i915/guc: handle GuC messages received with CTB disabled

Message ID 20190619214351.12000-3-daniele.ceraolospurio@intel.com (mailing list archive)
State New, archived
Headers show
Series GuC messaging enable/disable tweaks | expand

Commit Message

Daniele Ceraolo Spurio June 19, 2019, 9:43 p.m. UTC
There is a very small chance of triggering a log flush event when
enabling or disabling CT buffers. Events triggered while CT buffers
are disabled are logged in the SCRATCH_15 register using the same bits
used in the CT message payload. Since our communication channel with
GuC is turned off, we can save the message and handle it after we turn
it back on.
GuC should be idle and not generate more events in the meantime because
we're not talking to it.

Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/intel_guc.h |  5 +++
 drivers/gpu/drm/i915/intel_uc.c  | 73 ++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+)

Comments

Chris Wilson June 20, 2019, 1:48 p.m. UTC | #1
Quoting Daniele Ceraolo Spurio (2019-06-19 22:43:51)
> +/*
> + * Events triggered while CT buffers are disabled are logged in the SCRATCH_15
> + * register using the same bits used in the CT message payload. Since our
> + * communication channel with guc is turned off at this point, we can save the
> + * message and handle it after we turn it back on.
> + */
> +static void guc_clear_mmio_msg(struct intel_guc *guc)
> +{
> +       intel_uncore_write(&guc_to_i915(guc)->uncore, SOFT_SCRATCH(15), 0);

Should the register be cleared on intel_guc_reset()? Otherwise, we would
be associating the stale msg from an earlier guc instance with the
current one.

That would mean clear_mmio_msg would want to be called from
guc_stop_communication not just guc_disable_communication.
-Chris
Daniele Ceraolo Spurio June 20, 2019, 5:55 p.m. UTC | #2
On 6/20/19 6:48 AM, Chris Wilson wrote:
> Quoting Daniele Ceraolo Spurio (2019-06-19 22:43:51)
>> +/*
>> + * Events triggered while CT buffers are disabled are logged in the SCRATCH_15
>> + * register using the same bits used in the CT message payload. Since our
>> + * communication channel with guc is turned off at this point, we can save the
>> + * message and handle it after we turn it back on.
>> + */
>> +static void guc_clear_mmio_msg(struct intel_guc *guc)
>> +{
>> +       intel_uncore_write(&guc_to_i915(guc)->uncore, SOFT_SCRATCH(15), 0);
> 
> Should the register be cleared on intel_guc_reset()? Otherwise, we would
> be associating the stale msg from an earlier guc instance with the
> current one.
> 
> That would mean clear_mmio_msg would want to be called from
> guc_stop_communication not just guc_disable_communication.
> -Chris
> 

The register is reset by the HW as part of GuC reset (I've verified this 
on SKL). Still, explicitly clearing it won't hurt and it'll cover us if 
HW reset fails, so no problem in adding the call in.

Daniele
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index 08c906abdfa2..d6a75bc3d7f4 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -88,6 +88,9 @@  struct intel_guc {
 		enum forcewake_domains fw_domains;
 	} send_regs;
 
+	/* Store msg (e.g. log flush) that we see while CTBs are disabled */
+	u32 mmio_msg;
+
 	/* To serialize the intel_guc_send actions */
 	struct mutex send_mutex;
 
@@ -181,6 +184,8 @@  static inline bool intel_guc_is_loaded(struct intel_guc *guc)
 static inline int intel_guc_sanitize(struct intel_guc *guc)
 {
 	intel_uc_fw_sanitize(&guc->fw);
+	guc->mmio_msg = 0;
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c
index c7f82c944dd6..38c87885aae3 100644
--- a/drivers/gpu/drm/i915/intel_uc.c
+++ b/drivers/gpu/drm/i915/intel_uc.c
@@ -218,6 +218,54 @@  static void guc_free_load_err_log(struct intel_guc *guc)
 		i915_gem_object_put(guc->load_err_log);
 }
 
+/*
+ * Events triggered while CT buffers are disabled are logged in the SCRATCH_15
+ * register using the same bits used in the CT message payload. Since our
+ * communication channel with guc is turned off at this point, we can save the
+ * message and handle it after we turn it back on.
+ */
+static void guc_clear_mmio_msg(struct intel_guc *guc)
+{
+	intel_uncore_write(&guc_to_i915(guc)->uncore, SOFT_SCRATCH(15), 0);
+}
+
+static void guc_get_mmio_msg(struct intel_guc *guc)
+{
+	u32 val;
+
+	spin_lock_irq(&guc->irq_lock);
+
+	val = intel_uncore_read(&guc_to_i915(guc)->uncore, SOFT_SCRATCH(15));
+	guc->mmio_msg |= val & guc->msg_enabled_mask;
+
+	/*
+	 * clear all events, including the ones we're not currently servicing,
+	 * to make sure we don't try to process a stale message if we enable
+	 * handling of more events later.
+	 */
+	guc_clear_mmio_msg(guc);
+
+	spin_unlock_irq(&guc->irq_lock);
+}
+
+static void guc_handle_mmio_msg(struct intel_guc *guc)
+{
+	struct drm_i915_private *i915 = guc_to_i915(guc);
+
+	/* we need communication to be enabled to reply to GuC */
+	GEM_BUG_ON(guc->handler == intel_guc_to_host_event_handler_nop);
+
+	if (!guc->mmio_msg)
+		return;
+
+	spin_lock_irq(&i915->irq_lock);
+	intel_guc_to_host_process_recv_msg(guc, &guc->mmio_msg, 1);
+	spin_unlock_irq(&i915->irq_lock);
+
+	guc->mmio_msg = 0;
+}
+
+
 static void guc_reset_interrupts(struct intel_guc *guc)
 {
 	guc->interrupts.reset(guc_to_i915(guc));
@@ -235,6 +283,7 @@  static void guc_disable_interrupts(struct intel_guc *guc)
 
 static int guc_enable_communication(struct intel_guc *guc)
 {
+	struct drm_i915_private *i915 = guc_to_i915(guc);
 	int ret;
 
 	ret = intel_guc_ct_enable(&guc->ct);
@@ -244,8 +293,17 @@  static int guc_enable_communication(struct intel_guc *guc)
 	guc->send = intel_guc_send_ct;
 	guc->handler = intel_guc_to_host_event_handler_ct;
 
+	/* check for mmio messages received before/during the CT enable */
+	guc_get_mmio_msg(guc);
+	guc_handle_mmio_msg(guc);
+
 	guc_enable_interrupts(guc);
 
+	/* check for CT messages received before we enabled interrupts */
+	spin_lock_irq(&i915->irq_lock);
+	intel_guc_to_host_event_handler_ct(guc);
+	spin_unlock_irq(&i915->irq_lock);
+
 	DRM_INFO("GuC communication enabled\n");
 
 	return 0;
@@ -261,6 +319,13 @@  static void guc_stop_communication(struct intel_guc *guc)
 
 static void guc_disable_communication(struct intel_guc *guc)
 {
+	/*
+	 * Events generated during or after CT disable are logged by guc in
+	 * via mmio. Make sure the register is clear before disabling CT since
+	 * all events we cared about have already been processed via CT.
+	 */
+	guc_clear_mmio_msg(guc);
+
 	guc_disable_interrupts(guc);
 
 	guc->send = intel_guc_send_nop;
@@ -268,6 +333,14 @@  static void guc_disable_communication(struct intel_guc *guc)
 
 	intel_guc_ct_disable(&guc->ct);
 
+	/*
+	 * Check for messages received during/after the CT disable. We do not
+	 * expect any messages to have arrived via CT between the interrupt
+	 * disable and the CT disable because GuC should've been idle until we
+	 * triggered the CT disable protocol.
+	 */
+	guc_get_mmio_msg(guc);
+
 	DRM_INFO("GuC communication disabled\n");
 }