Index: if_vlan.c =================================================================== RCS file: /cvs/src/sys/net/if_vlan.c,v retrieving revision 1.138 diff -u -p -r1.138 if_vlan.c --- if_vlan.c 10 Sep 2015 17:32:32 -0000 1.138 +++ if_vlan.c 11 Sep 2015 13:24:20 -0000 @@ -1,4 +1,4 @@ -/* $OpenBSD: if_vlan.c,v 1.138 2015/09/10 17:32:32 dlg Exp $ */ +/* $OpenBSD: if_vlan.c,v 1.135 2015/07/20 22:16:41 rzalamena Exp $ */ /* * Copyright 1998 Massachusetts Institute of Technology @@ -57,6 +57,9 @@ #include #include #include +#include +#include +#include #include #include @@ -72,12 +75,12 @@ #include #endif -u_long vlan_tagmask, svlan_tagmask; - -#define TAG_HASH_SIZE 32 -#define TAG_HASH(tag) (tag & vlan_tagmask) -LIST_HEAD(vlan_taghash, ifvlan) *vlan_tagh, *svlan_tagh; - +#define TAG_HASH_BITS 5 +#define TAG_HASH_SIZE (1 << TAG_HASH_BITS) +#define TAG_HASH_MASK (TAG_HASH_SIZE - 1) +#define TAG_HASH(tag) (tag & TAG_HASH_MASK) +struct srpl *vlan_tagh, *svlan_tagh; +struct rwlock vlan_tagh_lk = RWLOCK_INITIALIZER("vlanhash"); int vlan_input(struct ifnet *, struct mbuf *, void *); void vlan_start(struct ifnet *ifp); @@ -100,22 +103,35 @@ struct if_clone vlan_cloner = struct if_clone svlan_cloner = IF_CLONE_INITIALIZER("svlan", vlan_clone_create, vlan_clone_destroy); +void vlan_ref(void *, void *); +void vlan_unref(void *, void *); + +struct srpl_rc vlan_tagh_rc = SRPL_RC_INITIALIZER(vlan_ref, vlan_unref, NULL); + /* ARGSUSED */ void vlanattach(int count) { + unsigned int i; + /* Normal VLAN */ - vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, - &vlan_tagmask); + vlan_tagh = mallocarray(TAG_HASH_SIZE, sizeof(*vlan_tagh), + M_DEVBUF, M_NOWAIT); if (vlan_tagh == NULL) panic("vlanattach: hashinit"); - if_clone_attach(&vlan_cloner); /* Service-VLAN for QinQ/802.1ad provider bridges */ - svlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, - &svlan_tagmask); + svlan_tagh = mallocarray(TAG_HASH_SIZE, sizeof(*svlan_tagh), + M_DEVBUF, M_NOWAIT); if (svlan_tagh == NULL) panic("vlanattach: hashinit"); + + for (i = 0; i < TAG_HASH_SIZE; i++) { + SRPL_INIT(&vlan_tagh[i]); + SRPL_INIT(&svlan_tagh[i]); + } + + if_clone_attach(&vlan_cloner); if_clone_attach(&svlan_cloner); } @@ -125,7 +141,8 @@ vlan_clone_create(struct if_clone *ifc, struct ifvlan *ifv; struct ifnet *ifp; - if ((ifv = malloc(sizeof(*ifv), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) + ifv = malloc(sizeof(*ifv), M_DEVBUF, M_NOWAIT|M_ZERO); + if (ifv == NULL) return (ENOMEM); LIST_INIT(&ifv->vlan_mc_listhead); @@ -142,6 +159,8 @@ vlan_clone_create(struct if_clone *ifc, else ifv->ifv_type = ETHERTYPE_VLAN; + ifv->ifv_refs = 1; + ifp->if_start = vlan_start; ifp->if_ioctl = vlan_ioctl; IFQ_SET_MAXLEN(&ifp->if_snd, 1); @@ -153,15 +172,46 @@ vlan_clone_create(struct if_clone *ifc, return (0); } +void +vlan_ref(void *null, void *v) +{ + struct ifvlan *ifv = v; + + atomic_inc_int(&ifv->ifv_refs); +} + +void +vlan_unref(void *null, void *v) +{ + struct ifvlan *ifv = v; + + if (atomic_dec_int_nv(&ifv->ifv_refs) == 0) + wakeup(&ifv->ifv_refs); +} + int vlan_clone_destroy(struct ifnet *ifp) { struct ifvlan *ifv = ifp->if_softc; + struct sleep_state sls; + u_int refs; vlan_unconfig(ifp, NULL); ether_ifdetach(ifp); if_detach(ifp); + + refs = atomic_dec_int_nv(&ifv->ifv_refs); + while (refs) { + sleep_setup(&sls, &ifv->ifv_refs, PWAIT, "vlandel"); + + membar_consumer(); + refs = ifv->ifv_refs; + + sleep_finish(&sls, refs); + } + free(ifv, M_DEVBUF, sizeof(*ifv)); + return (0); } @@ -275,7 +325,8 @@ vlan_input(struct ifnet *ifp, struct mbu struct ifvlan *ifv; struct ether_vlan_header *evl; struct ether_header *eh; - struct vlan_taghash *tagh; + struct srpl *tagh, *list; + struct srpl_iter i; u_int tag; struct mbuf_list ml = MBUF_LIST_INITIALIZER(); u_int16_t etype; @@ -309,7 +360,8 @@ vlan_input(struct ifnet *ifp, struct mbu if (m->m_pkthdr.pf.prio <= 1) m->m_pkthdr.pf.prio = !m->m_pkthdr.pf.prio; - LIST_FOREACH(ifv, &tagh[TAG_HASH(tag)], ifv_list) { + list = &tagh[TAG_HASH(tag)]; + SRPL_FOREACH(ifv, list, &i, ifv_list) { if (ifp == ifv->ifv_p && tag == ifv->ifv_tag && etype == ifv->ifv_type) break; @@ -317,15 +369,12 @@ vlan_input(struct ifnet *ifp, struct mbu if (ifv == NULL) { ifp->if_noproto++; - m_freem(m); - return (1); + goto drop; } if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) != - (IFF_UP|IFF_RUNNING)) { - m_freem(m); - return (1); - } + (IFF_UP|IFF_RUNNING)) + goto drop; /* * Drop promiscuously received packets if we are not in @@ -335,10 +384,8 @@ vlan_input(struct ifnet *ifp, struct mbu (ifp->if_flags & IFF_PROMISC) && (ifv->ifv_if.if_flags & IFF_PROMISC) == 0) { if (bcmp(&ifv->ifv_ac.ac_enaddr, eh->ether_dhost, - ETHER_ADDR_LEN)) { - m_freem(m); - return (1); - } + ETHER_ADDR_LEN)) + goto drop; } /* @@ -356,6 +403,12 @@ vlan_input(struct ifnet *ifp, struct mbu ml_enqueue(&ml, m); if_input(&ifv->ifv_if, &ml); + SRPL_LEAVE(&i, ifv); + return (1); + +drop: + SRPL_LEAVE(&i, ifv); + m_freem(m); return (1); } @@ -363,9 +416,8 @@ int vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag) { struct sockaddr_dl *sdl1, *sdl2; - struct vlan_taghash *tagh; + struct srpl *tagh, *list; u_int flags; - int s; if (p->if_type != IFT_ETHER) return EPROTONOSUPPORT; @@ -437,14 +489,15 @@ vlan_config(struct ifvlan *ifv, struct i vlan_vlandev_state(ifv); - tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh; + /* Change input handler of the physical interface. */ + if_ih_insert(p, vlan_input, NULL); - s = splnet(); - LIST_INSERT_HEAD(&tagh[TAG_HASH(tag)], ifv, ifv_list); - splx(s); + tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh; + list = &tagh[TAG_HASH(tag)]; - /* Change input handler of the physical interface. */ - if_ih_insert(p, vlan_input, NULL); + rw_enter_write(&vlan_tagh_lk); + SRPL_INSERT_HEAD_LOCKED(&vlan_tagh_rc, list, ifv, ifv_list); + rw_exit_write(&vlan_tagh_lk); return (0); } @@ -454,8 +507,8 @@ vlan_unconfig(struct ifnet *ifp, struct { struct sockaddr_dl *sdl; struct ifvlan *ifv; + struct srpl *tagh, *list; struct ifnet *p; - int s; ifv = ifp->if_softc; if ((p = ifv->ifv_p) == NULL) @@ -467,9 +520,12 @@ vlan_unconfig(struct ifnet *ifp, struct vlan_set_promisc(ifp); } - s = splnet(); - LIST_REMOVE(ifv, ifv_list); - splx(s); + tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh; + list = &tagh[TAG_HASH(ifv->ifv_tag)]; + + rw_enter_write(&vlan_tagh_lk); + SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list); + rw_exit_write(&vlan_tagh_lk); /* Restore previous input handler. */ if_ih_remove(p, vlan_input, NULL); Index: if_vlan_var.h =================================================================== RCS file: /cvs/src/sys/net/if_vlan_var.h,v retrieving revision 1.26 diff -u -p -r1.26 if_vlan_var.h --- if_vlan_var.h 20 May 2015 08:54:37 -0000 1.26 +++ if_vlan_var.h 11 Sep 2015 13:24:20 -0000 @@ -83,8 +83,9 @@ struct ifvlan { u_int16_t ifvm_type; /* non-standard ethertype or 0x8100 */ } ifv_mib; LIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead; - LIST_ENTRY(ifvlan) ifv_list; + struct srpl_entry ifv_list; int ifv_flags; + u_int ifv_refs; void *lh_cookie; void *dh_cookie; struct ifih *ifv_ifih;