static void guc_ct_buffer_reset(struct intel_guc_ct_buffer *ctb)
{
ctb->broken = false;
+ ctb->tail = 0;
+ ctb->head = 0;
+ ctb->space = CIRC_SPACE(ctb->tail, ctb->head, ctb->size);
+
guc_ct_buffer_desc_init(ctb->desc);
}
{
struct intel_guc_ct_buffer *ctb = &ct->ctbs.send;
struct guc_ct_buffer_desc *desc = ctb->desc;
- u32 head = desc->head;
- u32 tail = desc->tail;
+ u32 tail = ctb->tail;
u32 size = ctb->size;
- u32 used;
u32 header;
u32 hxg;
u32 type;
if (unlikely(desc->status))
goto corrupted;
- if (unlikely((tail | head) >= size)) {
- CT_ERROR(ct, "Invalid offsets head=%u tail=%u (size=%u)\n",
- head, tail, size);
+ GEM_BUG_ON(tail > size);
+
+#ifdef CONFIG_DRM_I915_DEBUG_GUC
+ if (unlikely(tail != READ_ONCE(desc->tail))) {
+ CT_ERROR(ct, "Tail was modified %u != %u\n",
+ desc->tail, tail);
+ desc->status |= GUC_CTB_STATUS_MISMATCH;
+ goto corrupted;
+ }
+ if (unlikely(READ_ONCE(desc->head) >= size)) {
+ CT_ERROR(ct, "Invalid head offset %u >= %u)\n",
+ desc->head, size);
desc->status |= GUC_CTB_STATUS_OVERFLOW;
goto corrupted;
}
-
- /*
- * tail == head condition indicates empty. GuC FW does not support
- * using up the entire buffer to get tail == head meaning full.
- */
- if (tail < head)
- used = (size - head) + tail;
- else
- used = tail - head;
-
- /* make sure there is a space including extra dw for the header */
- if (unlikely(used + len + GUC_CTB_HDR_LEN >= size))
- return -ENOSPC;
+#endif
/*
* dw0: CT header (including fence)
*/
write_barrier(ct);
+ /* update local copies */
+ ctb->tail = tail;
+ GEM_BUG_ON(ctb->space < len + GUC_CTB_HDR_LEN);
+ ctb->space -= len + GUC_CTB_HDR_LEN;
+
/* now update descriptor */
WRITE_ONCE(desc->tail, tail);
* @req: pointer to pending request
* @status: placeholder for status
*
- * For each sent request, Guc shall send bac CT response message.
+ * For each sent request, GuC shall send back CT response message.
* Our message handler will update status of tracked request once
* response message with given fence is received. Wait here and
* check for valid response status value.
return ret;
}
-static inline bool h2g_has_room(struct intel_guc_ct_buffer *ctb, u32 len_dw)
+static inline bool h2g_has_room(struct intel_guc_ct *ct, u32 len_dw)
{
+ struct intel_guc_ct_buffer *ctb = &ct->ctbs.send;
struct guc_ct_buffer_desc *desc = ctb->desc;
- u32 head = READ_ONCE(desc->head);
+ u32 head;
u32 space;
- space = CIRC_SPACE(desc->tail, head, ctb->size);
+ if (ctb->space >= len_dw)
+ return true;
+
+ head = READ_ONCE(desc->head);
+ if (unlikely(head > ctb->size)) {
+ CT_ERROR(ct, "Invalid head offset %u >= %u)\n",
+ head, ctb->size);
+ desc->status |= GUC_CTB_STATUS_OVERFLOW;
+ ctb->broken = true;
+ return false;
+ }
+
+ space = CIRC_SPACE(ctb->tail, head, ctb->size);
+ ctb->space = space;
return space >= len_dw;
}
static int has_room_nb(struct intel_guc_ct *ct, u32 len_dw)
{
- struct intel_guc_ct_buffer *ctb = &ct->ctbs.send;
-
lockdep_assert_held(&ct->ctbs.send.lock);
- if (unlikely(!h2g_has_room(ctb, len_dw))) {
+ if (unlikely(!h2g_has_room(ct, len_dw))) {
if (ct->stall_time == KTIME_MAX)
ct->stall_time = ktime_get();
*/
retry:
spin_lock_irqsave(&ctb->lock, flags);
- if (unlikely(!h2g_has_room(ctb, len + GUC_CTB_HDR_LEN))) {
+ if (unlikely(!h2g_has_room(ct, len + GUC_CTB_HDR_LEN))) {
if (ct->stall_time == KTIME_MAX)
ct->stall_time = ktime_get();
spin_unlock_irqrestore(&ctb->lock, flags);
{
struct intel_guc_ct_buffer *ctb = &ct->ctbs.recv;
struct guc_ct_buffer_desc *desc = ctb->desc;
- u32 head = desc->head;
- u32 tail = desc->tail;
+ u32 head = ctb->head;
+ u32 tail = READ_ONCE(desc->tail);
u32 size = ctb->size;
u32 *cmds = ctb->cmds;
s32 available;
if (unlikely(desc->status))
goto corrupted;
- if (unlikely((tail | head) >= size)) {
- CT_ERROR(ct, "Invalid offsets head=%u tail=%u (size=%u)\n",
- head, tail, size);
+ GEM_BUG_ON(head > size);
+
+#ifdef CONFIG_DRM_I915_DEBUG_GUC
+ if (unlikely(head != READ_ONCE(desc->head))) {
+ CT_ERROR(ct, "Head was modified %u != %u\n",
+ desc->head, head);
+ desc->status |= GUC_CTB_STATUS_MISMATCH;
+ goto corrupted;
+ }
+#endif
+ if (unlikely(tail >= size)) {
+ CT_ERROR(ct, "Invalid tail offset %u >= %u)\n",
+ tail, size);
desc->status |= GUC_CTB_STATUS_OVERFLOW;
goto corrupted;
}
}
CT_DEBUG(ct, "received %*ph\n", 4 * len, (*msg)->msg);
+ /* update local copies */
+ ctb->head = head;
+
/* now update descriptor */
WRITE_ONCE(desc->head, head);