From e938089b2bdf1b79099e2b954b11882b823edafd Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Fri, 21 Jun 2019 11:21:23 -0700 Subject: [PATCH] drm/i915/guc: handle GuC messages received with CTB disabled 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. v2: clear the mmio register on stop_communication as well (Chris) Signed-off-by: Daniele Ceraolo Spurio Cc: Michal Wajdeczko Cc: Chris Wilson Reviewed-by: Chris Wilson Signed-off-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20190621182123.31368-2-daniele.ceraolospurio@intel.com --- drivers/gpu/drm/i915/intel_guc.h | 5 +++ drivers/gpu/drm/i915/intel_uc.c | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 08c906abdfa28..d6a75bc3d7f46 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 c7f82c944dd62..fdf00f1ebb579 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -218,6 +218,53 @@ 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 +282,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 +292,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; @@ -257,10 +314,19 @@ static void guc_stop_communication(struct intel_guc *guc) guc->send = intel_guc_send_nop; guc->handler = intel_guc_to_host_event_handler_nop; + + guc_clear_mmio_msg(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 +334,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"); } -- 2.39.5