+ struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
+ uint64_t retval = 0;
+
+ switch (value) {
+ case RADEON_REQUESTED_VRAM_MEMORY:
+ return ws->allocated_vram;
+ case RADEON_REQUESTED_GTT_MEMORY:
+ return ws->allocated_gtt;
+ case RADEON_BUFFER_WAIT_TIME_NS:
+ return ws->buffer_wait_time;
+ case RADEON_TIMESTAMP:
+ if (ws->info.drm_minor < 20 || ws->gen < DRV_R600) {
+ assert(0);
+ return 0;
+ }
+
+ radeon_get_drm_value(ws->fd, RADEON_INFO_TIMESTAMP, "timestamp",
+ (uint32_t*)&retval);
+ return retval;
+ case RADEON_NUM_CS_FLUSHES:
+ return ws->num_cs_flushes;
+ case RADEON_NUM_BYTES_MOVED:
+ radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_BYTES_MOVED,
+ "num-bytes-moved", (uint32_t*)&retval);
+ return retval;
+ case RADEON_VRAM_USAGE:
+ radeon_get_drm_value(ws->fd, RADEON_INFO_VRAM_USAGE,
+ "vram-usage", (uint32_t*)&retval);
+ return retval;
+ case RADEON_GTT_USAGE:
+ radeon_get_drm_value(ws->fd, RADEON_INFO_GTT_USAGE,
+ "gtt-usage", (uint32_t*)&retval);
+ return retval;
+ }
+ return 0;
+}
+
+static unsigned hash_fd(void *key)
+{
+ int fd = pointer_to_intptr(key);
+ struct stat stat;
+ fstat(fd, &stat);
+
+ return stat.st_dev ^ stat.st_ino ^ stat.st_rdev;
+}
+
+static int compare_fd(void *key1, void *key2)
+{
+ int fd1 = pointer_to_intptr(key1);
+ int fd2 = pointer_to_intptr(key2);
+ struct stat stat1, stat2;
+ fstat(fd1, &stat1);
+ fstat(fd2, &stat2);
+
+ return stat1.st_dev != stat2.st_dev ||
+ stat1.st_ino != stat2.st_ino ||
+ stat1.st_rdev != stat2.st_rdev;
+}
+
+void radeon_drm_ws_queue_cs(struct radeon_drm_winsys *ws, struct radeon_drm_cs *cs)
+{
+retry:
+ pipe_mutex_lock(ws->cs_stack_lock);
+ if (ws->ncs >= RING_LAST) {
+ /* no room left for a flush */
+ pipe_mutex_unlock(ws->cs_stack_lock);
+ goto retry;
+ }
+ ws->cs_stack[ws->ncs++] = cs;
+ pipe_mutex_unlock(ws->cs_stack_lock);
+ pipe_semaphore_signal(&ws->cs_queued);
+}
+
+static PIPE_THREAD_ROUTINE(radeon_drm_cs_emit_ioctl, param)
+{
+ struct radeon_drm_winsys *ws = (struct radeon_drm_winsys *)param;
+ struct radeon_drm_cs *cs;
+ unsigned i;
+
+ while (1) {
+ pipe_semaphore_wait(&ws->cs_queued);
+ if (ws->kill_thread)
+ break;
+
+ pipe_mutex_lock(ws->cs_stack_lock);
+ cs = ws->cs_stack[0];
+ for (i = 1; i < ws->ncs; i++)
+ ws->cs_stack[i - 1] = ws->cs_stack[i];
+ ws->cs_stack[--ws->ncs] = NULL;
+ pipe_mutex_unlock(ws->cs_stack_lock);
+
+ if (cs) {
+ radeon_drm_cs_emit_ioctl_oneshot(cs, cs->cst);
+ pipe_semaphore_signal(&cs->flush_completed);
+ }
+ }
+ pipe_mutex_lock(ws->cs_stack_lock);
+ for (i = 0; i < ws->ncs; i++) {
+ pipe_semaphore_signal(&ws->cs_stack[i]->flush_completed);
+ ws->cs_stack[i] = NULL;
+ }
+ ws->ncs = 0;
+ pipe_mutex_unlock(ws->cs_stack_lock);
+ return 0;
+}
+
+DEBUG_GET_ONCE_BOOL_OPTION(thread, "RADEON_THREAD", TRUE)
+static PIPE_THREAD_ROUTINE(radeon_drm_cs_emit_ioctl, param);
+
+static bool radeon_winsys_unref(struct radeon_winsys *ws)
+{
+ struct radeon_drm_winsys *rws = (struct radeon_drm_winsys*)ws;
+ bool destroy;
+
+ /* When the reference counter drops to zero, remove the fd from the table.
+ * This must happen while the mutex is locked, so that
+ * radeon_drm_winsys_create in another thread doesn't get the winsys
+ * from the table when the counter drops to 0. */
+ pipe_mutex_lock(fd_tab_mutex);
+
+ destroy = pipe_reference(&rws->reference, NULL);
+ if (destroy && fd_tab)
+ util_hash_table_remove(fd_tab, intptr_to_pointer(rws->fd));
+
+ pipe_mutex_unlock(fd_tab_mutex);
+ return destroy;
+}
+
+PUBLIC struct radeon_winsys *
+radeon_drm_winsys_create(int fd, radeon_screen_create_t screen_create)
+{
+ struct radeon_drm_winsys *ws;
+
+ pipe_mutex_lock(fd_tab_mutex);
+ if (!fd_tab) {
+ fd_tab = util_hash_table_create(hash_fd, compare_fd);
+ }
+
+ ws = util_hash_table_get(fd_tab, intptr_to_pointer(fd));
+ if (ws) {
+ pipe_reference(NULL, &ws->reference);
+ pipe_mutex_unlock(fd_tab_mutex);
+ return &ws->base;
+ }
+
+ ws = CALLOC_STRUCT(radeon_drm_winsys);