]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
Dynamic hot-plug udev rules for policies
authorLabun, Marcin <Marcin.Labun@intel.com>
Tue, 25 Jan 2011 15:59:32 +0000 (15:59 +0000)
committerNeilBrown <neilb@suse.de>
Thu, 27 Jan 2011 02:48:04 +0000 (12:48 +1000)
Neil,
Please consider this patch that once was discussed and I think agreed with in general direction. It was sent a while ago
but somehow did not merged into your devel3-2. This patch enables hot-plug of so called bare devices (as understand by domain policies rules in mdadm.conf).
Without this patch we do NOT serve hot-plug of bare devices at all.

Thanks,
Marcin Labun

Subject was: FW: Autorebuild, new dynamic udev rules for hot-plugs

>>From c0aecd4dd96691e8bfa6f2dc187261ec8bb2c5a2 Mon Sep 17 00:00:00 2001
From: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
Date: Thu, 23 Dec 2010 16:35:01 +0100
Subject: [PATCH] Dynamic hot-plug udev rules for policies
Cc: linux-raid@vger.kernel.org, Williams, Dan J <dan.j.williams@intel.com>, Ciechanowski, Ed <ed.ciechanowski@intel.com>
When introducing policies, new hot-plug rules were added to support
bare disks. Mdadm was started for each hot plugged block device
to determine if it could be used as spare or as a replacement member for
degraded array.
This patch introduces limitation of range of devices that are handled
by mdadm.
It limits them to the ones specified in domains associated with
the actions: spare-same-port, spare and spare-force.
In order to enable hot-plug for bare disks one must update udev rules
with command

        mdadm --activate-domains[=filename]

Above command writes udev rule configuration to stdout. If 'filename'
is given output is written to the file provided as parameter. It is up
to system administrator what should be done later. To make such rule
permanent (i.e. remain after reboot) rule should be writen to
/lib/udev/rules.d directory. Other cases will just need to write it to
/dev/.udev/rules.d directory where temporary rules lies. One should be
aware of the meaning of names/priorities of the udev rules.

After mdadm.conf is changed one is obliged to re-run
"mdadm --activate-domains" command in order to bring the system
configuration up to date.
All hot-plugged disks containing metadata are still handled by existing
rules.

Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
Signed-off-by: NeilBrown <neilb@suse.de>
ReadMe.c
mdadm.c
mdadm.h
policy.c

index 5714849293a766b18d2ef26242f02dadbb7a9c18..f40453c530bb6ddda27f131b9e81dc29f0470281 100644 (file)
--- a/ReadMe.c
+++ b/ReadMe.c
@@ -110,6 +110,7 @@ struct option long_options[] = {
     {"detail-platform", 0, 0, DetailPlatform},
     {"kill-subarray", 1, 0, KillSubarray},
     {"update-subarray", 1, 0, UpdateSubarray},
+    {"udev-rules", 2, 0, UdevRules},
 
     /* synonyms */
     {"monitor",   0, 0, 'F'},
diff --git a/mdadm.c b/mdadm.c
index 2ffe94f7c6c7c30bdec788f1d5ec6900124bfb22..443c88a769b0405cc6cfcaaed31dc71fc1ad6969 100644 (file)
--- a/mdadm.c
+++ b/mdadm.c
@@ -106,6 +106,7 @@ int main(int argc, char *argv[])
        int auto_update_home = 0;
        char *subarray = NULL;
        char *remove_path = NULL;
+       char *udev_filename = NULL;
 
        int print_help = 0;
        FILE *outf;
@@ -234,6 +235,7 @@ int main(int argc, char *argv[])
                                }
                                subarray = optarg;
                        }
+               case UdevRules:
                case 'K': if (!mode) newmode = MISC; break;
                case NoSharing: newmode = MONITOR; break;
                }
@@ -929,6 +931,20 @@ int main(int argc, char *argv[])
                        }
                        devmode = opt;
                        continue;
+               case O(MISC, UdevRules):
+                      if (devmode && devmode != opt) {
+                               fprintf(stderr, Name ": --udev-rules must"
+                                      " be the only option.\n");
+                      } else {
+                              if (udev_filename)
+                                      fprintf(stderr, Name ": only specify one udev "
+                                              "rule filename. %s ignored.\n",
+                                              optarg);
+                              else
+                                      udev_filename = optarg;
+                      }
+                      devmode = opt;
+                      continue;
                case O(MISC,'t'):
                        test = 1;
                        continue;
@@ -1493,6 +1509,8 @@ int main(int argc, char *argv[])
                                                free_mdstat(ms);
                                        } while (!last && err);
                                        if (err) rv |= 1;
+                               } else if (devmode == UdevRules) {
+                                       rv = Write_rules(udev_filename);
                                } else {
                                        fprintf(stderr, Name ": No devices given.\n");
                                        exit(2);
diff --git a/mdadm.h b/mdadm.h
index a1a12bc70c01c076bd2008e220c876de94a4fb5f..369118c7bac13929468768a1dc37a9e7c4442bf9 100644 (file)
--- a/mdadm.h
+++ b/mdadm.h
@@ -311,6 +311,7 @@ enum special_options {
        Bitmap,
        RebuildMapOpt,
        InvalidBackup,
+       UdevRules,
 };
 
 /* structures read from config file */
@@ -1061,6 +1062,7 @@ extern int CreateBitmap(char *filename, int force, char uuid[16],
                        unsigned long long array_size,
                        int major);
 extern int ExamineBitmap(char *filename, int brief, struct supertype *st);
+extern int Write_rules(char *rule_name);
 extern int bitmap_update_uuid(int fd, int *uuid, int swap);
 extern unsigned long bitmap_sectors(struct bitmap_super_s *bsb);
 
index ba976db1a7c0fc8374916b2a37a9641672592505..38b0072b5d111ab6f76168185c575efc0a5d4d8c 100644 (file)
--- a/policy.c
+++ b/policy.c
@@ -764,3 +764,136 @@ int policy_check_path(struct mdinfo *disk, struct map_ent *array)
        fclose(f);
        return rv == 5;
 }
+
+/* invocation of udev rule file */
+char udev_template_start[] =
+"# do not edit this file, it is automatically generated by mdadm\n"
+"\n";
+
+/* find rule named rule_type and return its value */
+char *find_rule(struct rule *rule, char *rule_type)
+{
+       while (rule) {
+               if (rule->name == rule_type)
+                       return rule->value;
+
+               rule = rule->next;
+       }
+       return NULL;
+}
+
+#define UDEV_RULE_FORMAT \
+"ACTION==\"add\", SUBSYSTEM=\"block\", " \
+"ENV{DEVTYPE}==\"%s\", ENV{ID_PATH}==\"%s\", " \
+"RUN+=\"/sbin/mdadm --incremental $env{DEVNAME}\", "
+
+#define UDEV_RULE_FORMAT_NOTYPE \
+"ACTION==\"add\", SUBSYSTEM=\"block\", " \
+"ENV{ID_PATH}==\"%s\", " \
+"RUN+=\"/sbin/mdadm --incremental $env{DEVNAME}\", "
+
+/* Write rule in the rule file. Use format from UDEV_RULE_FORMAT */
+int write_rule(struct rule *rule, int fd, int force_part)
+{
+       char line[1024];
+       char *pth = find_rule(rule, rule_path);
+       char *typ = find_rule(rule, rule_type);
+       if (!pth)
+               return -1;
+
+       if (force_part)
+               typ = type_part;
+       if (typ)
+               snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT, typ, pth);
+       else
+               snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT_NOTYPE, pth);
+       return write(fd, line, strlen(line)) == (int)strlen(line);
+}
+
+/* Generate single entry in udev rule basing on POLICY line found in config
+ * file. Take only those with paths, only first occurrence if paths are equal
+ * and if actions supports handling of spares (>=act_spare_same_slot)
+ */
+int generate_entries(int fd)
+{
+       struct pol_rule *loop, *dup;
+       char *loop_value, *dup_value;
+       int duplicate;
+
+       for (loop = config_rules; loop; loop = loop->next) {
+               if (loop->type != rule_policy && loop->type != rule_part)
+                       continue;
+               duplicate = 0;
+
+               /* only policies with paths and with actions supporting
+                * bare disks are considered */
+               loop_value = find_rule(loop->rule, pol_act);
+               if (!loop_value || map_act(loop_value) < act_spare_same_slot)
+                       continue;
+               loop_value = find_rule(loop->rule, rule_path);
+               if (!loop_value)
+                       continue;
+               for (dup = config_rules; dup != loop; dup = dup->next) {
+                       if (dup->type != rule_policy && loop->type != rule_part)
+                               continue;
+                       dup_value = find_rule(dup->rule, pol_act);
+                       if (!dup_value || map_act(dup_value) < act_spare_same_slot)
+                               continue;
+                       dup_value = find_rule(dup->rule, rule_path);
+                       if (!dup_value)
+                               continue;
+                       if (strcmp(loop_value, dup_value) == 0) {
+                               duplicate = 1;
+                               break;
+                       }
+               }
+
+               /* not a dup or first occurrence */
+               if (!duplicate)
+                       if (!write_rule(loop->rule, fd, loop->type == rule_part) )
+                               return 0;
+       }
+       return 1;
+}
+
+/* Write_rules routine creates dynamic udev rules used to handle
+ * hot-plug events for bare devices (and making them spares)
+ */
+int Write_rules(char *rule_name)
+{
+       int fd;
+       char udev_rule_file[PATH_MAX];
+
+       if (rule_name) {
+              strcpy(udev_rule_file, rule_name);
+              strcat(udev_rule_file, ".temp");
+               fd = creat(udev_rule_file,
+                          S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+              if (fd == -1)
+                      return 1;
+       } else
+               fd = 1;
+
+       /* write static invocation */
+       if (write(fd, udev_template_start,
+                sizeof(udev_template_start) - 1)
+          != (int)sizeof(udev_template_start)-1)
+              goto abort;
+
+       /* iterate, if none created or error occurred, remove file */
+       if (generate_entries(fd) < 0)
+              goto abort;
+
+       fsync(fd);
+       if (rule_name) {
+              close(fd);
+              rename(udev_rule_file, rule_name);
+       }
+       return 0;
+abort:
+       if (rule_name) {
+              close(fd);
+              unlink(udev_rule_file);
+       }
+       return 1;
+}