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