Module Name:    src
Committed By:   rin
Date:           Wed Dec 12 01:46:47 UTC 2018

Modified Files:
        src/sys/net: if.c if_bridge.c if_bridgevar.h
        src/sys/rump/librump/rumpnet: net_stub.c

Log Message:
PR kern/53562

Handle TX offload in software when a packet is sent via
bridge_output(). We can send it as is in the following
exceptional cases:

For unicast:

(1) When the destination interface is the same as source.

(2) When the destination supports all TX offload options
    specified in a packet.

For multicast/broadcast:

(3) When all the members of the bridge support the specified
    TX offload options.

For (3), add sc_csum_flags_tx flag to bridge softc, which is
logical AND b/w capabilities of TX offload options in member
interface (ifp->if_csum_flags_tx). The flag is updated when a
member is (i) added to or (ii) removed from a bridge, or (iii)
if_csum_flags_tx flag of a member interface is manipulated via
ifconfig(8).

Turn on M_CSUM_TSOv[46] bit in ifp->if_csum_flags_tx flag when
TSO[46] is enabled for that interface.

OK msaitoh thorpej


To generate a diff of this commit:
cvs rdiff -u -r1.441 -r1.442 src/sys/net/if.c
cvs rdiff -u -r1.160 -r1.161 src/sys/net/if_bridge.c
cvs rdiff -u -r1.32 -r1.33 src/sys/net/if_bridgevar.h
cvs rdiff -u -r1.36 -r1.37 src/sys/rump/librump/rumpnet/net_stub.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if.c
diff -u src/sys/net/if.c:1.441 src/sys/net/if.c:1.442
--- src/sys/net/if.c:1.441	Thu Nov 15 10:23:56 2018
+++ src/sys/net/if.c	Wed Dec 12 01:46:47 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.c,v 1.441 2018/11/15 10:23:56 maxv Exp $	*/
+/*	$NetBSD: if.c,v 1.442 2018/12/12 01:46:47 rin Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc.
@@ -90,7 +90,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.441 2018/11/15 10:23:56 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.442 2018/12/12 01:46:47 rin Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_inet.h"
@@ -149,6 +149,11 @@ __KERNEL_RCSID(0, "$NetBSD: if.c,v 1.441
 #include "fddi.h"
 #include "token.h"
 
+#include "bridge.h"
+#if NBRIDGE > 0
+#include <net/if_bridgevar.h>
+#endif
+
 #include "carp.h"
 #if NCARP > 0
 #include <netinet/ip_carp.h>
@@ -2909,40 +2914,41 @@ ifioctl_common(struct ifnet *ifp, u_long
 		/* Pre-compute the checksum flags mask. */
 		ifp->if_csum_flags_tx = 0;
 		ifp->if_csum_flags_rx = 0;
-		if (ifp->if_capenable & IFCAP_CSUM_IPv4_Tx) {
+		if (ifp->if_capenable & IFCAP_CSUM_IPv4_Tx)
 			ifp->if_csum_flags_tx |= M_CSUM_IPv4;
-		}
-		if (ifp->if_capenable & IFCAP_CSUM_IPv4_Rx) {
+		if (ifp->if_capenable & IFCAP_CSUM_IPv4_Rx)
 			ifp->if_csum_flags_rx |= M_CSUM_IPv4;
-		}
 
-		if (ifp->if_capenable & IFCAP_CSUM_TCPv4_Tx) {
+		if (ifp->if_capenable & IFCAP_CSUM_TCPv4_Tx)
 			ifp->if_csum_flags_tx |= M_CSUM_TCPv4;
-		}
-		if (ifp->if_capenable & IFCAP_CSUM_TCPv4_Rx) {
+		if (ifp->if_capenable & IFCAP_CSUM_TCPv4_Rx)
 			ifp->if_csum_flags_rx |= M_CSUM_TCPv4;
-		}
 
-		if (ifp->if_capenable & IFCAP_CSUM_UDPv4_Tx) {
+		if (ifp->if_capenable & IFCAP_CSUM_UDPv4_Tx)
 			ifp->if_csum_flags_tx |= M_CSUM_UDPv4;
-		}
-		if (ifp->if_capenable & IFCAP_CSUM_UDPv4_Rx) {
+		if (ifp->if_capenable & IFCAP_CSUM_UDPv4_Rx)
 			ifp->if_csum_flags_rx |= M_CSUM_UDPv4;
-		}
 
-		if (ifp->if_capenable & IFCAP_CSUM_TCPv6_Tx) {
+		if (ifp->if_capenable & IFCAP_CSUM_TCPv6_Tx)
 			ifp->if_csum_flags_tx |= M_CSUM_TCPv6;
-		}
-		if (ifp->if_capenable & IFCAP_CSUM_TCPv6_Rx) {
+		if (ifp->if_capenable & IFCAP_CSUM_TCPv6_Rx)
 			ifp->if_csum_flags_rx |= M_CSUM_TCPv6;
-		}
 
-		if (ifp->if_capenable & IFCAP_CSUM_UDPv6_Tx) {
+		if (ifp->if_capenable & IFCAP_CSUM_UDPv6_Tx)
 			ifp->if_csum_flags_tx |= M_CSUM_UDPv6;
-		}
-		if (ifp->if_capenable & IFCAP_CSUM_UDPv6_Rx) {
+		if (ifp->if_capenable & IFCAP_CSUM_UDPv6_Rx)
 			ifp->if_csum_flags_rx |= M_CSUM_UDPv6;
-		}
+
+		if (ifp->if_capenable & IFCAP_TSOv4)
+			ifp->if_csum_flags_tx |= M_CSUM_TSOv4;
+		if (ifp->if_capenable & IFCAP_TSOv6)
+			ifp->if_csum_flags_tx |= M_CSUM_TSOv6;
+
+#if NBRIDGE > 0
+		if (ifp->if_bridge != NULL)
+			bridge_calc_csum_flags(ifp->if_bridge);
+#endif
+
 		if (ifp->if_flags & IFF_UP)
 			return ENETRESET;
 		return 0;

Index: src/sys/net/if_bridge.c
diff -u src/sys/net/if_bridge.c:1.160 src/sys/net/if_bridge.c:1.161
--- src/sys/net/if_bridge.c:1.160	Fri Nov  9 06:44:31 2018
+++ src/sys/net/if_bridge.c	Wed Dec 12 01:46:47 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bridge.c,v 1.160 2018/11/09 06:44:31 ozaki-r Exp $	*/
+/*	$NetBSD: if_bridge.c,v 1.161 2018/12/12 01:46:47 rin Exp $	*/
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.160 2018/11/09 06:44:31 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.161 2018/12/12 01:46:47 rin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_bridge_ipf.h"
@@ -112,6 +112,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_bridge.c,
 
 #include <net/if_ether.h>
 #include <net/if_bridgevar.h>
+#include <net/ether_sw_offload.h>
 
 #if defined(BRIDGE_IPF)
 /* Used for bridge_ip[6]_checkbasic */
@@ -123,7 +124,6 @@ __KERNEL_RCSID(0, "$NetBSD: if_bridge.c,
 
 #include <netinet/ip6.h>
 #include <netinet6/in6_var.h>
-#include <netinet6/ip6_var.h>
 #include <netinet6/ip6_private.h>	/* XXX */
 #endif /* BRIDGE_IPF */
 
@@ -750,6 +750,30 @@ bridge_delete_member(struct bridge_softc
 	BRIDGE_LOCK(sc);
 }
 
+/*
+ * bridge_calc_csum_flags:
+ *
+ *	Calculate logical and b/w csum flags each member interface supports.
+ */
+void
+bridge_calc_csum_flags(struct bridge_softc *sc)
+{
+	struct bridge_iflist *bif;
+	struct ifnet *ifs;
+	int flags = ~0;
+
+	BRIDGE_LOCK(sc);
+	BRIDGE_IFLIST_READER_FOREACH(bif, sc) {
+		ifs = bif->bif_ifp;
+		flags &= ifs->if_csum_flags_tx;
+	}
+	sc->sc_csum_flags_tx = flags;
+	BRIDGE_UNLOCK(sc);
+#ifdef DEBUG
+	printf("%s: 0x%x\n", __func__, flags);
+#endif
+}
+
 static int
 bridge_ioctl_add(struct bridge_softc *sc, void *arg)
 {
@@ -827,12 +851,14 @@ bridge_ioctl_add(struct bridge_softc *sc
 
 	BRIDGE_UNLOCK(sc);
 
+	bridge_calc_csum_flags(sc);
+
 	if (sc->sc_if.if_flags & IFF_RUNNING)
 		bstp_initialization(sc);
 	else
 		bstp_stop(sc);
 
- out:
+out:
 	if_put(ifs, &psref);
 	if (error) {
 		if (bif != NULL)
@@ -890,6 +916,7 @@ bridge_ioctl_del(struct bridge_softc *sc
 	}
 
 	bridge_rtdelete(sc, ifs);
+	bridge_calc_csum_flags(sc);
 
 	if (sc->sc_if.if_flags & IFF_RUNNING)
 		bstp_initialization(sc);
@@ -1086,7 +1113,7 @@ bridge_ioctl_rts(struct bridge_softc *sc
 		count++;
 		len -= sizeof(bareq);
 	}
- out:
+out:
 	BRIDGE_RT_UNLOCK(sc);
 
 	bac->ifbac_len = sizeof(bareq) * count;
@@ -1460,6 +1487,7 @@ bridge_output(struct ifnet *ifp, struct 
 	struct ether_header *eh;
 	struct ifnet *dst_if;
 	struct bridge_softc *sc;
+	struct mbuf *n;
 	int s;
 
 	/*
@@ -1493,7 +1521,7 @@ bridge_output(struct ifnet *ifp, struct 
 	if (__predict_false(sc == NULL) ||
 	    (sc->sc_if.if_flags & IFF_RUNNING) == 0) {
 		dst_if = ifp;
-		goto sendunicast;
+		goto unicast_asis;
 	}
 
 	/*
@@ -1504,13 +1532,85 @@ bridge_output(struct ifnet *ifp, struct 
 		dst_if = NULL;
 	else
 		dst_if = bridge_rtlookup(sc, eh->ether_dhost);
-	if (dst_if == NULL) {
+
+	/*
+	 * In general, we need to handle TX offload in software before
+	 * enqueueing a packet. However, we can send it as is in the
+	 * cases of unicast via (1) the source interface, or (2) an
+	 * interface which supports the specified offload options.
+	 * For multicast or broadcast, send it as is only if (3) all
+	 * the member interfaces support the specified options.
+	 */
+
+	/*
+	 * Unicast via the source interface.
+	 */
+	if (dst_if == ifp)
+		goto unicast_asis;
+
+	/*
+	 * Unicast via other interface.
+	 */
+	if (dst_if != NULL) {
+		KASSERT(m->m_flags & M_PKTHDR);
+		if (TX_OFFLOAD_SUPPORTED(dst_if->if_csum_flags_tx,
+		    m->m_pkthdr.csum_flags)) {
+			/*
+			 * Unicast via an interface which supports the
+			 * specified offload options.
+			 */
+			goto unicast_asis;
+		}
+
+		/*
+		 * Handle TX offload in software. For TSO, a packet is
+		 * split into multiple chunks. Thus, the return value of
+		 * ether_sw_offload_tx() is mbuf chain consists of them.
+		 */
+		m = ether_sw_offload_tx(ifp, m);
+		if (m == NULL)
+			return 0;
+
+		do {
+			n = m->m_nextpkt;
+			if ((dst_if->if_flags & IFF_RUNNING) == 0)
+				m_freem(m);
+			else
+				bridge_enqueue(sc, dst_if, m, 0);
+			m = n;
+		} while (m != NULL);
+
+		return 0;
+	}
+
+	/*
+	 * Multicast or broadcast.
+	 */
+	if (TX_OFFLOAD_SUPPORTED(sc->sc_csum_flags_tx,
+	    m->m_pkthdr.csum_flags)) {
+		/*
+		 * Specified TX offload options are supported by all
+		 * the member interfaces of this bridge.
+		 */
+		m->m_nextpkt = NULL;	/* XXX */
+	} else {
+		/*
+		 * Otherwise, handle TX offload in software.
+		 */
+		m = ether_sw_offload_tx(ifp, m);
+		if (m == NULL)
+			return 0;
+	}
+
+	do {
 		/* XXX Should call bridge_broadcast, but there are locking
 		 * issues which need resolving first. */
 		struct bridge_iflist *bif;
 		struct mbuf *mc;
 		bool used = false;
 
+		n = m->m_nextpkt;
+
 		BRIDGE_PSZ_RENTER(s);
 		BRIDGE_IFLIST_READER_FOREACH(bif, sc) {
 			struct psref psref;
@@ -1594,21 +1694,19 @@ next:
 
 		if (!used)
 			m_freem(m);
-		return 0;
-	}
 
- sendunicast:
+		m = n;
+	} while (m != NULL);
+	return 0;
+
+unicast_asis:
 	/*
 	 * XXX Spanning tree consideration here?
 	 */
-
-	if ((dst_if->if_flags & IFF_RUNNING) == 0) {
+	if ((dst_if->if_flags & IFF_RUNNING) == 0)
 		m_freem(m);
-		return 0;
-	}
-
-	bridge_enqueue(sc, dst_if, m, 0);
-
+	else
+		bridge_enqueue(sc, dst_if, m, 0);
 	return 0;
 }
 

Index: src/sys/net/if_bridgevar.h
diff -u src/sys/net/if_bridgevar.h:1.32 src/sys/net/if_bridgevar.h:1.33
--- src/sys/net/if_bridgevar.h:1.32	Wed Apr 18 04:01:58 2018
+++ src/sys/net/if_bridgevar.h	Wed Dec 12 01:46:47 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bridgevar.h,v 1.32 2018/04/18 04:01:58 ozaki-r Exp $	*/
+/*	$NetBSD: if_bridgevar.h,v 1.33 2018/12/12 01:46:47 rin Exp $	*/
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -327,6 +327,7 @@ struct bridge_softc {
 	struct work		sc_rtage_wk;
 	uint32_t		sc_rthash_key;	/* key for hash */
 	uint32_t		sc_filter_flags; /* ipf and flags */
+	int			sc_csum_flags_tx;
 };
 
 extern const uint8_t bstp_etheraddr[];
@@ -343,6 +344,8 @@ void	bstp_input(struct bridge_softc *, s
 void	bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *,
 	    int);
 
+void	bridge_calc_csum_flags(struct bridge_softc *);
+
 #define BRIDGE_LOCK(_sc)	mutex_enter(&(_sc)->sc_iflist_psref.bip_lock)
 #define BRIDGE_UNLOCK(_sc)	mutex_exit(&(_sc)->sc_iflist_psref.bip_lock)
 #define BRIDGE_LOCKED(_sc)	mutex_owned(&(_sc)->sc_iflist_psref.bip_lock)

Index: src/sys/rump/librump/rumpnet/net_stub.c
diff -u src/sys/rump/librump/rumpnet/net_stub.c:1.36 src/sys/rump/librump/rumpnet/net_stub.c:1.37
--- src/sys/rump/librump/rumpnet/net_stub.c:1.36	Mon May 14 17:34:26 2018
+++ src/sys/rump/librump/rumpnet/net_stub.c	Wed Dec 12 01:46:47 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: net_stub.c,v 1.36 2018/05/14 17:34:26 maxv Exp $	*/
+/*	$NetBSD: net_stub.c,v 1.37 2018/12/12 01:46:47 rin Exp $	*/
 
 /*
  * Copyright (c) 2008 Antti Kantee.  All Rights Reserved.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: net_stub.c,v 1.36 2018/05/14 17:34:26 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: net_stub.c,v 1.37 2018/12/12 01:46:47 rin Exp $");
 
 #include <sys/mutex.h>
 #include <sys/param.h>
@@ -63,6 +63,7 @@ rumpnet_stub(void)
 /* bridge */
 __weak_alias(bridge_ifdetach,rumpnet_stub);
 __weak_alias(bridge_output,rumpnet_stub);
+__weak_alias(bridge_calc_csum_flags,rumpnet_stub);
 
 /* agr */
 __weak_alias(agr_input,rumpnet_stub);

Reply via email to