X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fgallium%2Fwinsys%2Fnouveau%2Fdrm%2Fnouveau_drm_winsys.c;h=c6603e38a0012ca6626f88f3c623a0dd8a5c2455;hb=cd0dec0d9dfab642c51774c3f5788cbdf00b8c9b;hp=d4bf124ce6f2c28ae43831098b2298734cdb0d48;hpb=1218430e1200a08cd64b6555d3fd1fd0274ad9e5;p=mesa.git diff --git a/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c b/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c index d4bf124ce6f..c6603e38a00 100644 --- a/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c +++ b/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c @@ -1,48 +1,100 @@ +#include +#include #include "pipe/p_context.h" #include "pipe/p_state.h" #include "util/u_format.h" #include "util/u_memory.h" #include "util/u_inlines.h" +#include "util/u_hash_table.h" +#include "os/os_thread.h" -#include "nouveau_drm_winsys.h" #include "nouveau_drm_public.h" -#include "nouveau_drmif.h" -#include "nouveau_channel.h" -#include "nouveau_bo.h" - #include "nouveau/nouveau_winsys.h" #include "nouveau/nouveau_screen.h" -static void -nouveau_drm_destroy_winsys(struct pipe_winsys *s) +static struct util_hash_table *fd_tab = NULL; + +pipe_static_mutex(nouveau_screen_mutex); + +bool nouveau_drm_screen_unref(struct nouveau_screen *screen) { - struct nouveau_winsys *nv_winsys = nouveau_winsys(s); - struct nouveau_screen *nv_screen= nouveau_screen(nv_winsys->pscreen); - if (nv_screen) - nouveau_device_close(&nv_screen->device); - FREE(nv_winsys); + int ret; + if (screen->refcount == -1) + return true; + + pipe_mutex_lock(nouveau_screen_mutex); + ret = --screen->refcount; + assert(ret >= 0); + if (ret == 0) + util_hash_table_remove(fd_tab, intptr_to_pointer(screen->device->fd)); + pipe_mutex_unlock(nouveau_screen_mutex); + return ret == 0; } -struct pipe_screen * +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; +} + +PUBLIC struct pipe_screen * nouveau_drm_screen_create(int fd) { - struct nouveau_winsys *nvws; - struct pipe_winsys *ws; struct nouveau_device *dev = NULL; - struct pipe_screen *(*init)(struct pipe_winsys *, - struct nouveau_device *); - int ret; + struct pipe_screen *(*init)(struct nouveau_device *); + struct nouveau_screen *screen; + int ret, dupfd = -1; + + pipe_mutex_lock(nouveau_screen_mutex); + if (!fd_tab) { + fd_tab = util_hash_table_create(hash_fd, compare_fd); + if (!fd_tab) + goto err; + } - ret = nouveau_device_open_existing(&dev, 0, fd, 0); + screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd)); + if (screen) { + screen->refcount++; + pipe_mutex_unlock(nouveau_screen_mutex); + return &screen->base; + } + + /* Since the screen re-use is based on the device node and not the fd, + * create a copy of the fd to be owned by the device. Otherwise a + * scenario could occur where two screens are created, and the first + * one is shut down, along with the fd being closed. The second + * (identical) screen would now have a reference to the closed fd. We + * avoid this by duplicating the original fd. Note that + * nouveau_device_wrap does not close the fd in case of a device + * creation error. + */ + dupfd = dup(fd); + ret = nouveau_device_wrap(dupfd, 1, &dev); if (ret) - return NULL; + goto err; - switch (dev->chipset & 0xf0) { + switch (dev->chipset & ~0xf) { case 0x30: case 0x40: case 0x60: - init = nvfx_screen_create; + init = nv30_screen_create; break; case 0x50: case 0x80: @@ -50,25 +102,38 @@ nouveau_drm_screen_create(int fd) case 0xa0: init = nv50_screen_create; break; + case 0xc0: + case 0xd0: + case 0xe0: + case 0xf0: + case 0x100: + case 0x110: + init = nvc0_screen_create; + break; default: debug_printf("%s: unknown chipset nv%02x\n", __func__, dev->chipset); - return NULL; + goto err; } - nvws = CALLOC_STRUCT(nouveau_winsys); - if (!nvws) { - nouveau_device_close(&dev); - return NULL; - } - ws = &nvws->base; - ws->destroy = nouveau_drm_destroy_winsys; + screen = (struct nouveau_screen*)init(dev); + if (!screen) + goto err; - nvws->pscreen = init(ws, dev); - if (!nvws->pscreen) { - ws->destroy(ws); - return NULL; - } + /* Use dupfd in hash table, to avoid errors if the original fd gets + * closed by its owner. The hash key needs to live at least as long as + * the screen. + */ + util_hash_table_set(fd_tab, intptr_to_pointer(dupfd), screen); + screen->refcount = 1; + pipe_mutex_unlock(nouveau_screen_mutex); + return &screen->base; - return nvws->pscreen; +err: + if (dev) + nouveau_device_del(&dev); + else if (dupfd >= 0) + close(dupfd); + pipe_mutex_unlock(nouveau_screen_mutex); + return NULL; }