In ip_output(), do not acquire the IN_MULTI_LOCK(),

and do not attempt to perform a group lookup.
This is a socket layer lock, and the bottom half of IP
really has no business taking it.

Use the value of the in_mcast_loop sysctl to determine
if we should loop back by default, in the absence of
any multicast socket options. Because the check on
group membership is now deferred to the input path,
an m_copym() is now required.

This should increase multicast send performance where the
source has not requested loopback, although this has not been
benchmarked or measured.

It is also a necessary change for IN_MULTI_LOCK to become
non-recursive, which is required in order to implement IGMPv3
in a thread-safe way.
This commit is contained in:
Bruce M Simpson 2009-03-04 03:45:34 +00:00
parent 0a706446ba
commit 8b889dbb9e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=189359

View File

@ -112,6 +112,7 @@ static void ip_mloopback
(struct ifnet *, struct mbuf *, struct sockaddr_in *, int);
extern int in_mcast_loop;
extern struct protosw inetsw[];
/*
@ -293,8 +294,6 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
mtu = ifp->if_mtu;
}
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
struct in_multi *inm;
m->m_flags |= M_MCAST;
/*
* IP destination address is multicast. Make sure "dst"
@ -334,20 +333,17 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
ip->ip_src = IA_SIN(ia)->sin_addr;
}
IN_MULTI_LOCK();
IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm);
if (inm != NULL &&
(imo == NULL || imo->imo_multicast_loop)) {
IN_MULTI_UNLOCK();
if ((imo == NULL && in_mcast_loop) ||
(imo && imo->imo_multicast_loop)) {
/*
* If we belong to the destination multicast group
* on the outgoing interface, and the caller did not
* forbid loopback, loop back a copy.
* Loop back multicast datagram if not expressly
* forbidden to do so, even if we are not a member
* of the group; ip_input() will filter it later,
* thus deferring a hash lookup and mutex acquisition
* at the expense of a cheap copy using m_copym().
*/
ip_mloopback(ifp, m, dst, hlen);
}
else {
IN_MULTI_UNLOCK();
} else {
/*
* If we are acting as a multicast router, perform
* multicast forwarding as if the packet had just
@ -382,8 +378,9 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
* back, above, but must not be transmitted on a network.
* Also, multicasts addressed to the loopback interface
* are not sent -- the above call to ip_mloopback() will
* loop back a copy if this host actually belongs to the
* destination group on the loopback interface.
* loop back a copy. ip_input() will drop the copy if
* this host does not belong to the destination group on
* the loopback interface.
*/
if (ip->ip_ttl == 0 || ifp->if_flags & IFF_LOOPBACK) {
m_freem(m);
@ -758,7 +755,7 @@ ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu,
/*
* In the first mbuf, leave room for the link header, then
* copy the original IP header including options. The payload
* goes into an additional mbuf chain returned by m_copy().
* goes into an additional mbuf chain returned by m_copym().
*/
m->m_data += max_linkhdr;
mhip = mtod(m, struct ip *);
@ -777,7 +774,7 @@ ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu,
} else
mhip->ip_off |= IP_MF;
mhip->ip_len = htons((u_short)(len + mhlen));
m->m_next = m_copy(m0, off, len);
m->m_next = m_copym(m0, off, len, M_DONTWAIT);
if (m->m_next == NULL) { /* copy failed */
m_free(m);
error = ENOBUFS; /* ??? */