aboutsummaryrefslogtreecommitdiff
path: root/linux-core/drm_ttm.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core/drm_ttm.c')
-rw-r--r--linux-core/drm_ttm.c138
1 files changed, 117 insertions, 21 deletions
diff --git a/linux-core/drm_ttm.c b/linux-core/drm_ttm.c
index 05bae7de..293b1f8c 100644
--- a/linux-core/drm_ttm.c
+++ b/linux-core/drm_ttm.c
@@ -42,6 +42,7 @@ typedef struct drm_val_action {
int validated;
} drm_val_action_t;
+
/*
* We may be manipulating other processes page tables, so for each TTM, keep track of
* which mm_structs are currently mapping the ttm so that we can take the appropriate
@@ -275,6 +276,7 @@ static drm_ttm_t *drm_init_ttm(struct drm_device * dev, unsigned long size)
ttm->lhandle = 0;
atomic_set(&ttm->vma_count, 0);
+
ttm->destroy = 0;
ttm->num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -746,25 +748,80 @@ int drm_user_create_region(drm_device_t * dev, unsigned long start, int len,
return 0;
}
+
/*
- * Create a ttm and add it to the drm book-keeping.
+ * dev->struct_mutex locked.
*/
-int drm_add_ttm(drm_device_t * dev, unsigned size, drm_map_list_t ** maplist)
+static void drm_ttm_object_remove(drm_device_t *dev, drm_ttm_object_t *object)
{
+ drm_map_list_t *list = &object->map_list;
+ drm_local_map_t *map;
+
+ if (list->user_token)
+ drm_ht_remove_item(&dev->map_hash, &list->hash);
+
+ map = list->map;
+
+ if (map) {
+ drm_ttm_t *ttm = (drm_ttm_t *)map->offset;
+ if (ttm) {
+ if (drm_destroy_ttm(ttm) != -EBUSY) {
+ drm_free(map, sizeof(*map), DRM_MEM_TTM);
+ }
+ } else {
+ drm_free(map, sizeof(*map), DRM_MEM_TTM);
+ }
+ }
+
+ drm_free(object, sizeof(*object), DRM_MEM_TTM);
+}
+
+/*
+ * dev->struct_mutex locked.
+ */
+static void drm_ttm_user_object_remove(drm_file_t *priv, drm_user_object_t *base)
+{
+ drm_ttm_object_t *object;
+ drm_device_t *dev = priv->head->dev;
+
+ object = drm_user_object_entry(base, drm_ttm_object_t, base);
+ if (atomic_dec_and_test(&object->usage))
+ drm_ttm_object_remove(dev, object);
+}
+
+
+
+/*
+ * Create a ttm and add it to the drm book-keeping.
+ * dev->struct_mutex locked.
+ */
+
+int drm_ttm_object_create(drm_device_t *dev, unsigned long size,
+ uint32_t flags, drm_ttm_object_t **ttm_object)
+{
+ drm_ttm_object_t *object;
drm_map_list_t *list;
drm_map_t *map;
drm_ttm_t *ttm;
- map = drm_alloc(sizeof(*map), DRM_MEM_TTM);
- if (!map)
+ object = drm_calloc(1, sizeof(*object), DRM_MEM_TTM);
+ if (!object)
+ return -ENOMEM;
+ object->flags = flags;
+ list = &object->map_list;
+
+ list->map = drm_calloc(1, sizeof(*map), DRM_MEM_TTM);
+ if (!list->map) {
+ drm_ttm_object_remove(dev, object);
return -ENOMEM;
+ }
+ map = list->map;
ttm = drm_init_ttm(dev, size);
-
if (!ttm) {
DRM_ERROR("Could not create ttm\n");
- drm_free(map, sizeof(*map), DRM_MEM_TTM);
+ drm_ttm_object_remove(dev, object);
return -ENOMEM;
}
@@ -772,34 +829,73 @@ int drm_add_ttm(drm_device_t * dev, unsigned size, drm_map_list_t ** maplist)
map->type = _DRM_TTM;
map->flags = _DRM_REMOVABLE;
map->size = size;
-
- list = drm_calloc(1, sizeof(*list), DRM_MEM_TTM);
- if (!list) {
- drm_destroy_ttm(ttm);
- drm_free(map, sizeof(*map), DRM_MEM_TTM);
- return -ENOMEM;
- }
- map->handle = (void *)list;
-
+ map->handle = (void *)object;
+
if (drm_ht_just_insert_please(&dev->map_hash, &list->hash,
(unsigned long) map->handle,
32 - PAGE_SHIFT - 3, PAGE_SHIFT,
DRM_MAP_HASH_OFFSET)) {
- drm_destroy_ttm(ttm);
- drm_free(map, sizeof(*map), DRM_MEM_TTM);
- drm_free(list, sizeof(*list), DRM_MEM_TTM);
+ drm_ttm_object_remove(dev, object);
return -ENOMEM;
}
list->user_token = list->hash.key;
- list->map = map;
-
- *maplist = list;
+ object->base.remove = drm_ttm_user_object_remove;
+ object->base.type = drm_ttm_type;
+ object->base.ref_struct_locked = NULL;
+ object->base.unref = NULL;
+ atomic_set(&object->usage, 1);
return 0;
}
+
int drm_ttm_ioctl(drm_file_t *priv, drm_ttm_arg_t __user *data)
{
+ drm_ttm_arg_t arg;
+ drm_device_t *dev = priv->head->dev;
+ drm_ttm_object_t *entry;
+ drm_user_object_t *uo;
+ unsigned long size;
+ int ret;
+
+ DRM_COPY_FROM_USER_IOCTL(arg, (void __user *)data, sizeof(arg));
+
+ switch(arg.op) {
+ case drm_ttm_create:
+ mutex_lock(&dev->struct_mutex);
+ size = combine_64(arg.size_lo, arg.size_hi);
+ ret = drm_ttm_object_create(dev, size, arg.flags, &entry);
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+ ret = drm_add_user_object(priv, &entry->base,
+ arg.flags & DRM_TTM_FLAG_SHAREABLE);
+ if (ret) {
+ drm_ttm_object_remove(dev, entry);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+ arg.handle = entry->base.hash.key;
+ arg.user_token = entry->map_list.user_token;
+ mutex_unlock(&dev->struct_mutex);
+ break;
+ case drm_ttm_reference:
+ return drm_user_object_ref(priv, arg.handle, drm_ttm_type, &uo);
+ case drm_ttm_unreference:
+ return drm_user_object_unref(priv, arg.handle, drm_ttm_type);
+ case drm_ttm_destroy:
+ mutex_lock(&dev->struct_mutex);
+ uo = drm_lookup_user_object(priv, arg.handle);
+ if (!uo || (uo->type != drm_ttm_type) || uo->owner != priv) {
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+ ret = drm_remove_user_object(priv, uo);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+ DRM_COPY_TO_USER_IOCTL((void __user *)data, arg, sizeof(arg));
return 0;
}