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>
This commit is contained in:
Labun, Marcin 2011-01-25 15:59:32 +00:00 committed by NeilBrown
parent d6bd632c41
commit 20b60dcd6c
4 changed files with 154 additions and 0 deletions

View File

@ -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'},

18
mdadm.c
View File

@ -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);

View File

@ -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);

133
policy.c
View File

@ -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;
}