summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2006-08-21 20:30:19 +0200
committerThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2006-08-21 20:30:19 +0200
commit42c2cfcf7d5730a2961d425228e042f533b312fa (patch)
tree50cd98133445a2e3af6309f35c7bdf7af5603eaa
parent11f9e404fb66927146de30227fa05c5485aa1726 (diff)
Generic DRM support base-class support for user-space objects, like
fence objects and buffer objects: Refcounting, Inter-process sharing, Synchronization Destruction.
-rw-r--r--linux-core/Makefile.kernel2
-rw-r--r--linux-core/drmP.h147
-rw-r--r--linux-core/drm_drv.c1
-rw-r--r--linux-core/drm_fops.c123
-rw-r--r--linux-core/drm_lock.c57
-rw-r--r--linux-core/drm_object.c289
-rw-r--r--linux-core/drm_stub.c6
7 files changed, 579 insertions, 46 deletions
diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel
index 211e5b05..651e30b0 100644
--- a/linux-core/Makefile.kernel
+++ b/linux-core/Makefile.kernel
@@ -12,7 +12,7 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \
drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \
drm_memory_debug.o ati_pcigart.o drm_sman.o \
- drm_hashtab.o drm_mm.o
+ drm_hashtab.o drm_mm.o drm_object.o
tdfx-objs := tdfx_drv.o
r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o
mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index 6cbb810f..81ca6aec 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -154,6 +154,8 @@
#define DRM_MEM_CTXLIST 21
#define DRM_MEM_MM 22
#define DRM_MEM_HASHTAB 23
+#define DRM_MEM_OBJECTS 24
+
#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
#define DRM_MAP_HASH_OFFSET 0x10000000
@@ -387,6 +389,19 @@ typedef struct drm_buf_entry {
drm_freelist_t freelist;
} drm_buf_entry_t;
+/*
+ * This should be small enough to allow the use of kmalloc for hash tables
+ * instead of vmalloc.
+ */
+
+#define DRM_FILE_HASH_ORDER 8
+typedef enum{
+ _DRM_REF_USE=0,
+ _DRM_REF_TYPE1,
+ _DRM_NO_REF_TYPES
+} drm_ref_t;
+
+
/** File private data */
typedef struct drm_file {
int authenticated;
@@ -401,6 +416,18 @@ typedef struct drm_file {
struct drm_head *head;
int remove_auth_on_close;
unsigned long lock_count;
+
+ /*
+ * The user object hash table is global and resides in the
+ * drm_device structure. We protect the lists and hash tables with the
+ * device struct_mutex. A bit coarse-grained but probably the best
+ * option.
+ */
+
+ struct list_head refd_objects;
+ struct list_head user_objects;
+
+ drm_open_hash_t refd_object_hash[_DRM_NO_REF_TYPES];
void *driver_priv;
} drm_file_t;
@@ -564,6 +591,7 @@ typedef struct drm_mm {
* a family of cards. There will one drm_device for each card present
* in this family
*/
+
struct drm_device;
struct drm_driver {
int (*load) (struct drm_device *, unsigned long flags);
@@ -638,6 +666,7 @@ typedef struct drm_head {
struct class_device *dev_class;
} drm_head_t;
+
/**
* DRM device structure. This structure represent a complete card that
* may contain multiple heads.
@@ -685,6 +714,7 @@ typedef struct drm_device {
drm_map_list_t *maplist; /**< Linked list of regions */
int map_count; /**< Number of mappable regions */
drm_open_hash_t map_hash; /**< User token hash table for maps */
+ drm_open_hash_t object_hash; /**< User token hash table for objects */
/** \name Context handle management */
/*@{ */
@@ -809,6 +839,63 @@ static inline int drm_mtrr_del(int handle, unsigned long offset,
#define drm_core_has_MTRR(dev) (0)
#endif
+/*
+ * User space objects and their references.
+ */
+
+#define drm_user_object_entry(_ptr, _type, _member) container_of(_ptr, _type, _member)
+
+typedef enum {
+ drm_fence_type,
+ drm_buffer_type
+
+ /*
+ * Add other user space object types here.
+ */
+
+} drm_object_type_t;
+
+
+
+
+/*
+ * A user object is a structure that helps the drm give out user handles
+ * to kernel internal objects and to keep track of these objects so that
+ * they can be destroyed, for example when the user space process exits.
+ * Designed to be accessible using a user space 32-bit handle.
+ */
+
+typedef struct drm_user_object{
+ drm_hash_item_t hash;
+ struct list_head list;
+ drm_object_type_t type;
+ atomic_t refcount;
+ int shareable;
+ drm_file_t *owner;
+ void (*ref_struct_locked) (drm_file_t *priv, struct drm_user_object *obj,
+ drm_ref_t ref_action);
+ void (*unref)(drm_file_t *priv, struct drm_user_object *obj,
+ drm_ref_t unref_action);
+ void (*remove)(drm_file_t *priv, struct drm_user_object *obj);
+} drm_user_object_t;
+
+/*
+ * A ref object is a structure which is used to
+ * keep track of references to user objects and to keep track of these
+ * references so that they can be destroyed for example when the user space
+ * process exits. Designed to be accessible using a pointer to the _user_ object.
+ */
+
+
+typedef struct drm_ref_object {
+ drm_hash_item_t hash;
+ struct list_head list;
+ atomic_t refcount;
+ drm_ref_t unref_action;
+} drm_ref_object_t;
+
+
+
/******************************************************************/
/** \name Internal function definitions */
/*@{*/
@@ -837,6 +924,7 @@ unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait);
extern int drm_mmap(struct file *filp, struct vm_area_struct *vma);
extern unsigned long drm_core_get_map_ofs(drm_map_t * map);
extern unsigned long drm_core_get_reg_ofs(struct drm_device *dev);
+extern pgprot_t drm_io_prot(uint32_t map_type, struct vm_area_struct *vma);
/* Memory management support (drm_memory.h) */
#include "drm_memory.h"
@@ -915,6 +1003,13 @@ extern int drm_unlock(struct inode *inode, struct file *filp,
extern int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context);
extern int drm_lock_free(drm_device_t * dev,
__volatile__ unsigned int *lock, unsigned int context);
+/*
+ * These are exported to drivers so that they can implement fencing using
+ * DMA quiscent + idle. DMA quiescent usually requires the hardware lock.
+ */
+
+extern int drm_i_have_hw_lock(struct file *filp);
+extern int drm_kernel_take_hw_lock(struct file *filp);
/* Buffer management support (drm_bufs.h) */
extern int drm_addbufs_agp(drm_device_t * dev, drm_buf_desc_t * request);
@@ -1058,6 +1153,58 @@ extern int drm_mm_init(drm_mm_t *mm, unsigned long start, unsigned long size);
extern void drm_mm_takedown(drm_mm_t *mm);
+/*
+ * User space object bookkeeping (drm_object.c)
+ */
+
+/*
+ * Must be called with the struct_mutex held.
+ */
+
+extern int drm_add_user_object(drm_file_t *priv, drm_user_object_t *item,
+
+/*
+ * Must be called with the struct_mutex held.
+ */
+ int shareable);
+extern drm_user_object_t *drm_lookup_user_object(drm_file_t *priv, uint32_t key);
+
+/*
+ * Must be called with the struct_mutex held.
+ * If "item" has been obtained by a call to drm_lookup_user_object. You may not
+ * release the struct_mutex before calling drm_remove_ref_object.
+ * This function may temporarily release the struct_mutex.
+ */
+
+extern int drm_remove_user_object(drm_file_t *priv, drm_user_object_t *item);
+
+/*
+ * Must be called with the struct_mutex held. May temporarily release it.
+ */
+
+extern int drm_add_ref_object(drm_file_t *priv, drm_user_object_t *referenced_object,
+ drm_ref_t ref_action);
+
+/*
+ * Must be called with the struct_mutex held.
+ */
+
+drm_ref_object_t *drm_lookup_ref_object(drm_file_t *priv,
+ drm_user_object_t *referenced_object,
+ drm_ref_t ref_action);
+/*
+ * Must be called with the struct_mutex held.
+ * If "item" has been obtained by a call to drm_lookup_ref_object. You may not
+ * release the struct_mutex before calling drm_remove_ref_object.
+ * This function may temporarily release the struct_mutex.
+ */
+
+extern void drm_remove_ref_object(drm_file_t *priv, drm_ref_object_t *item);
+extern int drm_user_object_ref(drm_file_t *priv, uint32_t user_token, drm_object_type_t type,
+ drm_user_object_t **object);
+extern int drm_user_object_unref(drm_file_t *priv, uint32_t user_token, drm_object_type_t type);
+
+
/* Inline replacements for DRM_IOREMAP macros */
static __inline__ void drm_core_ioremap(struct drm_map *map,
struct drm_device *dev)
diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c
index 9712170b..ccfd1855 100644
--- a/linux-core/drm_drv.c
+++ b/linux-core/drm_drv.c
@@ -351,6 +351,7 @@ static void __exit drm_cleanup(drm_device_t * dev)
drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
dev->maplist = NULL;
drm_ht_remove(&dev->map_hash);
+ drm_ht_remove(&dev->object_hash);
}
if (!drm_fb_loaded)
diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c
index 691edff9..10516bdd 100644
--- a/linux-core/drm_fops.c
+++ b/linux-core/drm_fops.c
@@ -47,6 +47,7 @@ static int drm_setup(drm_device_t * dev)
int i;
int ret;
+
if (dev->driver->firstopen) {
ret = dev->driver->firstopen(dev);
if (ret != 0)
@@ -56,6 +57,7 @@ static int drm_setup(drm_device_t * dev)
dev->magicfree.next = NULL;
/* prebuild the SAREA */
+
i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, _DRM_CONTAINS_LOCK, &map);
if (i != 0)
return i;
@@ -233,6 +235,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
int minor = iminor(inode);
drm_file_t *priv;
int ret;
+ int i,j;
if (filp->f_flags & O_EXCL)
return -EBUSY; /* No exclusive opens */
@@ -256,6 +259,22 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->authenticated = capable(CAP_SYS_ADMIN);
priv->lock_count = 0;
+ INIT_LIST_HEAD(&priv->user_objects);
+ INIT_LIST_HEAD(&priv->refd_objects);
+
+ for (i=0; i<_DRM_NO_REF_TYPES; ++i) {
+ ret = drm_ht_create(&priv->refd_object_hash[i], DRM_FILE_HASH_ORDER);
+ if (ret)
+ break;
+ }
+
+ if (ret) {
+ for(j=0; j<i; ++j) {
+ drm_ht_remove(&priv->refd_object_hash[j]);
+ }
+ goto out_free;
+ }
+
if (dev->driver->open) {
ret = dev->driver->open(dev, priv);
if (ret < 0)
@@ -320,6 +339,53 @@ int drm_fasync(int fd, struct file *filp, int on)
}
EXPORT_SYMBOL(drm_fasync);
+static void drm_object_release(struct file *filp) {
+
+ drm_file_t *priv = filp->private_data;
+ struct list_head *head;
+ drm_user_object_t *user_object;
+ drm_ref_object_t *ref_object;
+ int i;
+
+ /*
+ * Free leftover ref objects created by me. Note that we cannot use
+ * list_for_each() here, as the struct_mutex may be temporarily released
+ * by the remove_() functions, and thus the lists may be altered.
+ * Also, a drm_remove_ref_object() will not remove it
+ * from the list unless its refcount is 1.
+ */
+
+ head = &priv->refd_objects;
+ while (head->next != head) {
+ ref_object = list_entry(head->next, drm_ref_object_t, list);
+ drm_remove_ref_object(priv, ref_object);
+ head = &priv->refd_objects;
+ }
+
+ /*
+ * Free leftover user objects created by me.
+ */
+
+ head = &priv->user_objects;
+ while (head->next != head) {
+ user_object = list_entry(head->next, drm_user_object_t, list);
+ drm_remove_user_object(priv, user_object);
+ head = &priv->user_objects;
+ }
+
+
+
+
+ for(i=0; i<_DRM_NO_REF_TYPES; ++i) {
+ drm_ht_remove(&priv->refd_object_hash[i]);
+ }
+}
+
+
+
+
+
+
/**
* Release file.
*
@@ -354,58 +420,24 @@ int drm_release(struct inode *inode, struct file *filp)
current->pid, (long)old_encode_dev(priv->head->device),
dev->open_count);
- if (priv->lock_count && dev->lock.hw_lock &&
- _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
- dev->lock.filp == filp) {
+ if (dev->driver->reclaim_buffers_locked) {
+ retcode = drm_kernel_take_hw_lock(filp);
+ if (!retcode) {
+ dev->driver->reclaim_buffers_locked(dev, filp);
+
+ drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ }
+
+ } else if (drm_i_have_hw_lock(filp)) {
DRM_DEBUG("File %p released, freeing lock for context %d\n",
filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
- if (dev->driver->reclaim_buffers_locked)
- dev->driver->reclaim_buffers_locked(dev, filp);
-
drm_lock_free(dev, &dev->lock.hw_lock->lock,
_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
-
- /* FIXME: may require heavy-handed reset of
- hardware at this point, possibly
- processed via a callback to the X
- server. */
- } else if (dev->driver->reclaim_buffers_locked && priv->lock_count
- && dev->lock.hw_lock) {
- /* The lock is required to reclaim buffers */
- DECLARE_WAITQUEUE(entry, current);
-
- add_wait_queue(&dev->lock.lock_queue, &entry);
- for (;;) {
- __set_current_state(TASK_INTERRUPTIBLE);
- if (!dev->lock.hw_lock) {
- /* Device has been unregistered */
- retcode = -EINTR;
- break;
- }
- if (drm_lock_take(&dev->lock.hw_lock->lock,
- DRM_KERNEL_CONTEXT)) {
- dev->lock.filp = filp;
- dev->lock.lock_time = jiffies;
- atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
- break; /* Got lock */
- }
- /* Contention */
- schedule();
- if (signal_pending(current)) {
- retcode = -ERESTARTSYS;
- break;
- }
- }
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&dev->lock.lock_queue, &entry);
- if (!retcode) {
- dev->driver->reclaim_buffers_locked(dev, filp);
- drm_lock_free(dev, &dev->lock.hw_lock->lock,
- DRM_KERNEL_CONTEXT);
- }
}
+
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
!dev->driver->reclaim_buffers_locked) {
dev->driver->reclaim_buffers(dev, filp);
@@ -435,6 +467,7 @@ int drm_release(struct inode *inode, struct file *filp)
mutex_unlock(&dev->ctxlist_mutex);
mutex_lock(&dev->struct_mutex);
+ drm_object_release(filp);
if (priv->remove_auth_on_close == 1) {
drm_file_t *temp = dev->file_first;
while (temp) {
diff --git a/linux-core/drm_lock.c b/linux-core/drm_lock.c
index a268d8ee..c12e4897 100644
--- a/linux-core/drm_lock.c
+++ b/linux-core/drm_lock.c
@@ -308,3 +308,60 @@ static int drm_notifier(void *priv)
} while (prev != old);
return 0;
}
+
+/*
+ * Can be used by drivers to take the hardware lock if necessary.
+ * (Waiting for idle before reclaiming buffers etc.)
+ */
+
+int drm_i_have_hw_lock(struct file *filp)
+{
+ DRM_DEVICE;
+
+ return (priv->lock_count && dev->lock.hw_lock &&
+ _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
+ dev->lock.filp == filp);
+}
+
+EXPORT_SYMBOL(drm_i_have_hw_lock);
+
+int drm_kernel_take_hw_lock(struct file *filp)
+{
+ DRM_DEVICE;
+
+ int ret = 0;
+
+ if (!drm_i_have_hw_lock(filp)) {
+
+ DECLARE_WAITQUEUE(entry, current);
+
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ ret = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ dev->lock.filp = filp;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
+ break; /* Got lock */
+ }
+ /* Contention */
+ schedule();
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ }
+ return ret;
+}
+
+EXPORT_SYMBOL(drm_kernel_take_hw_lock);
+
diff --git a/linux-core/drm_object.c b/linux-core/drm_object.c
new file mode 100644
index 00000000..b928c01e
--- /dev/null
+++ b/linux-core/drm_object.c
@@ -0,0 +1,289 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ *
+ **************************************************************************/
+
+#include "drmP.h"
+
+int drm_add_user_object(drm_file_t * priv, drm_user_object_t * item,
+ int shareable)
+{
+ drm_device_t *dev = priv->head->dev;
+ int ret;
+
+ atomic_set(&item->refcount, 1);
+ item->shareable = shareable;
+ item->owner = priv;
+
+ ret = drm_ht_just_insert_please(&dev->object_hash, &item->hash,
+ (unsigned long)item, 32, 0, 0);
+ if (ret)
+ return ret;
+
+ list_add_tail(&item->list, &priv->user_objects);
+ return 0;
+}
+
+drm_user_object_t *drm_lookup_user_object(drm_file_t * priv, uint32_t key)
+{
+ drm_device_t *dev = priv->head->dev;
+ drm_hash_item_t *hash;
+ int ret;
+ drm_user_object_t *item;
+
+ ret = drm_ht_find_item(&dev->object_hash, key, &hash);
+ if (ret) {
+ return NULL;
+ }
+ item = drm_hash_entry(hash, drm_user_object_t, hash);
+
+ if (priv != item->owner) {
+ drm_open_hash_t *ht = &priv->refd_object_hash[_DRM_REF_USE];
+ ret = drm_ht_find_item(ht, (unsigned long)item, &hash);
+ if (ret) {
+ DRM_ERROR("Object not registered for usage\n");
+ return NULL;
+ }
+ }
+ return item;
+}
+
+static void drm_deref_user_object(drm_file_t * priv, drm_user_object_t * item)
+{
+ drm_device_t *dev = priv->head->dev;
+ int ret;
+
+ if (atomic_dec_and_test(&item->refcount)) {
+ ret = drm_ht_remove_item(&dev->object_hash, &item->hash);
+ BUG_ON(ret);
+ list_del_init(&item->list);
+ item->remove(priv, item);
+ }
+}
+
+int drm_remove_user_object(drm_file_t * priv, drm_user_object_t * item)
+{
+ if (item->owner != priv) {
+ DRM_ERROR("Cannot destroy object not owned by you.\n");
+ return -EINVAL;
+ }
+ item->owner = 0;
+ item->shareable = 0;
+ list_del_init(&item->list);
+ drm_deref_user_object(priv, item);
+ return 0;
+}
+
+static int drm_object_ref_action(drm_file_t * priv, drm_user_object_t * ro,
+ drm_ref_t action)
+{
+ int ret = 0;
+
+ switch (action) {
+ case _DRM_REF_USE:
+ atomic_inc(&ro->refcount);
+ break;
+ default:
+ if (!ro->ref_struct_locked) {
+ DRM_ERROR("Register object called without register"
+ " capabilities\n");
+ ret = -EINVAL;
+ break;
+ } else {
+ ro->ref_struct_locked(priv, ro, action);
+ }
+ }
+ return ret;
+}
+
+int drm_add_ref_object(drm_file_t * priv, drm_user_object_t * referenced_object,
+ drm_ref_t ref_action)
+{
+ int ret = 0;
+ drm_ref_object_t *item;
+ drm_open_hash_t *ht = &priv->refd_object_hash[ref_action];
+
+ if (!referenced_object->shareable && priv != referenced_object->owner) {
+ DRM_ERROR("Not allowed to reference this object\n");
+ return -EINVAL;
+ }
+
+ /*
+ * If this is not a usage reference, Check that usage has been registered
+ * first. Otherwise strange things may happen on destruction.
+ */
+
+ if ((ref_action != _DRM_REF_USE) && priv != referenced_object->owner) {
+ item =
+ drm_lookup_ref_object(priv, referenced_object,
+ _DRM_REF_USE);
+ if (!item) {
+ DRM_ERROR
+ ("Object not registered for usage by this client\n");
+ return -EINVAL;
+ }
+ }
+
+ if (NULL !=
+ (item =
+ drm_lookup_ref_object(priv, referenced_object, ref_action))) {
+ atomic_inc(&item->refcount);
+ return drm_object_ref_action(priv, referenced_object,
+ ref_action);
+ }
+
+ item = drm_calloc(1, sizeof(*item), DRM_MEM_OBJECTS);
+ if (item == NULL) {
+ DRM_ERROR("Could not allocate reference object\n");
+ return -ENOMEM;
+ }
+
+ atomic_set(&item->refcount, 1);
+ item->hash.key = (unsigned long)referenced_object;
+ ret = drm_ht_insert_item(ht, &item->hash);
+
+ if (ret)
+ goto out;
+
+ list_add(&item->list, &priv->refd_objects);
+ ret = drm_object_ref_action(priv, referenced_object, ref_action);
+ out:
+ return ret;
+}
+
+drm_ref_object_t *drm_lookup_ref_object(drm_file_t * priv,
+ drm_user_object_t * referenced_object,
+ drm_ref_t ref_action)
+{
+ drm_hash_item_t *hash;
+ int ret;
+
+ ret = drm_ht_find_item(&priv->refd_object_hash[ref_action],
+ (unsigned long)referenced_object, &hash);
+ if (ret)
+ return NULL;
+
+ return drm_hash_entry(hash, drm_ref_object_t, hash);
+}
+
+static void drm_remove_other_references(drm_file_t * priv,
+ drm_user_object_t * ro)
+{
+ int i;
+ drm_open_hash_t *ht;
+ drm_hash_item_t *hash;
+ drm_ref_object_t *item;
+
+ for (i = _DRM_REF_USE + 1; i < _DRM_NO_REF_TYPES; ++i) {
+ ht = &priv->refd_object_hash[i];
+ while (!drm_ht_find_item(ht, (unsigned long)ro, &hash)) {
+ item = drm_hash_entry(hash, drm_ref_object_t, hash);
+ drm_remove_ref_object(priv, item);
+ }
+ }
+}
+
+void drm_remove_ref_object(drm_file_t * priv, drm_ref_object_t * item)
+{
+ int ret;
+ drm_user_object_t *user_object = (drm_user_object_t *) item->hash.key;
+ drm_open_hash_t *ht = &priv->refd_object_hash[item->unref_action];
+ drm_ref_t unref_action;
+
+ unref_action = item->unref_action;
+ if (atomic_dec_and_test(&item->refcount)) {
+ ret = drm_ht_remove_item(ht, &item->hash);
+ BUG_ON(ret);
+ list_del_init(&item->list);
+ if (unref_action == _DRM_REF_USE)
+ drm_remove_other_references(priv, user_object);
+ drm_free(item, sizeof(*item), DRM_MEM_OBJECTS);
+ }
+
+ switch (unref_action) {
+ case _DRM_REF_USE:
+ drm_deref_user_object(priv, user_object);
+ break;
+ default:
+ BUG_ON(!user_object->unref);
+ user_object->unref(priv, user_object, unref_action);
+ break;
+ }
+
+}
+
+int drm_user_object_ref(drm_file_t * priv, uint32_t user_token,
+ drm_object_type_t type, drm_user_object_t ** object)
+{
+ drm_device_t *dev = priv->head->dev;
+ drm_user_object_t *uo;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+ uo = drm_lookup_user_object(priv, user_token);
+ if (!uo || (uo->type != type)) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+ ret = drm_add_ref_object(priv, uo, _DRM_REF_USE);
+ if (ret)
+ goto out_err;
+ mutex_unlock(&dev->struct_mutex);
+ *object = uo;
+ DRM_ERROR("Referenced an object\n");
+ return 0;
+ out_err:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+int drm_user_object_unref(drm_file_t * priv, uint32_t user_token,
+ drm_object_type_t type)
+{
+ drm_device_t *dev = priv->head->dev;
+ drm_user_object_t *uo;
+ drm_ref_object_t *ro;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+ uo = drm_lookup_user_object(priv, user_token);
+ if (!uo || (uo->type != type)) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+ ro = drm_lookup_ref_object(priv, uo, _DRM_REF_USE);
+ if (!ro) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+ drm_remove_ref_object(priv, ro);
+ mutex_unlock(&dev->struct_mutex);
+ DRM_ERROR("Unreferenced an object\n");
+ return 0;
+ out_err:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c
index 25bb5f33..9059f42c 100644
--- a/linux-core/drm_stub.c
+++ b/linux-core/drm_stub.c
@@ -87,6 +87,12 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev,
return -ENOMEM;
}
+ if (drm_ht_create(&dev->object_hash, 12)) {
+ drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
+ drm_ht_remove(&dev->map_hash);
+ return -ENOMEM;
+ }
+
/* the DRM has 6 counters */
dev->counters = 6;
dev->types[0] = _DRM_STAT_LOCK;