Protect drm_mmap against disappearing maps.
authorThomas Hellstrom <thomas-at-tungstengraphics-dot-com>
Wed, 31 Jan 2007 23:38:57 +0000 (00:38 +0100)
committerThomas Hellstrom <thomas-at-tungstengraphics-dot-com>
Wed, 31 Jan 2007 23:38:57 +0000 (00:38 +0100)
The map lists and hash tables are protected using dev->struct_mutex,
but drm_mmap strangely never locked this mutex.

linux-core/drm_vm.c

index 827a7bd..b11e09f 100644 (file)
@@ -42,8 +42,7 @@
 static void drm_vm_open(struct vm_area_struct *vma);
 static void drm_vm_close(struct vm_area_struct *vma);
 static void drm_vm_ttm_close(struct vm_area_struct *vma);
-static int drm_vm_ttm_open(struct vm_area_struct *vma);
-static void drm_vm_ttm_open_wrapper(struct vm_area_struct *vma);
+static void drm_vm_ttm_open(struct vm_area_struct *vma);
 
 
 pgprot_t drm_io_prot(uint32_t map_type, struct vm_area_struct *vma)
@@ -511,13 +510,13 @@ static struct vm_operations_struct drm_vm_sg_ops = {
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21))
 static struct vm_operations_struct drm_vm_ttm_ops = {
        .nopage = drm_vm_ttm_nopage,
-       .open = drm_vm_ttm_open_wrapper,
+       .open = drm_vm_ttm_open,
        .close = drm_vm_ttm_close,
 };
 #else
 static struct vm_operations_struct drm_vm_ttm_ops = {
        .fault = drm_vm_ttm_fault,
-       .open = drm_vm_ttm_open_wrapper,
+       .open = drm_vm_ttm_open,
        .close = drm_vm_ttm_close,
 };
 #endif
@@ -530,7 +529,7 @@ static struct vm_operations_struct drm_vm_ttm_ops = {
  * Create a new drm_vma_entry structure as the \p vma private data entry and
  * add it to drm_device::vmalist.
  */
-static void drm_vm_open(struct vm_area_struct *vma)
+static void drm_vm_open_locked(struct vm_area_struct *vma)
 {
        drm_file_t *priv = vma->vm_file->private_data;
        drm_device_t *dev = priv->head->dev;
@@ -542,36 +541,43 @@ static void drm_vm_open(struct vm_area_struct *vma)
 
        vma_entry = drm_ctl_alloc(sizeof(*vma_entry), DRM_MEM_VMAS);
        if (vma_entry) {
-               mutex_lock(&dev->struct_mutex);
                vma_entry->vma = vma;
                vma_entry->next = dev->vmalist;
                vma_entry->pid = current->pid;
                dev->vmalist = vma_entry;
-               mutex_unlock(&dev->struct_mutex);
        }
 }
 
-static int drm_vm_ttm_open(struct vm_area_struct *vma) {
-  
-       drm_local_map_t *map = (drm_local_map_t *)vma->vm_private_data;
-       drm_ttm_t *ttm;
+static void drm_vm_open(struct vm_area_struct *vma)
+{
        drm_file_t *priv = vma->vm_file->private_data;
        drm_device_t *dev = priv->head->dev;
 
-       drm_vm_open(vma);
        mutex_lock(&dev->struct_mutex);
+       drm_vm_open_locked(vma);
+       mutex_unlock(&dev->struct_mutex);
+}
+
+static void drm_vm_ttm_open_locked(struct vm_area_struct *vma) {
+  
+       drm_local_map_t *map = (drm_local_map_t *)vma->vm_private_data;
+       drm_ttm_t *ttm;
+
+       drm_vm_open_locked(vma);
        ttm = (drm_ttm_t *) map->offset;
        atomic_inc(&ttm->vma_count);
 #ifdef DRM_ODD_MM_COMPAT
        drm_ttm_add_vma(ttm, vma);
 #endif
-       mutex_unlock(&dev->struct_mutex);
-       return 0;
 }
 
-static void drm_vm_ttm_open_wrapper(struct vm_area_struct *vma) 
-{
-       drm_vm_ttm_open(vma);
+static void drm_vm_ttm_open(struct vm_area_struct *vma) {
+       drm_file_t *priv = vma->vm_file->private_data;
+       drm_device_t *dev = priv->head->dev;
+       
+       mutex_lock(&dev->struct_mutex);
+       drm_vm_ttm_open_locked(vma);
+       mutex_unlock(&dev->struct_mutex);
 }
 
 /**
@@ -653,7 +659,6 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
        drm_device_dma_t *dma;
        unsigned long length = vma->vm_end - vma->vm_start;
 
-       lock_kernel();
        dev = priv->head->dev;
        dma = dev->dma;
        DRM_DEBUG("start = 0x%lx, end = 0x%lx, page offset = 0x%lx\n",
@@ -664,7 +669,6 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
                unlock_kernel();
                return -EINVAL;
        }
-       unlock_kernel();
 
        if (!capable(CAP_SYS_ADMIN) &&
            (dma->flags & _DRM_DMA_USE_PCI_RO)) {
@@ -686,7 +690,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
        vma->vm_flags |= VM_RESERVED;   /* Don't swap */
 
        vma->vm_file = filp;    /* Needed for drm_vm_open() */
-       drm_vm_open(vma);
+       drm_vm_open_locked(vma);
        return 0;
 }
 
@@ -719,7 +723,7 @@ EXPORT_SYMBOL(drm_core_get_reg_ofs);
  * according to the mapping type and remaps the pages. Finally sets the file
  * pointer and calls vm_open().
  */
-int drm_mmap(struct file *filp, struct vm_area_struct *vma)
+static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
 {
        drm_file_t *priv = filp->private_data;
        drm_device_t *dev = priv->head->dev;
@@ -839,12 +843,8 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
                vma->vm_file = filp;
                vma->vm_flags |= VM_RESERVED | VM_IO;
 #ifdef DRM_ODD_MM_COMPAT
-               mutex_lock(&dev->struct_mutex);
                drm_ttm_map_bound(vma);
-               mutex_unlock(&dev->struct_mutex);
 #endif         
-               if (drm_vm_ttm_open(vma))
-                       return -EAGAIN;
                return 0;
        }
        default:
@@ -853,7 +853,20 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
        vma->vm_flags |= VM_RESERVED;   /* Don't swap */
 
        vma->vm_file = filp;    /* Needed for drm_vm_open() */
-       drm_vm_open(vma);
+       drm_vm_open_locked(vma);
        return 0;
 }
+
+int drm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       drm_file_t *priv = filp->private_data;
+       drm_device_t *dev = priv->head->dev;
+       int ret;
+
+       mutex_lock(&dev->struct_mutex);
+       ret = drm_mmap_locked(filp, vma);
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
 EXPORT_SYMBOL(drm_mmap);