diff options
Diffstat (limited to 'freebsd/sys/net/if_vlan.c')
-rw-r--r-- | freebsd/sys/net/if_vlan.c | 115 |
1 files changed, 83 insertions, 32 deletions
diff --git a/freebsd/sys/net/if_vlan.c b/freebsd/sys/net/if_vlan.c index 5db11c41..daba2606 100644 --- a/freebsd/sys/net/if_vlan.c +++ b/freebsd/sys/net/if_vlan.c @@ -115,6 +115,7 @@ struct ifvlan { #define PARENT(ifv) ((ifv)->ifv_trunk->parent) void *ifv_cookie; int ifv_pflags; /* special flags we have set on parent */ + int ifv_capenable; struct ifv_linkmib { int ifvm_encaplen; /* encapsulation length */ int ifvm_mtufudge; /* MTU fudged by this much */ @@ -475,6 +476,7 @@ trunk_destroy(struct ifvlantrunk *trunk) trunk->parent->if_vlantrunk = NULL; TRUNK_UNLOCK(trunk); TRUNK_LOCK_DESTROY(trunk); + if_rele(trunk->parent); free(trunk, M_VLAN); } @@ -849,16 +851,20 @@ vlan_clone_match_ethervid(const char *name, int *vidp) if ((cp = strchr(ifname, '.')) == NULL) return (NULL); *cp = '\0'; - if ((ifp = ifunit(ifname)) == NULL) + if ((ifp = ifunit_ref(ifname)) == NULL) return (NULL); /* Parse VID. */ - if (*++cp == '\0') + if (*++cp == '\0') { + if_rele(ifp); return (NULL); + } vid = 0; for(; *cp >= '0' && *cp <= '9'; cp++) vid = (vid * 10) + (*cp - '0'); - if (*cp != '\0') + if (*cp != '\0') { + if_rele(ifp); return (NULL); + } if (vidp != NULL) *vidp = vid; @@ -891,7 +897,6 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) int unit; int error; int vid; - int ethertag; struct ifvlan *ifv; struct ifnet *ifp; struct ifnet *p; @@ -916,23 +921,21 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) error = copyin(params, &vlr, sizeof(vlr)); if (error) return error; - p = ifunit(vlr.vlr_parent); + p = ifunit_ref(vlr.vlr_parent); if (p == NULL) return (ENXIO); error = ifc_name2unit(name, &unit); - if (error != 0) + if (error != 0) { + if_rele(p); return (error); - - ethertag = 1; + } vid = vlr.vlr_tag; wildcard = (unit < 0); } else if ((p = vlan_clone_match_ethervid(name, &vid)) != NULL) { - ethertag = 1; unit = -1; wildcard = 0; } else { - ethertag = 0; - + p = NULL; error = ifc_name2unit(name, &unit); if (error != 0) return (error); @@ -941,8 +944,11 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) } error = ifc_alloc_unit(ifc, &unit); - if (error != 0) + if (error != 0) { + if (p != NULL) + if_rele(p); return (error); + } /* In the wildcard case, we need to update the name. */ if (wildcard) { @@ -958,6 +964,8 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) if (ifp == NULL) { ifc_free_unit(ifc, unit); free(ifv, M_VLAN); + if (p != NULL) + if_rele(p); return (ENOSPC); } SLIST_INIT(&ifv->vlan_mc_listhead); @@ -991,8 +999,9 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_L2VLAN; - if (ethertag) { + if (p != NULL) { error = vlan_config(ifv, p, vid); + if_rele(p); if (error != 0) { /* * Since we've partially failed, we need to back @@ -1279,6 +1288,7 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) TRUNK_LOCK(trunk); p->if_vlantrunk = trunk; trunk->parent = p; + if_ref(trunk->parent); } else { VLAN_LOCK(); exists: @@ -1296,6 +1306,7 @@ exists: ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; ifv->ifv_mintu = ETHERMIN; ifv->ifv_pflags = 0; + ifv->ifv_capenable = -1; /* * If the parent supports the VLAN_MTU capability, @@ -1547,9 +1558,14 @@ vlan_capabilities(struct ifvlan *ifv) struct ifnet *p = PARENT(ifv); struct ifnet *ifp = ifv->ifv_ifp; struct ifnet_hw_tsomax hw_tsomax; + int cap = 0, ena = 0, mena; + u_long hwa = 0; TRUNK_LOCK_ASSERT(TRUNK(ifv)); + /* Mask parent interface enabled capabilities disabled by user. */ + mena = p->if_capenable & ifv->ifv_capenable; + /* * If the parent interface can do checksum offloading * on VLANs, then propagate its hardware-assisted @@ -1557,17 +1573,18 @@ vlan_capabilities(struct ifvlan *ifv) * offloading requires hardware VLAN tagging. */ if (p->if_capabilities & IFCAP_VLAN_HWCSUM) - ifp->if_capabilities = p->if_capabilities & IFCAP_HWCSUM; - + cap |= p->if_capabilities & (IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6); if (p->if_capenable & IFCAP_VLAN_HWCSUM && p->if_capenable & IFCAP_VLAN_HWTAGGING) { - ifp->if_capenable = p->if_capenable & IFCAP_HWCSUM; - ifp->if_hwassist = p->if_hwassist & (CSUM_IP | CSUM_TCP | - CSUM_UDP | CSUM_SCTP); - } else { - ifp->if_capenable = 0; - ifp->if_hwassist = 0; + ena |= mena & (IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6); + if (ena & IFCAP_TXCSUM) + hwa |= p->if_hwassist & (CSUM_IP | CSUM_TCP | + CSUM_UDP | CSUM_SCTP); + if (ena & IFCAP_TXCSUM_IPV6) + hwa |= p->if_hwassist & (CSUM_TCP_IPV6 | + CSUM_UDP_IPV6 | CSUM_SCTP_IPV6); } + /* * If the parent interface can do TSO on VLANs then * propagate the hardware-assisted flag. TSO on VLANs @@ -1577,16 +1594,24 @@ vlan_capabilities(struct ifvlan *ifv) 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; + cap |= p->if_capabilities & IFCAP_TSO; if (p->if_capenable & IFCAP_VLAN_HWTSO) { - ifp->if_capenable |= p->if_capenable & IFCAP_TSO; - ifp->if_hwassist |= p->if_hwassist & CSUM_TSO; - } else { - ifp->if_capenable &= ~(p->if_capenable & IFCAP_TSO); - ifp->if_hwassist &= ~(p->if_hwassist & CSUM_TSO); + ena |= mena & IFCAP_TSO; + if (ena & IFCAP_TSO) + hwa |= p->if_hwassist & CSUM_TSO; } /* + * If the parent interface can do LRO and checksum offloading on + * VLANs, then guess it may do LRO on VLANs. False positive here + * cost nothing, while false negative may lead to some confusions. + */ + if (p->if_capabilities & IFCAP_VLAN_HWCSUM) + cap |= p->if_capabilities & IFCAP_LRO; + if (p->if_capenable & IFCAP_VLAN_HWCSUM) + ena |= p->if_capenable & IFCAP_LRO; + + /* * If the parent interface can offload TCP connections over VLANs then * propagate its TOE capability to the VLAN interface. * @@ -1596,20 +1621,31 @@ vlan_capabilities(struct ifvlan *ifv) */ #define IFCAP_VLAN_TOE IFCAP_TOE if (p->if_capabilities & IFCAP_VLAN_TOE) - ifp->if_capabilities |= p->if_capabilities & IFCAP_TOE; + cap |= p->if_capabilities & IFCAP_TOE; if (p->if_capenable & IFCAP_VLAN_TOE) { TOEDEV(ifp) = TOEDEV(p); - ifp->if_capenable |= p->if_capenable & IFCAP_TOE; + ena |= mena & IFCAP_TOE; } + /* + * If the parent interface supports dynamic link state, so does the + * VLAN interface. + */ + cap |= (p->if_capabilities & IFCAP_LINKSTATE); + ena |= (mena & IFCAP_LINKSTATE); + #ifdef RATELIMIT /* * If the parent interface supports ratelimiting, so does the * VLAN interface. */ - ifp->if_capabilities |= (p->if_capabilities & IFCAP_TXRTLMT); - ifp->if_capenable |= (p->if_capenable & IFCAP_TXRTLMT); + cap |= (p->if_capabilities & IFCAP_TXRTLMT); + ena |= (mena & IFCAP_TXRTLMT); #endif + + ifp->if_capabilities = cap; + ifp->if_capenable = ena; + ifp->if_hwassist = hwa; } static void @@ -1668,8 +1704,10 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) VLAN_LOCK(); if (TRUNK(ifv) != NULL) { p = PARENT(ifv); + if_ref(p); VLAN_UNLOCK(); error = (*p->if_ioctl)(p, SIOCGIFMEDIA, data); + if_rele(p); /* Limit the result to the parent's current config. */ if (error == 0) { struct ifmediareq *ifmr; @@ -1731,12 +1769,13 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) vlan_unconfig(ifp); break; } - p = ifunit(vlr.vlr_parent); + p = ifunit_ref(vlr.vlr_parent); if (p == NULL) { error = ENOENT; break; } error = vlan_config(ifv, p, vlr.vlr_tag); + if_rele(p); if (error) break; @@ -1813,6 +1852,18 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) vlan_tag_recalculate(ifv); break; + case SIOCSIFCAP: + VLAN_LOCK(); + ifv->ifv_capenable = ifr->ifr_reqcap; + trunk = TRUNK(ifv); + if (trunk != NULL) { + TRUNK_LOCK(trunk); + vlan_capabilities(ifv); + TRUNK_UNLOCK(trunk); + } + VLAN_UNLOCK(); + break; + default: error = EINVAL; break; |