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>
This commit is contained in:
Jonas Ådahl 2021-03-31 18:28:19 +02:00
parent 44af2c0d37
commit ee8c252a8c
9 changed files with 452 additions and 0 deletions

View File

@ -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 */

View File

@ -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
{

View File

@ -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)
{

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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)
{

View File

@ -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 */

View File

@ -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',