X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=net%2F8021q%2Fvlan.c;h=453512266ea1dd783dbc68f5e6b511dd64ec5ac5;hb=2c8c1e7297e19bdef3c178c3ea41d898a7716e3e;hp=5b183156307a6c3f2ddc245206e2892b53f4d7c5;hpb=e35de02615f97b785dc6f73cba421cea06bcbd10;p=safe%2Fjmp%2Flinux-2.6 diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 5b18315..4535122 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -3,7 +3,7 @@ * Ethernet-type device handling. * * Authors: Ben Greear - * 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: @@ -18,20 +18,20 @@ * 2 of the License, or (at your option) any later version. */ -#include /* for copy_from_user */ #include #include #include #include -#include -#include -#include #include +#include #include #include #include #include +#include #include +#include +#include #include #include "vlan.h" @@ -41,120 +41,37 @@ /* Global VLAN variables */ +int vlan_net_id __read_mostly; + /* 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; -static char vlan_copyright[] = "Ben Greear "; -static char vlan_buggyright[] = "David S. Miller "; - -static int vlan_device_event(struct notifier_block *, unsigned long, void *); -static int vlan_ioctl_handler(struct net *net, void __user *); -static int unregister_vlan_dev(struct net_device *, unsigned short ); - -static struct notifier_block vlan_notifier_block = { - .notifier_call = vlan_device_event, -}; -/* These may be changed at run-time through IOCTLs */ +const char vlan_fullname[] = "802.1Q VLAN Support"; +const char vlan_version[] = DRV_VERSION; +static const char vlan_copyright[] = "Ben Greear "; +static const char vlan_buggyright[] = "David S. Miller "; -/* Determines interface naming scheme. */ -unsigned short vlan_name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; - -static struct packet_type vlan_packet_type = { - .type = __constant_htons(ETH_P_8021Q), +static struct packet_type vlan_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_8021Q), .func = vlan_skb_recv, /* VLAN receive method */ }; /* End of global variables definitions. */ -/* - * Function vlan_proto_init (pro) - * - * Initialize VLAN protocol layer, - * - */ -static int __init vlan_proto_init(void) +static inline unsigned int vlan_grp_hashfn(unsigned int idx) { - int err; - - printk(VLAN_INF "%s v%s %s\n", - vlan_fullname, vlan_version, vlan_copyright); - printk(VLAN_INF "All bugs added by %s\n", - vlan_buggyright); - - /* proc file system initialization */ - err = vlan_proc_init(); - if (err < 0) { - printk(KERN_ERR - "%s %s: can't create entry in proc filesystem!\n", - __FUNCTION__, VLAN_NAME); - return err; - } - - dev_add_pack(&vlan_packet_type); - - /* Register us to receive netdevice events */ - err = register_netdevice_notifier(&vlan_notifier_block); - if (err < 0) - goto err1; - - err = vlan_netlink_init(); - if (err < 0) - goto err2; - - vlan_ioctl_set(vlan_ioctl_handler); - return 0; - -err2: - unregister_netdevice_notifier(&vlan_notifier_block); -err1: - vlan_proc_cleanup(); - dev_remove_pack(&vlan_packet_type); - return err; -} - -/* - * Module 'remove' entry point. - * o delete /proc/net/router directory and static entries. - */ -static void __exit vlan_cleanup_module(void) -{ - int i; - - vlan_netlink_fini(); - vlan_ioctl_set(NULL); - - /* Un-register us from receiving netdevice events */ - unregister_netdevice_notifier(&vlan_notifier_block); - - dev_remove_pack(&vlan_packet_type); - - /* This table must be empty if there are no module - * references left. - */ - for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) { - BUG_ON(!hlist_empty(&vlan_group_hash[i])); - } - vlan_proc_cleanup(); - - synchronize_net(); + return ((idx >> VLAN_GRP_HASH_SHIFT) ^ idx) & VLAN_GRP_HASH_MASK; } -module_init(vlan_proto_init); -module_exit(vlan_cleanup_module); - /* Must be invoked with RCU read lock (no preempt) */ -static struct vlan_group *__vlan_find_group(int real_dev_ifindex) +static struct vlan_group *__vlan_find_group(struct net_device *real_dev) { struct vlan_group *grp; struct hlist_node *n; - int hash = vlan_grp_hashfn(real_dev_ifindex); + int hash = vlan_grp_hashfn(real_dev->ifindex); hlist_for_each_entry_rcu(grp, n, &vlan_group_hash[hash], hlist) { - if (grp->real_dev_ifindex == real_dev_ifindex) + if (grp->real_dev == real_dev) return grp; } @@ -165,13 +82,12 @@ static struct vlan_group *__vlan_find_group(int real_dev_ifindex) * * Must be invoked with RCU read lock (no preempt) */ -struct net_device *__find_vlan_dev(struct net_device *real_dev, - unsigned short VID) +struct net_device *__find_vlan_dev(struct net_device *real_dev, u16 vlan_id) { - struct vlan_group *grp = __vlan_find_group(real_dev->ifindex); + struct vlan_group *grp = __vlan_find_group(real_dev); if (grp) - return vlan_group_get_device(grp, VID); + return vlan_group_get_device(grp, vlan_id); return NULL; } @@ -180,37 +96,43 @@ static void vlan_group_free(struct vlan_group *grp) { 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); } -static struct vlan_group *vlan_group_alloc(int ifindex) +static struct vlan_group *vlan_group_alloc(struct net_device *real_dev) { struct vlan_group *grp; - unsigned int size; - unsigned int i; grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL); if (!grp) return NULL; - size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN; - - for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) { - grp->vlan_devices_arrays[i] = kzalloc(size, GFP_KERNEL); - if (!grp->vlan_devices_arrays[i]) - goto err; - } - - grp->real_dev_ifindex = ifindex; + grp->real_dev = real_dev; hlist_add_head_rcu(&grp->hlist, - &vlan_group_hash[vlan_grp_hashfn(ifindex)]); + &vlan_group_hash[vlan_grp_hashfn(real_dev->ifindex)]); return grp; +} -err: - vlan_group_free(grp); - return NULL; +static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) +{ + struct net_device **array; + unsigned int size; + + ASSERT_RTNL(); + + array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN]; + if (array != NULL) + return 0; + + size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN; + array = kzalloc(size, GFP_KERNEL); + if (array == NULL) + return -ENOBUFS; + + vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN] = array; + return 0; } static void vlan_rcu_free(struct rcu_head *rcu) @@ -218,358 +140,202 @@ static void vlan_rcu_free(struct rcu_head *rcu) 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 list_head *head) { - struct net_device *dev = NULL; - int real_dev_ifindex = real_dev->ifindex; + struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct net_device *real_dev = vlan->real_dev; + const struct net_device_ops *ops = real_dev->netdev_ops; struct vlan_group *grp; - int i, ret; - -#ifdef VLAN_DEBUG - printk(VLAN_DBG "%s: VID: %i\n", __FUNCTION__, vlan_id); -#endif - - /* sanity check */ - if (vlan_id >= VLAN_VID_MASK) - return -EINVAL; + u16 vlan_id = vlan->vlan_id; ASSERT_RTNL(); - grp = __vlan_find_group(real_dev_ifindex); - - ret = 0; - - if (grp) { - dev = vlan_group_get_device(grp, vlan_id); - if (dev) { - /* Remove proc entry */ - vlan_proc_rem_dev(dev); - - /* Take it out of our own structures, but be sure to - * interlock with HW accelerating devices or SW vlan - * input packet processing. - */ - if (real_dev->features & NETIF_F_HW_VLAN_FILTER) - real_dev->vlan_rx_kill_vid(real_dev, vlan_id); - - vlan_group_set_device(grp, vlan_id, NULL); - synchronize_net(); - - - /* Caller unregisters (and if necessary, puts) - * VLAN device, but we get rid of the reference to - * real_dev here. - */ - dev_put(real_dev); - - /* If the group is now empty, kill off the - * group. - */ - for (i = 0; i < VLAN_VID_MASK; i++) - if (vlan_group_get_device(grp, i)) - break; - - if (i == VLAN_VID_MASK) { - if (real_dev->features & NETIF_F_HW_VLAN_RX) - real_dev->vlan_rx_register(real_dev, NULL); - - hlist_del_rcu(&grp->hlist); - /* Free the group, after all cpu's are done. */ - call_rcu(&grp->rcu, vlan_rcu_free); + grp = __vlan_find_group(real_dev); + BUG_ON(!grp); - grp = NULL; - ret = 1; - } - } - } + /* Take it out of our own structures, but be sure to interlock with + * HW accelerating devices or SW vlan input packet processing. + */ + if (real_dev->features & NETIF_F_HW_VLAN_FILTER) + ops->ndo_vlan_rx_kill_vid(real_dev, vlan_id); - return ret; -} + grp->nr_vlans--; -int unregister_vlan_device(struct net_device *dev) -{ - int ret; + vlan_group_set_device(grp, vlan_id, NULL); + if (!grp->killall) + synchronize_net(); - ret = unregister_vlan_dev(VLAN_DEV_INFO(dev)->real_dev, - VLAN_DEV_INFO(dev)->vlan_id); - unregister_netdevice(dev); + unregister_netdevice_queue(dev, head); - if (ret == 1) - ret = 0; - return ret; -} + /* If the group is now empty, kill off the group. */ + if (grp->nr_vlans == 0) { + vlan_gvrp_uninit_applicant(real_dev); -/* - * vlan network devices have devices nesting below it, and are a special - * "super class" of normal network devices; split their locks off into a - * separate class since they always nest. - */ -static struct lock_class_key vlan_netdev_xmit_lock_key; + if (real_dev->features & NETIF_F_HW_VLAN_RX) + ops->ndo_vlan_rx_register(real_dev, NULL); -static const struct header_ops vlan_header_ops = { - .create = vlan_dev_hard_header, - .rebuild = vlan_dev_rebuild_header, - .parse = eth_header_parse, -}; + hlist_del_rcu(&grp->hlist); -static int vlan_dev_init(struct net_device *dev) -{ - struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev; - - /* IFF_BROADCAST|IFF_MULTICAST; ??? */ - dev->flags = real_dev->flags & ~IFF_UP; - dev->iflink = real_dev->ifindex; - dev->state = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) | - (1<<__LINK_STATE_DORMANT))) | - (1<<__LINK_STATE_PRESENT); - - /* ipv6 shared card related stuff */ - dev->dev_id = real_dev->dev_id; - - if (is_zero_ether_addr(dev->dev_addr)) - memcpy(dev->dev_addr, real_dev->dev_addr, dev->addr_len); - if (is_zero_ether_addr(dev->broadcast)) - memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len); - - if (real_dev->features & NETIF_F_HW_VLAN_TX) { - dev->header_ops = real_dev->header_ops; - dev->hard_header_len = real_dev->hard_header_len; - dev->hard_start_xmit = vlan_dev_hwaccel_hard_start_xmit; - } else { - dev->header_ops = &vlan_header_ops; - dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN; - dev->hard_start_xmit = vlan_dev_hard_start_xmit; + /* Free the group, after all cpu's are done. */ + call_rcu(&grp->rcu, vlan_rcu_free); } - lockdep_set_class(&dev->_xmit_lock, &vlan_netdev_xmit_lock_key); - return 0; -} - -void vlan_setup(struct net_device *new_dev) -{ - ether_setup(new_dev); - - /* new_dev->ifindex = 0; it will be set when added to - * the global list. - * iflink is set as well. - */ - new_dev->get_stats = vlan_dev_get_stats; - - /* Make this thing known as a VLAN device */ - new_dev->priv_flags |= IFF_802_1Q_VLAN; - - /* Set us up to have no queue, as the underlying Hardware device - * can do all the queueing we could want. - */ - new_dev->tx_queue_len = 0; - - /* set up method calls */ - new_dev->change_mtu = vlan_dev_change_mtu; - new_dev->init = vlan_dev_init; - new_dev->open = vlan_dev_open; - new_dev->stop = vlan_dev_stop; - new_dev->set_mac_address = vlan_set_mac_address; - new_dev->set_multicast_list = vlan_dev_set_multicast_list; - new_dev->change_rx_flags = vlan_change_rx_flags; - new_dev->destructor = free_netdev; - new_dev->do_ioctl = vlan_dev_ioctl; - - memset(new_dev->broadcast, 0, ETH_ALEN); + /* Get rid of the vlan's reference to real_dev */ + dev_put(real_dev); } -static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev) +int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) { - /* Have to respect userspace enforced dormant state - * of real device, also must allow supplicant running - * on VLAN device - */ - if (dev->operstate == IF_OPER_DORMANT) - netif_dormant_on(vlandev); - else - netif_dormant_off(vlandev); - - if (netif_carrier_ok(dev)) { - if (!netif_carrier_ok(vlandev)) - netif_carrier_on(vlandev); - } else { - if (netif_carrier_ok(vlandev)) - netif_carrier_off(vlandev); - } -} + const char *name = real_dev->name; + const struct net_device_ops *ops = real_dev->netdev_ops; -int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id) -{ if (real_dev->features & NETIF_F_VLAN_CHALLENGED) { - printk(VLAN_DBG "%s: VLANs not supported on %s.\n", - __FUNCTION__, real_dev->name); + pr_info("8021q: VLANs not supported on %s\n", name); return -EOPNOTSUPP; } - if ((real_dev->features & NETIF_F_HW_VLAN_RX) && - !real_dev->vlan_rx_register) { - printk(VLAN_DBG "%s: Device %s has buggy VLAN hw accel.\n", - __FUNCTION__, real_dev->name); + if ((real_dev->features & NETIF_F_HW_VLAN_RX) && !ops->ndo_vlan_rx_register) { + pr_info("8021q: device %s has buggy VLAN hw accel\n", name); return -EOPNOTSUPP; } if ((real_dev->features & NETIF_F_HW_VLAN_FILTER) && - (!real_dev->vlan_rx_add_vid || !real_dev->vlan_rx_kill_vid)) { - printk(VLAN_DBG "%s: Device %s has buggy VLAN hw accel.\n", - __FUNCTION__, real_dev->name); + (!ops->ndo_vlan_rx_add_vid || !ops->ndo_vlan_rx_kill_vid)) { + pr_info("8021q: Device %s has buggy VLAN hw accel\n", name); return -EOPNOTSUPP; } - /* The real device must be up and operating in order to - * assosciate a VLAN device with it. - */ - if (!(real_dev->flags & IFF_UP)) - return -ENETDOWN; - - if (__find_vlan_dev(real_dev, vlan_id) != NULL) { - /* was already registered. */ - printk(VLAN_DBG "%s: ALREADY had VLAN registered\n", __FUNCTION__); + if (__find_vlan_dev(real_dev, vlan_id) != NULL) return -EEXIST; - } return 0; } 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; + const struct net_device_ops *ops = real_dev->netdev_ops; + u16 vlan_id = vlan->vlan_id; struct vlan_group *grp, *ngrp = NULL; int err; - grp = __vlan_find_group(real_dev->ifindex); + grp = __vlan_find_group(real_dev); if (!grp) { - ngrp = grp = vlan_group_alloc(real_dev->ifindex); + ngrp = grp = vlan_group_alloc(real_dev); if (!grp) return -ENOBUFS; + err = vlan_gvrp_init_applicant(real_dev); + if (err < 0) + goto out_free_group; } + err = vlan_group_prealloc_vid(grp, vlan_id); + if (err < 0) + goto out_uninit_applicant; + err = register_netdevice(dev); if (err < 0) - goto out_free_group; + goto out_uninit_applicant; /* Account for reference in struct vlan_dev_info */ dev_hold(real_dev); - vlan_transfer_operstate(real_dev, dev); + netif_stacked_transfer_operstate(real_dev, dev); linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */ /* So, got the sucker initialized, now lets place * 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); + ops->ndo_vlan_rx_register(real_dev, ngrp); if (real_dev->features & NETIF_F_HW_VLAN_FILTER) - real_dev->vlan_rx_add_vid(real_dev, vlan_id); + ops->ndo_vlan_rx_add_vid(real_dev, vlan_id); - if (vlan_proc_add_dev(dev) < 0) - printk(KERN_WARNING "VLAN: failed to add proc entry for %s\n", - dev->name); return 0; -out_free_group: +out_uninit_applicant: if (ngrp) - vlan_group_free(ngrp); + vlan_gvrp_uninit_applicant(real_dev); +out_free_group: + if (ngrp) { + hlist_del_rcu(&ngrp->hlist); + /* Free the group, after all cpu's are done. */ + call_rcu(&ngrp->rcu, vlan_rcu_free); + } return err; } /* Attach a VLAN device to a mac address (ie Ethernet Card). * Returns 0 if the device was created or a negative error code otherwise. */ -static int register_vlan_device(struct net_device *real_dev, - unsigned short VLAN_ID) +static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) { struct net_device *new_dev; + struct net *net = dev_net(real_dev); + struct vlan_net *vn = net_generic(net, vlan_net_id); char name[IFNAMSIZ]; int err; -#ifdef VLAN_DEBUG - printk(VLAN_DBG "%s: if_name -:%s:- vid: %i\n", - __FUNCTION__, eth_IF_name, VLAN_ID); -#endif - - if (VLAN_ID >= VLAN_VID_MASK) + if (vlan_id >= VLAN_VID_MASK) return -ERANGE; - err = vlan_check_real_dev(real_dev, VLAN_ID); + err = vlan_check_real_dev(real_dev, vlan_id); if (err < 0) return err; /* Gotta set up the fields for the device. */ -#ifdef VLAN_DEBUG - printk(VLAN_DBG "About to allocate name, vlan_name_type: %i\n", - vlan_name_type); -#endif - switch (vlan_name_type) { + switch (vn->name_type) { case VLAN_NAME_TYPE_RAW_PLUS_VID: /* name will look like: eth1.0005 */ - snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, VLAN_ID); + snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, vlan_id); break; case VLAN_NAME_TYPE_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: vlan5 */ - snprintf(name, IFNAMSIZ, "vlan%i", VLAN_ID); + snprintf(name, IFNAMSIZ, "vlan%i", vlan_id); break; case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: eth0.5 */ - snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, VLAN_ID); + snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, vlan_id); break; case VLAN_NAME_TYPE_PLUS_VID: /* Put our vlan.VID in the name. * Name will look like: vlan0005 */ default: - snprintf(name, IFNAMSIZ, "vlan%.4i", VLAN_ID); + snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); } - new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name, - vlan_setup); + new_dev = alloc_netdev_mq(sizeof(struct vlan_dev_info), name, + vlan_setup, real_dev->num_tx_queues); if (new_dev == NULL) return -ENOBUFS; + new_dev->real_num_tx_queues = real_dev->real_num_tx_queues; + dev_net_set(new_dev, net); /* need 4 bytes for extra VLAN header info, * hope the underlying device can handle it. */ new_dev->mtu = real_dev->mtu; -#ifdef VLAN_DEBUG - printk(VLAN_DBG "Allocated new name -:%s:-\n", new_dev->name); - VLAN_MEM_DBG("new_dev->priv malloc, addr: %p size: %i\n", - new_dev->priv, - sizeof(struct vlan_dev_info)); -#endif - - 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; + 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); if (err < 0) goto out_free_newdev; -#ifdef VLAN_DEBUG - printk(VLAN_DBG "Allocated new device successfully, returning.\n"); -#endif return 0; out_free_newdev: @@ -580,7 +346,7 @@ out_free_newdev: 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)) @@ -590,27 +356,67 @@ static void vlan_sync_address(struct net_device *dev, * the new address */ if (compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) && !compare_ether_addr(vlandev->dev_addr, dev->dev_addr)) - dev_unicast_delete(dev, vlandev->dev_addr, ETH_ALEN); + dev_unicast_delete(dev, vlandev->dev_addr); /* vlan address was equal to the old address and is different from * the new address */ if (!compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) && compare_ether_addr(vlandev->dev_addr, dev->dev_addr)) - dev_unicast_add(dev, vlandev->dev_addr, ETH_ALEN); + dev_unicast_add(dev, vlandev->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 void vlan_transfer_features(struct net_device *dev, + struct net_device *vlandev) +{ + unsigned long old_features = vlandev->features; + + vlandev->features &= ~dev->vlan_features; + vlandev->features |= dev->features & dev->vlan_features; + vlandev->gso_max_size = dev->gso_max_size; +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) + vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; +#endif + + if (old_features != vlandev->features) + netdev_features_change(vlandev); +} + +static void __vlan_device_event(struct net_device *dev, unsigned long event) +{ + switch (event) { + case NETDEV_CHANGENAME: + vlan_proc_rem_dev(dev); + if (vlan_proc_add_dev(dev) < 0) + pr_warning("8021q: failed to change proc name for %s\n", + dev->name); + break; + case NETDEV_REGISTER: + if (vlan_proc_add_dev(dev) < 0) + pr_warning("8021q: failed to add proc entry for %s\n", + dev->name); + break; + case NETDEV_UNREGISTER: + vlan_proc_rem_dev(dev); + break; + } +} + +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); + struct vlan_group *grp; int i, flgs; struct net_device *vlandev; + struct vlan_dev_info *vlan; + LIST_HEAD(list); - if (dev->nd_net != &init_net) - return NOTIFY_DONE; + if (is_vlan_dev(dev)) + __vlan_device_event(dev, event); + grp = __vlan_find_group(dev); if (!grp) goto out; @@ -626,7 +432,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (!vlandev) continue; - vlan_transfer_operstate(dev, vlandev); + netif_stacked_transfer_operstate(dev, vlandev); } break; @@ -645,6 +451,31 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, } break; + case NETDEV_CHANGEMTU: + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { + vlandev = vlan_group_get_device(grp, i); + if (!vlandev) + continue; + + if (vlandev->mtu <= dev->mtu) + continue; + + dev_set_mtu(vlandev, dev->mtu); + } + break; + + case NETDEV_FEAT_CHANGE: + /* Propagate device features to underlying device */ + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { + vlandev = vlan_group_get_device(grp, i); + if (!vlandev) + continue; + + vlan_transfer_features(dev, vlandev); + } + + break; + case NETDEV_DOWN: /* Put all VLANs for this dev in the down state too. */ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { @@ -656,7 +487,10 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (!(flgs & IFF_UP)) continue; - dev_change_flags(vlandev, flgs & ~IFF_UP); + vlan = vlan_dev_info(vlandev); + if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) + dev_change_flags(vlandev, flgs & ~IFF_UP); + netif_stacked_transfer_operstate(dev, vlandev); } break; @@ -671,28 +505,30 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (flgs & IFF_UP) continue; - dev_change_flags(vlandev, flgs | IFF_UP); + vlan = vlan_dev_info(vlandev); + if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) + dev_change_flags(vlandev, flgs | IFF_UP); + netif_stacked_transfer_operstate(dev, vlandev); } break; case NETDEV_UNREGISTER: /* Delete all VLANs for this dev. */ - for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - int ret; + grp->killall = 1; + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { 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, &list); } + unregister_netdevice_many(&list); break; } @@ -700,6 +536,10 @@ out: return NOTIFY_DONE; } +static struct notifier_block vlan_notifier_block __read_mostly = { + .notifier_call = vlan_device_event, +}; + /* * VLAN IOCTL handler. * o execute requested action or pass command to the device driver @@ -708,7 +548,6 @@ out: static int vlan_ioctl_handler(struct net *net, void __user *arg) { int err; - unsigned short vid = 0; struct vlan_ioctl_args args; struct net_device *dev = NULL; @@ -719,10 +558,6 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) args.device1[23] = 0; args.u.device2[23] = 0; -#ifdef VLAN_DEBUG - printk(VLAN_DBG "%s: args.cmd: %x\n", __FUNCTION__, args.cmd); -#endif - rtnl_lock(); switch (args.cmd) { @@ -734,13 +569,12 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) case GET_VLAN_REALDEV_NAME_CMD: case GET_VLAN_VID_CMD: err = -ENODEV; - dev = __dev_get_by_name(&init_net, args.device1); + dev = __dev_get_by_name(net, args.device1); if (!dev) goto out; err = -EINVAL; - if (args.cmd != ADD_VLAN_CMD && - !(dev->priv_flags & IFF_802_1Q_VLAN)) + if (args.cmd != ADD_VLAN_CMD && !is_vlan_dev(dev)) goto out; } @@ -768,9 +602,9 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) err = -EPERM; if (!capable(CAP_NET_ADMIN)) break; - err = vlan_dev_set_vlan_flag(dev, - args.u.flag, - args.vlan_qos); + err = vlan_dev_change_flags(dev, + args.vlan_qos ? args.u.flag : 0, + args.u.flag); break; case SET_VLAN_NAME_TYPE_CMD: @@ -779,7 +613,10 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) break; if ((args.u.name_type >= 0) && (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) { - vlan_name_type = args.u.name_type; + struct vlan_net *vn; + + vn = net_generic(net, vlan_net_id); + vn->name_type = args.u.name_type; err = 0; } else { err = -EINVAL; @@ -797,53 +634,28 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) err = -EPERM; if (!capable(CAP_NET_ADMIN)) break; - err = unregister_vlan_device(dev); + unregister_vlan_dev(dev, NULL); + err = 0; break; - case GET_VLAN_INGRESS_PRIORITY_CMD: - /* TODO: Implement - err = vlan_dev_get_ingress_priority(args); - if (copy_to_user((void*)arg, &args, - sizeof(struct vlan_ioctl_args))) { - err = -EFAULT; - } - */ - err = -EINVAL; - break; - case GET_VLAN_EGRESS_PRIORITY_CMD: - /* TODO: Implement - err = vlan_dev_get_egress_priority(args.device1, &(args.args); - if (copy_to_user((void*)arg, &args, - sizeof(struct vlan_ioctl_args))) { - err = -EFAULT; - } - */ - err = -EINVAL; - 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: err = 0; - vlan_dev_get_vid(dev, &vid); - args.u.VID = vid; + args.u.VID = vlan_dev_vlan_id(dev); if (copy_to_user(arg, &args, - sizeof(struct vlan_ioctl_args))) { + sizeof(struct vlan_ioctl_args))) err = -EFAULT; - } break; default: - /* pass on to underlying device instead?? */ - printk(VLAN_DBG "%s: Unknown VLAN CMD: %x \n", - __FUNCTION__, args.cmd); - err = -EINVAL; + err = -EOPNOTSUPP; break; } out: @@ -851,5 +663,90 @@ out: return err; } +static int __net_init vlan_init_net(struct net *net) +{ + struct vlan_net *vn = net_generic(net, vlan_net_id); + int err; + + vn->name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; + + err = vlan_proc_init(net); + + return err; +} + +static void __net_exit vlan_exit_net(struct net *net) +{ + vlan_proc_cleanup(net); +} + +static struct pernet_operations vlan_net_ops = { + .init = vlan_init_net, + .exit = vlan_exit_net, + .id = &vlan_net_id, + .size = sizeof(struct vlan_net), +}; + +static int __init vlan_proto_init(void) +{ + int err; + + pr_info("%s v%s %s\n", vlan_fullname, vlan_version, vlan_copyright); + pr_info("All bugs added by %s\n", vlan_buggyright); + + err = register_pernet_subsys(&vlan_net_ops); + if (err < 0) + goto err0; + + err = register_netdevice_notifier(&vlan_notifier_block); + if (err < 0) + goto err2; + + err = vlan_gvrp_init(); + if (err < 0) + goto err3; + + err = vlan_netlink_init(); + if (err < 0) + goto err4; + + dev_add_pack(&vlan_packet_type); + vlan_ioctl_set(vlan_ioctl_handler); + return 0; + +err4: + vlan_gvrp_uninit(); +err3: + unregister_netdevice_notifier(&vlan_notifier_block); +err2: + unregister_pernet_subsys(&vlan_net_ops); +err0: + return err; +} + +static void __exit vlan_cleanup_module(void) +{ + unsigned int i; + + vlan_ioctl_set(NULL); + vlan_netlink_fini(); + + unregister_netdevice_notifier(&vlan_notifier_block); + + dev_remove_pack(&vlan_packet_type); + + /* This table must be empty if there are no module references left. */ + for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) + BUG_ON(!hlist_empty(&vlan_group_hash[i])); + + unregister_pernet_subsys(&vlan_net_ops); + rcu_barrier(); /* Wait for completion of call_rcu()'s */ + + vlan_gvrp_uninit(); +} + +module_init(vlan_proto_init); +module_exit(vlan_cleanup_module); + MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION);