diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index b98afbd..ec3721c 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1174,8 +1174,20 @@ static int i915_workqueues_init(struct drm_i915_private *dev_priv)
 	if (dev_priv->gpu_error.hangcheck_wq == NULL)
 		goto out_free_dp_wq;
 
+	if (HAS_GUC_SCHED(dev_priv)) {
+		/* Need a dedicated wq to process log buffer flush interrupts
+		 * from GuC without much delay so as to avoid any loss of logs.
+		 */
+		dev_priv->guc.log.wq =
+			alloc_ordered_workqueue("i915-guc_log", 0);
+		if (dev_priv->guc.log.wq == NULL)
+			goto out_free_hangcheck_wq;
+	}
+
 	return 0;
 
+out_free_hangcheck_wq:
+	destroy_workqueue(dev_priv->gpu_error.hangcheck_wq);
 out_free_dp_wq:
 	destroy_workqueue(dev_priv->hotplug.dp_wq);
 out_free_wq:
@@ -1188,6 +1200,7 @@ out_err:
 
 static void i915_workqueues_cleanup(struct drm_i915_private *dev_priv)
 {
+	destroy_workqueue(dev_priv->guc.log.wq);
 	destroy_workqueue(dev_priv->gpu_error.hangcheck_wq);
 	destroy_workqueue(dev_priv->hotplug.dp_wq);
 	destroy_workqueue(dev_priv->wq);
diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c
index b441951..425226e 100644
--- a/drivers/gpu/drm/i915/i915_guc_submission.c
+++ b/drivers/gpu/drm/i915/i915_guc_submission.c
@@ -166,6 +166,15 @@ static int host2guc_sample_forcewake(struct intel_guc *guc,
 	return host2guc_action(guc, data, ARRAY_SIZE(data));
 }
 
+static int host2guc_logbuffer_flush_complete(struct intel_guc *guc)
+{
+	u32 data[1];
+
+	data[0] = HOST2GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE;
+
+	return host2guc_action(guc, data, 1);
+}
+
 /*
  * Initialise, update, or clear doorbell data shared with the GuC
  *
@@ -819,6 +828,75 @@ err:
 	return NULL;
 }
 
+static void* guc_get_write_buffer(struct intel_guc *guc)
+{
+	return NULL;
+}
+
+static void guc_read_update_log_buffer(struct drm_device *dev, bool capture_all)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_guc *guc = &dev_priv->guc;
+	struct guc_log_buffer_state *log_buffer_state;
+	struct guc_log_buffer_state *log_buffer_copy_state;
+	void *src_ptr, *dst_ptr;
+	u32 num_pages_to_copy;
+	int i;
+
+	if (!guc->log.obj)
+		return;
+
+	num_pages_to_copy = guc->log.obj->base.size / PAGE_SIZE;
+	/* Don't really need to copy crash buffer area in regular cases as there
+	 * won't be any unread data there.
+	 */
+	if (!capture_all)
+		num_pages_to_copy -= (GUC_LOG_CRASH_PAGES + 1);
+
+	log_buffer_state = src_ptr =
+		kmap_atomic(i915_gem_object_get_page(guc->log.obj, 0));
+
+	/* Get the pointer to local buffer to store the logs */
+	dst_ptr = log_buffer_copy_state = guc_get_write_buffer(guc);
+
+	for (i = 0; i < GUC_MAX_LOG_BUFFER; i++) {
+		if (log_buffer_copy_state) {
+			memcpy(log_buffer_copy_state, log_buffer_state,
+					sizeof(struct guc_log_buffer_state));
+
+			/* The write pointer could have been updated by the GuC
+			 * firmware, after sending the flush interrupt to Host,
+			 * for consistency set the write pointer value to same
+			 * value of sampled_write_ptr in the snapshot buffer.
+			 */
+			log_buffer_copy_state->write_ptr =
+				log_buffer_copy_state->sampled_write_ptr;
+
+			log_buffer_copy_state++;
+		}
+
+		/* FIXME: invalidate/flush for log buffer needed */
+
+		/* Update the read pointer in the shared log buffer */
+		log_buffer_state->read_ptr =
+			log_buffer_state->sampled_write_ptr;
+
+		/* Clear the 'flush to file' flag */
+		log_buffer_state->flush_to_file = 0;
+		log_buffer_state++;
+	}
+
+	kunmap_atomic(src_ptr);
+
+	/* Now copy the actual logs */
+	for (i=1; (i < num_pages_to_copy) && dst_ptr; i++) {
+		dst_ptr += PAGE_SIZE;
+		src_ptr = kmap_atomic(i915_gem_object_get_page(guc->log.obj, i));
+		memcpy(dst_ptr, src_ptr, PAGE_SIZE);
+		kunmap_atomic(src_ptr);
+	}
+}
+
 static void guc_create_log(struct intel_guc *guc)
 {
 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
@@ -1078,3 +1156,12 @@ int intel_guc_resume(struct drm_device *dev)
 
 	return host2guc_action(guc, data, ARRAY_SIZE(data));
 }
+
+void i915_guc_capture_logs(struct drm_device *dev, bool capture_all)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	guc_read_update_log_buffer(dev, capture_all);
+
+	host2guc_logbuffer_flush_complete(&dev_priv->guc);
+}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index aefe1a1..7b8894c1 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1255,7 +1255,8 @@ static void gen9_guc2host_events_work(struct work_struct *work)
 	msg = I915_READ(SOFT_SCRATCH(15));
 	if (msg & (GUC2HOST_MSG_CRASH_DUMP_POSTED |
 		   GUC2HOST_MSG_FLUSH_LOG_BUFFER)) {
-		/* TODO: Handle the events for which GuC interrupted host */
+		i915_guc_capture_logs(dev_priv->dev,
+			msg & GUC2HOST_MSG_CRASH_DUMP_POSTED);
 
 		/* Clear GuC to Host msg bits that are handled */
 		if (msg & GUC2HOST_MSG_FLUSH_LOG_BUFFER)
@@ -1739,7 +1740,8 @@ static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 gt_iir)
 		spin_lock(&dev_priv->irq_lock);
 		if (dev_priv->guc.interrupts_enabled) {
 			/* Process all the GuC to Host events in bottom half. */
-			queue_work(dev_priv->wq, &dev_priv->guc.events_work);
+			queue_work(dev_priv->guc.log.wq,
+					&dev_priv->guc.events_work);
 		}
 		spin_unlock(&dev_priv->irq_lock);
 	}
diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index 2663b41..71aab85 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -125,6 +125,7 @@ struct intel_guc_fw {
 struct intel_guc_log {
 	uint32_t flags;
 	struct drm_i915_gem_object *obj;
+	struct workqueue_struct *wq;
 };
 
 struct intel_guc {
@@ -171,5 +172,6 @@ int i915_guc_wq_check_space(struct drm_i915_gem_request *rq);
 int i915_guc_submit(struct drm_i915_gem_request *rq);
 void i915_guc_submission_disable(struct drm_i915_private *dev_priv);
 void i915_guc_submission_fini(struct drm_i915_private *dev_priv);
+void i915_guc_capture_logs(struct drm_device *dev, bool capture_all);
 
 #endif
