[gnome-disk-utility/new-ui] Add a benchmark dialog a show WWN, Write-Cache and Rotational Rate



commit 721d363455dbbcc730256f8600a33d333b143c69
Author: David Zeuthen <davidz redhat com>
Date:   Wed Nov 25 13:02:28 2009 -0500

    Add a benchmark dialog a show WWN, Write-Cache and Rotational Rate

 src/gdu-gtk/Makefile.am                     |    2 +
 src/gdu-gtk/gdu-drive-benchmark-dialog.c    | 1337 +++++++++++++++++++++++++++
 src/gdu-gtk/gdu-drive-benchmark-dialog.h    |   63 ++
 src/gdu-gtk/gdu-gtk-types.h                 |    1 +
 src/gdu-gtk/gdu-gtk.h                       |    1 +
 src/gdu/gdu-callbacks.h                     |    7 +
 src/gdu/gdu-device.c                        |   82 ++
 src/gdu/gdu-device.h                        |   11 +
 src/gdu/gdu-util.c                          |    8 +-
 src/palimpsest/gdu-section-drive.c          |   87 ++-
 src/palimpsest/gdu-section-drive.h          |    7 +-
 src/palimpsest/gdu-section-linux-md-drive.c |   15 +
 12 files changed, 1612 insertions(+), 9 deletions(-)
---
diff --git a/src/gdu-gtk/Makefile.am b/src/gdu-gtk/Makefile.am
index 19c9403..bdff6a7 100644
--- a/src/gdu-gtk/Makefile.am
+++ b/src/gdu-gtk/Makefile.am
@@ -53,6 +53,7 @@ libgdu_gtkinclude_HEADERS =              				\
 	gdu-disk-selection-widget.h					\
 	gdu-add-component-linux-md-dialog.h				\
 	gdu-edit-linux-md-dialog.h					\
+	gdu-drive-benchmark-dialog.h					\
 	$(NULL)
 
 libgdu_gtk_la_SOURCES =                 	               				\
@@ -83,6 +84,7 @@ libgdu_gtk_la_SOURCES =                 	               				\
 	gdu-disk-selection-widget.h		gdu-disk-selection-widget.c		\
 	gdu-add-component-linux-md-dialog.h	gdu-add-component-linux-md-dialog.c	\
 	gdu-edit-linux-md-dialog.h		gdu-edit-linux-md-dialog.c		\
+	gdu-drive-benchmark-dialog.h		gdu-drive-benchmark-dialog.c		\
 	$(NULL)
 
 libgdu_gtk_la_CPPFLAGS = 				\
diff --git a/src/gdu-gtk/gdu-drive-benchmark-dialog.c b/src/gdu-gtk/gdu-drive-benchmark-dialog.c
new file mode 100644
index 0000000..8080814
--- /dev/null
+++ b/src/gdu-gtk/gdu-drive-benchmark-dialog.c
@@ -0,0 +1,1337 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-drive-benchmark-dialog.c
+ *
+ * Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <math.h>
+#include <dbus/dbus-glib.h>
+
+#include "gdu-time-label.h"
+#include "gdu-drive-benchmark-dialog.h"
+#include "gdu-spinner.h"
+#include "gdu-details-table.h"
+#include "gdu-details-element.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* gah, omgwtf, dbus-glib - this will be much nicer when we switch to GVariant */
+
+/* For now we store the benchmark data in $HOME/.cache - once DKD
+ * grows a message subsystem (e.g. a database storing time-stamped
+ * messages/events on a per device basis) we can switch to using
+ * that.
+ */
+
+typedef struct {
+        guint64 offset;
+        gdouble value;
+} BenchmarkPoint;
+
+typedef struct {
+        guint64 time_collected;
+        guint64 disk_size;
+
+        GArray *read_transfer_rate_samples;
+        GArray *write_transfer_rate_samples;
+        GArray *access_time_samples;
+} BenchmarkData;
+
+static void
+benchmark_get_max_min_avg (GArray *array,
+                           gdouble *out_max,
+                           gdouble *out_min,
+                           gdouble *out_avg)
+{
+        guint n;
+        gdouble max;
+        gdouble min;
+        gdouble avg;
+        gdouble sum;
+
+        if (array->len == 0) {
+                max = 0;
+                min = 0;
+                avg = 0;
+                goto out;
+        }
+        max = -G_MAXDOUBLE;
+        min = G_MAXDOUBLE;
+        sum = 0;
+
+        for (n = 0; n < array->len; n++) {
+                BenchmarkPoint *point = &g_array_index (array, BenchmarkPoint, n);
+                if (point->value > max)
+                        max = point->value;
+                if (point->value < min)
+                        min = point->value;
+                sum += point->value;
+        }
+        avg = sum / array->len;
+
+ out:
+        if (out_max != NULL)
+                *out_max = max;
+        if (out_min != NULL)
+                *out_min = min;
+        if (out_avg != NULL)
+                *out_avg = avg;
+}
+
+static void
+benchmark_data_free (BenchmarkData *data)
+{
+        g_array_unref (data->read_transfer_rate_samples);
+        g_array_unref (data->write_transfer_rate_samples);
+        g_array_unref (data->access_time_samples);
+        g_free (data);
+}
+
+G_GNUC_UNUSED static void
+benchmark_data_print (BenchmarkData *data)
+{
+        gchar time_buf[256];
+        struct tm *time_tm;
+        time_t t;
+        gchar *s;
+        guint n;
+
+        t = data->time_collected;
+        time_tm = localtime (&t);
+        strftime (time_buf, sizeof time_buf, "%c", time_tm);
+
+        g_print ("Collected at: %s\n", time_buf);
+        s = gdu_util_get_size_for_display (data->disk_size, FALSE, TRUE);
+        g_print ("Disk size: %s\n", s);
+        g_free (s);
+        g_print ("Read transfer rate:\n");
+        for (n = 0; n < data->read_transfer_rate_samples->len; n++) {
+                BenchmarkPoint *point = &g_array_index (data->read_transfer_rate_samples, BenchmarkPoint, n);
+                g_print (" Position %3d%% -> %3.2f MB/s\n",
+                         (gint) (100.0 * point->offset / data->disk_size),
+                         point->value / (1000.0 * 1000.0));
+        }
+        g_print ("Write transfer rate:\n");
+        for (n = 0; n < data->write_transfer_rate_samples->len; n++) {
+                BenchmarkPoint *point = &g_array_index (data->write_transfer_rate_samples, BenchmarkPoint, n);
+                g_print (" Position %3d%% -> %3.2f MB/s\n",
+                         (gint) (100.0 * point->offset / data->disk_size),
+                         point->value / (1000.0 * 1000.0));
+        }
+        g_print ("Access times:\n");
+        for (n = 0; n < data->access_time_samples->len; n++) {
+                BenchmarkPoint *point = &g_array_index (data->access_time_samples, BenchmarkPoint, n);
+                g_print (" Position %3d%% -> %3.2f ms\n",
+                         (gint) (100.0 * point->offset / data->disk_size),
+                         point->value * 1000.0);
+        }
+}
+
+static void
+benchmark_convert (GPtrArray *in,
+                   GArray    *out)
+{
+        guint n;
+
+        GType pair_type;
+
+        pair_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT64, G_TYPE_DOUBLE, G_TYPE_INVALID);
+
+        for (n = 0; n < in->len; n++) {
+                GValue elem = {0};
+                BenchmarkPoint point;
+
+                g_value_init (&elem, pair_type);
+                g_value_set_static_boxed (&elem, in->pdata[n]);
+
+                dbus_g_type_struct_get (&elem,
+                                        0, &(point.offset),
+                                        1, &(point.value),
+                                        G_MAXUINT);
+                g_array_append_val (out, point);
+        }
+}
+
+static BenchmarkData *
+benchmark_data_from_dbus (GduDevice    *device,
+                          GPtrArray    *read_transfer_rate_results,
+                          GPtrArray    *write_transfer_rate_results,
+                          GPtrArray    *access_time_results)
+{
+        BenchmarkData *data;
+
+        data = g_new0 (BenchmarkData, 1);
+        data->time_collected = time (NULL);
+        data->disk_size = gdu_device_get_size (device);
+        data->read_transfer_rate_samples = g_array_sized_new (FALSE,
+                                                              FALSE,
+                                                              sizeof (BenchmarkPoint),
+                                                              read_transfer_rate_results->len);
+        data->write_transfer_rate_samples = g_array_sized_new (FALSE,
+                                                               FALSE,
+                                                               sizeof (BenchmarkPoint),
+                                                               write_transfer_rate_results->len);
+        data->access_time_samples = g_array_sized_new (FALSE,
+                                                       FALSE,
+                                                       sizeof (BenchmarkPoint),
+                                                       access_time_results->len);
+
+        benchmark_convert (read_transfer_rate_results,
+                           data->read_transfer_rate_samples);
+        benchmark_convert (write_transfer_rate_results,
+                           data->write_transfer_rate_samples);
+        benchmark_convert (access_time_results,
+                           data->access_time_samples);
+
+        return data;
+}
+
+static BenchmarkData *
+benchmark_data_load (GduDevice   *device,
+                     GError     **error)
+{
+        BenchmarkData *data;
+        gchar *filename;
+        gchar *s;
+        gchar *s2;
+        gchar *contents;
+        gchar **lines;
+        guint n;
+
+        contents = NULL;
+        lines = NULL;
+
+        data = g_new0 (BenchmarkData, 1);
+        data->time_collected = time (NULL);
+        data->disk_size = gdu_device_get_size (device);
+        data->read_transfer_rate_samples = g_array_new (FALSE,
+                                                        FALSE,
+                                                        sizeof (BenchmarkPoint));
+        data->write_transfer_rate_samples = g_array_new (FALSE,
+                                                         FALSE,
+                                                         sizeof (BenchmarkPoint));
+        data->access_time_samples = g_array_new (FALSE,
+                                                 FALSE,
+                                                 sizeof (BenchmarkPoint));
+
+
+        s = g_strdup_printf ("%s/gnome-disk-utility/drive-benchmark",
+                             g_get_user_cache_dir ());
+        s2 = g_strdup_printf ("%s-%s-%s-%s",
+                              gdu_device_drive_get_vendor (device),
+                              gdu_device_drive_get_model (device),
+                              gdu_device_drive_get_revision (device),
+                              gdu_device_drive_get_serial (device));
+        filename = g_strdup_printf ("%s/%s",
+                                    s, s2);
+        g_free (s);
+        g_free (s2);
+
+        if (!g_file_get_contents (filename,
+                                  &contents,
+                                  NULL,
+                                  error)) {
+                benchmark_data_free (data);
+                data = NULL;
+                goto out;
+        }
+
+        lines = g_strsplit (contents, "\n", 0);
+        for (n = 0; lines != NULL && lines[n] != NULL; n++) {
+                const gchar *line = lines[n];
+                gchar **samples;
+                guint m;
+
+                if (sscanf (line, "time_collected=%" G_GUINT64_FORMAT,
+                            &(data->time_collected)) == 1) {
+                        ;
+                } else if (sscanf (line, "disk_size=%" G_GUINT64_FORMAT,
+                                   &(data->disk_size)) == 1) {
+                        ;
+                } else if (g_str_has_prefix (line, "read_transfer_rate_samples=")) {
+                        samples = g_strsplit (line + sizeof "read_transfer_rate_samples=" - 1, ":", 0);
+                        for (m = 0; samples != NULL && samples[m] != NULL; m++) {
+                                const gchar *sample = samples[m];
+                                BenchmarkPoint point;
+
+                                if (sscanf (sample, "%" G_GUINT64_FORMAT ";%lf",
+                                            &(point.offset),
+                                            &(point.value)) != 2) {
+                                        g_set_error (error,
+                                                     GDU_ERROR,
+                                                     GDU_ERROR_FAILED,
+                                                     "Bogus read_transfer_rate sample %d `%s'",
+                                                     m,
+                                                     sample);
+                                        benchmark_data_free (data);
+                                        data = NULL;
+                                        goto out;
+                                }
+                                g_array_append_val (data->read_transfer_rate_samples, point);
+                        }
+                        g_strfreev (samples);
+
+                } else if (g_str_has_prefix (line, "write_transfer_rate_samples=")) {
+                        samples = g_strsplit (line + sizeof "write_transfer_rate_samples=" - 1, ":", 0);
+                        for (m = 0; samples != NULL && samples[m] != NULL; m++) {
+                                const gchar *sample = samples[m];
+                                BenchmarkPoint point;
+
+                                if (sscanf (sample, "%" G_GUINT64_FORMAT ";%lf",
+                                            &(point.offset),
+                                            &(point.value)) != 2) {
+                                        g_set_error (error,
+                                                     GDU_ERROR,
+                                                     GDU_ERROR_FAILED,
+                                                     "Bogus write_transfer_rate sample %d `%s'",
+                                                     m,
+                                                     sample);
+                                        benchmark_data_free (data);
+                                        data = NULL;
+                                        goto out;
+                                }
+                                g_array_append_val (data->write_transfer_rate_samples, point);
+                        }
+                        g_strfreev (samples);
+
+                } else if (g_str_has_prefix (line, "access_time_samples=")) {
+                        samples = g_strsplit (line + sizeof "access_time_samples=" - 1, ":", 0);
+                        for (m = 0; samples != NULL && samples[m] != NULL; m++) {
+                                const gchar *sample = samples[m];
+                                BenchmarkPoint point;
+
+                                if (sscanf (sample, "%" G_GUINT64_FORMAT ";%lf",
+                                            &(point.offset),
+                                            &(point.value)) != 2) {
+                                        g_set_error (error,
+                                                     GDU_ERROR,
+                                                     GDU_ERROR_FAILED,
+                                                     "Bogus access_time sample %d `%s'",
+                                                     m,
+                                                     sample);
+                                        benchmark_data_free (data);
+                                        data = NULL;
+                                        goto out;
+                                }
+                                g_array_append_val (data->access_time_samples, point);
+                        }
+                        g_strfreev (samples);
+                } else if (strlen (line) > 0) {
+                        g_set_error (error,
+                                     GDU_ERROR,
+                                     GDU_ERROR_FAILED,
+                                     "Unable to parse line `%s'", line);
+                        benchmark_data_free (data);
+                        data = NULL;
+                        goto out;
+                }
+        }
+
+ out:
+        g_free (filename);
+        g_free (contents);
+        g_strfreev (lines);
+        return data;
+}
+
+static gboolean
+benchmark_data_save (BenchmarkData  *data,
+                     GduDevice      *device,
+                     GError        **error)
+{
+        gboolean ret;
+        gchar *filename;
+        GString *str;
+        gchar *s;
+        gchar *s2;
+        guint n;
+
+        str = NULL;
+        filename = NULL;
+        ret = FALSE;
+
+        s = g_strdup_printf ("%s/gnome-disk-utility/drive-benchmark",
+                             g_get_user_cache_dir ());
+        if (g_mkdir_with_parents (s, 0755) != 0) {
+                g_set_error (error,
+                             GDU_ERROR,
+                             GDU_ERROR_FAILED,
+                             "Error creating directory %s: %m",
+                             s);
+                g_free (s);
+                goto out;
+        }
+
+        s2 = g_strdup_printf ("%s-%s-%s-%s",
+                              gdu_device_drive_get_vendor (device),
+                              gdu_device_drive_get_model (device),
+                              gdu_device_drive_get_revision (device),
+                              gdu_device_drive_get_serial (device));
+        filename = g_strdup_printf ("%s/%s",
+                                    s, s2);
+        g_free (s);
+        g_free (s2);
+
+        str = g_string_new (NULL);
+        g_string_append_printf (str, "time_collected=%" G_GUINT64_FORMAT "\n", data->time_collected);
+        g_string_append_printf (str, "disk_size=%" G_GUINT64_FORMAT "\n", data->disk_size);
+
+        g_string_append (str, "read_transfer_rate_samples=");
+        for (n = 0; n < data->read_transfer_rate_samples->len; n++) {
+                BenchmarkPoint *point = &g_array_index (data->read_transfer_rate_samples, BenchmarkPoint, n);
+                if (n > 0)
+                        g_string_append_c (str, ':');
+                g_string_append_printf (str,
+                                        "%" G_GUINT64_FORMAT ";%f",
+                                        point->offset,
+                                        point->value);
+        }
+        g_string_append_c (str, '\n');
+
+        g_string_append (str, "write_transfer_rate_samples=");
+        for (n = 0; n < data->write_transfer_rate_samples->len; n++) {
+                BenchmarkPoint *point = &g_array_index (data->write_transfer_rate_samples, BenchmarkPoint, n);
+                if (n > 0)
+                        g_string_append_c (str, ':');
+                g_string_append_printf (str,
+                                        "%" G_GUINT64_FORMAT ";%f",
+                                        point->offset,
+                                        point->value);
+        }
+        g_string_append_c (str, '\n');
+
+        g_string_append (str, "access_time_samples=");
+        for (n = 0; n < data->access_time_samples->len; n++) {
+                BenchmarkPoint *point = &g_array_index (data->access_time_samples, BenchmarkPoint, n);
+                if (n > 0)
+                        g_string_append_c (str, ':');
+                g_string_append_printf (str,
+                                        "%" G_GUINT64_FORMAT ";%f",
+                                        point->offset,
+                                        point->value);
+        }
+        g_string_append_c (str, '\n');
+
+        if (!g_file_set_contents (filename,
+                                  str->str,
+                                  -1,
+                                  error))
+                goto out;
+
+        ret = TRUE;
+
+ out:
+        g_free (filename);
+        if (str != NULL)
+                g_string_free (str, TRUE);
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+struct GduDriveBenchmarkDialogPrivate
+{
+        gulong device_changed_signal_handler_id;
+        gulong device_job_changed_signal_handler_id;
+
+        gboolean deleted;
+
+        GtkWidget *drawing_area;
+
+        BenchmarkData *benchmark_data;
+
+        /* elements for benchmark results */
+        GduDetailsElement *read_min_element;
+        GduDetailsElement *write_min_element;
+        GduDetailsElement *read_max_element;
+        GduDetailsElement *write_max_element;
+        GduDetailsElement *read_avg_element;
+        GduDetailsElement *write_avg_element;
+        GduDetailsElement *updated_element;
+        GduDetailsElement *access_avg_element;
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+G_DEFINE_TYPE (GduDriveBenchmarkDialog, gdu_drive_benchmark_dialog, GDU_TYPE_DIALOG)
+
+static gdouble is_benchmarking (GduDriveBenchmarkDialog *dialog,
+                                gdouble                 *out_progress);
+
+static gboolean on_drawing_area_expose_event (GtkWidget      *widget,
+                                              GdkEventExpose *event,
+                                              gpointer        user_data);
+
+static void update_dialog (GduDriveBenchmarkDialog *dialog);
+static void on_device_changed (GduDevice *device, gpointer user_data);
+static void on_device_job_changed (GduDevice *device, gpointer user_data);
+
+static void
+gdu_drive_benchmark_dialog_finalize (GObject *object)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (object);
+
+        g_signal_handler_disconnect (gdu_dialog_get_device (GDU_DIALOG (dialog)), dialog->priv->device_changed_signal_handler_id);
+        g_signal_handler_disconnect (gdu_dialog_get_device (GDU_DIALOG (dialog)), dialog->priv->device_job_changed_signal_handler_id);
+
+        if (dialog->priv->benchmark_data != NULL) {
+                benchmark_data_free (dialog->priv->benchmark_data);
+        }
+
+        if (G_OBJECT_CLASS (gdu_drive_benchmark_dialog_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_drive_benchmark_dialog_parent_class)->finalize (object);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+benchmark_cb (GduDevice    *device,
+              GPtrArray    *read_transfer_rate_results,
+              GPtrArray    *write_transfer_rate_results,
+              GPtrArray    *access_time_results,
+              GError       *error,
+              gpointer      user_data)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (user_data);
+        GError *local_error;
+
+        if (error != NULL) {
+                if (!(error->domain == GDU_ERROR && error->code == GDU_ERROR_CANCELLED)) {
+                        GtkWidget *error_dialog;
+                        error_dialog = gdu_error_dialog_new_for_drive (GTK_WINDOW (dialog),
+                                                                       device,
+                                                                       _("Error benchmarking drive"),
+                                                                       error);
+                        gtk_widget_show_all (error_dialog);
+                        gtk_window_present (GTK_WINDOW (error_dialog));
+                        gtk_dialog_run (GTK_DIALOG (error_dialog));
+                        gtk_widget_destroy (error_dialog);
+                }
+                g_error_free (error);
+                goto out;
+        }
+
+        if (dialog->priv->benchmark_data != NULL) {
+                benchmark_data_free (dialog->priv->benchmark_data);
+        }
+        dialog->priv->benchmark_data = benchmark_data_from_dbus (device,
+                                                                 read_transfer_rate_results,
+                                                                 write_transfer_rate_results,
+                                                                 access_time_results);
+        g_ptr_array_unref (read_transfer_rate_results);
+        g_ptr_array_unref (write_transfer_rate_results);
+        g_ptr_array_unref (access_time_results);
+
+        /*benchmark_data_print (dialog->priv->benchmark_data);*/
+
+        local_error = NULL;
+        if (!benchmark_data_save (dialog->priv->benchmark_data, device, &local_error)) {
+                g_warning ("Error saving benchmark data: %s", local_error->message);
+                g_error_free (local_error);
+        }
+
+ out:
+        if (!dialog->priv->deleted) {
+                update_dialog (dialog);
+                gtk_widget_queue_draw_area (dialog->priv->drawing_area,
+                                            0,
+                                            0,
+                                            dialog->priv->drawing_area->allocation.width,
+                                            dialog->priv->drawing_area->allocation.height);
+        }
+        g_object_unref (dialog);
+}
+
+static void
+on_run_benchmark_clicked (GduButtonElement *button_element,
+                          gpointer          user_data)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (user_data);
+        const gchar *options[1] = {NULL};
+
+        gdu_device_op_drive_benchmark (gdu_dialog_get_device (GDU_DIALOG (dialog)),
+                                       FALSE,
+                                       options,
+                                       benchmark_cb,
+                                       g_object_ref (dialog));
+}
+
+static void
+on_run_write_benchmark_clicked (GduButtonElement *button_element,
+                                gpointer          user_data)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (user_data);
+        const gchar *options[1] = {NULL};
+        GtkWidget *confirmation_dialog;
+        gint response;
+
+        confirmation_dialog = gdu_confirmation_dialog_new_for_drive (GTK_WINDOW (dialog),
+                                                                     gdu_dialog_get_device (GDU_DIALOG (dialog)),
+                                                                     _("Are you sure you want to start a read/write benchmark?"),
+                                                                     _("_Benchmark"));
+        gtk_widget_show_all (confirmation_dialog);
+        response = gtk_dialog_run (GTK_DIALOG (confirmation_dialog));
+        gtk_widget_hide (confirmation_dialog);
+        if (response != GTK_RESPONSE_OK)
+                goto out;
+
+        gdu_device_op_drive_benchmark (gdu_dialog_get_device (GDU_DIALOG (dialog)),
+                                       TRUE,
+                                       options,
+                                       benchmark_cb,
+                                       g_object_ref (dialog));
+
+ out:
+        gtk_widget_destroy (confirmation_dialog);
+}
+
+static gboolean
+on_delete_event (GtkWidget *widget,
+                 GdkEvent  *event,
+                 gpointer   user_data)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (user_data);
+
+        dialog->priv->deleted = TRUE;
+        return FALSE; /* propagate further */
+}
+
+static void
+cancel_job_cb (GduDevice  *device,
+               GError     *error,
+               gpointer    user_data)
+{
+        /* TODO: maybe show error dialog */
+        if (error != NULL)
+                g_error_free (error);
+}
+
+static void
+on_updated_element_activated (GduDetailsElement    *element,
+                              gpointer     user_data)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (user_data);
+
+        if (is_benchmarking (dialog, NULL)) {
+                gdu_device_op_cancel_job (gdu_dialog_get_device (GDU_DIALOG (dialog)),
+                                          cancel_job_cb,
+                                          dialog);
+        }
+}
+
+static void
+gdu_drive_benchmark_dialog_constructed (GObject *object)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (object);
+        GtkWidget *content_area;
+        GtkWidget *align;
+        GtkWidget *vbox;
+        GtkWidget *vbox2;
+        GtkWidget *table;
+        GError *error;
+        GtkWidget *drawing_area;
+        GPtrArray *elements;
+        GduButtonElement *button_element;
+        GduDetailsElement *element;
+        gchar *s;
+        gchar *name;
+        gchar *vpd_name;
+
+        dialog->priv->device_changed_signal_handler_id = g_signal_connect (gdu_dialog_get_device (GDU_DIALOG (dialog)),
+                                                                           "changed",
+                                                                           G_CALLBACK (on_device_changed),
+                                                                           dialog);
+        dialog->priv->device_job_changed_signal_handler_id = g_signal_connect (gdu_dialog_get_device (GDU_DIALOG (dialog)),
+                                                                               "job-changed",
+                                                                               G_CALLBACK (on_device_job_changed),
+                                                                               dialog);
+
+        g_signal_connect (dialog,
+                          "delete-event",
+                          G_CALLBACK (on_delete_event),
+                          dialog);
+
+        name = gdu_presentable_get_name (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+        vpd_name = gdu_presentable_get_vpd_name (gdu_dialog_get_presentable (GDU_DIALOG (dialog)));
+        /* Translators: The title of the benchmark dialog.
+         * First %s is the name for the drive (e.g. "Fedora" or "1.0 TB Hard Disk")
+         * Second %s is the VPD name for the array (e.g. "158 GB RAID-0 Array" or "ATA WDC WD1001FALS-00J7B1").
+         */
+        s = g_strdup_printf ("%s (%s) â?? Benchmark", name, vpd_name);
+        gtk_window_set_title (GTK_WINDOW (dialog), s);
+        g_free (s);
+        g_free (vpd_name);
+        g_free (name);
+
+        gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+
+        content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 12, 12, 12, 12);
+        gtk_box_pack_start (GTK_BOX (content_area), align, TRUE, TRUE, 0);
+
+        vbox = gtk_vbox_new (FALSE, 12);
+        gtk_container_add (GTK_CONTAINER (align), vbox);
+
+        /* ---------------------------------------------------------------------------------------------------- */
+
+        drawing_area = gtk_drawing_area_new ();
+        dialog->priv->drawing_area = drawing_area;
+        gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
+
+        g_signal_connect (drawing_area,
+                          "expose-event",
+                          G_CALLBACK (on_drawing_area_expose_event),
+                          dialog);
+
+        /* set minimum size for the graph */
+        gtk_widget_set_size_request (drawing_area,
+                                     400,
+                                     300);
+
+        /* ---------------------------------------------------------------------------------------------------- */
+
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 36, 0);
+        gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
+
+        vbox2 = gtk_vbox_new (FALSE, 12);
+        gtk_container_add (GTK_CONTAINER (align), vbox2);
+
+        /* ---------------------------------------------------------------------------------------------------- */
+
+        elements = g_ptr_array_new_with_free_func (g_object_unref);
+
+        /* benchmark results */
+
+        element = gdu_details_element_new (_("Minimum Read Rate:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->read_min_element = element;
+
+        element = gdu_details_element_new (_("Minimum Write Rate:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->write_min_element = element;
+
+        element = gdu_details_element_new (_("Maximum Read Rate:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->read_max_element = element;
+
+        element = gdu_details_element_new (_("Maximum Write Rate:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->write_max_element = element;
+
+        element = gdu_details_element_new (_("Average Read Rate:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->read_avg_element = element;
+
+        element = gdu_details_element_new (_("Average Write Rate:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->write_avg_element = element;
+
+        element = gdu_details_element_new (_("Last Benchmark:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->updated_element = element;
+        g_signal_connect (element,
+                          "activated",
+                          G_CALLBACK (on_updated_element_activated),
+                          dialog);
+
+        element = gdu_details_element_new (_("Average Access Time:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        dialog->priv->access_avg_element = element;
+
+        table = gdu_details_table_new (2, elements);
+        g_ptr_array_unref (elements);
+        gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
+
+        /* ---------------------------------------------------------------------------------------------------- */
+
+        elements = g_ptr_array_new_with_free_func (g_object_unref);
+
+        button_element = gdu_button_element_new ("gtk-execute",
+                                                 _("Start _Read-Only Benchmark"),
+                                                 _("Measure read rate and access time"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_run_benchmark_clicked),
+                          dialog);
+        g_ptr_array_add (elements, button_element);
+
+        button_element = gdu_button_element_new ("gtk-execute", /* TODO: better icon */
+                                                 _("Start Read/_Write Benchmark"),
+                                                 _("Measure read rate, write rate and access time"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_run_write_benchmark_clicked),
+                          dialog);
+        g_ptr_array_add (elements, button_element);
+
+        table = gdu_button_table_new (2, elements);
+        g_ptr_array_unref (elements);
+        gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
+
+        /* ---------------------------------------------------------------------------------------------------- */
+
+        /* load data from cache, if available */
+        error = NULL;
+        dialog->priv->benchmark_data = benchmark_data_load (gdu_dialog_get_device (GDU_DIALOG (dialog)), &error);
+        if (dialog->priv->benchmark_data == NULL) {
+                if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT)) {
+                        g_warning ("Unable to load existing benchmark results: %s", error->message);
+                }
+                g_error_free (error);
+        } else {
+                /*benchmark_data_print (dialog->priv->benchmark_data);*/
+        }
+
+        update_dialog (dialog);
+
+        if (G_OBJECT_CLASS (gdu_drive_benchmark_dialog_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_drive_benchmark_dialog_parent_class)->constructed (object);
+}
+
+static void
+gdu_drive_benchmark_dialog_class_init (GduDriveBenchmarkDialogClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduDriveBenchmarkDialogPrivate));
+
+        object_class->constructed  = gdu_drive_benchmark_dialog_constructed;
+        object_class->finalize     = gdu_drive_benchmark_dialog_finalize;
+}
+
+static void
+gdu_drive_benchmark_dialog_init (GduDriveBenchmarkDialog *dialog)
+{
+        dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog, GDU_TYPE_DRIVE_BENCHMARK_DIALOG, GduDriveBenchmarkDialogPrivate);
+}
+
+GtkWidget *
+gdu_drive_benchmark_dialog_new (GtkWindow *parent,
+                                GduDrive  *drive)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_DRIVE_BENCHMARK_DIALOG,
+                                         "transient-for", parent,
+                                         "presentable", drive,
+                                         NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gdouble
+measure_width (cairo_t     *cr,
+               const gchar *s)
+{
+        cairo_text_extents_t te;
+        cairo_text_extents (cr, s, &te);
+        return te.width;
+}
+
+static gdouble
+measure_height (cairo_t     *cr,
+                const gchar *s)
+{
+        cairo_text_extents_t te;
+        cairo_text_extents (cr, s, &te);
+        return te.height;
+}
+
+static gboolean
+on_drawing_area_expose_event (GtkWidget      *widget,
+                              GdkEventExpose *event,
+                              gpointer        user_data)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (user_data);
+        cairo_t *cr;
+        gdouble width, height;
+        gdouble gx, gy, gw, gh;
+        guint n;
+        gdouble w, h;
+        gdouble x, y;
+        gdouble x_marker_height;
+        guint64 size;
+        gchar *s;
+        gdouble max_speed;
+        gdouble max_visible_speed;
+        gdouble speed_res;
+        gdouble max_time;
+        gdouble time_res;
+        gdouble max_visible_time;
+        gchar **y_left_markers;
+        gchar **y_right_markers;
+        guint num_y_markers;
+        GPtrArray *p;
+        GPtrArray *p2;
+
+        if (dialog->priv->benchmark_data == NULL) {
+                max_speed = 100 * 1000 * 1000;
+                max_time = 50 / 1000.0;
+        } else {
+                gdouble read_transfer_rate_max;
+                gdouble write_transfer_rate_max;
+                gdouble access_time_max;
+
+                benchmark_get_max_min_avg (dialog->priv->benchmark_data->read_transfer_rate_samples,
+                                           &read_transfer_rate_max,
+                                           NULL,
+                                           NULL);
+                benchmark_get_max_min_avg (dialog->priv->benchmark_data->write_transfer_rate_samples,
+                                           &write_transfer_rate_max,
+                                           NULL,
+                                           NULL);
+
+                benchmark_get_max_min_avg (dialog->priv->benchmark_data->access_time_samples,
+                                           &access_time_max,
+                                           NULL,
+                                           NULL);
+
+                max_speed = MAX (read_transfer_rate_max, write_transfer_rate_max);
+
+                max_time = access_time_max;
+        }
+
+        speed_res = (floor (((gdouble) max_speed) / (100 * 1000 * 1000)) + 1) * 1000 * 1000;
+        speed_res *= 10.0;
+        num_y_markers = (max_speed / speed_res) + 1;
+        max_visible_speed = speed_res * num_y_markers;
+
+        time_res = max_time / num_y_markers;
+        if (time_res < 0.0001) {
+                time_res = 0.0001;
+        } else if (time_res < 0.0005) {
+                time_res = 0.0005;
+        } else if (time_res < 0.001) {
+                time_res = 0.001;
+        } else if (time_res < 0.0025) {
+                time_res = 0.0025;
+        } else if (time_res < 0.005) {
+                time_res = 0.005;
+        } else {
+                time_res = ceil (((gdouble) time_res) / 0.005) * 0.005;
+        }
+        max_visible_time = time_res * num_y_markers;
+
+        /*g_debug ("max_visible_speed=%f, max_speed=%f, speed_res=%f", max_visible_speed, max_speed, speed_res);*/
+        /*g_debug ("max_visible_time=%f, max_time=%f, time_res=%f", max_visible_time, max_time, time_res);*/
+
+        p = g_ptr_array_new ();
+        p2 = g_ptr_array_new ();
+        for (n = 0; n <= num_y_markers; n++) {
+                gdouble val;
+
+                val = n * speed_res;
+                s = g_strdup_printf ("%d MB/s", (gint) (val / (1000 * 1000)));
+                g_ptr_array_add (p, s);
+
+                val = n * time_res;
+                s = g_strdup_printf ("%3g ms", val * 1000.0);
+                g_ptr_array_add (p2, s);
+        }
+        g_ptr_array_add (p, NULL);
+        g_ptr_array_add (p2, NULL);
+        y_left_markers = (gchar **) g_ptr_array_free (p, FALSE);
+        y_right_markers = (gchar **) g_ptr_array_free (p2, FALSE);
+
+        size = gdu_device_get_size (gdu_dialog_get_device (GDU_DIALOG (dialog)));
+
+        width = widget->allocation.width;
+        height = widget->allocation.height;
+
+        cr = gdk_cairo_create (widget->window);
+
+        cairo_select_font_face (cr, "sans",
+                                CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+        cairo_set_font_size (cr, 8.0);
+        cairo_set_line_width (cr, 1.0);
+
+        cairo_rectangle (cr,
+                         event->area.x, event->area.y,
+                         event->area.width, event->area.height);
+        cairo_clip (cr);
+
+#if 0
+        cairo_set_source_rgb (cr, 0.25, 0.25, 0.25);
+        cairo_rectangle (cr, 0, 0, width, height);
+        cairo_set_line_width (cr, 0.0);
+	cairo_fill (cr);
+#endif
+
+        gx = 0;
+        gy = 0;
+        gw = width;
+        gh = height;
+
+        /* make horizontal and vertical room for x markers ("%d%%") */
+        w = ceil (measure_width (cr, "0%") / 2.0);
+        gx +=  w;
+        gw -=  w;
+        w = ceil (measure_width (cr, "100%") / 2.0);
+        x_marker_height = ceil (measure_height (cr, "100%")) + 10;
+        gw -= w;
+        gh -= x_marker_height;
+
+        /* make horizontal room for left y markers ("%d MB/s") */
+        for (n = 0; n <= num_y_markers; n++) {
+                w = ceil (measure_width (cr, y_left_markers[n])) + 2 * 3;
+                if (w > gx) {
+                        gdouble needed = w - gx;
+                        gx += needed;
+                        gw -= needed;
+                }
+        }
+        /* make vertical room for top-left y marker */
+        h = ceil (measure_height (cr, y_left_markers[num_y_markers]) / 2.0);
+        if (h > gy) {
+                gdouble needed = h - gy;
+                gy += needed;
+                gh -= needed;
+        }
+
+        /* make horizontal room for right y markers ("%d ms") */
+        for (n = 0; n <= num_y_markers; n++) {
+                w = ceil (measure_width (cr, y_right_markers[n])) + 2 * 3;
+                if (w > width - (gx + gw)) {
+                        gdouble needed = w - (width - (gx + gw));
+                        gw -= needed;
+                }
+        }
+        /* make vertical room for top-right y marker */
+        h = ceil (measure_height (cr, y_right_markers[num_y_markers]) / 2.0);
+        if (h > gy) {
+                gdouble needed = h - gy;
+                gy += needed;
+                gh -= needed;
+        }
+
+        /* draw x markers ("%d%%") + vertical grid */
+        for (n = 0; n <= 10; n++) {
+                cairo_text_extents_t te;
+
+                x = gx + ceil (n * gw / 10.0);
+                y = gy + gh + x_marker_height/2.0;
+
+                s = g_strdup_printf ("%d%%", n * 10);
+
+                cairo_text_extents (cr, s, &te);
+
+                cairo_move_to (cr,
+                               x - te.x_bearing - te.width/2,
+                               y - te.y_bearing - te.height/2);
+                cairo_set_source_rgb (cr, 0, 0, 0);
+                cairo_show_text (cr, s);
+
+                g_free (s);
+        }
+
+        /* draw left y markers ("%d MB/s") */
+        for (n = 0; n <= num_y_markers; n++) {
+                cairo_text_extents_t te;
+
+                x = gx/2.0;
+                y = gy + gh - gh * n / num_y_markers;
+
+                s = y_left_markers[n];
+                cairo_text_extents (cr, s, &te);
+                cairo_move_to (cr,
+                               x - te.x_bearing - te.width/2,
+                               y - te.y_bearing - te.height/2);
+                cairo_set_source_rgb (cr, 0, 0, 0);
+                cairo_show_text (cr, s);
+        }
+
+        /* draw right y markers ("%d ms") */
+        for (n = 0; n <= num_y_markers; n++) {
+                cairo_text_extents_t te;
+
+                x = gx + gw + (width - (gx + gw))/2.0;
+                y = gy + gh - gh * n / num_y_markers;
+
+                s = y_right_markers[n];
+                cairo_text_extents (cr, s, &te);
+                cairo_move_to (cr,
+                               x - te.x_bearing - te.width/2,
+                               y - te.y_bearing - te.height/2);
+                cairo_set_source_rgb (cr, 0, 0, 0);
+                cairo_show_text (cr, s);
+        }
+
+        /* fill graph area */
+        cairo_set_source_rgb (cr, 1, 1, 1);
+        cairo_rectangle (cr, gx + 0.5, gy + 0.5, gw, gh);
+	cairo_fill_preserve (cr);
+        /* grid - first a rect */
+        cairo_set_source_rgba (cr, 0, 0, 0, 0.25);
+        cairo_set_line_width (cr, 1.0);
+        /* rect - also clip to rect for all future drawing operations */
+        cairo_stroke_preserve (cr);
+        cairo_clip (cr);
+        /* vertical lines */
+        for (n = 1; n < 10; n++) {
+                x = gx + ceil (n * gw / 10.0);
+                cairo_move_to (cr, x + 0.5, gy + 0.5);
+                cairo_line_to (cr, x + 0.5, gy + gh + 0.5);
+                cairo_stroke (cr);
+        }
+        /* horizontal lines */
+        for (n = 1; n < num_y_markers; n++) {
+                y = gy + ceil (n * gh / num_y_markers);
+                cairo_move_to (cr, gx + 0.5, y + 0.5);
+                cairo_line_to (cr, gx + gw + 0.5, y + 0.5);
+                cairo_stroke (cr);
+        }
+
+        if (dialog->priv->benchmark_data != NULL) {
+                BenchmarkData *data = dialog->priv->benchmark_data;
+                gdouble prev_x;
+                gdouble prev_y;
+
+                /* draw access time dots + lines */
+                cairo_set_line_width (cr, 0.5);
+                for (n = 0; n < data->access_time_samples->len; n++) {
+                        BenchmarkPoint *point = &g_array_index (data->access_time_samples, BenchmarkPoint, n);
+
+                        x = gx + gw * point->offset / data->disk_size;
+                        y = gy + gh - gh * point->value / max_visible_time;
+
+                        /*g_debug ("time = %f @ %f", point->value, x);*/
+
+                        cairo_set_source_rgba (cr, 0.4, 1.0, 0.4, 0.5);
+                        cairo_arc (cr, x, y, 1.5, 0, 2 * M_PI);
+                        cairo_fill (cr);
+
+                        if (n > 0) {
+                                cairo_set_source_rgba (cr, 0.2, 0.5, 0.2, 0.10);
+                                cairo_move_to (cr, prev_x, prev_y);
+                                cairo_line_to (cr, x, y);
+                                cairo_stroke (cr);
+                        }
+
+                        prev_x = x;
+                        prev_y = y;
+                }
+
+                /* draw write transfer rate graph */
+                cairo_set_source_rgb (cr, 1.0, 0.5, 0.5);
+                cairo_set_line_width (cr, 2.0);
+                for (n = 0; n < data->write_transfer_rate_samples->len; n++) {
+                        BenchmarkPoint *point = &g_array_index (data->write_transfer_rate_samples, BenchmarkPoint, n);
+
+                        x = gx + gw * point->offset / data->disk_size;
+                        y = gy + gh - gh * point->value / max_visible_speed;
+
+                        if (n == 0)
+                                cairo_move_to (cr, x, y);
+                        else
+                                cairo_line_to (cr, x, y);
+                }
+                cairo_stroke (cr);
+
+                /* draw read transfer rate graph */
+                cairo_set_source_rgb (cr, 0.5, 0.5, 1.0);
+                cairo_set_line_width (cr, 1.5);
+                for (n = 0; n < data->read_transfer_rate_samples->len; n++) {
+                        BenchmarkPoint *point = &g_array_index (data->read_transfer_rate_samples, BenchmarkPoint, n);
+
+                        x = gx + gw * point->offset / data->disk_size;
+                        y = gy + gh - gh * point->value / max_visible_speed;
+
+                        if (n == 0)
+                                cairo_move_to (cr, x, y);
+                        else
+                                cairo_line_to (cr, x, y);
+                }
+                cairo_stroke (cr);
+
+        } else {
+                /* TODO: draw some text saying we don't have any data */
+        }
+
+
+
+        cairo_destroy (cr);
+
+        g_strfreev (y_left_markers);
+        g_strfreev (y_right_markers);
+
+        /* propagate event further */
+        return FALSE;
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gdouble
+is_benchmarking (GduDriveBenchmarkDialog *dialog,
+                 gdouble                 *out_progress)
+{
+        GduDevice *d;
+        gboolean ret;
+
+        ret = FALSE;
+
+        d = gdu_dialog_get_device (GDU_DIALOG (dialog));
+        if (!gdu_device_job_in_progress (d))
+                goto out;
+
+        if (g_strcmp0 (gdu_device_job_get_id (d), "DriveBenchmark") != 0)
+                goto out;
+
+        if (out_progress != NULL)
+                *out_progress = gdu_device_job_get_percentage (d);
+
+        ret = TRUE;
+
+ out:
+        return ret;
+}
+
+static void
+update_dialog (GduDriveBenchmarkDialog *dialog)
+{
+        gdouble progress;
+
+        if (dialog->priv->deleted)
+                goto out;
+
+        /* benchmark data */
+        if (dialog->priv->benchmark_data == NULL) {
+                gdu_details_element_set_text (dialog->priv->read_min_element, "â??");
+                gdu_details_element_set_text (dialog->priv->read_max_element, "â??");
+                gdu_details_element_set_text (dialog->priv->read_avg_element, "â??");
+                gdu_details_element_set_text (dialog->priv->write_min_element, "â??");
+                gdu_details_element_set_text (dialog->priv->write_max_element, "â??");
+                gdu_details_element_set_text (dialog->priv->write_avg_element, "â??");
+                /* Translators: This is used for the "Last Benchmark" element when we don't have benchmark data */
+                gdu_details_element_set_text (dialog->priv->updated_element, _("Never"));
+                gdu_details_element_set_text (dialog->priv->access_avg_element, "â??");
+                gdu_details_element_set_time (dialog->priv->updated_element, 0);
+        } else {
+                gdouble read_min;
+                gdouble read_max;
+                gdouble read_avg;
+                gdouble write_min;
+                gdouble write_max;
+                gdouble write_avg;
+                gdouble access_avg;
+                gchar *s;
+
+                benchmark_get_max_min_avg (dialog->priv->benchmark_data->read_transfer_rate_samples,
+                                           &read_max,
+                                           &read_min,
+                                           &read_avg);
+                benchmark_get_max_min_avg (dialog->priv->benchmark_data->write_transfer_rate_samples,
+                                           &write_max,
+                                           &write_min,
+                                           &write_avg);
+
+                benchmark_get_max_min_avg (dialog->priv->benchmark_data->access_time_samples,
+                                           NULL,
+                                           NULL,
+                                           &access_avg);
+
+                s = gdu_util_get_speed_for_display (read_min);
+                gdu_details_element_set_text (dialog->priv->read_min_element, s);
+                g_free (s);
+                s = gdu_util_get_speed_for_display (read_max);
+                gdu_details_element_set_text (dialog->priv->read_max_element, s);
+                g_free (s);
+                s = gdu_util_get_speed_for_display (read_avg);
+                gdu_details_element_set_text (dialog->priv->read_avg_element, s);
+                g_free (s);
+                if (dialog->priv->benchmark_data->write_transfer_rate_samples->len > 0) {
+                        s = gdu_util_get_speed_for_display (write_min);
+                        gdu_details_element_set_text (dialog->priv->write_min_element, s);
+                        g_free (s);
+                        s = gdu_util_get_speed_for_display (write_max);
+                        gdu_details_element_set_text (dialog->priv->write_max_element, s);
+                        g_free (s);
+                        s = gdu_util_get_speed_for_display (write_avg);
+                        gdu_details_element_set_text (dialog->priv->write_avg_element, s);
+                        g_free (s);
+                } else {
+                        gdu_details_element_set_text (dialog->priv->write_min_element, "â??");
+                        gdu_details_element_set_text (dialog->priv->write_max_element, "â??");
+                        gdu_details_element_set_text (dialog->priv->write_avg_element, "â??");
+                }
+                s = g_strdup_printf ("%.1f ms", access_avg * 1000.0);
+                gdu_details_element_set_text (dialog->priv->access_avg_element, s);
+                g_free (s);
+
+                gdu_details_element_set_time (dialog->priv->updated_element,
+                                              dialog->priv->benchmark_data->time_collected);
+        }
+
+        if (is_benchmarking (dialog, &progress)) {
+                gdouble fraction;
+
+                fraction = progress / 100.0;
+                if (fraction < 0.0)
+                        fraction = 0.0;
+                if (fraction > 1.0)
+                        fraction = 1.0;
+                gdu_details_element_set_progress (dialog->priv->updated_element, fraction);
+                gdu_details_element_set_time (dialog->priv->updated_element, 0);
+                gdu_details_element_set_text (dialog->priv->updated_element, NULL);
+                gdu_details_element_set_action_text (dialog->priv->updated_element,
+                                                     /* Translators: Text used in the hyperlink in the status
+                                                      * table to cancel a self-test */
+                                                     _("Cancel"));
+                gdu_details_element_set_action_tooltip (dialog->priv->updated_element,
+                                                        /* Translators: Tooptip for the "Cancel" hyperlink */
+                                                        _("Cancels the currently running benchmark"));
+        } else {
+                gdu_details_element_set_progress (dialog->priv->updated_element, -1.0);
+                gdu_details_element_set_text (dialog->priv->updated_element, NULL);
+                gdu_details_element_set_action_text (dialog->priv->updated_element, NULL);
+                gdu_details_element_set_action_tooltip (dialog->priv->updated_element, NULL);
+        }
+
+ out:
+        ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_device_changed (GduDevice *device,
+                   gpointer   user_data)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (user_data);
+
+        if (!dialog->priv->deleted)
+                update_dialog (dialog);
+}
+
+static void
+on_device_job_changed (GduDevice *device,
+                       gpointer   user_data)
+{
+        GduDriveBenchmarkDialog *dialog = GDU_DRIVE_BENCHMARK_DIALOG (user_data);
+
+        if (!dialog->priv->deleted)
+                update_dialog (dialog);
+}
diff --git a/src/gdu-gtk/gdu-drive-benchmark-dialog.h b/src/gdu-gtk/gdu-drive-benchmark-dialog.h
new file mode 100644
index 0000000..27c721a
--- /dev/null
+++ b/src/gdu-gtk/gdu-drive-benchmark-dialog.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-drive-benchmark-dialog.h
+ *
+ * Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#if !defined (__GDU_GTK_INSIDE_GDU_GTK_H) && !defined (GDU_GTK_COMPILATION)
+#error "Only <gdu-gtk/gdu-gtk.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef __GDU_DRIVE_BENCHMARK_DIALOG_H
+#define __GDU_DRIVE_BENCHMARK_DIALOG_H
+
+#include <gdu-gtk/gdu-gtk-types.h>
+#include <gdu-gtk/gdu-dialog.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_DRIVE_BENCHMARK_DIALOG            gdu_drive_benchmark_dialog_get_type()
+#define GDU_DRIVE_BENCHMARK_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDU_TYPE_DRIVE_BENCHMARK_DIALOG, GduDriveBenchmarkDialog))
+#define GDU_DRIVE_BENCHMARK_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDU_TYPE_DRIVE_BENCHMARK_DIALOG, GduDriveBenchmarkDialogClass))
+#define GDU_IS_DRIVE_BENCHMARK_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDU_TYPE_DRIVE_BENCHMARK_DIALOG))
+#define GDU_IS_DRIVE_BENCHMARK_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDU_TYPE_DRIVE_BENCHMARK_DIALOG))
+#define GDU_DRIVE_BENCHMARK_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDU_TYPE_DRIVE_BENCHMARK_DIALOG, GduDriveBenchmarkDialogClass))
+
+typedef struct GduDriveBenchmarkDialogClass   GduDriveBenchmarkDialogClass;
+typedef struct GduDriveBenchmarkDialogPrivate GduDriveBenchmarkDialogPrivate;
+
+struct GduDriveBenchmarkDialog
+{
+        GduDialog parent;
+
+        /*< private >*/
+        GduDriveBenchmarkDialogPrivate *priv;
+};
+
+struct GduDriveBenchmarkDialogClass
+{
+        GduDialogClass parent_class;
+};
+
+GType       gdu_drive_benchmark_dialog_get_type (void) G_GNUC_CONST;
+GtkWidget*  gdu_drive_benchmark_dialog_new      (GtkWindow *parent,
+                                                 GduDrive  *drive);
+
+G_END_DECLS
+
+#endif /* __GDU_DRIVE_BENCHMARK_DIALOG_H */
diff --git a/src/gdu-gtk/gdu-gtk-types.h b/src/gdu-gtk/gdu-gtk-types.h
index bb4639d..33e2948 100644
--- a/src/gdu-gtk/gdu-gtk-types.h
+++ b/src/gdu-gtk/gdu-gtk-types.h
@@ -60,6 +60,7 @@ typedef struct GduEditFilesystemDialog      GduEditFilesystemDialog;
 typedef struct GduDiskSelectionWidget       GduDiskSelectionWidget;
 typedef struct GduAddComponentLinuxMdDialog GduAddComponentLinuxMdDialog;
 typedef struct GduEditLinuxMdDialog         GduEditLinuxMdDialog;
+typedef struct GduDriveBenchmarkDialog      GduDriveBenchmarkDialog;
 
 
 G_END_DECLS
diff --git a/src/gdu-gtk/gdu-gtk.h b/src/gdu-gtk/gdu-gtk.h
index 3326628..0a6b445 100644
--- a/src/gdu-gtk/gdu-gtk.h
+++ b/src/gdu-gtk/gdu-gtk.h
@@ -52,6 +52,7 @@
 #include <gdu-gtk/gdu-disk-selection-widget.h>
 #include <gdu-gtk/gdu-add-component-linux-md-dialog.h>
 #include <gdu-gtk/gdu-edit-linux-md-dialog.h>
+#include <gdu-gtk/gdu-drive-benchmark-dialog.h>
 #undef __GDU_GTK_INSIDE_GDU_GTK_H
 
 G_BEGIN_DECLS
diff --git a/src/gdu/gdu-callbacks.h b/src/gdu/gdu-callbacks.h
index 31bc81a..4217806 100644
--- a/src/gdu/gdu-callbacks.h
+++ b/src/gdu/gdu-callbacks.h
@@ -140,6 +140,13 @@ typedef void (*GduDeviceDrivePollMediaCompletedFunc) (GduDevice    *device,
                                                       GError       *error,
                                                       gpointer      user_data);
 
+typedef void (*GduDeviceDriveBenchmarkCompletedFunc) (GduDevice    *device,
+                                                      GPtrArray    *read_transfer_rate_results,
+                                                      GPtrArray    *write_transfer_rate_results,
+                                                      GPtrArray    *access_time_results,
+                                                      GError       *error,
+                                                      gpointer      user_data);
+
 /* ---------------------------------------------------------------------------------------------------- */
 /* GduPool */
 
diff --git a/src/gdu/gdu-device.c b/src/gdu/gdu-device.c
index 67d3698..d41220c 100644
--- a/src/gdu/gdu-device.c
+++ b/src/gdu/gdu-device.c
@@ -112,6 +112,7 @@ typedef struct
         char    *drive_model;
         char    *drive_revision;
         char    *drive_serial;
+        char    *drive_wwn;
         char    *drive_connection_interface;
         guint64  drive_connection_speed;
         char   **drive_media_compatibility;
@@ -120,6 +121,8 @@ typedef struct
         gboolean drive_can_detach;
         gboolean drive_can_spindown;
         gboolean drive_is_rotational;
+        guint    drive_rotation_rate;
+        char    *drive_write_cache;
 
         gboolean optical_disc_is_blank;
         gboolean optical_disc_is_appendable;
@@ -293,6 +296,8 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props)
                 props->drive_revision = g_strdup (g_value_get_string (value));
         else if (strcmp (key, "DriveSerial") == 0)
                 props->drive_serial = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "DriveWwn") == 0)
+                props->drive_wwn = g_strdup (g_value_get_string (value));
         else if (strcmp (key, "DriveConnectionInterface") == 0)
                 props->drive_connection_interface = g_strdup (g_value_get_string (value));
         else if (strcmp (key, "DriveConnectionSpeed") == 0)
@@ -309,6 +314,10 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props)
                 props->drive_can_spindown = g_value_get_boolean (value);
         else if (strcmp (key, "DriveIsRotational") == 0)
                 props->drive_is_rotational = g_value_get_boolean (value);
+        else if (strcmp (key, "DriveRotationRate") == 0)
+                props->drive_rotation_rate = g_value_get_uint (value);
+        else if (strcmp (key, "DriveWriteCache") == 0)
+                props->drive_write_cache = g_strdup (g_value_get_string (value));
 
         else if (strcmp (key, "OpticalDiscIsBlank") == 0)
                 props->optical_disc_is_blank = g_value_get_boolean (value);
@@ -424,6 +433,7 @@ device_properties_free (DeviceProperties *props)
         g_free (props->drive_vendor);
         g_free (props->drive_revision);
         g_free (props->drive_serial);
+        g_free (props->drive_wwn);
         g_free (props->drive_connection_interface);
         g_strfreev (props->drive_media_compatibility);
         g_free (props->drive_media);
@@ -1036,6 +1046,12 @@ gdu_device_drive_get_serial (GduDevice *device)
 }
 
 const char *
+gdu_device_drive_get_wwn (GduDevice *device)
+{
+        return device->priv->props->drive_wwn;
+}
+
+const char *
 gdu_device_drive_get_connection_interface (GduDevice *device)
 {
         return device->priv->props->drive_connection_interface;
@@ -1059,6 +1075,12 @@ gdu_device_drive_get_media (GduDevice *device)
         return device->priv->props->drive_media;
 }
 
+const char *
+gdu_device_drive_get_write_cache (GduDevice *device)
+{
+        return device->priv->props->drive_write_cache;
+}
+
 gboolean
 gdu_device_drive_get_is_media_ejectable (GduDevice *device)
 {
@@ -1090,6 +1112,12 @@ gdu_device_drive_get_is_rotational (GduDevice *device)
         return device->priv->props->drive_is_rotational;
 }
 
+guint
+gdu_device_drive_get_rotation_rate (GduDevice *device)
+{
+        return device->priv->props->drive_rotation_rate;
+}
+
 gboolean
 gdu_device_optical_disc_get_is_blank (GduDevice *device)
 {
@@ -2343,3 +2371,57 @@ gdu_device_op_drive_poll_media (GduDevice                        *device,
                                                                        op_poll_media_cb,
                                                                        data);
 }
+
+/* -------------------------------------------------------------------------------- */
+
+typedef struct {
+        GduDevice *device;
+        GduDeviceDriveBenchmarkCompletedFunc callback;
+        gpointer user_data;
+} DriveBenchmarkData;
+
+static void
+op_drive_benchmark_cb (DBusGProxy *proxy,
+                       GPtrArray *read_transfer_rate_results,
+                       GPtrArray *write_transfer_rate_results,
+                       GPtrArray *access_time_results,
+                       GError *error,
+                       gpointer user_data)
+{
+        DriveBenchmarkData *data = user_data;
+        _gdu_error_fixup (error);
+
+        if (data->callback != NULL) {
+                data->callback (data->device,
+                                read_transfer_rate_results,
+                                write_transfer_rate_results,
+                                access_time_results,
+                                error,
+                                data->user_data);
+        }
+        g_object_unref (data->device);
+        g_free (data);
+}
+
+void gdu_device_op_drive_benchmark (GduDevice                             *device,
+                                    gboolean                               do_write_benchmark,
+                                    const gchar* const *                   options,
+                                    GduDeviceDriveBenchmarkCompletedFunc   callback,
+                                    gpointer                               user_data)
+{
+        DriveBenchmarkData *data;
+
+        data = g_new0 (DriveBenchmarkData, 1);
+        data->device = g_object_ref (device);
+        data->callback = callback;
+        data->user_data = user_data;
+
+        org_freedesktop_DeviceKit_Disks_Device_drive_benchmark_async (device->priv->proxy,
+                                                                      do_write_benchmark,
+                                                                      (const gchar **) options,
+                                                                      op_drive_benchmark_cb,
+                                                                      data);
+}
+
+
+/* -------------------------------------------------------------------------------- */
diff --git a/src/gdu/gdu-device.h b/src/gdu/gdu-device.h
index a53190d..6d66902 100644
--- a/src/gdu/gdu-device.h
+++ b/src/gdu/gdu-device.h
@@ -132,6 +132,7 @@ const char *gdu_device_drive_get_vendor (GduDevice *device);
 const char *gdu_device_drive_get_model (GduDevice *device);
 const char *gdu_device_drive_get_revision (GduDevice *device);
 const char *gdu_device_drive_get_serial (GduDevice *device);
+const char *gdu_device_drive_get_wwn (GduDevice *device);
 const char *gdu_device_drive_get_connection_interface (GduDevice *device);
 guint64 gdu_device_drive_get_connection_speed (GduDevice *device);
 char **gdu_device_drive_get_media_compatibility (GduDevice *device);
@@ -141,6 +142,8 @@ gboolean gdu_device_drive_get_requires_eject (GduDevice *device);
 gboolean gdu_device_drive_get_can_detach (GduDevice *device);
 gboolean gdu_device_drive_get_can_spindown (GduDevice *device);
 gboolean gdu_device_drive_get_is_rotational (GduDevice *device);
+guint    gdu_device_drive_get_rotation_rate (GduDevice *device);
+const char *gdu_device_drive_get_write_cache (GduDevice *device);
 
 gboolean gdu_device_optical_disc_get_is_blank (GduDevice *device);
 gboolean gdu_device_optical_disc_get_is_appendable (GduDevice *device);
@@ -345,6 +348,14 @@ void gdu_device_op_drive_poll_media                 (GduDevice
                                                      GduDeviceDrivePollMediaCompletedFunc   callback,
                                                      gpointer                          user_data);
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+void gdu_device_op_drive_benchmark (GduDevice                             *device,
+                                    gboolean                               do_write_benchmark,
+                                    const gchar* const *                   options,
+                                    GduDeviceDriveBenchmarkCompletedFunc   callback,
+                                    gpointer                               user_data);
+
 G_END_DECLS
 
 #endif /* __GDU_DEVICE_H */
diff --git a/src/gdu/gdu-util.c b/src/gdu/gdu-util.c
index 6e70b2b..73de29e 100644
--- a/src/gdu/gdu-util.c
+++ b/src/gdu/gdu-util.c
@@ -403,7 +403,7 @@ gdu_get_job_description (const char *job_id)
                 s = g_strdup (_("Forcibly Locking LUKS device"));
         } else {
                 s = g_strdup_printf ("%s", job_id);
-                g_warning ("No friendly string for job with id '%s'", job_id);
+                g_debug ("No friendly string for job with id '%s'", job_id);
         }
         return s;
 }
@@ -837,13 +837,13 @@ gdu_util_get_speed_for_display (guint64 speed)
 
         if (speed < 1000 * 1000) {
                 displayed_speed = (double) speed / 1000.0;
-                str = g_strdup_printf (_("%.1f kbit/s"), displayed_speed);
+                str = g_strdup_printf (_("%.1f KB/s"), displayed_speed);
         } else if (speed < 1000 * 1000 * 1000) {
                 displayed_speed = (double) speed / 1000.0 / 1000.0;
-                str = g_strdup_printf (_("%.1f Mbit/s"), displayed_speed);
+                str = g_strdup_printf (_("%.1f MB/s"), displayed_speed);
         } else {
                 displayed_speed = (double) speed / 1000.0 / 1000.0 / 1000.0;
-                str = g_strdup_printf (_("%.1f Gbit/s"), displayed_speed);
+                str = g_strdup_printf (_("%.1f GB/s"), displayed_speed);
         }
 
         return str;
diff --git a/src/palimpsest/gdu-section-drive.c b/src/palimpsest/gdu-section-drive.c
index 10a875f..0587487 100644
--- a/src/palimpsest/gdu-section-drive.c
+++ b/src/palimpsest/gdu-section-drive.c
@@ -36,6 +36,8 @@ struct _GduSectionDrivePrivate
         GduDetailsElement *firmware_element;
         GduDetailsElement *serial_element;
         GduDetailsElement *wwn_element;
+        GduDetailsElement *write_cache_element;
+        GduDetailsElement *rotation_rate_element;
         GduDetailsElement *capacity_element;
         GduDetailsElement *connection_element;
         GduDetailsElement *partitioning_element;
@@ -46,6 +48,7 @@ struct _GduSectionDrivePrivate
         GduButtonElement *eject_button;
         GduButtonElement *detach_button;
         GduButtonElement *smart_button;
+        GduButtonElement *benchmark_button;
 };
 
 G_DEFINE_TYPE (GduSectionDrive, gdu_section_drive, GDU_TYPE_SECTION)
@@ -95,20 +98,26 @@ gdu_section_drive_update (GduSection *_section)
         const gchar *firmware;
         const gchar *serial;
         const gchar *wwn;
+        const gchar *write_cache;
+        guint rotation_rate;
+        gboolean is_rotational;
         GIcon *icon;
         gboolean show_cddvd_button;
         gboolean show_format_button;
         gboolean show_eject_button;
         gboolean show_detach_button;
         gboolean show_smart_button;
+        gboolean show_benchmark_button;
 
         show_cddvd_button = FALSE;
         show_format_button = FALSE;
         show_eject_button = FALSE;
         show_detach_button = FALSE;
         show_smart_button = FALSE;
+        show_benchmark_button = FALSE;
 
         d = NULL;
+        wwn = NULL;
         p = gdu_section_get_presentable (_section);
 
         d = gdu_presentable_get_device (p);
@@ -138,10 +147,41 @@ gdu_section_drive_update (GduSection *_section)
                 serial = "â??";
         gdu_details_element_set_text (section->priv->serial_element, serial);
 
-        wwn = NULL; /*TODO: gdu_device_drive_get_wwn (d)*/
-        if (wwn == NULL || strlen (wwn) == 0)
-                wwn = "â??";
-        gdu_details_element_set_text (section->priv->wwn_element, wwn);
+        wwn = gdu_device_drive_get_wwn (d);
+        if (wwn == NULL || strlen (wwn) == 0) {
+                gdu_details_element_set_text (section->priv->wwn_element, "â??");
+        } else {
+                s = g_strdup_printf ("0x%s", wwn);
+                gdu_details_element_set_text (section->priv->wwn_element, s);
+                g_free (s);
+        }
+
+        write_cache = gdu_device_drive_get_write_cache (d);
+        is_rotational = gdu_device_drive_get_is_rotational (d);
+        rotation_rate = gdu_device_drive_get_rotation_rate (d);
+
+        if (write_cache == NULL || strlen (write_cache) == 0) {
+                gdu_details_element_set_text (section->priv->write_cache_element, "â??");
+        } else if (g_strcmp0 (write_cache, "enabled") == 0) {
+                gdu_details_element_set_text (section->priv->write_cache_element, C_("Write Cache", "Enabled"));
+        } else if (g_strcmp0 (write_cache, "disabled") == 0) {
+                gdu_details_element_set_text (section->priv->write_cache_element, C_("Write Cache", "Disabled"));
+        } else {
+                gdu_details_element_set_text (section->priv->write_cache_element, write_cache);
+        }
+
+        if (is_rotational) {
+                if (rotation_rate > 0) {
+                        s = g_strdup_printf (_("%d RPM"), rotation_rate);
+                        gdu_details_element_set_text (section->priv->rotation_rate_element, s);
+                        g_free (s);
+                } else {
+                        gdu_details_element_set_text (section->priv->rotation_rate_element, "â??");
+                }
+        } else {
+                gdu_details_element_set_text (section->priv->rotation_rate_element,
+                                              C_("Rotation Rate", "Solid-State Disk"));
+        }
 
         if (gdu_device_is_partition_table (d)) {
                 const gchar *scheme;
@@ -200,6 +240,7 @@ gdu_section_drive_update (GduSection *_section)
                                                    TRUE);
                 gdu_details_element_set_text (section->priv->capacity_element, s);
                 g_free (s);
+                show_benchmark_button = TRUE;
         } else {
                 gdu_details_element_set_text (section->priv->capacity_element,
                                               _("No Media Detected"));
@@ -237,6 +278,7 @@ gdu_section_drive_update (GduSection *_section)
         gdu_button_element_set_visible (section->priv->eject_button, show_eject_button);
         gdu_button_element_set_visible (section->priv->detach_button, show_detach_button);
         gdu_button_element_set_visible (section->priv->smart_button, show_smart_button);
+        gdu_button_element_set_visible (section->priv->benchmark_button, show_benchmark_button);
 
 
         if (d != NULL)
@@ -319,6 +361,22 @@ on_smart_button_clicked (GduButtonElement *button_element,
         gtk_widget_destroy (dialog);
 }
 
+void
+gdu_section_drive_on_benchmark_button_clicked (GduButtonElement *button_element,
+                                               gpointer          user_data)
+{
+        GduSection *section = GDU_SECTION (user_data);
+        GtkWindow *toplevel;
+        GtkWidget *dialog;
+
+        toplevel = GTK_WINDOW (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section))));
+        dialog = gdu_drive_benchmark_dialog_new (toplevel,
+                                                 GDU_DRIVE (gdu_section_get_presentable (GDU_SECTION (section))));
+        gtk_widget_show_all (dialog);
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
@@ -532,10 +590,19 @@ gdu_section_drive_constructed (GObject *object)
         g_ptr_array_add (elements, element);
         section->priv->firmware_element = element;
 
+        /* Translators: if you translate "World Wide Name", please include the abbreviation "WWN" */
         element = gdu_details_element_new (_("World Wide Name:"), NULL, NULL);
         g_ptr_array_add (elements, element);
         section->priv->wwn_element = element;
 
+        element = gdu_details_element_new (_("Write Cache:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->write_cache_element = element;
+
+        element = gdu_details_element_new (_("Rotation Rate:"), NULL, NULL);
+        g_ptr_array_add (elements, element);
+        section->priv->rotation_rate_element = element;
+
         element = gdu_details_element_new (_("Capacity:"), NULL, NULL);
         g_ptr_array_add (elements, element);
         section->priv->capacity_element = element;
@@ -562,6 +629,8 @@ gdu_section_drive_constructed (GObject *object)
         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0);
         gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
 
+        /* -------------------------------------------------------------------------------- */
+
         elements = g_ptr_array_new_with_free_func (g_object_unref);
 
         button_element = gdu_button_element_new ("brasero",
@@ -614,6 +683,16 @@ gdu_section_drive_constructed (GObject *object)
         g_ptr_array_add (elements, button_element);
         section->priv->detach_button = button_element;
 
+        button_element = gdu_button_element_new ("gtk-execute", /* TODO: better icon */
+                                                 _("_Benchmark"),
+                                                 _("Measure drive performance"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (gdu_section_drive_on_benchmark_button_clicked),
+                          section);
+        g_ptr_array_add (elements, button_element);
+        section->priv->benchmark_button = button_element;
+
         table = gdu_button_table_new (2, elements);
         g_ptr_array_unref (elements);
         gtk_container_add (GTK_CONTAINER (align), table);
diff --git a/src/palimpsest/gdu-section-drive.h b/src/palimpsest/gdu-section-drive.h
index 9c21b09..6c728a0 100644
--- a/src/palimpsest/gdu-section-drive.h
+++ b/src/palimpsest/gdu-section-drive.h
@@ -55,10 +55,15 @@ GType            gdu_section_drive_get_type (void);
 GtkWidget       *gdu_section_drive_new      (GduShell       *shell,
                                              GduPresentable *presentable);
 
-/* exported for use in GduSectionLinuxMd and others - user_data must be a GduSection instance */
+/* these functions are exported for use in GduSectionLinuxMd and other sections - user_data must
+ * be a GduSection instance
+ */
 void
 gdu_section_drive_on_format_button_clicked (GduButtonElement *button_element,
                                             gpointer          user_data);
+void
+gdu_section_drive_on_benchmark_button_clicked (GduButtonElement *button_element,
+                                               gpointer          user_data);
 
 
 #endif /* GDU_SECTION_DRIVE_H */
diff --git a/src/palimpsest/gdu-section-linux-md-drive.c b/src/palimpsest/gdu-section-linux-md-drive.c
index ec5633e..c82bd53 100644
--- a/src/palimpsest/gdu-section-linux-md-drive.c
+++ b/src/palimpsest/gdu-section-linux-md-drive.c
@@ -50,6 +50,7 @@ struct _GduSectionLinuxMdDrivePrivate
         GduButtonElement *format_button;
         GduButtonElement *edit_components_button;
         GduButtonElement *check_button;
+        GduButtonElement *benchmark_button;
 };
 
 G_DEFINE_TYPE (GduSectionLinuxMdDrive, gdu_section_linux_md_drive, GDU_TYPE_SECTION)
@@ -296,6 +297,7 @@ gdu_section_linux_md_drive_update (GduSection *_section)
         gboolean show_format_button;
         gboolean show_edit_components_button;
         gboolean show_check_button;
+        gboolean show_benchmark_button;
         GList *slaves;
         GduDevice *slave;
         const gchar *level;
@@ -309,6 +311,7 @@ gdu_section_linux_md_drive_update (GduSection *_section)
         show_format_button = FALSE;
         show_edit_components_button = FALSE;
         show_check_button = FALSE;
+        show_benchmark_button = FALSE;
 
         p = gdu_section_get_presentable (_section);
         d = gdu_presentable_get_device (p);
@@ -368,6 +371,7 @@ gdu_section_linux_md_drive_update (GduSection *_section)
 
                 show_format_button = TRUE;
                 show_check_button = TRUE;
+                show_benchmark_button = TRUE;
                 show_edit_components_button = TRUE;
                 show_md_stop_button = TRUE;
         } else {
@@ -386,6 +390,7 @@ gdu_section_linux_md_drive_update (GduSection *_section)
         gdu_button_element_set_visible (section->priv->format_button, show_format_button);
         gdu_button_element_set_visible (section->priv->edit_components_button, show_edit_components_button);
         gdu_button_element_set_visible (section->priv->check_button, show_check_button);
+        gdu_button_element_set_visible (section->priv->benchmark_button, show_benchmark_button);
 
         if (d != NULL)
                 g_object_unref (d);
@@ -1178,6 +1183,16 @@ gdu_section_linux_md_drive_constructed (GObject *object)
         g_ptr_array_add (elements, button_element);
         section->priv->edit_components_button = button_element;
 
+        button_element = gdu_button_element_new ("gtk-execute", /* TODO: better icon */
+                                                 _("_Benchmark"),
+                                                 _("Measure RAID array performance"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (gdu_section_drive_on_benchmark_button_clicked),
+                          section);
+        g_ptr_array_add (elements, button_element);
+        section->priv->benchmark_button = button_element;
+
         table = gdu_button_table_new (2, elements);
         g_ptr_array_unref (elements);
         gtk_container_add (GTK_CONTAINER (align), table);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]