Whamcloud - gitweb
LU-11893 lnet: consoldate secondary IP address handling 93/34993/8
authorJames Simmons <jsimmons@infradead.org>
Tue, 2 Jul 2019 13:11:15 +0000 (09:11 -0400)
committerOleg Drokin <green@whamcloud.com>
Fri, 12 Jul 2019 05:19:09 +0000 (05:19 +0000)
The last piece of code with broken secondary IP address
support is lnet_parse_ip2nets(). We could fix it like
o2iblnd or socklnd was done but since the LND drivers
resolved those issues instead we can move the handling
out of the LND drivers into one place in the LNet core.
To do this we introduce struct lnet_inetdev which is
a collection of data that the current LNet layer requires.
The new function lnet_inet_enumerate() is used to collect
this information.

Change-Id: I0c532caa3cf6b2178eb1ab65e55e5883d408a185
Signed-off-by: James Simmons <uja.ornl@yahoo.com>
Reviewed-on: https://review.whamcloud.com/34993
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Olaf Weber <olaf.weber@hpe.com>
Reviewed-by: Amir Shehata <ashehata@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lnet/include/lnet/lib-lnet.h
lnet/klnds/gnilnd/gnilnd_aries.h
lnet/klnds/o2iblnd/o2iblnd.c
lnet/klnds/socklnd/socklnd.c
lnet/lnet/config.c

index 52aeb92..1606dba 100644 (file)
@@ -40,9 +40,7 @@
 /* LNET has 0xeXXX */
 #define CFS_FAIL_PTLRPC_OST_BULK_CB2   0xe000
 
-#ifndef __KERNEL__
-# error This include is only for kernel use.
-#endif
+#include <linux/netdevice.h>
 
 #include <libcfs/libcfs.h>
 #include <lnet/api.h>
@@ -840,6 +838,15 @@ int lnet_acceptor_port(void);
 int lnet_acceptor_start(void);
 void lnet_acceptor_stop(void);
 
+struct lnet_inetdev {
+       u32     li_cpt;
+       u32     li_flags;
+       u32     li_ipaddr;
+       u32     li_netmask;
+       char    li_name[IFNAMSIZ];
+};
+
+int lnet_inet_enumerate(struct lnet_inetdev **dev_list);
 int lnet_sock_setbuf(struct socket *socket, int txbufsize, int rxbufsize);
 int lnet_sock_getbuf(struct socket *socket, int *txbufsize, int *rxbufsize);
 int lnet_sock_getaddr(struct socket *socket, bool remote, __u32 *ip, int *port);
index d9698b6..431442f 100644 (file)
@@ -24,7 +24,6 @@
 #ifndef _GNILND_ARIES_H
 #define _GNILND_ARIES_H
 
-/* for lnet_ipif_query */
 #include <lnet/lib-lnet.h>
 
 #ifndef _GNILND_HSS_OPS_H
index 29e82d7..d37237a 100644 (file)
@@ -2877,80 +2877,6 @@ kiblnd_destroy_dev(struct kib_dev *dev)
         LIBCFS_FREE(dev, sizeof(*dev));
 }
 
-static struct kib_dev *
-kiblnd_create_dev(char *ifname)
-{
-       struct net_device *netdev;
-       struct kib_dev *dev = NULL;
-       int flags;
-       int rc;
-
-       rtnl_lock();
-       for_each_netdev(&init_net, netdev) {
-               struct in_device *in_dev;
-
-               if (strcmp(netdev->name, "lo") == 0) /* skip the loopback IF */
-                       continue;
-
-               flags = dev_get_flags(netdev);
-               if (!(flags & IFF_UP)) {
-                       CWARN("Can't query IPoIB interface %s: it's down\n",
-                             netdev->name);
-                       continue;
-               }
-
-               in_dev = __in_dev_get_rtnl(netdev);
-               if (!in_dev) {
-                       CWARN("Interface %s has no IPv4 status.\n",
-                             netdev->name);
-                       continue;
-               }
-
-               for_ifa(in_dev)
-                       if (strcmp(ifname, ifa->ifa_label) == 0) {
-                               LIBCFS_ALLOC(dev, sizeof(*dev));
-                               if (!dev)
-                                       goto unlock;
-
-                               dev->ibd_can_failover = !!(flags & IFF_MASTER);
-                               dev->ibd_ifip = ntohl(ifa->ifa_local);
-
-                               INIT_LIST_HEAD(&dev->ibd_nets);
-                               INIT_LIST_HEAD(&dev->ibd_list); /* not yet in kib_devs */
-                               INIT_LIST_HEAD(&dev->ibd_fail_list);
-                               break;
-                       }
-               endfor_ifa(in_dev);
-       }
-       rtnl_unlock();
-
-       if (!dev) {
-               CERROR("Can't find any usable interfaces\n");
-               return NULL;
-       }
-
-       if (dev->ibd_ifip == 0) {
-               CERROR("Can't initialize device: no IP address\n");
-               goto free_dev;
-       }
-       strcpy(&dev->ibd_ifname[0], ifname);
-
-       /* initialize the device */
-       rc = kiblnd_dev_failover(dev);
-       if (rc != 0) {
-               CERROR("Can't initialize device: %d\n", rc);
-               goto free_dev;
-       }
-
-       list_add_tail(&dev->ibd_list, &kiblnd_data.kib_devs);
-       return dev;
-unlock:
-       rtnl_unlock();
-free_dev:
-       LIBCFS_FREE(dev, sizeof(*dev));
-       return NULL;
-}
-
 static void
 kiblnd_base_shutdown(void)
 {
@@ -3231,8 +3157,7 @@ kiblnd_start_schedulers(struct kib_sched_info *sched)
        return rc;
 }
 
-static int
-kiblnd_dev_start_threads(struct kib_dev *dev, int newdev, u32 *cpts, int ncpts)
+static int kiblnd_dev_start_threads(struct kib_dev *dev, u32 *cpts, int ncpts)
 {
        int     cpt;
        int     rc;
@@ -3244,7 +3169,7 @@ kiblnd_dev_start_threads(struct kib_dev *dev, int newdev, u32 *cpts, int ncpts)
                cpt = (cpts == NULL) ? i : cpts[i];
                sched = kiblnd_data.kib_scheds[cpt];
 
-               if (!newdev && sched->ibs_nthreads > 0)
+               if (sched->ibs_nthreads > 0)
                        continue;
 
                rc = kiblnd_start_schedulers(kiblnd_data.kib_scheds[cpt]);
@@ -3257,49 +3182,16 @@ kiblnd_dev_start_threads(struct kib_dev *dev, int newdev, u32 *cpts, int ncpts)
        return 0;
 }
 
-static struct kib_dev *
-kiblnd_dev_search(char *ifname)
-{
-       struct kib_dev *alias = NULL;
-       struct kib_dev *dev;
-       char            *colon;
-       char            *colon2;
-
-       colon = strchr(ifname, ':');
-       list_for_each_entry(dev, &kiblnd_data.kib_devs, ibd_list) {
-               if (strcmp(&dev->ibd_ifname[0], ifname) == 0)
-                       return dev;
-
-               if (alias != NULL)
-                       continue;
-
-               colon2 = strchr(dev->ibd_ifname, ':');
-               if (colon != NULL)
-                       *colon = 0;
-               if (colon2 != NULL)
-                       *colon2 = 0;
-
-               if (strcmp(&dev->ibd_ifname[0], ifname) == 0)
-                       alias = dev;
-
-               if (colon != NULL)
-                       *colon = ':';
-               if (colon2 != NULL)
-                       *colon2 = ':';
-       }
-       return alias;
-}
-
 static int
 kiblnd_startup(struct lnet_ni *ni)
 {
         char                     *ifname;
+       struct lnet_inetdev *ifaces = NULL;
        struct kib_dev *ibdev = NULL;
        struct kib_net *net;
         unsigned long             flags;
         int                       rc;
-       int                       newdev;
-       int                       node_id;
+       int i;
 
         LASSERT (ni->ni_net->net_lnd == &the_o2iblnd);
 
@@ -3325,10 +3217,8 @@ kiblnd_startup(struct lnet_ni *ni)
         */
        if (ni->ni_interfaces[0] != NULL) {
                /* Use the IPoIB interface specified in 'networks=' */
-
-               CLASSERT(LNET_INTERFACES_NUM > 1);
                if (ni->ni_interfaces[1] != NULL) {
-                       CERROR("Multiple interfaces not supported\n");
+                       CERROR("ko2iblnd: Multiple interfaces not supported\n");
                        goto failed;
                }
 
@@ -3342,24 +3232,51 @@ kiblnd_startup(struct lnet_ni *ni)
                 goto failed;
         }
 
-       ibdev = kiblnd_dev_search(ifname);
+       rc = lnet_inet_enumerate(&ifaces);
+       if (rc < 0)
+               goto failed;
+
+       for (i = 0; i < rc; i++) {
+               if (strcmp(ifname, ifaces[i].li_name) == 0)
+                       break;
+       }
 
-       newdev = ibdev == NULL;
-       /* hmm...create kib_dev even for alias */
-       if (ibdev == NULL || strcmp(&ibdev->ibd_ifname[0], ifname) != 0)
-               ibdev = kiblnd_create_dev(ifname);
+       if (i == rc) {
+               CERROR("ko2iblnd: No matching interfaces\n");
+               rc = -ENOENT;
+               goto failed;
+       }
 
-       if (ibdev == NULL)
+       LIBCFS_ALLOC(ibdev, sizeof(*ibdev));
+       if (!ibdev) {
+               rc = -ENOMEM;
                goto failed;
+       }
+
+       ibdev->ibd_ifip = ifaces[i].li_ipaddr;
+       strlcpy(ibdev->ibd_ifname, ifaces[i].li_name,
+               sizeof(ibdev->ibd_ifname));
+       ibdev->ibd_can_failover = !!(ifaces[i].li_flags & IFF_MASTER);
 
-       node_id = dev_to_node(ibdev->ibd_hdev->ibh_ibdev->dma_device);
-       ni->ni_dev_cpt = cfs_cpt_of_node(lnet_cpt_table(), node_id);
+       INIT_LIST_HEAD(&ibdev->ibd_nets);
+       INIT_LIST_HEAD(&ibdev->ibd_list); /* not yet in kib_devs */
+       INIT_LIST_HEAD(&ibdev->ibd_fail_list);
+
+       /* initialize the device */
+       rc = kiblnd_dev_failover(ibdev);
+       if (rc) {
+               CERROR("ko2iblnd: Can't initialize device: rc = %d\n", rc);
+               goto failed;
+       }
+
+       list_add_tail(&ibdev->ibd_list, &kiblnd_data.kib_devs);
 
        net->ibn_dev = ibdev;
        ni->ni_nid = LNET_MKNID(LNET_NIDNET(ni->ni_nid), ibdev->ibd_ifip);
 
-       rc = kiblnd_dev_start_threads(ibdev, newdev,
-                                     ni->ni_cpts, ni->ni_ncpts);
+       ni->ni_dev_cpt = ifaces[i].li_cpt;
+
+       rc = kiblnd_dev_start_threads(ibdev, ni->ni_cpts, ni->ni_ncpts);
        if (rc != 0)
                goto failed;
 
@@ -3382,6 +3299,7 @@ failed:
        if (net != NULL && net->ibn_dev == NULL && ibdev != NULL)
                 kiblnd_destroy_dev(ibdev);
 
+       kfree(ifaces);
         kiblnd_shutdown(ni);
 
         CDEBUG(D_NET, "kiblnd_startup failed\n");
index 5d60a0a..6eb56d0 100644 (file)
@@ -2568,62 +2568,6 @@ ksocknal_shutdown(struct lnet_ni *ni)
 }
 
 static int
-ksocknal_enumerate_interfaces(struct ksock_net *net, char *iname)
-{
-       struct net_device *dev;
-
-       rtnl_lock();
-       for_each_netdev(&init_net, dev) {
-               /* The iname specified by an user land configuration can
-                * map to an ifa_label so always treat iname as an ifa_label.
-                * If iname is NULL then fall back to the net device name.
-                */
-               const char *name = iname ? iname : dev->name;
-               struct in_device *in_dev;
-
-               if (strcmp(dev->name, "lo") == 0) /* skip the loopback IF */
-                       continue;
-
-               if (!(dev_get_flags(dev) & IFF_UP)) {
-                       CWARN("Ignoring interface %s (down)\n", dev->name);
-                       continue;
-               }
-
-               in_dev = __in_dev_get_rtnl(dev);
-               if (!in_dev) {
-                       CWARN("Interface %s has no IPv4 status.\n", dev->name);
-                       continue;
-               }
-
-               for_ifa(in_dev)
-                       if (strcmp(name, ifa->ifa_label) == 0) {
-                               int idx = net->ksnn_ninterfaces;
-                               struct ksock_interface *ksi;
-
-                               if (idx >= ARRAY_SIZE(net->ksnn_interfaces)) {
-                                       rtnl_unlock();
-                                       return -E2BIG;
-                               }
-
-                               ksi = &net->ksnn_interfaces[idx];
-                               ksi->ksni_ipaddr = ntohl(ifa->ifa_local);
-                               ksi->ksni_netmask = ifa->ifa_mask;
-                               strlcpy(ksi->ksni_name,
-                                       name, sizeof(ksi->ksni_name));
-                               net->ksnn_ninterfaces++;
-                               break;
-                       }
-               endfor_ifa(in_dev);
-        }
-       rtnl_unlock();
-
-       if (net->ksnn_ninterfaces == 0)
-                CERROR("Can't find any usable interfaces\n");
-
-       return net->ksnn_ninterfaces > 0 ? 0 : -ENOENT;
-}
-
-static int
 ksocknal_search_new_ipif(struct ksock_net *net)
 {
        int new_ipif = 0;
@@ -2742,10 +2686,10 @@ ksocknal_startup(struct lnet_ni *ni)
 {
        struct ksock_net *net;
        struct lnet_ioctl_config_lnd_cmn_tunables *net_tunables;
+       struct ksock_interface *ksi = NULL;
+       struct lnet_inetdev *ifaces = NULL;
+       int i = 0;
        int rc;
-       int i;
-       struct net_device *net_dev;
-       int node_id;
 
         LASSERT (ni->ni_net->net_lnd == &the_ksocklnd);
 
@@ -2785,10 +2729,20 @@ ksocknal_startup(struct lnet_ni *ni)
                net_tunables->lct_peer_rtr_credits =
                        *ksocknal_tunables.ksnd_peerrtrcredits;
 
+       rc = lnet_inet_enumerate(&ifaces);
+       if (rc < 0)
+               goto fail_1;
+
        if (!ni->ni_interfaces[0]) {
-               rc = ksocknal_enumerate_interfaces(net, NULL);
-               if (rc < 0)
-                       goto fail_1;
+               ksi = &net->ksnn_interfaces[0];
+
+               /* Use the first discovered interface */
+               net->ksnn_ninterfaces = 1;
+               ni->ni_dev_cpt = ifaces[0].li_cpt;
+               ksi->ksni_ipaddr = ifaces[0].li_ipaddr;
+               ksi->ksni_netmask = ifaces[0].li_netmask;
+               strlcpy(ksi->ksni_name, ifaces[0].li_name,
+                       sizeof(ksi->ksni_name));
        } else {
                /* Before Multi-Rail ksocklnd would manage
                 * multiple interfaces with its own tcp bonding.
@@ -2806,34 +2760,38 @@ ksocknal_startup(struct lnet_ni *ni)
                        if (!ni->ni_interfaces[i])
                                break;
 
-                       for (j = 0; j < net->ksnn_ninterfaces;  j++) {
-                               struct ksock_interface *ksi;
-
-                               ksi = &net->ksnn_interfaces[j];
-
-                               if (strcmp(ni->ni_interfaces[i],
-                                          ksi->ksni_name) == 0) {
-                                       CERROR("found duplicate %s\n",
-                                              ksi->ksni_name);
+                       for (j = 0; j < LNET_INTERFACES_NUM;  j++) {
+                               if (i != j && ni->ni_interfaces[j] &&
+                                   strcmp(ni->ni_interfaces[i],
+                                          ni->ni_interfaces[j]) == 0) {
                                        rc = -EEXIST;
+                                       CERROR("ksocklnd: found duplicate %s at %d and %d, rc = %d\n",
+                                              ni->ni_interfaces[i], i, j, rc);
                                        goto fail_1;
                                }
                        }
 
-                       rc = ksocknal_enumerate_interfaces(net, ni->ni_interfaces[i]);
-                       if (rc < 0)
-                               goto fail_1;
-               }
-       }
+                       for (j = 0; j < rc; j++) {
+                               if (strcmp(ifaces[j].li_name,
+                                          ni->ni_interfaces[i]) != 0)
+                                       continue;
 
-       net_dev = dev_get_by_name(&init_net,
-                                 net->ksnn_interfaces[0].ksni_name);
-       if (net_dev != NULL) {
-               node_id = dev_to_node(&net_dev->dev);
-               ni->ni_dev_cpt = cfs_cpt_of_node(lnet_cpt_table(), node_id);
-               dev_put(net_dev);
-       } else {
-               ni->ni_dev_cpt = CFS_CPT_ANY;
+                               ksi = &net->ksnn_interfaces[j];
+                               ni->ni_dev_cpt = ifaces[j].li_cpt;
+                               ksi->ksni_ipaddr = ifaces[j].li_ipaddr;
+                               ksi->ksni_netmask = ifaces[j].li_netmask;
+                               strlcpy(ksi->ksni_name, ifaces[j].li_name,
+                                       sizeof(ksi->ksni_name));
+                               net->ksnn_ninterfaces++;
+                               break;
+                       }
+               }
+               /* ni_interfaces don't map to all network interfaces */
+               if (!ksi || net->ksnn_ninterfaces != i) {
+                       CERROR("ksocklnd: requested %d but only %d interfaces found\n",
+                              i, net->ksnn_ninterfaces);
+                       goto fail_1;
+               }
        }
 
        /* call it before add it to ksocknal_data.ksnd_nets */
@@ -2841,8 +2799,8 @@ ksocknal_startup(struct lnet_ni *ni)
        if (rc != 0)
                goto fail_1;
 
-       ni->ni_nid = LNET_MKNID(LNET_NIDNET(ni->ni_nid),
-                               net->ksnn_interfaces[0].ksni_ipaddr);
+       LASSERT(ksi);
+       ni->ni_nid = LNET_MKNID(LNET_NIDNET(ni->ni_nid), ksi->ksni_ipaddr);
        list_add(&net->ksnn_list, &ksocknal_data.ksnd_nets);
 
         ksocknal_data.ksnd_nnets++;
index 15f80bd..2394f39 100644 (file)
@@ -1605,103 +1605,121 @@ lnet_match_networks (char **networksp, char *ip2nets, __u32 *ipaddrs, int nip)
        return count;
 }
 
-static void
-lnet_ipaddr_free_enumeration(__u32 *ipaddrs, int nip)
-{
-       LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
-}
-
-static int
-lnet_ipaddr_enumerate(u32 **ipaddrsp)
+int lnet_inet_enumerate(struct lnet_inetdev **dev_list)
 {
+       struct lnet_inetdev *ifaces = NULL;
        struct net_device *dev;
-       u32 *ipaddrs;
-       int nalloc = 64;
+       int nalloc = 0;
        int nip = 0;
 
-       LIBCFS_ALLOC(ipaddrs, nalloc * sizeof(*ipaddrs));
-       if (!ipaddrs) {
-               CERROR("Can't allocate ipaddrs[%d]\n", nalloc);
-               return -ENOMEM;
-       }
-
        rtnl_lock();
        for_each_netdev(&init_net, dev) {
+               int flags = dev_get_flags(dev);
                struct in_device *in_dev;
+               int node_id;
+               int cpt;
 
-               if (strcmp(dev->name, "lo") == 0)
+               if (flags & IFF_LOOPBACK) /* skip the loopback IF */
                        continue;
 
-               if (!(dev_get_flags(dev) & IFF_UP)) {
-                       CWARN("Ignoring interface %s: it's down\n", dev->name);
+               if (!(flags & IFF_UP)) {
+                       CWARN("lnet: Ignoring interface %s: it's down\n",
+                             dev->name);
                        continue;
                }
 
                in_dev = __in_dev_get_rtnl(dev);
                if (!in_dev) {
-                       CWARN("Interface %s has no IPv4 status.\n", dev->name);
+                       CWARN("lnet: Interface %s has no IPv4 status.\n",
+                             dev->name);
                        continue;
                }
 
-               if (nip >= nalloc) {
-                       u32 *ipaddrs2;
-
-                       nalloc += nalloc;
-                       ipaddrs2 = krealloc(ipaddrs, nalloc * sizeof(*ipaddrs2),
-                                           GFP_KERNEL);
-                       if (!ipaddrs2) {
-                               kfree(ipaddrs);
-                               CERROR("Can't allocate ipaddrs[%d]\n", nip);
-                               return -ENOMEM;
+               node_id = dev_to_node(&dev->dev);
+               cpt = cfs_cpt_of_node(lnet_cpt_table(), node_id);
+
+               for_ifa(in_dev) {
+                       if (nip >= nalloc) {
+                               struct lnet_inetdev *tmp;
+
+                               nalloc += LNET_INTERFACES_NUM;
+                               tmp = krealloc(ifaces, nalloc * sizeof(*tmp),
+                                              GFP_KERNEL);
+                               if (!tmp) {
+                                       kfree(ifaces);
+                                       ifaces = NULL;
+                                       nip = -ENOMEM;
+                                       goto unlock_rtnl;
+                               }
+                               ifaces = tmp;
                        }
-                       ipaddrs = ipaddrs2;
-               }
 
-               for_primary_ifa(in_dev)
-                       if (strcmp(ifa->ifa_label, dev->name) == 0) {
-                               ipaddrs[nip++] = ifa->ifa_local;
-                               break;
-                       }
+                       ifaces[nip].li_cpt = cpt;
+                       ifaces[nip].li_flags = flags;
+                       ifaces[nip].li_ipaddr = ntohl(ifa->ifa_local);
+                       ifaces[nip].li_netmask = ntohl(ifa->ifa_mask);
+                       strlcpy(ifaces[nip].li_name, ifa->ifa_label,
+                               sizeof(ifaces[nip].li_name));
+                       nip++;
+               }
                endfor_ifa(in_dev);
        }
+unlock_rtnl:
        rtnl_unlock();
 
-       *ipaddrsp = ipaddrs;
+       if (nip == 0) {
+               CERROR("lnet: Can't find any usable interfaces, rc = -ENOENT\n");
+               nip = -ENOENT;
+       }
+
+       *dev_list = ifaces;
        return nip;
 }
+EXPORT_SYMBOL(lnet_inet_enumerate);
 
 int
 lnet_parse_ip2nets (char **networksp, char *ip2nets)
 {
+       struct lnet_inetdev *ifaces = NULL;
        __u32     *ipaddrs = NULL;
-       int        nip = lnet_ipaddr_enumerate(&ipaddrs);
+       int nip;
        int        rc;
+       int i;
 
+       nip = lnet_inet_enumerate(&ifaces);
        if (nip < 0) {
-               LCONSOLE_ERROR_MSG(0x117, "Error %d enumerating local IP "
-                                  "interfaces for ip2nets to match\n", nip);
+               if (nip != -ENOENT) {
+                       LCONSOLE_ERROR_MSG(0x117,
+                                          "Error %d enumerating local IP interfaces for ip2nets to match\n",
+                                          nip);
+               } else {
+                       LCONSOLE_ERROR_MSG(0x118,
+                                          "No local IP interfaces for ip2nets to match\n");
+               }
                return nip;
        }
 
-       if (nip == 0) {
-               LCONSOLE_ERROR_MSG(0x118, "No local IP interfaces "
-                                  "for ip2nets to match\n");
-               return -ENOENT;
+       LIBCFS_ALLOC(ipaddrs, nip * sizeof(*ipaddrs));
+       if (!ipaddrs) {
+               rc = -ENOMEM;
+               CERROR("lnet: Can't allocate ipaddrs[%d], rc = %d\n",
+                      nip, rc);
+               goto out_free_addrs;
        }
 
-       rc = lnet_match_networks(networksp, ip2nets, ipaddrs, nip);
-       lnet_ipaddr_free_enumeration(ipaddrs, nip);
+       for (i = 0; i < nip; i++)
+               ipaddrs[i] = ifaces[i].li_ipaddr;
 
+       rc = lnet_match_networks(networksp, ip2nets, ipaddrs, nip);
        if (rc < 0) {
                LCONSOLE_ERROR_MSG(0x119, "Error %d parsing ip2nets\n", rc);
-               return rc;
-       }
-
-       if (rc == 0) {
+       } else if (rc == 0) {
                LCONSOLE_ERROR_MSG(0x11a, "ip2nets does not match "
                                   "any local IP interfaces\n");
-               return -ENOENT;
+               rc = -ENOENT;
        }
-
-       return 0;
+       LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
+out_free_addrs:
+       kfree(ifaces);
+       return rc > 0 ? 0 : rc;
 }