udisks2/0001-udiskslinuxfilesystem-Make-the-size-property-retriev.patch

278 lines
10 KiB
Diff

From 9a2a96b46803b1d76d105f3bed994188b8205133 Mon Sep 17 00:00:00 2001
From: Tomas Bzatek <tbzatek@redhat.com>
Date: Sun, 2 Jan 2022 23:45:12 +0100
Subject: [PATCH] udiskslinuxfilesystem: Make the 'size' property retrieval
on-demand
Filesystem size value retrieval is very expensive as it typically calls
filesystem tools that read superblock -> doing some I/O. Other
filesystem properties are typically retrieved from existing stateful
sources, either udev or sysfs.
This change overrides the gdbus-codegen-generated GDBusInterfaceSkeleton
property retrieval and adds a custom hook that retrieves the filesystem
size value when actually requested.
One limitation of such approach is that the hook is called with
the GDBusObjectManager lock held and thus it needs to be as minimal
as possible and avoiding access to any GDBusObject.
---
src/udiskslinuxfilesystem.c | 129 +++++++++++++++++++++++++++---------
1 file changed, 97 insertions(+), 32 deletions(-)
diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c
index a8390a04..413a5a37 100644
--- a/src/udiskslinuxfilesystem.c
+++ b/src/udiskslinuxfilesystem.c
@@ -56,6 +56,7 @@
#include "udiskssimplejob.h"
#include "udiskslinuxdriveata.h"
#include "udiskslinuxmountoptions.h"
+#include "udisksata.h"
/**
* SECTION:udiskslinuxfilesystem
@@ -78,6 +79,10 @@ struct _UDisksLinuxFilesystem
{
UDisksFilesystemSkeleton parent_instance;
GMutex lock;
+ guint64 cached_fs_size;
+ gchar *cached_device_file;
+ gchar *cached_fs_type;
+ gboolean cached_drive_is_ata;
};
struct _UDisksLinuxFilesystemClass
@@ -85,7 +90,14 @@ struct _UDisksLinuxFilesystemClass
UDisksFilesystemSkeletonClass parent_class;
};
+enum
+{
+ PROP_0,
+ PROP_SIZE,
+};
+
static void filesystem_iface_init (UDisksFilesystemIface *iface);
+static guint64 get_filesystem_size (UDisksLinuxFilesystem *filesystem);
G_DEFINE_TYPE_WITH_CODE (UDisksLinuxFilesystem, udisks_linux_filesystem, UDISKS_TYPE_FILESYSTEM_SKELETON,
G_IMPLEMENT_INTERFACE (UDISKS_TYPE_FILESYSTEM, filesystem_iface_init));
@@ -106,6 +118,8 @@ udisks_linux_filesystem_finalize (GObject *object)
UDisksLinuxFilesystem *filesystem = UDISKS_LINUX_FILESYSTEM (object);
g_mutex_clear (&(filesystem->lock));
+ g_free (filesystem->cached_device_file);
+ g_free (filesystem->cached_fs_type);
if (G_OBJECT_CLASS (udisks_linux_filesystem_parent_class)->finalize != NULL)
G_OBJECT_CLASS (udisks_linux_filesystem_parent_class)->finalize (object);
@@ -119,6 +133,44 @@ udisks_linux_filesystem_init (UDisksLinuxFilesystem *filesystem)
G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
}
+static void
+udisks_linux_filesystem_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ UDisksLinuxFilesystem *filesystem = UDISKS_LINUX_FILESYSTEM (object);
+
+ switch (prop_id)
+ {
+ case PROP_SIZE:
+ g_value_set_uint64 (value, get_filesystem_size (filesystem));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+udisks_linux_filesystem_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_SIZE:
+ g_warning ("udisks_linux_filesystem_set_property() should never be called, value = %lu", g_value_get_uint64 (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
static void
udisks_linux_filesystem_class_init (UDisksLinuxFilesystemClass *klass)
{
@@ -126,6 +178,10 @@ udisks_linux_filesystem_class_init (UDisksLinuxFilesystemClass *klass)
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = udisks_linux_filesystem_finalize;
+ gobject_class->get_property = udisks_linux_filesystem_get_property;
+ gobject_class->set_property = udisks_linux_filesystem_set_property;
+
+ g_object_class_override_property (gobject_class, PROP_SIZE, "size");
}
/**
@@ -144,49 +200,58 @@ udisks_linux_filesystem_new (void)
/* ---------------------------------------------------------------------------------------------------- */
+/* WARNING: called with GDBusObjectManager lock held, avoid any object lookup */
static guint64
-get_filesystem_size (UDisksLinuxBlockObject *object)
+get_filesystem_size (UDisksLinuxFilesystem *filesystem)
{
guint64 size = 0;
- UDisksLinuxDevice *device;
- gchar *dev;
- const gchar *type;
GError *error = NULL;
- device = udisks_linux_block_object_get_device (object);
- dev = udisks_linux_block_object_get_device_file (object);
- type = g_udev_device_get_property (device->udev_device, "ID_FS_TYPE");
+ if (!filesystem->cached_device_file || !filesystem->cached_fs_type)
+ return 0;
+
+ /* if the drive is ATA and is sleeping, skip filesystem size check to prevent
+ * drive waking up - nothing has changed anyway since it's been sleeping...
+ */
+ if (filesystem->cached_drive_is_ata)
+ {
+ guchar pm_state = 0;
+
+ if (udisks_ata_get_pm_state (filesystem->cached_device_file, NULL, &pm_state))
+ if (!UDISKS_ATA_PM_STATE_AWAKE (pm_state) && filesystem->cached_fs_size > 0)
+ return filesystem->cached_fs_size;
+ }
- if (g_strcmp0 (type, "ext2") == 0)
+ if (g_strcmp0 (filesystem->cached_fs_type, "ext2") == 0)
{
- BDFSExt2Info *info = bd_fs_ext2_get_info (dev, &error);
+ BDFSExt2Info *info = bd_fs_ext2_get_info (filesystem->cached_device_file, &error);
if (info)
{
size = info->block_size * info->block_count;
bd_fs_ext2_info_free (info);
}
}
- else if (g_strcmp0 (type, "ext3") == 0)
+ else if (g_strcmp0 (filesystem->cached_fs_type, "ext3") == 0)
{
- BDFSExt3Info *info = bd_fs_ext3_get_info (dev, &error);
+ BDFSExt3Info *info = bd_fs_ext3_get_info (filesystem->cached_device_file, &error);
if (info)
{
size = info->block_size * info->block_count;
bd_fs_ext3_info_free (info);
}
}
- else if (g_strcmp0 (type, "ext4") == 0)
+ else if (g_strcmp0 (filesystem->cached_fs_type, "ext4") == 0)
{
- BDFSExt4Info *info = bd_fs_ext4_get_info (dev, &error);
+ BDFSExt4Info *info = bd_fs_ext4_get_info (filesystem->cached_device_file, &error);
if (info)
{
size = info->block_size * info->block_count;
bd_fs_ext4_info_free (info);
}
}
- else if (g_strcmp0 (type, "xfs") == 0)
+ else if (g_strcmp0 (filesystem->cached_fs_type, "xfs") == 0)
{
- BDFSXfsInfo *info = bd_fs_xfs_get_info (dev, &error);
+ BDFSXfsInfo *info = bd_fs_xfs_get_info (filesystem->cached_device_file, &error);
if (info)
{
size = info->block_size * info->block_count;
@@ -194,10 +259,9 @@ get_filesystem_size (UDisksLinuxBlockObject *object)
}
}
- g_free (dev);
- g_object_unref (device);
g_clear_error (&error);
+ filesystem->cached_fs_size = size;
return size;
}
@@ -234,14 +298,12 @@ void
udisks_linux_filesystem_update (UDisksLinuxFilesystem *filesystem,
UDisksLinuxBlockObject *object)
{
+ UDisksDriveAta *ata = NULL;
UDisksMountMonitor *mount_monitor;
UDisksLinuxDevice *device;
- UDisksDriveAta *ata = NULL;
GPtrArray *p;
GList *mounts;
GList *l;
- gboolean skip_fs_size = FALSE;
- guchar pm_state;
mount_monitor = udisks_daemon_get_mount_monitor (udisks_linux_block_object_get_daemon (object));
device = udisks_linux_block_object_get_device (object);
@@ -263,20 +325,24 @@ udisks_linux_filesystem_update (UDisksLinuxFilesystem *filesystem,
g_ptr_array_free (p, TRUE);
g_list_free_full (mounts, g_object_unref);
- /* if the drive is ATA and is sleeping, skip filesystem size check to prevent
- * drive waking up - nothing has changed anyway since it's been sleeping...
+ /* cached device properties for on-demand filesystem size retrieval */
+ g_free (filesystem->cached_device_file);
+ g_free (filesystem->cached_fs_type);
+ filesystem->cached_fs_type = g_strdup (g_udev_device_get_property (device->udev_device, "ID_FS_TYPE"));
+ if (g_strcmp0 (filesystem->cached_fs_type, "ext2") == 0 ||
+ g_strcmp0 (filesystem->cached_fs_type, "ext3") == 0 ||
+ g_strcmp0 (filesystem->cached_fs_type, "ext4") == 0 ||
+ g_strcmp0 (filesystem->cached_fs_type, "xfs") == 0)
+ filesystem->cached_device_file = udisks_linux_block_object_get_device_file (object);
+
+ /* TODO: this only looks for a drive object associated with the current
+ * block object. In case of a complex layered structure this needs to walk
+ * the tree and return a list of physical drives to check the powermanagement on.
*/
ata = get_drive_ata (object);
- if (ata != NULL)
- {
- if (udisks_linux_drive_ata_get_pm_state (UDISKS_LINUX_DRIVE_ATA (ata), NULL, &pm_state))
- skip_fs_size = ! UDISKS_LINUX_DRIVE_ATA_IS_AWAKE (pm_state);
- }
+ filesystem->cached_drive_is_ata = ata != NULL && udisks_drive_ata_get_pm_supported (ata);
g_clear_object (&ata);
- if (! skip_fs_size)
- udisks_filesystem_set_size (UDISKS_FILESYSTEM (filesystem), get_filesystem_size (object));
-
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (filesystem));
g_object_unref (device);
@@ -1872,10 +1938,9 @@ handle_resize (UDisksFilesystem *filesystem,
/* At least resize2fs might need another uevent after it is done.
*/
+ UDISKS_LINUX_FILESYSTEM (filesystem)->cached_fs_size = 0;
udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
UDISKS_DEFAULT_WAIT_TIMEOUT);
-
- udisks_filesystem_set_size (filesystem, get_filesystem_size (UDISKS_LINUX_BLOCK_OBJECT (object)));
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (filesystem));
udisks_filesystem_complete_resize (filesystem, invocation);
udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
--
2.38.1