Add support for querying and configuring the Write Cache for ATA devices
authorDavid Zeuthen <zeuthen@gmail.com>
Fri, 7 Dec 2012 20:47:02 +0000 (15:47 -0500)
committerDavid Zeuthen <zeuthen@gmail.com>
Fri, 7 Dec 2012 20:47:02 +0000 (15:47 -0500)
http://people.freedesktop.org/~david/gnome-disks-write-cache-setting.png

Signed-off-by: David Zeuthen <zeuthen@gmail.com>
data/org.freedesktop.UDisks2.xml
doc/man/udisks.xml
doc/udisks2-sections.txt
src/udiskslinuxdrive.c
src/udiskslinuxdriveata.c

index 7b1c5d6..4ec0edc 100644 (file)
                The AAM level for ATA drives (See ATA command <quote>SET FEATURES</quote>, sub-commands 0x42 and 0xc2).
              </para></listitem>
            </varlistentry>
+           <varlistentry>
+             <term>ata-write-cache-enabled (type <literal>'b'</literal>)</term>
+             <listitem><para>
+               Whether the write-cache is enabled (See ATA command <quote>SET FEATURES</quote>, sub-commands 0x42 and 0xc2). Since 2.1.
+             </para></listitem>
+           </varlistentry>
          </variablelist>
          The contents of this property is read from the configuration
          file <filename>/etc/udisks2/IDENTIFIER.conf</filename>
     <!-- AamVendorRecommendedValue: The vendor-recommended AAM value (or 0 if AAM is not supported). -->
     <property name="AamVendorRecommendedValue" type="i" access="read"/>
 
+    <!-- WriteCacheSupported:
+         @since: 2.1
+         Whether the drive supports configuring the write cache.
+    -->
+    <property name="WriteCacheSupported" type="b" access="read"/>
+
+    <!-- WriteCacheEnabled:
+         @since: 2.1
+         Whether the write-cache is enabled (or %FALSE if not supported).
+    -->
+    <property name="WriteCacheEnabled" type="b" access="read"/>
+
     <!--
         PmGetState:
         @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>).
index d7b5702..46a6241 100644 (file)
             </para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><option>WriteCacheEnabled</option> (Since 2.1)</term>
+          <listitem>
+            <para>
+              Whether to enabled or disable the Write Cache.
+              This is similar to the <option>-W</option> option in
+              <citerefentry><refentrytitle>hdparm</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+            </para>
+          </listitem>
+        </varlistentry>
       </variablelist>
     </refsect2>
   </refsect1>
index 53a578c..aa329f3 100644 (file)
@@ -772,6 +772,8 @@ udisks_drive_ata_get_apm_enabled
 udisks_drive_ata_get_apm_supported
 udisks_drive_ata_get_pm_enabled
 udisks_drive_ata_get_pm_supported
+udisks_drive_ata_get_write_cache_enabled
+udisks_drive_ata_get_write_cache_supported
 udisks_drive_ata_get_security_enhanced_erase_unit_minutes
 udisks_drive_ata_get_security_erase_unit_minutes
 udisks_drive_ata_get_security_frozen
@@ -793,6 +795,8 @@ udisks_drive_ata_set_apm_enabled
 udisks_drive_ata_set_apm_supported
 udisks_drive_ata_set_pm_enabled
 udisks_drive_ata_set_pm_supported
+udisks_drive_ata_set_write_cache_enabled
+udisks_drive_ata_set_write_cache_supported
 udisks_drive_ata_set_security_enhanced_erase_unit_minutes
 udisks_drive_ata_set_security_erase_unit_minutes
 udisks_drive_ata_set_security_frozen
index 78e0f3e..9bf279a 100644 (file)
@@ -211,10 +211,11 @@ typedef struct {
   const GVariantType *type;
 } VariantKeyfileMapping;
 
-static const VariantKeyfileMapping drive_configuration_mapping[3] = {
-  {"ata-pm-standby",  "ATA", "StandbyTimeout", G_VARIANT_TYPE_INT32},
-  {"ata-apm-level",   "ATA", "APMLevel",       G_VARIANT_TYPE_INT32},
-  {"ata-aam-level",   "ATA", "AAMLevel",       G_VARIANT_TYPE_INT32},
+static const VariantKeyfileMapping drive_configuration_mapping[4] = {
+  {"ata-pm-standby",          "ATA", "StandbyTimeout",    G_VARIANT_TYPE_INT32},
+  {"ata-apm-level",           "ATA", "APMLevel",          G_VARIANT_TYPE_INT32},
+  {"ata-aam-level",           "ATA", "AAMLevel",          G_VARIANT_TYPE_INT32},
+  {"ata-write-cache-enabled", "ATA", "WriteCacheEnabled", G_VARIANT_TYPE_BOOLEAN},
 };
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -291,6 +292,21 @@ update_configuration (UDisksLinuxDrive       *drive,
               g_variant_builder_add (&builder, "{sv}", mapping->asv_key, g_variant_new_int32 (int_value));
             }
         }
+      else if (mapping->type == G_VARIANT_TYPE_BOOLEAN)
+        {
+          gboolean bool_value = g_key_file_get_boolean (key_file, mapping->group, mapping->key, &error);
+          if (error != NULL)
+            {
+              udisks_error ("Error parsing boolean key %s in group %s in drive config file %s: %s (%s, %d)",
+                            mapping->key, mapping->group, path,
+                            error->message, g_quark_to_string (error->domain), error->code);
+              g_clear_error (&error);
+            }
+          else
+            {
+              g_variant_builder_add (&builder, "{sv}", mapping->asv_key, g_variant_new_boolean (bool_value));
+            }
+        }
       else
         {
           g_assert_not_reached ();
@@ -1129,6 +1145,10 @@ handle_set_configuration (UDisksDrive           *_drive,
             {
               g_key_file_set_integer (key_file, mapping->group, mapping->key, g_variant_get_int32 (value));
             }
+          else if (mapping->type == G_VARIANT_TYPE_BOOLEAN)
+            {
+              g_key_file_set_boolean (key_file, mapping->group, mapping->key, g_variant_get_boolean (value));
+            }
           else
             {
               g_assert_not_reached ();
index 06fa0c8..5a0bebe 100644 (file)
@@ -222,6 +222,8 @@ update_pm (UDisksLinuxDriveAta *drive,
   gboolean apm_enabled = FALSE;
   gboolean aam_supported = FALSE;
   gboolean aam_enabled = FALSE;
+  gboolean write_cache_supported = FALSE;
+  gboolean write_cache_enabled = FALSE;
   gint aam_vendor_recommended_value = 0;
   guint16 word_82 = 0;
   guint16 word_83 = 0;
@@ -244,6 +246,8 @@ update_pm (UDisksLinuxDriveAta *drive,
   aam_enabled   = word_86 & (1<<9);
   if (aam_supported)
     aam_vendor_recommended_value = (word_94 >> 8);
+  write_cache_supported  = word_82 & (1<<5);
+  write_cache_enabled    = word_85 & (1<<5);
 
   g_object_freeze_notify (G_OBJECT (drive));
   udisks_drive_ata_set_pm_supported (UDISKS_DRIVE_ATA (drive), !!pm_supported);
@@ -253,6 +257,8 @@ update_pm (UDisksLinuxDriveAta *drive,
   udisks_drive_ata_set_aam_supported (UDISKS_DRIVE_ATA (drive), !!aam_supported);
   udisks_drive_ata_set_aam_enabled (UDISKS_DRIVE_ATA (drive), !!aam_enabled);
   udisks_drive_ata_set_aam_vendor_recommended_value (UDISKS_DRIVE_ATA (drive), aam_vendor_recommended_value);
+  udisks_drive_ata_set_write_cache_supported (UDISKS_DRIVE_ATA (drive), !!write_cache_supported);
+  udisks_drive_ata_set_write_cache_enabled (UDISKS_DRIVE_ATA (drive), !!write_cache_enabled);
   g_object_thaw_notify (G_OBJECT (drive));
 }
 
@@ -1585,6 +1591,8 @@ typedef struct
   gint ata_pm_standby;
   gint ata_apm_level;
   gint ata_aam_level;
+  gboolean ata_write_cache_enabled;
+  gboolean ata_write_cache_enabled_set;
   UDisksLinuxDriveAta *ata;
   UDisksLinuxDevice *device;
   GVariant *configuration;
@@ -1712,6 +1720,35 @@ apply_configuration_thread_func (gpointer user_data)
         }
     }
 
+  if (data->ata_write_cache_enabled_set)
+    {
+      /* ATA8: 7.48 SET FEATURES - EFh, Non-Data
+       *       7.48.4 Enable/disable volatile write cache
+       */
+      UDisksAtaCommandInput input = {.command = 0xef, .feature = 0x82};
+      UDisksAtaCommandOutput output = {0};
+      if (data->ata_write_cache_enabled)
+        input.feature = 0x02;
+      if (!udisks_ata_send_command_sync (fd,
+                                         -1,
+                                         UDISKS_ATA_COMMAND_PROTOCOL_NONE,
+                                         &input,
+                                         &output,
+                                         &error))
+        {
+          udisks_error ("Error sending ATA command SET FEATURES, sub-command 0x%02x to %s: %s (%s, %d)",
+                        input.feature, device_file,
+                        error->message, g_quark_to_string (error->domain), error->code);
+          g_clear_error (&error);
+        }
+      else
+        {
+          udisks_notice ("%s Write-Cache on %s [%s]",
+                         data->ata_write_cache_enabled ? "Enabled" : "Disabled",
+                         device_file, udisks_drive_get_id (data->drive));
+        }
+    }
+
  out:
   if (fd != -1)
     close (fd);
@@ -1740,6 +1777,8 @@ udisks_linux_drive_ata_apply_configuration (UDisksLinuxDriveAta     *drive,
   data->ata_pm_standby = -1;
   data->ata_apm_level = -1;
   data->ata_aam_level = -1;
+  data->ata_write_cache_enabled = FALSE;
+  data->ata_write_cache_enabled_set = FALSE;
   data->ata = g_object_ref (drive);
   data->device = g_object_ref (device);
   data->configuration = g_variant_ref (configuration);
@@ -1756,6 +1795,11 @@ udisks_linux_drive_ata_apply_configuration (UDisksLinuxDriveAta     *drive,
   has_conf |= g_variant_lookup (configuration, "ata-pm-standby", "i", &data->ata_pm_standby);
   has_conf |= g_variant_lookup (configuration, "ata-apm-level", "i", &data->ata_apm_level);
   has_conf |= g_variant_lookup (configuration, "ata-aam-level", "i", &data->ata_aam_level);
+  if (g_variant_lookup (configuration, "ata-write-cache-enabled", "b", &data->ata_write_cache_enabled))
+    {
+      data->ata_write_cache_enabled_set = TRUE;
+      has_conf = TRUE;
+    }
 
   /* don't do anything if none of the configuration is set */
   if (!has_conf)