[mutter] native: Release output device files that are unused



commit ce5a5789bb149c7dd91c7c43d1523a3a6f302cc3
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Mon Apr 12 16:25:53 2021 +0200

    native: Release output device files that are unused
    
    In order to make it possible to e.g. unload an unused DRM device, we
    need to make sure that we don't keep the file descriptor open if we
    don't need it; otherwise we block anyone from unloading the
    corresponding module.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1828>

 src/backends/native/meta-device-pool.h            |   2 +
 src/backends/native/meta-kms-connector.c          |  35 +++++
 src/backends/native/meta-kms-impl-device-atomic.c |  46 ++++--
 src/backends/native/meta-kms-impl-device-simple.c |  58 +++++---
 src/backends/native/meta-kms-impl-device.c        | 169 +++++++++++++++++++---
 src/backends/native/meta-kms-impl-device.h        |  12 ++
 src/backends/native/meta-kms-impl.c               |  10 ++
 src/backends/native/meta-kms-impl.h               |   2 +
 src/backends/native/meta-kms.c                    |  18 +++
 src/backends/native/meta-kms.h                    |   2 +
 src/backends/native/meta-renderer-native.c        | 143 +++++++++++++++---
 11 files changed, 426 insertions(+), 71 deletions(-)
---
diff --git a/src/backends/native/meta-device-pool.h b/src/backends/native/meta-device-pool.h
index 083bc7b53e..0e9653bd6f 100644
--- a/src/backends/native/meta-device-pool.h
+++ b/src/backends/native/meta-device-pool.h
@@ -32,6 +32,8 @@ typedef enum _MetaDeviceFileFlags
 
 typedef enum _MetaDeviceFileTags
 {
+  META_DEVICE_FILE_TAG_KMS,
+
   META_DEVICE_FILE_N_TAGS,
 } MetaDeviceFileTags;
 
diff --git a/src/backends/native/meta-kms-connector.c b/src/backends/native/meta-kms-connector.c
index 3508d22ccd..a666bb45cb 100644
--- a/src/backends/native/meta-kms-connector.c
+++ b/src/backends/native/meta-kms-connector.c
@@ -52,6 +52,8 @@ struct _MetaKmsConnector
 
   uint32_t edid_blob_id;
   uint32_t tile_blob_id;
+
+  gboolean fd_held;
 };
 
 G_DEFINE_TYPE (MetaKmsConnector, meta_kms_connector, G_TYPE_OBJECT)
@@ -128,6 +130,25 @@ meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector)
   return underscan_prop_id != 0;
 }
 
+static void
+sync_fd_held (MetaKmsConnector  *connector,
+              MetaKmsImplDevice *impl_device)
+{
+  gboolean should_hold_fd;
+
+  should_hold_fd = connector->current_state->current_crtc_id != 0;
+
+  if (connector->fd_held == should_hold_fd)
+    return;
+
+  if (should_hold_fd)
+    meta_kms_impl_device_hold_fd (impl_device);
+  else
+    meta_kms_impl_device_unhold_fd (impl_device);
+
+  connector->fd_held = should_hold_fd;
+}
+
 static void
 set_panel_orientation (MetaKmsConnectorState *state,
                        drmModePropertyPtr     prop,
@@ -475,6 +496,8 @@ meta_kms_connector_read_state (MetaKmsConnector  *connector,
   state_set_crtc_state (state, drm_connector, impl_device, drm_resources);
 
   connector->current_state = state;
+
+  sync_fd_held (connector, impl_device);
 }
 
 void
@@ -498,6 +521,7 @@ void
 meta_kms_connector_predict_state (MetaKmsConnector *connector,
                                   MetaKmsUpdate    *update)
 {
+  MetaKmsImplDevice *impl_device;
   MetaKmsConnectorState *current_state;
   GList *mode_sets;
   GList *l;
@@ -527,6 +551,9 @@ meta_kms_connector_predict_state (MetaKmsConnector *connector,
             }
         }
     }
+
+  impl_device = meta_kms_device_get_impl_device (connector->device);
+  sync_fd_held (connector, impl_device);
 }
 
 static void
@@ -645,6 +672,14 @@ meta_kms_connector_finalize (GObject *object)
 {
   MetaKmsConnector *connector = META_KMS_CONNECTOR (object);
 
+  if (connector->fd_held)
+    {
+      MetaKmsImplDevice *impl_device;
+
+      impl_device = meta_kms_device_get_impl_device (connector->device);
+      meta_kms_impl_device_unhold_fd (impl_device);
+    }
+
   g_clear_pointer (&connector->current_state, meta_kms_connector_state_free);
   g_free (connector->name);
 
diff --git a/src/backends/native/meta-kms-impl-device-atomic.c 
b/src/backends/native/meta-kms-impl-device-atomic.c
index 85897e3304..66246e87e8 100644
--- a/src/backends/native/meta-kms-impl-device-atomic.c
+++ b/src/backends/native/meta-kms-impl-device-atomic.c
@@ -602,6 +602,8 @@ process_page_flip_listener (MetaKmsImplDevice  *impl_device,
                            GUINT_TO_POINTER (crtc_id),
                            page_flip_data);
 
+      meta_kms_impl_device_hold_fd (impl_device);
+
       meta_topic (META_DEBUG_KMS,
                   "[atomic] Adding page flip data for (%u, %s): %p",
                   crtc_id,
@@ -709,6 +711,8 @@ atomic_page_flip_handler (int           fd,
   if (!page_flip_data)
     return;
 
+  meta_kms_impl_device_unhold_fd (impl_device);
+
   meta_kms_page_flip_data_set_timings_in_impl (page_flip_data,
                                                sequence, tv_sec, tv_usec);
   meta_kms_impl_device_handle_page_flip_callback (impl_device, page_flip_data);
@@ -1014,8 +1018,10 @@ dispose_page_flip_data (gpointer key,
                         gpointer user_data)
 {
   MetaKmsPageFlipData *page_flip_data = value;
+  MetaKmsImplDevice *impl_device = user_data;
 
   meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
+  meta_kms_impl_device_unhold_fd (impl_device);
 
   return TRUE;
 }
@@ -1028,7 +1034,7 @@ meta_kms_impl_device_atomic_prepare_shutdown (MetaKmsImplDevice *impl_device)
 
   g_hash_table_foreach_remove (impl_device_atomic->page_flip_datas,
                                dispose_page_flip_data,
-                               NULL);
+                               impl_device);
 }
 
 static void
@@ -1055,7 +1061,6 @@ meta_kms_impl_device_atomic_open_device_file (MetaKmsImplDevice  *impl_device,
   MetaDevicePool *device_pool =
     meta_backend_native_get_device_pool (META_BACKEND_NATIVE (backend));
   g_autoptr (MetaDeviceFile) device_file = NULL;
-  int fd;
 
   device_file = meta_device_pool_open (device_pool, path,
                                        META_DEVICE_FILE_FLAG_TAKE_CONTROL,
@@ -1063,20 +1068,33 @@ meta_kms_impl_device_atomic_open_device_file (MetaKmsImplDevice  *impl_device,
   if (!device_file)
     return NULL;
 
-  fd = meta_device_file_get_fd (device_file);
-
-  if (drmSetClientCap (fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0)
+  if (!meta_device_file_has_tag (device_file,
+                                 META_DEVICE_FILE_TAG_KMS,
+                                 META_KMS_DEVICE_FILE_TAG_ATOMIC))
     {
-      g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
-                   "DRM_CLIENT_CAP_UNIVERSAL_PLANES not supported");
-      return NULL;
-    }
+      int fd = meta_device_file_get_fd (device_file);
 
-  if (drmSetClientCap (fd, DRM_CLIENT_CAP_ATOMIC, 1) != 0)
-    {
-      g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
-                   "DRM_CLIENT_CAP_ATOMIC not supported");
-      return NULL;
+      g_warn_if_fail (!meta_device_file_has_tag (device_file,
+                                                 META_DEVICE_FILE_TAG_KMS,
+                                                 META_KMS_DEVICE_FILE_TAG_SIMPLE));
+
+      if (drmSetClientCap (fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0)
+        {
+          g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
+                       "DRM_CLIENT_CAP_UNIVERSAL_PLANES not supported");
+          return NULL;
+        }
+
+      if (drmSetClientCap (fd, DRM_CLIENT_CAP_ATOMIC, 1) != 0)
+        {
+          g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
+                       "DRM_CLIENT_CAP_ATOMIC not supported");
+          return NULL;
+        }
+
+      meta_device_file_tag (device_file,
+                            META_DEVICE_FILE_TAG_KMS,
+                            META_KMS_DEVICE_FILE_TAG_ATOMIC);
     }
 
   return g_steal_pointer (&device_file);
diff --git a/src/backends/native/meta-kms-impl-device-simple.c 
b/src/backends/native/meta-kms-impl-device-simple.c
index eabafa096d..28d5127208 100644
--- a/src/backends/native/meta-kms-impl-device-simple.c
+++ b/src/backends/native/meta-kms-impl-device-simple.c
@@ -655,6 +655,7 @@ retry_page_flips (gpointer user_data)
             g_critical ("Failed to page flip: %s", error->message);
 
           meta_kms_page_flip_data_discard_in_impl (page_flip_data, error);
+          meta_kms_impl_device_unhold_fd (impl_device);
         }
       else
         {
@@ -977,6 +978,7 @@ dispatch_page_flip (MetaKmsImplDevice    *impl_device,
             fb_id = 0;
           drm_mode = cached_mode_set->drm_mode;
           refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
+          meta_kms_impl_device_hold_fd (impl_device);
           schedule_retry_page_flip (impl_device_simple,
                                     crtc,
                                     fb_id,
@@ -1016,6 +1018,8 @@ dispatch_page_flip (MetaKmsImplDevice    *impl_device,
     }
   else
     {
+      meta_kms_impl_device_hold_fd (impl_device);
+
       impl_device_simple->posted_page_flip_datas =
         g_list_prepend (impl_device_simple->posted_page_flip_datas,
                         page_flip_data);
@@ -1367,6 +1371,8 @@ page_flip_handler (int           fd,
               page_flip_data,
               meta_kms_crtc_get_id (crtc));
 
+  meta_kms_impl_device_unhold_fd (impl_device);
+
   meta_kms_impl_device_handle_page_flip_callback (impl_device, page_flip_data);
   impl_device_simple->posted_page_flip_datas =
     g_list_remove (impl_device_simple->posted_page_flip_datas,
@@ -1465,6 +1471,14 @@ meta_kms_impl_device_simple_handle_page_flip_callback (MetaKmsImplDevice   *impl
     }
 }
 
+static void
+dispose_page_flip_data (MetaKmsPageFlipData *page_flip_data,
+                        MetaKmsImplDevice   *impl_device)
+{
+  meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
+  meta_kms_impl_device_unhold_fd (impl_device);
+}
+
 static void
 meta_kms_impl_device_simple_discard_pending_page_flips (MetaKmsImplDevice *impl_device)
 {
@@ -1489,7 +1503,7 @@ meta_kms_impl_device_simple_discard_pending_page_flips (MetaKmsImplDevice *impl_
                   meta_kms_impl_device_get_path (
                     meta_kms_page_flip_data_get_impl_device (page_flip_data)));
 
-      meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
+      dispose_page_flip_data (page_flip_data, impl_device);
       retry_page_flip_data_free (retry_page_flip_data);
     }
   g_clear_pointer (&impl_device_simple->pending_page_flip_retries, g_list_free);
@@ -1505,8 +1519,8 @@ meta_kms_impl_device_simple_prepare_shutdown (MetaKmsImplDevice *impl_device)
     META_KMS_IMPL_DEVICE_SIMPLE (impl_device);
 
   g_list_foreach (impl_device_simple->posted_page_flip_datas,
-                  (GFunc) meta_kms_page_flip_data_discard_in_impl,
-                  NULL);
+                  (GFunc) dispose_page_flip_data,
+                  impl_device);
   g_clear_list (&impl_device_simple->posted_page_flip_datas, NULL);
 }
 
@@ -1515,18 +1529,16 @@ meta_kms_impl_device_simple_finalize (GObject *object)
 {
   MetaKmsImplDeviceSimple *impl_device_simple =
     META_KMS_IMPL_DEVICE_SIMPLE (object);
+  MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (impl_device_simple);
 
   g_list_free_full (impl_device_simple->pending_page_flip_retries,
                     (GDestroyNotify) retry_page_flip_data_free);
-  dispatch_page_flip_datas (&impl_device_simple->posted_page_flip_datas,
-                            (GFunc) meta_kms_page_flip_data_discard_in_impl,
-                            NULL);
   dispatch_page_flip_datas (&impl_device_simple->postponed_page_flip_datas,
-                            (GFunc) meta_kms_page_flip_data_discard_in_impl,
-                            NULL);
+                            (GFunc) dispose_page_flip_data,
+                            impl_device);
   dispatch_page_flip_datas (&impl_device_simple->postponed_mode_set_fallback_datas,
-                            (GFunc) meta_kms_page_flip_data_discard_in_impl,
-                            NULL);
+                            (GFunc) dispose_page_flip_data,
+                            impl_device);
 
   g_assert (!impl_device_simple->posted_page_flip_datas);
 
@@ -1548,7 +1560,6 @@ meta_kms_impl_device_simple_open_device_file (MetaKmsImplDevice  *impl_device,
   MetaDevicePool *device_pool =
     meta_backend_native_get_device_pool (META_BACKEND_NATIVE (backend));
   g_autoptr (MetaDeviceFile) device_file = NULL;
-  int fd;
 
   device_file = meta_device_pool_open (device_pool, path,
                                        META_DEVICE_FILE_FLAG_TAKE_CONTROL,
@@ -1556,13 +1567,26 @@ meta_kms_impl_device_simple_open_device_file (MetaKmsImplDevice  *impl_device,
   if (!device_file)
     return NULL;
 
-  fd = meta_device_file_get_fd (device_file);
-
-  if (drmSetClientCap (fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0)
+  if (!meta_device_file_has_tag (device_file,
+                                 META_DEVICE_FILE_TAG_KMS,
+                                 META_KMS_DEVICE_FILE_TAG_SIMPLE))
     {
-      g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
-                   "DRM_CLIENT_CAP_UNIVERSAL_PLANES not supported");
-      return NULL;
+      int fd = meta_device_file_get_fd (device_file);
+
+      g_warn_if_fail (!meta_device_file_has_tag (device_file,
+                                                 META_DEVICE_FILE_TAG_KMS,
+                                                 META_KMS_DEVICE_FILE_TAG_ATOMIC));
+
+      if (drmSetClientCap (fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0)
+        {
+          g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
+                       "DRM_CLIENT_CAP_UNIVERSAL_PLANES not supported");
+          return NULL;
+        }
+
+      meta_device_file_tag (device_file,
+                            META_DEVICE_FILE_TAG_KMS,
+                            META_KMS_DEVICE_FILE_TAG_SIMPLE);
     }
 
   return g_steal_pointer (&device_file);
diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c
index d288ec4114..e08e672e5e 100644
--- a/src/backends/native/meta-kms-impl-device.c
+++ b/src/backends/native/meta-kms-impl-device.c
@@ -36,7 +36,7 @@
 #include "backends/native/meta-kms-plane-private.h"
 #include "backends/native/meta-kms-plane.h"
 #include "backends/native/meta-kms-private.h"
-#include "backends/native/meta-kms-update.h"
+#include "backends/native/meta-kms-update-private.h"
 
 #include "meta-default-modes.h"
 #include "meta-private-enum-types.h"
@@ -60,10 +60,12 @@ typedef struct _MetaKmsImplDevicePrivate
   MetaKmsDevice *device;
   MetaKmsImpl *impl;
 
+  int fd_hold_count;
   MetaDeviceFile *device_file;
   GSource *fd_source;
   char *path;
   MetaKmsDeviceFlag flags;
+  gboolean has_latched_fd_hold;
 
   char *driver_name;
   char *driver_description;
@@ -619,11 +621,81 @@ init_fallback_modes (MetaKmsImplDevice *impl_device)
   priv->fallback_modes = g_list_reverse (modes);
 }
 
+static MetaDeviceFile *
+meta_kms_impl_device_open_device_file (MetaKmsImplDevice  *impl_device,
+                                       const char         *path,
+                                       GError            **error)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
+
+  return klass->open_device_file (impl_device, priv->path, error);
+}
+
+static gboolean
+ensure_device_file (MetaKmsImplDevice  *impl_device,
+                    GError            **error)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  MetaDeviceFile *device_file;
+
+  if (priv->device_file)
+    return TRUE;
+
+  device_file = meta_kms_impl_device_open_device_file (impl_device,
+                                                       priv->path,
+                                                       error);
+  if (!device_file)
+    return FALSE;
+
+  priv->device_file = device_file;
+
+  if (!(priv->flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING))
+    {
+      priv->fd_source =
+        meta_kms_register_fd_in_impl (meta_kms_impl_get_kms (priv->impl),
+                                      meta_device_file_get_fd (device_file),
+                                      kms_event_dispatch_in_impl,
+                                      impl_device);
+    }
+
+  return TRUE;
+}
+
+static void
+ensure_latched_fd_hold (MetaKmsImplDevice *impl_device)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+
+  if (!priv->has_latched_fd_hold)
+    {
+      meta_kms_impl_device_hold_fd (impl_device);
+      priv->has_latched_fd_hold = TRUE;
+    }
+}
+
+static void
+clear_latched_fd_hold (MetaKmsImplDevice *impl_device)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+
+  if (priv->has_latched_fd_hold)
+    {
+      meta_kms_impl_device_unhold_fd (impl_device);
+      priv->has_latched_fd_hold = FALSE;
+    }
+}
+
 void
 meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device)
 {
   MetaKmsImplDevicePrivate *priv =
     meta_kms_impl_device_get_instance_private (impl_device);
+  g_autoptr (GError) error = NULL;
   int fd;
   drmModeRes *drm_resources;
 
@@ -631,17 +703,21 @@ meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device)
 
   meta_topic (META_DEBUG_KMS, "Updating device state for %s", priv->path);
 
+  if (!ensure_device_file (impl_device, &error))
+    {
+      g_warning ("Failed to reopen '%s': %s", priv->path, error->message);
+      goto err;
+    }
+
+  ensure_latched_fd_hold (impl_device);
+
   fd = meta_device_file_get_fd (priv->device_file);
   drm_resources = drmModeGetResources (fd);
   if (!drm_resources)
     {
-      g_list_free_full (priv->planes, g_object_unref);
-      g_list_free_full (priv->crtcs, g_object_unref);
-      g_list_free_full (priv->connectors, g_object_unref);
-      priv->planes = NULL;
-      priv->crtcs = NULL;
-      priv->connectors = NULL;
-      return;
+      meta_topic (META_DEBUG_KMS, "Device '%s' didn't return any resources",
+                  priv->path);
+      goto err;
     }
 
   update_connectors (impl_device, drm_resources);
@@ -651,6 +727,13 @@ meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device)
   g_list_foreach (priv->connectors, (GFunc) meta_kms_connector_update_state,
                   drm_resources);
   drmModeFreeResources (drm_resources);
+
+  return;
+
+err:
+  g_clear_list (&priv->planes, g_object_unref);
+  g_clear_list (&priv->crtcs, g_object_unref);
+  g_clear_list (&priv->connectors, g_object_unref);
 }
 
 void
@@ -666,6 +749,12 @@ meta_kms_impl_device_predict_states (MetaKmsImplDevice *impl_device,
                   update);
 }
 
+void
+meta_kms_impl_device_notify_modes_set (MetaKmsImplDevice *impl_device)
+{
+  clear_latched_fd_hold (impl_device);
+}
+
 int
 meta_kms_impl_device_get_fd (MetaKmsImplDevice *impl_device)
 {
@@ -683,8 +772,17 @@ meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device,
                                      MetaKmsUpdateFlag  flags)
 {
   MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
+  MetaKmsFeedback *feedback;
+  g_autoptr (GError) error = NULL;
+
+  if (!ensure_device_file (impl_device, &error))
+    return meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error));
+
+  meta_kms_impl_device_hold_fd (impl_device);
+  feedback = klass->process_update (impl_device, update, flags);
+  meta_kms_impl_device_unhold_fd (impl_device);
 
-  return klass->process_update (impl_device, update, flags);
+  return feedback;
 }
 
 void
@@ -704,6 +802,44 @@ meta_kms_impl_device_discard_pending_page_flips (MetaKmsImplDevice *impl_device)
   klass->discard_pending_page_flips (impl_device);
 }
 
+void
+meta_kms_impl_device_hold_fd (MetaKmsImplDevice *impl_device)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  MetaKms *kms = meta_kms_device_get_kms (priv->device);
+
+  meta_assert_in_kms_impl (kms);
+
+  g_assert (priv->device_file);
+
+  priv->fd_hold_count++;
+}
+
+void
+meta_kms_impl_device_unhold_fd (MetaKmsImplDevice *impl_device)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  MetaKms *kms = meta_kms_device_get_kms (priv->device);
+
+  meta_assert_in_kms_impl (kms);
+
+  g_return_if_fail (priv->fd_hold_count > 0);
+
+  priv->fd_hold_count--;
+  if (priv->fd_hold_count == 0)
+    {
+      g_clear_pointer (&priv->device_file, meta_device_file_release);
+
+      if (priv->fd_source)
+        {
+          g_source_destroy (priv->fd_source);
+          g_clear_pointer (&priv->fd_source, g_source_unref);
+        }
+    }
+}
+
 static void
 meta_kms_impl_device_get_property (GObject    *object,
                                    guint       prop_id,
@@ -776,7 +912,8 @@ meta_kms_impl_device_finalize (GObject *object)
   g_list_free_full (priv->fallback_modes,
                     (GDestroyNotify) meta_kms_mode_free);
 
-  g_clear_pointer (&priv->device_file, meta_device_file_release);
+  clear_latched_fd_hold (impl_device);
+  g_warn_if_fail (!priv->device_file);
 
   g_free (priv->driver_name);
   g_free (priv->driver_description);
@@ -816,12 +953,6 @@ meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice  *impl_device,
 
   drmModeFreeResources (drm_resources);
 
-  priv->fd_source =
-    meta_kms_register_fd_in_impl (meta_kms_impl_get_kms (priv->impl),
-                                  fd,
-                                  kms_event_dispatch_in_impl,
-                                  impl_device);
-
   return TRUE;
 }
 
@@ -862,13 +993,13 @@ meta_kms_impl_device_initable_init (GInitable     *initable,
   MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (initable);
   MetaKmsImplDevicePrivate *priv =
     meta_kms_impl_device_get_instance_private (impl_device);
-  MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
   int fd;
 
-  priv->device_file = klass->open_device_file (impl_device, priv->path, error);
-  if (!priv->device_file)
+  if (!ensure_device_file (impl_device, error))
     return FALSE;
 
+  ensure_latched_fd_hold (impl_device);
+
   g_clear_pointer (&priv->path, g_free);
   priv->path = g_strdup (meta_device_file_get_path (priv->device_file));
 
diff --git a/src/backends/native/meta-kms-impl-device.h b/src/backends/native/meta-kms-impl-device.h
index 3826b2c5b2..7dbb14b8ec 100644
--- a/src/backends/native/meta-kms-impl-device.h
+++ b/src/backends/native/meta-kms-impl-device.h
@@ -87,6 +87,12 @@ enum
   META_KMS_ERROR_NOT_SUPPORTED,
 };
 
+enum
+{
+  META_KMS_DEVICE_FILE_TAG_ATOMIC = 1 << 0,
+  META_KMS_DEVICE_FILE_TAG_SIMPLE = 1 << 1,
+};
+
 #define META_KMS_ERROR meta_kms_error_quark ()
 GQuark meta_kms_error_quark (void);
 
@@ -124,11 +130,17 @@ drmModePropertyPtr meta_kms_impl_device_find_property (MetaKmsImplDevice       *
 
 int meta_kms_impl_device_get_fd (MetaKmsImplDevice *impl_device);
 
+void meta_kms_impl_device_hold_fd (MetaKmsImplDevice *impl_device);
+
+void meta_kms_impl_device_unhold_fd (MetaKmsImplDevice *impl_device);
+
 void meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device);
 
 void meta_kms_impl_device_predict_states (MetaKmsImplDevice *impl_device,
                                           MetaKmsUpdate     *update);
 
+void meta_kms_impl_device_notify_modes_set (MetaKmsImplDevice *impl_device);
+
 MetaKmsPlane * meta_kms_impl_device_add_fake_plane (MetaKmsImplDevice *impl_device,
                                                     MetaKmsPlaneType   plane_type,
                                                     MetaKmsCrtc       *crtc);
diff --git a/src/backends/native/meta-kms-impl.c b/src/backends/native/meta-kms-impl.c
index 3a186209c6..0ad76e085b 100644
--- a/src/backends/native/meta-kms-impl.c
+++ b/src/backends/native/meta-kms-impl.c
@@ -119,6 +119,16 @@ meta_kms_impl_prepare_shutdown (MetaKmsImpl *impl)
     }
 }
 
+void
+meta_kms_impl_notify_modes_set (MetaKmsImpl *impl)
+{
+  MetaKmsImplPrivate *priv = meta_kms_impl_get_instance_private (impl);
+
+  g_list_foreach (priv->impl_devices,
+                  (GFunc) meta_kms_impl_device_notify_modes_set,
+                  NULL);
+}
+
 MetaKmsImpl *
 meta_kms_impl_new (MetaKms *kms)
 {
diff --git a/src/backends/native/meta-kms-impl.h b/src/backends/native/meta-kms-impl.h
index 00cf5538d0..58f03f81a6 100644
--- a/src/backends/native/meta-kms-impl.h
+++ b/src/backends/native/meta-kms-impl.h
@@ -45,6 +45,8 @@ void meta_kms_impl_discard_pending_page_flips (MetaKmsImpl *impl);
 
 void meta_kms_impl_prepare_shutdown (MetaKmsImpl *impl);
 
+void meta_kms_impl_notify_modes_set (MetaKmsImpl *impl);
+
 MetaKmsImpl * meta_kms_impl_new (MetaKms *kms);
 
 #endif /* META_KMS_IMPL_H */
diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c
index edb6f29eec..70feccc223 100644
--- a/src/backends/native/meta-kms.c
+++ b/src/backends/native/meta-kms.c
@@ -345,6 +345,24 @@ meta_kms_discard_pending_page_flips (MetaKms *kms)
                                NULL);
 }
 
+static gpointer
+meta_kms_notify_modes_set_in_impl (MetaKmsImpl  *impl,
+                                   gpointer      user_data,
+                                   GError      **error)
+{
+  meta_kms_impl_notify_modes_set (impl);
+  return GINT_TO_POINTER (TRUE);
+}
+
+void
+meta_kms_notify_modes_set (MetaKms *kms)
+{
+  meta_kms_run_impl_task_sync (kms,
+                               meta_kms_notify_modes_set_in_impl,
+                               NULL,
+                               NULL);
+}
+
 static void
 meta_kms_callback_data_free (MetaKmsCallbackData *callback_data)
 {
diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h
index 79713cc405..9eae80e3fa 100644
--- a/src/backends/native/meta-kms.h
+++ b/src/backends/native/meta-kms.h
@@ -52,6 +52,8 @@ MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms           *kms,
 
 void meta_kms_discard_pending_page_flips (MetaKms *kms);
 
+void meta_kms_notify_modes_set (MetaKms *kms);
+
 MetaBackend * meta_kms_get_backend (MetaKms *kms);
 
 GList * meta_kms_get_devices (MetaKms *kms);
diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
index 54216224a4..9256b679d3 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -102,6 +102,11 @@ G_DEFINE_TYPE_WITH_CODE (MetaRendererNative,
 static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
 static const CoglWinsysVtable *parent_vtable;
 
+static gboolean
+meta_renderer_native_ensure_gpu_data (MetaRendererNative  *renderer_native,
+                                      MetaGpuKms          *gpu_kms,
+                                      GError             **error);
+
 static void
 meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native);
 
@@ -625,6 +630,44 @@ clear_kept_alive_onscreens (MetaRendererNative *renderer_native)
                 g_object_unref);
 }
 
+static gboolean
+is_gpu_unused (gpointer key,
+               gpointer value,
+               gpointer user_data)
+{
+  GHashTable *used_gpus = user_data;
+
+  return !g_hash_table_contains (used_gpus, key);
+}
+
+static void
+free_unused_gpu_datas (MetaRendererNative *renderer_native)
+{
+  MetaRenderer *renderer = META_RENDERER (renderer_native);
+  g_autoptr (GHashTable) used_gpus = NULL;
+  GList *l;
+
+  used_gpus = g_hash_table_new (NULL, NULL);
+  g_hash_table_add (used_gpus, renderer_native->primary_gpu_kms);
+
+  for (l = meta_renderer_get_views (renderer); l; l = l->next)
+    {
+      MetaRendererView *view = l->data;
+      MetaCrtc *crtc = meta_renderer_view_get_crtc (view);
+      MetaGpu *gpu;
+
+      gpu = meta_crtc_get_gpu (crtc);
+      if (!gpu)
+        continue;
+
+      g_hash_table_add (used_gpus, gpu);
+    }
+
+  g_hash_table_foreach_remove (renderer_native->gpu_datas,
+                               is_gpu_unused,
+                               used_gpus);
+}
+
 void
 meta_renderer_native_post_mode_set_updates (MetaRendererNative *renderer_native)
 {
@@ -665,6 +708,10 @@ meta_renderer_native_post_mode_set_updates (MetaRendererNative *renderer_native)
     }
 
   clear_kept_alive_onscreens (renderer_native);
+
+  meta_kms_notify_modes_set (kms);
+
+  free_unused_gpu_datas (renderer_native);
 }
 
 static void
@@ -1061,21 +1108,15 @@ meta_renderer_native_create_view (MetaRenderer       *renderer,
   if (META_IS_CRTC_KMS (crtc))
     {
       MetaGpuKms *gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
-      MetaGpuKms *primary_gpu_kms = renderer_native->primary_gpu_kms;
       MetaOnscreenNative *onscreen_native;
 
-      onscreen_native = meta_onscreen_native_new (renderer_native,
-                                                  primary_gpu_kms,
-                                                  output,
-                                                  crtc,
-                                                  cogl_context,
-                                                  onscreen_width,
-                                                  onscreen_height);
-
-      if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen_native), &error))
+      if (!meta_renderer_native_ensure_gpu_data (renderer_native,
+                                                 gpu_kms,
+                                                 &error))
         {
-          g_warning ("Failed to allocate onscreen framebuffer for %s",
-                     meta_gpu_kms_get_file_path (gpu_kms));
+          g_warning ("Failed to create secondary GPU data for %s",
+                      meta_gpu_kms_get_file_path (gpu_kms));
+          use_shadowfb = FALSE;
           framebuffer = create_fallback_offscreen (renderer_native,
                                                    cogl_context,
                                                    onscreen_width,
@@ -1083,9 +1124,32 @@ meta_renderer_native_create_view (MetaRenderer       *renderer,
         }
       else
         {
-          use_shadowfb = should_force_shadow_fb (renderer_native,
-                                                 primary_gpu_kms);
-          framebuffer = COGL_FRAMEBUFFER (onscreen_native);
+          MetaGpuKms *primary_gpu_kms = renderer_native->primary_gpu_kms;
+
+          onscreen_native = meta_onscreen_native_new (renderer_native,
+                                                      primary_gpu_kms,
+                                                      output,
+                                                      crtc,
+                                                      cogl_context,
+                                                      onscreen_width,
+                                                      onscreen_height);
+
+          if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen_native), &error))
+            {
+              g_warning ("Failed to allocate onscreen framebuffer for %s",
+                         meta_gpu_kms_get_file_path (gpu_kms));
+              use_shadowfb = FALSE;
+              framebuffer = create_fallback_offscreen (renderer_native,
+                                                       cogl_context,
+                                                       onscreen_width,
+                                                       onscreen_height);
+            }
+          else
+            {
+              use_shadowfb = should_force_shadow_fb (renderer_native,
+                                                     primary_gpu_kms);
+              framebuffer = COGL_FRAMEBUFFER (onscreen_native);
+            }
         }
     }
   else
@@ -1331,6 +1395,22 @@ meta_renderer_native_ensure_gles3 (MetaRendererNative *renderer_native)
   renderer_native->gles3 = meta_gles3_new (egl);
 }
 
+static void
+maybe_restore_cogl_egl_api (MetaRendererNative *renderer_native)
+{
+  CoglContext *cogl_context;
+  CoglDisplay *cogl_display;
+  CoglRenderer *cogl_renderer;
+
+  cogl_context = cogl_context_from_renderer_native (renderer_native);
+  if (!cogl_context)
+    return;
+
+  cogl_display = cogl_context_get_display (cogl_context);
+  cogl_renderer = cogl_display_get_renderer (cogl_display);
+  cogl_renderer_bind_api (cogl_renderer);
+}
+
 static gboolean
 init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
                              GError                   **error)
@@ -1343,13 +1423,15 @@ init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
   const char **missing_gl_extensions;
   const char *renderer_str;
 
+  meta_egl_bind_api (egl, EGL_OPENGL_ES_API, NULL);
+
   if (!create_secondary_egl_config (egl, renderer_gpu_data->mode, egl_display,
                                     &egl_config, error))
-    return FALSE;
+    goto err;
 
   egl_context = create_secondary_egl_context (egl, egl_display, egl_config, error);
   if (egl_context == EGL_NO_CONTEXT)
-    return FALSE;
+    goto err;
 
   meta_renderer_native_ensure_gles3 (renderer_native);
 
@@ -1361,7 +1443,7 @@ init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
                               error))
     {
       meta_egl_destroy_context (egl, egl_display, egl_context, NULL);
-      return FALSE;
+      goto err;
     }
 
   renderer_str = (const char *) glGetString (GL_RENDERER);
@@ -1372,7 +1454,7 @@ init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                    "Do not want to use software renderer (%s), falling back to CPU copy path",
                    renderer_str);
-      goto out_fail_with_context;
+      goto err_fail_with_context;
     }
 
   if (!meta_gles3_has_extensions (renderer_native->gles3,
@@ -1390,7 +1472,7 @@ init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
       g_free (missing_gl_extensions_str);
       g_free (missing_gl_extensions);
 
-      goto out_fail_with_context;
+      goto err_fail_with_context;
     }
 
   renderer_gpu_data->secondary.is_hardware_rendering = TRUE;
@@ -1403,9 +1485,11 @@ init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
                              "EGL_EXT_image_dma_buf_import_modifiers",
                              NULL);
 
+  maybe_restore_cogl_egl_api (renderer_native);
+
   return TRUE;
 
-out_fail_with_context:
+err_fail_with_context:
   meta_egl_make_current (egl,
                          egl_display,
                          EGL_NO_SURFACE,
@@ -1414,6 +1498,9 @@ out_fail_with_context:
                          NULL);
   meta_egl_destroy_context (egl, egl_display, egl_context, NULL);
 
+err:
+  maybe_restore_cogl_egl_api (renderer_native);
+
   return FALSE;
 }
 
@@ -1874,6 +1961,20 @@ create_renderer_gpu_data (MetaRendererNative  *renderer_native,
   return TRUE;
 }
 
+static gboolean
+meta_renderer_native_ensure_gpu_data (MetaRendererNative  *renderer_native,
+                                      MetaGpuKms          *gpu_kms,
+                                      GError             **error)
+{
+  MetaRendererNativeGpuData *renderer_gpu_data;
+
+  renderer_gpu_data = g_hash_table_lookup (renderer_native->gpu_datas, gpu_kms);
+  if (renderer_gpu_data)
+    return TRUE;
+
+  return create_renderer_gpu_data (renderer_native, gpu_kms, error);
+}
+
 static void
 on_gpu_added (MetaBackendNative  *backend_native,
               MetaGpuKms         *gpu_kms,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]