[Nouveau] [PATCH] drm/ttm/nouveau: add DRM_NOUVEAU_GEM_CPU_PREP_TIMEOUT
Marcin Slusarz
marcin.slusarz at gmail.com
Sun Sep 18 06:18:57 PDT 2011
Currently DRM_NOUVEAU_GEM_CPU_PREP ioctl is broken WRT handling of signals.
nouveau_gem_ioctl_cpu_prep calls ttm_bo_wait which waits for fence to
"signal" or 3 seconds timeout pass.
But if it detects pending signal, it returns ERESTARTSYS and goes back
to userspace. After signal handler, userspace repeats the same ioctl which
starts _new 3 seconds loop_.
So when the application relies on signals, some ioctls may never finish
from application POV.
There is one important application which does this - Xorg. It uses SIGIO
(for input handling) and SIGALARM.
GPU lockups lead to endless ioctl loop which eventually manifests in crash
with "[mi] EQ overflowing. The server is probably stuck in an infinite loop."
message instead of being propagated to DDX.
The solutions is to add new ioctl NOUVEAU_GEM_CPU_PREP_TIMEOUT with
timeout parameter and decrease it on every signal.
Signed-off-by: Marcin Slusarz <marcin.slusarz at gmail.com>
---
drivers/gpu/drm/nouveau/nouveau_channel.c | 1 +
drivers/gpu/drm/nouveau/nouveau_drv.h | 7 ++-
drivers/gpu/drm/nouveau/nouveau_fence.c | 34 ++++++++++++++++-
drivers/gpu/drm/nouveau/nouveau_gem.c | 34 +++++++++++++---
drivers/gpu/drm/radeon/radeon_ttm.c | 3 +-
drivers/gpu/drm/ttm/ttm_bo.c | 23 +++++++++--
drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 3 +-
include/drm/nouveau_drm.h | 58 ++++++++++++++++------------
include/drm/ttm/ttm_bo_api.h | 23 +++++++++++-
include/drm/ttm/ttm_bo_driver.h | 3 +-
10 files changed, 145 insertions(+), 44 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index 0e3241c..62c09b0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -479,6 +479,7 @@ struct drm_ioctl_desc nouveau_ioctls[] = {
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_PREP_TIMEOUT, nouveau_gem_ioctl_cpu_prep_timeout, DRM_UNLOCKED|DRM_AUTH),
};
int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 000e44f..33ddd69 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1383,7 +1383,8 @@ extern void nouveau_fence_work(struct nouveau_fence *fence,
struct nouveau_channel *nouveau_fence_channel(struct nouveau_fence *);
extern bool __nouveau_fence_signalled(void *obj, void *arg);
-extern int __nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr);
+extern int __nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr,
+ struct timespec *remaining_time);
extern int __nouveau_fence_flush(void *obj, void *arg);
extern void __nouveau_fence_unref(void **obj);
extern void *__nouveau_fence_ref(void *obj);
@@ -1395,7 +1396,7 @@ static inline bool nouveau_fence_signalled(struct nouveau_fence *obj)
static inline int
nouveau_fence_wait(struct nouveau_fence *obj, bool lazy, bool intr)
{
- return __nouveau_fence_wait(obj, NULL, lazy, intr);
+ return __nouveau_fence_wait(obj, NULL, lazy, intr, NULL);
}
extern int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *);
static inline int nouveau_fence_flush(struct nouveau_fence *obj)
@@ -1430,6 +1431,8 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *,
struct drm_file *);
extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
struct drm_file *);
+extern int nouveau_gem_ioctl_cpu_prep_timeout(struct drm_device *, void *,
+ struct drm_file *);
/* nouveau_display.c */
int nouveau_vblank_enable(struct drm_device *dev, int crtc);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 12f522f..614e37a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -228,19 +228,37 @@ __nouveau_fence_signalled(void *sync_obj, void *sync_arg)
return fence->signalled;
}
+static inline void zero_timespec(struct timespec *t)
+{
+ t->tv_sec = 0;
+ t->tv_nsec = 0;
+}
+
int
-__nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)
+__nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr,
+ struct timespec *remaining_time)
{
- unsigned long timeout = jiffies + (3 * DRM_HZ);
+ unsigned long timeout = jiffies;
+ struct timespec start;
unsigned long sleep_time = NSEC_PER_MSEC / 1000;
ktime_t t;
int ret = 0;
+ if (remaining_time) {
+ getnstimeofday(&start);
+ timeout += timespec_to_jiffies(remaining_time);
+ } else {
+ timeout += 3 * DRM_HZ;
+ }
+
+
while (1) {
if (__nouveau_fence_signalled(sync_obj, sync_arg))
break;
if (time_after_eq(jiffies, timeout)) {
+ if (remaining_time)
+ zero_timespec(remaining_time);
ret = -EBUSY;
break;
}
@@ -256,6 +274,18 @@ __nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)
}
if (intr && signal_pending(current)) {
+ if (remaining_time) {
+ struct timespec now, diff;
+ getnstimeofday(&now);
+ diff = timespec_sub(now, start);
+ *remaining_time =
+ timespec_sub(*remaining_time, diff);
+
+ if (remaining_time->tv_sec < 0 ||
+ remaining_time->tv_nsec < 0)
+ zero_timespec(remaining_time);
+ }
+
ret = -ERESTARTSYS;
break;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index b3d17be..f4979d0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -853,29 +853,49 @@ domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain)
return flags;
}
-int
-nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+static int
+__nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, struct drm_file *file_priv,
+ u32 handle, u32 flags, struct timespec *timeout)
{
- struct drm_nouveau_gem_cpu_prep *req = data;
struct drm_gem_object *gem;
struct nouveau_bo *nvbo;
- bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
+ bool no_wait = !!(flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
+
int ret = -EINVAL;
- gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+ gem = drm_gem_object_lookup(dev, file_priv, handle);
if (!gem)
return -ENOENT;
nvbo = nouveau_gem_object(gem);
spin_lock(&nvbo->bo.bdev->fence_lock);
- ret = ttm_bo_wait(&nvbo->bo, true, true, no_wait);
+ ret = ttm_bo_wait_timeout(&nvbo->bo, true, true, no_wait, timeout);
spin_unlock(&nvbo->bo.bdev->fence_lock);
drm_gem_object_unreference_unlocked(gem);
return ret;
}
int
+nouveau_gem_ioctl_cpu_prep_timeout(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_nouveau_gem_cpu_prep_timeout *req = data;
+
+ return __nouveau_gem_ioctl_cpu_prep(dev, file_priv, req->handle,
+ req->flags, &req->timeout);
+}
+
+int
+nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_nouveau_gem_cpu_prep *req = data;
+
+ return __nouveau_gem_ioctl_cpu_prep(dev, file_priv, req->handle,
+ req->flags, NULL);
+}
+
+int
nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 60125dd..2b6d703 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -462,7 +462,8 @@ static void radeon_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_re
}
static int radeon_sync_obj_wait(void *sync_obj, void *sync_arg,
- bool lazy, bool interruptible)
+ bool lazy, bool interruptible,
+ struct timespec *remaining_time)
{
return radeon_fence_wait((struct radeon_fence *)sync_obj, interruptible);
}
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 81b6850..49b17a3 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1693,8 +1693,9 @@ out_unlock:
return ret;
}
-int ttm_bo_wait(struct ttm_buffer_object *bo,
- bool lazy, bool interruptible, bool no_wait)
+int ttm_bo_wait_timeout(struct ttm_buffer_object *bo,
+ bool lazy, bool interruptible, bool no_wait,
+ struct timespec *remaining_time)
{
struct ttm_bo_driver *driver = bo->bdev->driver;
struct ttm_bo_device *bdev = bo->bdev;
@@ -1703,7 +1704,7 @@ int ttm_bo_wait(struct ttm_buffer_object *bo,
int ret = 0;
if (likely(bo->sync_obj == NULL))
- return 0;
+ goto success;
while (bo->sync_obj) {
@@ -1724,7 +1725,7 @@ int ttm_bo_wait(struct ttm_buffer_object *bo,
sync_obj_arg = bo->sync_obj_arg;
spin_unlock(&bdev->fence_lock);
ret = driver->sync_obj_wait(sync_obj, sync_obj_arg,
- lazy, interruptible);
+ lazy, interruptible, remaining_time);
if (unlikely(ret != 0)) {
driver->sync_obj_unref(&sync_obj);
spin_lock(&bdev->fence_lock);
@@ -1747,8 +1748,22 @@ int ttm_bo_wait(struct ttm_buffer_object *bo,
spin_lock(&bdev->fence_lock);
}
}
+
+success:
+ if (remaining_time) {
+ remaining_time->tv_sec = 0;
+ remaining_time->tv_nsec = 0;
+ }
+
return 0;
}
+EXPORT_SYMBOL(ttm_bo_wait_timeout);
+
+int ttm_bo_wait(struct ttm_buffer_object *bo,
+ bool lazy, bool interruptible, bool no_wait)
+{
+ return ttm_bo_wait_timeout(bo, lazy, interruptible, no_wait, NULL);
+}
EXPORT_SYMBOL(ttm_bo_wait);
int ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
index 87e43e0..6c6b9c6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
@@ -301,7 +301,8 @@ static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg)
}
static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg,
- bool lazy, bool interruptible)
+ bool lazy, bool interruptible,
+ struct timespec *remaining_time)
{
struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
uint32_t sequence = (unsigned long) sync_obj;
diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h
index 5edd3a7..b7e34d0 100644
--- a/include/drm/nouveau_drm.h
+++ b/include/drm/nouveau_drm.h
@@ -176,6 +176,12 @@ struct drm_nouveau_gem_cpu_prep {
uint32_t flags;
};
+struct drm_nouveau_gem_cpu_prep_timeout {
+ uint32_t handle;
+ uint32_t flags;
+ struct timespec timeout;
+};
+
struct drm_nouveau_gem_cpu_fini {
uint32_t handle;
};
@@ -189,30 +195,32 @@ enum nouveau_bus_type {
struct drm_nouveau_sarea {
};
-#define DRM_NOUVEAU_GETPARAM 0x00
-#define DRM_NOUVEAU_SETPARAM 0x01
-#define DRM_NOUVEAU_CHANNEL_ALLOC 0x02
-#define DRM_NOUVEAU_CHANNEL_FREE 0x03
-#define DRM_NOUVEAU_GROBJ_ALLOC 0x04
-#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC 0x05
-#define DRM_NOUVEAU_GPUOBJ_FREE 0x06
-#define DRM_NOUVEAU_GEM_NEW 0x40
-#define DRM_NOUVEAU_GEM_PUSHBUF 0x41
-#define DRM_NOUVEAU_GEM_CPU_PREP 0x42
-#define DRM_NOUVEAU_GEM_CPU_FINI 0x43
-#define DRM_NOUVEAU_GEM_INFO 0x44
-
-#define DRM_IOCTL_NOUVEAU_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GETPARAM, struct drm_nouveau_getparam)
-#define DRM_IOCTL_NOUVEAU_SETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_SETPARAM, struct drm_nouveau_setparam)
-#define DRM_IOCTL_NOUVEAU_CHANNEL_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_CHANNEL_ALLOC, struct drm_nouveau_channel_alloc)
-#define DRM_IOCTL_NOUVEAU_CHANNEL_FREE DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_CHANNEL_FREE, struct drm_nouveau_channel_free)
-#define DRM_IOCTL_NOUVEAU_GROBJ_ALLOC DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GROBJ_ALLOC, struct drm_nouveau_grobj_alloc)
-#define DRM_IOCTL_NOUVEAU_NOTIFIEROBJ_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, struct drm_nouveau_notifierobj_alloc)
-#define DRM_IOCTL_NOUVEAU_GPUOBJ_FREE DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GPUOBJ_FREE, struct drm_nouveau_gpuobj_free)
-#define DRM_IOCTL_NOUVEAU_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_NEW, struct drm_nouveau_gem_new)
-#define DRM_IOCTL_NOUVEAU_GEM_PUSHBUF DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_PUSHBUF, struct drm_nouveau_gem_pushbuf)
-#define DRM_IOCTL_NOUVEAU_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_CPU_PREP, struct drm_nouveau_gem_cpu_prep)
-#define DRM_IOCTL_NOUVEAU_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_CPU_FINI, struct drm_nouveau_gem_cpu_fini)
-#define DRM_IOCTL_NOUVEAU_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_INFO, struct drm_nouveau_gem_info)
+#define DRM_NOUVEAU_GETPARAM 0x00
+#define DRM_NOUVEAU_SETPARAM 0x01
+#define DRM_NOUVEAU_CHANNEL_ALLOC 0x02
+#define DRM_NOUVEAU_CHANNEL_FREE 0x03
+#define DRM_NOUVEAU_GROBJ_ALLOC 0x04
+#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC 0x05
+#define DRM_NOUVEAU_GPUOBJ_FREE 0x06
+#define DRM_NOUVEAU_GEM_NEW 0x40
+#define DRM_NOUVEAU_GEM_PUSHBUF 0x41
+#define DRM_NOUVEAU_GEM_CPU_PREP 0x42
+#define DRM_NOUVEAU_GEM_CPU_FINI 0x43
+#define DRM_NOUVEAU_GEM_INFO 0x44
+#define DRM_NOUVEAU_GEM_CPU_PREP_TIMEOUT 0x45
+
+#define DRM_IOCTL_NOUVEAU_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GETPARAM, struct drm_nouveau_getparam)
+#define DRM_IOCTL_NOUVEAU_SETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_SETPARAM, struct drm_nouveau_setparam)
+#define DRM_IOCTL_NOUVEAU_CHANNEL_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_CHANNEL_ALLOC, struct drm_nouveau_channel_alloc)
+#define DRM_IOCTL_NOUVEAU_CHANNEL_FREE DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_CHANNEL_FREE, struct drm_nouveau_channel_free)
+#define DRM_IOCTL_NOUVEAU_GROBJ_ALLOC DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GROBJ_ALLOC, struct drm_nouveau_grobj_alloc)
+#define DRM_IOCTL_NOUVEAU_NOTIFIEROBJ_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, struct drm_nouveau_notifierobj_alloc)
+#define DRM_IOCTL_NOUVEAU_GPUOBJ_FREE DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GPUOBJ_FREE, struct drm_nouveau_gpuobj_free)
+#define DRM_IOCTL_NOUVEAU_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_NEW, struct drm_nouveau_gem_new)
+#define DRM_IOCTL_NOUVEAU_GEM_PUSHBUF DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_PUSHBUF, struct drm_nouveau_gem_pushbuf)
+#define DRM_IOCTL_NOUVEAU_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_CPU_PREP, struct drm_nouveau_gem_cpu_prep)
+#define DRM_IOCTL_NOUVEAU_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_CPU_FINI, struct drm_nouveau_gem_cpu_fini)
+#define DRM_IOCTL_NOUVEAU_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_INFO, struct drm_nouveau_gem_info)
+#define DRM_IOCTL_NOUVEAU_GEM_CPU_PREP_TIMEOUT DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_CPU_PREP_TIMEOUT, struct drm_nouveau_gem_cpu_prep_timeout)
#endif /* __NOUVEAU_DRM_H__ */
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 62a0e4c..7692690 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -330,11 +330,32 @@ ttm_bo_reference(struct ttm_buffer_object *bo)
* sure any previous rendering to the buffer is completed.
* Note: It might be necessary to block validations before the
* wait by reserving the buffer.
- * Returns -EBUSY if no_wait is true and the buffer is busy.
+ * Returns -EBUSY if no_wait is true and the buffer is busy or 3 secs passed
* Returns -ERESTARTSYS if interrupted by a signal.
*/
extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy,
bool interruptible, bool no_wait);
+
+/**
+ * ttm_bo_wait - wait for buffer idle.
+ *
+ * @bo: The buffer object.
+ * @interruptible: Use interruptible wait.
+ * @no_wait: Return immediately if buffer is busy.
+ * @remaining_time: Wait only this amount of time.
+ *
+ * This function must be called with the bo::mutex held, and makes
+ * sure any previous rendering to the buffer is completed.
+ * Note: It might be necessary to block validations before the
+ * wait by reserving the buffer.
+ * Note 2: timeout is decreased if interrupted by signal, and zeroed otherwise
+ * Returns -EBUSY if no_wait is true and the buffer is busy or timeout passed
+ * Returns -ERESTARTSYS if interrupted by a signal.
+ */
+extern int ttm_bo_wait_timeout(struct ttm_buffer_object *bo, bool lazy,
+ bool interruptible, bool no_wait,
+ struct timespec *remaining_time);
+
/**
* ttm_bo_validate
*
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
index 09af2d7..bd9678f 100644
--- a/include/drm/ttm/ttm_bo_driver.h
+++ b/include/drm/ttm/ttm_bo_driver.h
@@ -433,7 +433,8 @@ struct ttm_bo_driver {
bool (*sync_obj_signaled) (void *sync_obj, void *sync_arg);
int (*sync_obj_wait) (void *sync_obj, void *sync_arg,
- bool lazy, bool interruptible);
+ bool lazy, bool interruptible,
+ struct timespec *remaining_time);
int (*sync_obj_flush) (void *sync_obj, void *sync_arg);
void (*sync_obj_unref) (void **sync_obj);
void *(*sync_obj_ref) (void *sync_obj);
--
1.7.6.1
More information about the Nouveau
mailing list