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
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();
 
_______________________________________________
freebsd-net@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to