Home | History | Annotate | Download | only in drm
      1 #include <sys/stat.h>
      2 #include <unistd.h>
      3 #include <fcntl.h>
      4 #include "pipe/p_context.h"
      5 #include "pipe/p_state.h"
      6 #include "util/u_format.h"
      7 #include "util/u_memory.h"
      8 #include "util/u_inlines.h"
      9 #include "util/u_hash_table.h"
     10 #include "os/os_thread.h"
     11 
     12 #include "nouveau_drm_public.h"
     13 
     14 #include "nouveau/nouveau_winsys.h"
     15 #include "nouveau/nouveau_screen.h"
     16 
     17 #include <nvif/class.h>
     18 #include <nvif/cl0080.h>
     19 
     20 static struct util_hash_table *fd_tab = NULL;
     21 
     22 pipe_static_mutex(nouveau_screen_mutex);
     23 
     24 bool nouveau_drm_screen_unref(struct nouveau_screen *screen)
     25 {
     26 	int ret;
     27 	if (screen->refcount == -1)
     28 		return true;
     29 
     30 	pipe_mutex_lock(nouveau_screen_mutex);
     31 	ret = --screen->refcount;
     32 	assert(ret >= 0);
     33 	if (ret == 0)
     34 		util_hash_table_remove(fd_tab, intptr_to_pointer(screen->drm->fd));
     35 	pipe_mutex_unlock(nouveau_screen_mutex);
     36 	return ret == 0;
     37 }
     38 
     39 static unsigned hash_fd(void *key)
     40 {
     41     int fd = pointer_to_intptr(key);
     42     struct stat stat;
     43     fstat(fd, &stat);
     44 
     45     return stat.st_dev ^ stat.st_ino ^ stat.st_rdev;
     46 }
     47 
     48 static int compare_fd(void *key1, void *key2)
     49 {
     50     int fd1 = pointer_to_intptr(key1);
     51     int fd2 = pointer_to_intptr(key2);
     52     struct stat stat1, stat2;
     53     fstat(fd1, &stat1);
     54     fstat(fd2, &stat2);
     55 
     56     return stat1.st_dev != stat2.st_dev ||
     57            stat1.st_ino != stat2.st_ino ||
     58            stat1.st_rdev != stat2.st_rdev;
     59 }
     60 
     61 PUBLIC struct pipe_screen *
     62 nouveau_drm_screen_create(int fd)
     63 {
     64 	struct nouveau_drm *drm = NULL;
     65 	struct nouveau_device *dev = NULL;
     66 	struct nouveau_screen *(*init)(struct nouveau_device *);
     67 	struct nouveau_screen *screen = NULL;
     68 	int ret, dupfd;
     69 
     70 	pipe_mutex_lock(nouveau_screen_mutex);
     71 	if (!fd_tab) {
     72 		fd_tab = util_hash_table_create(hash_fd, compare_fd);
     73 		if (!fd_tab) {
     74 			pipe_mutex_unlock(nouveau_screen_mutex);
     75 			return NULL;
     76 		}
     77 	}
     78 
     79 	screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd));
     80 	if (screen) {
     81 		screen->refcount++;
     82 		pipe_mutex_unlock(nouveau_screen_mutex);
     83 		return &screen->base;
     84 	}
     85 
     86 	/* Since the screen re-use is based on the device node and not the fd,
     87 	 * create a copy of the fd to be owned by the device. Otherwise a
     88 	 * scenario could occur where two screens are created, and the first
     89 	 * one is shut down, along with the fd being closed. The second
     90 	 * (identical) screen would now have a reference to the closed fd. We
     91 	 * avoid this by duplicating the original fd. Note that
     92 	 * nouveau_device_wrap does not close the fd in case of a device
     93 	 * creation error.
     94 	 */
     95 	dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
     96 
     97 	ret = nouveau_drm_new(dupfd, &drm);
     98 	if (ret)
     99 		goto err;
    100 
    101 	ret = nouveau_device_new(&drm->client, NV_DEVICE,
    102 				 &(struct nv_device_v0) {
    103 					.device = ~0ULL,
    104 				 }, sizeof(struct nv_device_v0), &dev);
    105 	if (ret)
    106 		goto err;
    107 
    108 	switch (dev->chipset & ~0xf) {
    109 	case 0x30:
    110 	case 0x40:
    111 	case 0x60:
    112 		init = nv30_screen_create;
    113 		break;
    114 	case 0x50:
    115 	case 0x80:
    116 	case 0x90:
    117 	case 0xa0:
    118 		init = nv50_screen_create;
    119 		break;
    120 	case 0xc0:
    121 	case 0xd0:
    122 	case 0xe0:
    123 	case 0xf0:
    124 	case 0x100:
    125 	case 0x110:
    126 	case 0x120:
    127 	case 0x130:
    128 		init = nvc0_screen_create;
    129 		break;
    130 	default:
    131 		debug_printf("%s: unknown chipset nv%02x\n", __func__,
    132 			     dev->chipset);
    133 		goto err;
    134 	}
    135 
    136 	screen = init(dev);
    137 	if (!screen || !screen->base.context_create)
    138 		goto err;
    139 
    140 	/* Use dupfd in hash table, to avoid errors if the original fd gets
    141 	 * closed by its owner. The hash key needs to live at least as long as
    142 	 * the screen.
    143 	 */
    144 	util_hash_table_set(fd_tab, intptr_to_pointer(dupfd), screen);
    145 	screen->refcount = 1;
    146 	pipe_mutex_unlock(nouveau_screen_mutex);
    147 	return &screen->base;
    148 
    149 err:
    150 	if (screen) {
    151 		screen->base.destroy(&screen->base);
    152 	} else {
    153 		nouveau_device_del(&dev);
    154 		nouveau_drm_del(&drm);
    155 		close(dupfd);
    156 	}
    157 	pipe_mutex_unlock(nouveau_screen_mutex);
    158 	return NULL;
    159 }
    160