mirror of
https://gitlab.freedesktop.org/mesa/drm.git
synced 2025-12-24 21:50:20 +01:00
Generic DRM support base-class support for user-space objects, like
fence objects and buffer objects: Refcounting, Inter-process sharing, Synchronization Destruction.
This commit is contained in:
parent
11f9e404fb
commit
42c2cfcf7d
7 changed files with 579 additions and 46 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
289
linux-core/drm_object.c
Normal file
289
linux-core/drm_object.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue