drm/i915: Fix lock order reversal with cliprects and cmdbuf in non-DRI2 paths.

This introduces allocation in the batch submission path that wasn't there
previously, but these are compatibility paths so we care about simplicity
more than performance.

kernel.org bug #12419.

Signed-off-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
Eric Anholt 2009-03-11 12:30:04 -07:00
parent eb01459fbb
commit 201361a54e
3 changed files with 97 additions and 39 deletions

View File

@ -356,7 +356,7 @@ static int validate_cmd(int cmd)
return ret; return ret;
} }
static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwords) static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
int i; int i;
@ -370,8 +370,7 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor
for (i = 0; i < dwords;) { for (i = 0; i < dwords;) {
int cmd, sz; int cmd, sz;
if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) cmd = buffer[i];
return -EINVAL;
if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords)
return -EINVAL; return -EINVAL;
@ -379,11 +378,7 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor
OUT_RING(cmd); OUT_RING(cmd);
while (++i, --sz) { while (++i, --sz) {
if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], OUT_RING(buffer[i]);
sizeof(cmd))) {
return -EINVAL;
}
OUT_RING(cmd);
} }
} }
@ -397,17 +392,13 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor
int int
i915_emit_box(struct drm_device *dev, i915_emit_box(struct drm_device *dev,
struct drm_clip_rect __user *boxes, struct drm_clip_rect *boxes,
int i, int DR1, int DR4) int i, int DR1, int DR4)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_clip_rect box; struct drm_clip_rect box = boxes[i];
RING_LOCALS; 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", 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);
@ -460,7 +451,9 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
} }
static int i915_dispatch_cmdbuffer(struct drm_device * dev, static int i915_dispatch_cmdbuffer(struct drm_device * dev,
drm_i915_cmdbuffer_t * cmd) drm_i915_cmdbuffer_t *cmd,
struct drm_clip_rect *cliprects,
void *cmdbuf)
{ {
int nbox = cmd->num_cliprects; int nbox = cmd->num_cliprects;
int i = 0, count, ret; int i = 0, count, ret;
@ -476,13 +469,13 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev,
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
if (i < nbox) { if (i < nbox) {
ret = i915_emit_box(dev, cmd->cliprects, i, ret = i915_emit_box(dev, cliprects, i,
cmd->DR1, cmd->DR4); cmd->DR1, cmd->DR4);
if (ret) if (ret)
return ret; return ret;
} }
ret = i915_emit_cmds(dev, (int __user *)cmd->buf, cmd->sz / 4); ret = i915_emit_cmds(dev, cmdbuf, cmd->sz / 4);
if (ret) if (ret)
return ret; return ret;
} }
@ -492,10 +485,10 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev,
} }
static int i915_dispatch_batchbuffer(struct drm_device * dev, static int i915_dispatch_batchbuffer(struct drm_device * dev,
drm_i915_batchbuffer_t * batch) drm_i915_batchbuffer_t * batch,
struct drm_clip_rect *cliprects)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_clip_rect __user *boxes = batch->cliprects;
int nbox = batch->num_cliprects; int nbox = batch->num_cliprects;
int i = 0, count; int i = 0, count;
RING_LOCALS; RING_LOCALS;
@ -511,7 +504,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
if (i < nbox) { if (i < nbox) {
int ret = i915_emit_box(dev, boxes, i, int ret = i915_emit_box(dev, cliprects, i,
batch->DR1, batch->DR4); batch->DR1, batch->DR4);
if (ret) if (ret)
return ret; return ret;
@ -626,6 +619,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
master_priv->sarea_priv; master_priv->sarea_priv;
drm_i915_batchbuffer_t *batch = data; drm_i915_batchbuffer_t *batch = data;
int ret; int ret;
struct drm_clip_rect *cliprects = NULL;
if (!dev_priv->allow_batchbuffer) { if (!dev_priv->allow_batchbuffer) {
DRM_ERROR("Batchbuffer ioctl disabled\n"); DRM_ERROR("Batchbuffer ioctl disabled\n");
@ -637,17 +631,35 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
RING_LOCK_TEST_WITH_RETURN(dev, file_priv); RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
if (batch->num_cliprects && DRM_VERIFYAREA_READ(batch->cliprects, if (batch->num_cliprects < 0)
batch->num_cliprects * return -EINVAL;
sizeof(struct drm_clip_rect)))
return -EFAULT; if (batch->num_cliprects) {
cliprects = drm_calloc(batch->num_cliprects,
sizeof(struct drm_clip_rect),
DRM_MEM_DRIVER);
if (cliprects == NULL)
return -ENOMEM;
ret = copy_from_user(cliprects, batch->cliprects,
batch->num_cliprects *
sizeof(struct drm_clip_rect));
if (ret != 0)
goto fail_free;
}
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
ret = i915_dispatch_batchbuffer(dev, batch); ret = i915_dispatch_batchbuffer(dev, batch, cliprects);
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
if (sarea_priv) if (sarea_priv)
sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
fail_free:
drm_free(cliprects,
batch->num_cliprects * sizeof(struct drm_clip_rect),
DRM_MEM_DRIVER);
return ret; return ret;
} }
@ -659,6 +671,8 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
master_priv->sarea_priv; master_priv->sarea_priv;
drm_i915_cmdbuffer_t *cmdbuf = data; drm_i915_cmdbuffer_t *cmdbuf = data;
struct drm_clip_rect *cliprects = NULL;
void *batch_data;
int ret; int ret;
DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n", DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n",
@ -666,25 +680,50 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
RING_LOCK_TEST_WITH_RETURN(dev, file_priv); RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
if (cmdbuf->num_cliprects && if (cmdbuf->num_cliprects < 0)
DRM_VERIFYAREA_READ(cmdbuf->cliprects, return -EINVAL;
cmdbuf->num_cliprects *
sizeof(struct drm_clip_rect))) { batch_data = drm_alloc(cmdbuf->sz, DRM_MEM_DRIVER);
DRM_ERROR("Fault accessing cliprects\n"); if (batch_data == NULL)
return -EFAULT; return -ENOMEM;
ret = copy_from_user(batch_data, cmdbuf->buf, cmdbuf->sz);
if (ret != 0)
goto fail_batch_free;
if (cmdbuf->num_cliprects) {
cliprects = drm_calloc(cmdbuf->num_cliprects,
sizeof(struct drm_clip_rect),
DRM_MEM_DRIVER);
if (cliprects == NULL)
goto fail_batch_free;
ret = copy_from_user(cliprects, cmdbuf->cliprects,
cmdbuf->num_cliprects *
sizeof(struct drm_clip_rect));
if (ret != 0)
goto fail_clip_free;
} }
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
ret = i915_dispatch_cmdbuffer(dev, cmdbuf); ret = i915_dispatch_cmdbuffer(dev, cmdbuf, cliprects, batch_data);
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
if (ret) { if (ret) {
DRM_ERROR("i915_dispatch_cmdbuffer failed\n"); DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
return ret; goto fail_batch_free;
} }
if (sarea_priv) if (sarea_priv)
sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
return 0;
fail_batch_free:
drm_free(batch_data, cmdbuf->sz, DRM_MEM_DRIVER);
fail_clip_free:
drm_free(cliprects,
cmdbuf->num_cliprects * sizeof(struct drm_clip_rect),
DRM_MEM_DRIVER);
return ret;
} }
static int i915_flip_bufs(struct drm_device *dev, void *data, static int i915_flip_bufs(struct drm_device *dev, void *data,

View File

@ -520,7 +520,7 @@ extern int i915_driver_device_is_agp(struct drm_device * dev);
extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg); unsigned long arg);
extern int i915_emit_box(struct drm_device *dev, extern int i915_emit_box(struct drm_device *dev,
struct drm_clip_rect __user *boxes, struct drm_clip_rect *boxes,
int i, int DR1, int DR4); int i, int DR1, int DR4);
/* i915_irq.c */ /* i915_irq.c */

View File

@ -2891,11 +2891,10 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
static int static int
i915_dispatch_gem_execbuffer(struct drm_device *dev, i915_dispatch_gem_execbuffer(struct drm_device *dev,
struct drm_i915_gem_execbuffer *exec, struct drm_i915_gem_execbuffer *exec,
struct drm_clip_rect *cliprects,
uint64_t exec_offset) uint64_t exec_offset)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; 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 nbox = exec->num_cliprects;
int i = 0, count; int i = 0, count;
uint32_t exec_start, exec_len; uint32_t exec_start, exec_len;
@ -2916,7 +2915,7 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
if (i < nbox) { if (i < nbox) {
int ret = i915_emit_box(dev, boxes, i, int ret = i915_emit_box(dev, cliprects, i,
exec->DR1, exec->DR4); exec->DR1, exec->DR4);
if (ret) if (ret)
return ret; return ret;
@ -2983,6 +2982,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_gem_object **object_list = NULL; struct drm_gem_object **object_list = NULL;
struct drm_gem_object *batch_obj; struct drm_gem_object *batch_obj;
struct drm_i915_gem_object *obj_priv; struct drm_i915_gem_object *obj_priv;
struct drm_clip_rect *cliprects = NULL;
int ret, i, pinned = 0; int ret, i, pinned = 0;
uint64_t exec_offset; uint64_t exec_offset;
uint32_t seqno, flush_domains; uint32_t seqno, flush_domains;
@ -3019,6 +3019,23 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
goto pre_mutex_err; goto pre_mutex_err;
} }
if (args->num_cliprects != 0) {
cliprects = drm_calloc(args->num_cliprects, sizeof(*cliprects),
DRM_MEM_DRIVER);
if (cliprects == NULL)
goto pre_mutex_err;
ret = copy_from_user(cliprects,
(struct drm_clip_rect __user *)
(uintptr_t) args->cliprects_ptr,
sizeof(*cliprects) * args->num_cliprects);
if (ret != 0) {
DRM_ERROR("copy %d cliprects failed: %d\n",
args->num_cliprects, ret);
goto pre_mutex_err;
}
}
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
i915_verify_inactive(dev, __FILE__, __LINE__); i915_verify_inactive(dev, __FILE__, __LINE__);
@ -3155,7 +3172,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
#endif #endif
/* Exec the batchbuffer */ /* Exec the batchbuffer */
ret = i915_dispatch_gem_execbuffer(dev, args, exec_offset); ret = i915_dispatch_gem_execbuffer(dev, args, cliprects, exec_offset);
if (ret) { if (ret) {
DRM_ERROR("dispatch failed %d\n", ret); DRM_ERROR("dispatch failed %d\n", ret);
goto err; goto err;
@ -3224,6 +3241,8 @@ pre_mutex_err:
DRM_MEM_DRIVER); DRM_MEM_DRIVER);
drm_free(exec_list, sizeof(*exec_list) * args->buffer_count, drm_free(exec_list, sizeof(*exec_list) * args->buffer_count,
DRM_MEM_DRIVER); DRM_MEM_DRIVER);
drm_free(cliprects, sizeof(*cliprects) * args->num_cliprects,
DRM_MEM_DRIVER);
return ret; return ret;
} }