diff options
-rw-r--r-- | drivers/gpu/drm/i915/Kconfig | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 90 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem_execbuffer.c | 101 | ||||
-rw-r--r-- | include/uapi/drm/i915_drm.h | 16 |
5 files changed, 207 insertions, 10 deletions
diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 29a32b11953b..634bf20e671a 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -18,6 +18,9 @@ config DRM_I915 select INPUT if ACPI select ACPI_VIDEO if ACPI select ACPI_BUTTON if ACPI + # ANDROID is required for SYNC + select ANDROID + select SYNC help Choose this option if you have a system that has "Intel Graphics Media Accelerator" or "HD Graphics" integrated graphics, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ac9fcc19897a..b95d67ba42c1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2366,6 +2366,13 @@ void i915_gem_request_notify(struct intel_engine_cs *ring, bool fence_locked); int i915_create_fence_timeline(struct drm_device *dev, struct intel_context *ctx, struct intel_engine_cs *ring); +struct sync_fence; +int i915_create_sync_fence(struct drm_i915_gem_request *req, + struct sync_fence **sync_fence, int *fence_fd); +void i915_install_sync_fence_fd(struct drm_i915_gem_request *req, + struct sync_fence *sync_fence, int fence_fd); +bool i915_safe_to_ignore_fence(struct intel_engine_cs *ring, + struct sync_fence *fence); static inline bool i915_gem_request_completed(struct drm_i915_gem_request *req) { diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 165b8a38c16f..95e5bdc18558 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -38,6 +38,7 @@ #include <linux/swap.h> #include <linux/pci.h> #include <linux/dma-buf.h> +#include <../drivers/android/sync.h> #define RQ_BUG_ON(expr) @@ -2680,7 +2681,13 @@ void __i915_add_request(struct drm_i915_gem_request *request, /* * Add the fence to the pending list before emitting the commands to - * generate a seqno notification interrupt. + * generate a seqno notification interrupt. This will also enable + * interrupts if 'signal_requested' has been set. + * + * For example, if an exported sync point has been requested for this + * request then it can be waited on without the driver's knowledge, + * i.e. without calling __i915_wait_request(). Thus interrupts must + * be enabled from the start rather than only on demand. */ i915_gem_request_submit(request); @@ -3061,6 +3068,87 @@ static unsigned i915_fence_timeline_get_next_seqno(struct i915_fence_timeline *t return seqno; } +int i915_create_sync_fence(struct drm_i915_gem_request *req, + struct sync_fence **sync_fence, int *fence_fd) +{ + char engine_name[] = "i915_engine0"; + int fd; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + DRM_DEBUG("No available file descriptors!\n"); + *fence_fd = -1; + return fd; + } + + engine_name[11] += req->engine->id; + *sync_fence = sync_fence_create_dma(engine_name, &req->fence); + if (!*sync_fence) { + put_unused_fd(fd); + *fence_fd = -1; + return -ENOMEM; + } + + *fence_fd = fd; + + return 0; +} + +void i915_install_sync_fence_fd(struct drm_i915_gem_request *req, + struct sync_fence *sync_fence, int fence_fd) +{ + sync_fence_install(sync_fence, fence_fd); + + /* + * NB: The corresponding put happens automatically on file close + * from sync_fence_release() via the fops callback. + */ + fence_get(&req->fence); + + /* + * The sync framework adds a callback to the fence. The fence + * framework calls 'enable_signalling' when a callback is added. + * Thus this flag should have been set by now. If not then + * 'enable_signalling' must be called explicitly because exporting + * a fence to user land means it can be waited on asynchronously and + * thus must be signalled asynchronously. + */ + WARN_ON(!req->signal_requested); +} + +bool i915_safe_to_ignore_fence(struct intel_engine_cs *engine, + struct sync_fence *sync_fence) +{ + struct fence *dma_fence; + struct drm_i915_gem_request *req; + int i; + + if (sync_fence_is_signaled(sync_fence)) + return true; + + for (i = 0; i < sync_fence->num_fences; i++) { + dma_fence = sync_fence->cbs[i].sync_pt; + + /* No need to worry about dead points: */ + if (fence_is_signaled(dma_fence)) + continue; + + /* Can't ignore other people's points: */ + if (dma_fence->ops != &i915_gem_request_fops) + return false; + + req = container_of(dma_fence, typeof(*req), fence); + + /* Can't ignore points on other rings: */ + if (req->engine != engine) + return false; + + /* Same engine means guaranteed to be in order so ignore it. */ + } + + return true; +} + static inline int __i915_gem_request_alloc(struct intel_engine_cs *engine, struct intel_context *ctx, diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 84ef58003dd6..fa0278b16dcf 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -26,6 +26,7 @@ * */ +#include <linux/syscalls.h> #include <drm/drmP.h> #include <drm/i915_drm.h> #include "i915_drv.h" @@ -33,6 +34,7 @@ #include "intel_drv.h" #include <linux/dma_remapping.h> #include <linux/uaccess.h> +#include <../drivers/android/sync.h> #include "i915_scheduler.h" #define __EXEC_OBJECT_HAS_PIN (1<<31) @@ -1503,6 +1505,41 @@ eb_select_ring(struct drm_i915_private *dev_priv, return 0; } +/* + * Do a synchronous wait on any incoming fence object (until the scheduler + * arrives and implements asynchronous waits). NB: This must be called before + * acquiring the driver mutex lock! + */ +static int i915_early_fence_wait(struct intel_engine_cs *ring, int fence_fd) +{ + struct sync_fence *fence; + int ret = 0; + + if (fence_fd < 0) { + DRM_DEBUG("Invalid wait fence fd %d on ring %d\n", fence_fd, + (int) ring->id); + return -EINVAL; + } + + fence = sync_fence_fdget(fence_fd); + if (fence == NULL) { + DRM_DEBUG("Invalid wait fence %d on ring %d\n", fence_fd, + (int) ring->id); + return -EINVAL; + } + + /* + * Wait forever for the fence to be signalled. This is safe + * because the the mutex lock has not yet been acquired and + * the wait is interruptible. + */ + if (!i915_safe_to_ignore_fence(ring, fence)) + ret = sync_fence_wait(fence, -1); + + sync_fence_put(fence); + return ret; +} + static int i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_file *file, @@ -1523,6 +1560,18 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, u32 dispatch_flags; int ret, i; bool need_relocs; + int fd_fence_complete = -1; + int fd_fence_wait = lower_32_bits(args->rsvd2); + struct sync_fence *sync_fence; + + /* + * Make sure an broken fence handle is not returned no matter how + * early an error might be hit. Note that rsvd2 is both an input and + * an output parameter. Need to preserve the input half to allow + * calls to be retried. + */ + if (args->flags & I915_EXEC_CREATE_FENCE) + args->rsvd2 = fd_fence_wait | (((__u64) -1) << 32); if (!i915_gem_check_execbuffer(args)) return -EINVAL; @@ -1564,6 +1613,17 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, dispatch_flags |= I915_DISPATCH_RS; } + /* + * Without a GPU scheduler, any fence waits must be done up front. + */ + if (args->flags & I915_EXEC_WAIT_FENCE) { + ret = i915_early_fence_wait(engine, fd_fence_wait); + if (ret < 0) + return ret; + + args->flags &= ~I915_EXEC_WAIT_FENCE; + } + intel_runtime_pm_get(dev_priv); ret = i915_mutex_lock_interruptible(dev); @@ -1753,13 +1813,41 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, i915_gem_context_reference(ctx); params->ctx = ctx; + if (args->flags & I915_EXEC_CREATE_FENCE) { + /* + * Caller has requested a sync fence. + * User interrupts will be enabled to make sure that + * the timeline is signalled on completion. + */ + ret = i915_create_sync_fence(params->request, &sync_fence, + &fd_fence_complete); + if (ret) { + DRM_ERROR("Fence creation failed for engine %d, ctx %p\n", + engine->id, ctx); + goto err_client; + } + } + ret = dev_priv->gt.execbuf_submit(params, args, &eb->vmas); if (ret) - goto err_client; + goto err_fence; /* the request owns the ref now */ i915_gem_context_unreference(ctx); + if (fd_fence_complete != -1) { + /* + * Install the fence into the pre-allocated file + * descriptor to the fence object so that user land + * can wait on it... + */ + i915_install_sync_fence_fd(params->request, + sync_fence, fd_fence_complete); + + /* Return the fence through the rsvd2 field */ + args->rsvd2 = fd_fence_wait | (((__u64) fd_fence_complete) << 32); + } + /* * The eb list is no longer required. The scheduler has extracted all * the information than needs to persist. @@ -1776,6 +1864,12 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, return 0; +err_fence: + if (fd_fence_complete != -1) { + sync_fence_put(sync_fence); + put_unused_fd(fd_fence_complete); + } + err_client: if (req->file_priv) i915_gem_request_remove_from_client(req); @@ -1939,11 +2033,6 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, return -EINVAL; } - if (args->rsvd2 != 0) { - DRM_DEBUG("dirty rvsd2 field\n"); - return -EINVAL; - } - /* Throttle batch requests per device file */ if (i915_scheduler_file_queue_wait(file)) return -EAGAIN; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 0e87551db1b7..88af8ea4370a 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -250,7 +250,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_HWS_ADDR DRM_IOW(DRM_COMMAND_BASE + DRM_I915_HWS_ADDR, struct drm_i915_gem_init) #define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init) #define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer) -#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2) +#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2) #define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin) #define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin) #define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy) @@ -701,7 +701,7 @@ struct drm_i915_gem_exec_object2 { __u64 flags; __u64 rsvd1; - __u64 rsvd2; + __u64 rsvd2; /* Used for fence fd */ }; struct drm_i915_gem_execbuffer2 { @@ -784,7 +784,17 @@ struct drm_i915_gem_execbuffer2 { */ #define I915_EXEC_RESOURCE_STREAMER (1<<15) -#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_RESOURCE_STREAMER<<1) +/** Caller supplies a sync fence fd in the rsvd2 field. + * Wait for it to be signalled before starting the work + */ +#define I915_EXEC_WAIT_FENCE (1<<16) + +/** Caller wants a sync fence fd for this execbuffer. + * It will be returned in rsvd2 + */ +#define I915_EXEC_CREATE_FENCE (1<<17) + +#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_CREATE_FENCE<<1) #define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) #define i915_execbuffer2_set_context_id(eb2, context) \ |