summaryrefslogtreecommitdiff
path: root/linux/drm_vm.h
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drm_vm.h')
-rw-r--r--linux/drm_vm.h154
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;