Whamcloud - gitweb
Branch HEAD
authorvs <vs>
Thu, 27 Nov 2008 01:55:35 +0000 (01:55 +0000)
committervs <vs>
Thu, 27 Nov 2008 01:55:35 +0000 (01:55 +0000)
b=12749
i=maxim,isaac

First of three patches for the root squash feature

This adds three functions to manipulate with lists of NID ranges:
cfs_parse_nidlist - parses nid range list and
                    creates structure of lists of [lo,hi,stride,net]
                    which can be used to check matching a NID to set of NIDs
cfs_free_nidlist - frees structures allocated on nidlist parsing
cfs_match_nid - check whether a NID matches to set of NIDs
cfs_iswhite - auxiliary function to check if a character is a whitespace

These functions have prefixes cfs_ instead of libcfs_ because of bug #17167.

Example of NID range list: 10.2.0.[1-20/2]@tcp [0,2,6]@elan0 [1,2,4-100/4]@elan1
nidlist syntax:
  <nidlist>         :== <nidrange> [ ' ' <nidrange> ]
  <nidrange>        :== <addrrange> '@' <net>
  <addrrange>       :== '*' |
                        <ipaddr_range> |
                        <numaddr_range>
  <ipaddr_range>    :== <numaddr_range>.<numaddr_range>.<numaddr_range>.<numaddr_range>
  <numaddr_range>   :== <number> |
                        <expr_list>
  <expr_list>       :== '[' <range_expr> [ ',' <range_expr>] ']'
  <range_expr>      :== <number> |
                        <number> '-' <number> |
                        <number> '-' <number> '/' <number>
  <net>             :== <netname> | <netname><number>
  <netname>         :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" |
                        "vib" | "ra" | "elan" | "gm" | "mx" | "ptl"
  <number>          :== <nonnegative decimal> | <hexadecimal>

libcfs/include/libcfs/libcfs_private.h
libcfs/libcfs/nidstrings.c
lnet/lnet/config.c

index 9e3fe71..a791c32 100644 (file)
@@ -312,6 +312,10 @@ __u32       libcfs_str2net(const char *str);
 lnet_nid_t  libcfs_str2nid(const char *str);
 int         libcfs_str2anynid(lnet_nid_t *nid, const char *str);
 char       *libcfs_id2str(lnet_process_id_t id);
+int         cfs_iswhite(char c);
+void        cfs_free_nidlist(struct list_head *list);
+int         cfs_parse_nidlist(char *str, int len, struct list_head *list);
+int         cfs_match_nid(lnet_nid_t nid, struct list_head *list);
 
 /* how an LNET NID encodes net:address */
 #define LNET_NIDADDR(nid)      ((__u32)((nid) & 0xffffffff))
@@ -322,6 +326,9 @@ char       *libcfs_id2str(lnet_process_id_t id);
 #define LNET_NETTYP(net)       (((net) >> 16) & 0xffff)
 #define LNET_MKNET(typ,num)    ((((__u32)(typ))<<16)|((__u32)(num)))
 
+/* max value for numeric network address */
+#define MAX_NUMERIC_VALUE 0xffffffff
+
 /* implication */
 #define ergo(a, b) (!(a) || (b))
 /* logical equivalence */
index df02584..28796e7 100644 (file)
@@ -108,6 +108,10 @@ static int  libcfs_ip_str2addr(const char *str, int nob, __u32 *addr);
 static void libcfs_decnum_addr2str(__u32 addr, char *str);
 static void libcfs_hexnum_addr2str(__u32 addr, char *str);
 static int  libcfs_num_str2addr(const char *str, int nob, __u32 *addr);
+static int  libcfs_ip_parse(char *str, int len, struct list_head *list);
+static int  libcfs_num_parse(char *str, int len, struct list_head *list);
+static int  libcfs_ip_match(__u32 addr, struct list_head *list);
+static int  libcfs_num_match(__u32 addr, struct list_head *list);
 
 struct netstrfns {
         int          nf_type;
@@ -115,6 +119,9 @@ struct netstrfns {
         char        *nf_modname;
         void       (*nf_addr2str)(__u32 addr, char *str);
         int        (*nf_str2addr)(const char *str, int nob, __u32 *addr);
+        int        (*nf_parse_addrlist)(char *str, int len,
+                                        struct list_head *list);
+        int        (*nf_match_addr)(__u32 addr, struct list_head *list);
 };
 
 static struct netstrfns  libcfs_netstrfns[] = {
@@ -122,62 +129,86 @@ static struct netstrfns  libcfs_netstrfns[] = {
          /* .nf_name      */  "lo",
          /* .nf_modname   */  "klolnd",
          /* .nf_addr2str  */  libcfs_decnum_addr2str,
-         /* .nf_str2addr  */  libcfs_lo_str2addr},
+         /* .nf_str2addr  */  libcfs_lo_str2addr,
+         /* .nf_parse_addr*/  libcfs_num_parse,
+         /* .nf_match_addr*/  libcfs_num_match},
         {/* .nf_type      */  SOCKLND,
          /* .nf_name      */  "tcp",
          /* .nf_modname   */  "ksocklnd",
          /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
+         /* .nf_str2addr  */  libcfs_ip_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_ip_parse,
+         /* .nf_match_addr*/  libcfs_ip_match},
         {/* .nf_type      */  O2IBLND,
          /* .nf_name      */  "o2ib",
          /* .nf_modname   */  "ko2iblnd",
          /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
+         /* .nf_str2addr  */  libcfs_ip_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_ip_parse,
+         /* .nf_match_addr*/  libcfs_ip_match},
         {/* .nf_type      */  CIBLND,
          /* .nf_name      */  "cib",
          /* .nf_modname   */  "kciblnd",
          /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
+         /* .nf_str2addr  */  libcfs_ip_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_ip_parse,
+         /* .nf_match_addr*/  libcfs_ip_match},
         {/* .nf_type      */  OPENIBLND,
          /* .nf_name      */  "openib",
          /* .nf_modname   */  "kopeniblnd",
          /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
+         /* .nf_str2addr  */  libcfs_ip_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_ip_parse,
+         /* .nf_match_addr*/  libcfs_ip_match},
         {/* .nf_type      */  IIBLND,
          /* .nf_name      */  "iib",
          /* .nf_modname   */  "kiiblnd",
          /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
+         /* .nf_str2addr  */  libcfs_ip_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_ip_parse,
+         /* .nf_match_addr*/  libcfs_ip_match},
         {/* .nf_type      */  VIBLND,
          /* .nf_name      */  "vib",
          /* .nf_modname   */  "kviblnd",
          /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
+         /* .nf_str2addr  */  libcfs_ip_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_ip_parse,
+         /* .nf_match_addr*/  libcfs_ip_match},
         {/* .nf_type      */  RALND,
          /* .nf_name      */  "ra",
          /* .nf_modname   */  "kralnd",
          /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
+         /* .nf_str2addr  */  libcfs_ip_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_ip_parse,
+         /* .nf_match_addr*/  libcfs_ip_match},
         {/* .nf_type      */  QSWLND,
          /* .nf_name      */  "elan",
          /* .nf_modname   */  "kqswlnd",
          /* .nf_addr2str  */  libcfs_decnum_addr2str,
-         /* .nf_str2addr  */  libcfs_num_str2addr},
+         /* .nf_str2addr  */  libcfs_num_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_num_parse,
+         /* .nf_match_addr*/  libcfs_num_match},
         {/* .nf_type      */  GMLND,
          /* .nf_name      */  "gm",
          /* .nf_modname   */  "kgmlnd",
          /* .nf_addr2str  */  libcfs_hexnum_addr2str,
-         /* .nf_str2addr  */  libcfs_num_str2addr},
+         /* .nf_str2addr  */  libcfs_num_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_num_parse,
+         /* .nf_match_addr*/  libcfs_num_match},
         {/* .nf_type      */  MXLND,
          /* .nf_name      */  "mx",
          /* .nf_modname   */  "kmxlnd",
          /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
+         /* .nf_str2addr  */  libcfs_ip_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_ip_parse,
+         /* .nf_match_addr*/  libcfs_ip_match},
         {/* .nf_type      */  PTLLND,
          /* .nf_name      */  "ptl",
          /* .nf_modname   */  "kptllnd",
          /* .nf_addr2str  */  libcfs_decnum_addr2str,
-         /* .nf_str2addr  */  libcfs_num_str2addr},
+         /* .nf_str2addr  */  libcfs_num_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_num_parse,
+         /* .nf_match_addr*/  libcfs_num_match},
         /* placeholder for net0 alias.  It MUST BE THE LAST ENTRY */
         {/* .nf_type      */  -1},
 };
@@ -310,6 +341,21 @@ libcfs_lnd2netstrfns(int lnd)
 }
 
 struct netstrfns *
+libcfs_namenum2netstrfns(const char *name)
+{
+        struct netstrfns *nf;
+        int               i;
+
+        for (i = 0; i < libcfs_nnetstrfns; i++) {
+                nf = &libcfs_netstrfns[i];
+                if (nf->nf_type >= 0 &&
+                    !strncmp(name, nf->nf_name, strlen(nf->nf_name)))
+                        return nf;
+        }
+        return NULL;
+}
+
+struct netstrfns *
 libcfs_name2netstrfns(const char *name)
 {
         int    i;
@@ -514,6 +560,708 @@ libcfs_str2anynid(lnet_nid_t *nidp, const char *str)
         return *nidp != LNET_NID_ANY;
 }
 
+/**
+ * Nid range list syntax.
+ *
+ * <nidlist>         :== <nidrange> [ ' ' <nidrange> ]
+ * <nidrange>        :== <addrrange> '@' <net>
+ * <addrrange>       :== '*' |
+ *                       <ipaddr_range> |
+ *                       <numaddr_range>
+ * <ipaddr_range>    :== <numaddr_range>.<numaddr_range>.<numaddr_range>.
+ *                       <numaddr_range>
+ * <numaddr_range>   :== <number> |
+ *                       <expr_list>
+ * <expr_list>       :== '[' <range_expr> [ ',' <range_expr>] ']'
+ * <range_expr>      :== <number> |
+ *                       <number> '-' <number> |
+ *                       <number> '-' <number> '/' <number>
+ * <net>             :== <netname> | <netname><number>
+ * <netname>         :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" |
+ *                       "vib" | "ra" | "elan" | "gm" | "mx" | "ptl"
+ */
+
+/**
+ * Structure to represent NULL-less strings.
+ */
+struct lstr {
+        char *ls_str;
+        int ls_len;
+};
+
+/**
+ * Structure to represent <nidrange> token of the syntax.
+ *
+ * One of this is created for each <net> parsed.
+ */
+struct nidrange {
+        /**
+         * Link to list of this structures which is built on nid range
+         * list parsing.
+         */
+        struct list_head nr_link;
+        /**
+         * List head for addrrange::ar_link.
+         */
+        struct list_head nr_addrranges;
+        /**
+         * Flag indicating that *@<net> is found.
+         */
+        int nr_all;
+        /**
+         * Pointer to corresponding element of libcfs_netstrfns.
+         */
+        struct netstrfns *nr_netstrfns;
+        /**
+         * Number of network. E.g. 5 if <net> is "elan5".
+         */
+        int nr_netnum;
+};
+
+/**
+ * Structure to represent <addrrange> token of the syntax.
+ */
+struct addrrange {
+        /**
+         * Link to nidrange::nr_addrranges.
+         */
+        struct list_head ar_link;
+        /**
+         * List head for numaddr_range::nar_link.
+         */
+        struct list_head ar_numaddr_ranges;
+};
+
+/**
+ * Structure to represent <numaddr_range> token of the syntax.
+ */
+struct numaddr_range {
+        /**
+         * Link to addrrange::ar_numaddr_ranges.
+         */
+        struct list_head nar_link;
+        /**
+         * List head for range_expr::re_link.
+         */
+        struct list_head nar_range_exprs;
+};
+
+/**
+ * Structure to represent <range_expr> token of the syntax.
+ */
+struct range_expr {
+        /**
+         * Link to numaddr_range::nar_range_exprs.
+         */
+        struct list_head re_link;
+        __u32 re_lo;
+        __u32 re_hi;
+        __u32 re_stride;
+};
+
+int
+cfs_iswhite(char c)
+{
+        switch (c) {
+        case ' ':
+        case '\t':
+        case '\n':
+        case '\r':
+                return 1;
+        default:
+                break;
+        }
+        return 0;
+}
+
+/*
+ * Extracts tokens from strings.
+ *
+ * Looks for \a delim in string \a next, sets \a res to point to
+ * substring before the delimiter, sets \a next right after the found
+ * delimiter.
+ *
+ * \retval 1 if \a res points to a string of non-whitespace characters
+ * \retval 0 otherwise
+ */
+static int
+gettok(struct lstr *next, char delim, struct lstr *res)
+{
+        char *end;
+
+        if (next->ls_str == NULL)
+                return 0;
+
+        /* skip leading white spaces */
+        while (next->ls_len) {
+                if (!cfs_iswhite(*next->ls_str))
+                        break;
+                next->ls_str ++;
+                next->ls_len --;
+        }
+        if (next->ls_len == 0)
+                /* whitespaces only */
+                return 0;
+
+        if (*next->ls_str == delim)
+                /* first non-writespace is the delimiter */
+                return 0;
+
+        res->ls_str = next->ls_str;
+        end = memchr(next->ls_str, delim, next->ls_len);
+        if (end == NULL) {
+                /* there is no the delimeter in the string */
+                end = next->ls_str + next->ls_len;
+                next->ls_str = NULL;
+        } else {
+                next->ls_str = end + 1;
+                next->ls_len -= (end - res->ls_str + 1);
+        }
+
+        /* skip ending whitespaces */
+        while (--end != res->ls_str)
+                if (!cfs_iswhite(*end))
+                        break;
+
+        res->ls_len = end - res->ls_str + 1;
+        return 1;
+}
+
+/**
+ * Converts string to integer.
+ *
+ * Accepts decimal and hexadecimal number recordings.
+ *
+ * \retval 1 if first \a nob chars of \a str convert to decimal or
+ * hexadecimal integer in the range [\a min, \a max]
+ * \retval 0 otherwise
+ */
+static int
+libcfs_str2num_check(const char *str, int nob, unsigned *num,
+                     unsigned min, unsigned max)
+{
+        int n;
+        char nstr[12];
+
+        n = nob;
+        if (sscanf(str, "%u%n", num, &n) != 1 || n != nob)
+                if (sscanf(str, "0x%x%n", num, &n) != 1 || n != nob)
+                        if (sscanf(str, "0X%x%n", num, &n) != 1 || n != nob)
+                                return 0;
+        sprintf(nstr, "%u", *num);
+        if (n != strlen(nstr) || memcmp(nstr, str, n)) {
+                sprintf(nstr, "0x%x", *num);
+                if (n != strlen(nstr) || memcmp(nstr, str, n)) {
+                        sprintf(nstr, "0X%x", *num);
+                        if (n != strlen(nstr) || memcmp(nstr, str, n))
+                                return 0;
+                }
+        }
+        if (*num < min || *num > max)
+                return 0;
+        return 1;
+}
+
+/**
+ * Parses <range_expr> token of the syntax.
+ *
+ * \retval pointer to allocated range_expr and initialized
+ * range_expr::re_lo, range_expr::re_hi and range_expr:re_stride if \a
+ * src parses to
+ * <number> |
+ * <number> '-' <number> |
+ * <number> '-' <number> '/' <number>
+ * \retval NULL othersize
+ */
+static struct range_expr *
+parse_range_expr(struct lstr *src, unsigned min, unsigned max)
+{
+        struct lstr tok;
+        struct range_expr *expr;
+
+        LIBCFS_ALLOC(expr, sizeof(struct range_expr));
+        if (expr == NULL)
+                return NULL;
+
+        if (libcfs_str2num_check(src->ls_str, src->ls_len, &expr->re_lo,
+                                 min, max)) {
+                /* <number> is parsed */
+                expr->re_hi = expr->re_lo;
+                expr->re_stride = 1;
+                return expr;
+        }
+
+        if (!gettok(src, '-', &tok))
+                goto failed;
+        if (!libcfs_str2num_check(tok.ls_str, tok.ls_len, &expr->re_lo,
+                                  min, max))
+                goto failed;
+        /* <number> - */
+        if (libcfs_str2num_check(src->ls_str, src->ls_len, &expr->re_hi,
+                                 min, max)) {
+                /* <number> - <number> is parsed */
+                expr->re_stride = 1;
+                return expr;
+        }
+
+        /* go to check <number> '-' <number> '/' <number> */
+        if (gettok(src, '/', &tok)) {
+                if (!libcfs_str2num_check(tok.ls_str, tok.ls_len,
+                                          &expr->re_hi, min, max))
+                        goto failed;
+                /* <number> - <number> / ... */
+                if (libcfs_str2num_check(src->ls_str, src->ls_len,
+                                         &expr->re_stride, min, max))
+                        /* <number> - <number> / <number> is parsed */
+                        return expr;
+        }
+
+failed:
+        LIBCFS_FREE(expr, sizeof(struct range_expr));
+        return NULL;
+}
+
+/**
+ * Parsed <expr_list> token of the syntax.
+ *
+ * \retval 1 if \a str parses to '[' <range_expr> [ ',' <range_expr>] ']'
+ * \return 0 otherwise
+ */
+static int
+parse_expr_list(struct lstr *str, struct list_head *list,
+                unsigned min, unsigned max)
+{
+        struct lstr res;
+        struct range_expr *range;
+
+        if (str->ls_str[0] != '[' || str->ls_str[str->ls_len - 1] != ']')
+                return 0;
+        str->ls_str ++;
+        str->ls_len -= 2;
+
+        while (str->ls_str) {
+                if (gettok(str, ',', &res) == 0)
+                        return 0;
+                range = parse_range_expr(&res, min, max);
+                if (range == NULL)
+                        return 0;
+                list_add_tail(&range->re_link, list);
+        }
+        return 1;
+}
+
+/**
+ * Parses <numaddr_range> token of the syntax.
+ *
+ * \retval 1 if \a str parses to <number> | <expr_list>
+ * \retval 0 otherwise
+ */
+static int
+num_parse(char *str, int len,
+          struct list_head *list, unsigned min, unsigned max)
+{
+        __u32 num;
+        struct lstr src;
+        struct numaddr_range *numaddr;
+
+        src.ls_str = str;
+        src.ls_len = len;
+
+        LIBCFS_ALLOC(numaddr, sizeof(struct numaddr_range));
+        if (numaddr == NULL)
+                return 0;
+        list_add_tail(&numaddr->nar_link, list);
+        CFS_INIT_LIST_HEAD(&numaddr->nar_range_exprs);
+
+        if (libcfs_str2num_check(src.ls_str, src.ls_len, &num, min, max)) {
+                /* <number> */
+                struct range_expr *expr;
+
+                LIBCFS_ALLOC(expr, sizeof(struct range_expr));
+                if (expr == NULL)
+                        return 0;
+
+                expr->re_lo = expr->re_hi = num;
+                expr->re_stride = 1;
+                list_add_tail(&expr->re_link, &numaddr->nar_range_exprs);
+                return 1;
+        }
+
+        return parse_expr_list(&src, &numaddr->nar_range_exprs, min, max);
+}
+
+/**
+ * Nf_parse_addrlist method for networks using numeric addresses.
+ *
+ * Examples of such networks are gm and elan.
+ *
+ * \retval 1 if \a str parsed to numeric address
+ * \retval 0 otherwise
+ */
+static int
+libcfs_num_parse(char *str, int len, struct list_head *list)
+{
+        return num_parse(str, len, list, 0, MAX_NUMERIC_VALUE);
+}
+
+/**
+ * Nf_parse_addrlist method for networks using ip addresses.
+ *
+ * Examples of such networks are tcp and o2ib.
+ *
+ * \retval 1 if \a str parsed to ip address
+ * \retval 0 otherwise
+ */
+static int
+libcfs_ip_parse(char *str, int len,
+                struct list_head *list)
+{
+        struct lstr src, res;
+        int i;
+
+        src.ls_str = str;
+        src.ls_len = len;
+        i = 0;
+        while (src.ls_str) {
+                if (gettok(&src, '.', &res) == 0)
+                        return 0;
+                if (!num_parse(res.ls_str, res.ls_len, list, 0, 255))
+                        return 0;
+                i ++;
+        }
+
+        return (i == 4) ? 1 : 0;
+}
+
+/**
+ * Parses <addrrange> token on the syntax.
+ *
+ * Allocates struct addrrange and links to \a nidrange via
+ * (nidrange::nr_addrranges)
+ *
+ * \retval 1 if \a src parses to '*' | <ipaddr_range> | <numaddr_range>
+ * \retval 0 otherwise
+ */
+static int
+parse_addrange(const struct lstr *src, struct nidrange *nidrange)
+{
+        struct addrrange *addrrange;
+
+        if (src->ls_len == 1 && src->ls_str[0] == '*') {
+                nidrange->nr_all = 1;
+                return 1;
+        }
+
+        LIBCFS_ALLOC(addrrange, sizeof(struct addrrange));
+        if (addrrange == NULL)
+                return 0;
+        list_add_tail(&addrrange->ar_link, &nidrange->nr_addrranges);
+        CFS_INIT_LIST_HEAD(&addrrange->ar_numaddr_ranges);
+
+        return nidrange->nr_netstrfns->nf_parse_addrlist(src->ls_str,
+                                                src->ls_len,
+                                                &addrrange->ar_numaddr_ranges);
+}
+
+/**
+ * Finds or creates struct nidrange.
+ *
+ * Checks if \a src is a valid network name, looks for corresponding
+ * nidrange on the ist of nidranges (\a nidlist), creates new struct
+ * nidrange if it is not found.
+ *
+ * \retval pointer to struct nidrange matching network specified via \a src
+ * \retval NULL if \a src does not match any network
+ */
+static struct nidrange *
+add_nidrange(const struct lstr *src,
+             struct list_head *nidlist)
+{
+        struct netstrfns *nf;
+        struct nidrange *nr;
+        int endlen;
+        unsigned netnum;
+
+        if (src->ls_len >= LNET_NIDSTR_SIZE)
+                return NULL;
+
+        nf = libcfs_namenum2netstrfns(src->ls_str);
+        if (nf == NULL)
+                return NULL;
+        endlen = src->ls_len - strlen(nf->nf_name);
+        if (endlen == 0)
+                /* network name only, e.g. "elan" or "tcp" */
+                netnum = 0;
+        else {
+                /* e.g. "elan25" or "tcp23", refuse to parse if
+                 * network name is not appended with decimal or
+                 * hexadecimal number */
+                if (!libcfs_str2num_check(src->ls_str + strlen(nf->nf_name),
+                                          endlen, &netnum,
+                                          0, MAX_NUMERIC_VALUE))
+                        return NULL;
+        }
+
+        list_for_each_entry(nr, nidlist, nr_link) {
+                if (nr->nr_netstrfns != nf)
+                        continue;
+                if (nr->nr_netnum != netnum)
+                        continue;
+                return nr;
+        }
+
+        LIBCFS_ALLOC(nr, sizeof(struct nidrange));
+        if (nr == NULL)
+                return NULL;
+        list_add_tail(&nr->nr_link, nidlist);
+        CFS_INIT_LIST_HEAD(&nr->nr_addrranges);
+        nr->nr_netstrfns = nf;
+        nr->nr_all = 0;
+        nr->nr_netnum = netnum;
+
+        return nr;
+}
+
+/**
+ * Parses <nidrange> token of the syntax.
+ *
+ * \retval 1 if \a src parses to <addrrange> '@' <net>
+ * \retval 0 otherwise
+ */
+static int
+parse_nidrange(struct lstr *src, struct list_head *nidlist)
+{
+        struct lstr addrrange, net, tmp;
+        struct nidrange *nr;
+
+        tmp = *src;
+        if (gettok(src, '@', &addrrange) == 0)
+                goto failed;
+
+        if (gettok(src, '@', &net) == 0 || src->ls_str != NULL)
+                goto failed;
+
+        nr = add_nidrange(&net, nidlist);
+        if (nr == NULL)
+                goto failed;
+
+        if (!parse_addrange(&addrrange, nr))
+                goto failed;
+
+        return 1;
+ failed:
+        CWARN("can't parse nidrange: \"%.*s\"\n", tmp.ls_len, tmp.ls_str);
+        return 0;
+}
+
+/**
+ * Frees range_expr structures of \a list.
+ *
+ * \retval none
+ */
+static void
+free_range_exprs(struct list_head *list)
+{
+        struct list_head *pos, *next;
+
+        list_for_each_safe(pos, next, list) {
+                list_del(pos);
+                LIBCFS_FREE(list_entry(pos, struct range_expr, re_link),
+                            sizeof(struct range_expr));
+        }
+}
+
+/**
+ * Frees numaddr_range structures of \a list.
+ *
+ * For each struct numaddr_range structure found on \a list it frees
+ * range_expr list attached to it and frees the numddr_range itself.
+ *
+ * \retval none
+ */
+static void
+free_numaddr_ranges(struct list_head *list)
+{
+        struct list_head *pos, *next;
+        struct numaddr_range *numaddr;
+
+        list_for_each_safe(pos, next, list) {
+                numaddr = list_entry(pos, struct numaddr_range, nar_link);
+                free_range_exprs(&numaddr->nar_range_exprs);
+                list_del(pos);
+                LIBCFS_FREE(numaddr, sizeof(struct numaddr_range));
+        }
+}
+
+/**
+ * Frees addrrange structures of \a list.
+ *
+ * For each struct addrrange structure found on \a list it frees
+ * numaddr_range list attached to it and frees the addrrange itself.
+ *
+ * \retval none
+ */
+static void
+free_addrranges(struct list_head *list)
+{
+        struct list_head *pos, *next;
+        struct addrrange *ar;
+
+        list_for_each_safe(pos, next, list) {
+                ar = list_entry(pos, struct addrrange, ar_link);
+                free_numaddr_ranges(&ar->ar_numaddr_ranges);
+                list_del(pos);
+                LIBCFS_FREE(ar, sizeof(struct addrrange));
+        }
+}
+
+/**
+ * Frees nidrange strutures of \a list.
+ *
+ * For each struct nidrange structure found on \a list it frees
+ * addrrange list attached to it and frees the nidrange itself.
+ *
+ * \retval none
+ */
+void
+cfs_free_nidlist(struct list_head *list)
+{
+        struct list_head *pos, *next;
+        struct nidrange *nr;
+
+        list_for_each_safe(pos, next, list) {
+                nr = list_entry(pos, struct nidrange, nr_link);
+                free_addrranges(&nr->nr_addrranges);
+                list_del(pos);
+                LIBCFS_FREE(nr, sizeof(struct nidrange));
+        }
+}
+
+/**
+ * Parses nid range list.
+ *
+ * Parses with rigorous syntax and overflow checking \a str into
+ * <nidrange> [ ' ' <nidrange> ], compiles \a str into set of
+ * structures and links that structure to \a nidlist. The resulting
+ * list can be used to match a NID againts set of NIDS defined by \a
+ * str.
+ * \see cfs_match_nid
+ *
+ * \retval 1 on success
+ * \retval 0 otherwise
+ */
+int
+cfs_parse_nidlist(char *str, int len, struct list_head *nidlist)
+{
+        struct lstr src, res;
+        int rc;
+        ENTRY;
+
+        src.ls_str = str;
+        src.ls_len = len;
+        CFS_INIT_LIST_HEAD(nidlist);
+        while (src.ls_str) {
+                rc = gettok(&src, ' ', &res);
+                if (rc == 0) {
+                        cfs_free_nidlist(nidlist);
+                        RETURN(0);
+                }
+                rc = parse_nidrange(&res, nidlist);
+                if (rc == 0) {
+                        cfs_free_nidlist(nidlist);
+                        RETURN(0);
+                }
+        }
+        RETURN(1);
+}
+
+/**
+ * Matches address (\a addr) against address set encoded in \a list.
+ *
+ * \see libcfs_num_match(), libcfs_ip_match()
+ *
+ * \retval 1 if \a addr matches
+ * \retval 0 otherwise
+ */
+static int
+match_numaddr(__u32 addr, struct list_head *list, int shift, __u32 mask)
+{
+        struct numaddr_range *numaddr;
+        struct range_expr *expr;
+        int ip, ok;
+        ENTRY;
+
+        list_for_each_entry(numaddr, list, nar_link) {
+                ip = (addr >> shift) & mask;
+                shift -= 8;
+                ok = 0;
+                list_for_each_entry(expr, &numaddr->nar_range_exprs, re_link) {
+                        if (ip >= expr->re_lo &&
+                            ip <= expr->re_hi &&
+                            ((ip - expr->re_lo) % expr->re_stride) == 0) {
+                                ok = 1;
+                                break;
+                        }
+                }
+                if (!ok)
+                        RETURN(0);
+        }
+        RETURN(1);
+}
+
+/*
+ * Nf_match_addr method for networks using numeric addresses
+ *
+ * \retval 1 on match
+ * \retval 0 otherwise
+ */
+static int
+libcfs_num_match(__u32 addr, struct list_head *numaddr)
+{
+        return match_numaddr(addr, numaddr, 0, 0xffffffff);
+}
+
+/*
+ * Nf_match_addr method for networks using ip addresses
+ *
+ * \retval 1 on match
+ * \retval 0 otherwise
+ */
+static int
+libcfs_ip_match(__u32 addr, struct list_head *numaddr)
+{
+        return match_numaddr(addr, numaddr, 24, 0xff);
+}
+
+/**
+ * Matches a nid (\a nid) against the compiled list of nidranges (\a nidlist).
+ *
+ * \see cfs_parse_nidlist()
+ *
+ * \retval 1 on match
+ * \retval 0  otherwises
+ */
+int cfs_match_nid(lnet_nid_t nid, struct list_head *nidlist)
+{
+        struct nidrange *nr;
+        struct addrrange *ar;
+        ENTRY;
+
+        list_for_each_entry(nr, nidlist, nr_link) {
+                if (nr->nr_netstrfns->nf_type != LNET_NETTYP(LNET_NIDNET(nid)))
+                        continue;
+                if (nr->nr_netnum != LNET_NETNUM(LNET_NIDNET(nid)))
+                        continue;
+                if (nr->nr_all)
+                        RETURN(1);
+                list_for_each_entry(ar, &nr->nr_addrranges, ar_link)
+                        if (nr->nr_netstrfns->nf_match_addr(LNET_NIDADDR(nid),
+                                                       &ar->ar_numaddr_ranges))
+                                RETURN(1);
+        }
+        RETURN(0);
+}
+
 #ifdef __KERNEL__
 
 EXPORT_SYMBOL(libcfs_isknown_lnd);
@@ -526,5 +1274,9 @@ EXPORT_SYMBOL(libcfs_str2net);
 EXPORT_SYMBOL(libcfs_str2nid);
 EXPORT_SYMBOL(libcfs_id2str);
 EXPORT_SYMBOL(libcfs_str2anynid);
+EXPORT_SYMBOL(cfs_iswhite);
+EXPORT_SYMBOL(cfs_free_nidlist);
+EXPORT_SYMBOL(cfs_parse_nidlist);
+EXPORT_SYMBOL(cfs_match_nid);
 
 #endif
index 43303dd..45bd89f 100644 (file)
@@ -86,31 +86,17 @@ lnet_issep (char c)
        }
 }
 
-int
-lnet_iswhite (char c)
-{
-       switch (c) {
-       case ' ':
-       case '\t':
-       case '\n':
-       case '\r':
-               return 1;
-       default:
-               return 0;
-       }
-}
-
 char *
 lnet_trimwhite(char *str)
 {
        char *end;
        
-       while (lnet_iswhite(*str))
+        while (cfs_iswhite(*str))
                str++;
        
        end = str + strlen(str);
        while (end > str) {
-               if (!lnet_iswhite(end[-1]))
+                if (!cfs_iswhite(end[-1]))
                        break;
                end--;
        }
@@ -394,7 +380,7 @@ lnet_str2tbs_sep (struct list_head *tbs, char *str)
        /* Split 'str' into separate commands */
        for (;;) {
                 /* skip leading whitespace */
-                while (lnet_iswhite(*str))
+                while (cfs_iswhite(*str))
                         str++;
                 
                /* scan for separator or comment */
@@ -411,7 +397,7 @@ lnet_str2tbs_sep (struct list_head *tbs, char *str)
                        }
                        
                         for (i = 0; i < nob; i++)
-                                if (lnet_iswhite(str[i]))
+                                if (cfs_iswhite(str[i]))
                                         ltb->ltb_text[i] = ' ';
                                 else
                                         ltb->ltb_text[i] = str[i];
@@ -586,7 +572,7 @@ lnet_parse_route (char *str, int *im_a_router)
        sep = str;
        for (;;) {
                /* scan for token start */
-               while (lnet_iswhite(*sep))
+                while (cfs_iswhite(*sep))
                        sep++;
                if (*sep == 0) {
                        if (ntokens < (got_hops ? 3 : 2))
@@ -598,7 +584,7 @@ lnet_parse_route (char *str, int *im_a_router)
                token = sep++;
 
                /* scan for token end */
-               while (*sep != 0 && !lnet_iswhite(*sep))
+                while (*sep != 0 && !cfs_iswhite(*sep))
                        sep++;
                if (*sep != 0)
                        *sep++ = 0;
@@ -946,7 +932,7 @@ lnet_match_network_tokens(char *net_entry, __u32 *ipaddrs, int nip)
         sep = tokens;
         for (;;) {
                 /* scan for token start */
-                while (lnet_iswhite(*sep))
+                while (cfs_iswhite(*sep))
                         sep++;
                 if (*sep == 0)
                         break;
@@ -954,7 +940,7 @@ lnet_match_network_tokens(char *net_entry, __u32 *ipaddrs, int nip)
                 token = sep++;
                 
                 /* scan for token end */
-                while (*sep != 0 && !lnet_iswhite(*sep))
+                while (*sep != 0 && !cfs_iswhite(*sep))
                         sep++;
                 if (*sep != 0)
                         *sep++ = 0;