RE: A policy frame work for mdadm (incorporating domains andhotplug and such)

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 [at] vger.kernel.org [mailto:linux-raid-
> owner [at] vger.kernel.org] 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 [at] vger.kernel.org
> 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 [at] suse.de> 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 [at] suse.de>
>
> ---
> 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
> [at] [at] -87,26 +87,26 [at] [at] 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
> [at] [at] -114,7 +114,7 [at] [at] 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
> [at] [at] -75,7 +75,7 [at] [at] 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",
> [at] [at] -85,6 +85,8 [at] [at] char *keywords[] = {
> [CreateDev]= "create",
> [Homehost] = "homehost",
> [AutoMode] = "auto",
> + [Policy] = "policy",
> + [PartPolicy]="part-policy",
> [LTEnd] = NULL
> };
>
> [at] [at] -766,6 +768,12 [at] [at] 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
> [at] [at] -724,6 +724,65 [at] [at] 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
> [at] [at] -0,0 +1,471 [at] [at]
> +/*
> + * mdadm - manage Linux "md" devices aka RAID arrays.
> + *
> + * Copyright (C) 2001-2009 Neil Brown <neilb [at] suse.de>
> + *
> + *
> + * 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 [at] suse.de>
> + */
> +
> +#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 [at] vger.kernel.org
> 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 [at] vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
&quot;Hawrylewicz Cza [ Di, 27 Juli 2010 18:17 ] [ ID #2045147 ]
Linux » gmane.linux.raid » RE: A policy frame work for mdadm (incorporating domains andhotplug and such)

Vorheriges Thema: Equity Business Investment .,
Nächstes Thema: Increase request size for levels other than raid0?