diff --git a/0001-udisksata-Move-the-low-level-PM-state-call.patch b/0001-udisksata-Move-the-low-level-PM-state-call.patch new file mode 100644 index 0000000..20798f6 --- /dev/null +++ b/0001-udisksata-Move-the-low-level-PM-state-call.patch @@ -0,0 +1,129 @@ +From a7d9b97c9460f65a726b727e9eaee31ea5016538 Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Wed, 5 Jan 2022 20:17:55 +0100 +Subject: [PATCH] udisksata: Move the low-level PM state call + +(cherry picked from commit 4588dbeecd23c17d1cb7f7fa60afd56702acd455) +--- + doc/udisks2-sections.txt.daemon.sections.in | 2 + + src/udisksata.c | 62 +++++++++++++++++++++ + src/udisksata.h | 13 +++++ + 3 files changed, 77 insertions(+) + +diff --git a/doc/udisks2-sections.txt.daemon.sections.in b/doc/udisks2-sections.txt.daemon.sections.in +index 12935c4d..9a2bfa03 100644 +--- a/doc/udisks2-sections.txt.daemon.sections.in ++++ b/doc/udisks2-sections.txt.daemon.sections.in +@@ -270,6 +270,8 @@ UDisksAtaCommandProtocol + UDisksAtaCommandInput + UDisksAtaCommandOutput + udisks_ata_send_command_sync ++udisks_ata_get_pm_state ++UDISKS_ATA_PM_STATE_AWAKE + + +
+diff --git a/src/udisksata.c b/src/udisksata.c +index 9491af5e..e6da8c35 100644 +--- a/src/udisksata.c ++++ b/src/udisksata.c +@@ -308,3 +308,65 @@ udisks_ata_send_command_sync (gint fd, + out: + return ret; + } ++ ++/** ++ * udisks_ata_get_pm_state: ++ * @device: ATA drive block device path. ++ * @error: Return location for error. ++ * @pm_state: Return location for the current power state value. ++ * ++ * Get the current power mode state. ++ * ++ * The format of @pm_state is the result obtained from sending the ++ * ATA command `CHECK POWER MODE` to the drive. ++ * ++ * Known values include: ++ * - `0x00`: Device is in PM2: Standby state. ++ * - `0x40`: Device is in the PM0: Active state, the NV Cache power mode is enabled, and the spindle is spun down or spinning down. ++ * - `0x41`: Device is in the PM0: Active state, the NV Cache power mode is enabled, and the spindle is spun up or spinning up. ++ * - `0x80`: Device is in PM1: Idle state. ++ * - `0xff`: Device is in the PM0: Active state or PM1: Idle State. ++ * ++ * Typically user interfaces will report "Drive is spun down" if @pm_state is ++ * 0x00 and "Drive is spun up" otherwise. ++ * ++ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. ++ */ ++gboolean ++udisks_ata_get_pm_state (const gchar *device, GError **error, guchar *count) ++{ ++ int fd; ++ gboolean rc = FALSE; ++ /* ATA8: 7.8 CHECK POWER MODE - E5h, Non-Data */ ++ UDisksAtaCommandInput input = {.command = 0xe5}; ++ UDisksAtaCommandOutput output = {0}; ++ ++ g_warn_if_fail (device != NULL); ++ ++ fd = open (device, O_RDONLY|O_NONBLOCK); ++ if (fd == -1) ++ { ++ g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, ++ "Error opening device file %s while getting PM state: %m", ++ device); ++ goto out; ++ } ++ ++ if (!udisks_ata_send_command_sync (fd, ++ -1, ++ UDISKS_ATA_COMMAND_PROTOCOL_NONE, ++ &input, ++ &output, ++ error)) ++ { ++ g_prefix_error (error, "Error sending ATA command CHECK POWER MODE: "); ++ goto out; ++ } ++ /* count field is used for the state, see ATA8: table 102 */ ++ *count = output.count; ++ rc = TRUE; ++ out: ++ if (fd != -1) ++ close (fd); ++ return rc; ++} +diff --git a/src/udisksata.h b/src/udisksata.h +index 1d4918f1..d652f3ab 100644 +--- a/src/udisksata.h ++++ b/src/udisksata.h +@@ -73,6 +73,16 @@ struct _UDisksAtaCommandOutput + guchar *buffer; + }; + ++/** ++ * UDISKS_ATA_PM_STATE_AWAKE: ++ * @pm_state: The power state value. ++ * ++ * Decodes the power state value as returned by #udisks_ata_get_pm_state. ++ * ++ * Returns: %TRUE when the drive is awake, %FALSE when sleeping. ++*/ ++#define UDISKS_ATA_PM_STATE_AWAKE(pm_state) (pm_state >= 0x41) ++ + gboolean udisks_ata_send_command_sync (gint fd, + gint timeout_msec, + UDisksAtaCommandProtocol protocol, +@@ -80,6 +90,9 @@ gboolean udisks_ata_send_command_sync (gint fd, + UDisksAtaCommandOutput *output, + GError **error); + ++gboolean udisks_ata_get_pm_state (const gchar *device, ++ GError **error, ++ guchar *count); + + G_END_DECLS + +-- +2.38.1 + diff --git a/0001-udiskslinuxfilesystem-Make-the-size-property-retriev.patch b/0001-udiskslinuxfilesystem-Make-the-size-property-retriev.patch new file mode 100644 index 0000000..a056ad0 --- /dev/null +++ b/0001-udiskslinuxfilesystem-Make-the-size-property-retriev.patch @@ -0,0 +1,277 @@ +From 9a2a96b46803b1d76d105f3bed994188b8205133 Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +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 + diff --git a/0001-udiskslinuxprovider-Only-update-related-objects-on-u.patch b/0001-udiskslinuxprovider-Only-update-related-objects-on-u.patch new file mode 100644 index 0000000..28d549b --- /dev/null +++ b/0001-udiskslinuxprovider-Only-update-related-objects-on-u.patch @@ -0,0 +1,115 @@ +From ec380135ed8cf57a70501542081dad51d2d11fa8 Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Thu, 6 Jan 2022 20:45:45 +0100 +Subject: [PATCH] udiskslinuxprovider: Only update related objects on utab + changes + +Updating all existing block objects on any utab change +was unnecessary and overly expensive. With the UDisksUtabMonitor +providing specific utab entry, update just block objects +with matching device file. + +Note that there is a room for similar optimization in fstab +and crypttab monitoring. +--- + src/udiskslinuxprovider.c | 33 ++++++++++++++++++++++++++------- + 1 file changed, 26 insertions(+), 7 deletions(-) + +diff --git a/src/udiskslinuxprovider.c b/src/udiskslinuxprovider.c +index cfc6a330..4231a33c 100644 +--- a/src/udiskslinuxprovider.c ++++ b/src/udiskslinuxprovider.c +@@ -39,6 +39,7 @@ + #include "udisksmoduleobject.h" + #include "udisksdaemonutil.h" + #include "udisksconfigmanager.h" ++#include "udisksutabentry.h" + + /** + * SECTION:udiskslinuxprovider +@@ -1559,7 +1560,7 @@ on_housekeeping_timeout (gpointer user_data) + /* ---------------------------------------------------------------------------------------------------- */ + + static void +-update_all_block_objects (UDisksLinuxProvider *provider) ++update_block_objects (UDisksLinuxProvider *provider, const gchar *device_path) + { + GList *objects; + GList *l; +@@ -1572,18 +1573,36 @@ update_all_block_objects (UDisksLinuxProvider *provider) + for (l = objects; l != NULL; l = l->next) + { + UDisksLinuxBlockObject *object = UDISKS_LINUX_BLOCK_OBJECT (l->data); +- udisks_linux_block_object_uevent (object, "change", NULL); ++ ++ if (device_path == NULL) ++ udisks_linux_block_object_uevent (object, "change", NULL); ++ else ++ { ++ gchar *block_dev; ++ gboolean match; ++ ++ block_dev = udisks_linux_block_object_get_device_file (object); ++ match = g_strcmp0 (block_dev, device_path) == 0; ++ g_free (block_dev); ++ if (match) ++ { ++ udisks_linux_block_object_uevent (object, "change", NULL); ++ break; ++ } ++ } + } + + g_list_free_full (objects, g_object_unref); + } + ++/* fstab monitoring */ + static void + mount_monitor_on_mountpoints_changed (GUnixMountMonitor *monitor, + gpointer user_data) + { + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); +- update_all_block_objects (provider); ++ /* TODO: compare differences and only update relevant objects */ ++ update_block_objects (provider, NULL); + } + + static void +@@ -1592,7 +1611,7 @@ crypttab_monitor_on_entry_added (UDisksCrypttabMonitor *monitor, + gpointer user_data) + { + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); +- update_all_block_objects (provider); ++ update_block_objects (provider, NULL); + } + + static void +@@ -1601,7 +1620,7 @@ crypttab_monitor_on_entry_removed (UDisksCrypttabMonitor *monitor, + gpointer user_data) + { + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); +- update_all_block_objects (provider); ++ update_block_objects (provider, NULL); + } + + #ifdef HAVE_LIBMOUNT_UTAB +@@ -1611,7 +1630,7 @@ utab_monitor_on_entry_added (UDisksUtabMonitor *monitor, + gpointer user_data) + { + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); +- update_all_block_objects (provider); ++ update_block_objects (provider, udisks_utab_entry_get_source (entry)); + } + + static void +@@ -1620,6 +1639,6 @@ utab_monitor_on_entry_removed (UDisksUtabMonitor *monitor, + gpointer user_data) + { + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); +- update_all_block_objects (provider); ++ update_block_objects (provider, udisks_utab_entry_get_source (entry)); + } + #endif +-- +2.38.1 + diff --git a/udisks2.changes b/udisks2.changes index c8238dc..c9a4459 100644 --- a/udisks2.changes +++ b/udisks2.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Tue Nov 22 08:21:22 UTC 2022 - Thomas Blume + +- avoid wakening spun-down disks on unrelated events (bsc#1120608) + * add: + 0001-udisksata-Move-the-low-level-PM-state-call.patch + 0001-udiskslinuxfilesystem-Make-the-size-property-retriev.patch + 0001-udiskslinuxprovider-Only-update-related-objects-on-u.patch + ------------------------------------------------------------------- Wed Nov 16 08:14:50 UTC 2022 - Thomas Blume diff --git a/udisks2.spec b/udisks2.spec index 10c3a53..d00cfbf 100644 --- a/udisks2.spec +++ b/udisks2.spec @@ -33,6 +33,9 @@ Source0: %{url}/releases/download/udisks-%{version}/udisks-%{version}.tar Patch0: harden_udisks2-zram-setup@.service.patch Patch1: harden_udisks2.service.patch Patch2: 0001-udiskslinuxmountoptions-Do-not-free-static-daemon-re.patch +Patch3: 0001-udisksata-Move-the-low-level-PM-state-call.patch +Patch4: 0001-udiskslinuxfilesystem-Make-the-size-property-retriev.patch +Patch5: 0001-udiskslinuxprovider-Only-update-related-objects-on-u.patch BuildRequires: chrpath BuildRequires: docbook-xsl-stylesheets BuildRequires: gobject-introspection-devel >= 0.6.2