[mutter] native: Add device pool for tracking device file descriptors



commit ee8c252a8c755a7164f70f43e2b8b43309d6a306
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Wed Mar 31 18:28:19 2021 +0200

    native: Add device pool for tracking device file descriptors
    
    This practically does the same thing as part of MetaLauncher, except
    with added thread safety and caching. For example, opening the same file
    a second time will return the same MetaDeviceFile, and only once all
    acquired MetaDeviceFile's are released, will the file descriptor be
    closed and control of the device released.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1828>

 src/backends/native/meta-backend-native-private.h |   3 +
 src/backends/native/meta-backend-native-types.h   |   1 +
 src/backends/native/meta-backend-native.c         |  11 +
 src/backends/native/meta-device-pool-private.h    |  35 +++
 src/backends/native/meta-device-pool.c            | 338 ++++++++++++++++++++++
 src/backends/native/meta-device-pool.h            |  52 ++++
 src/backends/native/meta-launcher.c               |   6 +
 src/backends/native/meta-launcher.h               |   3 +
 src/meson.build                                   |   3 +
 9 files changed, 452 insertions(+)
---
diff --git a/src/backends/native/meta-backend-native-private.h 
b/src/backends/native/meta-backend-native-private.h
index 04583259eb..cd184685f0 100644
--- a/src/backends/native/meta-backend-native-private.h
+++ b/src/backends/native/meta-backend-native-private.h
@@ -25,8 +25,11 @@
 #ifndef META_BACKEND_NATIVE_PRIVATE_H
 #define META_BACKEND_NATIVE_PRIVATE_H
 
+#include "backends/native/meta-backend-native.h"
 #include "backends/native/meta-barrier-native.h"
 
 MetaBarrierManagerNative *meta_backend_native_get_barrier_manager (MetaBackendNative *native);
 
+MetaDevicePool * meta_backend_native_get_device_pool (MetaBackendNative *native);
+
 #endif /* META_BACKEND_NATIVE_PRIVATE_H */
diff --git a/src/backends/native/meta-backend-native-types.h b/src/backends/native/meta-backend-native-types.h
index f16f6b1d0f..5fb7d37047 100644
--- a/src/backends/native/meta-backend-native-types.h
+++ b/src/backends/native/meta-backend-native-types.h
@@ -29,6 +29,7 @@ typedef struct _MetaRendererNative MetaRendererNative;
 typedef struct _MetaGpuKms MetaGpuKms;
 typedef struct _MetaCrtcVirtual MetaCrtcVirtual;
 typedef struct _MetaCrtcModeVirtual MetaCrtcModeVirtual;
+typedef struct _MetaDevicePool MetaDevicePool;
 
 typedef enum _MetaSeatNativeFlag
 {
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index 9524d91f36..79c73f35cd 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -50,6 +50,7 @@
 #include "backends/meta-settings-private.h"
 #include "backends/meta-stage-private.h"
 #include "backends/native/meta-clutter-backend-native.h"
+#include "backends/native/meta-device-pool-private.h"
 #include "backends/native/meta-kms.h"
 #include "backends/native/meta-kms-device.h"
 #include "backends/native/meta-launcher.h"
@@ -81,6 +82,7 @@ struct _MetaBackendNative
   MetaBackend parent;
 
   MetaLauncher *launcher;
+  MetaDevicePool *device_pool;
   MetaUdev *udev;
   MetaKms *kms;
 
@@ -119,6 +121,7 @@ meta_backend_native_dispose (GObject *object)
 
   g_clear_object (&native->kms);
   g_clear_object (&native->udev);
+  g_clear_object (&native->device_pool);
   g_clear_pointer (&native->launcher, meta_launcher_free);
 }
 
@@ -574,6 +577,8 @@ meta_backend_native_initable_init (GInitable     *initable,
         return FALSE;
     }
 
+  native->device_pool = meta_device_pool_new (native->launcher);
+
 #ifdef HAVE_WAYLAND
   meta_backend_init_wayland_display (META_BACKEND (native));
 #endif
@@ -672,6 +677,12 @@ meta_backend_native_get_launcher (MetaBackendNative *native)
   return native->launcher;
 }
 
+MetaDevicePool *
+meta_backend_native_get_device_pool (MetaBackendNative *native)
+{
+  return native->device_pool;
+}
+
 MetaUdev *
 meta_backend_native_get_udev (MetaBackendNative *native)
 {
diff --git a/src/backends/native/meta-device-pool-private.h b/src/backends/native/meta-device-pool-private.h
new file mode 100644
index 0000000000..06a43f6f49
--- /dev/null
+++ b/src/backends/native/meta-device-pool-private.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_DEVICE_POOL_PRIVATE_H
+#define META_DEVICE_POOL_PRIVATE_H
+
+#include <glib-object.h>
+
+#include "backends/native/meta-device-pool.h"
+#include "backends/native/meta-launcher.h"
+
+#define META_TYPE_DEVICE_POOL (meta_device_pool_get_type ())
+G_DECLARE_FINAL_TYPE (MetaDevicePool, meta_device_pool,
+                      META, DEVICE_POOL,
+                      GObject)
+
+MetaDevicePool * meta_device_pool_new (MetaLauncher *launcher);
+
+#endif /* META_DEVICE_POOL_PRIVATE_H */
diff --git a/src/backends/native/meta-device-pool.c b/src/backends/native/meta-device-pool.c
new file mode 100644
index 0000000000..baca12c5fe
--- /dev/null
+++ b/src/backends/native/meta-device-pool.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2013-2021 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "backends/native/meta-device-pool-private.h"
+
+#include <fcntl.h>
+#include <gio/gunixfdlist.h>
+#include <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+
+#include "backends/native/meta-launcher.h"
+
+#include "meta-dbus-login1.h"
+
+struct _MetaDeviceFile
+{
+  MetaDevicePool *pool;
+
+  grefcount ref_count;
+
+  char *path;
+  int major;
+  int minor;
+  int fd;
+  MetaDeviceFileFlags flags;
+};
+
+struct _MetaDevicePool
+{
+  GObject parent;
+
+  MetaDbusLogin1Session *session_proxy;
+
+  GMutex mutex;
+
+  GList *files;
+};
+
+G_DEFINE_TYPE (MetaDevicePool, meta_device_pool, G_TYPE_OBJECT)
+
+static void
+release_device_file (MetaDevicePool *pool,
+                     MetaDeviceFile *file);
+
+static MetaDeviceFile *
+meta_device_file_new (MetaDevicePool      *pool,
+                      const char          *path,
+                      int                  major,
+                      int                  minor,
+                      int                  fd,
+                      MetaDeviceFileFlags  flags)
+{
+  MetaDeviceFile *file;
+
+  file = g_new0 (MetaDeviceFile, 1);
+
+  file->pool = pool;
+  g_ref_count_init (&file->ref_count);
+
+  file->path = g_strdup (path);
+  file->major = major;
+  file->minor = minor;
+  file->fd = fd;
+  file->flags = flags;
+
+  return file;
+}
+
+static void
+meta_device_file_free (MetaDeviceFile *file)
+{
+  g_free (file->path);
+  g_free (file);
+}
+
+int
+meta_device_file_get_fd (MetaDeviceFile *device_file)
+{
+  g_assert (!g_ref_count_compare (&device_file->ref_count, 0));
+
+  return device_file->fd;
+}
+
+const char *
+meta_device_file_get_path (MetaDeviceFile *device_file)
+{
+  return device_file->path;
+}
+
+static MetaDeviceFile *
+meta_device_file_acquire_locked (MetaDeviceFile *file)
+{
+  g_ref_count_inc (&file->ref_count);
+  return file;
+}
+
+MetaDeviceFile *
+meta_device_file_acquire (MetaDeviceFile *file)
+{
+  g_mutex_lock (&file->pool->mutex);
+  meta_device_file_acquire_locked (file);
+  g_mutex_unlock (&file->pool->mutex);
+
+  return file;
+}
+
+void
+meta_device_file_release (MetaDeviceFile *file)
+{
+  g_warn_if_fail (file->fd != -1);
+
+  release_device_file (file->pool, file);
+}
+
+MetaDevicePool *
+meta_device_file_get_pool (MetaDeviceFile *device_file)
+{
+  return device_file->pool;
+}
+
+static MetaDeviceFile *
+find_device_file_from_path (MetaDevicePool *pool,
+                            const char     *path)
+{
+  GList *l;
+
+  for (l = pool->files; l; l = l->next)
+    {
+      MetaDeviceFile *file = l->data;
+
+      if (g_strcmp0 (file->path, path) == 0)
+        return file;
+    }
+
+  return NULL;
+}
+
+static gboolean
+take_device (MetaDbusLogin1Session  *session_proxy,
+             int                     dev_major,
+             int                     dev_minor,
+             int                    *out_fd,
+             GCancellable           *cancellable,
+             GError                **error)
+{
+  g_autoptr (GVariant) fd_variant = NULL;
+  g_autoptr (GUnixFDList) fd_list = NULL;
+  int fd = -1;
+
+  if (!meta_dbus_login1_session_call_take_device_sync (session_proxy,
+                                                       dev_major,
+                                                       dev_minor,
+                                                       NULL,
+                                                       &fd_variant,
+                                                       NULL, /* paused */
+                                                       &fd_list,
+                                                       cancellable,
+                                                       error))
+    return FALSE;
+
+  fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_variant), error);
+  if (fd == -1)
+    return FALSE;
+
+  *out_fd = fd;
+  return TRUE;
+}
+
+static gboolean
+get_device_info_from_path (const char *path,
+                           int        *out_major,
+                           int        *out_minor)
+{
+  int ret;
+  struct stat st;
+
+  ret = stat (path, &st);
+  if (ret < 0 || !S_ISCHR (st.st_mode))
+    return FALSE;
+
+  *out_major = major (st.st_rdev);
+  *out_minor = minor (st.st_rdev);
+  return TRUE;
+}
+
+MetaDeviceFile *
+meta_device_pool_open (MetaDevicePool       *pool,
+                       const char           *path,
+                       MetaDeviceFileFlags   flags,
+                       GError              **error)
+{
+  g_autoptr (GMutexLocker) locker = NULL;
+  MetaDeviceFile *file;
+  int major, minor;
+  int fd;
+
+  locker = g_mutex_locker_new (&pool->mutex);
+
+  file = find_device_file_from_path (pool, path);
+  if (file)
+    {
+      g_warn_if_fail (file->flags == flags);
+      meta_device_file_acquire_locked (file);
+      return file;
+    }
+
+  if (!get_device_info_from_path (path, &major, &minor))
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_NOT_FOUND,
+                   "Could not get device info for path %s: %m", path);
+      return NULL;
+    }
+
+  if (flags & META_DEVICE_FILE_FLAG_TAKE_CONTROL)
+    {
+      if (!pool->session_proxy)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                       "Can't take control without logind session");
+          return NULL;
+        }
+
+      if (!take_device (pool->session_proxy, major, minor, &fd, NULL, error))
+        return NULL;
+    }
+  else
+    {
+      fd = open (path, O_RDWR | O_CLOEXEC, 0);
+      if (fd == -1)
+        {
+          g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
+                       "Failed to open device '%s': %s",
+                       path, g_strerror (errno));
+          return NULL;
+        }
+    }
+
+  file = meta_device_file_new (pool, path, major, minor, fd, flags);
+  pool->files = g_list_prepend (pool->files, file);
+
+  return file;
+}
+
+static void
+release_device_file (MetaDevicePool *pool,
+                     MetaDeviceFile *file)
+{
+  g_autoptr (GMutexLocker) locker = NULL;
+  g_autoptr (GError) error = NULL;
+
+  locker = g_mutex_locker_new (&pool->mutex);
+
+  if (!g_ref_count_dec (&file->ref_count))
+    return;
+
+  pool->files = g_list_remove (pool->files, file);
+
+  if (file->flags & META_DEVICE_FILE_FLAG_TAKE_CONTROL)
+    {
+      MetaDbusLogin1Session *session_proxy;
+
+      session_proxy = pool->session_proxy;
+      if (!meta_dbus_login1_session_call_release_device_sync (session_proxy,
+                                                              file->major,
+                                                              file->minor,
+                                                              NULL, &error))
+        {
+          g_warning ("Could not release device '%s' (%d,%d): %s",
+                     file->path,
+                     file->major, file->minor,
+                     error->message);
+        }
+    }
+
+  close (file->fd);
+
+  meta_device_file_free (file);
+}
+
+MetaDevicePool *
+meta_device_pool_new (MetaLauncher *launcher)
+{
+  MetaDevicePool *pool;
+
+  pool = g_object_new (META_TYPE_DEVICE_POOL, NULL);
+
+  if (launcher)
+    pool->session_proxy = meta_launcher_get_session_proxy (launcher);
+
+  return pool;
+}
+
+static void
+meta_device_pool_finalize (GObject *object)
+{
+  MetaDevicePool *pool = META_DEVICE_POOL (object);
+
+  g_mutex_clear (&pool->mutex);
+  g_warn_if_fail (!pool->files);
+
+  G_OBJECT_CLASS (meta_device_pool_parent_class)->finalize (object);
+}
+
+static void
+meta_device_pool_init (MetaDevicePool *pool)
+{
+  g_mutex_init (&pool->mutex);
+}
+
+static void
+meta_device_pool_class_init (MetaDevicePoolClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = meta_device_pool_finalize;
+}
diff --git a/src/backends/native/meta-device-pool.h b/src/backends/native/meta-device-pool.h
new file mode 100644
index 0000000000..5889ac7f17
--- /dev/null
+++ b/src/backends/native/meta-device-pool.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_DEVICE_POOL_H
+#define META_DEVICE_POOL_H
+
+#include <glib-object.h>
+#include <stdint.h>
+
+typedef enum _MetaDeviceFileFlags
+{
+  META_DEVICE_FILE_FLAG_NONE = 0,
+  META_DEVICE_FILE_FLAG_TAKE_CONTROL = 1 << 0,
+} MetaDeviceFileFlags;
+
+typedef struct _MetaDeviceFile MetaDeviceFile;
+typedef struct _MetaDevicePool MetaDevicePool;
+
+int meta_device_file_get_fd (MetaDeviceFile *device_file);
+
+const char * meta_device_file_get_path (MetaDeviceFile *device_file);
+
+MetaDeviceFile * meta_device_file_acquire (MetaDeviceFile *file);
+
+void meta_device_file_release (MetaDeviceFile *device_file);
+
+MetaDevicePool * meta_device_file_get_pool (MetaDeviceFile *device_file);
+
+MetaDeviceFile * meta_device_pool_open (MetaDevicePool       *pool,
+                                        const char           *path,
+                                        MetaDeviceFileFlags   flags,
+                                        GError              **error);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaDeviceFile, meta_device_file_release)
+
+#endif /* META_DEVICE_FILE_POOL_H */
diff --git a/src/backends/native/meta-launcher.c b/src/backends/native/meta-launcher.c
index 301c409374..251fa3eb26 100644
--- a/src/backends/native/meta-launcher.c
+++ b/src/backends/native/meta-launcher.c
@@ -503,6 +503,12 @@ get_seat_id (GError **error)
   return seat_id;
 }
 
+MetaDbusLogin1Session *
+meta_launcher_get_session_proxy (MetaLauncher *launcher)
+{
+  return launcher->session_proxy;
+}
+
 MetaLauncher *
 meta_launcher_new (GError **error)
 {
diff --git a/src/backends/native/meta-launcher.h b/src/backends/native/meta-launcher.h
index 3473f0cf7d..265be958e5 100644
--- a/src/backends/native/meta-launcher.h
+++ b/src/backends/native/meta-launcher.h
@@ -23,6 +23,7 @@
 #include <glib-object.h>
 
 typedef struct _MetaLauncher MetaLauncher;
+typedef struct _MetaDbusLogin1Session MetaDbusLogin1Session;
 
 MetaLauncher     *meta_launcher_new                     (GError       **error);
 void              meta_launcher_free                    (MetaLauncher  *self);
@@ -40,5 +41,7 @@ int               meta_launcher_open_restricted         (MetaLauncher *launcher,
 void              meta_launcher_close_restricted        (MetaLauncher *launcher,
                                                          int           fd);
 
+MetaDbusLogin1Session * meta_launcher_get_session_proxy (MetaLauncher *launcher);
+
 
 #endif /* META_LAUNCHER_H */
diff --git a/src/meson.build b/src/meson.build
index 85d66a455a..5e4aff9b66 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -662,6 +662,9 @@ if have_native_backend
     'backends/native/meta-crtc-virtual.h',
     'backends/native/meta-cursor-renderer-native.c',
     'backends/native/meta-cursor-renderer-native.h',
+    'backends/native/meta-device-pool-private.h',
+    'backends/native/meta-device-pool.c',
+    'backends/native/meta-device-pool.h',
     'backends/native/meta-drm-buffer-dumb.c',
     'backends/native/meta-drm-buffer-dumb.h',
     'backends/native/meta-drm-buffer-gbm.c',


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