summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2014-11-03 12:38:29 +0000
committerhselasky <hselasky@FreeBSD.org>2014-11-03 12:38:29 +0000
commitfa183f01741aa54ff3ba0fcf31b7b1404b7a7e53 (patch)
tree1bba595c897541325069c2ad2bb01c04241c25c0 /sys/net
parent8dd879bfaf9ef726ec0754bd1c4368f02df4f798 (diff)
downloadFreeBSD-src-fa183f01741aa54ff3ba0fcf31b7b1404b7a7e53.zip
FreeBSD-src-fa183f01741aa54ff3ba0fcf31b7b1404b7a7e53.tar.gz
MFC r271946 and r272595:
Improve transmit sending offload, TSO, algorithm in general. This change allows all HCAs from Mellanox Technologies to function properly when TSO is enabled. See r271946 and r272595 for more details about this commit. Sponsored by: Mellanox Technologies
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/if.c86
-rw-r--r--sys/net/if_lagg.c16
-rw-r--r--sys/net/if_var.h28
-rw-r--r--sys/net/if_vlan.c6
4 files changed, 112 insertions, 24 deletions
diff --git a/sys/net/if.c b/sys/net/if.c
index a658384..2e5d0ab 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -583,6 +583,57 @@ if_attach(struct ifnet *ifp)
if_attach_internal(ifp, 0);
}
+/*
+ * Compute the least common TSO limit.
+ */
+void
+if_hw_tsomax_common(struct ifnet *ifp, struct ifnet_hw_tsomax *pmax)
+{
+ /*
+ * 1) If there is no limit currently, take the limit from
+ * the network adapter.
+ *
+ * 2) If the network adapter has a limit below the current
+ * limit, apply it.
+ */
+ if (pmax->tsomaxbytes == 0 || (ifp->if_hw_tsomax != 0 &&
+ ifp->if_hw_tsomax < pmax->tsomaxbytes)) {
+ pmax->tsomaxbytes = ifp->if_hw_tsomax;
+ }
+ if (pmax->tsomaxsegcount == 0 || (ifp->if_hw_tsomaxsegcount != 0 &&
+ ifp->if_hw_tsomaxsegcount < pmax->tsomaxsegcount)) {
+ pmax->tsomaxsegcount = ifp->if_hw_tsomaxsegcount;
+ }
+ if (pmax->tsomaxsegsize == 0 || (ifp->if_hw_tsomaxsegsize != 0 &&
+ ifp->if_hw_tsomaxsegsize < pmax->tsomaxsegsize)) {
+ pmax->tsomaxsegsize = ifp->if_hw_tsomaxsegsize;
+ }
+}
+
+/*
+ * Update TSO limit of a network adapter.
+ *
+ * Returns zero if no change. Else non-zero.
+ */
+int
+if_hw_tsomax_update(struct ifnet *ifp, struct ifnet_hw_tsomax *pmax)
+{
+ int retval = 0;
+ if (ifp->if_hw_tsomax != pmax->tsomaxbytes) {
+ ifp->if_hw_tsomax = pmax->tsomaxbytes;
+ retval++;
+ }
+ if (ifp->if_hw_tsomaxsegsize != pmax->tsomaxsegsize) {
+ ifp->if_hw_tsomaxsegsize = pmax->tsomaxsegsize;
+ retval++;
+ }
+ if (ifp->if_hw_tsomaxsegcount != pmax->tsomaxsegcount) {
+ ifp->if_hw_tsomaxsegcount = pmax->tsomaxsegcount;
+ retval++;
+ }
+ return (retval);
+}
+
static void
if_attach_internal(struct ifnet *ifp, int vmove)
{
@@ -657,13 +708,36 @@ if_attach_internal(struct ifnet *ifp, int vmove)
ifp->if_broadcastaddr = NULL;
#if defined(INET) || defined(INET6)
- /* Initialize to max value. */
- if (ifp->if_hw_tsomax == 0)
- ifp->if_hw_tsomax = min(IP_MAXPACKET, 32 * MCLBYTES -
+ /* Use defaults for TSO, if nothing is set */
+ if (ifp->if_hw_tsomax == 0 &&
+ ifp->if_hw_tsomaxsegcount == 0 &&
+ ifp->if_hw_tsomaxsegsize == 0) {
+ /*
+ * The TSO defaults needs to be such that an
+ * NFS mbuf list of 35 mbufs totalling just
+ * below 64K works and that a chain of mbufs
+ * can be defragged into at most 32 segments:
+ */
+ ifp->if_hw_tsomax = min(IP_MAXPACKET, (32 * MCLBYTES) -
(ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN));
- KASSERT(ifp->if_hw_tsomax <= IP_MAXPACKET &&
- ifp->if_hw_tsomax >= IP_MAXPACKET / 8,
- ("%s: tsomax outside of range", __func__));
+ ifp->if_hw_tsomaxsegcount = 35;
+ ifp->if_hw_tsomaxsegsize = 2048; /* 2K */
+
+ /* XXX some drivers set IFCAP_TSO after ethernet attach */
+ if (ifp->if_capabilities & IFCAP_TSO) {
+ if_printf(ifp, "Using defaults for TSO: %u/%u/%u\n",
+ ifp->if_hw_tsomax,
+ ifp->if_hw_tsomaxsegcount,
+ ifp->if_hw_tsomaxsegsize);
+ }
+ }
+ /*
+ * If the "if_hw_tsomax" limit is set, check if it is
+ * too small:
+ */
+ KASSERT(ifp->if_hw_tsomax == 0 ||
+ ifp->if_hw_tsomax >= (IP_MAXPACKET / 8),
+ ("%s: if_hw_tsomax is outside of range", __func__));
#endif
}
#ifdef VIMAGE
diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
index 58b7207..7218e29 100644
--- a/sys/net/if_lagg.c
+++ b/sys/net/if_lagg.c
@@ -448,23 +448,18 @@ lagg_capabilities(struct lagg_softc *sc)
struct lagg_port *lp;
int cap = ~0, ena = ~0;
u_long hwa = ~0UL;
-#if defined(INET) || defined(INET6)
- u_int hw_tsomax = IP_MAXPACKET; /* Initialize to the maximum value. */
-#else
- u_int hw_tsomax = ~0; /* if_hw_tsomax is only for INET/INET6, but.. */
-#endif
+ struct ifnet_hw_tsomax hw_tsomax;
LAGG_WLOCK_ASSERT(sc);
+ memset(&hw_tsomax, 0, sizeof(hw_tsomax));
+
/* Get capabilities from the lagg ports */
SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
cap &= lp->lp_ifp->if_capabilities;
ena &= lp->lp_ifp->if_capenable;
hwa &= lp->lp_ifp->if_hwassist;
- /* Set to the minimum value of the lagg ports. */
- if (lp->lp_ifp->if_hw_tsomax < hw_tsomax &&
- lp->lp_ifp->if_hw_tsomax > 0)
- hw_tsomax = lp->lp_ifp->if_hw_tsomax;
+ if_hw_tsomax_common(lp->lp_ifp, &hw_tsomax);
}
cap = (cap == ~0 ? 0 : cap);
ena = (ena == ~0 ? 0 : ena);
@@ -473,11 +468,10 @@ lagg_capabilities(struct lagg_softc *sc)
if (sc->sc_ifp->if_capabilities != cap ||
sc->sc_ifp->if_capenable != ena ||
sc->sc_ifp->if_hwassist != hwa ||
- sc->sc_ifp->if_hw_tsomax != hw_tsomax) {
+ if_hw_tsomax_update(sc->sc_ifp, &hw_tsomax) != 0) {
sc->sc_ifp->if_capabilities = cap;
sc->sc_ifp->if_capenable = ena;
sc->sc_ifp->if_hwassist = hwa;
- sc->sc_ifp->if_hw_tsomax = hw_tsomax;
getmicrotime(&sc->sc_ifp->if_lastchange);
if (sc->sc_ifflags & IFF_DEBUG)
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index 677a688..9312728 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -116,6 +116,12 @@ struct ifqueue {
struct mtx ifq_mtx;
};
+struct ifnet_hw_tsomax {
+ u_int tsomaxbytes; /* TSO total burst length limit in bytes */
+ u_int tsomaxsegcount; /* TSO maximum segment count */
+ u_int tsomaxsegsize; /* TSO maximum segment size in bytes */
+};
+
/*
* Structure defining a network interface.
*
@@ -204,10 +210,11 @@ struct ifnet {
u_int if_fib; /* interface FIB */
u_char if_alloctype; /* if_type at time of allocation */
- u_int if_hw_tsomax; /* tso burst length limit, the minimum
- * is (IP_MAXPACKET / 8).
- * XXXAO: Have to find a better place
- * for it eventually. */
+ u_int if_hw_tsomax; /* TSO total burst length
+ * limit in bytes. A value of
+ * zero means no limit. Have
+ * to find a better place for
+ * it eventually. */
/*
* Spare fields are added so that we can modify sensitive data
@@ -215,7 +222,14 @@ struct ifnet {
* be used with care where binary compatibility is required.
*/
char if_cspare[3];
- int if_ispare[4];
+ int if_ispare[2];
+
+ /*
+ * TSO fields for segment limits. If a field is zero below,
+ * there is no limit:
+ */
+ u_int if_hw_tsomaxsegcount; /* TSO maximum segment count */
+ u_int if_hw_tsomaxsegsize; /* TSO maximum segment size in bytes */
void *if_pspare[8]; /* 1 netmap, 7 TDB */
};
@@ -967,6 +981,10 @@ int ether_poll_register(poll_handler_t *h, struct ifnet *ifp);
int ether_poll_deregister(struct ifnet *ifp);
#endif /* DEVICE_POLLING */
+/* TSO */
+void if_hw_tsomax_common(struct ifnet *, struct ifnet_hw_tsomax *);
+int if_hw_tsomax_update(struct ifnet *, struct ifnet_hw_tsomax *);
+
#endif /* _KERNEL */
#endif /* !_NET_IF_VAR_H_ */
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c
index 9d547a6..6a43e37 100644
--- a/sys/net/if_vlan.c
+++ b/sys/net/if_vlan.c
@@ -1483,6 +1483,7 @@ vlan_capabilities(struct ifvlan *ifv)
{
struct ifnet *p = PARENT(ifv);
struct ifnet *ifp = ifv->ifv_ifp;
+ struct ifnet_hw_tsomax hw_tsomax;
TRUNK_LOCK_ASSERT(TRUNK(ifv));
@@ -1509,8 +1510,9 @@ vlan_capabilities(struct ifvlan *ifv)
* propagate the hardware-assisted flag. TSO on VLANs
* does not necessarily require hardware VLAN tagging.
*/
- if (p->if_hw_tsomax > 0)
- ifp->if_hw_tsomax = p->if_hw_tsomax;
+ memset(&hw_tsomax, 0, sizeof(hw_tsomax));
+ if_hw_tsomax_common(p, &hw_tsomax);
+ if_hw_tsomax_update(ifp, &hw_tsomax);
if (p->if_capabilities & IFCAP_VLAN_HWTSO)
ifp->if_capabilities |= p->if_capabilities & IFCAP_TSO;
if (p->if_capenable & IFCAP_VLAN_HWTSO) {
OpenPOWER on IntegriCloud