From ee8c252a8c755a7164f70f43e2b8b43309d6a306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 31 Mar 2021 18:28:19 +0200 Subject: [PATCH] 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: --- .../native/meta-backend-native-private.h | 3 + .../native/meta-backend-native-types.h | 1 + src/backends/native/meta-backend-native.c | 11 + .../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(+) create mode 100644 src/backends/native/meta-device-pool-private.h create mode 100644 src/backends/native/meta-device-pool.c create mode 100644 src/backends/native/meta-device-pool.h diff --git a/src/backends/native/meta-backend-native-private.h b/src/backends/native/meta-backend-native-private.h index 04583259e..cd184685f 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 f16f6b1d0..5fb7d3704 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 9524d91f3..79c73f35c 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 000000000..06a43f6f4 --- /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 + +#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 000000000..baca12c5f --- /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 +#include +#include +#include +#include +#include + +#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 000000000..5889ac7f1 --- /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 +#include + +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 301c40937..251fa3eb2 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 3473f0cf7..265be958e 100644 --- a/src/backends/native/meta-launcher.h +++ b/src/backends/native/meta-launcher.h @@ -23,6 +23,7 @@ #include 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 85d66a455..5e4aff9b6 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',