From 57036a3de3d3a567da2618de64895ee0bc31297c Mon Sep 17 00:00:00 2001 From: Phillip Susi Date: Sun, 22 Jun 2014 19:23:05 -0400 Subject: [PATCH] Fix standby timers If the drive was set to standby after >= 10 minutes of inactivity, it would never standby because udisks would check every 10 minutes to see if it was already in standby, and if not, update the SMART stats, which would reset the standby timer. This is especially problematic since many drives enforce a 10 minute minimum standby timer even if a lower value is specified. This patch checks the kernel IO stats to see if any other IO has been done on the drive since the last check, and if not, behaves as if the drive is already sleeping. --- src/udiskslinuxdriveata.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/udiskslinuxdriveata.c b/src/udiskslinuxdriveata.c index 9148dee..4184732 100644 --- a/src/udiskslinuxdriveata.c +++ b/src/udiskslinuxdriveata.c @@ -88,6 +88,8 @@ struct _UDisksLinuxDriveAta UDisksThreadedJob *selftest_job; gboolean secure_erase_in_progress; + unsigned long driveread, drivewrite; + gboolean standby_enabled; }; struct _UDisksLinuxDriveAtaClass @@ -463,6 +465,32 @@ static gboolean get_pm_state (UDisksLinuxDevice *device, GError **error, guchar return rc; } +static gboolean update_io_stats (UDisksLinuxDriveAta *drive, UDisksLinuxDevice *device) +{ + const gchar *drivepath = g_udev_device_get_sysfs_path (device->udev_device); + gchar statpath[1024]; + unsigned long driveread, drivewrite; + FILE *statf; + gboolean noio = FALSE; + snprintf (statpath, sizeof(statpath), "%s/stat", drivepath); + statf = fopen (statpath, "r"); + if (statf == NULL) + { + udisks_warning ("Failed to open %s\n", statpath); + } + else + { + fscanf (statf, "%lu %*u %*u %*u %lu", &driveread, &drivewrite); + fclose (statf); + noio = driveread == drive->driveread && drivewrite == drive->drivewrite; + udisks_debug ("driveread=%lu, drivewrite=%lu, old_driveread=%lu, old_drivewrite=%lu\n", + driveread, drivewrite, drive->driveread, drive->drivewrite); + drive->driveread = driveread; + drive->drivewrite = drivewrite; + } + return noio; +} + /** * udisks_linux_drive_ata_refresh_smart_sync: * @drive: The #UDisksLinuxDriveAta to refresh. @@ -556,11 +584,14 @@ udisks_linux_drive_ata_refresh_smart_sync (UDisksLinuxDriveAta *drive, else { guchar count; + gboolean noio = FALSE; if (!get_pm_state(device, error, &count)) goto out; awake = count == 0xFF || count == 0x80; + if (drive->standby_enabled) + noio = update_io_stats (drive, device); /* don't wake up disk unless specically asked to */ - if (nowakeup && !awake) + if (nowakeup && (!awake || noio)) { g_set_error (error, UDISKS_ERROR, @@ -634,6 +665,8 @@ udisks_linux_drive_ata_refresh_smart_sync (UDisksLinuxDriveAta *drive, update_smart (drive, device); ret = TRUE; + /* update stats again to account for the IO we just did to read the SMART info */ + update_io_stats (drive, device); out: g_clear_object (&device); @@ -1660,6 +1693,7 @@ apply_configuration_thread_func (gpointer user_data) udisks_notice ("Set standby timer to %s (value %d) on %s [%s]", pretty, data->ata_pm_standby, device_file, udisks_drive_get_id (data->drive)); g_free (pretty); + data->ata->standby_enabled = data->ata_pm_standby != 0; } } -- 1.9.1