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:
Thomas Hellstrom 2006-08-21 20:30:19 +02:00
parent 11f9e404fb
commit 42c2cfcf7d
7 changed files with 579 additions and 46 deletions

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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) {

View file

@ -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
View 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;
}

View file

@ -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;