From: Matthew Brost <matthew.brost@xxxxxxxxx> Add non blocking CTB send fuction, intel_guc_send_nb. In order to support a non blocking CTB send fuction a spin lock is needed to protect the CTB descriptors fields. Also the non blocking call must not update the fence value as this value is owned by the blocking call (intel_guc_send). The blocking CTB now must have a flow control mechanism to ensure the buffer isn't overrun. A lazy spin wait is used as we believe the flow control condition should be rare with properly sized buffer. A retry counter is also implemented which fails H2G CTBs once a limit is reached to prevent deadlock. The function, intel_guc_send_nb, is exported in this patch but unused. Several patches later in the series make use of this function. Cc: John Harrison <john.c.harrison@xxxxxxxxx> Signed-off-by: Matthew Brost <matthew.brost@xxxxxxxxx> --- drivers/gpu/drm/i915/gt/uc/intel_guc.h | 2 + drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c | 97 +++++++++++++++++++---- drivers/gpu/drm/i915/gt/uc/intel_guc_ct.h | 10 ++- 3 files changed, 91 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index e6400204a2bd..77c5af919ace 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -94,6 +94,8 @@ intel_guc_send_and_receive(struct intel_guc *guc, const u32 *action, u32 len, return guc->send(guc, action, len, response_buf, response_buf_size); } +int intel_guc_send_nb(struct intel_guc_ct *ct, const u32 *action, u32 len); + static inline void intel_guc_notify(struct intel_guc *guc) { guc->notify(guc); diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c index b49115517510..e50d968b15d5 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c @@ -3,6 +3,8 @@ * Copyright © 2016-2019 Intel Corporation */ +#include <linux/circ_buf.h> + #include "i915_drv.h" #include "intel_guc_ct.h" @@ -12,6 +14,8 @@ #define CT_DEBUG_DRIVER(...) do { } while (0) #endif +#define MAX_RETRY 0x1000000 + struct ct_request { struct list_head link; u32 fence; @@ -40,7 +44,8 @@ void intel_guc_ct_init_early(struct intel_guc_ct *ct) /* we're using static channel owners */ ct->host_channel.owner = CTB_OWNER_HOST; - spin_lock_init(&ct->lock); + spin_lock_init(&ct->request_lock); + spin_lock_init(&ct->send_lock); INIT_LIST_HEAD(&ct->pending_requests); INIT_LIST_HEAD(&ct->incoming_requests); INIT_WORK(&ct->worker, ct_incoming_request_worker_func); @@ -291,7 +296,8 @@ static u32 ctch_get_next_fence(struct intel_guc_ct_channel *ctch) static int ctb_write(struct intel_guc_ct_buffer *ctb, const u32 *action, u32 len /* in dwords */, - u32 fence, + u32 fence_value, + bool enable_fence, bool want_response) { struct guc_ct_buffer_desc *desc = ctb->desc; @@ -328,18 +334,18 @@ static int ctb_write(struct intel_guc_ct_buffer *ctb, * DW2+: action data */ header = (len << GUC_CT_MSG_LEN_SHIFT) | - (GUC_CT_MSG_WRITE_FENCE_TO_DESC) | + (enable_fence ? GUC_CT_MSG_WRITE_FENCE_TO_DESC : 0) | (want_response ? GUC_CT_MSG_SEND_STATUS : 0) | (action[0] << GUC_CT_MSG_ACTION_SHIFT); CT_DEBUG_DRIVER("CT: writing %*ph %*ph %*ph\n", - 4, &header, 4, &fence, + 4, &header, 4, &fence_value, 4 * (len - 1), &action[1]); cmds[tail] = header; tail = (tail + 1) % size; - cmds[tail] = fence; + cmds[tail] = fence_value; tail = (tail + 1) % size; for (i = 1; i < len; i++) { @@ -440,6 +446,47 @@ static int wait_for_ct_request_update(struct ct_request *req, u32 *status) return err; } +static inline bool ctb_has_room(struct guc_ct_buffer_desc *desc, u32 len) +{ + u32 head = READ_ONCE(desc->head); + u32 space; + + space = CIRC_SPACE(desc->tail, head, desc->size); + + return space >= len; +} + +int intel_guc_send_nb(struct intel_guc_ct *ct, + const u32 *action, + u32 len) +{ + struct intel_guc_ct_channel *ctch = &ct->host_channel; + struct intel_guc_ct_buffer *ctb = &ctch->ctbs[CTB_SEND]; + struct guc_ct_buffer_desc *desc = ctb->desc; + int err; + + GEM_BUG_ON(!ctch->enabled); + GEM_BUG_ON(!len); + GEM_BUG_ON(len & ~GUC_CT_MSG_LEN_MASK); + lockdep_assert_held(&ct->send_lock); + + if (unlikely(!ctb_has_room(desc, (len + 1) * 4))) { + ct->retry++; + if (ct->retry >= MAX_RETRY) + return -EDEADLK; + else + return -EBUSY; + } + + ct->retry = 0; + err = ctb_write(ctb, action, len, 0, false, false); + if (unlikely(err)) + return err; + + intel_guc_notify(ct_to_guc(ct)); + return 0; +} + static int ctch_send(struct intel_guc_ct *ct, struct intel_guc_ct_channel *ctch, const u32 *action, @@ -460,17 +507,35 @@ static int ctch_send(struct intel_guc_ct *ct, GEM_BUG_ON(len & ~GUC_CT_MSG_LEN_MASK); GEM_BUG_ON(!response_buf && response_buf_size); + /* + * We use a lazy spin wait loop here as we believe that if the CT + * buffers are sized correctly the flow control condition should be + * rare. + */ +retry: + spin_lock_irqsave(&ct->send_lock, flags); + if (unlikely(!ctb_has_room(desc, (len + 1) * 4))) { + spin_unlock_irqrestore(&ct->send_lock, flags); + ct->retry++; + if (ct->retry >= MAX_RETRY) + return -EDEADLK; + cpu_relax(); + goto retry; + } + + ct->retry = 0; fence = ctch_get_next_fence(ctch); request.fence = fence; request.status = 0; request.response_len = response_buf_size; request.response_buf = response_buf; - spin_lock_irqsave(&ct->lock, flags); + spin_lock(&ct->request_lock); list_add_tail(&request.link, &ct->pending_requests); - spin_unlock_irqrestore(&ct->lock, flags); + spin_unlock(&ct->request_lock); - err = ctb_write(ctb, action, len, fence, !!response_buf); + err = ctb_write(ctb, action, len, fence, true, !!response_buf); + spin_unlock_irqrestore(&ct->send_lock, flags); if (unlikely(err)) goto unlink; @@ -501,9 +566,9 @@ static int ctch_send(struct intel_guc_ct *ct, } unlink: - spin_lock_irqsave(&ct->lock, flags); + spin_lock_irqsave(&ct->request_lock, flags); list_del(&request.link); - spin_unlock_irqrestore(&ct->lock, flags); + spin_unlock_irqrestore(&ct->request_lock, flags); return err; } @@ -653,7 +718,7 @@ static int ct_handle_response(struct intel_guc_ct *ct, const u32 *msg) CT_DEBUG_DRIVER("CT: response fence %u status %#x\n", fence, status); - spin_lock(&ct->lock); + spin_lock(&ct->request_lock); list_for_each_entry(req, &ct->pending_requests, link) { if (unlikely(fence != req->fence)) { CT_DEBUG_DRIVER("CT: request %u awaits response\n", @@ -672,7 +737,7 @@ static int ct_handle_response(struct intel_guc_ct *ct, const u32 *msg) found = true; break; } - spin_unlock(&ct->lock); + spin_unlock(&ct->request_lock); if (!found) DRM_ERROR("CT: unsolicited response %*ph\n", 4 * msglen, msg); @@ -710,13 +775,13 @@ static bool ct_process_incoming_requests(struct intel_guc_ct *ct) u32 *payload; bool done; - spin_lock_irqsave(&ct->lock, flags); + spin_lock_irqsave(&ct->request_lock, flags); request = list_first_entry_or_null(&ct->incoming_requests, struct ct_incoming_request, link); if (request) list_del(&request->link); done = !!list_empty(&ct->incoming_requests); - spin_unlock_irqrestore(&ct->lock, flags); + spin_unlock_irqrestore(&ct->request_lock, flags); if (!request) return true; @@ -777,9 +842,9 @@ static int ct_handle_request(struct intel_guc_ct *ct, const u32 *msg) } memcpy(request->msg, msg, 4 * msglen); - spin_lock_irqsave(&ct->lock, flags); + spin_lock_irqsave(&ct->request_lock, flags); list_add_tail(&request->link, &ct->incoming_requests); - spin_unlock_irqrestore(&ct->lock, flags); + spin_unlock_irqrestore(&ct->request_lock, flags); queue_work(system_unbound_wq, &ct->worker); return 0; diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.h index 7c24d83f5c24..bc670a796bd8 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.h @@ -62,8 +62,11 @@ struct intel_guc_ct { struct intel_guc_ct_channel host_channel; /* other channels are tbd */ - /** @lock: protects pending requests list */ - spinlock_t lock; + /** @request_lock: protects pending requests list */ + spinlock_t request_lock; + + /** @send_lock: protects h2g channel */ + spinlock_t send_lock; /** @pending_requests: list of requests waiting for response */ struct list_head pending_requests; @@ -73,6 +76,9 @@ struct intel_guc_ct { /** @worker: worker for handling incoming requests */ struct work_struct worker; + + /** @retry: the number of times a H2G CTB has been retried */ + u32 retry; }; void intel_guc_ct_init_early(struct intel_guc_ct *ct); -- 2.21.0.5.gaeb582a983 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx