Whamcloud - gitweb
LU-5577 obdclass: change loop indexes to unsigned
[fs/lustre-release.git] / libcfs / libcfs / nidstrings.c
index a78dd85..10e8f8d 100644 (file)
 #define DEBUG_SUBSYSTEM S_LNET
 
 #include <libcfs/libcfs.h>
-#include <lnet/lnet.h>
+#include <lnet/nidstr.h>
 #ifndef __KERNEL__
 #ifdef HAVE_GETHOSTBYNAME
 # include <netdb.h>
 #endif
 #endif
 
+/* max value for numeric network address */
+#define MAX_NUMERIC_VALUE 0xffffffff
+
+#define IPSTRING_LENGTH 16
+
 /* CAVEAT VENDITOR! Keep the canonical string representation of nets/nids
  * consistent in all conversion functions.  Some code fragments are copied
  * around for the sake of clarity...
@@ -98,6 +103,8 @@ libcfs_next_nidstring (void)
 static int  libcfs_lo_str2addr(const char *str, int nob, __u32 *addr);
 static void libcfs_ip_addr2str(__u32 addr, char *str);
 static int  libcfs_ip_str2addr(const char *str, int nob, __u32 *addr);
+static bool cfs_ip_is_contiguous(struct list_head *nidlist);
+static void cfs_ip_min_max(struct list_head *nidlist, __u32 *min, __u32 *max);
 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);
@@ -107,6 +114,8 @@ 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);
+static bool cfs_num_is_contiguous(struct list_head *nidlist);
+static void cfs_num_min_max(struct list_head *nidlist, __u32 *min, __u32 *max);
 
 struct netstrfns {
        int      nf_type;
@@ -119,6 +128,9 @@ struct netstrfns {
        int     (*nf_print_addrlist)(char *buffer, int count,
                                     struct list_head *list);
        int     (*nf_match_addr)(__u32 addr, struct list_head *list);
+       bool    (*nf_is_contiguous)(struct list_head *nidlist);
+       void    (*nf_min_max)(struct list_head *nidlist, __u32 *min_nid,
+                               __u32 *max_nid);
 };
 
 static struct netstrfns  libcfs_netstrfns[] = {
@@ -129,7 +141,9 @@ static struct netstrfns  libcfs_netstrfns[] = {
         /* .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_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
        {/* .nf_type      */  SOCKLND,
         /* .nf_name      */  "tcp",
         /* .nf_modname   */  "ksocklnd",
@@ -137,7 +151,9 @@ static struct netstrfns  libcfs_netstrfns[] = {
         /* .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_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
        {/* .nf_type      */  O2IBLND,
         /* .nf_name      */  "o2ib",
         /* .nf_modname   */  "ko2iblnd",
@@ -145,7 +161,9 @@ static struct netstrfns  libcfs_netstrfns[] = {
         /* .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_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
        {/* .nf_type      */  CIBLND,
         /* .nf_name      */  "cib",
         /* .nf_modname   */  "kciblnd",
@@ -153,7 +171,9 @@ static struct netstrfns  libcfs_netstrfns[] = {
         /* .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_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
        {/* .nf_type      */  OPENIBLND,
         /* .nf_name      */  "openib",
         /* .nf_modname   */  "kopeniblnd",
@@ -161,7 +181,9 @@ static struct netstrfns  libcfs_netstrfns[] = {
         /* .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_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
        {/* .nf_type      */  IIBLND,
         /* .nf_name      */  "iib",
         /* .nf_modname   */  "kiiblnd",
@@ -169,7 +191,9 @@ static struct netstrfns  libcfs_netstrfns[] = {
         /* .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_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
        {/* .nf_type      */  VIBLND,
         /* .nf_name      */  "vib",
         /* .nf_modname   */  "kviblnd",
@@ -177,7 +201,9 @@ static struct netstrfns  libcfs_netstrfns[] = {
         /* .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_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
        {/* .nf_type      */  RALND,
         /* .nf_name      */  "ra",
         /* .nf_modname   */  "kralnd",
@@ -185,57 +211,71 @@ static struct netstrfns  libcfs_netstrfns[] = {
         /* .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_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .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",
-         /* .nf_modname   */  "kgmlnd",
-         /* .nf_addr2str  */  libcfs_hexnum_addr2str,
-         /* .nf_str2addr  */  libcfs_num_str2addr,
-         /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .nf_type      */      GMLND,
+        /* .nf_name      */      "gm",
+        /* .nf_modname   */      "kgmlnd",
+        /* .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_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .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_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .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_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .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_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .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},
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+        /* placeholder for net0 alias.  It MUST BE THE LAST ENTRY */
+       {/* .nf_type      */  -1},
 };
 
 const int libcfs_nnetstrfns = sizeof(libcfs_netstrfns)/sizeof(libcfs_netstrfns[0]);
@@ -276,11 +316,11 @@ libcfs_ip_addr2str(__u32 addr, char *str)
 int
 libcfs_ip_str2addr(const char *str, int nob, __u32 *addr)
 {
-        int   a;
-        int   b;
-        int   c;
-        int   d;
-        int   n = nob;                          /* XscanfX */
+       unsigned int    a;
+       unsigned int    b;
+       unsigned int    c;
+       unsigned int    d;
+       int             n = nob; /* XscanfX */
 
         /* numeric IP? */
         if (sscanf(str, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) >= 4 &&
@@ -487,9 +527,9 @@ static struct netstrfns *
 libcfs_str2net_internal(const char *str, __u32 *net)
 {
        struct netstrfns *nf = NULL;
-        int               nob;
-        int               netnum;
-        int               i;
+       int               nob;
+       unsigned int      netnum;
+       int               i;
 
         for (i = 0; i < libcfs_nnetstrfns; i++) {
                 nf = &libcfs_netstrfns[i];
@@ -857,7 +897,6 @@ cfs_parse_nidlist(char *str, int len, struct list_head *nidlist)
        struct cfs_lstr src;
        struct cfs_lstr res;
        int rc;
-       ENTRY;
 
        src.ls_str = str;
        src.ls_len = len;
@@ -866,15 +905,15 @@ cfs_parse_nidlist(char *str, int len, struct list_head *nidlist)
                rc = cfs_gettok(&src, ' ', &res);
                if (rc == 0) {
                        cfs_free_nidlist(nidlist);
-                       RETURN(0);
+                       return 0;
                }
                rc = parse_nidrange(&res, nidlist);
                if (rc == 0) {
                        cfs_free_nidlist(nidlist);
-                       RETURN(0);
+                       return 0;
                }
        }
-       RETURN(1);
+       return 1;
 }
 
 /*
@@ -906,7 +945,6 @@ 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)))
@@ -914,13 +952,13 @@ int cfs_match_nid(lnet_nid_t nid, struct list_head *nidlist)
                if (nr->nr_netnum != LNET_NETNUM(LNET_NIDNET(nid)))
                        continue;
                if (nr->nr_all)
-                       RETURN(1);
+                       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 1;
        }
-       RETURN(0);
+       return 0;
 }
 
 static int
@@ -1006,10 +1044,9 @@ int cfs_print_nidlist(char *buffer, int count, struct list_head *nidlist)
 {
        int i = 0;
        struct nidrange *nr;
-       ENTRY;
 
        if (count <= 0)
-               RETURN(0);
+               return 0;
 
        list_for_each_entry(nr, nidlist, nr_link) {
                if (i != 0)
@@ -1024,7 +1061,319 @@ int cfs_print_nidlist(char *buffer, int count, struct list_head *nidlist)
                                                  &nr->nr_addrranges, nr);
                }
        }
-       RETURN(i);
+       return i;
+}
+
+/**
+ * Determines minimum and maximum addresses for a single
+ * numeric address range
+ *
+ * \param      ar
+ * \param      min_nid
+ * \param      max_nid
+ */
+static void cfs_ip_ar_min_max(struct addrrange *ar, __u32 *min_nid,
+                             __u32 *max_nid)
+{
+       struct cfs_expr_list    *el;
+       struct cfs_range_expr   *re;
+       __u32                   tmp_ip_addr = 0;
+       unsigned int            min_ip[4] = {0};
+       unsigned int            max_ip[4] = {0};
+       int                     re_count = 0;
+
+       list_for_each_entry(el, &ar->ar_numaddr_ranges, el_link) {
+               list_for_each_entry(re, &el->el_exprs, re_link) {
+                       min_ip[re_count] = re->re_lo;
+                       max_ip[re_count] = re->re_hi;
+                       re_count++;
+               }
+       }
+
+       tmp_ip_addr = ((min_ip[0] << 24) | (min_ip[1] << 16) |
+                      (min_ip[2] << 8) | min_ip[3]);
+
+       if (min_nid != NULL)
+               *min_nid = tmp_ip_addr;
+
+       tmp_ip_addr = ((max_ip[0] << 24) | (max_ip[1] << 16) |
+                      (max_ip[2] << 8) | max_ip[3]);
+
+       if (max_nid != NULL)
+               *max_nid = tmp_ip_addr;
+}
+
+/**
+ * Determines minimum and maximum addresses for a single
+ * numeric address range
+ *
+ * \param      ar
+ * \param      min_nid
+ * \param      max_nid
+ */
+static void cfs_num_ar_min_max(struct addrrange *ar, __u32 *min_nid,
+                              __u32 *max_nid)
+{
+       struct cfs_expr_list    *el;
+       struct cfs_range_expr   *re;
+       unsigned int            min_addr = 0;
+       unsigned int            max_addr = 0;
+
+       list_for_each_entry(el, &ar->ar_numaddr_ranges, el_link) {
+               list_for_each_entry(re, &el->el_exprs, re_link) {
+                       if (re->re_lo < min_addr || min_addr == 0)
+                               min_addr = re->re_lo;
+                       if (re->re_hi > max_addr)
+                               max_addr = re->re_hi;
+               }
+       }
+
+       if (min_nid != NULL)
+               *min_nid = min_addr;
+       if (max_nid != NULL)
+               *max_nid = max_addr;
+}
+
+/**
+ * Determines whether an expression list in an nidrange contains exactly
+ * one contiguous address range. Calls the correct netstrfns for the LND
+ *
+ * \param      *nidlist
+ *
+ * \retval     true if contiguous
+ * \retval     false if not contiguous
+ */
+bool cfs_nidrange_is_contiguous(struct list_head *nidlist)
+{
+       struct nidrange         *nr;
+       struct netstrfns        *nf = NULL;
+       char                    *lndname = NULL;
+       int                     netnum = -1;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               nf = nr->nr_netstrfns;
+               if (lndname == NULL)
+                       lndname = nf->nf_name;
+               if (netnum == -1)
+                       netnum = nr->nr_netnum;
+
+               if (strcmp(lndname, nf->nf_name) != 0 ||
+                   netnum != nr->nr_netnum)
+                       return false;
+       }
+
+       if (nf == NULL)
+               return false;
+
+       if (!nf->nf_is_contiguous(nidlist))
+               return false;
+
+       return true;
+}
+EXPORT_SYMBOL(cfs_nidrange_is_contiguous);
+
+/**
+ * Determines whether an expression list in an num nidrange contains exactly
+ * one contiguous address range.
+ *
+ * \param      *nidlist
+ *
+ * \retval     true if contiguous
+ * \retval     false if not contiguous
+ */
+static bool cfs_num_is_contiguous(struct list_head *nidlist)
+{
+       struct nidrange         *nr;
+       struct addrrange        *ar;
+       struct cfs_expr_list    *el;
+       struct cfs_range_expr   *re;
+       int                     last_hi = 0;
+       __u32                   last_end_nid = 0;
+       __u32                   current_start_nid = 0;
+       __u32                   current_end_nid = 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link) {
+                       cfs_num_ar_min_max(ar, &current_start_nid,
+                                          &current_end_nid);
+                       if (last_end_nid != 0 &&
+                           (current_start_nid - last_end_nid != 1))
+                                       return false;
+                       last_end_nid = current_end_nid;
+                       list_for_each_entry(el, &ar->ar_numaddr_ranges,
+                                           el_link) {
+                               list_for_each_entry(re, &el->el_exprs,
+                                                   re_link) {
+                                       if (re->re_stride > 1)
+                                               return false;
+                                       else if (last_hi != 0 &&
+                                                re->re_hi - last_hi != 1)
+                                               return false;
+                                       last_hi = re->re_hi;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Determines whether an expression list in an ip nidrange contains exactly
+ * one contiguous address range.
+ *
+ * \param      *nidlist
+ *
+ * \retval     true if contiguous
+ * \retval     false if not contiguous
+ */
+static bool cfs_ip_is_contiguous(struct list_head *nidlist)
+{
+       struct nidrange         *nr;
+       struct addrrange        *ar;
+       struct cfs_expr_list    *el;
+       struct cfs_range_expr   *re;
+       int                     expr_count;
+       int                     last_hi = 255;
+       int                     last_diff = 0;
+       __u32                   last_end_nid = 0;
+       __u32                   current_start_nid = 0;
+       __u32                   current_end_nid = 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link) {
+                       last_hi = 255;
+                       last_diff = 0;
+                       cfs_ip_ar_min_max(ar, &current_start_nid,
+                                         &current_end_nid);
+                       if (last_end_nid != 0 &&
+                           (current_start_nid - last_end_nid != 1))
+                                       return false;
+                       last_end_nid = current_end_nid;
+                       list_for_each_entry(el,
+                                           &ar->ar_numaddr_ranges,
+                                           el_link) {
+                               expr_count = 0;
+                               list_for_each_entry(re, &el->el_exprs,
+                                                   re_link) {
+                                       expr_count++;
+                                       if (re->re_stride > 1 ||
+                                           (last_diff > 0 && last_hi != 255) ||
+                                           (last_diff > 0 && last_hi == 255 &&
+                                            re->re_lo > 0))
+                                               return false;
+                                       last_hi = re->re_hi;
+                                       last_diff = re->re_hi - re->re_lo;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Takes a linked list of nidrange expressions, determines the minimum
+ * and maximum nid and creates appropriate nid structures
+ *
+ * \param      *nidlist
+ * \param      *min_nid
+ * \param      *max_nid
+ */
+void cfs_nidrange_find_min_max(struct list_head *nidlist, char *min_nid,
+                              char *max_nid, int nidstr_length)
+{
+       struct nidrange         *nr;
+       struct netstrfns        *nf = NULL;
+       int                     netnum = -1;
+       __u32                   min_addr;
+       __u32                   max_addr;
+       char                    *lndname = NULL;
+       char                    min_addr_str[IPSTRING_LENGTH];
+       char                    max_addr_str[IPSTRING_LENGTH];
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               nf = nr->nr_netstrfns;
+               lndname = nf->nf_name;
+               if (netnum == -1)
+                       netnum = nr->nr_netnum;
+
+               nf->nf_min_max(nidlist, &min_addr, &max_addr);
+       }
+       nf->nf_addr2str(min_addr, min_addr_str);
+       nf->nf_addr2str(max_addr, max_addr_str);
+
+       snprintf(min_nid, nidstr_length, "%s@%s%d", min_addr_str, lndname,
+                netnum);
+       snprintf(max_nid, nidstr_length, "%s@%s%d", max_addr_str, lndname,
+                netnum);
+}
+EXPORT_SYMBOL(cfs_nidrange_find_min_max);
+
+/**
+ * Determines the min and max NID values for num LNDs
+ *
+ * \param      *nidlist
+ * \param      *min_nid
+ * \param      *max_nid
+ */
+static void cfs_num_min_max(struct list_head *nidlist, __u32 *min_nid,
+                           __u32 *max_nid)
+{
+       struct nidrange         *nr;
+       struct addrrange        *ar;
+       unsigned int            tmp_min_addr = 0;
+       unsigned int            tmp_max_addr = 0;
+       unsigned int            min_addr = 0;
+       unsigned int            max_addr = 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link) {
+                       cfs_num_ar_min_max(ar, &tmp_min_addr,
+                                          &tmp_max_addr);
+                       if (tmp_min_addr < min_addr || min_addr == 0)
+                               min_addr = tmp_min_addr;
+                       if (tmp_max_addr > max_addr)
+                               max_addr = tmp_min_addr;
+               }
+       }
+       *max_nid = max_addr;
+       *min_nid = min_addr;
+}
+
+/**
+ * Takes an nidlist and determines the minimum and maximum
+ * ip addresses.
+ *
+ * \param      *nidlist
+ * \param      *min_nid
+ * \param      *max_nid
+ */
+static void cfs_ip_min_max(struct list_head *nidlist, __u32 *min_nid,
+                          __u32 *max_nid)
+{
+       struct nidrange         *nr;
+       struct addrrange        *ar;
+       __u32                   tmp_min_ip_addr = 0;
+       __u32                   tmp_max_ip_addr = 0;
+       __u32                   min_ip_addr = 0;
+       __u32                   max_ip_addr = 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link) {
+                       cfs_ip_ar_min_max(ar, &tmp_min_ip_addr,
+                                         &tmp_max_ip_addr);
+                       if (tmp_min_ip_addr < min_ip_addr || min_ip_addr == 0)
+                               min_ip_addr = tmp_min_ip_addr;
+                       if (tmp_max_ip_addr > max_ip_addr)
+                               max_ip_addr = tmp_max_ip_addr;
+               }
+       }
+
+       if (min_nid != NULL)
+               *min_nid = min_ip_addr;
+       if (max_nid != NULL)
+               *max_nid = max_ip_addr;
 }
 
 #ifdef __KERNEL__