Commit cd727221 authored by Jacek Lawrynowicz's avatar Jacek Lawrynowicz Committed by Daniel Vetter

accel/ivpu: Add command buffer submission logic

Each of the user contexts has two command queues, one for compute engine
and one for the copy engine. Command queues are allocated and registered
in the device when the first job (command buffer) is submitted from
the user space to the VPU device. The userspace provides a list of
GEM buffer object handles to submit to the VPU, the driver resolves
buffer handles, pins physical memory if needed, increments ref count
for each buffer and stores pointers to buffer objects in
the ivpu_job objects that track jobs submitted to the device.
The VPU signals job completion with an asynchronous message that
contains the job id passed to firmware when the job was submitted.

Currently, the driver supports simple scheduling logic
where jobs submitted from user space are immediately pushed
to the VPU device command queues. In the future, it will be
extended to use hardware base scheduling and/or drm_sched.
Co-developed-by: default avatarAndrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
Signed-off-by: default avatarAndrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
Signed-off-by: default avatarJacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Reviewed-by: default avatarOded Gabbay <ogabbay@kernel.org>
Reviewed-by: default avatarJeffrey Hugo <quic_jhugo@quicinc.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20230117092723.60441-7-jacek.lawrynowicz@linux.intel.com
parent 02d5b0aa
......@@ -7,6 +7,7 @@ intel_vpu-y := \
ivpu_gem.o \
ivpu_hw_mtl.o \
ivpu_ipc.o \
ivpu_job.o \
ivpu_jsm_msg.o \
ivpu_mmu.o \
ivpu_mmu_context.o
......
......@@ -20,6 +20,7 @@
#include "ivpu_gem.h"
#include "ivpu_hw.h"
#include "ivpu_ipc.h"
#include "ivpu_job.h"
#include "ivpu_jsm_msg.h"
#include "ivpu_mmu.h"
#include "ivpu_mmu_context.h"
......@@ -31,6 +32,8 @@
static const struct drm_driver driver;
static struct lock_class_key submitted_jobs_xa_lock_class_key;
int ivpu_dbg_mask;
module_param_named(dbg_mask, ivpu_dbg_mask, int, 0644);
MODULE_PARM_DESC(dbg_mask, "Driver debug mask. See IVPU_DBG_* macros.");
......@@ -84,8 +87,11 @@ static void file_priv_release(struct kref *ref)
ivpu_dbg(vdev, FILE, "file_priv release: ctx %u\n", file_priv->ctx.id);
ivpu_cmdq_release_all(file_priv);
ivpu_bo_remove_all_bos_from_context(&file_priv->ctx);
ivpu_mmu_user_context_fini(vdev, &file_priv->ctx);
drm_WARN_ON(&vdev->drm, xa_erase_irq(&vdev->context_xa, file_priv->ctx.id) != file_priv);
mutex_destroy(&file_priv->lock);
kfree(file_priv);
}
......@@ -209,10 +215,11 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file)
file_priv->vdev = vdev;
file_priv->priority = DRM_IVPU_CONTEXT_PRIORITY_NORMAL;
kref_init(&file_priv->ref);
mutex_init(&file_priv->lock);
ret = ivpu_mmu_user_context_init(vdev, &file_priv->ctx, ctx_id);
if (ret)
goto err_free_file_priv;
goto err_mutex_destroy;
old = xa_store_irq(&vdev->context_xa, ctx_id, file_priv, GFP_KERNEL);
if (xa_is_err(old)) {
......@@ -229,7 +236,8 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file)
err_ctx_fini:
ivpu_mmu_user_context_fini(vdev, &file_priv->ctx);
err_free_file_priv:
err_mutex_destroy:
mutex_destroy(&file_priv->lock);
kfree(file_priv);
err_xa_erase:
xa_erase_irq(&vdev->context_xa, ctx_id);
......@@ -252,6 +260,8 @@ static const struct drm_ioctl_desc ivpu_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(IVPU_SET_PARAM, ivpu_set_param_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_BO_CREATE, ivpu_bo_create_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_BO_INFO, ivpu_bo_info_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_SUBMIT, ivpu_submit_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_BO_WAIT, ivpu_bo_wait_ioctl, 0),
};
static int ivpu_wait_for_ready(struct ivpu_device *vdev)
......@@ -458,6 +468,8 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
vdev->context_xa_limit.max = IVPU_CONTEXT_LIMIT;
atomic64_set(&vdev->unique_id_counter, 0);
xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC);
xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1);
lockdep_set_class(&vdev->submitted_jobs_xa.xa_lock, &submitted_jobs_xa_lock_class_key);
ret = ivpu_pci_init(vdev);
if (ret) {
......@@ -509,20 +521,30 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
goto err_fw_fini;
}
ret = ivpu_job_done_thread_init(vdev);
if (ret) {
ivpu_err(vdev, "Failed to initialize job done thread: %d\n", ret);
goto err_ipc_fini;
}
ret = ivpu_fw_load(vdev);
if (ret) {
ivpu_err(vdev, "Failed to load firmware: %d\n", ret);
goto err_fw_fini;
goto err_job_done_thread_fini;
}
ret = ivpu_boot(vdev);
if (ret) {
ivpu_err(vdev, "Failed to boot: %d\n", ret);
goto err_fw_fini;
goto err_job_done_thread_fini;
}
return 0;
err_job_done_thread_fini:
ivpu_job_done_thread_fini(vdev);
err_ipc_fini:
ivpu_ipc_fini(vdev);
err_fw_fini:
ivpu_fw_fini(vdev);
err_mmu_gctx_fini:
......@@ -530,6 +552,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
err_power_down:
ivpu_hw_power_down(vdev);
err_xa_destroy:
xa_destroy(&vdev->submitted_jobs_xa);
xa_destroy(&vdev->context_xa);
return ret;
}
......@@ -537,10 +560,13 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
static void ivpu_dev_fini(struct ivpu_device *vdev)
{
ivpu_shutdown(vdev);
ivpu_job_done_thread_fini(vdev);
ivpu_ipc_fini(vdev);
ivpu_fw_fini(vdev);
ivpu_mmu_global_context_fini(vdev);
drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa));
xa_destroy(&vdev->submitted_jobs_xa);
drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->context_xa));
xa_destroy(&vdev->context_xa);
}
......
......@@ -94,6 +94,9 @@ struct ivpu_device {
struct xarray context_xa;
struct xa_limit context_xa_limit;
struct xarray submitted_jobs_xa;
struct task_struct *job_done_thread;
atomic64_t unique_id_counter;
struct {
......@@ -111,6 +114,8 @@ struct ivpu_device {
struct ivpu_file_priv {
struct kref ref;
struct ivpu_device *vdev;
struct mutex lock; /* Protects cmdq */
struct ivpu_cmdq *cmdq[IVPU_NUM_ENGINES];
struct ivpu_mmu_context ctx;
u32 priority;
bool has_mmu_faults;
......
......@@ -678,6 +678,32 @@ int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file
return ret;
}
int ivpu_bo_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct drm_ivpu_bo_wait *args = data;
struct drm_gem_object *obj;
unsigned long timeout;
long ret;
timeout = drm_timeout_abs_to_jiffies(args->timeout_ns);
obj = drm_gem_object_lookup(file, args->handle);
if (!obj)
return -EINVAL;
ret = dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_READ, true, timeout);
if (ret == 0) {
ret = -ETIMEDOUT;
} else if (ret > 0) {
ret = 0;
args->job_status = to_ivpu_bo(obj)->job_status;
}
drm_gem_object_put(obj);
return ret;
}
static void ivpu_bo_print_info(struct ivpu_bo *bo, struct drm_printer *p)
{
unsigned long dma_refcount = 0;
......
......@@ -30,6 +30,7 @@ struct ivpu_bo {
u32 handle;
u32 flags;
uintptr_t user_ptr;
u32 job_status;
};
enum ivpu_bo_type {
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2020-2023 Intel Corporation
*/
#ifndef __IVPU_JOB_H__
#define __IVPU_JOB_H__
#include <linux/kref.h>
#include <linux/idr.h>
#include "ivpu_gem.h"
struct ivpu_device;
struct ivpu_file_priv;
/**
* struct ivpu_cmdq - Object representing device queue used to send jobs.
* @jobq: Pointer to job queue memory shared with the device
* @mem: Memory allocated for the job queue, shared with device
* @entry_count Number of job entries in the queue
* @db_id: Doorbell assigned to this job queue
* @db_registered: True if doorbell is registered in device
*/
struct ivpu_cmdq {
struct vpu_job_queue *jobq;
struct ivpu_bo *mem;
u32 entry_count;
u32 db_id;
bool db_registered;
};
/**
* struct ivpu_job - KMD object that represents batchbuffer / DMA buffer.
* Each batch / DMA buffer is a job to be submitted and executed by the VPU FW.
* This is a unit of execution, and be tracked by the job_id for
* any status reporting from VPU FW through IPC JOB RET/DONE message.
* @file_priv: The client that submitted this job
* @job_id: Job ID for KMD tracking and job status reporting from VPU FW
* @status: Status of the Job from IPC JOB RET/DONE message
* @batch_buffer: CPU vaddr points to the batch buffer memory allocated for the job
* @submit_status_offset: Offset within batch buffer where job completion handler
will update the job status
*/
struct ivpu_job {
struct kref ref;
struct ivpu_device *vdev;
struct ivpu_file_priv *file_priv;
struct dma_fence *done_fence;
u64 cmd_buf_vpu_addr;
u32 job_id;
u32 engine_idx;
size_t bo_count;
struct ivpu_bo *bos[];
};
int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
void ivpu_cmdq_release_all(struct ivpu_file_priv *file_priv);
void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev);
int ivpu_job_done_thread_init(struct ivpu_device *vdev);
void ivpu_job_done_thread_fini(struct ivpu_device *vdev);
void ivpu_jobs_abort_all(struct ivpu_device *vdev);
#endif /* __IVPU_JOB_H__ */
......@@ -19,6 +19,8 @@ extern "C" {
#define DRM_IVPU_SET_PARAM 0x01
#define DRM_IVPU_BO_CREATE 0x02
#define DRM_IVPU_BO_INFO 0x03
#define DRM_IVPU_SUBMIT 0x05
#define DRM_IVPU_BO_WAIT 0x06
#define DRM_IOCTL_IVPU_GET_PARAM \
DRM_IOWR(DRM_COMMAND_BASE + DRM_IVPU_GET_PARAM, struct drm_ivpu_param)
......@@ -32,6 +34,12 @@ extern "C" {
#define DRM_IOCTL_IVPU_BO_INFO \
DRM_IOWR(DRM_COMMAND_BASE + DRM_IVPU_BO_INFO, struct drm_ivpu_bo_info)
#define DRM_IOCTL_IVPU_SUBMIT \
DRM_IOW(DRM_COMMAND_BASE + DRM_IVPU_SUBMIT, struct drm_ivpu_submit)
#define DRM_IOCTL_IVPU_BO_WAIT \
DRM_IOWR(DRM_COMMAND_BASE + DRM_IVPU_BO_WAIT, struct drm_ivpu_bo_wait)
/**
* DOC: contexts
*
......@@ -207,6 +215,90 @@ struct drm_ivpu_bo_info {
__u64 size;
};
/* drm_ivpu_submit engines */
#define DRM_IVPU_ENGINE_COMPUTE 0
#define DRM_IVPU_ENGINE_COPY 1
/**
* struct drm_ivpu_submit - Submit commands to the VPU
*
* Execute a single command buffer on a given VPU engine.
* Handles to all referenced buffer objects have to be provided in @buffers_ptr.
*
* User space may wait on job completion using %DRM_IVPU_BO_WAIT ioctl.
*/
struct drm_ivpu_submit {
/**
* @buffers_ptr:
*
* A pointer to an u32 array of GEM handles of the BOs required for this job.
* The number of elements in the array must be equal to the value given by @buffer_count.
*
* The first BO is the command buffer. The rest of array has to contain all
* BOs referenced from the command buffer.
*/
__u64 buffers_ptr;
/** @buffer_count: Number of elements in the @buffers_ptr */
__u32 buffer_count;
/**
* @engine: Select the engine this job should be executed on
*
* %DRM_IVPU_ENGINE_COMPUTE:
*
* Performs Deep Learning Neural Compute Inference Operations
*
* %DRM_IVPU_ENGINE_COPY:
*
* Performs memory copy operations to/from system memory allocated for VPU
*/
__u32 engine;
/** @flags: Reserved for future use - must be zero */
__u32 flags;
/**
* @commands_offset:
*
* Offset inside the first buffer in @buffers_ptr containing commands
* to be executed. The offset has to be 8-byte aligned.
*/
__u32 commands_offset;
};
/* drm_ivpu_bo_wait job status codes */
#define DRM_IVPU_JOB_STATUS_SUCCESS 0
/**
* struct drm_ivpu_bo_wait - Wait for BO to become inactive
*
* Blocks until a given buffer object becomes inactive.
* With @timeout_ms set to 0 returns immediately.
*/
struct drm_ivpu_bo_wait {
/** @handle: Handle to the buffer object to be waited on */
__u32 handle;
/** @flags: Reserved for future use - must be zero */
__u32 flags;
/** @timeout_ns: Absolute timeout in nanoseconds (may be zero) */
__s64 timeout_ns;
/**
* @job_status:
*
* Job status code which is updated after the job is completed.
* &DRM_IVPU_JOB_STATUS_SUCCESS or device specific error otherwise.
* Valid only if @handle points to a command buffer.
*/
__u32 job_status;
/** @pad: Padding - must be zero */
__u32 pad;
};
#if defined(__cplusplus)
}
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment