diff mbox

[v4,06/13] drm/i915/guc: Prepare to handle messages from CT RECV buffer

Message ID 20180323144728.61548-7-michal.wajdeczko@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Michal Wajdeczko March 23, 2018, 2:47 p.m. UTC
GuC can respond to our commands not only by updating SEND buffer
descriptor, but can also send a response message over RECV buffer.
Guc can also send unsolicited request messages over RECV buffer.
Let's start reading those messages and make placeholders
for actual response/request handlers.

v2: misc improvements (Michal)
v3: change response detection (Michal)
    invalid status is protocol error (Michal)
v4: rebase

Signed-off-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
Cc: Oscar Mateo <oscar.mateo@intel.com>
Cc: Michel Thierry <michel.thierry@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
---
 drivers/gpu/drm/i915/intel_guc_ct.c | 139 ++++++++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

Comments

Michel Thierry March 23, 2018, 10:39 p.m. UTC | #1
On 3/23/2018 7:47 AM, Michal Wajdeczko wrote:
> GuC can respond to our commands not only by updating SEND buffer
> descriptor, but can also send a response message over RECV buffer.
> Guc can also send unsolicited request messages over RECV buffer.
> Let's start reading those messages and make placeholders
> for actual response/request handlers.
> 
> v2: misc improvements (Michal)
> v3: change response detection (Michal)
>      invalid status is protocol error (Michal)
> v4: rebase
> 
> Signed-off-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
> Cc: Oscar Mateo <oscar.mateo@intel.com>
> Cc: Michel Thierry <michel.thierry@intel.com>
> Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
> ---
>   drivers/gpu/drm/i915/intel_guc_ct.c | 139 ++++++++++++++++++++++++++++++++++++
>   1 file changed, 139 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/intel_guc_ct.c b/drivers/gpu/drm/i915/intel_guc_ct.c
> index a54bf58..f029ff3 100644
> --- a/drivers/gpu/drm/i915/intel_guc_ct.c
> +++ b/drivers/gpu/drm/i915/intel_guc_ct.c
> @@ -427,6 +427,143 @@ static int intel_guc_send_ct(struct intel_guc *guc, const u32 *action, u32 len,
>   	return ret;
>   }
>   
> +static inline unsigned int ct_header_get_len(u32 header)
> +{
> +	return (header >> GUC_CT_MSG_LEN_SHIFT) & GUC_CT_MSG_LEN_MASK;
> +}
> +
> +static inline unsigned int ct_header_get_action(u32 header)
> +{
> +	return (header >> GUC_CT_MSG_ACTION_SHIFT) & GUC_CT_MSG_ACTION_MASK;
> +}
> +
> +static inline bool ct_header_is_response(u32 header)
> +{
> +	return ct_header_get_action(header) == INTEL_GUC_ACTION_DEFAULT;
> +}
> +
> +static int ctb_read(struct intel_guc_ct_buffer *ctb, u32 *data)
> +{
> +	struct guc_ct_buffer_desc *desc = ctb->desc;
> +	u32 head = desc->head / 4;	/* in dwords */
> +	u32 tail = desc->tail / 4;	/* in dwords */
> +	u32 size = desc->size / 4;	/* in dwords */
> +	u32 *cmds = ctb->cmds;
> +	s32 available;			/* in dwords */
> +	unsigned int len;
> +	unsigned int i;
> +
> +	GEM_BUG_ON(desc->size % 4);
> +	GEM_BUG_ON(desc->head % 4);
> +	GEM_BUG_ON(desc->tail % 4);
> +	GEM_BUG_ON(tail >= size);
> +	GEM_BUG_ON(head >= size);
> +
> +	/* tail == head condition indicates empty */
> +	available = tail - head;
> +	if (unlikely(available == 0))
> +		return -ENODATA;
> +
> +	/* beware of buffer wrap case */
> +	if (unlikely(available < 0))
> +		available += size;
> +	GEM_BUG_ON(available < 0);
> +
> +	data[0] = cmds[head];
> +	head = (head + 1) % size;
> +
> +	/* message len with header */
> +	len = ct_header_get_len(data[0]) + 1;
> +	if (unlikely(len > (u32)available)) {
> +		DRM_ERROR("CT: incomplete message %*phn %*phn %*phn\n",
> +			  4, data,
> +			  4 * (head + available - 1 > size ?
> +			       size - head : available - 1), &cmds[head],
> +			  4 * (head + available - 1 > size ?
> +			       available - 1 - size + head : 0), &cmds[0]);
> +		return -EPROTO;
> +	}
> +
> +	for (i = 1; i < len; i++) {
> +		data[i] = cmds[head];
> +		head = (head + 1) % size;
> +	}
> +
> +	desc->head = head * 4;
> +	return 0;
> +}
> +
> +static int ct_handle_response(struct intel_guc_ct *ct, const u32 *msg)
> +{
> +	u32 header = msg[0];
> +	u32 status = msg[2];
> +	u32 len = ct_header_get_len(header) + 1; /* total len with header */
> +
> +	GEM_BUG_ON(!ct_header_is_response(header));
> +
> +	/* Response message shall at least include header, fence and status */
> +	if (unlikely(len < 3)) {
> +		DRM_ERROR("CT: corrupted response %*phn\n", 4*len, msg);
Fi.CI.CHECKPATCH wants a space around that '4*len'

> +		return -EPROTO;
> +	}
> +	/* Format of the status follows RESPONSE message */
> +	if (unlikely(!INTEL_GUC_MSG_IS_RESPONSE(status))) {
> +		DRM_ERROR("CT: corrupted response %*phn\n", 4*len, msg);
also here

> +		return -EPROTO;
> +	}
> +
> +	/* XXX */
> +	return 0;
> +}
> +
> +static int ct_handle_request(struct intel_guc_ct *ct, const u32 *msg)
> +{
> +	u32 header = msg[0];
> +
> +	GEM_BUG_ON(ct_header_is_response(header));
> +
> +	/* XXX */
> +	return 0;
> +}
> +
> +static void ct_process_host_channel(struct intel_guc_ct *ct)
> +{
> +	struct intel_guc_ct_channel *ctch = &ct->host_channel;
> +	struct intel_guc_ct_buffer *ctb = &ctch->ctbs[CTB_RECV];
> +	u32 msg[GUC_CT_MSG_LEN_MASK+1]; /* one extra dw for the header *and here.

Otherwise

Reviewed-by: Michel Thierry <michel.thierry@intel.com>

> +	int err = 0;
> +
> +	if (!ctch_is_open(ctch))
> +		return;
> +
> +	do {
> +		err = ctb_read(ctb, msg);
> +		if (err)
> +			break;
> +
> +		if (ct_header_is_response(msg[0]))
> +			err = ct_handle_response(ct, msg);
> +		else
> +			err = ct_handle_request(ct, msg);
> +	} while (!err);
> +
> +	if (GEM_WARN_ON(err == -EPROTO)) {
> +		DRM_ERROR("CT: corrupted message detected!\n");
> +		ctb->desc->is_in_error = 1;
> +	}
> +}
> +
> +/*
> + * When we're communicating with the GuC over CT, GuC uses events
> + * to notify us about new messages being posted on the RECV buffer.
> + */
> +static void intel_guc_to_host_event_handler_ct(struct intel_guc *guc)
> +{
> +	struct intel_guc_ct *ct = &guc->ct;
> +
> +	ct_process_host_channel(ct);
> +}
> +
>   /**
>    * intel_guc_ct_enable - Enable buffer based command transport.
>    * @ct: pointer to CT struct
> @@ -450,6 +587,7 @@ int intel_guc_ct_enable(struct intel_guc_ct *ct)
>   
>   	/* Switch into cmd transport buffer based send() */
>   	guc->send = intel_guc_send_ct;
> +	guc->handler = intel_guc_to_host_event_handler_ct;
>   	DRM_INFO("CT: %s\n", enableddisabled(true));
>   	return 0;
>   }
> @@ -475,5 +613,6 @@ void intel_guc_ct_disable(struct intel_guc_ct *ct)
>   
>   	/* Disable send */
>   	guc->send = intel_guc_send_nop;
> +	guc->handler = intel_guc_to_host_event_handler_nop;
>   	DRM_INFO("CT: %s\n", enableddisabled(false));
>   }
>
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_guc_ct.c b/drivers/gpu/drm/i915/intel_guc_ct.c
index a54bf58..f029ff3 100644
--- a/drivers/gpu/drm/i915/intel_guc_ct.c
+++ b/drivers/gpu/drm/i915/intel_guc_ct.c
@@ -427,6 +427,143 @@  static int intel_guc_send_ct(struct intel_guc *guc, const u32 *action, u32 len,
 	return ret;
 }
 
+static inline unsigned int ct_header_get_len(u32 header)
+{
+	return (header >> GUC_CT_MSG_LEN_SHIFT) & GUC_CT_MSG_LEN_MASK;
+}
+
+static inline unsigned int ct_header_get_action(u32 header)
+{
+	return (header >> GUC_CT_MSG_ACTION_SHIFT) & GUC_CT_MSG_ACTION_MASK;
+}
+
+static inline bool ct_header_is_response(u32 header)
+{
+	return ct_header_get_action(header) == INTEL_GUC_ACTION_DEFAULT;
+}
+
+static int ctb_read(struct intel_guc_ct_buffer *ctb, u32 *data)
+{
+	struct guc_ct_buffer_desc *desc = ctb->desc;
+	u32 head = desc->head / 4;	/* in dwords */
+	u32 tail = desc->tail / 4;	/* in dwords */
+	u32 size = desc->size / 4;	/* in dwords */
+	u32 *cmds = ctb->cmds;
+	s32 available;			/* in dwords */
+	unsigned int len;
+	unsigned int i;
+
+	GEM_BUG_ON(desc->size % 4);
+	GEM_BUG_ON(desc->head % 4);
+	GEM_BUG_ON(desc->tail % 4);
+	GEM_BUG_ON(tail >= size);
+	GEM_BUG_ON(head >= size);
+
+	/* tail == head condition indicates empty */
+	available = tail - head;
+	if (unlikely(available == 0))
+		return -ENODATA;
+
+	/* beware of buffer wrap case */
+	if (unlikely(available < 0))
+		available += size;
+	GEM_BUG_ON(available < 0);
+
+	data[0] = cmds[head];
+	head = (head + 1) % size;
+
+	/* message len with header */
+	len = ct_header_get_len(data[0]) + 1;
+	if (unlikely(len > (u32)available)) {
+		DRM_ERROR("CT: incomplete message %*phn %*phn %*phn\n",
+			  4, data,
+			  4 * (head + available - 1 > size ?
+			       size - head : available - 1), &cmds[head],
+			  4 * (head + available - 1 > size ?
+			       available - 1 - size + head : 0), &cmds[0]);
+		return -EPROTO;
+	}
+
+	for (i = 1; i < len; i++) {
+		data[i] = cmds[head];
+		head = (head + 1) % size;
+	}
+
+	desc->head = head * 4;
+	return 0;
+}
+
+static int ct_handle_response(struct intel_guc_ct *ct, const u32 *msg)
+{
+	u32 header = msg[0];
+	u32 status = msg[2];
+	u32 len = ct_header_get_len(header) + 1; /* total len with header */
+
+	GEM_BUG_ON(!ct_header_is_response(header));
+
+	/* Response message shall at least include header, fence and status */
+	if (unlikely(len < 3)) {
+		DRM_ERROR("CT: corrupted response %*phn\n", 4*len, msg);
+		return -EPROTO;
+	}
+	/* Format of the status follows RESPONSE message */
+	if (unlikely(!INTEL_GUC_MSG_IS_RESPONSE(status))) {
+		DRM_ERROR("CT: corrupted response %*phn\n", 4*len, msg);
+		return -EPROTO;
+	}
+
+	/* XXX */
+	return 0;
+}
+
+static int ct_handle_request(struct intel_guc_ct *ct, const u32 *msg)
+{
+	u32 header = msg[0];
+
+	GEM_BUG_ON(ct_header_is_response(header));
+
+	/* XXX */
+	return 0;
+}
+
+static void ct_process_host_channel(struct intel_guc_ct *ct)
+{
+	struct intel_guc_ct_channel *ctch = &ct->host_channel;
+	struct intel_guc_ct_buffer *ctb = &ctch->ctbs[CTB_RECV];
+	u32 msg[GUC_CT_MSG_LEN_MASK+1]; /* one extra dw for the header */
+	int err = 0;
+
+	if (!ctch_is_open(ctch))
+		return;
+
+	do {
+		err = ctb_read(ctb, msg);
+		if (err)
+			break;
+
+		if (ct_header_is_response(msg[0]))
+			err = ct_handle_response(ct, msg);
+		else
+			err = ct_handle_request(ct, msg);
+	} while (!err);
+
+	if (GEM_WARN_ON(err == -EPROTO)) {
+		DRM_ERROR("CT: corrupted message detected!\n");
+		ctb->desc->is_in_error = 1;
+	}
+}
+
+/*
+ * When we're communicating with the GuC over CT, GuC uses events
+ * to notify us about new messages being posted on the RECV buffer.
+ */
+static void intel_guc_to_host_event_handler_ct(struct intel_guc *guc)
+{
+	struct intel_guc_ct *ct = &guc->ct;
+
+	ct_process_host_channel(ct);
+}
+
 /**
  * intel_guc_ct_enable - Enable buffer based command transport.
  * @ct: pointer to CT struct
@@ -450,6 +587,7 @@  int intel_guc_ct_enable(struct intel_guc_ct *ct)
 
 	/* Switch into cmd transport buffer based send() */
 	guc->send = intel_guc_send_ct;
+	guc->handler = intel_guc_to_host_event_handler_ct;
 	DRM_INFO("CT: %s\n", enableddisabled(true));
 	return 0;
 }
@@ -475,5 +613,6 @@  void intel_guc_ct_disable(struct intel_guc_ct *ct)
 
 	/* Disable send */
 	guc->send = intel_guc_send_nop;
+	guc->handler = intel_guc_to_host_event_handler_nop;
 	DRM_INFO("CT: %s\n", enableddisabled(false));
 }