From 08cb65f7f7545c28165326f8f3b539934fef1f3f Mon Sep 17 00:00:00 2001 From: Jeff Hartmann Date: Mon, 19 Feb 2001 18:25:26 +0000 Subject: [PATCH] Added rmmap vma fixup routine so that the kernel does the right thing when removing mappings that might still exist in a forked process. --- linux-core/drmP.h | 1 + linux-core/drm_bufs.c | 14 +++++++++++++- linux-core/drm_vm.c | 38 ++++++++++++++++++++++++++++++++++---- linux/drmP.h | 1 + linux/drm_bufs.h | 14 +++++++++++++- linux/drm_vm.h | 38 ++++++++++++++++++++++++++++++++++---- 6 files changed, 96 insertions(+), 10 deletions(-) diff --git a/linux-core/drmP.h b/linux-core/drmP.h index a01e91d9..722a7eb5 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -725,6 +725,7 @@ extern void DRM(vm_close)(struct vm_area_struct *vma); extern int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma); extern int DRM(mmap)(struct file *filp, struct vm_area_struct *vma); +extern void DRM(rmmap_fixup_vmas)(drm_device_t *dev, drm_map_t *map); /* Proc support (proc.c) */ diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c index a16e854f..b7f0bd6d 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -82,6 +82,14 @@ int DRM(addmap)( struct inode *inode, struct file *filp, return -EFAULT; } + /* Only allow shared memory to be removable since we only keep enough + * book keeping information about shared memory to allow for removal + * when processes fork. + */ + if ( (map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } DRM_DEBUG( "offset = 0x%08lx, size = 0x%08lx, type = %d\n", map->offset, map->size, map->type ); if ( (map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK)) ) { @@ -157,6 +165,8 @@ int DRM(addmap)( struct inode *inode, struct file *filp, return 0; } +void DRM(rmmap_fixup_vmas)(drm_device_t *dev, drm_map_t *map) + /* Remove a map private from list and deallocate resources */ int DRM(rmmap)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) @@ -192,10 +202,12 @@ int DRM(rmmap)(struct inode *inode, struct file *filp, return -EINVAL; } map = r_list->map; - list_del(list); + list_del(list); up(&dev->struct_sem); DRM(free)(list, sizeof(*list), DRM_MEM_MAPS); + /* Zap any pages that are still mapped and remove no_page vm_op. */ + DRM(rmmap_fixup_vmas)(dev, map); switch (map->type) { case _DRM_REGISTERS: diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c index 23758779..63df2da5 100644 --- a/linux-core/drm_vm.c +++ b/linux-core/drm_vm.c @@ -151,9 +151,7 @@ void DRM(vm_open)(struct vm_area_struct *vma) { drm_file_t *priv = vma->vm_file->private_data; drm_device_t *dev = priv->dev; -#if DRM_DEBUG_CODE drm_vma_entry_t *vma_entry; -#endif DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); @@ -178,9 +176,7 @@ void DRM(vm_close)(struct vm_area_struct *vma) { drm_file_t *priv = vma->vm_file->private_data; drm_device_t *dev = priv->dev; -#if DRM_DEBUG_CODE drm_vma_entry_t *pt, *prev; -#endif DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); @@ -335,3 +331,37 @@ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) DRM(vm_open)(vma); return 0; } + +/* Support for rmmap so we can safely delete mappings without forcing + * them to be unmapped (which isn't possible if the process forked) + * before we call rmmap. + */ + +void DRM(rmmap_fixup_vmas)(drm_device_t *dev, drm_map_t *map) +{ + drm_vma_entry_t *pt, *prev; + + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { +#if LINUX_VERSION_CODE >= 0x020300 + if (pt->vma->vm_private_data == (void *)map) +#else + if (pt->vma->vm_pte == (unsigned long)map) +#endif + { + /* Zap the mappings */ + flush_cache_range(vma->vm_mm, + vma->vm_start, + vma->vm_end - vma->vm_start); + zap_page_range(vma->vm_mm, + vma->vm_start, + vma->vm_end - vma->vm_start); + flush_tlb_range(vma->vm_mm, + vma->vm_start, + vma->vm_end - vma->vm_start); + /* Change the vm_ops so no page isn't defined */ + vma->vm_ops = &drm_vm_ops; + } + } + up(&dev->struct_sem); +} diff --git a/linux/drmP.h b/linux/drmP.h index a01e91d9..722a7eb5 100644 --- a/linux/drmP.h +++ b/linux/drmP.h @@ -725,6 +725,7 @@ extern void DRM(vm_close)(struct vm_area_struct *vma); extern int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma); extern int DRM(mmap)(struct file *filp, struct vm_area_struct *vma); +extern void DRM(rmmap_fixup_vmas)(drm_device_t *dev, drm_map_t *map); /* Proc support (proc.c) */ diff --git a/linux/drm_bufs.h b/linux/drm_bufs.h index a16e854f..b7f0bd6d 100644 --- a/linux/drm_bufs.h +++ b/linux/drm_bufs.h @@ -82,6 +82,14 @@ int DRM(addmap)( struct inode *inode, struct file *filp, return -EFAULT; } + /* Only allow shared memory to be removable since we only keep enough + * book keeping information about shared memory to allow for removal + * when processes fork. + */ + if ( (map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } DRM_DEBUG( "offset = 0x%08lx, size = 0x%08lx, type = %d\n", map->offset, map->size, map->type ); if ( (map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK)) ) { @@ -157,6 +165,8 @@ int DRM(addmap)( struct inode *inode, struct file *filp, return 0; } +void DRM(rmmap_fixup_vmas)(drm_device_t *dev, drm_map_t *map) + /* Remove a map private from list and deallocate resources */ int DRM(rmmap)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) @@ -192,10 +202,12 @@ int DRM(rmmap)(struct inode *inode, struct file *filp, return -EINVAL; } map = r_list->map; - list_del(list); + list_del(list); up(&dev->struct_sem); DRM(free)(list, sizeof(*list), DRM_MEM_MAPS); + /* Zap any pages that are still mapped and remove no_page vm_op. */ + DRM(rmmap_fixup_vmas)(dev, map); switch (map->type) { case _DRM_REGISTERS: diff --git a/linux/drm_vm.h b/linux/drm_vm.h index 23758779..63df2da5 100644 --- a/linux/drm_vm.h +++ b/linux/drm_vm.h @@ -151,9 +151,7 @@ void DRM(vm_open)(struct vm_area_struct *vma) { drm_file_t *priv = vma->vm_file->private_data; drm_device_t *dev = priv->dev; -#if DRM_DEBUG_CODE drm_vma_entry_t *vma_entry; -#endif DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); @@ -178,9 +176,7 @@ void DRM(vm_close)(struct vm_area_struct *vma) { drm_file_t *priv = vma->vm_file->private_data; drm_device_t *dev = priv->dev; -#if DRM_DEBUG_CODE drm_vma_entry_t *pt, *prev; -#endif DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); @@ -335,3 +331,37 @@ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) DRM(vm_open)(vma); return 0; } + +/* Support for rmmap so we can safely delete mappings without forcing + * them to be unmapped (which isn't possible if the process forked) + * before we call rmmap. + */ + +void DRM(rmmap_fixup_vmas)(drm_device_t *dev, drm_map_t *map) +{ + drm_vma_entry_t *pt, *prev; + + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { +#if LINUX_VERSION_CODE >= 0x020300 + if (pt->vma->vm_private_data == (void *)map) +#else + if (pt->vma->vm_pte == (unsigned long)map) +#endif + { + /* Zap the mappings */ + flush_cache_range(vma->vm_mm, + vma->vm_start, + vma->vm_end - vma->vm_start); + zap_page_range(vma->vm_mm, + vma->vm_start, + vma->vm_end - vma->vm_start); + flush_tlb_range(vma->vm_mm, + vma->vm_start, + vma->vm_end - vma->vm_start); + /* Change the vm_ops so no page isn't defined */ + vma->vm_ops = &drm_vm_ops; + } + } + up(&dev->struct_sem); +}