Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 201361a5 authored by Eric Anholt's avatar Eric Anholt
Browse files

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: default avatarEric Anholt <eric@anholt.net>
Reviewed-by: default avatarKeith Packard <keithp@keithp.com>
Acked-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent eb01459f
Loading
Loading
Loading
Loading
+73 −34
Original line number Original line Diff line number Diff line
@@ -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)
		return -EINVAL;

	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 *
				     batch->num_cliprects *
						       sizeof(struct drm_clip_rect)))
				     sizeof(struct drm_clip_rect));
		return -EFAULT;
		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;

	batch_data = drm_alloc(cmdbuf->sz, DRM_MEM_DRIVER);
	if (batch_data == NULL)
		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 *
				     cmdbuf->num_cliprects *
				sizeof(struct drm_clip_rect))) {
				     sizeof(struct drm_clip_rect));
		DRM_ERROR("Fault accessing cliprects\n");
		if (ret != 0)
		return -EFAULT;
			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,
+1 −1
Original line number Original line Diff line number Diff line
@@ -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 */
+23 −4
Original line number Original line Diff line number Diff line
@@ -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 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
		 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;
}
}