diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_guc_submission.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_guc_submission.c | 126 |
1 files changed, 71 insertions, 55 deletions
diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index e4369411f07d..43358e18d34c 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -59,7 +59,7 @@ * WQ_TYPE_INORDER is needed to support legacy submission via GuC, which * represents in-order queue. The kernel driver packs ring tail pointer and an * ELSP context descriptor dword into Work Item. - * See guc_add_workqueue_item() + * See guc_wq_item_append() * */ @@ -114,10 +114,8 @@ static int host2guc_action(struct intel_guc *guc, u32 *data, u32 len) if (ret != -ETIMEDOUT) ret = -EIO; - DRM_ERROR("GUC: host2guc action 0x%X failed. ret=%d " - "status=0x%08X response=0x%08X\n", - data[0], ret, status, - I915_READ(SOFT_SCRATCH(15))); + DRM_WARN("Action 0x%X failed; ret=%d status=0x%08X response=0x%08X\n", + data[0], ret, status, I915_READ(SOFT_SCRATCH(15))); dev_priv->guc.action_fail += 1; dev_priv->guc.action_err = ret; @@ -290,7 +288,7 @@ static uint32_t select_doorbell_cacheline(struct intel_guc *guc) /* * Initialise the process descriptor shared with the GuC firmware. */ -static void guc_init_proc_desc(struct intel_guc *guc, +static void guc_proc_desc_init(struct intel_guc *guc, struct i915_guc_client *client) { struct guc_process_desc *desc; @@ -322,7 +320,7 @@ static void guc_init_proc_desc(struct intel_guc *guc, * write queue, etc). */ -static void guc_init_ctx_desc(struct intel_guc *guc, +static void guc_ctx_desc_init(struct intel_guc *guc, struct i915_guc_client *client) { struct drm_i915_private *dev_priv = guc_to_i915(guc); @@ -330,6 +328,7 @@ static void guc_init_ctx_desc(struct intel_guc *guc, struct i915_gem_context *ctx = client->owner; struct guc_context_desc desc; struct sg_table *sg; + unsigned int tmp; u32 gfx_addr; memset(&desc, 0, sizeof(desc)); @@ -339,7 +338,7 @@ static void guc_init_ctx_desc(struct intel_guc *guc, desc.priority = client->priority; desc.db_id = client->doorbell_id; - for_each_engine_masked(engine, dev_priv, client->engines) { + for_each_engine_masked(engine, dev_priv, client->engines, tmp) { struct intel_context *ce = &ctx->engine[engine->id]; uint32_t guc_engine_id = engine->guc_id; struct guc_execlist_context *lrc = &desc.lrc[guc_engine_id]; @@ -400,7 +399,7 @@ static void guc_init_ctx_desc(struct intel_guc *guc, sizeof(desc) * client->ctx_index); } -static void guc_fini_ctx_desc(struct intel_guc *guc, +static void guc_ctx_desc_fini(struct intel_guc *guc, struct i915_guc_client *client) { struct guc_context_desc desc; @@ -414,7 +413,7 @@ static void guc_fini_ctx_desc(struct intel_guc *guc, } /** - * i915_guc_wq_check_space() - check that the GuC can accept a request + * i915_guc_wq_reserve() - reserve space in the GuC's workqueue * @request: request associated with the commands * * Return: 0 if space is available @@ -422,35 +421,39 @@ static void guc_fini_ctx_desc(struct intel_guc *guc, * * This function must be called (and must return 0) before a request * is submitted to the GuC via i915_guc_submit() below. Once a result - * of 0 has been returned, it remains valid until (but only until) - * the next call to submit(). + * of 0 has been returned, it must be balanced by a corresponding + * call to submit(). * - * This precheck allows the caller to determine in advance that space + * Reservation allows the caller to determine in advance that space * will be available for the next submission before committing resources * to it, and helps avoid late failures with complicated recovery paths. */ -int i915_guc_wq_check_space(struct drm_i915_gem_request *request) +int i915_guc_wq_reserve(struct drm_i915_gem_request *request) { const size_t wqi_size = sizeof(struct guc_wq_item); struct i915_guc_client *gc = request->i915->guc.execbuf_client; - struct guc_process_desc *desc; + struct guc_process_desc *desc = gc->client_base + gc->proc_desc_offset; u32 freespace; + int ret; - GEM_BUG_ON(gc == NULL); - - desc = gc->client_base + gc->proc_desc_offset; - + spin_lock(&gc->wq_lock); freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size); - if (likely(freespace >= wqi_size)) - return 0; - - gc->no_wq_space += 1; + freespace -= gc->wq_rsvd; + if (likely(freespace >= wqi_size)) { + gc->wq_rsvd += wqi_size; + ret = 0; + } else { + gc->no_wq_space++; + ret = -EAGAIN; + } + spin_unlock(&gc->wq_lock); - return -EAGAIN; + return ret; } -static void guc_add_workqueue_item(struct i915_guc_client *gc, - struct drm_i915_gem_request *rq) +/* Construct a Work Item and append it to the GuC's Work Queue */ +static void guc_wq_item_append(struct i915_guc_client *gc, + struct drm_i915_gem_request *rq) { /* wqi_len is in DWords, and does not include the one-word header */ const size_t wqi_size = sizeof(struct guc_wq_item); @@ -463,7 +466,7 @@ static void guc_add_workqueue_item(struct i915_guc_client *gc, desc = gc->client_base + gc->proc_desc_offset; - /* Free space is guaranteed, see i915_guc_wq_check_space() above */ + /* Free space is guaranteed, see i915_guc_wq_reserve() above */ freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size); GEM_BUG_ON(freespace < wqi_size); @@ -481,12 +484,14 @@ static void guc_add_workqueue_item(struct i915_guc_client *gc, * workqueue buffer dw by dw. */ BUILD_BUG_ON(wqi_size != 16); + GEM_BUG_ON(gc->wq_rsvd < wqi_size); /* postincrement WQ tail for next time */ wq_off = gc->wq_tail; + GEM_BUG_ON(wq_off & (wqi_size - 1)); gc->wq_tail += wqi_size; gc->wq_tail &= gc->wq_size - 1; - GEM_BUG_ON(wq_off & (wqi_size - 1)); + gc->wq_rsvd -= wqi_size; /* WQ starts from the page after doorbell / process_desc */ wq_page = (wq_off + GUC_DB_SIZE) >> PAGE_SHIFT; @@ -551,8 +556,8 @@ static int guc_ring_doorbell(struct i915_guc_client *gc) if (db_ret.db_status == GUC_DOORBELL_DISABLED) break; - DRM_ERROR("Cookie mismatch. Expected %d, returned %d\n", - db_cmp.cookie, db_ret.cookie); + DRM_WARN("Cookie mismatch. Expected %d, found %d\n", + db_cmp.cookie, db_ret.cookie); /* update the cookie to newly read cookie from GuC */ db_cmp.cookie = db_ret.cookie; @@ -571,14 +576,13 @@ static int guc_ring_doorbell(struct i915_guc_client *gc) * Return: 0 on success, otherwise an errno. * (Note: nonzero really shouldn't happen!) * - * The caller must have already called i915_guc_wq_check_space() above - * with a result of 0 (success) since the last request submission. This - * guarantees that there is space in the work queue for the new request, - * so enqueuing the item cannot fail. + * The caller must have already called i915_guc_wq_reserve() above with + * a result of 0 (success), guaranteeing that there is space in the work + * queue for the new request, so enqueuing the item cannot fail. * * Bad Things Will Happen if the caller violates this protocol e.g. calls - * submit() when check() says there's no space, or calls submit() multiple - * times with no intervening check(). + * submit() when _reserve() says there's no space, or calls _submit() + * a different number of times from (successful) calls to _reserve(). * * The only error here arises if the doorbell hardware isn't functioning * as expected, which really shouln't happen. @@ -590,7 +594,8 @@ static void i915_guc_submit(struct drm_i915_gem_request *rq) struct i915_guc_client *client = guc->execbuf_client; int b_ret; - guc_add_workqueue_item(client, rq); + spin_lock(&client->wq_lock); + guc_wq_item_append(client, rq); b_ret = guc_ring_doorbell(client); client->submissions[engine_id] += 1; @@ -600,6 +605,7 @@ static void i915_guc_submit(struct drm_i915_gem_request *rq) guc->submissions[engine_id] += 1; guc->last_seqno[engine_id] = rq->fence.seqno; + spin_unlock(&client->wq_lock); } /* @@ -680,7 +686,7 @@ guc_client_free(struct drm_i915_private *dev_priv, i915_vma_unpin_and_release(&client->vma); if (client->ctx_index != GUC_INVALID_CTX_ID) { - guc_fini_ctx_desc(guc, client); + guc_ctx_desc_fini(guc, client); ida_simple_remove(&guc->ctx_ids, client->ctx_index); } @@ -733,8 +739,8 @@ static void guc_init_doorbell_hw(struct intel_guc *guc) /* Restore to original value */ err = guc_update_doorbell_id(guc, client, db_id); if (err) - DRM_ERROR("Failed to restore doorbell to %d, err %d\n", - db_id, err); + DRM_WARN("Failed to restore doorbell to %d, err %d\n", + db_id, err); /* Read back & verify all doorbell registers */ for (i = 0; i < GUC_MAX_DOORBELLS; ++i) @@ -790,6 +796,8 @@ guc_client_alloc(struct drm_i915_private *dev_priv, /* We'll keep just the first (doorbell/proc) page permanently kmap'd. */ client->vma = vma; client->client_base = kmap(i915_vma_first_page(vma)); + + spin_lock_init(&client->wq_lock); client->wq_offset = GUC_DB_SIZE; client->wq_size = GUC_WQ_SIZE; @@ -810,8 +818,8 @@ guc_client_alloc(struct drm_i915_private *dev_priv, else client->proc_desc_offset = (GUC_DB_SIZE / 2); - guc_init_proc_desc(guc, client); - guc_init_ctx_desc(guc, client); + guc_proc_desc_init(guc, client); + guc_ctx_desc_init(guc, client); if (guc_init_doorbell(guc, client, db_id)) goto err; @@ -823,13 +831,11 @@ guc_client_alloc(struct drm_i915_private *dev_priv, return client; err: - DRM_ERROR("FAILED to create priority %u GuC client!\n", priority); - guc_client_free(dev_priv, client); return NULL; } -static void guc_create_log(struct intel_guc *guc) +static void guc_log_create(struct intel_guc *guc) { struct i915_vma *vma; unsigned long offset; @@ -869,7 +875,7 @@ static void guc_create_log(struct intel_guc *guc) guc->log_flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags; } -static void init_guc_policies(struct guc_policies *policies) +static void guc_policies_init(struct guc_policies *policies) { struct guc_policy *policy; u32 p, i; @@ -891,7 +897,7 @@ static void init_guc_policies(struct guc_policies *policies) policies->is_valid = 1; } -static void guc_create_ads(struct intel_guc *guc) +static void guc_addon_create(struct intel_guc *guc) { struct drm_i915_private *dev_priv = guc_to_i915(guc); struct i915_vma *vma; @@ -934,7 +940,7 @@ static void guc_create_ads(struct intel_guc *guc) /* GuC scheduling policies */ policies = (void *)ads + sizeof(struct guc_ads); - init_guc_policies(policies); + guc_policies_init(policies); ads->scheduler_policies = i915_ggtt_offset(vma) + sizeof(struct guc_ads); @@ -965,9 +971,11 @@ static void guc_create_ads(struct intel_guc *guc) */ int i915_guc_submission_init(struct drm_i915_private *dev_priv) { + const size_t ctxsize = sizeof(struct guc_context_desc); + const size_t poolsize = GUC_MAX_GPU_CONTEXTS * ctxsize; + const size_t gemsize = round_up(poolsize, PAGE_SIZE); struct intel_guc *guc = &dev_priv->guc; struct i915_vma *vma; - u32 size; /* Wipe bitmap & delete client in case of reinitialisation */ bitmap_clear(guc->doorbell_bitmap, 0, GUC_MAX_DOORBELLS); @@ -979,15 +987,14 @@ int i915_guc_submission_init(struct drm_i915_private *dev_priv) if (guc->ctx_pool_vma) return 0; /* already allocated */ - size = PAGE_ALIGN(GUC_MAX_GPU_CONTEXTS*sizeof(struct guc_context_desc)); - vma = guc_allocate_vma(guc, size); + vma = guc_allocate_vma(guc, gemsize); if (IS_ERR(vma)) return PTR_ERR(vma); guc->ctx_pool_vma = vma; ida_init(&guc->ctx_ids); - guc_create_log(guc); - guc_create_ads(guc); + guc_log_create(guc); + guc_addon_create(guc); return 0; } @@ -997,6 +1004,7 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv) struct intel_guc *guc = &dev_priv->guc; struct i915_guc_client *client; struct intel_engine_cs *engine; + struct drm_i915_gem_request *request; /* client for execbuf submission */ client = guc_client_alloc(dev_priv, @@ -1004,7 +1012,7 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv) GUC_CTX_PRIORITY_KMD_NORMAL, dev_priv->kernel_context); if (!client) { - DRM_ERROR("Failed to create execbuf guc_client\n"); + DRM_ERROR("Failed to create normal GuC client!\n"); return -ENOMEM; } @@ -1013,9 +1021,17 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv) guc_init_doorbell_hw(guc); /* Take over from manual control of ELSP (execlists) */ - for_each_engine(engine, dev_priv) + for_each_engine(engine, dev_priv) { engine->submit_request = i915_guc_submit; + /* Replay the current set of previously submitted requests */ + list_for_each_entry(request, &engine->request_list, link) { + client->wq_rsvd += sizeof(struct guc_wq_item); + if (i915_sw_fence_done(&request->submit)) + i915_guc_submit(request); + } + } + return 0; } |