Whamcloud - gitweb
LU-1778 libcfs: add a service that prints a nidlist
[fs/lustre-release.git] / libcfs / libcfs / nidstrings.c
index 15c0a67..a78dd85 100644 (file)
@@ -1,6 +1,4 @@
-/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
- * vim:expandtab:shiftwidth=8:tabstop=8:
- *
+/*
  * GPL HEADER START
  *
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -28,6 +26,8 @@
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
  * Author: Phil Schwan <phil@clusterfs.com>
  */
 
-#ifndef EXPORT_SYMTAB
-# define EXPORT_SYMTAB
-#endif
-
 #define DEBUG_SUBSYSTEM S_LNET
 
 #include <libcfs/libcfs.h>
@@ -68,18 +64,18 @@ static char      libcfs_nidstrings[LNET_NIDSTR_COUNT][LNET_NIDSTR_SIZE];
 static int       libcfs_nidstring_idx = 0;
 
 #ifdef __KERNEL__
-static cfs_spinlock_t libcfs_nidstring_lock;
+static spinlock_t libcfs_nidstring_lock;
 
 void libcfs_init_nidstrings (void)
 {
-        cfs_spin_lock_init(&libcfs_nidstring_lock);
+       spin_lock_init(&libcfs_nidstring_lock);
 }
 
-# define NIDSTR_LOCK(f)   cfs_spin_lock_irqsave(&libcfs_nidstring_lock, f)
-# define NIDSTR_UNLOCK(f) cfs_spin_unlock_irqrestore(&libcfs_nidstring_lock, f)
+# define NIDSTR_LOCK(f)   spin_lock_irqsave(&libcfs_nidstring_lock, f)
+# define NIDSTR_UNLOCK(f) spin_unlock_irqrestore(&libcfs_nidstring_lock, f)
 #else
-# define NIDSTR_LOCK(f)   (f=0)                 /* avoid unused var warnings */
-# define NIDSTR_UNLOCK(f) (f=0)
+# define NIDSTR_LOCK(f)   (f=sizeof(f))  /* avoid set-but-unused warnings */
+# define NIDSTR_UNLOCK(f) (f=sizeof(f))
 #endif
 
 static char *
@@ -105,85 +101,98 @@ 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, cfs_list_t *list);
-static int  libcfs_num_parse(char *str, int len, cfs_list_t *list);
-static int  libcfs_ip_match(__u32 addr, cfs_list_t *list);
-static int  libcfs_num_match(__u32 addr, cfs_list_t *list);
+static int  libcfs_num_parse(char *str, int len, struct list_head *list);
+static int  libcfs_num_match(__u32 addr, struct list_head *list);
+static int  libcfs_num_addr_range_print(char *buffer, int count,
+                                       struct list_head *list);
+static int  libcfs_ip_addr_range_print(char *buffer, int count,
+                                      struct list_head *list);
 
 struct netstrfns {
-        int          nf_type;
-        char        *nf_name;
-        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,
-                                        cfs_list_t *list);
-        int        (*nf_match_addr)(__u32 addr, cfs_list_t *list);
+       int      nf_type;
+       char    *nf_name;
+       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_print_addrlist)(char *buffer, int count,
+                                    struct list_head *list);
+       int     (*nf_match_addr)(__u32 addr, struct list_head *list);
 };
 
 static struct netstrfns  libcfs_netstrfns[] = {
-        {/* .nf_type      */  LOLND,
-         /* .nf_name      */  "lo",
-         /* .nf_modname   */  "klolnd",
-         /* .nf_addr2str  */  libcfs_decnum_addr2str,
-         /* .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_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_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_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_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_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_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_parse_addrlist*/  libcfs_ip_parse,
-         /* .nf_match_addr*/  libcfs_ip_match},
+       {/* .nf_type      */  LOLND,
+        /* .nf_name      */  "lo",
+        /* .nf_modname   */  "klolnd",
+        /* .nf_addr2str  */  libcfs_decnum_addr2str,
+        /* .nf_str2addr  */  libcfs_lo_str2addr,
+        /* .nf_parse_addr*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
+        /* .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_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
+       {/* .nf_type      */  O2IBLND,
+        /* .nf_name      */  "o2ib",
+        /* .nf_modname   */  "ko2iblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
+       {/* .nf_type      */  CIBLND,
+        /* .nf_name      */  "cib",
+        /* .nf_modname   */  "kciblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
+       {/* .nf_type      */  OPENIBLND,
+        /* .nf_name      */  "openib",
+        /* .nf_modname   */  "kopeniblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
+       {/* .nf_type      */  IIBLND,
+        /* .nf_name      */  "iib",
+        /* .nf_modname   */  "kiiblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
+       {/* .nf_type      */  VIBLND,
+        /* .nf_name      */  "vib",
+        /* .nf_modname   */  "kviblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
+       {/* .nf_type      */  RALND,
+        /* .nf_name      */  "ra",
+        /* .nf_modname   */  "kralnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
         {/* .nf_type      */  QSWLND,
          /* .nf_name      */  "elan",
          /* .nf_modname   */  "kqswlnd",
          /* .nf_addr2str  */  libcfs_decnum_addr2str,
          /* .nf_str2addr  */  libcfs_num_str2addr,
          /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
          /* .nf_match_addr*/  libcfs_num_match},
         {/* .nf_type      */  GMLND,
          /* .nf_name      */  "gm",
@@ -191,21 +200,40 @@ static struct netstrfns  libcfs_netstrfns[] = {
          /* .nf_addr2str  */  libcfs_hexnum_addr2str,
          /* .nf_str2addr  */  libcfs_num_str2addr,
          /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
          /* .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_parse_addrlist*/  libcfs_ip_parse,
-         /* .nf_match_addr*/  libcfs_ip_match},
+       {/* .nf_type      */  MXLND,
+        /* .nf_name      */  "mx",
+        /* .nf_modname   */  "kmxlnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
         {/* .nf_type      */  PTLLND,
          /* .nf_name      */  "ptl",
          /* .nf_modname   */  "kptllnd",
          /* .nf_addr2str  */  libcfs_decnum_addr2str,
          /* .nf_str2addr  */  libcfs_num_str2addr,
          /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
          /* .nf_match_addr*/  libcfs_num_match},
+        {/* .nf_type      */  GNILND,
+         /* .nf_name      */  "gni",
+         /* .nf_modname   */  "kgnilnd",
+         /* .nf_addr2str  */  libcfs_decnum_addr2str,
+         /* .nf_str2addr  */  libcfs_num_str2addr,
+         /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
+         /* .nf_match_addr*/  libcfs_num_match},
+       {/* .nf_type      */  GNIIPLND,
+        /* .nf_name      */  "gip",
+        /* .nf_modname   */  "kgnilnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/  cfs_ip_addr_match},
         /* placeholder for net0 alias.  It MUST BE THE LAST ENTRY */
         {/* .nf_type      */  -1},
 };
@@ -434,7 +462,7 @@ libcfs_nid2str(lnet_nid_t nid)
         int               nob;
 
         if (nid == LNET_NID_ANY)
-                return "LNET_NID_ANY";
+                return "<?>";
 
         nf = libcfs_lnd2netstrfns(lnd);
         str = libcfs_next_nidstring();
@@ -458,7 +486,7 @@ libcfs_nid2str(lnet_nid_t nid)
 static struct netstrfns *
 libcfs_str2net_internal(const char *str, __u32 *net)
 {
-        struct netstrfns *nf;
+       struct netstrfns *nf = NULL;
         int               nob;
         int               netnum;
         int               i;
@@ -565,10 +593,10 @@ libcfs_str2anynid(lnet_nid_t *nidp, const char *str)
  * <nidrange>        :== <addrrange> '@' <net>
  * <addrrange>       :== '*' |
  *                       <ipaddr_range> |
- *                       <numaddr_range>
- * <ipaddr_range>    :== <numaddr_range>.<numaddr_range>.<numaddr_range>.
- *                       <numaddr_range>
- * <numaddr_range>   :== <number> |
+ *                      <cfs_expr_list>
+ * <ipaddr_range>    :== <cfs_expr_list>.<cfs_expr_list>.<cfs_expr_list>.
+ *                      <cfs_expr_list>
+ * <cfs_expr_list>   :== <number> |
  *                       <expr_list>
  * <expr_list>       :== '[' <range_expr> [ ',' <range_expr>] ']'
  * <range_expr>      :== <number> |
@@ -576,360 +604,72 @@ libcfs_str2anynid(lnet_nid_t *nidp, const char *str)
  *                       <number> '-' <number> '/' <number>
  * <net>             :== <netname> | <netname><number>
  * <netname>         :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" |
- *                       "vib" | "ra" | "elan" | "gm" | "mx" | "ptl"
+ *                       "vib" | "ra" | "elan" | "mx" | "ptl"
  * \endverbatim
  */
 
 /**
- * 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.
-         */
-        cfs_list_t nr_link;
-        /**
-         * List head for addrrange::ar_link.
-         */
-        cfs_list_t 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;
+       /**
+        * 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.
-         */
-        cfs_list_t ar_link;
-        /**
-         * List head for numaddr_range::nar_link.
-         */
-        cfs_list_t ar_numaddr_ranges;
-};
-
-/**
- * Structure to represent \<numaddr_range\> token of the syntax.
- */
-struct numaddr_range {
-        /**
-         * Link to addrrange::ar_numaddr_ranges.
-         */
-        cfs_list_t nar_link;
-        /**
-         * List head for range_expr::re_link.
-         */
-        cfs_list_t nar_range_exprs;
-};
-
-/**
- * Structure to represent \<range_expr\> token of the syntax.
- */
-struct range_expr {
-        /**
-         * Link to numaddr_range::nar_range_exprs.
-         */
-        cfs_list_t re_link;
-        __u32 re_lo;
-        __u32 re_hi;
-        __u32 re_stride;
+       /**
+        * Link to nidrange::nr_addrranges.
+        */
+       struct list_head ar_link;
+       /**
+        * List head for cfs_expr_list::el_list.
+        */
+       struct list_head ar_numaddr_ranges;
 };
 
-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;
-}
-
-/**
- * Parses \<expr_list\> token of the syntax.
- *
- * \retval 1 if \a str parses to '[' \<range_expr\> [ ',' \<range_expr\>] ']'
- * \retval 0 otherwise
- */
-static int
-parse_expr_list(struct lstr *str, cfs_list_t *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;
-                cfs_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,
-          cfs_list_t *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;
-        cfs_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;
-                cfs_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
+ * \retval 0 if \a str parsed to numeric address
+ * \retval errno otherwise
  */
 static int
-libcfs_num_parse(char *str, int len, cfs_list_t *list)
+libcfs_num_parse(char *str, int len, struct list_head *list)
 {
-        return num_parse(str, len, list, 0, MAX_NUMERIC_VALUE);
-}
+       struct cfs_expr_list *el;
+       int     rc;
 
-/**
- * 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,
-                cfs_list_t *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 ++;
-        }
+       rc = cfs_expr_list_parse(str, len, 0, MAX_NUMERIC_VALUE, &el);
+       if (rc == 0)
+               list_add_tail(&el->el_link, list);
 
-        return (i == 4) ? 1 : 0;
+       return rc;
 }
 
 /**
@@ -938,28 +678,28 @@ libcfs_ip_parse(char *str, int len,
  * 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
+ * \retval 0 if \a src parses to '*' | \<ipaddr_range\> | \<cfs_expr_list\>
+ * \retval -errno otherwise
  */
 static int
-parse_addrange(const struct lstr *src, struct nidrange *nidrange)
+parse_addrange(const struct cfs_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;
-        cfs_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);
+       struct addrrange *addrrange;
+
+       if (src->ls_len == 1 && src->ls_str[0] == '*') {
+               nidrange->nr_all = 1;
+               return 0;
+       }
+
+       LIBCFS_ALLOC(addrrange, sizeof(struct addrrange));
+       if (addrrange == NULL)
+               return -ENOMEM;
+       list_add_tail(&addrrange->ar_link, &nidrange->nr_addrranges);
+       INIT_LIST_HEAD(&addrrange->ar_numaddr_ranges);
+
+       return nidrange->nr_netstrfns->nf_parse_addrlist(src->ls_str,
+                                               src->ls_len,
+                                               &addrrange->ar_numaddr_ranges);
 }
 
 /**
@@ -973,8 +713,8 @@ parse_addrange(const struct lstr *src, struct nidrange *nidrange)
  * \retval NULL if \a src does not match any network
  */
 static struct nidrange *
-add_nidrange(const struct lstr *src,
-             cfs_list_t *nidlist)
+add_nidrange(const struct cfs_lstr *src,
+            struct list_head *nidlist)
 {
         struct netstrfns *nf;
         struct nidrange *nr;
@@ -995,30 +735,29 @@ add_nidrange(const struct lstr *src,
                 /* 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))
+               if (!cfs_str2num_check(src->ls_str + strlen(nf->nf_name),
+                                      endlen, &netnum, 0, MAX_NUMERIC_VALUE))
                         return NULL;
         }
 
-        cfs_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;
-        cfs_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;
+       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);
+       INIT_LIST_HEAD(&nr->nr_addrranges);
+       nr->nr_netstrfns = nf;
+       nr->nr_all = 0;
+       nr->nr_netnum = netnum;
+
+       return nr;
 }
 
 /**
@@ -1028,23 +767,25 @@ add_nidrange(const struct lstr *src,
  * \retval 0 otherwise
  */
 static int
-parse_nidrange(struct lstr *src, cfs_list_t *nidlist)
+parse_nidrange(struct cfs_lstr *src, struct list_head *nidlist)
 {
-        struct lstr addrrange, net, tmp;
+       struct cfs_lstr addrrange;
+       struct cfs_lstr net;
+       struct cfs_lstr tmp;
         struct nidrange *nr;
 
         tmp = *src;
-        if (gettok(src, '@', &addrrange) == 0)
+       if (cfs_gettok(src, '@', &addrrange) == 0)
                 goto failed;
 
-        if (gettok(src, '@', &net) == 0 || src->ls_str != NULL)
+       if (cfs_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))
+       if (parse_addrange(&addrrange, nr) != 0)
                 goto failed;
 
         return 1;
@@ -1054,64 +795,25 @@ parse_nidrange(struct lstr *src, cfs_list_t *nidlist)
 }
 
 /**
- * Frees range_expr structures of \a list.
- *
- * \retval none
- */
-static void
-free_range_exprs(cfs_list_t *list)
-{
-        cfs_list_t *pos, *next;
-
-        cfs_list_for_each_safe(pos, next, list) {
-                cfs_list_del(pos);
-                LIBCFS_FREE(cfs_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(cfs_list_t *list)
-{
-        cfs_list_t *pos, *next;
-        struct numaddr_range *numaddr;
-
-        cfs_list_for_each_safe(pos, next, list) {
-                numaddr = cfs_list_entry(pos, struct numaddr_range, nar_link);
-                free_range_exprs(&numaddr->nar_range_exprs);
-                cfs_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.
+ * cfs_expr_list list attached to it and frees the addrrange itself.
  *
  * \retval none
  */
 static void
-free_addrranges(cfs_list_t *list)
+free_addrranges(struct list_head *list)
 {
-        cfs_list_t *pos, *next;
-        struct addrrange *ar;
-
-        cfs_list_for_each_safe(pos, next, list) {
-                ar = cfs_list_entry(pos, struct addrrange, ar_link);
-                free_numaddr_ranges(&ar->ar_numaddr_ranges);
-                cfs_list_del(pos);
-                LIBCFS_FREE(ar, sizeof(struct addrrange));
-        }
+       while (!list_empty(list)) {
+               struct addrrange *ar;
+
+               ar = list_entry(list->next, struct addrrange, ar_link);
+
+               cfs_expr_list_free_list(&ar->ar_numaddr_ranges);
+               list_del(&ar->ar_link);
+               LIBCFS_FREE(ar, sizeof(struct addrrange));
+       }
 }
 
 /**
@@ -1123,17 +825,17 @@ free_addrranges(cfs_list_t *list)
  * \retval none
  */
 void
-cfs_free_nidlist(cfs_list_t *list)
+cfs_free_nidlist(struct list_head *list)
 {
-        cfs_list_t *pos, *next;
-        struct nidrange *nr;
-
-        cfs_list_for_each_safe(pos, next, list) {
-                nr = cfs_list_entry(pos, struct nidrange, nr_link);
-                free_addrranges(&nr->nr_addrranges);
-                cfs_list_del(pos);
-                LIBCFS_FREE(nr, sizeof(struct nidrange));
-        }
+       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));
+       }
 }
 
 /**
@@ -1150,116 +852,179 @@ cfs_free_nidlist(cfs_list_t *list)
  * \retval 0 otherwise
  */
 int
-cfs_parse_nidlist(char *str, int len, cfs_list_t *nidlist)
+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);
+       struct cfs_lstr src;
+       struct cfs_lstr res;
+       int rc;
+       ENTRY;
+
+       src.ls_str = str;
+       src.ls_len = len;
+       INIT_LIST_HEAD(nidlist);
+       while (src.ls_str) {
+               rc = cfs_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()
+/*
+ * Nf_match_addr method for networks using numeric addresses
  *
- * \retval 1 if \a addr matches
+ * \retval 1 on match
  * \retval 0 otherwise
  */
 static int
-match_numaddr(__u32 addr, cfs_list_t *list, int shift, __u32 mask)
+libcfs_num_match(__u32 addr, struct list_head *numaddr)
 {
-        struct numaddr_range *numaddr;
-        struct range_expr *expr;
-        int ip, ok;
-        ENTRY;
-
-        cfs_list_for_each_entry(numaddr, list, nar_link) {
-                ip = (addr >> shift) & mask;
-                shift -= 8;
-                ok = 0;
-                cfs_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);
+       struct cfs_expr_list *el;
+
+       LASSERT(!list_empty(numaddr));
+       el = list_entry(numaddr->next, struct cfs_expr_list, el_link);
+
+       return cfs_expr_list_match(addr, el);
 }
 
-/*
- * Nf_match_addr method for networks using numeric addresses
+/**
+ * Matches a nid (\a nid) against the compiled list of nidranges (\a nidlist).
+ *
+ * \see cfs_parse_nidlist()
  *
  * \retval 1 on match
- * \retval 0 otherwise
+ * \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);
+}
+
 static int
-libcfs_num_match(__u32 addr, cfs_list_t *numaddr)
+libcfs_num_addr_range_print(char *buffer, int count, struct list_head *list)
 {
-        return match_numaddr(addr, numaddr, 0, 0xffffffff);
+       int i = 0, j = 0;
+       struct cfs_expr_list *el;
+
+       list_for_each_entry(el, list, el_link) {
+               LASSERT(j++ < 1);
+               i += cfs_expr_list_print(buffer + i, count - i, el);
+       }
+       return i;
 }
 
-/*
- * Nf_match_addr method for networks using ip addresses
+static int
+libcfs_ip_addr_range_print(char *buffer, int count, struct list_head *list)
+{
+       int i = 0, j = 0;
+       struct cfs_expr_list *el;
+
+       list_for_each_entry(el, list, el_link) {
+               LASSERT(j++ < 4);
+               if (i != 0)
+                       i += cfs_snprintf(buffer + i, count - i, ".");
+               i += cfs_expr_list_print(buffer + i, count - i, el);
+       }
+       return i;
+}
+
+
+/**
+ * Print the network part of the nidrange \a nr into the specified \a buffer.
  *
- * \retval 1 on match
- * \retval 0 otherwise
+ * \retval number of characters written
  */
 static int
-libcfs_ip_match(__u32 addr, cfs_list_t *numaddr)
+cfs_print_network(char *buffer, int count, struct nidrange *nr)
 {
-        return match_numaddr(addr, numaddr, 24, 0xff);
+       struct netstrfns *nf = nr->nr_netstrfns;
+
+       if (nr->nr_netnum == 0)
+               return cfs_snprintf(buffer, count, "@%s", nf->nf_name);
+       else
+               return cfs_snprintf(buffer, count, "@%s%u",
+                                   nf->nf_name, nr->nr_netnum);
 }
 
+
 /**
- * Matches a nid (\a nid) against the compiled list of nidranges (\a nidlist).
+ * Print a list of addrrange (\a addrranges) into the specified \a buffer.
+ * At max \a count characters can be printed into \a buffer.
  *
- * \see cfs_parse_nidlist()
+ * \retval number of characters written
+ */
+static int
+cfs_print_addrranges(char *buffer, int count, struct list_head *addrranges,
+                    struct nidrange *nr)
+{
+       int i = 0;
+       struct addrrange *ar;
+       struct netstrfns *nf = nr->nr_netstrfns;
+
+       list_for_each_entry(ar, addrranges, ar_link) {
+               if (i != 0)
+                       i += cfs_snprintf(buffer + i, count - i, " ");
+               i += nf->nf_print_addrlist(buffer + i, count - i,
+                                          &ar->ar_numaddr_ranges);
+               i += cfs_print_network(buffer + i, count - i, nr);
+       }
+       return i;
+}
+
+
+/**
+ * Print a list of nidranges (\a nidlist) into the specified \a buffer.
+ * At max \a count characters can be printed into \a buffer.
+ * Nidranges are separated by a space character.
  *
- * \retval 1 on match
- * \retval 0  otherwises
+ * \retval number of characters written
  */
-int cfs_match_nid(lnet_nid_t nid, cfs_list_t *nidlist)
+int cfs_print_nidlist(char *buffer, int count, struct list_head *nidlist)
 {
-        struct nidrange *nr;
-        struct addrrange *ar;
-        ENTRY;
-
-        cfs_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);
-                cfs_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);
+       int i = 0;
+       struct nidrange *nr;
+       ENTRY;
+
+       if (count <= 0)
+               RETURN(0);
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               if (i != 0)
+                       i += cfs_snprintf(buffer + i, count - i, " ");
+
+               if (nr->nr_all != 0) {
+                       LASSERT(list_empty(&nr->nr_addrranges));
+                       i += cfs_snprintf(buffer + i, count - i, "*");
+                       i += cfs_print_network(buffer + i, count - i, nr);
+               } else {
+                       i += cfs_print_addrranges(buffer + i, count - i,
+                                                 &nr->nr_addrranges, nr);
+               }
+       }
+       RETURN(i);
 }
 
 #ifdef __KERNEL__
@@ -1274,9 +1039,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_print_nidlist);
 EXPORT_SYMBOL(cfs_match_nid);
 
 #endif