* Ethernet-type device handling.
*
* Authors: Ben Greear <greearb@candelatech.com>
- * Please send support related email to: vlan@scry.wanfear.com
+ * Please send support related email to: netdev@vger.kernel.org
* VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
*
* Fixes:
/* Our listing of VLAN group(s) */
static struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE];
-#define vlan_grp_hashfn(IDX) ((((IDX) >> VLAN_GRP_HASH_SHIFT) ^ (IDX)) & VLAN_GRP_HASH_MASK)
static char vlan_fullname[] = "802.1Q VLAN Support";
static char vlan_version[] = DRV_VERSION;
/* End of global variables definitions. */
+static inline unsigned int vlan_grp_hashfn(unsigned int idx)
+{
+ return ((idx >> VLAN_GRP_HASH_SHIFT) ^ idx) & VLAN_GRP_HASH_MASK;
+}
+
/* Must be invoked with RCU read lock (no preempt) */
static struct vlan_group *__vlan_find_group(int real_dev_ifindex)
{
{
int i;
- for (i=0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++)
+ for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++)
kfree(grp->vlan_devices_arrays[i]);
kfree(grp);
}
vlan_group_free(container_of(rcu, struct vlan_group, rcu));
}
-
-/* This returns 0 if everything went fine.
- * It will return 1 if the group was killed as a result.
- * A negative return indicates failure.
- *
- * The RTNL lock must be held.
- */
-static int unregister_vlan_dev(struct net_device *real_dev,
- unsigned short vlan_id)
+void unregister_vlan_dev(struct net_device *dev)
{
- struct net_device *dev;
- int real_dev_ifindex = real_dev->ifindex;
+ struct vlan_dev_info *vlan = vlan_dev_info(dev);
+ struct net_device *real_dev = vlan->real_dev;
struct vlan_group *grp;
- unsigned int i;
- int ret;
-
- if (vlan_id >= VLAN_VID_MASK)
- return -EINVAL;
+ unsigned short vlan_id = vlan->vlan_id;
ASSERT_RTNL();
- grp = __vlan_find_group(real_dev_ifindex);
- if (!grp)
- return -ENOENT;
- dev = vlan_group_get_device(grp, vlan_id);
- if (!dev)
- return -ENOENT;
+ grp = __vlan_find_group(real_dev->ifindex);
+ BUG_ON(!grp);
vlan_proc_rem_dev(dev);
real_dev->vlan_rx_kill_vid(real_dev, vlan_id);
vlan_group_set_device(grp, vlan_id, NULL);
- synchronize_net();
+ grp->nr_vlans--;
- /* Caller unregisters (and if necessary, puts) VLAN device, but we
- * get rid of the reference to real_dev here.
- */
- dev_put(real_dev);
+ synchronize_net();
/* If the group is now empty, kill off the group. */
- ret = 0;
- for (i = 0; i < VLAN_VID_MASK; i++)
- if (vlan_group_get_device(grp, i))
- break;
-
- if (i == VLAN_VID_MASK) {
+ if (grp->nr_vlans == 0) {
if (real_dev->features & NETIF_F_HW_VLAN_RX)
real_dev->vlan_rx_register(real_dev, NULL);
/* Free the group, after all cpu's are done. */
call_rcu(&grp->rcu, vlan_rcu_free);
- ret = 1;
}
- return ret;
-}
-
-int unregister_vlan_device(struct net_device *dev)
-{
- int ret;
+ /* Get rid of the vlan's reference to real_dev */
+ dev_put(real_dev);
- ret = unregister_vlan_dev(VLAN_DEV_INFO(dev)->real_dev,
- VLAN_DEV_INFO(dev)->vlan_id);
unregister_netdevice(dev);
-
- if (ret == 1)
- ret = 0;
- return ret;
}
-static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev)
+static void vlan_transfer_operstate(const struct net_device *dev,
+ struct net_device *vlandev)
{
/* Have to respect userspace enforced dormant state
* of real device, also must allow supplicant running
int register_vlan_dev(struct net_device *dev)
{
- struct vlan_dev_info *vlan = VLAN_DEV_INFO(dev);
+ struct vlan_dev_info *vlan = vlan_dev_info(dev);
struct net_device *real_dev = vlan->real_dev;
unsigned short vlan_id = vlan->vlan_id;
struct vlan_group *grp, *ngrp = NULL;
* it into our local structure.
*/
vlan_group_set_device(grp, vlan_id, dev);
+ grp->nr_vlans++;
+
if (ngrp && real_dev->features & NETIF_F_HW_VLAN_RX)
real_dev->vlan_rx_register(real_dev, ngrp);
if (real_dev->features & NETIF_F_HW_VLAN_FILTER)
*/
new_dev->mtu = real_dev->mtu;
- VLAN_DEV_INFO(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */
- VLAN_DEV_INFO(new_dev)->real_dev = real_dev;
- VLAN_DEV_INFO(new_dev)->dent = NULL;
- VLAN_DEV_INFO(new_dev)->flags = VLAN_FLAG_REORDER_HDR;
+ vlan_dev_info(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */
+ vlan_dev_info(new_dev)->real_dev = real_dev;
+ vlan_dev_info(new_dev)->dent = NULL;
+ vlan_dev_info(new_dev)->flags = VLAN_FLAG_REORDER_HDR;
new_dev->rtnl_link_ops = &vlan_link_ops;
err = register_vlan_dev(new_dev);
static void vlan_sync_address(struct net_device *dev,
struct net_device *vlandev)
{
- struct vlan_dev_info *vlan = VLAN_DEV_INFO(vlandev);
+ struct vlan_dev_info *vlan = vlan_dev_info(vlandev);
/* May be called without an actual change */
if (!compare_ether_addr(vlan->real_dev_addr, dev->dev_addr))
memcpy(vlan->real_dev_addr, dev->dev_addr, ETH_ALEN);
}
-static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
+static int vlan_device_event(struct notifier_block *unused, unsigned long event,
+ void *ptr)
{
struct net_device *dev = ptr;
struct vlan_group *grp = __vlan_find_group(dev->ifindex);
case NETDEV_UNREGISTER:
/* Delete all VLANs for this dev. */
for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- int ret;
-
vlandev = vlan_group_get_device(grp, i);
if (!vlandev)
continue;
- ret = unregister_vlan_dev(dev,
- VLAN_DEV_INFO(vlandev)->vlan_id);
-
- unregister_netdevice(vlandev);
+ /* unregistration of last vlan destroys group, abort
+ * afterwards */
+ if (grp->nr_vlans == 1)
+ i = VLAN_GROUP_ARRAY_LEN;
- /* Group was destroyed? */
- if (ret == 1)
- break;
+ unregister_vlan_dev(vlandev);
}
break;
}
err = -EPERM;
if (!capable(CAP_NET_ADMIN))
break;
- err = unregister_vlan_device(dev);
+ unregister_vlan_dev(dev);
+ err = 0;
break;
case GET_VLAN_REALDEV_NAME_CMD:
err = 0;
vlan_dev_get_realdev_name(dev, args.u.device2);
if (copy_to_user(arg, &args,
- sizeof(struct vlan_ioctl_args))) {
+ sizeof(struct vlan_ioctl_args)))
err = -EFAULT;
- }
break;
case GET_VLAN_VID_CMD:
vlan_dev_get_vid(dev, &vid);
args.u.VID = vid;
if (copy_to_user(arg, &args,
- sizeof(struct vlan_ioctl_args))) {
+ sizeof(struct vlan_ioctl_args)))
err = -EFAULT;
- }
break;
default: