Hi Neil, I was wondering if there is anything new in this area? Please share your ideas and/or a new code :) > -----Original Message----- > From: linux-raid-owner@xxxxxxxxxxxxxxx [mailto:linux-raid- > owner@xxxxxxxxxxxxxxx] On Behalf Of Neil Brown > Sent: Thursday, July 08, 2010 9:59 AM > To: Williams, Dan J > Cc: Doug Ledford; Labun, Marcin; Czarnowska, Anna; Hawrylewicz > Czarnowski, Przemyslaw; Ciechanowski, Ed; Healey, Douglas D; Neubauer, > Wojciech; linux-raid@xxxxxxxxxxxxxxx > Subject: Re: A policy frame work for mdadm (incorporating domains and > hotplug and such) > > On Thu, 1 Jul 2010 16:50:07 +1000 > Neil Brown <neilb@xxxxxxx> wrote: > > > So this is how I want these things to work, and this is what I'm > going to be > > coding. I should have the basic framework in place early next week > (assuming > > no major interruptions) at which point I'll make the code available. > > As one might expect there was a fairly significant interruption, so I > didn't > get as far as I hoped. > > Below is my current code, which compiles but is otherwise untested. > > This is just the infrastructure for reading, manipulating, and checking > policy. > > The next big step is implementing 'mbr' and 'gpt' metadata types (for > partitioning) and making sure I can make that idea work. > Then I need to generate a domain list given an array, and write code > to compare domain lists. > > Then we should be able to start connecting the policy framework with > the code > that will make use of the policy. > > NeilBrown > > Add policy framework. > > From: NeilBrown <neilb@xxxxxxx> > > --- > Makefile | 10 + > config.c | 10 + > mdadm.h | 59 ++++++++ > policy.c | 471 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 544 insertions(+), 6 deletions(-) > create mode 100644 policy.c > > diff --git a/Makefile b/Makefile > index 3af1665..11181e7 100644 > --- a/Makefile > +++ b/Makefile > @@ -87,26 +87,26 @@ MAN4DIR = $(MANDIR)/man4 > MAN5DIR = $(MANDIR)/man5 > MAN8DIR = $(MANDIR)/man8 > > -OBJS = mdadm.o config.o mdstat.o ReadMe.o util.o Manage.o Assemble.o > Build.o \ > +OBJS = mdadm.o config.o policy.o mdstat.o ReadMe.o util.o Manage.o > Assemble.o Build.o \ > Create.o Detail.o Examine.o Grow.o Monitor.o dlink.o Kill.o > Query.o \ > Incremental.o \ > mdopen.o super0.o super1.o super-ddf.o super-intel.o bitmap.o \ > restripe.o sysfs.o sha1.o mapfile.o crc32.o sg_io.o msg.o \ > platform-intel.o probe_roms.o > > -SRCS = mdadm.c config.c mdstat.c ReadMe.c util.c Manage.c Assemble.c > Build.c \ > +SRCS = mdadm.c config.c policy.c mdstat.c ReadMe.c util.c Manage.c > Assemble.c Build.c \ > Create.c Detail.c Examine.c Grow.c Monitor.c dlink.c Kill.c > Query.c \ > Incremental.c \ > mdopen.c super0.c super1.c super-ddf.c super-intel.c bitmap.c \ > restripe.c sysfs.c sha1.c mapfile.c crc32.c sg_io.c msg.c \ > platform-intel.c probe_roms.c > > -MON_OBJS = mdmon.o monitor.o managemon.o util.o mdstat.o sysfs.o > config.o \ > +MON_OBJS = mdmon.o monitor.o managemon.o util.o mdstat.o sysfs.o > config.o policy.o \ > Kill.o sg_io.o dlink.o ReadMe.o super0.o super1.o super-intel.o \ > super-ddf.o sha1.o crc32.o msg.o bitmap.o \ > platform-intel.o probe_roms.o > > -MON_SRCS = mdmon.c monitor.c managemon.c util.c mdstat.c sysfs.c > config.c \ > +MON_SRCS = mdmon.c monitor.c managemon.c util.c mdstat.c sysfs.c > config.c policy.c \ > Kill.c sg_io.c dlink.c ReadMe.c super0.c super1.c super-intel.c \ > super-ddf.c sha1.c crc32.c msg.c bitmap.c \ > platform-intel.c probe_roms.c > @@ -114,7 +114,7 @@ MON_SRCS = mdmon.c monitor.c managemon.c util.c > mdstat.c sysfs.c config.c \ > STATICSRC = pwgr.c > STATICOBJS = pwgr.o > > -ASSEMBLE_SRCS := mdassemble.c Assemble.c Manage.c config.c dlink.c > util.c \ > +ASSEMBLE_SRCS := mdassemble.c Assemble.c Manage.c config.c policy.c > dlink.c util.c \ > super0.c super1.c super-ddf.c super-intel.c sha1.c crc32.c > sg_io.c mdstat.c \ > platform-intel.c probe_roms.c sysfs.c > ASSEMBLE_AUTO_SRCS := mdopen.c > diff --git a/config.c b/config.c > index 20c46e9..995b41d 100644 > --- a/config.c > +++ b/config.c > @@ -75,7 +75,7 @@ char DefaultConfFile[] = CONFFILE; > char DefaultAltConfFile[] = CONFFILE2; > > enum linetype { Devices, Array, Mailaddr, Mailfrom, Program, > CreateDev, > - Homehost, AutoMode, LTEnd }; > + Homehost, AutoMode, Policy, PartPolicy, LTEnd }; > char *keywords[] = { > [Devices] = "devices", > [Array] = "array", > @@ -85,6 +85,8 @@ char *keywords[] = { > [CreateDev]= "create", > [Homehost] = "homehost", > [AutoMode] = "auto", > + [Policy] = "policy", > + [PartPolicy]="part-policy", > [LTEnd] = NULL > }; > > @@ -766,6 +768,12 @@ void load_conffile(void) > case AutoMode: > autoline(line); > break; > + case Policy: > + policyline(line, rule_policy); > + break; > + case PartPolicy: > + policyline(line, rule_part); > + break; > default: > fprintf(stderr, Name ": Unknown keyword %s\n", line); > } > diff --git a/mdadm.h b/mdadm.h > index d15e73e..f7e6548 100644 > --- a/mdadm.h > +++ b/mdadm.h > @@ -724,6 +724,65 @@ extern void get_one_disk(int mdfd, > mdu_array_info_t *ainf, > mdu_disk_info_t *disk); > void wait_for(char *dev, int fd); > > +/* > + * Data structures for policy management. > + * Each device can have a policy structure that lists > + * various name/value pairs each possibly with a metadata associated. > + * The policy list is sorted by name/value/metadata > + */ > +struct dev_policy { > + struct dev_policy *next; > + char *name; /* None of these strings are allocated. They are > + * all just references to strings which are known > + * to exist elsewhere. > + * name and metadata can be compared by address > equality. > + */ > + char *metadata; > + char *value; > +}; > + > +extern char pol_act[], pol_domain[], pol_metadata[]; > + > +/* iterate over the sublist starting at list, having the same > + * 'name' as 'list', and matching the given metadata (Where > + * NULL matches anything > + */ > +#define pol_for_each(item, list, metadata) \ > + for (item = list; \ > + item && item->name == list->name; \ > + item = item->next) \ > + if (!(!metadata || !item->metadata || metadata == item- > >metadata)) \ > + ; else > + > +/* > + * policy records read from mdadm are largely just name-value pairs. > + * The names are constants, not strdupped > + */ > +struct pol_rule { > + struct pol_rule *next; > + char *type; /* rule_policy or rule_part */ > + struct rule { > + struct rule *next; > + char *name; > + char *value; > + char *dups; /* duplicates of 'value' with a partNN appended > */ > + } *rule; > +}; > + > +extern char rule_policy[], rule_part[]; > +extern char rule_path[], rule_type[]; > + > +extern void policyline(char *line, char *type); > + > +enum policy_action { > + act_default, > + act_include, > + act_re_add, > + act_spare, > + act_force_spare, > + act_err > +}; > + > #if __GNUC__ < 3 > struct stat64; > #endif > diff --git a/policy.c b/policy.c > new file mode 100644 > index 0000000..7019d11 > --- /dev/null > +++ b/policy.c > @@ -0,0 +1,471 @@ > +/* > + * mdadm - manage Linux "md" devices aka RAID arrays. > + * > + * Copyright (C) 2001-2009 Neil Brown <neilb@xxxxxxx> > + * > + * > + * This program is free software; you can redistribute it and/or > modify > + * it under the terms of the GNU General Public License as > published by > + * the Free Software Foundation; either version 2 of the License, > or > + * (at your option) any later version. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public > License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- > 1307 USA > + * > + * Author: Neil Brown > + * Email: <neilb@xxxxxxx> > + */ > + > +#include "mdadm.h" > +#include <dirent.h> > +#include <fnmatch.h> > +#include <ctype.h> > +#include "dlink.h" > +/* > + * Policy module for mdadm. > + * A policy statement about a device lists a set of values for each > + * of a set of names. Each value can have a metadata type as context. > + * > + * names include: > + * action - the actions that can be taken on hot-plug > + * domain - the domain(s) that the device is part of > + * > + * Policy information is extracted from various sources, but > + * particularly from a set of policy rules in mdadm.conf > + */ > + > +void pol_new(struct dev_policy **pol, char *name, char *val, char > *metadata) > +{ > + struct dev_policy *n = malloc(sizeof(*n)); > + n->name = name; > + n->value = val; > + n->metadata = metadata; > + n->next = *pol; > + *pol = n; > +} > + > +int pol_lesseq(struct dev_policy *a, struct dev_policy *b) > +{ > + int cmp; > + > + if (a->name < b->name) > + return 1; > + if (a->name > b->name) > + return 0; > + > + cmp = strcmp(a->value, b->value); > + if (cmp < 0) > + return 1; > + if (cmp > 0) > + return 0; > + > + return (a->metadata <= b->metadata); > +} > + > +void pol_sort(struct dev_policy **pol) > +{ > + /* sort policy list in *pol by name/metadata/value > + * using merge sort > + */ > + > + struct dev_policy *pl[2]; > + pl[0] = *pol; > + pl[1] = NULL; > + > + do { > + struct dev_policy **plp[2], *p[2]; > + int curr = 0; > + struct dev_policy nul = { NULL }; > + struct dev_policy *prev = &nul; > + int next = 0; > + > + /* p[] are the two lists that we are merging. > + * plp[] are the ends of the two lists we create > + * from the merge. > + * 'curr' is which of plp[] that we are currently > + * adding items to. > + * 'next' is which if p[] we will take the next > + * item from. > + * 'prev' is that last value, which was placed in > + * plp[curr]. > + */ > + plp[0] = &pl[0]; > + plp[1] = &pl[1]; > + p[0] = pl[0]; > + p[1] = pl[1]; > + > + /* take least of p[0] and p[1] > + * if it is larger than prev, add to > + * plp[curr], else swap curr then add > + */ > + while (p[0] || p[1]) { > + if (p[next] == NULL || > + (p[1-next] != NULL && > + !(pol_lesseq(prev, p[1-next]) > + ^pol_lesseq(p[1-next], p[next]) > + ^pol_lesseq(p[next], prev))) > + ) > + next = 1 - next; > + > + if (!pol_lesseq(prev, p[next])) > + curr = 1 - curr; > + > + *plp[curr] = prev = p[next]; > + plp[curr] = &p[next]->next; > + p[next] = p[next]->next; > + } > + *plp[0] = NULL; > + *plp[1] = NULL; > + } while (pl[0] && pl[1]); > + if (pl[0]) > + *pol = pl[0]; > + else > + *pol = pl[1]; > +} > + > +void pol_dedup(struct dev_policy *pol) > +{ > + /* This is a sorted list - remove duplicates. */ > + while (pol && pol->next) { > + if (pol_lesseq(pol->next, pol)) { > + struct dev_policy *tmp = pol->next; > + pol->next = tmp->next; > + free(tmp); > + } else > + pol = pol->next; > + } > +} > + > +#if 0 > +struct dev_policy *pol_dup(struct dev_policy *pol) > +{ > + struct dev_policy *rv = NULL; > + struct dev_policy **ep = &rv; > + > + while (pol) { > + pol_new(ep, pol->name, pol->val, pol->metadata); > + ep = &(*ep)->next; > + pol = pol->next; > + } > + return rv; > +} > +#endif > + > +/* > + * pol_find finds the first entry in the policy > + * list to match name. > + * If it returns non-NULL there is at least one > + * value, but how many can only be found by > + * iterating through the list. > + */ > +struct dev_policy *pol_find(struct dev_policy *pol, char *name) > +{ > + while (pol && pol->name < name) > + pol = pol->next; > + > + if (!pol || pol->name != name) > + return NULL; > + return pol; > +} > + > +char *path_from_fd(int fd) > +{ > + struct stat stb1, stb2; > + int prefix_len; > + DIR *by_path; > + char symlink[PATH_MAX] = "/dev/disk/by_path/"; > + struct dirent *ent; > + > + fstat(fd, &stb1); > + > + by_path = opendir(symlink); > + if (!by_path) > + return NULL; > + prefix_len = strlen(symlink); > + > + while ((ent = readdir(by_path)) != NULL) { > + if (ent->d_type != DT_LNK) > + continue; > + strncpy(symlink + prefix_len, > + ent->d_name, > + sizeof(symlink) - prefix_len); > + if (stat(symlink, &stb2) < 0) > + continue; > + if ((stb1.st_mode & S_IFMT) != > + (stb2.st_mode & S_IFMT)) > + continue; > + if (stb1.st_rdev != stb2.st_rdev) > + continue; > + closedir(by_path); > + return strdup(ent->d_name); > + } > + closedir(by_path); > + return NULL; > +} > + > +char type_part[] = "part"; > +char type_disk[] = "disk"; > +char *type_from_fd(int fd) > +{ > + if (test_partition(fd)) > + return type_part; > + else > + return type_disk; > +} > + > +int pol_match(struct rule *rule, char *path, char *type) > +{ > + /* check if this rule matches on path and type */ > + int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet > */ > + int typeok = 0; > + > + while (rule) { > + if (rule->name == rule_path) { > + if (pathok == 0) > + pathok = -1; > + if (fnmatch(rule->value, path, 0) == 0) > + pathok = 1; > + } > + if (rule->name == rule_type) { > + if (typeok == 0) > + typeok = -1; > + if (strcmp(rule->value, type) == 0) > + typeok = 1; > + } > + rule = rule->next; > + } > + return pathok >= 0 && typeok >= 0; > +} > + > +void pol_merge(struct dev_policy **pol, struct rule *rule) > +{ > + /* copy any name assignments from rule into pol */ > + struct rule *r; > + char *metadata = NULL; > + for (r = rule; r ; r = r->next) > + if (r->name == pol_metadata) > + metadata = r->value; > + > + for (r = rule; r ; r = r->next) > + if (r->name == pol_act || > + r->name == pol_domain) > + pol_new(pol, r->name, r->value, metadata); > +} > + > +static int path_has_part(char *path, char **part) > +{ > + /* check if path ends with "-partNN" and > + * if it does, place a pointer to "-pathNN" > + * in 'part'. > + */ > + int l = strlen(path); > + while (l > 1 && isdigit(path[l-1])) > + l--; > + if (l < 5 || strncmp(path+l-5, "-part", 5) != 0) > + return 0; > + *part = path+l-4; > + return 1; > +} > + > +void pol_merge_part(struct dev_policy **pol, struct rule *rule, char > *part) > +{ > + /* copy any name assignments from rule into pol, appending > + * -part to any domain. The string with -part appended is > + * stored with the rule so it has a lifetime to match > + * the rule. > + */ > + struct rule *r; > + char *metadata = NULL; > + for (r = rule; r ; r = r->next) > + if (r->name == pol_metadata) > + metadata = r->value; > + > + for (r = rule; r ; r = r->next) { > + if (r->name == pol_act) > + pol_new(pol, r->name, r->value, metadata); > + else if (r->name == pol_domain) { > + char *dom; > + int len; > + if (r->dups == NULL) > + r->dups = dl_head(); > + len = strlen(r->value); > + for (dom = dl_next(r->dups); dom != r->dups; dom = > dl_next(dom)) > + if (strcmp(dom+len+1, part)== 0) > + break; > + if (dom == r->dups) { > + char *newdom = dl_strndup(r->value, len + 1 + > strlen(part)); > + strcat(strcat(newdom, "-"), part); > + dl_add(r->dups, newdom); > + dom = newdom; > + } > + pol_new(pol, r->name, dom, metadata); > + } > + } > +} > + > +static struct pol_rule *config_rules = NULL; > +static struct pol_rule **config_rules_end = NULL; > +static int config_rules_has_path = 0; > + > +/* > + * most policy comes from a set policy rules that are > + * read from the config file. > + * device_policy() gathers policy information for the > + * device opened in 'fd'. > + */ > +struct dev_policy *device_policy(int fd) > +{ > + char *path; > + char *type = type_from_fd(fd); > + struct pol_rule *rules; > + struct dev_policy *pol = NULL; > + > + if (config_rules_has_path) { > + path = path_from_fd(fd); > + if (!path || !type) { > + free(path); > + return NULL; > + } > + } > + > + rules = config_rules; > + > + while (rules) { > + char *part; > + if (rules->type == rule_policy) > + if (pol_match(rules->rule, path, type)) > + pol_merge(&pol, rules->rule); > + if (rules->type == rule_part && strcmp(type, type_part) == > 0) > + if (path_has_part(path, &part)) { > + *part = 0; > + if (pol_match(rules->rule, path, type_disk)) > + pol_merge_part(&pol, rules->rule, > part+1); > + *part = '-'; > + } > + rules = rules->next; > + } > + pol_sort(&pol); > + pol_dedup(pol); > + free(path); > + return pol; > +} > + > +/* > + * process policy rules read from config file. > + */ > + > +char rule_path[] = "path"; > +char rule_type[] = "type"; > + > +char rule_policy[] = "policy"; > +char rule_part[] = "part-policy"; > + > +char pol_metadata[] = "metadata"; > +char pol_act[] = "action"; > +char pol_domain[] = "domain"; > + > +static int try_rule(char *w, char *name, struct rule **rp) > +{ > + struct rule *r; > + int len = strlen(name); > + if (strncmp(w, name, len) != 0 || > + name[len] != '=') > + return 0; > + r = malloc(sizeof(*r)); > + r->next = *rp; > + r->name = name; > + r->value = strdup(w+len+1); > + r->dups = NULL; > + *rp = r; > + return 1; > +} > + > +void policyline(char *line, char *type) > +{ > + struct pol_rule *pr; > + char *w; > + > + if (config_rules_end == NULL) > + config_rules_end = &config_rules; > + > + pr = malloc(sizeof(*pr)); > + pr->type = type; > + pr->rule = NULL; > + for (w = dl_next(line); w != line ; w = dl_next(w)) { > + if (try_rule(w, rule_path, &pr->rule)) > + config_rules_has_path = 1; > + else if (! try_rule(w, rule_type, &pr->rule) && > + ! try_rule(w, pol_metadata, &pr->rule) && > + ! try_rule(w, pol_act, &pr->rule) && > + ! try_rule(w, pol_domain, &pr->rule)) > + fprintf(stderr, Name ": policy rule %s unrecognised > and ignored\n", > + w); > + } > + pr->next = config_rules; > + config_rules = pr; > +} > + > +void policy_free(void) > +{ > + while (config_rules) { > + struct pol_rule *pr = config_rules; > + struct rule *r; > + > + config_rules = config_rules->next; > + > + for (r = pr->rule; r; ) { > + struct rule *next = r->next; > + free(r->value); > + if (r->dups) > + free_line(r->dups); > + free(r); > + r = next; > + } > + free(pr); > + } > + config_rules_end = NULL; > + config_rules_has_path = 0; > +} > + > +void dev_policy_free(struct dev_policy *p) > +{ > + struct dev_policy *t; > + while (p) { > + t = p; > + p = p->next; > + free(t); > + } > +} > + > +enum policy_action map_act(char *act) > +{ > + if (strcmp(act, "include") == 0) > + return act_include; > + if (strcmp(act, "re-add") == 0) > + return act_re_add; > + if (strcmp(act, "spare") == 0) > + return act_spare; > + if (strcmp(act, "force-spare") == 0) > + return act_force_spare; > + return act_err; > +} > + > +enum policy_action policy_action(struct dev_policy *plist, char > *metadata) > +{ > + enum policy_action rv = act_default; > + struct dev_policy *p; > + > + plist = pol_find(plist, pol_act); > + pol_for_each(p, plist, metadata) { > + enum policy_action a = map_act(p->value); > + if (a > rv) > + rv = a; > + } > + return rv; > +} > -- > To unsubscribe from this list: send the line "unsubscribe linux-raid" > in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-raid" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html