summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLabun, Marcin <Marcin.Labun@intel.com>2011-01-25 16:59:32 +0100
committerNeilBrown <neilb@suse.de>2011-01-27 03:48:04 +0100
commit20b60dcd6cea6cb517cea31056241607a2964e5d (patch)
tree0f7e2f1d5a24af137a68b2137cbac485452a7df8
parentIgnore/don't set data_disks for level=1 (diff)
downloadmdadm-20b60dcd6cea6cb517cea31056241607a2964e5d.tar.xz
mdadm-20b60dcd6cea6cb517cea31056241607a2964e5d.zip
Dynamic hot-plug udev rules for policies
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>
-rw-r--r--ReadMe.c1
-rw-r--r--mdadm.c18
-rw-r--r--mdadm.h2
-rw-r--r--policy.c133
4 files changed, 154 insertions, 0 deletions
diff --git a/ReadMe.c b/ReadMe.c
index 57148492..f40453c5 100644
--- 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 2ffe94f7..443c88a7 100644
--- 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 a1a12bc7..369118c7 100644
--- 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);
diff --git a/policy.c b/policy.c
index ba976db1..38b0072b 100644
--- 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;
+}