From d2b65bb7c8f0e6689682daadd47770c5c40cd85d Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Fri, 27 Jun 2014 23:24:28 +0400 Subject: ipoe: improved vlan monitor Previous version used linear list of vlan monitor enabled interfaces, this caused huge overhead in packet receive path because for each received packet it performed search on linear list to check if vlan monitor is enabled on input device. Current version binds internal structure to net_device->ml_priv so search is not performed more. --- drivers/ipoe/ipoe.c | 80 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 29 deletions(-) (limited to 'drivers/ipoe') diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 4a1a798b..eb8f8de1 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -38,6 +38,7 @@ #define HASH_BITS 0xff #define IPOE_MAGIC 0x55aa +#define IPOE_MAGIC2 0x67f8bc32 #define IPOE_QUEUE_LEN 100 #define IPOE_RATE_U 3000 //3s @@ -102,10 +103,11 @@ struct ipoe_entry_u { }; struct vlan_dev { + unsigned int magic; + int ifindex; struct rcu_head rcu_head; struct list_head entry; - int ifindex; spinlock_t lock; unsigned long vid[4096/8/sizeof(long)]; }; @@ -855,22 +857,19 @@ static int vlan_pt_recv(struct sk_buff *skb, struct net_device *dev, struct pack struct vlan_notify *n; int vid; + if (!dev->ml_priv) + goto out; + + d = dev->ml_priv; + if (d->magic != IPOE_MAGIC2 || d->ifindex != dev->ifindex) + goto out; + if (!vlan_tx_tag_present(skb)) goto out; vid = skb->vlan_tci & VLAN_VID_MASK; //pr_info("vid %i\n", vid); - rcu_read_lock(); - list_for_each_entry_rcu(d, &vlan_devices, entry) { - if (d->ifindex == dev->ifindex) - goto found; - } - rcu_read_unlock(); - goto out; - -found: - //pr_info("found %i\n", d->ifindex); if (d->vid[vid / (8*sizeof(long))] & (1lu << (vid % (8*sizeof(long))))) vid = -1; else { @@ -1629,15 +1628,23 @@ static int ipoe_nl_cmd_add_vlan_mon(struct sk_buff *skb, struct genl_info *info) if (!dev) return -ENODEV; + if (dev->ml_priv) { + dev_put(dev); + return -EBUSY; + } + d = kzalloc(sizeof(*d), GFP_KERNEL); if (!d) { dev_put(dev); return -ENOMEM; } + d->magic = IPOE_MAGIC2; d->ifindex = ifindex; spin_lock_init(&d->lock); + dev->ml_priv = d; + if (info->attrs[IPOE_ATTR_VLAN_MASK]) { memcpy(d->vid, nla_data(info->attrs[IPOE_ATTR_VLAN_MASK]), min((int)nla_len(info->attrs[IPOE_ATTR_VLAN_MASK]), (int)sizeof(d->vid))); @@ -1691,23 +1698,23 @@ static int ipoe_nl_cmd_add_vlan_mon_vid(struct sk_buff *skb, struct genl_info *i if (!dev) return -ENODEV; - down(&ipoe_wlock); - list_for_each_entry(d, &vlan_devices, entry) { - if (d->ifindex == ifindex) { - spin_lock_irqsave(&d->lock, flags); - d->vid[vid / (8*sizeof(long))] &= ~(1lu << (vid % (8*sizeof(long)))); - spin_unlock_irqrestore(&d->lock, flags); + if (!dev->ml_priv) { + dev_put(dev); + return -EINVAL; + } + + d = dev->ml_priv; + + spin_lock_irqsave(&d->lock, flags); + d->vid[vid / (8*sizeof(long))] &= ~(1lu << (vid % (8*sizeof(long)))); + spin_unlock_irqrestore(&d->lock, flags); #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) - if (dev->features & NETIF_F_HW_VLAN_FILTER) - dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid); + if (dev->features & NETIF_F_HW_VLAN_FILTER) + dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid); #else - if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) - dev->netdev_ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q), vid); + if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) + dev->netdev_ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q), vid); #endif - break; - } - } - up(&ipoe_wlock); dev_put(dev); @@ -1721,16 +1728,31 @@ static int ipoe_nl_cmd_del_vlan_mon(struct sk_buff *skb, struct genl_info *info) int ifindex; unsigned long flags; struct list_head *pos, *n; + struct net_device *dev; if (info->attrs[IPOE_ATTR_IFINDEX]) ifindex = nla_get_u32(info->attrs[IPOE_ATTR_IFINDEX]); else ifindex = -1; - rcu_read_lock(); - list_for_each_entry_rcu(d, &vlan_devices, entry) { + down(&ipoe_wlock); + list_for_each_entry(d, &vlan_devices, entry) { if (ifindex == -1 || d->ifindex == ifindex) { //pr_info("del net %08x/%08x\n", n->addr, n->mask); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) + rtnl_lock(); + dev = __dev_get_by_index(&init_net, d->ifindex); + rtnl_unlock(); +#else + dev = dev_get_by_index(&init_net, d->ifindex); +#endif + + if (dev) { + if (dev->ml_priv == d) + dev->ml_priv = NULL; + dev_put(dev); + } + list_del_rcu(&d->entry); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) kfree_rcu(d, rcu_head); @@ -1739,7 +1761,7 @@ static int ipoe_nl_cmd_del_vlan_mon(struct sk_buff *skb, struct genl_info *info) #endif } } - rcu_read_unlock(); + up(&ipoe_wlock); spin_lock_irqsave(&vlan_lock, flags); list_for_each_safe(pos, n, &vlan_notifies) { @@ -1902,7 +1924,7 @@ static int __init ipoe_init(void) { int err, i; - printk("IPoE session driver v1.8.0\n"); + printk("IPoE session driver v1.8.0.1\n"); /*err = register_pernet_device(&ipoe_net_ops); if (err < 0) -- cgit v1.2.3