Skip to content

Commit

Permalink
drm/imagination: Implement job submission and scheduling
Browse files Browse the repository at this point in the history
Implement job submission ioctl. Job scheduling is implemented using
drm_sched.

Jobs are submitted in a stream format. This is intended to allow the UAPI
data format to be independent of the actual FWIF structures in use, which
vary depending on the GPU in use.

The stream formats are documented at:
https://gitlab.freedesktop.org/mesa/mesa/-/blob/f8d2b42ae65c2f16f36a43e0ae39d288431e4263/src/imagination/csbgen/rogue_kmd_stream.xml

Changes since v8:
- Updated for upstreamed DRM scheduler changes
- Removed workaround code for the pending_list previously being updated
  after run_job() returned
- Fixed null deref in pvr_queue_cleanup_fw_context() for bad stream ptr
  given to create_context ioctl
- Corrected license identifiers

Changes since v7:
- Updated for v8 "DRM scheduler changes for XE" patchset

Changes since v6:
- Fix fence handling in pvr_sync_signal_array_add()
- Add handling for SUBMIT_JOB_FRAG_CMD_DISABLE_PIXELMERGE flag
- Fix missing dma_resv locking in job submit path

Changes since v5:
- Fix leak in job creation error path

Changes since v4:
- Use a regular workqueue for job scheduling

Changes since v3:
- Support partial render jobs
- Add job timeout handler
- Split sync handling out of job code
- Use drm_dev_{enter,exit}

Changes since v2:
- Use drm_sched for job scheduling

Co-developed-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Co-developed-by: Donald Robson <donald.robson@imgtec.com>
Signed-off-by: Donald Robson <donald.robson@imgtec.com>
Signed-off-by: Sarah Walker <sarah.walker@imgtec.com>
Link: https://lore.kernel.org/r/c98dab7a5f5fb891fbed7e4990d19b5d13964365.1700668843.git.donald.robson@imgtec.com
Signed-off-by: Maxime Ripard <mripard@kernel.org>
  • Loading branch information
Sarah Walker authored and Maxime Ripard committed Nov 23, 2023
1 parent d2d79d2 commit eaf01ee
Show file tree
Hide file tree
Showing 15 changed files with 3,438 additions and 4 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/imagination/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ config DRM_POWERVR
depends on ARM64
depends on DRM
depends on PM
select DRM_EXEC
select DRM_GEM_SHMEM_HELPER
select DRM_SCHED
select DRM_GPUVM
Expand Down
3 changes: 3 additions & 0 deletions drivers/gpu/drm/imagination/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ powervr-y := \
pvr_fw_trace.o \
pvr_gem.o \
pvr_hwrt.o \
pvr_job.o \
pvr_mmu.o \
pvr_power.o \
pvr_queue.o \
pvr_stream.o \
pvr_stream_defs.o \
pvr_sync.o \
pvr_vm.o \
pvr_vm_mips.o

Expand Down
125 changes: 124 additions & 1 deletion drivers/gpu/drm/imagination/pvr_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
#include "pvr_device.h"
#include "pvr_drv.h"
#include "pvr_gem.h"
#include "pvr_job.h"
#include "pvr_power.h"
#include "pvr_rogue_fwif.h"
#include "pvr_rogue_fwif_common.h"
#include "pvr_rogue_fwif_resetframework.h"
#include "pvr_stream.h"
#include "pvr_stream_defs.h"
#include "pvr_vm.h"

Expand Down Expand Up @@ -164,6 +166,116 @@ ctx_fw_data_init(void *cpu_ptr, void *priv)
memcpy(cpu_ptr, ctx->data, ctx->data_size);
}

/**
* pvr_context_destroy_queues() - Destroy all queues attached to a context.
* @ctx: Context to destroy queues on.
*
* Should be called when the last reference to a context object is dropped.
* It releases all resources attached to the queues bound to this context.
*/
static void pvr_context_destroy_queues(struct pvr_context *ctx)
{
switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
pvr_queue_destroy(ctx->queues.fragment);
pvr_queue_destroy(ctx->queues.geometry);
break;
case DRM_PVR_CTX_TYPE_COMPUTE:
pvr_queue_destroy(ctx->queues.compute);
break;
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
pvr_queue_destroy(ctx->queues.transfer);
break;
}
}

/**
* pvr_context_create_queues() - Create all queues attached to a context.
* @ctx: Context to create queues on.
* @args: Context creation arguments passed by userspace.
* @fw_ctx_map: CPU mapping of the FW context object.
*
* Return:
* * 0 on success, or
* * A negative error code otherwise.
*/
static int pvr_context_create_queues(struct pvr_context *ctx,
struct drm_pvr_ioctl_create_context_args *args,
void *fw_ctx_map)
{
int err;

switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
ctx->queues.geometry = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_GEOMETRY,
args, fw_ctx_map);
if (IS_ERR(ctx->queues.geometry)) {
err = PTR_ERR(ctx->queues.geometry);
ctx->queues.geometry = NULL;
goto err_destroy_queues;
}

ctx->queues.fragment = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_FRAGMENT,
args, fw_ctx_map);
if (IS_ERR(ctx->queues.fragment)) {
err = PTR_ERR(ctx->queues.fragment);
ctx->queues.fragment = NULL;
goto err_destroy_queues;
}
return 0;

case DRM_PVR_CTX_TYPE_COMPUTE:
ctx->queues.compute = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_COMPUTE,
args, fw_ctx_map);
if (IS_ERR(ctx->queues.compute)) {
err = PTR_ERR(ctx->queues.compute);
ctx->queues.compute = NULL;
goto err_destroy_queues;
}
return 0;

case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
ctx->queues.transfer = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_TRANSFER_FRAG,
args, fw_ctx_map);
if (IS_ERR(ctx->queues.transfer)) {
err = PTR_ERR(ctx->queues.transfer);
ctx->queues.transfer = NULL;
goto err_destroy_queues;
}
return 0;
}

return -EINVAL;

err_destroy_queues:
pvr_context_destroy_queues(ctx);
return err;
}

/**
* pvr_context_kill_queues() - Kill queues attached to context.
* @ctx: Context to kill queues on.
*
* Killing the queues implies making them unusable for future jobs, while still
* letting the currently submitted jobs a chance to finish. Queue resources will
* stay around until pvr_context_destroy_queues() is called.
*/
static void pvr_context_kill_queues(struct pvr_context *ctx)
{
switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
pvr_queue_kill(ctx->queues.fragment);
pvr_queue_kill(ctx->queues.geometry);
break;
case DRM_PVR_CTX_TYPE_COMPUTE:
pvr_queue_kill(ctx->queues.compute);
break;
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
pvr_queue_kill(ctx->queues.transfer);
break;
}
}

/**
* pvr_context_create() - Create a context.
* @pvr_file: File to attach the created context to.
Expand Down Expand Up @@ -214,10 +326,14 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
goto err_put_vm;
}

err = init_fw_objs(ctx, args, ctx->data);
err = pvr_context_create_queues(ctx, args, ctx->data);
if (err)
goto err_free_ctx_data;

err = init_fw_objs(ctx, args, ctx->data);
if (err)
goto err_destroy_queues;

err = pvr_fw_object_create(pvr_dev, ctx_size, PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
ctx_fw_data_init, ctx, &ctx->fw_obj);
if (err)
Expand All @@ -243,6 +359,9 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
err_destroy_fw_obj:
pvr_fw_object_destroy(ctx->fw_obj);

err_destroy_queues:
pvr_context_destroy_queues(ctx);

err_free_ctx_data:
kfree(ctx->data);

Expand All @@ -262,6 +381,7 @@ pvr_context_release(struct kref *ref_count)
struct pvr_device *pvr_dev = ctx->pvr_dev;

xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id);
pvr_context_destroy_queues(ctx);
pvr_fw_object_destroy(ctx->fw_obj);
kfree(ctx->data);
pvr_vm_context_put(ctx->vm_ctx);
Expand Down Expand Up @@ -299,6 +419,9 @@ pvr_context_destroy(struct pvr_file *pvr_file, u32 handle)
if (!ctx)
return -EINVAL;

/* Make sure nothing can be queued to the queues after that point. */
pvr_context_kill_queues(ctx);

/* Release the reference held by the handle set. */
pvr_context_put(ctx);

Expand Down
44 changes: 44 additions & 0 deletions drivers/gpu/drm/imagination/pvr_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "pvr_cccb.h"
#include "pvr_device.h"
#include "pvr_queue.h"

/* Forward declaration from pvr_gem.h. */
struct pvr_fw_object;
Expand Down Expand Up @@ -58,8 +59,51 @@ struct pvr_context {

/** @ctx_id: FW context ID. */
u32 ctx_id;

/**
* @faulty: Set to 1 when the context queues had unfinished job when
* a GPU reset happened.
*
* In that case, the context is in an inconsistent state and can't be
* used anymore.
*/
atomic_t faulty;

/** @queues: Union containing all kind of queues. */
union {
struct {
/** @geometry: Geometry queue. */
struct pvr_queue *geometry;

/** @fragment: Fragment queue. */
struct pvr_queue *fragment;
};

/** @compute: Compute queue. */
struct pvr_queue *compute;

/** @compute: Transfer queue. */
struct pvr_queue *transfer;
} queues;
};

static __always_inline struct pvr_queue *
pvr_context_get_queue_for_job(struct pvr_context *ctx, enum drm_pvr_job_type type)
{
switch (type) {
case DRM_PVR_JOB_TYPE_GEOMETRY:
return ctx->type == DRM_PVR_CTX_TYPE_RENDER ? ctx->queues.geometry : NULL;
case DRM_PVR_JOB_TYPE_FRAGMENT:
return ctx->type == DRM_PVR_CTX_TYPE_RENDER ? ctx->queues.fragment : NULL;
case DRM_PVR_JOB_TYPE_COMPUTE:
return ctx->type == DRM_PVR_CTX_TYPE_COMPUTE ? ctx->queues.compute : NULL;
case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
return ctx->type == DRM_PVR_CTX_TYPE_TRANSFER_FRAG ? ctx->queues.transfer : NULL;
}

return NULL;
}

/**
* pvr_context_get() - Take additional reference on context.
* @ctx: Context pointer.
Expand Down
31 changes: 31 additions & 0 deletions drivers/gpu/drm/imagination/pvr_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

#include "pvr_fw.h"
#include "pvr_power.h"
#include "pvr_queue.h"
#include "pvr_rogue_cr_defs.h"
#include "pvr_stream.h"
#include "pvr_vm.h"

#include <drm/drm_print.h>
Expand Down Expand Up @@ -117,6 +119,32 @@ static int pvr_device_clk_init(struct pvr_device *pvr_dev)
return 0;
}

/**
* pvr_device_process_active_queues() - Process all queue related events.
* @pvr_dev: PowerVR device to check
*
* This is called any time we receive a FW event. It iterates over all
* active queues and calls pvr_queue_process() on them.
*/
void pvr_device_process_active_queues(struct pvr_device *pvr_dev)
{
struct pvr_queue *queue, *tmp_queue;
LIST_HEAD(active_queues);

mutex_lock(&pvr_dev->queues.lock);

/* Move all active queues to a temporary list. Queues that remain
* active after we're done processing them are re-inserted to
* the queues.active list by pvr_queue_process().
*/
list_splice_init(&pvr_dev->queues.active, &active_queues);

list_for_each_entry_safe(queue, tmp_queue, &active_queues, node)
pvr_queue_process(queue);

mutex_unlock(&pvr_dev->queues.lock);
}

static irqreturn_t pvr_device_irq_thread_handler(int irq, void *data)
{
struct pvr_device *pvr_dev = data;
Expand All @@ -132,6 +160,7 @@ static irqreturn_t pvr_device_irq_thread_handler(int irq, void *data)
if (pvr_dev->fw_dev.booted) {
pvr_fwccb_process(pvr_dev);
pvr_kccb_wake_up_waiters(pvr_dev);
pvr_device_process_active_queues(pvr_dev);
}

pm_runtime_mark_last_busy(from_pvr_device(pvr_dev)->dev);
Expand Down Expand Up @@ -398,6 +427,8 @@ pvr_device_gpu_init(struct pvr_device *pvr_dev)
else
return -EINVAL;

pvr_stream_create_musthave_masks(pvr_dev);

err = pvr_set_dma_info(pvr_dev);
if (err)
return err;
Expand Down
21 changes: 21 additions & 0 deletions drivers/gpu/drm/imagination/pvr_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,26 @@ struct pvr_device {
*/
struct xarray free_list_ids;

/**
* @job_ids: Array of jobs belonging to this device. Array members
* are of type "struct pvr_job *".
*/
struct xarray job_ids;

/**
* @queues: Queue-related fields.
*/
struct {
/** @active: Active queue list. */
struct list_head active;

/** @idle: Idle queue list. */
struct list_head idle;

/** @lock: Lock protecting access to the active/idle lists. */
struct mutex lock;
} queues;

struct {
/** @work: Work item for watchdog callback. */
struct delayed_work work;
Expand Down Expand Up @@ -442,6 +462,7 @@ packed_bvnc_to_pvr_gpu_id(u64 bvnc, struct pvr_gpu_id *gpu_id)

int pvr_device_init(struct pvr_device *pvr_dev);
void pvr_device_fini(struct pvr_device *pvr_dev);
void pvr_device_reset(struct pvr_device *pvr_dev);

bool
pvr_device_has_uapi_quirk(struct pvr_device *pvr_dev, u32 quirk);
Expand Down
Loading

0 comments on commit eaf01ee

Please sign in to comment.