drm/vmwgfx: Drop authentication requirement on UNREF ioctls
[linux-2.6-block.git] / drivers / gpu / drm / vmwgfx / vmwgfx_drv.c
index 3bdc0adc656d002a22e1b95b28c4ba6f3a7d7747..de8a9dc25f0749e5e9a3dc09de58624b982e6cbd 100644 (file)
@@ -146,7 +146,7 @@ static const struct drm_ioctl_desc vmw_ioctls[] = {
        VMW_IOCTL_DEF(VMW_ALLOC_DMABUF, vmw_dmabuf_alloc_ioctl,
                      DRM_AUTH | DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_UNREF_DMABUF, vmw_dmabuf_unref_ioctl,
-                     DRM_AUTH | DRM_UNLOCKED),
+                     DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_CURSOR_BYPASS,
                      vmw_kms_cursor_bypass_ioctl,
                      DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
@@ -161,11 +161,11 @@ static const struct drm_ioctl_desc vmw_ioctls[] = {
        VMW_IOCTL_DEF(VMW_CREATE_CONTEXT, vmw_context_define_ioctl,
                      DRM_AUTH | DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_UNREF_CONTEXT, vmw_context_destroy_ioctl,
-                     DRM_AUTH | DRM_UNLOCKED),
+                     DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_CREATE_SURFACE, vmw_surface_define_ioctl,
                      DRM_AUTH | DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_UNREF_SURFACE, vmw_surface_destroy_ioctl,
-                     DRM_AUTH | DRM_UNLOCKED),
+                     DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_REF_SURFACE, vmw_surface_reference_ioctl,
                      DRM_AUTH | DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_EXECBUF, vmw_execbuf_ioctl,
@@ -176,7 +176,7 @@ static const struct drm_ioctl_desc vmw_ioctls[] = {
                      vmw_fence_obj_signaled_ioctl,
                      DRM_AUTH | DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_FENCE_UNREF, vmw_fence_obj_unref_ioctl,
-                     DRM_AUTH | DRM_UNLOCKED),
+                     DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_FENCE_EVENT,
                      vmw_fence_event_ioctl,
                      DRM_AUTH | DRM_UNLOCKED),
@@ -197,7 +197,7 @@ static const struct drm_ioctl_desc vmw_ioctls[] = {
                      DRM_AUTH | DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_UNREF_SHADER,
                      vmw_shader_destroy_ioctl,
-                     DRM_AUTH | DRM_UNLOCKED),
+                     DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_GB_SURFACE_CREATE,
                      vmw_gb_surface_define_ioctl,
                      DRM_AUTH | DRM_UNLOCKED),
@@ -606,6 +606,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        mutex_init(&dev_priv->release_mutex);
        mutex_init(&dev_priv->binding_mutex);
        rwlock_init(&dev_priv->resource_lock);
+       ttm_lock_init(&dev_priv->reservation_sem);
 
        for (i = vmw_res_context; i < vmw_res_max; ++i) {
                idr_init(&dev_priv->res_idr[i]);
@@ -667,6 +668,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
                dev_priv->memory_size = 512*1024*1024;
        }
        dev_priv->max_mob_pages = 0;
+       dev_priv->max_mob_size = 0;
        if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) {
                uint64_t mem_size =
                        vmw_read(dev_priv,
@@ -676,6 +678,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
                dev_priv->prim_bb_mem =
                        vmw_read(dev_priv,
                                 SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM);
+               dev_priv->max_mob_size =
+                       vmw_read(dev_priv, SVGA_REG_MOB_MAX_SIZE);
        } else
                dev_priv->prim_bb_mem = dev_priv->vram_size;
 
@@ -719,7 +723,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
 
        ret = ttm_bo_device_init(&dev_priv->bdev,
                                 dev_priv->bo_global_ref.ref.object,
-                                &vmw_bo_driver, VMWGFX_FILE_PAGE_OFFSET,
+                                &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");
@@ -966,7 +972,6 @@ static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv)
                goto out_no_shman;
 
        file_priv->driver_priv = vmw_fp;
-       dev_priv->bdev.dev_mapping = dev->dev_mapping;
 
        return 0;
 
@@ -977,12 +982,70 @@ out_no_tfile:
        return ret;
 }
 
-static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
-                              unsigned long arg)
+static struct vmw_master *vmw_master_check(struct drm_device *dev,
+                                          struct drm_file *file_priv,
+                                          unsigned int flags)
+{
+       int ret;
+       struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+       struct vmw_master *vmaster;
+
+       if (file_priv->minor->type != DRM_MINOR_LEGACY ||
+           !(flags & DRM_AUTH))
+               return NULL;
+
+       ret = mutex_lock_interruptible(&dev->master_mutex);
+       if (unlikely(ret != 0))
+               return ERR_PTR(-ERESTARTSYS);
+
+       if (file_priv->is_master) {
+               mutex_unlock(&dev->master_mutex);
+               return NULL;
+       }
+
+       /*
+        * Check if we were previously master, but now dropped.
+        */
+       if (vmw_fp->locked_master) {
+               mutex_unlock(&dev->master_mutex);
+               DRM_ERROR("Dropped master trying to access ioctl that "
+                         "requires authentication.\n");
+               return ERR_PTR(-EACCES);
+       }
+       mutex_unlock(&dev->master_mutex);
+
+       /*
+        * Taking the drm_global_mutex after the TTM lock might deadlock
+        */
+       if (!(flags & DRM_UNLOCKED)) {
+               DRM_ERROR("Refusing locked ioctl access.\n");
+               return ERR_PTR(-EDEADLK);
+       }
+
+       /*
+        * Take the TTM lock. Possibly sleep waiting for the authenticating
+        * master to become master again, or for a SIGTERM if the
+        * authenticating master exits.
+        */
+       vmaster = vmw_master(file_priv->master);
+       ret = ttm_read_lock(&vmaster->lock, true);
+       if (unlikely(ret != 0))
+               vmaster = ERR_PTR(ret);
+
+       return vmaster;
+}
+
+static long vmw_generic_ioctl(struct file *filp, unsigned int cmd,
+                             unsigned long arg,
+                             long (*ioctl_func)(struct file *, unsigned int,
+                                                unsigned long))
 {
        struct drm_file *file_priv = filp->private_data;
        struct drm_device *dev = file_priv->minor->dev;
        unsigned int nr = DRM_IOCTL_NR(cmd);
+       struct vmw_master *vmaster;
+       unsigned int flags;
+       long ret;
 
        /*
         * Do extra checking on driver private ioctls.
@@ -991,18 +1054,44 @@ static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
        if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END)
            && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
                const struct drm_ioctl_desc *ioctl =
-                   &vmw_ioctls[nr - DRM_COMMAND_BASE];
+                       &vmw_ioctls[nr - DRM_COMMAND_BASE];
 
                if (unlikely(ioctl->cmd_drv != cmd)) {
                        DRM_ERROR("Invalid command format, ioctl %d\n",
                                  nr - DRM_COMMAND_BASE);
                        return -EINVAL;
                }
+               flags = ioctl->flags;
+       } else if (!drm_ioctl_flags(nr, &flags))
+               return -EINVAL;
+
+       vmaster = vmw_master_check(dev, file_priv, flags);
+       if (unlikely(IS_ERR(vmaster))) {
+               DRM_INFO("IOCTL ERROR %d\n", nr);
+               return PTR_ERR(vmaster);
        }
 
-       return drm_ioctl(filp, cmd, arg);
+       ret = ioctl_func(filp, cmd, arg);
+       if (vmaster)
+               ttm_read_unlock(&vmaster->lock);
+
+       return ret;
 }
 
+static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
+                              unsigned long arg)
+{
+       return vmw_generic_ioctl(filp, cmd, arg, &drm_ioctl);
+}
+
+#ifdef CONFIG_COMPAT
+static long vmw_compat_ioctl(struct file *filp, unsigned int cmd,
+                            unsigned long arg)
+{
+       return vmw_generic_ioctl(filp, cmd, arg, &drm_compat_ioctl);
+}
+#endif
+
 static void vmw_lastclose(struct drm_device *dev)
 {
        struct drm_crtc *crtc;
@@ -1171,12 +1260,11 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
 {
        struct vmw_private *dev_priv =
                container_of(nb, struct vmw_private, pm_nb);
-       struct vmw_master *vmaster = dev_priv->active_master;
 
        switch (val) {
        case PM_HIBERNATION_PREPARE:
        case PM_SUSPEND_PREPARE:
-               ttm_suspend_lock(&vmaster->lock);
+               ttm_suspend_lock(&dev_priv->reservation_sem);
 
                /**
                 * This empties VRAM and unbinds all GMR bindings.
@@ -1190,7 +1278,7 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
        case PM_POST_HIBERNATION:
        case PM_POST_SUSPEND:
        case PM_POST_RESTORE:
-               ttm_suspend_unlock(&vmaster->lock);
+               ttm_suspend_unlock(&dev_priv->reservation_sem);
 
                break;
        case PM_RESTORE_PREPARE:
@@ -1311,7 +1399,7 @@ static const struct file_operations vmwgfx_driver_fops = {
        .poll = vmw_fops_poll,
        .read = vmw_fops_read,
 #if defined(CONFIG_COMPAT)
-       .compat_ioctl = drm_compat_ioctl,
+       .compat_ioctl = vmw_compat_ioctl,
 #endif
        .llseek = noop_llseek,
 };