struct g2h_fence {
u32 *response_buffer;
u32 seqno;
+ /* fields below this point are setup based on the response */
u32 response_data;
u16 response_len;
u16 error;
u16 hint;
u16 reason;
+ u32 counter;
bool cancel;
bool retry;
+ bool wait;
bool fail;
bool done;
};
g2h_fence->seqno = ~0x0;
}
+static void g2h_fence_reinit(struct g2h_fence *g2h_fence)
+{
+ memset_after(g2h_fence, 0, seqno);
+}
+
static void g2h_fence_cancel(struct g2h_fence *g2h_fence)
{
g2h_fence->cancel = true;
/* READ_ONCEs pairs with WRITE_ONCEs in parse_g2h_response
* and g2h_fence_cancel.
*/
+wait_again:
ret = wait_event_timeout(ct->g2h_fence_wq, READ_ONCE(g2h_fence.done), HZ);
if (!ret) {
LNL_FLUSH_WORK(&ct->g2h_worker);
return -ETIME;
}
+ if (g2h_fence.wait) {
+ xe_gt_dbg(gt, "H2G action %#x busy: counter %u\n",
+ action[0], g2h_fence.counter);
+ /* we can't leave any response data if we want to wait again */
+ g2h_fence_reinit(&g2h_fence);
+ mutex_unlock(&ct->lock);
+ goto wait_again;
+ }
if (g2h_fence.retry) {
xe_gt_dbg(gt, "H2G action %#x retrying: reason %#x\n",
action[0], g2h_fence.reason);
return -EPROTO;
}
- g2h_fence = xa_erase(&ct->fence_lookup, fence);
+ /* don't erase as we still expect a final response with the same fence */
+ if (type == GUC_HXG_TYPE_NO_RESPONSE_BUSY)
+ g2h_fence = xa_load(&ct->fence_lookup, fence);
+ else
+ g2h_fence = xa_erase(&ct->fence_lookup, fence);
+
if (unlikely(!g2h_fence)) {
/* Don't tear down channel, as send could've timed out */
/* CT_DEAD(ct, NULL, PARSE_G2H_UNKNOWN); */
xe_gt_assert(gt, fence == g2h_fence->seqno);
+ /*
+ * reinit as we might have already process this g2h_fence before
+ * if we received a NO_RESPONSE_BUSY reply
+ */
+ g2h_fence_reinit(g2h_fence);
+
if (type == GUC_HXG_TYPE_RESPONSE_FAILURE) {
g2h_fence->fail = true;
g2h_fence->error = FIELD_GET(GUC_HXG_FAILURE_MSG_0_ERROR, hxg[0]);
} else if (type == GUC_HXG_TYPE_NO_RESPONSE_RETRY) {
g2h_fence->retry = true;
g2h_fence->reason = FIELD_GET(GUC_HXG_RETRY_MSG_0_REASON, hxg[0]);
+ } else if (type == GUC_HXG_TYPE_NO_RESPONSE_BUSY) {
+ g2h_fence->wait = true;
+ g2h_fence->counter = FIELD_GET(GUC_HXG_BUSY_MSG_0_COUNTER, hxg[0]);
} else if (g2h_fence->response_buffer) {
g2h_fence->response_len = hxg_len;
memcpy(g2h_fence->response_buffer, hxg, hxg_len * sizeof(u32));
g2h_fence->response_data = FIELD_GET(GUC_HXG_RESPONSE_MSG_0_DATA0, hxg[0]);
}
- g2h_release_space(ct, GUC_CTB_HXG_MSG_MAX_LEN);
+ /* don't release any space if it was an intermediate message */
+ if (!g2h_fence->wait)
+ g2h_release_space(ct, GUC_CTB_HXG_MSG_MAX_LEN);
/* WRITE_ONCE pairs with READ_ONCEs in guc_ct_send_recv. */
WRITE_ONCE(g2h_fence->done, true);
case GUC_HXG_TYPE_RESPONSE_SUCCESS:
case GUC_HXG_TYPE_RESPONSE_FAILURE:
case GUC_HXG_TYPE_NO_RESPONSE_RETRY:
+ case GUC_HXG_TYPE_NO_RESPONSE_BUSY:
ret = parse_g2h_response(ct, msg, len);
break;
default: