From: Kristian Høgsberg <k...@redhat.com>

This fixes a potential dead lock similar to the one where we copy the
new buffer offsets back to userspace, fixed in
0e2f967303023f7f82a0963b7f7f3980d4c08f20.  In this case the lock order
violation occurs when i915_emit_box() copies the cliprects from
userspace from i915_dispatch_gem_execbuffer().

The fix in this case is a little more involved since we can't just
copy the cliprects from userspace up front without either limiting the
number of cliprects or allocating a buffer to hold them.  We also can't
lift the struct_mutex lock to copy them as that would allow other GEM
users to pre-empt the batch between setting up the domains and
submitting the commands.

What this patch does is loop through the cliprects and call
i915_gem_execbuffer() per cliprect.  This will only affect legacy DRI
rendering and even then it's not expected to be a significant slow
down.  Most of the overhead will be in the first invocation of
i915_gem_execbuffer() and subsequent invocations will find the buffer
objects already in the right cache domains.

Signed-off-by: Kristian Høgsberg <k...@redhat.com>
---
 drivers/gpu/drm/i915/i915_dma.c |   68 ++++++++-------
 drivers/gpu/drm/i915/i915_drv.h |    7 +-
 drivers/gpu/drm/i915/i915_gem.c |  180 ++++++++++++++++++++++-----------------
 3 files changed, 144 insertions(+), 111 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 81f1cff..c27b09e 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -397,36 +397,30 @@ static int i915_emit_cmds(struct drm_device * dev, int 
__user * buffer, int dwor
 
 int
 i915_emit_box(struct drm_device *dev,
-             struct drm_clip_rect __user *boxes,
-             int i, int DR1, int DR4)
+             struct drm_clip_rect *box, int DR1, int DR4)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       struct drm_clip_rect box;
        RING_LOCALS;
 
-       if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {
-               return -EFAULT;
-       }
-
-       if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) 
{
+       if (box->y2 <= box->y1 || box->x2 <= box->x1 || box->y2 <= 0 || box->x2 
<= 0) {
                DRM_ERROR("Bad box %d,%d..%d,%d\n",
-                         box.x1, box.y1, box.x2, box.y2);
+                         box->x1, box->y1, box->x2, box->y2);
                return -EINVAL;
        }
 
        if (IS_I965G(dev)) {
                BEGIN_LP_RING(4);
                OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
-               OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
-               OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
+               OUT_RING((box->x1 & 0xffff) | (box->y1 << 16));
+               OUT_RING(((box->x2 - 1) & 0xffff) | ((box->y2 - 1) << 16));
                OUT_RING(DR4);
                ADVANCE_LP_RING();
        } else {
                BEGIN_LP_RING(6);
                OUT_RING(GFX_OP_DRAWRECT_INFO);
                OUT_RING(DR1);
-               OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
-               OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
+               OUT_RING((box->x1 & 0xffff) | (box->y1 << 16));
+               OUT_RING(((box->x2 - 1) & 0xffff) | ((box->y2 - 1) << 16));
                OUT_RING(DR4);
                OUT_RING(0);
                ADVANCE_LP_RING();
@@ -462,6 +456,7 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
 static int i915_dispatch_cmdbuffer(struct drm_device * dev,
                                   drm_i915_cmdbuffer_t * cmd)
 {
+       struct drm_clip_rect box;
        int nbox = cmd->num_cliprects;
        int i = 0, count, ret;
 
@@ -476,13 +471,20 @@ static int i915_dispatch_cmdbuffer(struct drm_device * 
dev,
 
        for (i = 0; i < count; i++) {
                if (i < nbox) {
-                       ret = i915_emit_box(dev, cmd->cliprects, i,
-                                           cmd->DR1, cmd->DR4);
-                       if (ret)
-                               return ret;
+                       if (DRM_COPY_FROM_USER_UNCHECKED(&box, 
&cmd->cliprects[i], sizeof(box))) {
+                               return -EFAULT;
+                       }
                }
 
-               ret = i915_emit_cmds(dev, (int __user *)cmd->buf, cmd->sz / 4);
+               mutex_lock(&dev->struct_mutex);
+
+               if (i < nbox)
+                       ret = i915_emit_box(dev, &box, cmd->DR1, cmd->DR4);
+               if (!ret)
+                       ret = i915_emit_cmds(dev, (int __user *)cmd->buf, 
cmd->sz / 4);
+
+               mutex_unlock(&dev->struct_mutex);
+
                if (ret)
                        return ret;
        }
@@ -496,8 +498,9 @@ static int i915_dispatch_batchbuffer(struct drm_device * 
dev,
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_clip_rect __user *boxes = batch->cliprects;
+       struct drm_clip_rect box;
        int nbox = batch->num_cliprects;
-       int i = 0, count;
+       int i = 0, count, ret;
        RING_LOCALS;
 
        if ((batch->start | batch->used) & 0x7) {
@@ -511,13 +514,17 @@ static int i915_dispatch_batchbuffer(struct drm_device * 
dev,
 
        for (i = 0; i < count; i++) {
                if (i < nbox) {
-                       int ret = i915_emit_box(dev, boxes, i,
-                                               batch->DR1, batch->DR4);
-                       if (ret)
-                               return ret;
+                       if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], 
sizeof(box))) {
+                               return -EFAULT;
+                       }
                }
 
-               if (!IS_I830(dev) && !IS_845G(dev)) {
+               mutex_lock(&dev->struct_mutex);
+
+               if (i < nbox)
+                       ret = i915_emit_box(dev, &box, batch->DR1, batch->DR4);
+
+               if (!IS_I830(dev) && !IS_845G(dev) && !ret) {
                        BEGIN_LP_RING(2);
                        if (IS_I965G(dev)) {
                                OUT_RING(MI_BATCH_BUFFER_START | (2 << 6) | 
MI_BATCH_NON_SECURE_I965);
@@ -527,7 +534,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * 
dev,
                                OUT_RING(batch->start | MI_BATCH_NON_SECURE);
                        }
                        ADVANCE_LP_RING();
-               } else {
+               } else if (!ret) {
                        BEGIN_LP_RING(4);
                        OUT_RING(MI_BATCH_BUFFER);
                        OUT_RING(batch->start | MI_BATCH_NON_SECURE);
@@ -535,6 +542,11 @@ static int i915_dispatch_batchbuffer(struct drm_device * 
dev,
                        OUT_RING(0);
                        ADVANCE_LP_RING();
                }
+
+               mutex_unlock(&dev->struct_mutex);
+
+               if (ret)
+                       return ret;
        }
 
        i915_emit_breadcrumb(dev);
@@ -642,9 +654,7 @@ static int i915_batchbuffer(struct drm_device *dev, void 
*data,
                                                       sizeof(struct 
drm_clip_rect)))
                return -EFAULT;
 
-       mutex_lock(&dev->struct_mutex);
        ret = i915_dispatch_batchbuffer(dev, batch);
-       mutex_unlock(&dev->struct_mutex);
 
        if (sarea_priv)
                sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
@@ -674,9 +684,7 @@ static int i915_cmdbuffer(struct drm_device *dev, void 
*data,
                return -EFAULT;
        }
 
-       mutex_lock(&dev->struct_mutex);
        ret = i915_dispatch_cmdbuffer(dev, cmdbuf);
-       mutex_unlock(&dev->struct_mutex);
        if (ret) {
                DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
                return ret;
@@ -1290,7 +1298,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
        DRM_IOCTL_DEF(DRM_I915_VBLANK_SWAP, i915_vblank_swap, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_I915_HWS_ADDR, i915_set_status_page, 
DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
        DRM_IOCTL_DEF(DRM_I915_GEM_INIT, i915_gem_init_ioctl, 
DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER, i915_gem_execbuffer, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER, i915_gem_execbuffer_ioctl, 
DRM_AUTH),
        DRM_IOCTL_DEF(DRM_I915_GEM_PIN, i915_gem_pin_ioctl, 
DRM_AUTH|DRM_ROOT_ONLY),
        DRM_IOCTL_DEF(DRM_I915_GEM_UNPIN, i915_gem_unpin_ioctl, 
DRM_AUTH|DRM_ROOT_ONLY),
        DRM_IOCTL_DEF(DRM_I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH),
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7325363..fe12a94 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -512,8 +512,7 @@ extern int i915_driver_device_is_agp(struct drm_device * 
dev);
 extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
                              unsigned long arg);
 extern int i915_emit_box(struct drm_device *dev,
-                        struct drm_clip_rect __user *boxes,
-                        int i, int DR1, int DR4);
+                        struct drm_clip_rect *box, int DR1, int DR4);
 
 /* i915_irq.c */
 extern int i915_irq_emit(struct drm_device *dev, void *data,
@@ -576,8 +575,8 @@ int i915_gem_set_domain_ioctl(struct drm_device *dev, void 
*data,
                              struct drm_file *file_priv);
 int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
                             struct drm_file *file_priv);
-int i915_gem_execbuffer(struct drm_device *dev, void *data,
-                       struct drm_file *file_priv);
+int i915_gem_execbuffer_ioctl(struct drm_device *dev, void *data,
+                             struct drm_file *file_priv);
 int i915_gem_pin_ioctl(struct drm_device *dev, void *data,
                       struct drm_file *file_priv);
 int i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 0788581..68037cf 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2350,15 +2350,13 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object 
*obj,
  */
 static int
 i915_dispatch_gem_execbuffer(struct drm_device *dev,
-                             struct drm_i915_gem_execbuffer *exec,
-                             uint64_t exec_offset)
+                            struct drm_i915_gem_execbuffer *exec,
+                            uint64_t exec_offset,
+                            struct drm_clip_rect *box)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       struct drm_clip_rect __user *boxes = (struct drm_clip_rect __user *)
-                                            (uintptr_t) exec->cliprects_ptr;
-       int nbox = exec->num_cliprects;
-       int i = 0, count;
-       uint32_t        exec_start, exec_len;
+       uint32_t exec_start, exec_len;
+       int ret;
        RING_LOCALS;
 
        exec_start = (uint32_t) exec_offset + exec->batch_start_offset;
@@ -2372,37 +2370,32 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
        if (!exec_start)
                return -EINVAL;
 
-       count = nbox ? nbox : 1;
-
-       for (i = 0; i < count; i++) {
-               if (i < nbox) {
-                       int ret = i915_emit_box(dev, boxes, i,
-                                               exec->DR1, exec->DR4);
-                       if (ret)
-                               return ret;
-               }
+       if (box) {
+               ret = i915_emit_box(dev, box, exec->DR1, exec->DR4);
+               if (ret)
+                       return ret;
+       }
 
-               if (IS_I830(dev) || IS_845G(dev)) {
-                       BEGIN_LP_RING(4);
-                       OUT_RING(MI_BATCH_BUFFER);
-                       OUT_RING(exec_start | MI_BATCH_NON_SECURE);
-                       OUT_RING(exec_start + exec_len - 4);
-                       OUT_RING(0);
-                       ADVANCE_LP_RING();
+       if (IS_I830(dev) || IS_845G(dev)) {
+               BEGIN_LP_RING(4);
+               OUT_RING(MI_BATCH_BUFFER);
+               OUT_RING(exec_start | MI_BATCH_NON_SECURE);
+               OUT_RING(exec_start + exec_len - 4);
+               OUT_RING(0);
+               ADVANCE_LP_RING();
+       } else {
+               BEGIN_LP_RING(2);
+               if (IS_I965G(dev)) {
+                       OUT_RING(MI_BATCH_BUFFER_START |
+                                (2 << 6) |
+                                MI_BATCH_NON_SECURE_I965);
+                       OUT_RING(exec_start);
                } else {
-                       BEGIN_LP_RING(2);
-                       if (IS_I965G(dev)) {
-                               OUT_RING(MI_BATCH_BUFFER_START |
-                                        (2 << 6) |
-                                        MI_BATCH_NON_SECURE_I965);
-                               OUT_RING(exec_start);
-                       } else {
-                               OUT_RING(MI_BATCH_BUFFER_START |
-                                        (2 << 6));
-                               OUT_RING(exec_start | MI_BATCH_NON_SECURE);
-                       }
-                       ADVANCE_LP_RING();
+                       OUT_RING(MI_BATCH_BUFFER_START |
+                                (2 << 6));
+                       OUT_RING(exec_start | MI_BATCH_NON_SECURE);
                }
+               ADVANCE_LP_RING();
        }
 
        /* XXX breadcrumb */
@@ -2432,52 +2425,22 @@ i915_gem_ring_throttle(struct drm_device *dev, struct 
drm_file *file_priv)
        return ret;
 }
 
-int
-i915_gem_execbuffer(struct drm_device *dev, void *data,
+static int
+i915_gem_execbuffer(struct drm_device *dev,
+                   struct drm_i915_gem_execbuffer *args,
+                   struct drm_i915_gem_exec_object *exec_list,
+                   struct drm_gem_object **object_list,
+                   struct drm_clip_rect *box,
                    struct drm_file *file_priv)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
-       struct drm_i915_gem_execbuffer *args = data;
-       struct drm_i915_gem_exec_object *exec_list = NULL;
-       struct drm_gem_object **object_list = NULL;
        struct drm_gem_object *batch_obj;
        int ret, i, pinned = 0;
        uint64_t exec_offset;
        uint32_t seqno, flush_domains;
        int pin_tries;
 
-#if WATCH_EXEC
-       DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
-                 (int) args->buffers_ptr, args->buffer_count, args->batch_len);
-#endif
-
-       if (args->buffer_count < 1) {
-               DRM_ERROR("execbuf with %d buffers\n", args->buffer_count);
-               return -EINVAL;
-       }
-       /* Copy in the exec list from userland */
-       exec_list = drm_calloc(sizeof(*exec_list), args->buffer_count,
-                              DRM_MEM_DRIVER);
-       object_list = drm_calloc(sizeof(*object_list), args->buffer_count,
-                                DRM_MEM_DRIVER);
-       if (exec_list == NULL || object_list == NULL) {
-               DRM_ERROR("Failed to allocate exec or object list "
-                         "for %d buffers\n",
-                         args->buffer_count);
-               ret = -ENOMEM;
-               goto pre_mutex_err;
-       }
-       ret = copy_from_user(exec_list,
-                            (struct drm_i915_relocation_entry __user *)
-                            (uintptr_t) args->buffers_ptr,
-                            sizeof(*exec_list) * args->buffer_count);
-       if (ret != 0) {
-               DRM_ERROR("copy %d exec entries failed %d\n",
-                         args->buffer_count, ret);
-               goto pre_mutex_err;
-       }
-
        mutex_lock(&dev->struct_mutex);
 
        i915_verify_inactive(dev, __FILE__, __LINE__);
@@ -2485,15 +2448,13 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
        if (dev_priv->mm.wedged) {
                DRM_ERROR("Execbuf while wedged\n");
                mutex_unlock(&dev->struct_mutex);
-               ret = -EIO;
-               goto pre_mutex_err;
+               return -EIO;
        }
 
        if (dev_priv->mm.suspended) {
                DRM_ERROR("Execbuf while VT-switched.\n");
                mutex_unlock(&dev->struct_mutex);
-               ret = -EBUSY;
-               goto pre_mutex_err;
+               return -EBUSY;
        }
 
        /* Look up object handles */
@@ -2601,7 +2562,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
 #endif
 
        /* Exec the batchbuffer */
-       ret = i915_dispatch_gem_execbuffer(dev, args, exec_offset);
+       ret = i915_dispatch_gem_execbuffer(dev, args, exec_offset, box);
        if (ret) {
                DRM_ERROR("dispatch failed %d\n", ret);
                goto err;
@@ -2648,6 +2609,71 @@ err:
 
        mutex_unlock(&dev->struct_mutex);
 
+       return ret;
+}
+
+int
+i915_gem_execbuffer_ioctl(struct drm_device *dev, void *data,
+                         struct drm_file *file_priv)
+{
+       struct drm_i915_gem_execbuffer *args = data;
+       struct drm_i915_gem_exec_object *exec_list;
+       struct drm_gem_object **object_list;
+       struct drm_clip_rect box;
+       struct drm_clip_rect __user *boxes =
+               (struct drm_clip_rect __user *) (uintptr_t) args->cliprects_ptr;
+       int i, ret;
+
+#if WATCH_EXEC
+       DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
+                 (int) args->buffers_ptr, args->buffer_count, args->batch_len);
+#endif
+
+       if (args->buffer_count < 1) {
+               DRM_ERROR("execbuf with %d buffers\n", args->buffer_count);
+               return -EINVAL;
+       }
+       /* Copy in the exec list from userland */
+       exec_list = drm_calloc(sizeof(*exec_list), args->buffer_count,
+                              DRM_MEM_DRIVER);
+       object_list = drm_calloc(sizeof(*object_list), args->buffer_count,
+                                DRM_MEM_DRIVER);
+       if (exec_list == NULL || object_list == NULL) {
+               DRM_ERROR("Failed to allocate exec or object list "
+                         "for %d buffers\n",
+                         args->buffer_count);
+               ret = -ENOMEM;
+               goto fail;
+       }
+       ret = copy_from_user(exec_list,
+                            (struct drm_i915_relocation_entry __user *)
+                            (uintptr_t) args->buffers_ptr,
+                            sizeof(*exec_list) * args->buffer_count);
+       if (ret != 0) {
+               DRM_ERROR("copy %d exec entries failed %d\n",
+                         args->buffer_count, ret);
+               goto fail;
+       }
+
+       if (args->num_cliprects == 0) {
+               ret = i915_gem_execbuffer(dev, args,
+                                         exec_list, object_list,
+                                         NULL, file_priv);
+       } else {
+               for (i = 0; i < args->num_cliprects; i++) {
+                       if (copy_from_user(&box, &boxes[i], sizeof(box))) {
+                               ret = -EFAULT;
+                               break;
+                       }
+
+                       ret = i915_gem_execbuffer(dev, args,
+                                                 exec_list, object_list,
+                                                 &box, file_priv);
+                       if (ret)
+                               break;
+               }
+       }
+
        if (!ret) {
                /* Copy the new buffer offsets back to the user's exec list. */
                ret = copy_to_user((struct drm_i915_relocation_entry __user *)
@@ -2660,7 +2686,7 @@ err:
                                  args->buffer_count, ret);
        }
 
-pre_mutex_err:
+ fail:
        drm_free(object_list, sizeof(*object_list) * args->buffer_count,
                 DRM_MEM_DRIVER);
        drm_free(exec_list, sizeof(*exec_list) * args->buffer_count,
-- 
1.6.1.3


------------------------------------------------------------------------------
Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA
-OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise
-Strategies to boost innovation and cut costs with open source participation
-Receive a $600 discount off the registration fee with the source code: SFAD
http://p.sf.net/sfu/XcvMzF8H
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to