diff options
Diffstat (limited to 'linux-core/drm_bufs.c')
-rw-r--r-- | linux-core/drm_bufs.c | 118 |
1 files changed, 99 insertions, 19 deletions
diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c index b7e27d172..1d5403225 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -69,6 +69,7 @@ int DRM(addmap)( struct inode *inode, struct file *filp, drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; drm_map_t *map; + drm_map_list_t *list; if ( !(filp->f_mode & 3) ) return -EACCES; /* Require read/write */ @@ -81,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)) ) { @@ -100,7 +109,7 @@ int DRM(addmap)( struct inode *inode, struct file *filp, return -EINVAL; } #endif -#ifdef CONFIG_MTRR +#ifdef __REALLY_HAVE_MTRR if ( map->type == _DRM_FRAME_BUFFER || (map->flags & _DRM_WRITE_COMBINING) ) { map->mtrr = mtrr_add( map->offset, map->size, @@ -111,9 +120,7 @@ int DRM(addmap)( struct inode *inode, struct file *filp, break; case _DRM_SHM: - map->handle = (void *)DRM(alloc_pages)( DRM(order)( map->size ) - - PAGE_SHIFT, - DRM_MEM_SAREA ); + map->handle = vmalloc_32(map->size); DRM_DEBUG( "%ld %d %p\n", map->size, DRM(order)( map->size ), map->handle ); if ( !map->handle ) { @@ -136,22 +143,17 @@ int DRM(addmap)( struct inode *inode, struct file *filp, return -EINVAL; } - down( &dev->struct_sem ); - if ( dev->maplist ) { - ++dev->map_count; - dev->maplist = DRM(realloc)( dev->maplist, - (dev->map_count-1) - * sizeof(*dev->maplist), - dev->map_count - * sizeof(*dev->maplist), - DRM_MEM_MAPS ); - } else { - dev->map_count = 1; - dev->maplist = DRM(alloc)( dev->map_count*sizeof(*dev->maplist), - DRM_MEM_MAPS ); + list = DRM(alloc)(sizeof(*list), DRM_MEM_MAPS); + if(!list) { + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; } - dev->maplist[dev->map_count-1] = map; - up( &dev->struct_sem ); + memset(list, 0, sizeof(*list)); + list->map = map; + + down(&dev->struct_sem); + list_add(&list->head, &dev->maplist->head); + up(&dev->struct_sem); if ( copy_to_user( (drm_map_t *)arg, map, sizeof(*map) ) ) return -EFAULT; @@ -164,6 +166,84 @@ int DRM(addmap)( struct inode *inode, struct file *filp, return 0; } + +/* Remove a map private from list and deallocate resources if the mapping + * isn't in use. + */ + +int DRM(rmmap)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + struct list_head *list; + drm_map_list_t *r_list; + drm_vma_entry_t *pt, *prev; + drm_map_t *map; + drm_map_t request; + int found_maps = 0; + + if (copy_from_user(&request, (drm_map_t *)arg, + sizeof(request))) { + return -EFAULT; + } + + down(&dev->struct_sem); + list = &dev->maplist->head; + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *) list; + + if(r_list->map && + r_list->map->handle == request.handle && + r_list->map->flags & _DRM_REMOVABLE) break; + } + + /* List has wrapped around to the head pointer, or its empty we didn't + * find anything. + */ + if(list == (&dev->maplist->head)) { + up(&dev->struct_sem); + return -EINVAL; + } + map = r_list->map; + list_del(list); + DRM(free)(list, sizeof(*list), DRM_MEM_MAPS); + + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { +#if LINUX_VERSION_CODE >= 0x020300 + if (pt->vma->vm_private_data == map) found_maps++; +#else + if (pt->vma->vm_pte == map) found_maps++; +#endif + } + + if(!found_maps) { + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef __REALLY_HAVE_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + DRM(ioremapfree)(map->handle, map->size); + break; + case _DRM_SHM: + vfree(map->handle); + break; + case _DRM_AGP: + break; + } + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + } + up(&dev->struct_sem); + return 0; +} + #if __HAVE_DMA #if __REALLY_HAVE_AGP |