winsys/radeon: fix a race condition between winsys_create and winsys_destroy
authorMarek Olšák <marek.olsak@amd.com>
Tue, 8 Apr 2014 22:26:32 +0000 (00:26 +0200)
committerMarek Olšák <marek.olsak@amd.com>
Thu, 10 Apr 2014 18:50:17 +0000 (20:50 +0200)
This also hides the reference count from drivers.

v2: update the reference count while the mutex is locked in winsys_create

Reviewed-by: Michel Dänzer <michel.daenzer@amd.com>
Reviewed-by: Christian König <christian.koenig@amd.com>
src/gallium/drivers/r300/r300_screen.c
src/gallium/drivers/r600/r600_pipe.c
src/gallium/drivers/radeonsi/si_pipe.c
src/gallium/winsys/radeon/drm/radeon_drm_winsys.c
src/gallium/winsys/radeon/drm/radeon_drm_winsys.h
src/gallium/winsys/radeon/drm/radeon_winsys.h

index 70c9cdf468a9b2e9cf7ed040be2b3bcc03b0aca3..8e601e3d37f60703e8b856999b6a5ff1d8556ebf 100644 (file)
@@ -552,7 +552,7 @@ static void r300_destroy_screen(struct pipe_screen* pscreen)
     struct r300_screen* r300screen = r300_screen(pscreen);
     struct radeon_winsys *rws = radeon_winsys(pscreen);
 
-    if (rws && !radeon_winsys_unref(rws))
+    if (rws && !rws->unref(rws))
       return;
 
     pipe_mutex_destroy(r300screen->cmask_mutex);
index 8d69358d8023e38bac08057b11422fd5641bf6e5..18fde7453bade2ea7587cca03e981fe7b81239f7 100644 (file)
@@ -514,7 +514,7 @@ static void r600_destroy_screen(struct pipe_screen* pscreen)
        if (rscreen == NULL)
                return;
 
-       if (!radeon_winsys_unref(rscreen->b.ws))
+       if (!rscreen->b.ws->unref(rscreen->b.ws))
                return;
 
        if (rscreen->global_pool) {
index 09ec60343fec19870a1b58312d143e8e595fcea7..7dac287266451a548ff994447c639e298f87da13 100644 (file)
@@ -424,7 +424,7 @@ static void si_destroy_screen(struct pipe_screen* pscreen)
        if (sscreen == NULL)
                return;
 
-       if (!radeon_winsys_unref(sscreen->b.ws))
+       if (!sscreen->b.ws->unref(sscreen->b.ws))
                return;
 
        r600_destroy_common_screen(&sscreen->b);
index ebf7697ac33dfca8a0dab33fcec0bce42fc7cb06..07b3c05f2b7012f7471a2198598ae988913f3af7 100644 (file)
@@ -392,12 +392,6 @@ static void radeon_winsys_destroy(struct radeon_winsys *rws)
 {
     struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
 
-    pipe_mutex_lock(fd_tab_mutex);
-    if (fd_tab) {
-        util_hash_table_remove(fd_tab, intptr_to_pointer(ws->fd));
-    }
-    pipe_mutex_unlock(fd_tab_mutex);
-
     if (ws->thread) {
         ws->kill_thread = 1;
         pipe_semaphore_signal(&ws->cs_queued);
@@ -573,6 +567,25 @@ static PIPE_THREAD_ROUTINE(radeon_drm_cs_emit_ioctl, param)
 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)
 {
     struct radeon_drm_winsys *ws;
@@ -584,8 +597,8 @@ PUBLIC struct radeon_winsys *radeon_drm_winsys_create(int 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);
-        pipe_reference(NULL, &ws->base.reference);
         return &ws->base;
     }
 
@@ -616,9 +629,10 @@ PUBLIC struct radeon_winsys *radeon_drm_winsys_create(int fd)
     }
 
     /* init reference */
-    pipe_reference_init(&ws->base.reference, 1);
+    pipe_reference_init(&ws->reference, 1);
 
     /* Set functions. */
+    ws->base.unref = radeon_winsys_unref;
     ws->base.destroy = radeon_winsys_destroy;
     ws->base.query_info = radeon_query_info;
     ws->base.cs_request_feature = radeon_cs_request_feature;
index 1aa9cf41288db53f46daef2bf3591e40899b9e4b..18fe0aeab1076ea3759b36427cd488a07c8cb9ff 100644 (file)
@@ -43,6 +43,7 @@ enum radeon_generation {
 
 struct radeon_drm_winsys {
     struct radeon_winsys base;
+    struct pipe_reference reference;
 
     int fd; /* DRM file descriptor */
     int num_cs; /* The number of command streams created. */
index eeae724d04242bafe21a1f279f89d74ae7591a06..485e9259be3eabe9b9ef2f9a14b56207655c8064 100644 (file)
@@ -232,14 +232,17 @@ enum radeon_feature_id {
 
 struct radeon_winsys {
     /**
-     * Reference counting
+     * The screen object this winsys was created for
      */
-    struct pipe_reference reference;
+    struct pipe_screen *screen;
 
     /**
-     * The screen object this winsys was created for
+     * Decrement the winsys reference count.
+     *
+     * \param ws  The winsys this function is called for.
+     * \return    True if the winsys and screen should be destroyed.
      */
-    struct pipe_screen *screen;
+    bool (*unref)(struct radeon_winsys *ws);
 
     /**
      * Destroy this winsys.
@@ -568,15 +571,6 @@ struct radeon_winsys {
                             enum radeon_value_id value);
 };
 
-/**
- * Decrement the winsys reference count.
- *
- * \param ws The winsys this function is called for.
- */
-static INLINE boolean radeon_winsys_unref(struct radeon_winsys *ws)
-{
-   return pipe_reference(&ws->reference, NULL);
-}
 
 static INLINE void radeon_emit(struct radeon_winsys_cs *cs, uint32_t value)
 {