git: 3ab3c9c29cf0 - main - linux(4): Consolidate a FreeBSD interface names translation code

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Thu, 23 Feb 2023 08:01:36 UTC
The branch main has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=3ab3c9c29cf0e5df8dbbaaf2003456445534bad8

commit 3ab3c9c29cf0e5df8dbbaaf2003456445534bad8
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2023-02-23 08:00:29 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2023-02-23 08:00:29 +0000

    linux(4): Consolidate a FreeBSD interface names translation code
    
    We have some amount of interface names translation functions which are
    differs by bugs implementation. Consolidates it in a one place.
    
    Fixup loopback interface names translation and use ifnet methods and
    accessors, where possible.
    
    Reviewed by:            melifaro
    Differential Revision:  https://reviews.freebsd.org/D38714
    MFC after:              3 days
    X-MFC with:             32fdc75fe7
---
 sys/compat/linprocfs/linprocfs.c | 40 +++++-------------
 sys/compat/linux/linux.c         | 87 ++++++++++++++++++++++++++++++++++++++--
 sys/compat/linux/linux_common.h  |  4 ++
 sys/compat/linux/linux_ioctl.c   | 56 +++++++-------------------
 4 files changed, 113 insertions(+), 74 deletions(-)

diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c
index 15c4d3c41749..83b00dc0d7c2 100644
--- a/sys/compat/linprocfs/linprocfs.c
+++ b/sys/compat/linprocfs/linprocfs.c
@@ -111,6 +111,7 @@ __FBSDID("$FreeBSD$");
 #endif /* __i386__ || __amd64__ */
 
 #include <compat/linux/linux.h>
+#include <compat/linux/linux_common.h>
 #include <compat/linux/linux_emul.h>
 #include <compat/linux/linux_mib.h>
 #include <compat/linux/linux_misc.h>
@@ -1474,36 +1475,13 @@ linprocfs_doprocmem(PFS_FILL_ARGS)
 	return (error);
 }
 
-static int
-linux_ifname(struct ifnet *ifp, char *buffer, size_t buflen)
-{
-	struct ifnet *ifscan;
-	int ethno;
-
-	IFNET_RLOCK_ASSERT();
-
-	/* Short-circuit non ethernet interfaces */
-	if (linux_use_real_ifname(ifp))
-		return (strlcpy(buffer, ifp->if_xname, buflen));
-
-	/* Determine the (relative) unit number for ethernet interfaces */
-	ethno = 0;
-	CK_STAILQ_FOREACH(ifscan, &V_ifnet, if_link) {
-		if (ifscan == ifp)
-			return (snprintf(buffer, buflen, "eth%d", ethno));
-		if (!linux_use_real_ifname(ifscan))
-			ethno++;
-	}
-
-	return (0);
-}
-
 /*
  * Filler function for proc/net/dev
  */
 static int
 linprocfs_donetdev(PFS_FILL_ARGS)
 {
+	struct epoch_tracker et;
 	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
 	struct ifnet *ifp;
 
@@ -1515,9 +1493,9 @@ linprocfs_donetdev(PFS_FILL_ARGS)
 	    "bytes    packets errs drop fifo colls carrier compressed");
 
 	CURVNET_SET(TD_TO_VNET(curthread));
-	IFNET_RLOCK();
+	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
-		linux_ifname(ifp, ifname, sizeof ifname);
+		ifname_bsd_to_linux_ifp(ifp, ifname, sizeof(ifname));
 		sbuf_printf(sb, "%6.6s: ", ifname);
 		sbuf_printf(sb, "%7ju %7ju %4ju %4ju %4lu %5lu %10lu %9ju ",
 		    (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IBYTES),
@@ -1546,7 +1524,7 @@ linprocfs_donetdev(PFS_FILL_ARGS)
 							 * tx_heartbeat_errors*/
 		    0UL);				/* tx_compressed */
 	}
-	IFNET_RUNLOCK();
+	NET_EPOCH_EXIT(et);
 	CURVNET_RESTORE();
 
 	return (0);
@@ -1576,7 +1554,8 @@ linux_route_print(struct rtentry *rt, void *vw)
 	/* select only first route in case of multipath */
 	nh = nhop_select_func(rnd.rnd_nhop, 0);
 
-	linux_ifname(nh->nh_ifp, ifname, sizeof(ifname));
+	if (ifname_bsd_to_linux_ifp(nh->nh_ifp, ifname, sizeof(ifname)) <= 0)
+		return (ENODEV);
 
 	gw = (nh->nh_flags & NHF_GATEWAY)
 		? nh->gw4_sa.sin_addr.s_addr : 0;
@@ -1605,6 +1584,7 @@ linux_route_print(struct rtentry *rt, void *vw)
 static int
 linprocfs_donetroute(PFS_FILL_ARGS)
 {
+	struct epoch_tracker et;
 	struct walkarg w = {
 		.sb = sb
 	};
@@ -1615,9 +1595,9 @@ linprocfs_donetroute(PFS_FILL_ARGS)
                "\tWindow\tIRTT");
 
 	CURVNET_SET(TD_TO_VNET(curthread));
-	IFNET_RLOCK();
+	NET_EPOCH_ENTER(et);
 	rib_walk(fibnum, AF_INET, false, linux_route_print, &w);
-	IFNET_RUNLOCK();
+	NET_EPOCH_EXIT(et);
 	CURVNET_RESTORE();
 
 	return (0);
diff --git a/sys/compat/linux/linux.c b/sys/compat/linux/linux.c
index 4e435081b3b6..2570a5fcc89c 100644
--- a/sys/compat/linux/linux.c
+++ b/sys/compat/linux/linux.c
@@ -241,6 +241,84 @@ bsd_to_linux_sigset(sigset_t *bss, l_sigset_t *lss)
 	}
 }
 
+/*
+ * Translate a FreeBSD interface name to a Linux interface name
+ * by interface name, and return the number of bytes copied to lxname.
+ */
+int
+ifname_bsd_to_linux_name(const char *bsdname, char *lxname, size_t len)
+{
+	struct epoch_tracker et;
+	struct ifnet *ifp;
+	int ret;
+
+	ret = 0;
+	CURVNET_SET(TD_TO_VNET(curthread));
+	NET_EPOCH_ENTER(et);
+	ifp = ifunit(bsdname);
+	if (ifp != NULL)
+		ret = ifname_bsd_to_linux_ifp(ifp, lxname, len);
+	NET_EPOCH_EXIT(et);
+	CURVNET_RESTORE();
+	return (ret);
+}
+
+/*
+ * Translate a FreeBSD interface name to a Linux interface name
+ * by interface index, and return the number of bytes copied to lxname.
+ */
+int
+ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len)
+{
+	struct epoch_tracker et;
+	struct ifnet *ifp;
+	int ret;
+
+	ret = 0;
+	CURVNET_SET(TD_TO_VNET(curthread));
+	NET_EPOCH_ENTER(et);
+	ifp = ifnet_byindex(idx);
+	if (ifp != NULL)
+		ret = ifname_bsd_to_linux_ifp(ifp, lxname, len);
+	NET_EPOCH_EXIT(et);
+	CURVNET_RESTORE();
+	return (ret);
+}
+
+/*
+ * Translate a FreeBSD interface name to a Linux interface name,
+ * and return the number of bytes copied to lxname.
+ */
+int
+ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len)
+{
+	struct ifnet *ifscan;
+	int unit;
+
+	NET_EPOCH_ASSERT();
+
+	/*
+	 * Linux loopback interface name is lo (not lo0),
+	 * we translate lo to lo0, loX to loX.
+	 */
+	if (IFP_IS_LOOP(ifp) && strncmp(ifp->if_xname, "lo0", IFNAMSIZ) == 0)
+		return (strlcpy(lxname, "lo", len));
+
+	/* Short-circuit non ethernet interfaces. */
+	if (!IFP_IS_ETH(ifp) || linux_use_real_ifname(ifp))
+		return (strlcpy(lxname, ifp->if_xname, len));
+
+	/* Determine the (relative) unit number for ethernet interfaces. */
+	unit = 0;
+	CK_STAILQ_FOREACH(ifscan, &V_ifnet, if_link) {
+		if (ifscan == ifp)
+			return (snprintf(lxname, len, "eth%d", unit));
+		if (IFP_IS_ETH(ifscan))
+			unit++;
+	}
+	return (0);
+}
+
 /*
  * Translate a Linux interface name to a FreeBSD interface name,
  * and return the associated ifnet structure
@@ -261,8 +339,11 @@ ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname)
 			break;
 	if (len == 0 || len == LINUX_IFNAMSIZ)
 		return (NULL);
-	/* Linux loopback interface name is lo (not lo0) */
-	is_lo = (len == 2 && strncmp(lxname, "lo", len) == 0);
+	/*
+	 * Linux loopback interface name is lo (not lo0),
+	 * we translate lo to lo0, loX to loX.
+	 */
+	is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0);
 	unit = (int)strtoul(lxname + len, &ep, 10);
 	if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) &&
 	    is_lo == 0)
@@ -732,5 +813,5 @@ bool
 linux_use_real_ifname(const struct ifnet *ifp)
 {
 
-	return (use_real_ifnames || !IFP_IS_ETH(ifp));
+	return (use_real_ifnames);
 }
diff --git a/sys/compat/linux/linux_common.h b/sys/compat/linux/linux_common.h
index 0eb302bfcd17..9ebaff26b9ff 100644
--- a/sys/compat/linux/linux_common.h
+++ b/sys/compat/linux/linux_common.h
@@ -30,6 +30,10 @@
 #ifndef _LINUX_COMMON_H_
 #define _LINUX_COMMON_H_
 
+int	ifname_bsd_to_linux_ifp(struct ifnet *, char *, size_t);
+int	ifname_bsd_to_linux_idx(u_int, char *, size_t);
+int	ifname_bsd_to_linux_name(const char *, char *, size_t);
+
 struct ifnet	*ifname_linux_to_bsd(struct thread *td,
 		    const char *lxname, char *bsdname);
 void		linux_ifflags(struct ifnet *ifp, short *flags);
diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c
index 2cb0e9ead23f..bfab28f841a0 100644
--- a/sys/compat/linux/linux_ioctl.c
+++ b/sys/compat/linux/linux_ioctl.c
@@ -2092,39 +2092,17 @@ static int
 linux_ioctl_ifname(struct thread *td, struct l_ifreq *uifr)
 {
 	struct l_ifreq ifr;
-	struct ifnet *ifp;
-	int error, ethno, index;
+	int error, ret;
 
 	error = copyin(uifr, &ifr, sizeof(ifr));
 	if (error != 0)
 		return (error);
-
-	CURVNET_SET(TD_TO_VNET(curthread));
-	IFNET_RLOCK();
-	index = 1;	/* ifr.ifr_ifindex starts from 1 */
-	ethno = 0;
-	error = ENODEV;
-	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
-		if (ifr.ifr_ifindex == index) {
-			if (!linux_use_real_ifname(ifp))
-				snprintf(ifr.ifr_name, LINUX_IFNAMSIZ,
-				    "eth%d", ethno);
-			else
-				strlcpy(ifr.ifr_name, ifp->if_xname,
-				    LINUX_IFNAMSIZ);
-			error = 0;
-			break;
-		}
-		if (!linux_use_real_ifname(ifp))
-			ethno++;
-		index++;
-	}
-	IFNET_RUNLOCK();
-	if (error == 0)
-		error = copyout(&ifr, uifr, sizeof(ifr));
-	CURVNET_RESTORE();
-
-	return (error);
+	ret = ifname_bsd_to_linux_idx(ifr.ifr_ifindex, ifr.ifr_name,
+	    LINUX_IFNAMSIZ);
+	if (ret > 0)
+		return (copyout(&ifr, uifr, sizeof(ifr)));
+	else
+		return (ENODEV);
 }
 
 /*
@@ -2134,6 +2112,7 @@ linux_ioctl_ifname(struct thread *td, struct l_ifreq *uifr)
 static int
 linux_ifconf(struct thread *td, struct ifconf *uifc)
 {
+	struct epoch_tracker et;
 #ifdef COMPAT_LINUX32
 	struct l_ifconf ifc;
 #else
@@ -2143,7 +2122,7 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
 	struct ifnet *ifp;
 	struct ifaddr *ifa;
 	struct sbuf *sb;
-	int error, ethno, full = 0, valid_len, max_len;
+	int error, full = 0, valid_len, max_len;
 
 	error = copyin(uifc, &ifc, sizeof(ifc));
 	if (error != 0)
@@ -2155,7 +2134,7 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
 	/* handle the 'request buffer size' case */
 	if ((l_uintptr_t)ifc.ifc_buf == PTROUT(NULL)) {
 		ifc.ifc_len = 0;
-		IFNET_RLOCK();
+		NET_EPOCH_ENTER(et);
 		CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 			CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 				struct sockaddr *sa = ifa->ifa_addr;
@@ -2163,7 +2142,7 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
 					ifc.ifc_len += sizeof(ifr);
 			}
 		}
-		IFNET_RUNLOCK();
+		NET_EPOCH_EXIT(et);
 		error = copyout(&ifc, uifc, sizeof(ifc));
 		CURVNET_RESTORE();
 		return (error);
@@ -2175,8 +2154,6 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
 	}
 
 again:
-	/* Keep track of eth interfaces */
-	ethno = 0;
 	if (ifc.ifc_len <= max_len) {
 		max_len = ifc.ifc_len;
 		full = 1;
@@ -2186,16 +2163,13 @@ again:
 	valid_len = 0;
 
 	/* Return all AF_INET addresses of all interfaces */
-	IFNET_RLOCK();
+	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 		int addrs = 0;
 
 		bzero(&ifr, sizeof(ifr));
-		if (IFP_IS_ETH(ifp))
-			snprintf(ifr.ifr_name, LINUX_IFNAMSIZ, "eth%d",
-			    ethno++);
-		else
-			strlcpy(ifr.ifr_name, ifp->if_xname, LINUX_IFNAMSIZ);
+		ifname_bsd_to_linux_ifp(ifp, ifr.ifr_name,
+		    sizeof(ifr.ifr_name));
 
 		/* Walk the address list */
 		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
@@ -2222,7 +2196,7 @@ again:
 				valid_len = sbuf_len(sb);
 		}
 	}
-	IFNET_RUNLOCK();
+	NET_EPOCH_EXIT(et);
 
 	if (valid_len != max_len && !full) {
 		sbuf_delete(sb);