[PATCH 4.6 139/203] drm/vmwgfx: Check pin count before attempting to move a buffer

Greg Kroah-Hartman
2016-07-25T22:06:29Z

From: Sinclair Yeh <syeh@vmware.com>

commit 4ed7e2242b637bc4af0416e4aa9f945db30fb44a upstream.

In certain scenarios, e.g. when fbdev is enabled, we can get into
a situation where a vmw_framebuffer_pin() is called on a buffer
that is already pinned.

When this happens, ttm_bo_validate() will unintentially remove the
TTM_PL_FLAG_NO_EVICT flag, thus unpinning it, and leaving no way
to actually pin the buffer again.

To prevent this, if a buffer is already pinned, then instead of
calling ttm_bo_validate(), just make sure the proposed placement is
compatible with the existing placement.

Signed-off-by: Sinclair Yeh <syeh@vmware.com>
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

---
drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)

--- a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
@@ -49,6 +49,7 @@ int vmw_dmabuf_pin_in_placement(struct v
{
struct ttm_buffer_object *bo = &buf->base;
int ret;
+ uint32_t new_flags;

ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
if (unlikely(ret != 0))
@@ -60,7 +61,12 @@ int vmw_dmabuf_pin_in_placement(struct v
if (unlikely(ret != 0))
goto err;

- ret = ttm_bo_validate(bo, placement, interruptible, false);
+ if (buf->pin_count > 0)
+ ret = ttm_bo_mem_compat(placement, &bo->mem,
+ &new_flags) == true ? 0 : -EINVAL;
+ else
+ ret = ttm_bo_validate(bo, placement, interruptible, false);
+
if (!ret)
vmw_bo_pin_reserved(buf, true);

@@ -91,6 +97,7 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct
{
struct ttm_buffer_object *bo = &buf->base;
int ret;
+ uint32_t new_flags;

ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
if (unlikely(ret != 0))
@@ -102,6 +109,12 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct
if (unlikely(ret != 0))
goto err;

+ if (buf->pin_count > 0) {
+ ret = ttm_bo_mem_compat(&vmw_vram_gmr_placement, &bo->mem,
+ &new_flags) == true ? 0 : -EINVAL;
+ goto out_unreserve;
+ }
+
ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, interruptible,
false);
if (likely(ret == 0) || ret == -ERESTARTSYS)
@@ -161,6 +174,7 @@ int vmw_dmabuf_pin_in_start_of_vram(stru
struct ttm_placement placement;
struct ttm_place place;
int ret = 0;
+ uint32_t new_flags;

place = vmw_vram_placement.placement[0];
place.lpfn = bo->num_pages;
@@ -185,10 +199,15 @@ int vmw_dmabuf_pin_in_start_of_vram(stru
*/
if (bo->mem.mem_type == TTM_PL_VRAM &&
bo->mem.start < bo->num_pages &&
- bo->mem.start > 0)
+ bo->mem.start > 0 &&
+ buf->pin_count == 0)
(void) ttm_bo_validate(bo, &vmw_sys_placement, false, false);

- ret = ttm_bo_validate(bo, &placement, interruptible, false);
+ if (buf->pin_count > 0)
+ ret = ttm_bo_mem_compat(&placement, &bo->mem,
+ &new_flags) == true ? 0 : -EINVAL;
+ else
+ ret = ttm_bo_validate(bo, &placement, interruptible, false);

/* For some reason we didn't end up at the start of vram */
WARN_ON(ret == 0 && bo->offset != 0);