hal: Branch 'master'

David Zeuthen david at kemper.freedesktop.org
Sun May 6 18:21:08 PDT 2007


 NEWS                             |    2 
 doc/spec/hal-spec-properties.xml |  115 +++++++++
 hald/linux/blockdev.c            |  487 ++++++++++++++++++++++++++++++++++++++-
 hald/linux/blockdev.h            |    2 
 hald/linux/coldplug.c            |   10 
 hald/linux/hotplug.c             |    5 
 hald/linux/osspec.c              |   46 +++
 hald/linux/osspec_linux.h        |    2 
 8 files changed, 650 insertions(+), 19 deletions(-)

New commits:
diff-tree c53f3e46e53204bb1d1910b7fbbd81edaf61c29b (from a5861634bc0961d379c16fdafb4e1268ecbbd37b)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sun May 6 21:14:08 2007 -0400

    recognize Linux MD aka Software RAID devices
    
    Since pictures says more than a thousand words...
    
     http://people.freedesktop.org/~david/hal-md-normal.png
     http://people.freedesktop.org/~david/hal-md-degraded.png
     http://people.freedesktop.org/~david/hal-md-recover.png
    
    This patch only adds detection by exploiting the fact that in Linux
    2.6.19 and later /proc/mdstat is pollable. Later on we'll add D-Bus
    interfaces to add methods for assembly, teardown of RAID arrays as
    well as adding hot spares.
    
    I wonder if it's worth abstracting Software RAID so FreeBSD and
    Solaris can implement this too? It's probably too
    different.. Thoughts?

diff --git a/NEWS b/NEWS
index 8815c36..801708d 100644
--- a/NEWS
+++ b/NEWS
@@ -4,7 +4,7 @@ HAL 0.5.10 ""
 
 Requirements for HAL 0.5.10 "":
 
- - Linux kernel     >= 2.6.17
+ - Linux kernel     >= 2.6.19
  - util-linux       >= 2.12r1    (--enable-umount-helper requires patch from RH #188193)
  - bash             >= 2.0
  - udev             >= 089
diff --git a/doc/spec/hal-spec-properties.xml b/doc/spec/hal-spec-properties.xml
index e527e43..64176c1 100644
--- a/doc/spec/hal-spec-properties.xml
+++ b/doc/spec/hal-spec-properties.xml
@@ -3624,6 +3624,12 @@ org.freedesktop.Hal.Device.Volume.method
             </row>
             <row>
               <entry></entry>
+              <entry>linux_raid</entry>
+              <entry></entry>
+              <entry>Linux MD (multi disk) RAID device</entry>
+            </row>
+            <row>
+              <entry></entry>
               <entry></entry>
               <entry></entry>
               <entry></entry>
@@ -4107,6 +4113,115 @@ org.freedesktop.Hal.Device.Volume.method
         </tgroup>
       </informaltable>
     </sect2>
+    <sect2 id="device-properties-storage-linux-raid">
+      <title>
+        storage.linux_raid namespace
+      </title>
+      <para>
+        This namespace is used to describe logical Software RAID
+        devices under Linux using the <literal>md</literal> driver. By
+        and large, all the same properties under
+        the <literal>storage</literal> name space applies except
+        that <literal>storage.serial</literal> is set to the UUID of
+        the RAID set, <literal>storage.firmware_version</literal> is
+        set to the version of the <literal>md</literal> driver and the
+        value of <literal>storage.hotpluggable</literal> is taken from
+        the enclosing drive of the first RAID component
+        encountered. In addition, the following properties are
+        available.
+      </para>
+      <informaltable>
+        <tgroup cols="2">
+          <thead>
+            <row>
+              <entry>Key (type)</entry>
+              <entry>Values</entry>
+              <entry>Mandatory</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.level</literal> (string)
+              </entry>
+              <entry></entry>
+              <entry>Yes</entry>
+              <entry>the RAID level of the device as reported by the kernel (linear, raid0, raid1, raid4, raid5, raid6, raid10)</entry>
+            </row>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.sysfs_path</literal> (string)
+              </entry>
+              <entry></entry>
+              <entry>Yes</entry>
+              <entry>sysfs path of device, e.g. /sys/block/md0</entry>
+            </row>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.num_components</literal> (int)
+              </entry>
+              <entry></entry>
+              <entry>Yes</entry>
+              <entry>Number of components in the RAID array</entry>
+            </row>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.num_components_active</literal> (int)
+              </entry>
+              <entry></entry>
+              <entry>Yes</entry>
+              <entry>
+                Number of active components in the RAID array. If less
+                than <literal>storage.linux_raid.num_components</literal>
+                it means that the RAID array is running in degraded
+                mode.
+              </entry>
+            </row>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.components</literal> (strlist)
+              </entry>
+              <entry></entry>
+              <entry>Yes</entry>
+              <entry>UDI's of the volumes constituting the array.</entry>
+            </row>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.is_syncing</literal> (bool)
+              </entry>
+              <entry></entry>
+              <entry>Yes</entry>
+              <entry>TRUE if, and only if, the array is currently syncing</entry>
+            </row>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.sync.action</literal> (string)
+              </entry>
+              <entry></entry>
+              <entry>only if <literal>.is_syncing</literal> is TRUE</entry>
+              <entry>The syncing mechanism as reported by the kernel (idle, resync, check, repair, recover)</entry>
+            </row>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.sync.progress</literal> (double)
+              </entry>
+              <entry></entry>
+              <entry>only if <literal>.is_syncing</literal> is TRUE</entry>
+              <entry>Number between 0 and 1 representing progress of the sync operation. This is updated regulary when syncing is happening.</entry>
+            </row>
+            <row>
+              <entry>
+                <literal>storage.linux_raid.sync.speed</literal> (uint64)
+              </entry>
+              <entry></entry>
+              <entry>only if <literal>.is_syncing</literal> is TRUE</entry>
+              <entry>Speed of the sync operation, in kB/s. This is updated regulary when syncing is happening.</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </informaltable>
+    </sect2>
     <sect2 id="device-properties-net">
       <title>
         net namespace
diff --git a/hald/linux/blockdev.c b/hald/linux/blockdev.c
index f0c6eba..b9ad9f1 100644
--- a/hald/linux/blockdev.c
+++ b/hald/linux/blockdev.c
@@ -635,6 +635,198 @@ out:
 	return f;
 }
 
+static gboolean
+refresh_md_state (HalDevice *d);
+
+static gboolean
+md_check_sync_timeout (gpointer user_data)
+{
+        HalDevice *d;
+        char *sysfs_path = (char *) user_data;
+
+        HAL_INFO (("In md_check_sync_timeout for sysfs path %s", sysfs_path));
+
+        d = hal_device_store_match_key_value_string (hald_get_gdl (), 
+                                                     "storage.linux_raid.sysfs_path", 
+                                                     sysfs_path);
+        if (d == NULL)
+                d = hal_device_store_match_key_value_string (hald_get_tdl (), 
+                                                             "storage.linux_raid.sysfs_path", 
+                                                             sysfs_path);
+        if (d == NULL) {
+                HAL_WARNING (("Cannot find md device with sysfs path '%s'", sysfs_path));
+                goto out;
+        }
+
+        refresh_md_state (d);
+        
+out:
+        g_free (sysfs_path);
+        return FALSE;
+}
+
+static gboolean
+refresh_md_state (HalDevice *d)
+{
+        int n;
+        char *sync_action;
+        int num_components;
+        gboolean ret;
+        const char *sysfs_path;
+
+        ret = FALSE;
+
+        sysfs_path = hal_device_property_get_string (d, "storage.linux_raid.sysfs_path");
+        if (sysfs_path == NULL) {
+                HAL_WARNING (("Cannot get sysfs_path for udi %s", hal_device_get_udi (d)));
+                goto error;
+        }
+
+        HAL_INFO (("In refresh_md_state() for '%s'", sysfs_path));
+
+        sync_action = hal_util_get_string_from_file (sysfs_path, "md/sync_action");
+        if (sync_action == NULL) {
+                HAL_WARNING (("Cannot get sync_action for %s", sysfs_path));
+                goto error;
+        }
+        if (strcmp (sync_action, "idle") == 0) {
+                hal_device_property_set_bool (d, "storage.linux_raid.is_syncing", FALSE);
+		hal_device_property_remove (d, "storage.linux_raid.sync.action");
+		hal_device_property_remove (d, "storage.linux_raid.sync.speed");
+		hal_device_property_remove (d, "storage.linux_raid.sync.progress");
+        } else {
+                int speed;
+                char *str_completed;
+
+                hal_device_property_set_bool (d, "storage.linux_raid.is_syncing", TRUE);
+
+                hal_device_property_set_string (d, "storage.linux_raid.sync.action", sync_action);
+
+		if (!hal_util_get_int_from_file (sysfs_path, "md/sync_speed", &speed, 10)) {
+                        HAL_WARNING (("Cannot get sync_speed for %s", sysfs_path));
+                } else {
+                        hal_device_property_set_uint64 (d, "storage.linux_raid.sync.speed", speed);
+                }
+
+
+                if ((str_completed = hal_util_get_string_from_file (sysfs_path, "md/sync_completed")) == NULL) {
+                        HAL_WARNING (("Cannot get sync_completed for %s", sysfs_path));
+                } else {
+                        dbus_uint64_t sync_pos, sync_total;
+
+                        if (sscanf (str_completed, "%ld / %ld", &sync_pos, &sync_total) != 2) {
+                                HAL_WARNING (("Malformed sync_completed '%s'", str_completed));
+                        } else {
+                                double sync_progress;
+                                sync_progress = ((double) sync_pos) / ((double) sync_total);
+                                hal_device_property_set_double (d, "storage.linux_raid.sync.progress", sync_progress);
+                        }
+                }
+
+                /* check again in two seconds */
+                g_timeout_add (2000, md_check_sync_timeout, g_strdup (sysfs_path));
+        }
+        
+        if (!hal_util_get_int_from_file (sysfs_path, "md/raid_disks", &num_components, 0)) {
+                HAL_WARNING (("Cannot get number of RAID components"));
+                goto error;
+        }
+        hal_device_property_set_int (d, "storage.linux_raid.num_components", num_components);
+        
+        /* add all components */
+        for (n = 0; n < num_components; n++) {
+                char *s;
+                char *link;
+                char *target;
+                HalDevice *slave_volume;
+                const char *slave_volume_stordev_udi;
+                HalDevice *slave_volume_stordev;
+
+                s = g_strdup_printf ("%s/md/rd%d", sysfs_path, n);
+                if (!g_file_test (s, G_FILE_TEST_IS_SYMLINK)) {
+                        g_free (s);
+                        break;
+                }
+                g_free (s);
+                
+                link = g_strdup_printf ("%s/md/rd%d/block", sysfs_path, n);
+                target = resolve_symlink (link);
+                if (target == NULL) {
+                        HAL_WARNING (("Cannot resolve %s", link));
+                        g_free (link);
+                        goto error;
+                }
+                HAL_INFO (("link->target: '%s' -> '%s'", link, target));
+                
+                slave_volume = hal_device_store_match_key_value_string (hald_get_gdl (), "linux.sysfs_path", target);
+                if (slave_volume == NULL) {
+                        HAL_WARNING (("No volume for sysfs path %s", target));
+                        g_free (target);
+                        g_free (link);
+                        goto error;
+                }
+                
+                
+                slave_volume_stordev_udi = hal_device_property_get_string (slave_volume, "block.storage_device");
+                if (slave_volume_stordev_udi == NULL) {
+                        HAL_WARNING (("No storage device for slave"));
+                        g_free (target);
+                        g_free (link);
+                        goto error;
+                }
+                slave_volume_stordev = hal_device_store_find (hald_get_gdl (), slave_volume_stordev_udi);
+                if (slave_volume_stordev == NULL) {
+                        HAL_WARNING (("No storage device for slave"));
+                        g_free (target);
+                        g_free (link);
+                        goto error;
+                }
+                
+                
+                hal_device_property_strlist_add (d, "storage.linux_raid.components", hal_device_get_udi (slave_volume));
+                
+                /* derive 
+                 *
+                 * - hotpluggability (is that a word?) 
+                 * - array uuid
+                 *
+                 * Hmm.. every raid member (PV) get the array UUID. That's
+                 * probably.. wrong. TODO: check with Kay.
+                 *
+                 * from component 0.
+                 */
+                if (n == 0) {
+                        const char *uuid;
+
+                        hal_device_property_set_bool (
+                                d, "storage.hotpluggable",
+                                hal_device_property_get_bool (slave_volume_stordev, "storage.hotpluggable"));
+
+                        
+                        uuid = hal_device_property_get_string (
+                                slave_volume, "volume.uuid");
+                        if (uuid != NULL) {
+                                hal_device_property_set_string (
+                                        d, "storage.serial", uuid);
+                        }
+                }
+                
+                g_free (target);
+                g_free (link);
+                
+        } /* for all components */
+
+        hal_device_property_set_int (d, "storage.linux_raid.num_components_active", n);
+        
+        /* TODO: add more state here */
+
+        ret = TRUE;
+
+error:
+        return ret;
+}
+
+
 void
 hotplug_event_begin_add_blockdev (const gchar *sysfs_path, const gchar *device_file, gboolean is_partition,
 				  HalDevice *parent, void *end_token)
@@ -647,8 +839,12 @@ hotplug_event_begin_add_blockdev (const 
 	char *sysfs_path_real = NULL;
 	int floppy_num;
 	gboolean is_device_mapper;
+        gboolean is_md_device;
+        int md_number;
 
 	is_device_mapper = FALSE;
+        is_fakevolume = FALSE;
+        is_md_device = FALSE;
 
 	HAL_INFO (("block_add: sysfs_path=%s dev=%s is_part=%d, parent=0x%08x", 
 		   sysfs_path, device_file, is_partition, parent));
@@ -659,11 +855,18 @@ hotplug_event_begin_add_blockdev (const 
 	}
 
 	if (strcmp (hal_util_get_last_element (sysfs_path), "fakevolume") == 0) {
+		HAL_INFO (("Handling %s as fakevolume - sysfs_path_real=%s", device_file, sysfs_path_real));
 		is_fakevolume = TRUE;
 		sysfs_path_real = hal_util_get_parent_path (sysfs_path);
-		HAL_INFO (("Handling %s as fakevolume - sysfs_path_real=%s", device_file, sysfs_path_real));
+        } else if (sscanf (hal_util_get_last_element (sysfs_path), "md%d", &md_number) == 1) {
+		HAL_INFO (("Handling %s as MD device", device_file));
+                is_md_device = TRUE;
+		sysfs_path_real = g_strdup (sysfs_path);
+                /* set parent to root computer device object */
+                parent = hal_device_store_find (hald_get_gdl (), "/org/freedesktop/Hal/devices/computer");
+                if (parent == NULL)
+                        d = hal_device_store_find (hald_get_tdl (), "/org/freedesktop/Hal/devices/computer");
 	} else {
-		is_fakevolume = FALSE;
 		sysfs_path_real = g_strdup (sysfs_path);
 	}
 	
@@ -679,13 +882,13 @@ hotplug_event_begin_add_blockdev (const 
 	d = hal_device_new ();
 
 	/* OK, no parent... it might a device-mapper device => check slaves/ subdir in sysfs */
-	if (parent == NULL && !is_partition && !is_fakevolume) {
+	if (parent == NULL && !is_partition && !is_fakevolume && !hotplug_event->reposted) {
 		DIR * dir;
 		struct dirent *dp;
 		char path[HAL_PATH_MAX];
 
 		g_snprintf (path, HAL_PATH_MAX, "%s/slaves", sysfs_path);
-		HAL_INFO (("Looking in %s", path));
+		HAL_INFO (("Looking in %s for Device Mapper", path));
 
 		if ((dir = opendir (path)) == NULL) {
 			HAL_WARNING (("Unable to open %s: %s", path, strerror(errno)));
@@ -734,7 +937,7 @@ hotplug_event_begin_add_blockdev (const 
 			closedir(dir);
 			HAL_INFO (("Done looking in %s", path));
 		}
-		
+
 	}
 
 	if (parent == NULL) {
@@ -865,6 +1068,53 @@ hotplug_event_begin_add_blockdev (const 
 				}
 			}
 
+                        if (strcmp (udi_it, "/org/freedesktop/Hal/devices/computer") == 0) {
+					physdev = d_it;
+					physdev_udi = udi_it;
+                                        if (is_md_device) {
+                                                char *level;
+                                                char *model_name;
+
+                                                hal_device_property_set_string (d, "storage.bus", "linux_raid");
+
+                                                level = hal_util_get_string_from_file (sysfs_path_real, "md/level");
+                                                if (level == NULL)
+                                                        goto error;
+                                                hal_device_property_set_string (d, "storage.linux_raid.level", level);
+
+                                                hal_device_property_set_string (d, "storage.linux_raid.sysfs_path", sysfs_path_real);
+
+                                                hal_device_property_set_string (d, "storage.vendor", "Linux");
+                                                if (strcmp (level, "linear") == 0) {
+                                                        model_name = g_strdup ("Software RAID (Linear)");
+                                                } else if (strcmp (level, "raid0") == 0) {
+                                                        model_name = g_strdup ("Software RAID-0 (Stripe)");
+                                                } else if (strcmp (level, "raid1") == 0) {
+                                                        model_name = g_strdup ("Software RAID-1 (Mirror)");
+                                                } else if (strcmp (level, "raid5") == 0) {
+                                                        model_name = g_strdup ("Software RAID-5");
+                                                } else {
+                                                        model_name = g_strdup_printf ("Software RAID (%s)", level);
+                                                }
+                                                hal_device_property_set_string (d, "storage.model", model_name);
+                                                g_free (model_name);
+
+                                                hal_util_set_string_from_file (
+                                                        d, "storage.firmware_version", 
+                                                        sysfs_path_real, "md/metadata_version");
+                                                
+                                                hal_device_add_capability (d, "storage.linux_raid");
+
+                                                if (!refresh_md_state (d))
+                                                        goto error;
+
+                                                is_hotpluggable = hal_device_property_get_bool (
+                                                        d, "storage.hotpluggable");
+
+                                        }
+                                        break;
+                        }
+
 			/* Check info.subsystem */
 			if ((bus = hal_device_property_get_string (d_it, "info.subsystem")) != NULL) {
 				if (strcmp (bus, "scsi") == 0) {
@@ -927,9 +1177,9 @@ hotplug_event_begin_add_blockdev (const 
 		hal_device_property_set_string (d, "storage.originating_device", physdev_udi);
 		hal_device_property_set_string (d, "storage.physical_device", physdev_udi);
 
-		if (!hal_util_get_int_from_file (sysfs_path, "removable", (gint *) &is_removable, 10)) {
+		if (!hal_util_get_int_from_file (sysfs_path_real, "removable", (gint *) &is_removable, 10)) {
 			HAL_WARNING (("Cannot get 'removable' file"));
-			goto error;
+                        is_removable = FALSE;
 		}
 
 		hal_device_property_set_bool (d, "storage.removable.media_available", FALSE);
@@ -937,7 +1187,7 @@ hotplug_event_begin_add_blockdev (const 
 		/* set storage.size only if we have fixed media */
 		if (!is_removable) {
 			guint64 num_blocks;
-			if (hal_util_get_uint64_from_file (sysfs_path, "size", &num_blocks, 0)) {
+			if (hal_util_get_uint64_from_file (sysfs_path_real, "size", &num_blocks, 0)) {
 				/* TODO: sane to assume this is always 512 for non-removable? 
 				 * I think genhd.c guarantees this... */
 				hal_device_property_set_uint64 (d, "storage.size", num_blocks * 512);
@@ -972,7 +1222,7 @@ hotplug_event_begin_add_blockdev (const 
 			 *
 			 * "disk", "cdrom", "tape", "floppy", "UNKNOWN"
 			 */
-			snprintf (buf, sizeof (buf), "%s/ide/%s", get_hal_proc_path (), hal_util_get_last_element (sysfs_path));
+			snprintf (buf, sizeof (buf), "%s/ide/%s", get_hal_proc_path (), hal_util_get_last_element (sysfs_path_real));
 			if ((media = hal_util_get_string_from_file (buf, "media")) != NULL) {
 				if (strcmp (media, "disk") == 0 ||
 				    strcmp (media, "cdrom") == 0 ||
@@ -1161,10 +1411,12 @@ error:
 	if (d != NULL)
 		g_object_unref (d);
 out:
-	hotplug_event_end (end_token);
+        hotplug_event_end (end_token);
 	g_free (sysfs_path_real);
+        return;
 }
 
+
 static void
 force_unmount_cb (HalDevice *d, guint32 exit_type, 
 		  gint return_code, gchar **error,
@@ -1412,3 +1664,218 @@ blockdev_generate_remove_hotplug_event (
 
 	return hotplug_event;
 }
+
+static GSList *md_devs = NULL;
+
+static char *
+udev_get_device_file_for_sysfs_path (const char *sysfs_path)
+{
+        char *ret;
+        char *u_stdout;
+        int u_exit_status;
+        const char *argv[] = {"/usr/bin/udevinfo", "--root", "--query", "name", "--path", NULL, NULL};
+        GError *g_error;
+
+        ret = NULL;
+        argv[5] = sysfs_path;
+
+        g_error = NULL;
+
+        if (!g_spawn_sync("/", 
+                          (char **) argv, 
+                          NULL,           /* envp */
+                          0,              /* flags */
+                          NULL,           /* child_setup */
+                          NULL,           /* user_data */
+                          &u_stdout,
+                          NULL,           /* stderr */
+                          &u_exit_status,
+                          &g_error)) {
+                HAL_ERROR (("Error spawning udevinfo: %s", g_error->message));
+                g_error_free (g_error);
+                goto out;
+        }
+
+        if (u_exit_status != 0) {
+                HAL_ERROR (("udevinfo returned exit code %d", u_exit_status));
+                g_free (u_stdout);
+                goto out;
+        }
+
+        ret = u_stdout;
+        g_strchomp (ret);
+        HAL_INFO (("Got '%s'", ret));
+
+out:
+        return ret;
+}
+
+
+void 
+blockdev_process_mdstat (void)
+{
+	HotplugEvent *hotplug_event;
+        GIOChannel *channel;
+        GSList *read_md_devs;
+        GSList *i;
+        GSList *j;
+        GSList *k;
+
+        channel = get_mdstat_channel ();
+        if (channel == NULL)
+                goto error;
+
+        if (g_io_channel_seek (channel, 0, G_SEEK_SET) != G_IO_ERROR_NONE) {
+                HAL_ERROR (("Cannot seek in /proc/mdstat"));
+                goto error;
+        }
+
+        read_md_devs = NULL;
+        while (TRUE) {
+                int num;
+                char *line;
+
+                if (g_io_channel_read_line (channel, &line, NULL, NULL, NULL) != G_IO_STATUS_NORMAL)
+                        break;
+
+                if (sscanf (line, "md%d : ", &num) == 1) {
+                        char *sysfs_path;
+                        sysfs_path = g_strdup_printf ("%s/block/md%d", get_hal_sysfs_path (), num);
+                        read_md_devs = g_slist_prepend (read_md_devs, sysfs_path);
+                }
+
+                g_free (line);
+        }
+
+        /* now compute the delta */
+
+        /* add devices */
+        for (i = read_md_devs; i != NULL; i = i->next) {
+                gboolean should_add = TRUE;
+
+                for (j = md_devs; j != NULL && should_add; j = j->next) {
+                        if (strcmp (i->data, j->data) == 0) {
+                                should_add = FALSE;
+                        }
+                }
+
+                if (should_add) {
+                        char *sysfs_path = i->data;
+                        char *device_file;
+                        int num_tries;
+
+                        num_tries = 0;
+                retry_add:
+                        device_file = udev_get_device_file_for_sysfs_path (sysfs_path);
+                        if (device_file == NULL) {
+                                if (num_tries <= 6) {
+                                        int num_ms;
+                                        num_ms = 10 * (1<<num_tries);
+                                        HAL_INFO (("spinning %d ms waiting for device file for sysfs path %s", 
+                                                   num_ms, sysfs_path));
+                                        usleep (1000 * num_ms);
+                                        num_tries++;
+                                        goto retry_add;
+                                } else {
+                                        HAL_ERROR (("Cannot get device file for sysfs path %s", sysfs_path));
+                                }
+                        } else {
+                                HAL_INFO (("Adding md device at '%s' ('%s')", sysfs_path, device_file));
+
+                                hotplug_event = g_new0 (HotplugEvent, 1);
+                                hotplug_event->action = HOTPLUG_ACTION_ADD;
+                                hotplug_event->type = HOTPLUG_EVENT_SYSFS_BLOCK;
+                                g_strlcpy (hotplug_event->sysfs.subsystem, "block", sizeof (hotplug_event->sysfs.subsystem));
+                                g_strlcpy (hotplug_event->sysfs.sysfs_path, sysfs_path, sizeof (hotplug_event->sysfs.sysfs_path));
+                                g_strlcpy (hotplug_event->sysfs.device_file, device_file, sizeof (hotplug_event->sysfs.device_file));
+                                hotplug_event->sysfs.net_ifindex = -1;
+                                hotplug_event_enqueue (hotplug_event);
+                                
+                                md_devs = g_slist_prepend (md_devs, g_strdup (sysfs_path));
+
+                                g_free (device_file);
+                        }
+
+                }
+        }
+
+        /* remove devices */
+        for (i = md_devs; i != NULL; i = k) {
+                gboolean should_remove = TRUE;
+
+                k = i->next;
+
+                for (j = read_md_devs; j != NULL && should_remove; j = j->next) {
+                        if (strcmp (i->data, j->data) == 0) {
+                                should_remove = FALSE;
+                        }
+                }
+
+                if (should_remove) {
+                        char *sysfs_path = i->data;
+                        char *device_file;
+                        int num_tries;
+                        
+                        num_tries = 0;
+                retry_rem:
+                        device_file = udev_get_device_file_for_sysfs_path (sysfs_path);
+                        if (device_file == NULL) {
+                                if (num_tries <= 6) {
+                                        int num_ms;
+                                        num_ms = 10 * (1<<num_tries);
+                                        HAL_INFO (("spinning %d ms waiting for device file for sysfs path %s", 
+                                                   num_ms, sysfs_path));
+                                        usleep (1000 * num_ms);
+                                        num_tries++;
+                                        goto retry_rem;
+                                } else {
+                                        HAL_ERROR (("Cannot get device file for sysfs path %s", sysfs_path));
+                                }
+                        } else {
+                                
+                                HAL_INFO (("Removing md device at '%s' ('%s')", sysfs_path, device_file));
+
+                                hotplug_event = g_new0 (HotplugEvent, 1);
+                                hotplug_event->action = HOTPLUG_ACTION_REMOVE;
+                                hotplug_event->type = HOTPLUG_EVENT_SYSFS_BLOCK;
+                                g_strlcpy (hotplug_event->sysfs.subsystem, "block", sizeof (hotplug_event->sysfs.subsystem));
+                                g_strlcpy (hotplug_event->sysfs.sysfs_path, sysfs_path, sizeof (hotplug_event->sysfs.sysfs_path));
+                                g_strlcpy (hotplug_event->sysfs.device_file, device_file, sizeof (hotplug_event->sysfs.device_file));
+                                hotplug_event->sysfs.net_ifindex = -1;
+                                hotplug_event_enqueue (hotplug_event);
+                                
+                                md_devs = g_slist_remove_link (md_devs, i);
+                                g_free (i->data);
+                                g_slist_free (i);
+
+                                g_free (device_file);
+                        }
+
+                }
+        }
+
+        g_slist_foreach (read_md_devs, (GFunc) g_free, NULL);
+        g_slist_free (read_md_devs);
+
+        /* finally, refresh all md devices */
+        for (i = md_devs; i != NULL; i = i->next) {
+                char *sysfs_path = i->data;
+                HalDevice *d;
+
+                d = hal_device_store_match_key_value_string (hald_get_gdl (), 
+                                                             "storage.linux_raid.sysfs_path", 
+                                                             sysfs_path);
+                if (d == NULL)
+                        d = hal_device_store_match_key_value_string (hald_get_tdl (), 
+                                                                     "storage.linux_raid.sysfs_path", 
+                                                                     sysfs_path);
+                if (d != NULL)
+                        refresh_md_state (d);
+        }
+        
+
+        hotplug_event_process_queue ();
+
+error:
+        ;
+}
diff --git a/hald/linux/blockdev.h b/hald/linux/blockdev.h
index 9095d8c..0d28298 100644
--- a/hald/linux/blockdev.h
+++ b/hald/linux/blockdev.h
@@ -40,4 +40,6 @@ HotplugEvent *blockdev_generate_remove_h
 
 void blockdev_refresh_mount_state (HalDevice *d);
 
+void blockdev_process_mdstat (void);
+
 #endif /* BLOCKDEV_H */
diff --git a/hald/linux/coldplug.c b/hald/linux/coldplug.c
index d5771b6..934d4a4 100644
--- a/hald/linux/coldplug.c
+++ b/hald/linux/coldplug.c
@@ -42,6 +42,7 @@
 #include "osspec_linux.h"
 #include "hotplug.h"
 #include "coldplug.h"
+#include "blockdev.h"
 
 #define DMPREFIX "dm-"
 
@@ -428,6 +429,12 @@ static void scan_block(void)
 			if (dent->d_name[0] == '.')
 				continue;
 
+                        /* md devices are handled via looking at /proc/mdstat */
+                        if (g_str_has_prefix (dent->d_name, "md")) {
+                                HAL_INFO (("skipping md event for %s", dent->d_name));
+                                continue;
+                        }
+
 			g_strlcpy(dirname, base, sizeof(dirname));
 			g_strlcat(dirname, "/", sizeof(dirname));
 			g_strlcat(dirname, dent->d_name, sizeof(dirname));
@@ -574,6 +581,9 @@ coldplug_synthesize_events (void)
 			device_list = g_slist_sort (device_list, _device_order);
 			queue_events ();
 		}
+
+                /* add events from reading /proc/mdstat */
+                blockdev_process_mdstat ();
 	}
 
 	g_hash_table_destroy (sysfs_to_udev_map);
diff --git a/hald/linux/hotplug.c b/hald/linux/hotplug.c
index 96c8540..e836fd9 100644
--- a/hald/linux/hotplug.c
+++ b/hald/linux/hotplug.c
@@ -184,10 +184,11 @@ hotplug_event_begin_sysfs (HotplugEvent 
 			 * from within HAL for partitions on the main block device
 			 */
 			if ((strstr (hotplug_event->sysfs.sysfs_path, "/fakevolume") != NULL) ||
-			    hal_util_get_int_from_file (hotplug_event->sysfs.sysfs_path, "range", &range, 0))
+                            hal_util_get_int_from_file (hotplug_event->sysfs.sysfs_path, "range", &range, 0)) {
 				is_partition = FALSE;
-			else
+			} else {
 				is_partition = TRUE;
+                        }
 
 			hal_util_find_known_parent (hotplug_event->sysfs.sysfs_path, &parent, NULL);
 			hotplug_event_begin_add_blockdev (hotplug_event->sysfs.sysfs_path,
diff --git a/hald/linux/osspec.c b/hald/linux/osspec.c
index 67e1be9..afecfeb 100644
--- a/hald/linux/osspec.c
+++ b/hald/linux/osspec.c
@@ -149,10 +149,17 @@ hald_udev_data (GIOChannel *source, GIOC
 
 		if (strncmp(key, "ACTION=", 7) == 0)
 			action = &key[7];
-		else if (strncmp(key, "DEVPATH=", 8) == 0)
+		else if (strncmp(key, "DEVPATH=", 8) == 0) {
+
+                        /* md devices are handled via looking at /proc/mdstat */
+                        if (g_str_has_prefix (key + 8, "/block/md")) {
+                                HAL_INFO (("skipping md event for %s", key + 8));
+                                goto invalid;
+                        }
+
 			g_snprintf (hotplug_event->sysfs.sysfs_path, sizeof (hotplug_event->sysfs.sysfs_path),
 				    "%s%s", hal_sysfs_path, &key[8]);
-		else if (strncmp(key, "SUBSYSTEM=", 10) == 0)
+		} else if (strncmp(key, "SUBSYSTEM=", 10) == 0)
 			g_strlcpy (hotplug_event->sysfs.subsystem, &key[10], sizeof (hotplug_event->sysfs.subsystem));
 		else if (strncmp(key, "DEVNAME=", 8) == 0)
 			g_strlcpy (hotplug_event->sysfs.device_file, &key[8], sizeof (hotplug_event->sysfs.device_file));
@@ -247,8 +254,7 @@ out:
 }
 
 static gboolean
-mount_tree_changed_event (GIOChannel *channel, GIOCondition cond,
-		    gpointer user_data)
+mount_tree_changed_event (GIOChannel *channel, GIOCondition cond, gpointer user_data)
 {
 	if (cond & ~G_IO_ERR)
 		return TRUE;
@@ -259,6 +265,19 @@ mount_tree_changed_event (GIOChannel *ch
 	return TRUE;
 }
 
+static gboolean
+mdstat_changed_event (GIOChannel *channel, GIOCondition cond, gpointer user_data)
+{
+	if (cond & ~G_IO_PRI)
+		return TRUE;
+
+	HAL_INFO (("/proc/mdstat changed"));
+
+        blockdev_process_mdstat ();
+
+	return TRUE;
+}
+
 static HalFileMonitor *file_monitor = NULL;
 
 HalFileMonitor *
@@ -267,6 +286,14 @@ osspec_get_file_monitor (void)
         return file_monitor;
 }
 
+static GIOChannel *mdstat_channel = NULL;
+
+GIOChannel *get_mdstat_channel (void)
+{
+        return mdstat_channel;
+}
+
+
 void
 osspec_privileged_init (void)
 {
@@ -274,6 +301,14 @@ osspec_privileged_init (void)
         if (file_monitor == NULL) {
                 DIE (("Cannot initialize file monitor"));
         }
+
+	/* watch /proc/mdstat for md changes
+	 * kernel 2.6.19 throws a POLLPRI event for every change
+	 */
+	mdstat_channel = g_io_channel_new_file ("/proc/mdstat", "r", NULL);
+	if (mdstat_channel == NULL)
+		DIE (("Unable to read /proc/mdstat"));
+	g_io_add_watch (mdstat_channel, G_IO_PRI, mdstat_changed_event, NULL);
 }
 
 void
@@ -323,8 +358,7 @@ osspec_init (void)
 	if (hal_proc_path == NULL)
 		hal_proc_path = "/proc";
 
-	/*
-	 * watch /proc/mounts for mount tree changes
+	/* watch /proc/mounts for mount tree changes
 	 * kernel 2.6.15 vfs throws a POLLERR event for every change
 	 */
 	g_snprintf (path, sizeof (path), "%s/mounts", get_hal_proc_path ());
diff --git a/hald/linux/osspec_linux.h b/hald/linux/osspec_linux.h
index 8ecda30..41851c5 100644
--- a/hald/linux/osspec_linux.h
+++ b/hald/linux/osspec_linux.h
@@ -39,5 +39,7 @@ gboolean hal_util_set_driver (HalDevice 
 
 gboolean hal_util_find_known_parent (const gchar *sysfs_path, HalDevice **parent, gchar **parent_path);
 
+GIOChannel *get_mdstat_channel (void);
+
 
 #endif /* OSSPEC_LINUX_H */


More information about the hal-commit mailing list