vlan patch
Andrew Thompson
thompsa at freebsd.org
Wed Oct 19 14:21:00 PDT 2005
On Wed, Oct 19, 2005 at 01:19:27PM +0200, Andre Oppermann wrote:
> Andrew Thompson wrote:
> >
> > Hi,
> >
> > It has always bugged me how the vlan code traverses the linked-list for
> > each incoming packet to find the right ifvlan, I have this patch which
> > attempts to fix this.
> >
>
> I like the concept. You may want to switch to direct indexing only
> when the number of vlan exceeds 10 or so.
>
Thanks for the feedback (and others too),
I have updated the patch to switch between array and linked-list
searching depending on the number of vlans.
cheers,
Andrew
-------------- next part --------------
Index: if_var.h
===================================================================
RCS file: /home/ncvs/src/sys/net/if_var.h,v
retrieving revision 1.103
diff -u -p -r1.103 if_var.h
--- if_var.h 1 Oct 2005 18:56:18 -0000 1.103
+++ if_var.h 19 Oct 2005 19:35:55 -0000
@@ -134,6 +134,7 @@ struct ifnet {
u_short if_index; /* numeric abbreviation for this if */
short if_timer; /* time 'til if_watchdog called */
u_short if_nvlans; /* number of active vlans */
+ /* correct location for *if_vlantags when changing ABI */
int if_flags; /* up/down, broadcast, etc. */
int if_capabilities; /* interface capabilities */
int if_capenable; /* enabled features */
@@ -180,6 +181,7 @@ struct ifnet {
struct task if_starttask; /* task for IFF_NEEDSGIANT */
struct task if_linktask; /* task for link change events */
struct mtx if_addr_mtx; /* mutex to protect address lists */
+ void *if_vlantags; /* array to hold active vlans */
};
typedef void if_init_f_t(void *);
Index: if_vlan.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_vlan.c,v
retrieving revision 1.88
diff -u -p -r1.88 if_vlan.c
--- if_vlan.c 3 Oct 2005 02:24:21 -0000 1.88
+++ if_vlan.c 19 Oct 2005 21:09:56 -0000
@@ -69,6 +69,7 @@
#endif
#define VLANNAME "vlan"
+#define VLAN_LIST_THRESHOLD 10
struct vlan_mc_entry {
struct ether_addr mc_addr;
@@ -133,7 +134,7 @@ static int vlan_setflag(struct ifnet *if
static int vlan_setflags(struct ifnet *ifp, int status);
static int vlan_setmulti(struct ifnet *ifp);
static int vlan_unconfig(struct ifnet *ifp);
-static int vlan_config(struct ifvlan *ifv, struct ifnet *p);
+static int vlan_config(struct ifvlan *ifv, struct ifnet *p, int tag);
static void vlan_link_state(struct ifnet *ifp, int link);
static struct ifnet *vlan_clone_match_ethertag(struct if_clone *,
@@ -406,7 +407,7 @@ vlan_clone_create(struct if_clone *ifc,
if (ethertag) {
VLAN_LOCK();
- error = vlan_config(ifv, p);
+ error = vlan_config(ifv, p, tag);
if (error != 0) {
/*
* Since we've partialy failed, we need to back
@@ -422,7 +423,6 @@ vlan_clone_create(struct if_clone *ifc,
return (error);
}
- ifv->ifv_tag = tag;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
VLAN_UNLOCK();
@@ -568,10 +568,13 @@ static void
vlan_input(struct ifnet *ifp, struct mbuf *m)
{
struct ether_vlan_header *evl;
- struct ifvlan *ifv;
+ struct ifvlan *ifv = NULL;
+ struct ifvlan **ifv_tags;
struct m_tag *mtag;
u_int tag;
+ ifv_tags = ifp->if_vlantags;
+
if (m->m_flags & M_VLANTAG) {
/*
* Packet is tagged, but m contains a normal
@@ -620,9 +623,16 @@ vlan_input(struct ifnet *ifp, struct mbu
}
VLAN_LOCK();
- LIST_FOREACH(ifv, &ifv_list, ifv_list)
- if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
- break;
+ /* Look up the vlan in the parents tag array if allocated. O(1) */
+ if (ifp->if_vlantags != NULL)
+ ifv = ifv_tags[tag];
+ else {
+ /* Fall back to a linear search. O(n) */
+ LIST_FOREACH(ifv, &ifv_list, ifv_list)
+ if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
+ break;
+ }
+
if (ifv == NULL || (ifv->ifv_ifp->if_flags & IFF_UP) == 0) {
VLAN_UNLOCK();
@@ -658,18 +668,28 @@ vlan_input(struct ifnet *ifp, struct mbu
}
static int
-vlan_config(struct ifvlan *ifv, struct ifnet *p)
+vlan_config(struct ifvlan *ifv, struct ifnet *p, int tag)
{
struct ifaddr *ifa1, *ifa2;
struct ifnet *ifp;
struct sockaddr_dl *sdl1, *sdl2;
+ struct ifvlan *ifv2, **ifv_tags;
VLAN_LOCK_ASSERT();
+ ifv_tags = p->if_vlantags;
+
+ /* VID numbers 0x0 and 0xFFF are reserved */
+ if (tag == 0 || tag == EVL_VLID_MASK)
+ return (EINVAL);
if (p->if_type != IFT_ETHER)
return (EPROTONOSUPPORT);
if (ifv->ifv_p)
return (EBUSY);
+ /* Ensure unique ifnet + tag */
+ LIST_FOREACH(ifv2, &ifv_list, ifv_list)
+ if (p == ifv2->ifv_p && tag == ifv2->ifv_tag)
+ return (EBUSY);
ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
ifv->ifv_mintu = ETHERMIN;
@@ -682,6 +702,27 @@ vlan_config(struct ifvlan *ifv, struct i
*/
p->if_nvlans++;
+ /* Allocate the parents tag array */
+ if (p->if_vlantags == NULL && p->if_nvlans > VLAN_LIST_THRESHOLD) {
+ p->if_vlantags = malloc(
+ (int)(EVL_VLID_MASK * sizeof(struct ifvlan *)),
+ M_VLAN, M_WAITOK | M_ZERO);
+ ifv_tags = p->if_vlantags;
+
+#ifdef DEBUG
+ printf("vlan: changing to array lookup for %s\n", p->if_xname);
+#endif
+
+ /* Fill it with existing vlans */
+ LIST_FOREACH(ifv2, &ifv_list, ifv_list)
+ if (ifv2->ifv_p == p)
+ ifv_tags[ifv2->ifv_tag] = ifv2;
+ }
+
+ ifv->ifv_tag = tag;
+ if (p->if_vlantags != NULL)
+ ifv_tags[tag] = ifv;
+
/*
* If the parent supports the VLAN_MTU capability,
* i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
@@ -763,6 +804,7 @@ vlan_unconfig(struct ifnet *ifp)
struct sockaddr_dl *sdl;
struct vlan_mc_entry *mc;
struct ifvlan *ifv;
+ struct ifvlan **ifv_tags;
struct ifnet *p;
int error;
@@ -798,7 +840,22 @@ vlan_unconfig(struct ifnet *ifp)
}
vlan_setflags(ifp, 0); /* clear special flags on parent */
+
+ ifv_tags = p->if_vlantags;
+ if (p->if_vlantags != NULL && ifv->ifv_tag > 0)
+ ifv_tags[ifv->ifv_tag] = NULL;
+
p->if_nvlans--;
+ /* Deallocate the parents tag array */
+ if (p->if_vlantags != NULL &&
+ p->if_nvlans <= VLAN_LIST_THRESHOLD) {
+ free(p->if_vlantags, M_VLAN);
+ p->if_vlantags = NULL;
+#ifdef DEBUG
+ printf("vlan: changing to list lookup for %s\n",
+ p->if_xname);
+#endif
+ }
}
/* Disconnect from parent. */
@@ -998,12 +1055,11 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
break;
}
VLAN_LOCK();
- error = vlan_config(ifv, p);
+ error = vlan_config(ifv, p, vlr.vlr_tag);
if (error) {
VLAN_UNLOCK();
break;
}
- ifv->ifv_tag = vlr.vlr_tag;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
VLAN_UNLOCK();
More information about the freebsd-net
mailing list