Changeset View
Standalone View
sys/net/if_ethersubr.c
Show First 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/eventhandler.h> | #include <sys/eventhandler.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/priv.h> | |||||
#include <sys/random.h> | #include <sys/random.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/sockio.h> | #include <sys/sockio.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/uuid.h> | #include <sys/uuid.h> | ||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
▲ Show 20 Lines • Show All 377 Lines • ▼ Show 20 Lines | |||||
* Ethernet link layer output routine to send a raw frame to the device. | * Ethernet link layer output routine to send a raw frame to the device. | ||||
* | * | ||||
* This assumes that the 14 byte Ethernet header is present and contiguous | * This assumes that the 14 byte Ethernet header is present and contiguous | ||||
* in the first mbuf (if BRIDGE'ing). | * in the first mbuf (if BRIDGE'ing). | ||||
*/ | */ | ||||
int | int | ||||
ether_output_frame(struct ifnet *ifp, struct mbuf *m) | ether_output_frame(struct ifnet *ifp, struct mbuf *m) | ||||
{ | { | ||||
int i; | struct ether_header *eh; | ||||
const char *errmsg; | |||||
int error; | |||||
uint8_t pcp; | |||||
ae: the "//error//" and "//i//" variables seems have the same meaning. Also, are "//etype//" and… | |||||
Not Done Inline ActionsI eliminated 'i'. 'pcp' is in fact needed to consistently make the decision to frame the packet with the VLAN tag, and to put right pcp into the tag. It is the bug that code used ifp_pcp as the argument to ether_8021q_frame(), instead of pcp. kib: I eliminated 'i'. 'pcp' is in fact needed to consistently make the decision to frame the… | |||||
if (PFIL_HOOKED(&V_link_pfil_hook)) { | pcp = ifp->if_pcp; | ||||
i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_OUT, NULL); | if (pcp != IFNET_PCP_NONE) { | ||||
eh = mtod(m, struct ether_header *); | |||||
if (ntohs(eh->ether_type) != ETHERTYPE_VLAN) { | |||||
error = ether_8021q_frame(&m, ifp, 0, pcp, &errmsg); | |||||
if (error == EJUSTRETURN) { | |||||
if_printf(ifp, "%s\n", errmsg); | |||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
if (m != NULL) | |||||
Done Inline ActionsRe-order to save a branch prediction? if (error != 0) { if (error == EJUSTRETURN) { } return (error); } hselasky: Re-order to save a branch prediction?
```
if (error != 0) {
if (error == EJUSTRETURN) {
}… | |||||
Not Done Inline ActionsI do not believe that compiler is obliged to do anything differently compared with the existing code, if changed as you suggested. The only guaranteed way to mark 'error == 0' case as common is to use __predict_true() ugliness. It certainly requires numbers demonstrating the effect, which I doubt is possible in this case. kib: I do not believe that compiler is obliged to do anything differently compared with the existing… | |||||
m_freem(m); | |||||
return (0); | |||||
} else if (error != 0) | |||||
return (error); | |||||
} | |||||
} | |||||
if (i != 0) | if (PFIL_HOOKED(&V_link_pfil_hook)) { | ||||
error = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, | |||||
PFIL_OUT, NULL); | |||||
if (error != 0) | |||||
return (EACCES); | return (EACCES); | ||||
if (m == NULL) | if (m == NULL) | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Queue message on interface, update output statistics if | * Queue message on interface, update output statistics if | ||||
▲ Show 20 Lines • Show All 639 Lines • ▼ Show 20 Lines | case SIOCSIFMTU: | ||||
* Set the interface MTU. | * Set the interface MTU. | ||||
*/ | */ | ||||
if (ifr->ifr_mtu > ETHERMTU) { | if (ifr->ifr_mtu > ETHERMTU) { | ||||
error = EINVAL; | error = EINVAL; | ||||
} else { | } else { | ||||
ifp->if_mtu = ifr->ifr_mtu; | ifp->if_mtu = ifr->ifr_mtu; | ||||
} | } | ||||
break; | break; | ||||
case SIOCSLANPCP: | |||||
error = priv_check(curthread, PRIV_NET_SETLANPCP); | |||||
if (error != 0) | |||||
break; | |||||
if (ifr->ifr_lan_pcp > 7 && | |||||
ifr->ifr_lan_pcp != IFNET_PCP_NONE) | |||||
error = EINVAL; | |||||
else | |||||
ifp->if_pcp = ifr->ifr_lan_pcp; | |||||
break; | |||||
case SIOCGLANPCP: | |||||
ifr->ifr_lan_pcp = ifp->if_pcp; | |||||
break; | |||||
default: | default: | ||||
error = EINVAL; /* XXX netbsd has ENOTTY??? */ | error = EINVAL; /* XXX netbsd has ENOTTY??? */ | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | ether_vlanencap(struct mbuf *m, uint16_t tag) | ||||
* with 802.1Q encapsulation. | * with 802.1Q encapsulation. | ||||
*/ | */ | ||||
evl = mtod(m, struct ether_vlan_header *); | evl = mtod(m, struct ether_vlan_header *); | ||||
bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, | bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, | ||||
(char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); | (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); | ||||
evl->evl_encap_proto = htons(ETHERTYPE_VLAN); | evl->evl_encap_proto = htons(ETHERTYPE_VLAN); | ||||
evl->evl_tag = htons(tag); | evl->evl_tag = htons(tag); | ||||
return (m); | return (m); | ||||
} | |||||
SYSCTL_DECL(_net_link); | |||||
static SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, | |||||
"IEEE 802.1Q VLAN"); | |||||
static SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, | |||||
"for consistency"); | |||||
static VNET_DEFINE(int, soft_pad); | |||||
#define V_soft_pad VNET(soft_pad) | |||||
SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW | CTLFLAG_VNET, | |||||
&VNET_NAME(soft_pad), 0, | |||||
"pad short frames before tagging"); | |||||
/* | |||||
* For now, make preserving PCP via an mbuf tag optional, as it increases | |||||
* per-packet memory allocations and frees. In the future, it would be | |||||
* preferable to reuse ether_vtag for this, or similar. | |||||
*/ | |||||
int vlan_mtag_pcp = 0; | |||||
SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW, | |||||
&vlan_mtag_pcp, 0, | |||||
"Retain VLAN PCP information as packets are passed up the stack"); | |||||
int | |||||
ether_8021q_frame(struct mbuf **mp, struct ifnet *p, uint16_t vid, uint8_t pcp, | |||||
const char **errmsg) | |||||
{ | |||||
struct m_tag *mtag; | |||||
int n; | |||||
uint16_t tag; | |||||
static const char pad[8]; /* just zeros */ | |||||
Done Inline ActionsWhy is pad not marked "const". Further, I suggest adding __aligned(4) to optimise the access to this field. hselasky: Why is pad not marked "const". Further, I suggest adding __aligned(4) to optimise the access to… | |||||
Not Done Inline ActionsIt was copied from the existing code, so no const, and I added the qualifier in the update. __aligned(4) is quite useless for byte access. The minor effect can be achieved if we put this into dedicated cache line, but again it is impossible to measure the effect, I believe. I contemplated using zero_region as the source of high-quality zeros, but decided to not rototile this code too much. kib: It was copied from the existing code, so no const, and I added the qualifier in the update. | |||||
/* | |||||
* 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 (V_soft_pad && p->if_type == IFT_ETHER) { | |||||
for (n = ETHERMIN + ETHER_HDR_LEN - (*mp)->m_pkthdr.len; | |||||
n > 0; n -= sizeof(pad)) | |||||
if (!m_append(*mp, min(n, sizeof(pad)), pad)) | |||||
break; | |||||
if (n > 0) { | |||||
*errmsg = "cannot pad short frame"; | |||||
return (EJUSTRETURN); | |||||
} | |||||
} | |||||
/* | |||||
* 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 (vlan_mtag_pcp && (mtag = m_tag_locate(*mp, MTAG_8021Q, | |||||
MTAG_8021Q_PCP_OUT, NULL)) != NULL) | |||||
tag = EVL_MAKETAG(vid, *(uint8_t *)(mtag + 1), 0); | |||||
else | |||||
tag = EVL_MAKETAG(vid, pcp, 0); | |||||
if (p->if_capenable & IFCAP_VLAN_HWTAGGING) { | |||||
(*mp)->m_pkthdr.ether_vtag = tag; | |||||
(*mp)->m_flags |= M_VLANTAG; | |||||
} else { | |||||
*mp = ether_vlanencap(*mp, tag); | |||||
if (*mp == NULL) { | |||||
*errmsg = "unable to prepend 802.1Q header"; | |||||
return (EJUSTRETURN); | |||||
} | |||||
} | |||||
return (0); | |||||
} | } | ||||
DECLARE_MODULE(ether, ether_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); | DECLARE_MODULE(ether, ether_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); | ||||
MODULE_VERSION(ether, 1); | MODULE_VERSION(ether, 1); |
the "error" and "i" variables seems have the same meaning. Also, are "etype" and "pcp" variables really needed?