1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-22 11:17:19 +00:00

Change the if_vlan driver to use if_transmit for forwarding packets to the

parent interface.  This avoids the overhead of queueing a packet to an IFQ
only to immediately dequeue it again.

Suggested by:	np
Reviewed by:	brooks
MFC after:	1 month
This commit is contained in:
John Baldwin 2011-11-28 19:35:08 +00:00
parent ba3ba72812
commit d9b1d61535
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=228089

View File

@ -34,9 +34,8 @@
* we need to pretend to be enough of an Ethernet implementation
* to make arp work. The way we do this is by telling everyone
* that we are an Ethernet, and then catch the packets that
* ether_output() left on our output queue when it calls
* if_start(), rewrite them for use by the real outgoing interface,
* and ask it to send them.
* ether_output() sends to us via if_transmit(), rewrite them for
* use by the real outgoing interface, and ask it to send them.
*/
#include <sys/cdefs.h>
@ -183,14 +182,15 @@ static __inline struct ifvlan * vlan_gethash(struct ifvlantrunk *trunk,
#endif
static void trunk_destroy(struct ifvlantrunk *trunk);
static void vlan_start(struct ifnet *ifp);
static void vlan_init(void *foo);
static void vlan_input(struct ifnet *ifp, struct mbuf *m);
static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
static void vlan_qflush(struct ifnet *ifp);
static int vlan_setflag(struct ifnet *ifp, int flag, int status,
int (*func)(struct ifnet *, int));
static int vlan_setflags(struct ifnet *ifp, int status);
static int vlan_setmulti(struct ifnet *ifp);
static int vlan_transmit(struct ifnet *ifp, struct mbuf *m);
static void vlan_unconfig(struct ifnet *ifp);
static void vlan_unconfig_locked(struct ifnet *ifp);
static int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag);
@ -944,9 +944,9 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
/* NB: mtu is not set here */
ifp->if_init = vlan_init;
ifp->if_start = vlan_start;
ifp->if_transmit = vlan_transmit;
ifp->if_qflush = vlan_qflush;
ifp->if_ioctl = vlan_ioctl;
ifp->if_snd.ifq_maxlen = ifqmaxlen;
ifp->if_flags = VLAN_IFFLAGS;
ether_ifattach(ifp, eaddr);
/* Now undo some of the damage... */
@ -1005,99 +1005,95 @@ vlan_init(void *foo __unused)
}
/*
* The if_start method for vlan(4) interface. It doesn't
* raises the IFF_DRV_OACTIVE flag, since it is called
* only from IFQ_HANDOFF() macro in ether_output_frame().
* If the interface queue is full, and vlan_start() is
* not called, the queue would never get emptied and
* interface would stall forever.
* The if_transmit method for vlan(4) interface.
*/
static void
vlan_start(struct ifnet *ifp)
static int
vlan_transmit(struct ifnet *ifp, struct mbuf *m)
{
struct ifvlan *ifv;
struct ifnet *p;
struct mbuf *m;
int error;
ifv = ifp->if_softc;
p = PARENT(ifv);
for (;;) {
IF_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
break;
BPF_MTAP(ifp, m);
BPF_MTAP(ifp, m);
/*
* Do not run parent's if_start() if the parent is not up,
* or parent's driver will cause a system crash.
*/
if (!UP_AND_RUNNING(p)) {
m_freem(m);
ifp->if_collisions++;
continue;
}
/*
* Pad the frame to the minimum size allowed if told to.
* This option is in accord with IEEE Std 802.1Q, 2003 Ed.,
* paragraph C.4.4.3.b. It can help to work around buggy
* bridges that violate paragraph C.4.4.3.a from the same
* document, i.e., fail to pad short frames after untagging.
* E.g., a tagged frame 66 bytes long (incl. FCS) is OK, but
* untagging it will produce a 62-byte frame, which is a runt
* and requires padding. There are VLAN-enabled network
* devices that just discard such runts instead or mishandle
* them somehow.
*/
if (soft_pad && p->if_type == IFT_ETHER) {
static char pad[8]; /* just zeros */
int n;
for (n = ETHERMIN + ETHER_HDR_LEN - m->m_pkthdr.len;
n > 0; n -= sizeof(pad))
if (!m_append(m, min(n, sizeof(pad)), pad))
break;
if (n > 0) {
if_printf(ifp, "cannot pad short frame\n");
ifp->if_oerrors++;
m_freem(m);
continue;
}
}
/*
* If underlying interface can do VLAN tag insertion itself,
* just pass the packet along. However, we need some way to
* tell the interface where the packet came from so that it
* knows how to find the VLAN tag to use, so we attach a
* packet tag that holds it.
*/
if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
m->m_pkthdr.ether_vtag = ifv->ifv_tag;
m->m_flags |= M_VLANTAG;
} else {
m = ether_vlanencap(m, ifv->ifv_tag);
if (m == NULL) {
if_printf(ifp,
"unable to prepend VLAN header\n");
ifp->if_oerrors++;
continue;
}
}
/*
* Send it, precisely as ether_output() would have.
* We are already running at splimp.
*/
error = (p->if_transmit)(p, m);
if (!error)
ifp->if_opackets++;
else
ifp->if_oerrors++;
/*
* Do not run parent's if_transmit() if the parent is not up,
* or parent's driver will cause a system crash.
*/
if (!UP_AND_RUNNING(p)) {
m_freem(m);
ifp->if_collisions++;
return (0);
}
/*
* Pad the frame to the minimum size allowed if told to.
* This option is in accord with IEEE Std 802.1Q, 2003 Ed.,
* paragraph C.4.4.3.b. It can help to work around buggy
* bridges that violate paragraph C.4.4.3.a from the same
* document, i.e., fail to pad short frames after untagging.
* E.g., a tagged frame 66 bytes long (incl. FCS) is OK, but
* untagging it will produce a 62-byte frame, which is a runt
* and requires padding. There are VLAN-enabled network
* devices that just discard such runts instead or mishandle
* them somehow.
*/
if (soft_pad && p->if_type == IFT_ETHER) {
static char pad[8]; /* just zeros */
int n;
for (n = ETHERMIN + ETHER_HDR_LEN - m->m_pkthdr.len;
n > 0; n -= sizeof(pad))
if (!m_append(m, min(n, sizeof(pad)), pad))
break;
if (n > 0) {
if_printf(ifp, "cannot pad short frame\n");
ifp->if_oerrors++;
m_freem(m);
return (0);
}
}
/*
* If underlying interface can do VLAN tag insertion itself,
* just pass the packet along. However, we need some way to
* tell the interface where the packet came from so that it
* knows how to find the VLAN tag to use, so we attach a
* packet tag that holds it.
*/
if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
m->m_pkthdr.ether_vtag = ifv->ifv_tag;
m->m_flags |= M_VLANTAG;
} else {
m = ether_vlanencap(m, ifv->ifv_tag);
if (m == NULL) {
if_printf(ifp, "unable to prepend VLAN header\n");
ifp->if_oerrors++;
return (0);
}
}
/*
* Send it, precisely as ether_output() would have.
*/
error = (p->if_transmit)(p, m);
if (!error)
ifp->if_opackets++;
else
ifp->if_oerrors++;
return (error);
}
/*
* The ifp->if_qflush entry point for vlan(4) is a no-op.
*/
static void
vlan_qflush(struct ifnet *ifp __unused)
{
}
static void