summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/i915/Kconfig3
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h7
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c90
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c101
-rw-r--r--include/uapi/drm/i915_drm.h16
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) \