diff --git a/drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch b/drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch new file mode 100644 index 0000000..70204e5 --- /dev/null +++ b/drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch @@ -0,0 +1,60 @@ +From a19afebb883f2a02ecf4b8d5a114ce6957a59238 Mon Sep 17 00:00:00 2001 +From: Thomas Hellstrom +Date: Wed, 26 Aug 2015 05:49:21 -0700 +Subject: [PATCH 2/2] drm/vmwgfx: Allow dropped masters render-node like access + on legacy nodes v2 + +Applications like gnome-shell may try to render after dropping master +privileges. Since the driver should now be safe against this scenario, +allow those applications to use their legacy node like a render node. + +v2: Add missing return statement. + +Signed-off-by: Thomas Hellstrom +Reviewed-by: Sinclair Yeh +--- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 7 ++++++- + drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 6 ++++++ + 2 files changed, 12 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +index a4766acd0ea2..d022b509f1ac 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +@@ -993,10 +993,15 @@ static struct vmw_master *vmw_master_check(struct drm_device *dev, + } + + /* +- * Check if we were previously master, but now dropped. ++ * Check if we were previously master, but now dropped. In that ++ * case, allow at least render node functionality. + */ + if (vmw_fp->locked_master) { + mutex_unlock(&dev->master_mutex); ++ ++ if (flags & DRM_RENDER_ALLOW) ++ return NULL; ++ + DRM_ERROR("Dropped master trying to access ioctl that " + "requires authentication.\n"); + return ERR_PTR(-EACCES); +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +index 4d0c98edeb6a..7fc3e8abd0c4 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +@@ -906,6 +906,12 @@ vmw_surface_handle_reference(struct vmw_private *dev_priv, + "surface reference.\n"); + return -EACCES; + } ++ if (ACCESS_ONCE(vmw_fpriv(file_priv)->locked_master)) { ++ DRM_ERROR("Locked master refused legacy " ++ "surface reference.\n"); ++ return -EACCES; ++ } ++ + handle = u_handle; + } + +-- +2.4.3 + diff --git a/kernel.spec b/kernel.spec index 34c1aea..81168cf 100644 --- a/kernel.spec +++ b/kernel.spec @@ -653,6 +653,10 @@ Patch515: nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch #rhbz 1212201 Patch514: drm-qxl-validate-monitors-config-modes.patch +#rhbz 1257500 +Patch517: vmwgfx-Rework-device-initialization.patch +Patch518: drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch + # END OF PATCH DEFINITIONS %endif @@ -1423,6 +1427,10 @@ ApplyPatch nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch #rhbz 1212201 ApplyPatch drm-qxl-validate-monitors-config-modes.patch +#rhbz 1257500 +ApplyPatch vmwgfx-Rework-device-initialization.patch +ApplyPatch drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch + # END OF PATCH APPLICATIONS %endif @@ -2274,6 +2282,7 @@ fi # %changelog * Thu Aug 27 2015 Josh Boyer +- Fix vmware driver issues from Thomas Hellström (rhbz 1227193) - Add patch from Hans de Goede to fix nv46 based cards (rhbz 1257534) - Add patch from Jonathon Jongsma to fix modes in qxl (rhbz 1212201) diff --git a/vmwgfx-Rework-device-initialization.patch b/vmwgfx-Rework-device-initialization.patch new file mode 100644 index 0000000..183ba9c --- /dev/null +++ b/vmwgfx-Rework-device-initialization.patch @@ -0,0 +1,890 @@ +From c1d9b32d8ee2e97e2867fa759eb84d436cca0311 Mon Sep 17 00:00:00 2001 +From: Thomas Hellstrom +Date: Thu, 25 Jun 2015 10:47:43 -0700 +Subject: [PATCH 1/2] vmwgfx: Rework device initialization + +This commit reworks device initialization so that we always enable the +FIFO at driver load, deferring SVGA enable until either first modeset +or fbdev enable. +This should always leave the fifo properly enabled for render- and +control nodes. +In addition, +*) We disable the use of VRAM when SVGA is not enabled. +*) We simplify PM support so that we only throw out resources on hibernate, +not on suspend, since the device keeps its state on suspend. + +Signed-off-by: Thomas Hellstrom +Reviewed-by: Sinclair Yeh +--- + drivers/gpu/drm/vmwgfx/vmwgfx_context.c | 8 +- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 337 ++++++++++++++++++-------------- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 19 +- + drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 4 + + drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c | 12 +- + drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 + + drivers/gpu/drm/vmwgfx/vmwgfx_mob.c | 6 +- + drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 + + drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 4 +- + drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 12 +- + 10 files changed, 230 insertions(+), 174 deletions(-) + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c +index 5ac92874404d..a8e370a55e90 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c +@@ -140,7 +140,7 @@ static void vmw_hw_context_destroy(struct vmw_resource *res) + cmd->body.cid = cpu_to_le32(res->id); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + } + + static int vmw_gb_context_init(struct vmw_private *dev_priv, +@@ -220,7 +220,7 @@ static int vmw_context_init(struct vmw_private *dev_priv, + cmd->body.cid = cpu_to_le32(res->id); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + vmw_resource_activate(res, vmw_hw_context_destroy); + return 0; + +@@ -281,7 +281,7 @@ static int vmw_gb_context_create(struct vmw_resource *res) + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = res->id; + vmw_fifo_commit(dev_priv, sizeof(*cmd)); +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + + return 0; + +@@ -414,7 +414,7 @@ static int vmw_gb_context_destroy(struct vmw_resource *res) + if (dev_priv->query_cid == res->id) + dev_priv->query_cid_valid = false; + vmw_resource_release_id(res); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + + return 0; + } +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +index 620bb5cf617c..a4766acd0ea2 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +@@ -339,24 +339,47 @@ static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv) + return ret; + } + +-static int vmw_request_device(struct vmw_private *dev_priv) ++/** ++ * vmw_request_device_late - Perform late device setup ++ * ++ * @dev_priv: Pointer to device private. ++ * ++ * This function performs setup of otables and enables large command ++ * buffer submission. These tasks are split out to a separate function ++ * because it reverts vmw_release_device_early and is intended to be used ++ * by an error path in the hibernation code. ++ */ ++static int vmw_request_device_late(struct vmw_private *dev_priv) + { + int ret; + +- ret = vmw_fifo_init(dev_priv, &dev_priv->fifo); +- if (unlikely(ret != 0)) { +- DRM_ERROR("Unable to initialize FIFO.\n"); +- return ret; +- } +- vmw_fence_fifo_up(dev_priv->fman); + if (dev_priv->has_mob) { + ret = vmw_otables_setup(dev_priv); + if (unlikely(ret != 0)) { + DRM_ERROR("Unable to initialize " + "guest Memory OBjects.\n"); +- goto out_no_mob; ++ return ret; + } + } ++ ++ return 0; ++} ++ ++static int vmw_request_device(struct vmw_private *dev_priv) ++{ ++ int ret; ++ ++ ret = vmw_fifo_init(dev_priv, &dev_priv->fifo); ++ if (unlikely(ret != 0)) { ++ DRM_ERROR("Unable to initialize FIFO.\n"); ++ return ret; ++ } ++ vmw_fence_fifo_up(dev_priv->fman); ++ ++ ret = vmw_request_device_late(dev_priv); ++ if (ret) ++ goto out_no_mob; ++ + ret = vmw_dummy_query_bo_create(dev_priv); + if (unlikely(ret != 0)) + goto out_no_query_bo; +@@ -364,15 +387,25 @@ static int vmw_request_device(struct vmw_private *dev_priv) + return 0; + + out_no_query_bo: +- if (dev_priv->has_mob) ++ if (dev_priv->has_mob) { ++ (void) ttm_bo_evict_mm(&dev_priv->bdev, VMW_PL_MOB); + vmw_otables_takedown(dev_priv); ++ } + out_no_mob: + vmw_fence_fifo_down(dev_priv->fman); + vmw_fifo_release(dev_priv, &dev_priv->fifo); + return ret; + } + +-static void vmw_release_device(struct vmw_private *dev_priv) ++/** ++ * vmw_release_device_early - Early part of fifo takedown. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * ++ * This is the first part of command submission takedown, to be called before ++ * buffer management is taken down. ++ */ ++static void vmw_release_device_early(struct vmw_private *dev_priv) + { + /* + * Previous destructions should've released +@@ -382,64 +415,24 @@ static void vmw_release_device(struct vmw_private *dev_priv) + BUG_ON(dev_priv->pinned_bo != NULL); + + ttm_bo_unref(&dev_priv->dummy_query_bo); +- if (dev_priv->has_mob) ++ if (dev_priv->has_mob) { ++ ttm_bo_evict_mm(&dev_priv->bdev, VMW_PL_MOB); + vmw_otables_takedown(dev_priv); +- vmw_fence_fifo_down(dev_priv->fman); +- vmw_fifo_release(dev_priv, &dev_priv->fifo); +-} +- +- +-/** +- * Increase the 3d resource refcount. +- * If the count was prevously zero, initialize the fifo, switching to svga +- * mode. Note that the master holds a ref as well, and may request an +- * explicit switch to svga mode if fb is not running, using @unhide_svga. +- */ +-int vmw_3d_resource_inc(struct vmw_private *dev_priv, +- bool unhide_svga) +-{ +- int ret = 0; +- +- mutex_lock(&dev_priv->release_mutex); +- if (unlikely(dev_priv->num_3d_resources++ == 0)) { +- ret = vmw_request_device(dev_priv); +- if (unlikely(ret != 0)) +- --dev_priv->num_3d_resources; +- } else if (unhide_svga) { +- vmw_write(dev_priv, SVGA_REG_ENABLE, +- vmw_read(dev_priv, SVGA_REG_ENABLE) & +- ~SVGA_REG_ENABLE_HIDE); + } +- +- mutex_unlock(&dev_priv->release_mutex); +- return ret; + } + + /** +- * Decrease the 3d resource refcount. +- * If the count reaches zero, disable the fifo, switching to vga mode. +- * Note that the master holds a refcount as well, and may request an +- * explicit switch to vga mode when it releases its refcount to account +- * for the situation of an X server vt switch to VGA with 3d resources +- * active. ++ * vmw_release_device_late - Late part of fifo takedown. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * ++ * This is the last part of the command submission takedown, to be called when ++ * command submission is no longer needed. It may wait on pending fences. + */ +-void vmw_3d_resource_dec(struct vmw_private *dev_priv, +- bool hide_svga) ++static void vmw_release_device_late(struct vmw_private *dev_priv) + { +- int32_t n3d; +- +- mutex_lock(&dev_priv->release_mutex); +- if (unlikely(--dev_priv->num_3d_resources == 0)) +- vmw_release_device(dev_priv); +- else if (hide_svga) +- vmw_write(dev_priv, SVGA_REG_ENABLE, +- vmw_read(dev_priv, SVGA_REG_ENABLE) | +- SVGA_REG_ENABLE_HIDE); +- +- n3d = (int32_t) dev_priv->num_3d_resources; +- mutex_unlock(&dev_priv->release_mutex); +- +- BUG_ON(n3d < 0); ++ vmw_fence_fifo_down(dev_priv->fman); ++ vmw_fifo_release(dev_priv, &dev_priv->fifo); + } + + /** +@@ -603,6 +596,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) + spin_lock_init(&dev_priv->hw_lock); + spin_lock_init(&dev_priv->waiter_lock); + spin_lock_init(&dev_priv->cap_lock); ++ spin_lock_init(&dev_priv->svga_lock); + + for (i = vmw_res_context; i < vmw_res_max; ++i) { + idr_init(&dev_priv->res_idr[i]); +@@ -714,17 +708,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) + dev_priv->active_master = &dev_priv->fbdev_master; + + +- ret = ttm_bo_device_init(&dev_priv->bdev, +- dev_priv->bo_global_ref.ref.object, +- &vmw_bo_driver, +- dev->anon_inode->i_mapping, +- VMWGFX_FILE_PAGE_OFFSET, +- false); +- if (unlikely(ret != 0)) { +- DRM_ERROR("Failed initializing TTM buffer object driver.\n"); +- goto out_err1; +- } +- + dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start, + dev_priv->mmio_size); + +@@ -787,13 +770,28 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) + goto out_no_fman; + } + ++ ret = ttm_bo_device_init(&dev_priv->bdev, ++ dev_priv->bo_global_ref.ref.object, ++ &vmw_bo_driver, ++ dev->anon_inode->i_mapping, ++ VMWGFX_FILE_PAGE_OFFSET, ++ false); ++ if (unlikely(ret != 0)) { ++ DRM_ERROR("Failed initializing TTM buffer object driver.\n"); ++ goto out_no_bdev; ++ } + ++ /* ++ * Enable VRAM, but initially don't use it until SVGA is enabled and ++ * unhidden. ++ */ + ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, + (dev_priv->vram_size >> PAGE_SHIFT)); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed initializing memory manager for VRAM.\n"); + goto out_no_vram; + } ++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false; + + dev_priv->has_gmr = true; + if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || +@@ -814,18 +812,18 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) + } + } + +- vmw_kms_save_vga(dev_priv); +- +- /* Start kms and overlay systems, needs fifo. */ + ret = vmw_kms_init(dev_priv); + if (unlikely(ret != 0)) + goto out_no_kms; + vmw_overlay_init(dev_priv); + ++ ret = vmw_request_device(dev_priv); ++ if (ret) ++ goto out_no_fifo; ++ + if (dev_priv->enable_fb) { +- ret = vmw_3d_resource_inc(dev_priv, true); +- if (unlikely(ret != 0)) +- goto out_no_fifo; ++ vmw_fifo_resource_inc(dev_priv); ++ vmw_svga_enable(dev_priv); + vmw_fb_init(dev_priv); + } + +@@ -838,13 +836,14 @@ out_no_fifo: + vmw_overlay_close(dev_priv); + vmw_kms_close(dev_priv); + out_no_kms: +- vmw_kms_restore_vga(dev_priv); + if (dev_priv->has_mob) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); + if (dev_priv->has_gmr) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + out_no_vram: ++ (void)ttm_bo_device_release(&dev_priv->bdev); ++out_no_bdev: + vmw_fence_manager_takedown(dev_priv->fman); + out_no_fman: + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) +@@ -860,8 +859,6 @@ out_err4: + iounmap(dev_priv->mmio_virt); + out_err3: + arch_phys_wc_del(dev_priv->mmio_mtrr); +- (void)ttm_bo_device_release(&dev_priv->bdev); +-out_err1: + vmw_ttm_global_release(dev_priv); + out_err0: + for (i = vmw_res_context; i < vmw_res_max; ++i) +@@ -883,18 +880,22 @@ static int vmw_driver_unload(struct drm_device *dev) + vfree(dev_priv->ctx.cmd_bounce); + if (dev_priv->enable_fb) { + vmw_fb_close(dev_priv); +- vmw_kms_restore_vga(dev_priv); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); ++ vmw_svga_disable(dev_priv); + } ++ + vmw_kms_close(dev_priv); + vmw_overlay_close(dev_priv); + +- if (dev_priv->has_mob) +- (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); + if (dev_priv->has_gmr) + (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + ++ vmw_release_device_early(dev_priv); ++ if (dev_priv->has_mob) ++ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); ++ (void) ttm_bo_device_release(&dev_priv->bdev); ++ vmw_release_device_late(dev_priv); + vmw_fence_manager_takedown(dev_priv->fman); + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) + drm_irq_uninstall(dev_priv->dev); +@@ -1148,27 +1149,13 @@ static int vmw_master_set(struct drm_device *dev, + struct vmw_master *vmaster = vmw_master(file_priv->master); + int ret = 0; + +- if (!dev_priv->enable_fb) { +- ret = vmw_3d_resource_inc(dev_priv, true); +- if (unlikely(ret != 0)) +- return ret; +- vmw_kms_save_vga(dev_priv); +- vmw_write(dev_priv, SVGA_REG_TRACES, 0); +- } +- + if (active) { + BUG_ON(active != &dev_priv->fbdev_master); + ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile); + if (unlikely(ret != 0)) +- goto out_no_active_lock; ++ return ret; + + ttm_lock_set_kill(&active->lock, true, SIGTERM); +- ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM); +- if (unlikely(ret != 0)) { +- DRM_ERROR("Unable to clean VRAM on " +- "master drop.\n"); +- } +- + dev_priv->active_master = NULL; + } + +@@ -1182,14 +1169,6 @@ static int vmw_master_set(struct drm_device *dev, + dev_priv->active_master = vmaster; + + return 0; +- +-out_no_active_lock: +- if (!dev_priv->enable_fb) { +- vmw_kms_restore_vga(dev_priv); +- vmw_3d_resource_dec(dev_priv, true); +- vmw_write(dev_priv, SVGA_REG_TRACES, 1); +- } +- return ret; + } + + static void vmw_master_drop(struct drm_device *dev, +@@ -1214,16 +1193,9 @@ static void vmw_master_drop(struct drm_device *dev, + } + + ttm_lock_set_kill(&vmaster->lock, false, SIGTERM); +- vmw_execbuf_release_pinned_bo(dev_priv); + +- if (!dev_priv->enable_fb) { +- ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM); +- if (unlikely(ret != 0)) +- DRM_ERROR("Unable to clean VRAM on master drop.\n"); +- vmw_kms_restore_vga(dev_priv); +- vmw_3d_resource_dec(dev_priv, true); +- vmw_write(dev_priv, SVGA_REG_TRACES, 1); +- } ++ if (!dev_priv->enable_fb) ++ vmw_svga_disable(dev_priv); + + dev_priv->active_master = &dev_priv->fbdev_master; + ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM); +@@ -1233,6 +1205,74 @@ static void vmw_master_drop(struct drm_device *dev, + vmw_fb_on(dev_priv); + } + ++/** ++ * __vmw_svga_enable - Enable SVGA mode, FIFO and use of VRAM. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * Needs the reservation sem to be held in non-exclusive mode. ++ */ ++void __vmw_svga_enable(struct vmw_private *dev_priv) ++{ ++ spin_lock(&dev_priv->svga_lock); ++ if (!dev_priv->bdev.man[TTM_PL_VRAM].use_type) { ++ vmw_write(dev_priv, SVGA_REG_ENABLE, SVGA_REG_ENABLE); ++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = true; ++ } ++ spin_unlock(&dev_priv->svga_lock); ++} ++ ++/** ++ * vmw_svga_enable - Enable SVGA mode, FIFO and use of VRAM. ++ * ++ * @dev_priv: Pointer to device private struct. ++ */ ++void vmw_svga_enable(struct vmw_private *dev_priv) ++{ ++ ttm_read_lock(&dev_priv->reservation_sem, false); ++ __vmw_svga_enable(dev_priv); ++ ttm_read_unlock(&dev_priv->reservation_sem); ++} ++ ++/** ++ * __vmw_svga_disable - Disable SVGA mode and use of VRAM. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * Needs the reservation sem to be held in exclusive mode. ++ * Will not empty VRAM. VRAM must be emptied by caller. ++ */ ++void __vmw_svga_disable(struct vmw_private *dev_priv) ++{ ++ spin_lock(&dev_priv->svga_lock); ++ if (dev_priv->bdev.man[TTM_PL_VRAM].use_type) { ++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false; ++ vmw_write(dev_priv, SVGA_REG_ENABLE, ++ SVGA_REG_ENABLE_ENABLE_HIDE); ++ } ++ spin_unlock(&dev_priv->svga_lock); ++} ++ ++/** ++ * vmw_svga_disable - Disable SVGA_MODE, and use of VRAM. Keep the fifo ++ * running. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * Will empty VRAM. ++ */ ++void vmw_svga_disable(struct vmw_private *dev_priv) ++{ ++ ttm_write_lock(&dev_priv->reservation_sem, false); ++ spin_lock(&dev_priv->svga_lock); ++ if (dev_priv->bdev.man[TTM_PL_VRAM].use_type) { ++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false; ++ vmw_write(dev_priv, SVGA_REG_ENABLE, ++ SVGA_REG_ENABLE_ENABLE_HIDE); ++ spin_unlock(&dev_priv->svga_lock); ++ if (ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM)) ++ DRM_ERROR("Failed evicting VRAM buffers.\n"); ++ } else ++ spin_unlock(&dev_priv->svga_lock); ++ ttm_write_unlock(&dev_priv->reservation_sem); ++} + + static void vmw_remove(struct pci_dev *pdev) + { +@@ -1250,21 +1290,21 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, + + switch (val) { + case PM_HIBERNATION_PREPARE: +- case PM_SUSPEND_PREPARE: + ttm_suspend_lock(&dev_priv->reservation_sem); + +- /** ++ /* + * This empties VRAM and unbinds all GMR bindings. + * Buffer contents is moved to swappable memory. + */ + vmw_execbuf_release_pinned_bo(dev_priv); + vmw_resource_evict_all(dev_priv); ++ vmw_release_device_early(dev_priv); + ttm_bo_swapout_all(&dev_priv->bdev); +- ++ vmw_fence_fifo_down(dev_priv->fman); + break; + case PM_POST_HIBERNATION: +- case PM_POST_SUSPEND: + case PM_POST_RESTORE: ++ vmw_fence_fifo_up(dev_priv->fman); + ttm_suspend_unlock(&dev_priv->reservation_sem); + + break; +@@ -1276,20 +1316,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, + return 0; + } + +-/** +- * These might not be needed with the virtual SVGA device. +- */ +- + static int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) + { + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + +- if (dev_priv->num_3d_resources != 0) { +- DRM_INFO("Can't suspend or hibernate " +- "while 3D resources are active.\n"); ++ if (dev_priv->refuse_hibernation) + return -EBUSY; +- } + + pci_save_state(pdev); + pci_disable_device(pdev); +@@ -1321,56 +1354,62 @@ static int vmw_pm_resume(struct device *kdev) + return vmw_pci_resume(pdev); + } + +-static int vmw_pm_prepare(struct device *kdev) ++static int vmw_pm_freeze(struct device *kdev) + { + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + +- /** +- * Release 3d reference held by fbdev and potentially +- * stop fifo. +- */ + dev_priv->suspended = true; + if (dev_priv->enable_fb) +- vmw_3d_resource_dec(dev_priv, true); +- +- if (dev_priv->num_3d_resources != 0) { +- +- DRM_INFO("Can't suspend or hibernate " +- "while 3D resources are active.\n"); ++ vmw_fifo_resource_dec(dev_priv); + ++ if (atomic_read(&dev_priv->num_fifo_resources) != 0) { ++ DRM_ERROR("Can't hibernate while 3D resources are active.\n"); + if (dev_priv->enable_fb) +- vmw_3d_resource_inc(dev_priv, true); ++ vmw_fifo_resource_inc(dev_priv); ++ WARN_ON(vmw_request_device_late(dev_priv)); + dev_priv->suspended = false; + return -EBUSY; + } + ++ if (dev_priv->enable_fb) ++ __vmw_svga_disable(dev_priv); ++ ++ vmw_release_device_late(dev_priv); ++ + return 0; + } + +-static void vmw_pm_complete(struct device *kdev) ++static int vmw_pm_restore(struct device *kdev) + { + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); ++ int ret; + + vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2); + (void) vmw_read(dev_priv, SVGA_REG_ID); + +- /** +- * Reclaim 3d reference held by fbdev and potentially +- * start fifo. +- */ + if (dev_priv->enable_fb) +- vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); ++ ++ ret = vmw_request_device(dev_priv); ++ if (ret) ++ return ret; ++ ++ if (dev_priv->enable_fb) ++ __vmw_svga_enable(dev_priv); + + dev_priv->suspended = false; ++ ++ return 0; + } + + static const struct dev_pm_ops vmw_pm_ops = { +- .prepare = vmw_pm_prepare, +- .complete = vmw_pm_complete, ++ .freeze = vmw_pm_freeze, ++ .thaw = vmw_pm_restore, ++ .restore = vmw_pm_restore, + .suspend = vmw_pm_suspend, + .resume = vmw_pm_resume, + }; +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +index d26a6daa9719..a5f221eaf076 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +@@ -484,6 +484,7 @@ struct vmw_private { + + bool stealth; + bool enable_fb; ++ spinlock_t svga_lock; + + /** + * Master management. +@@ -493,9 +494,10 @@ struct vmw_private { + struct vmw_master fbdev_master; + struct notifier_block pm_nb; + bool suspended; ++ bool refuse_hibernation; + + struct mutex release_mutex; +- uint32_t num_3d_resources; ++ atomic_t num_fifo_resources; + + /* + * Replace this with an rwsem as soon as we have down_xx_interruptible() +@@ -587,8 +589,9 @@ static inline uint32_t vmw_read(struct vmw_private *dev_priv, + return val; + } + +-int vmw_3d_resource_inc(struct vmw_private *dev_priv, bool unhide_svga); +-void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga); ++extern void vmw_svga_enable(struct vmw_private *dev_priv); ++extern void vmw_svga_disable(struct vmw_private *dev_priv); ++ + + /** + * GMR utilities - vmwgfx_gmr.c +@@ -1116,4 +1119,14 @@ static inline struct ttm_mem_global *vmw_mem_glob(struct vmw_private *dev_priv) + { + return (struct ttm_mem_global *) dev_priv->mem_global_ref.object; + } ++ ++static inline void vmw_fifo_resource_inc(struct vmw_private *dev_priv) ++{ ++ atomic_inc(&dev_priv->num_fifo_resources); ++} ++ ++static inline void vmw_fifo_resource_dec(struct vmw_private *dev_priv) ++{ ++ atomic_dec(&dev_priv->num_fifo_resources); ++} + #endif +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +index 0a474f391fad..0e062613a7db 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +@@ -596,7 +596,10 @@ int vmw_fb_off(struct vmw_private *vmw_priv) + + info = vmw_priv->fb_info; + par = info->par; ++ if (!par->bo_ptr) ++ return 0; + ++ vmw_kms_save_vga(vmw_priv); + spin_lock_irqsave(&par->dirty.lock, flags); + par->dirty.active = false; + spin_unlock_irqrestore(&par->dirty.lock, flags); +@@ -648,6 +651,7 @@ int vmw_fb_on(struct vmw_private *vmw_priv) + spin_lock_irqsave(&par->dirty.lock, flags); + par->dirty.active = true; + spin_unlock_irqrestore(&par->dirty.lock, flags); ++ vmw_kms_restore_vga(vmw_priv); + + err_no_buffer: + vmw_fb_set_par(info); +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +index 39f2b03888e7..cd5d9f3fe0e0 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +@@ -98,7 +98,6 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) + __le32 __iomem *fifo_mem = dev_priv->mmio_virt; + uint32_t max; + uint32_t min; +- uint32_t dummy; + + fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE; + fifo->static_buffer = vmalloc(fifo->static_buffer_size); +@@ -112,10 +111,6 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) + mutex_init(&fifo->fifo_mutex); + init_rwsem(&fifo->rwsem); + +- /* +- * Allow mapping the first page read-only to user-space. +- */ +- + DRM_INFO("width %d\n", vmw_read(dev_priv, SVGA_REG_WIDTH)); + DRM_INFO("height %d\n", vmw_read(dev_priv, SVGA_REG_HEIGHT)); + DRM_INFO("bpp %d\n", vmw_read(dev_priv, SVGA_REG_BITS_PER_PIXEL)); +@@ -123,7 +118,9 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) + dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE); + dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE); + dev_priv->traces_state = vmw_read(dev_priv, SVGA_REG_TRACES); +- vmw_write(dev_priv, SVGA_REG_ENABLE, 1); ++ ++ vmw_write(dev_priv, SVGA_REG_ENABLE, SVGA_REG_ENABLE_ENABLE_HIDE); ++ vmw_write(dev_priv, SVGA_REG_TRACES, 0); + + min = 4; + if (dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO) +@@ -155,7 +152,8 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) + atomic_set(&dev_priv->marker_seq, dev_priv->last_read_seqno); + iowrite32(dev_priv->last_read_seqno, fifo_mem + SVGA_FIFO_FENCE); + vmw_marker_queue_init(&fifo->marker_queue); +- return vmw_fifo_send_fence(dev_priv, &dummy); ++ ++ return 0; + } + + void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason) +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +index 5c289f748ab4..53579f278b63 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +@@ -280,6 +280,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) + } + + vmw_fb_off(dev_priv); ++ vmw_svga_enable(dev_priv); + + crtc->primary->fb = fb; + encoder->crtc = crtc; +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c +index 04a64b8cd3cd..f06d60f41fa7 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c +@@ -574,7 +574,7 @@ void vmw_mob_unbind(struct vmw_private *dev_priv, + vmw_fence_single_bo(bo, NULL); + ttm_bo_unreserve(bo); + } +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + } + + /* +@@ -627,7 +627,7 @@ int vmw_mob_bind(struct vmw_private *dev_priv, + mob->pt_level += VMW_MOBFMT_PTDEPTH_1 - SVGA3D_MOBFMT_PTDEPTH_1; + } + +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { +@@ -648,7 +648,7 @@ int vmw_mob_bind(struct vmw_private *dev_priv, + return 0; + + out_no_cmd_space: +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + if (pt_set_up) + ttm_bo_unref(&mob->pt_bo); + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +index 7dc591d04d9a..9e8eb364a6ac 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +@@ -332,6 +332,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set) + } + + vmw_fb_off(dev_priv); ++ vmw_svga_enable(dev_priv); + + if (mode->hdisplay != crtc->mode.hdisplay || + mode->vdisplay != crtc->mode.vdisplay) { +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +index 6a4584a43aa6..6110a433ebfe 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +@@ -165,7 +165,7 @@ static int vmw_gb_shader_create(struct vmw_resource *res) + cmd->body.type = shader->type; + cmd->body.sizeInBytes = shader->size; + vmw_fifo_commit(dev_priv, sizeof(*cmd)); +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + + return 0; + +@@ -275,7 +275,7 @@ static int vmw_gb_shader_destroy(struct vmw_resource *res) + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + mutex_unlock(&dev_priv->binding_mutex); + vmw_resource_release_id(res); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + + return 0; + } +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +index 4ecdbf3e59da..4d0c98edeb6a 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +@@ -340,7 +340,7 @@ static void vmw_hw_surface_destroy(struct vmw_resource *res) + dev_priv->used_memory_size -= res->backup_size; + mutex_unlock(&dev_priv->cmdbuf_mutex); + } +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + } + + /** +@@ -576,14 +576,14 @@ static int vmw_surface_init(struct vmw_private *dev_priv, + + BUG_ON(res_free == NULL); + if (!dev_priv->has_mob) +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + ret = vmw_resource_init(dev_priv, res, true, res_free, + (dev_priv->has_mob) ? &vmw_gb_surface_func : + &vmw_legacy_surface_func); + + if (unlikely(ret != 0)) { + if (!dev_priv->has_mob) +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + res_free(res); + return ret; + } +@@ -1028,7 +1028,7 @@ static int vmw_gb_surface_create(struct vmw_resource *res) + if (likely(res->id != -1)) + return 0; + +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + ret = vmw_resource_alloc_id(res); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate a surface id.\n"); +@@ -1068,7 +1068,7 @@ static int vmw_gb_surface_create(struct vmw_resource *res) + out_no_fifo: + vmw_resource_release_id(res); + out_no_id: +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + return ret; + } + +@@ -1213,7 +1213,7 @@ static int vmw_gb_surface_destroy(struct vmw_resource *res) + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + mutex_unlock(&dev_priv->binding_mutex); + vmw_resource_release_id(res); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + + return 0; + } +-- +2.4.3 +