On Mon, Feb 08, 2016 at 11:02:06PM +1000, David Gwynne wrote:
> On Sat, Feb 06, 2016 at 04:43:28PM -0500, Anthony Eden wrote:
> > >Synopsis: <alignment fault on armv7 (omap) using carp(4)>
> >
> > To me that behavior might suggest the problem is deeper than a
> > bookkeeping mistake of aligning memory in mbuf.
>
> nope, you were right, it's a screwup with alignment.
>
> the problem is multicast packets that arent to a carp interfaces
> mac address have to be duplicated and sent to all carp interfaces
> on a parent. the duplication is done with m_copym2, which doesn't
> respect the alignment requirements of the ip header inside the 14
> byte ethernet header.
>
> the following dups the packet inside carp, and makes sure the
> ethernet payload is aligned properly.
>
> i was able to reproduce this on sparc64, and i believe this fixes
> it. could you test it and see if it helps?
mpi@ pointed out that bridge@ has a special function to do a deep
copy of mbufs that get the ip payload alignment right, and that we
should share.
this moves the functionality in with the rest of the mbuf functions.
could a bridge user test this to see if it still works? carp seems
fine with this on sparc64 stil.
ok?
Index: kern/uipc_mbuf.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_mbuf.c,v
retrieving revision 1.218
diff -u -p -r1.218 uipc_mbuf.c
--- kern/uipc_mbuf.c 31 Jan 2016 00:18:07 -0000 1.218
+++ kern/uipc_mbuf.c 9 Feb 2016 09:49:21 -0000
@@ -1213,6 +1213,40 @@ m_dup_pkthdr(struct mbuf *to, struct mbu
return (0);
}
+struct mbuf *
+m_dup_pkt(struct mbuf *m0, unsigned int adj, int wait)
+{
+ struct mbuf *m;
+ int len;
+
+ len = m0->m_pkthdr.len + adj;
+ if (len > MAXMCLBYTES) /* XXX */
+ return (NULL);
+
+ m = m_get(m0->m_type, wait);
+ if (m == NULL)
+ return (NULL);
+
+ if (m_dup_pkthdr(m, m0, wait) != 0)
+ goto fail;
+
+ if (len > MHLEN) {
+ MCLGETI(m, len, NULL, wait);
+ if (!ISSET(m->m_flags, M_EXT))
+ goto fail;
+ }
+
+ m->m_len = m->m_pkthdr.len = len;
+ m_adj(m, adj);
+ m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t));
+
+ return (m);
+
+fail:
+ m_freem(m);
+ return (NULL);
+}
+
#ifdef DDB
void
m_print(void *v,
Index: net/if_bridge.c
===================================================================
RCS file: /cvs/src/sys/net/if_bridge.c,v
retrieving revision 1.275
diff -u -p -r1.275 if_bridge.c
--- net/if_bridge.c 5 Dec 2015 10:07:55 -0000 1.275
+++ net/if_bridge.c 9 Feb 2016 09:49:21 -0000
@@ -137,7 +137,6 @@ int bridge_ipsec(struct bridge_softc *,
int bridge_clone_create(struct if_clone *, int);
int bridge_clone_destroy(struct ifnet *ifp);
int bridge_delete(struct bridge_softc *, struct bridge_iflist *);
-struct mbuf *bridge_m_dup(struct mbuf *);
#define ETHERADDR_IS_IP_MCAST(a) \
/* struct etheraddr *a; */ \
@@ -800,7 +799,7 @@ bridge_output(struct ifnet *ifp, struct
used = 1;
mc = m;
} else {
- mc = bridge_m_dup(m);
+ mc = m_dup_pkt(m, ETHER_ALIGN, M_DONTWAIT);
if (mc == NULL) {
sc->sc_if.if_oerrors++;
continue;
@@ -1090,7 +1089,7 @@ bridge_process(struct ifnet *ifp, struct
(ifl->bif_state == BSTP_IFSTATE_DISCARDING))
goto reenqueue;
- mc = bridge_m_dup(m);
+ mc = m_dup_pkt(m, ETHER_ALIGN, M_DONTWAIT);
if (mc == NULL)
goto reenqueue;
@@ -1227,7 +1226,7 @@ bridge_broadcast(struct bridge_softc *sc
mc = m;
used = 1;
} else {
- mc = bridge_m_dup(m);
+ mc = m_dup_pkt(m, ETHER_ALIGN, M_DONTWAIT);
if (mc == NULL) {
sc->sc_if.if_oerrors++;
continue;
@@ -1277,7 +1276,7 @@ bridge_localbroadcast(struct bridge_soft
return;
}
- m1 = bridge_m_dup(m);
+ m1 = m_dup_pkt(m, ETHER_ALIGN, M_DONTWAIT);
if (m1 == NULL) {
sc->sc_if.if_oerrors++;
return;
@@ -2017,37 +2016,4 @@ bridge_copyaddr(struct sockaddr *src, st
memcpy(dst, src, src->sa_len);
else
dst->sa_family = AF_UNSPEC;
-}
-
-/*
- * Specialized deep copy to ensure that the payload after the Ethernet
- * header is nicely aligned.
- */
-struct mbuf *
-bridge_m_dup(struct mbuf *m)
-{
- struct mbuf *m1, *m2, *mx;
-
- m1 = m_copym2(m, 0, ETHER_HDR_LEN, M_DONTWAIT);
- if (m1 == NULL) {
- return (NULL);
- }
- m2 = m_copym2(m, ETHER_HDR_LEN, M_COPYALL, M_DONTWAIT);
- if (m2 == NULL) {
- m_freem(m1);
- return (NULL);
- }
-
- for (mx = m1; mx->m_next != NULL; mx = mx->m_next)
- /*EMPTY*/;
- mx->m_next = m2;
-
- if (m1->m_flags & M_PKTHDR) {
- int len = 0;
- for (mx = m1; mx != NULL; mx = mx->m_next)
- len += mx->m_len;
- m1->m_pkthdr.len = len;
- }
-
- return (m1);
}
Index: netinet/ip_carp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_carp.c,v
retrieving revision 1.286
diff -u -p -r1.286 ip_carp.c
--- netinet/ip_carp.c 21 Jan 2016 11:23:48 -0000 1.286
+++ netinet/ip_carp.c 9 Feb 2016 09:49:21 -0000
@@ -210,6 +210,7 @@ int carp_input(struct ifnet *, struct mb
void carp_proto_input_c(struct ifnet *, struct mbuf *,
struct carp_header *, int, sa_family_t);
void carp_proto_input_if(struct ifnet *, struct mbuf *, int);
+int carp_input_mcast(struct carp_softc *, struct mbuf *);
void carpattach(int);
void carpdetach(struct carp_softc *);
int carp_prepare_ad(struct mbuf *, struct carp_vhost_entry *,
@@ -1429,9 +1430,10 @@ carp_input(struct ifnet *ifp0, struct mb
if (!(sc->sc_if.if_flags & IFF_UP))
continue;
- m0 = m_copym2(m, 0, M_COPYALL, M_DONTWAIT);
+ m0 = m_dup_pkt(m, ETHER_ALIGN, M_DONTWAIT);
+ /* if we cant send one we probably cant send more */
if (m0 == NULL)
- continue;
+ break;
ml_init(&ml);
ml_enqueue(&ml, m0);
Index: sys/mbuf.h
===================================================================
RCS file: /cvs/src/sys/sys/mbuf.h,v
retrieving revision 1.207
diff -u -p -r1.207 mbuf.h
--- sys/mbuf.h 31 Jan 2016 00:18:07 -0000 1.207
+++ sys/mbuf.h 9 Feb 2016 09:49:21 -0000
@@ -447,6 +447,7 @@ void m_cat(struct mbuf *, struct mbuf *)
struct mbuf *m_devget(char *, int, int);
int m_apply(struct mbuf *, int, int,
int (*)(caddr_t, caddr_t, unsigned int), caddr_t);
+struct mbuf *m_dup_pkt(struct mbuf *, unsigned int, int);
int m_dup_pkthdr(struct mbuf *, struct mbuf *, int);
/* Packet tag routines */