diff options
Diffstat (limited to 'linux/drm_vm.h')
-rw-r--r-- | linux/drm_vm.h | 154 |
1 files changed, 100 insertions, 54 deletions
diff --git a/linux/drm_vm.h b/linux/drm_vm.h index 347816ac..123eea29 100644 --- a/linux/drm_vm.h +++ b/linux/drm_vm.h @@ -41,13 +41,7 @@ struct vm_operations_struct drm_vm_ops = { struct vm_operations_struct drm_vm_shm_ops = { nopage: DRM(vm_shm_nopage), open: DRM(vm_open), - close: DRM(vm_close), -}; - -struct vm_operations_struct drm_vm_shm_lock_ops = { - nopage: DRM(vm_shm_nopage_lock), - open: DRM(vm_open), - close: DRM(vm_close), + close: DRM(vm_shm_close), }; struct vm_operations_struct drm_vm_dma_ops = { @@ -88,12 +82,26 @@ struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, #endif unsigned long physical; unsigned long offset; + unsigned long i; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ if (!map) return NOPAGE_OOM; /* Nothing allocated */ offset = address - vma->vm_start; - physical = (unsigned long)map->handle + offset; + i = (unsigned long)map->handle + offset; + /* We have to walk page tables here because we need large SAREA's, and + * they need to be virtually contigious in kernel space. + */ + pgd = pgd_offset_k( i ); + if( !pgd_present( *pgd ) ) return NOPAGE_OOM; + pmd = pmd_offset( pgd, i ); + if( !pmd_present( *pmd ) ) return NOPAGE_OOM; + pte = pte_offset( pmd, i ); + if( !pte_present( *pte ) ) return NOPAGE_OOM; + physical = (unsigned long)pte_page( *pte )->virtual; atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ DRM_DEBUG("0x%08lx => 0x%08lx\n", address, physical); @@ -104,37 +112,87 @@ struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, #endif } -#if LINUX_VERSION_CODE < 0x020317 -unsigned long DRM(vm_shm_nopage_lock)(struct vm_area_struct *vma, - unsigned long address, - int write_access) -#else - /* Return type changed in 2.3.23 */ -struct page *DRM(vm_shm_nopage_lock)(struct vm_area_struct *vma, - unsigned long address, - int write_access) -#endif +/* Special close routine which deletes map information if we are the last + * person to close a mapping and its not in the global maplist. + */ + +void DRM(vm_shm_close)(struct vm_area_struct *vma) { - drm_file_t *priv = vma->vm_file->private_data; - drm_device_t *dev = priv->dev; - unsigned long physical; - unsigned long offset; - unsigned long page; + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_vma_entry_t *pt, *prev; + drm_map_t *map; + drm_map_list_t *r_list; + struct list_head *list; + int found_maps = 0; - if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ - if (!dev->lock.hw_lock) return NOPAGE_OOM; /* Nothing allocated */ + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_dec(&dev->vma_count); - offset = address - vma->vm_start; - page = offset >> PAGE_SHIFT; - physical = (unsigned long)dev->lock.hw_lock + offset; - atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ +#if LINUX_VERSION_CODE >= 0x020300 + map = vma->vm_private_data; +#else + map = vma->vm_pte; +#endif - DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); -#if LINUX_VERSION_CODE < 0x020317 - return physical; + 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 == map) found_maps++; #else - return virt_to_page(physical); + if (pt->vma->vm_pte == map) found_maps++; #endif + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS); + } + } + /* We were the only map that was found */ + if(found_maps == 1 && + map->flags & _DRM_REMOVABLE) { + /* Check to see if we are in the maplist, if we are not, then + * we delete this mappings information. + */ + found_maps = 0; + list = &dev->maplist->head; + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *) list; + if (r_list->map == map) found_maps++; + } + + 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); } #if LINUX_VERSION_CODE < 0x020317 @@ -176,9 +234,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); @@ -188,8 +244,6 @@ void DRM(vm_open)(struct vm_area_struct *vma) MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ #endif - -#if DRM_DEBUG_CODE vma_entry = DRM(alloc)(sizeof(*vma_entry), DRM_MEM_VMAS); if (vma_entry) { down(&dev->struct_sem); @@ -199,16 +253,13 @@ void DRM(vm_open)(struct vm_area_struct *vma) dev->vmalist = vma_entry; up(&dev->struct_sem); } -#endif } 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); @@ -217,7 +268,6 @@ void DRM(vm_close)(struct vm_area_struct *vma) #endif atomic_dec(&dev->vma_count); -#if DRM_DEBUG_CODE down(&dev->struct_sem); for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { if (pt->vma == vma) { @@ -231,7 +281,6 @@ void DRM(vm_close)(struct vm_area_struct *vma) } } up(&dev->struct_sem); -#endif } int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma) @@ -272,7 +321,8 @@ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; drm_map_t *map = NULL; - int i; + drm_map_list_t *r_list; + struct list_head *list; DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", vma->vm_start, vma->vm_end, VM_OFFSET(vma)); @@ -286,12 +336,13 @@ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) once, so it doesn't have to be optimized for performance, even if the list was a bit longer. */ - for (i = 0; i < dev->map_count; i++) { - map = dev->maplist[i]; + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *)list; + map = r_list->map; + if (!map) continue; if (map->offset == VM_OFFSET(vma)) break; } - if (i >= dev->map_count) return -EINVAL; if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) return -EPERM; @@ -339,17 +390,12 @@ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) vma->vm_ops = &drm_vm_ops; break; case _DRM_SHM: - if (map->flags & _DRM_CONTAINS_LOCK) - vma->vm_ops = &drm_vm_shm_lock_ops; - else { - vma->vm_ops = &drm_vm_shm_ops; + vma->vm_ops = &drm_vm_shm_ops; #if LINUX_VERSION_CODE >= 0x020300 - vma->vm_private_data = (void *)map; + vma->vm_private_data = (void *)map; #else - vma->vm_pte = (unsigned long)map; + vma->vm_pte = (unsigned long)map; #endif - } - /* Don't let this area swap. Change when DRM_KERNEL advisory is supported. */ vma->vm_flags |= VM_LOCKED; |