diff options
Diffstat (limited to 'net/8021q')
-rw-r--r-- | net/8021q/vlan.c | 126 | ||||
-rw-r--r-- | net/8021q/vlan.h | 43 | ||||
-rw-r--r-- | net/8021q/vlan_core.c | 270 | ||||
-rw-r--r-- | net/8021q/vlan_dev.c | 140 | ||||
-rw-r--r-- | net/8021q/vlan_gvrp.c | 4 | ||||
-rw-r--r-- | net/8021q/vlan_netlink.c | 10 | ||||
-rw-r--r-- | net/8021q/vlanproc.c | 42 |
7 files changed, 439 insertions, 196 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 5471628d3ff..efea35b02e7 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -51,27 +51,6 @@ const char vlan_version[] = DRV_VERSION; /* End of global variables definitions. */ -static void vlan_group_free(struct vlan_group *grp) -{ - int 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(struct net_device *real_dev) -{ - struct vlan_group *grp; - - grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL); - if (!grp) - return NULL; - - grp->real_dev = real_dev; - return grp; -} - static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) { struct net_device **array; @@ -92,32 +71,29 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) return 0; } -static void vlan_rcu_free(struct rcu_head *rcu) -{ - vlan_group_free(container_of(rcu, struct vlan_group, rcu)); -} - void unregister_vlan_dev(struct net_device *dev, struct list_head *head) { - struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct net_device *real_dev = vlan->real_dev; - const struct net_device_ops *ops = real_dev->netdev_ops; + struct vlan_info *vlan_info; struct vlan_group *grp; u16 vlan_id = vlan->vlan_id; ASSERT_RTNL(); - grp = rtnl_dereference(real_dev->vlgrp); - BUG_ON(!grp); + vlan_info = rtnl_dereference(real_dev->vlan_info); + BUG_ON(!vlan_info); + + grp = &vlan_info->grp; /* Take it out of our own structures, but be sure to interlock with * HW accelerating devices or SW vlan input packet processing if * VLAN is not 0 (leave it there for 802.1p). */ - if (vlan_id && (real_dev->features & NETIF_F_HW_VLAN_FILTER)) - ops->ndo_vlan_rx_kill_vid(real_dev, vlan_id); + if (vlan_id) + vlan_vid_del(real_dev, vlan_id); - grp->nr_vlans--; + grp->nr_vlan_devs--; if (vlan->flags & VLAN_FLAG_GVRP) vlan_gvrp_request_leave(dev); @@ -129,16 +105,9 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) */ unregister_netdevice_queue(dev, head); - /* If the group is now empty, kill off the group. */ - if (grp->nr_vlans == 0) { + if (grp->nr_vlan_devs == 0) vlan_gvrp_uninit_applicant(real_dev); - RCU_INIT_POINTER(real_dev->vlgrp, NULL); - - /* Free the group, after all cpu's are done. */ - call_rcu(&grp->rcu, vlan_rcu_free); - } - /* Get rid of the vlan's reference to real_dev */ dev_put(real_dev); } @@ -167,21 +136,26 @@ int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) int register_vlan_dev(struct net_device *dev) { - struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct net_device *real_dev = vlan->real_dev; - const struct net_device_ops *ops = real_dev->netdev_ops; u16 vlan_id = vlan->vlan_id; - struct vlan_group *grp, *ngrp = NULL; + struct vlan_info *vlan_info; + struct vlan_group *grp; int err; - grp = rtnl_dereference(real_dev->vlgrp); - if (!grp) { - ngrp = grp = vlan_group_alloc(real_dev); - if (!grp) - return -ENOBUFS; + err = vlan_vid_add(real_dev, vlan_id); + if (err) + return err; + + vlan_info = rtnl_dereference(real_dev->vlan_info); + /* vlan_info should be there now. vlan_vid_add took care of it */ + BUG_ON(!vlan_info); + + grp = &vlan_info->grp; + if (grp->nr_vlan_devs == 0) { err = vlan_gvrp_init_applicant(real_dev); if (err < 0) - goto out_free_group; + goto out_vid_del; } err = vlan_group_prealloc_vid(grp, vlan_id); @@ -192,7 +166,7 @@ int register_vlan_dev(struct net_device *dev) if (err < 0) goto out_uninit_applicant; - /* Account for reference in struct vlan_dev_info */ + /* Account for reference in struct vlan_dev_priv */ dev_hold(real_dev); netif_stacked_transfer_operstate(real_dev, dev); @@ -202,24 +176,15 @@ int register_vlan_dev(struct net_device *dev) * it into our local structure. */ vlan_group_set_device(grp, vlan_id, dev); - grp->nr_vlans++; - - if (ngrp) { - rcu_assign_pointer(real_dev->vlgrp, ngrp); - } - if (real_dev->features & NETIF_F_HW_VLAN_FILTER) - ops->ndo_vlan_rx_add_vid(real_dev, vlan_id); + grp->nr_vlan_devs++; return 0; out_uninit_applicant: - if (ngrp) + if (grp->nr_vlan_devs == 0) vlan_gvrp_uninit_applicant(real_dev); -out_free_group: - if (ngrp) { - /* Free the group, after all cpu's are done. */ - call_rcu(&ngrp->rcu, vlan_rcu_free); - } +out_vid_del: + vlan_vid_del(real_dev, vlan_id); return err; } @@ -267,7 +232,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 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(sizeof(struct vlan_dev_priv), name, vlan_setup); if (new_dev == NULL) return -ENOBUFS; @@ -278,10 +243,10 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) */ new_dev->mtu = real_dev->mtu; - 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; + vlan_dev_priv(new_dev)->vlan_id = vlan_id; + vlan_dev_priv(new_dev)->real_dev = real_dev; + vlan_dev_priv(new_dev)->dent = NULL; + vlan_dev_priv(new_dev)->flags = VLAN_FLAG_REORDER_HDR; new_dev->rtnl_link_ops = &vlan_link_ops; err = register_vlan_dev(new_dev); @@ -298,7 +263,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_priv *vlan = vlan_dev_priv(vlandev); /* May be called without an actual change */ if (!compare_ether_addr(vlan->real_dev_addr, dev->dev_addr)) @@ -360,25 +325,26 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, { struct net_device *dev = ptr; struct vlan_group *grp; + struct vlan_info *vlan_info; int i, flgs; struct net_device *vlandev; - struct vlan_dev_info *vlan; + struct vlan_dev_priv *vlan; LIST_HEAD(list); if (is_vlan_dev(dev)) __vlan_device_event(dev, event); if ((event == NETDEV_UP) && - (dev->features & NETIF_F_HW_VLAN_FILTER) && - dev->netdev_ops->ndo_vlan_rx_add_vid) { + (dev->features & NETIF_F_HW_VLAN_FILTER)) { pr_info("adding VLAN 0 to HW filter on device %s\n", dev->name); - dev->netdev_ops->ndo_vlan_rx_add_vid(dev, 0); + vlan_vid_add(dev, 0); } - grp = rtnl_dereference(dev->vlgrp); - if (!grp) + vlan_info = rtnl_dereference(dev->vlan_info); + if (!vlan_info) goto out; + grp = &vlan_info->grp; /* It is OK that we do not hold the group lock right now, * as we run under the RTNL lock. @@ -447,7 +413,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (!(flgs & IFF_UP)) continue; - vlan = vlan_dev_info(vlandev); + vlan = vlan_dev_priv(vlandev); if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) dev_change_flags(vlandev, flgs & ~IFF_UP); netif_stacked_transfer_operstate(dev, vlandev); @@ -465,7 +431,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (flgs & IFF_UP) continue; - vlan = vlan_dev_info(vlandev); + vlan = vlan_dev_priv(vlandev); if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) dev_change_flags(vlandev, flgs | IFF_UP); netif_stacked_transfer_operstate(dev, vlandev); @@ -482,9 +448,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (!vlandev) continue; - /* unregistration of last vlan destroys group, abort + /* removal of last vid destroys vlan_info, abort * afterwards */ - if (grp->nr_vlans == 1) + if (vlan_info->nr_vids == 1) i = VLAN_N_VID; unregister_vlan_dev(vlandev, &list); diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 9fd45f3571f..a4886d94c40 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -3,6 +3,7 @@ #include <linux/if_vlan.h> #include <linux/u64_stats_sync.h> +#include <linux/list.h> /** @@ -40,8 +41,10 @@ struct vlan_pcpu_stats { u32 tx_dropped; }; +struct netpoll; + /** - * struct vlan_dev_info - VLAN private device data + * struct vlan_dev_priv - VLAN private device data * @nr_ingress_mappings: number of ingress priority mappings * @ingress_priority_map: ingress priority mappings * @nr_egress_mappings: number of egress priority mappings @@ -53,7 +56,7 @@ struct vlan_pcpu_stats { * @dent: proc dir entry * @vlan_pcpu_stats: ptr to percpu rx stats */ -struct vlan_dev_info { +struct vlan_dev_priv { unsigned int nr_ingress_mappings; u32 ingress_priority_map[8]; unsigned int nr_egress_mappings; @@ -67,13 +70,39 @@ struct vlan_dev_info { struct proc_dir_entry *dent; struct vlan_pcpu_stats __percpu *vlan_pcpu_stats; +#ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; +#endif }; -static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) +static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev) { return netdev_priv(dev); } +/* if this changes, algorithm will have to be reworked because this + * depends on completely exhausting the VLAN identifier space. Thus + * it gives constant time look-up, but in many cases it wastes memory. + */ +#define VLAN_GROUP_ARRAY_SPLIT_PARTS 8 +#define VLAN_GROUP_ARRAY_PART_LEN (VLAN_N_VID/VLAN_GROUP_ARRAY_SPLIT_PARTS) + +struct vlan_group { + unsigned int nr_vlan_devs; + struct hlist_node hlist; /* linked list */ + struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS]; +}; + +struct vlan_info { + struct net_device *real_dev; /* The ethernet(like) device + * the vlan is attached to. + */ + struct vlan_group grp; + struct list_head vid_list; + unsigned int nr_vids; + struct rcu_head rcu; +}; + static inline struct net_device *vlan_group_get_device(struct vlan_group *vg, u16 vlan_id) { @@ -97,10 +126,10 @@ static inline void vlan_group_set_device(struct vlan_group *vg, static inline struct net_device *vlan_find_dev(struct net_device *real_dev, u16 vlan_id) { - struct vlan_group *grp = rcu_dereference_rtnl(real_dev->vlgrp); + struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info); - if (grp) - return vlan_group_get_device(grp, vlan_id); + if (vlan_info) + return vlan_group_get_device(&vlan_info->grp, vlan_id); return NULL; } @@ -121,7 +150,7 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head); static inline u32 vlan_get_ingress_priority(struct net_device *dev, u16 vlan_tci) { - struct vlan_dev_info *vip = vlan_dev_info(dev); + struct vlan_dev_priv *vip = vlan_dev_priv(dev); return vip->ingress_priority_map[(vlan_tci >> VLAN_PRIO_SHIFT) & 0x7]; } diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index f5ffc02729d..4d39d802be2 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -36,7 +36,7 @@ bool vlan_do_receive(struct sk_buff **skbp, bool last_handler) skb->pkt_type = PACKET_HOST; } - if (!(vlan_dev_info(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR)) { + if (!(vlan_dev_priv(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR)) { unsigned int offset = skb->data - skb_mac_header(skb); /* @@ -55,7 +55,7 @@ bool vlan_do_receive(struct sk_buff **skbp, bool last_handler) skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci); skb->vlan_tci = 0; - rx_stats = this_cpu_ptr(vlan_dev_info(vlan_dev)->vlan_pcpu_stats); + rx_stats = this_cpu_ptr(vlan_dev_priv(vlan_dev)->vlan_pcpu_stats); u64_stats_update_begin(&rx_stats->syncp); rx_stats->rx_packets++; @@ -71,10 +71,10 @@ bool vlan_do_receive(struct sk_buff **skbp, bool last_handler) struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, u16 vlan_id) { - struct vlan_group *grp = rcu_dereference_rtnl(real_dev->vlgrp); + struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info); - if (grp) { - return vlan_group_get_device(grp, vlan_id); + if (vlan_info) { + return vlan_group_get_device(&vlan_info->grp, vlan_id); } else { /* * Bonding slaves do not have grp assigned to themselves. @@ -90,13 +90,13 @@ EXPORT_SYMBOL(__vlan_find_dev_deep); struct net_device *vlan_dev_real_dev(const struct net_device *dev) { - return vlan_dev_info(dev)->real_dev; + return vlan_dev_priv(dev)->real_dev; } EXPORT_SYMBOL(vlan_dev_real_dev); u16 vlan_dev_vlan_id(const struct net_device *dev) { - return vlan_dev_info(dev)->vlan_id; + return vlan_dev_priv(dev)->vlan_id; } EXPORT_SYMBOL(vlan_dev_vlan_id); @@ -110,39 +110,6 @@ static struct sk_buff *vlan_reorder_header(struct sk_buff *skb) return skb; } -static void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr) -{ - __be16 proto; - unsigned char *rawp; - - /* - * Was a VLAN packet, grab the encapsulated protocol, which the layer - * three protocols care about. - */ - - proto = vhdr->h_vlan_encapsulated_proto; - if (ntohs(proto) >= 1536) { - skb->protocol = proto; - return; - } - - rawp = skb->data; - if (*(unsigned short *) rawp == 0xFFFF) - /* - * This is a magic hack to spot IPX packets. Older Novell - * breaks the protocol design and runs IPX over 802.3 without - * an 802.2 LLC layer. We look for FFFF which isn't a used - * 802.2 SSAP/DSAP. This won't work for fault tolerant netware - * but does for the rest. - */ - skb->protocol = htons(ETH_P_802_3); - else - /* - * Real 802.2 LLC - */ - skb->protocol = htons(ETH_P_802_2); -} - struct sk_buff *vlan_untag(struct sk_buff *skb) { struct vlan_hdr *vhdr; @@ -179,3 +146,226 @@ err_free: kfree_skb(skb); return NULL; } + + +/* + * vlan info and vid list + */ + +static void vlan_group_free(struct vlan_group *grp) +{ + int i; + + for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) + kfree(grp->vlan_devices_arrays[i]); +} + +static void vlan_info_free(struct vlan_info *vlan_info) +{ + vlan_group_free(&vlan_info->grp); + kfree(vlan_info); +} + +static void vlan_info_rcu_free(struct rcu_head *rcu) +{ + vlan_info_free(container_of(rcu, struct vlan_info, rcu)); +} + +static struct vlan_info *vlan_info_alloc(struct net_device *dev) +{ + struct vlan_info *vlan_info; + + vlan_info = kzalloc(sizeof(struct vlan_info), GFP_KERNEL); + if (!vlan_info) + return NULL; + + vlan_info->real_dev = dev; + INIT_LIST_HEAD(&vlan_info->vid_list); + return vlan_info; +} + +struct vlan_vid_info { + struct list_head list; + unsigned short vid; + int refcount; +}; + +static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info, + unsigned short vid) +{ + struct vlan_vid_info *vid_info; + + list_for_each_entry(vid_info, &vlan_info->vid_list, list) { + if (vid_info->vid == vid) + return vid_info; + } + return NULL; +} + +static struct vlan_vid_info *vlan_vid_info_alloc(unsigned short vid) +{ + struct vlan_vid_info *vid_info; + + vid_info = kzalloc(sizeof(struct vlan_vid_info), GFP_KERNEL); + if (!vid_info) + return NULL; + vid_info->vid = vid; + + return vid_info; +} + +static int __vlan_vid_add(struct vlan_info *vlan_info, unsigned short vid, + struct vlan_vid_info **pvid_info) +{ + struct net_device *dev = vlan_info->real_dev; + const struct net_device_ops *ops = dev->netdev_ops; + struct vlan_vid_info *vid_info; + int err; + + vid_info = vlan_vid_info_alloc(vid); + if (!vid_info) + return -ENOMEM; + + if ((dev->features & NETIF_F_HW_VLAN_FILTER) && + ops->ndo_vlan_rx_add_vid) { + err = ops->ndo_vlan_rx_add_vid(dev, vid); + if (err) { + kfree(vid_info); + return err; + } + } + list_add(&vid_info->list, &vlan_info->vid_list); + vlan_info->nr_vids++; + *pvid_info = vid_info; + return 0; +} + +int vlan_vid_add(struct net_device *dev, unsigned short vid) +{ + struct vlan_info *vlan_info; + struct vlan_vid_info *vid_info; + bool vlan_info_created = false; + int err; + + ASSERT_RTNL(); + + vlan_info = rtnl_dereference(dev->vlan_info); + if (!vlan_info) { + vlan_info = vlan_info_alloc(dev); + if (!vlan_info) + return -ENOMEM; + vlan_info_created = true; + } + vid_info = vlan_vid_info_get(vlan_info, vid); + if (!vid_info) { + err = __vlan_vid_add(vlan_info, vid, &vid_info); + if (err) + goto out_free_vlan_info; + } + vid_info->refcount++; + + if (vlan_info_created) + rcu_assign_pointer(dev->vlan_info, vlan_info); + + return 0; + +out_free_vlan_info: + if (vlan_info_created) + kfree(vlan_info); + return err; +} +EXPORT_SYMBOL(vlan_vid_add); + +static void __vlan_vid_del(struct vlan_info *vlan_info, + struct vlan_vid_info *vid_info) +{ + struct net_device *dev = vlan_info->real_dev; + const struct net_device_ops *ops = dev->netdev_ops; + unsigned short vid = vid_info->vid; + int err; + + if ((dev->features & NETIF_F_HW_VLAN_FILTER) && + ops->ndo_vlan_rx_kill_vid) { + err = ops->ndo_vlan_rx_kill_vid(dev, vid); + if (err) { + pr_warn("failed to kill vid %d for device %s\n", + vid, dev->name); + } + } + list_del(&vid_info->list); + kfree(vid_info); + vlan_info->nr_vids--; +} + +void vlan_vid_del(struct net_device *dev, unsigned short vid) +{ + struct vlan_info *vlan_info; + struct vlan_vid_info *vid_info; + + ASSERT_RTNL(); + + vlan_info = rtnl_dereference(dev->vlan_info); + if (!vlan_info) + return; + + vid_info = vlan_vid_info_get(vlan_info, vid); + if (!vid_info) + return; + vid_info->refcount--; + if (vid_info->refcount == 0) { + __vlan_vid_del(vlan_info, vid_info); + if (vlan_info->nr_vids == 0) { + RCU_INIT_POINTER(dev->vlan_info, NULL); + call_rcu(&vlan_info->rcu, vlan_info_rcu_free); + } + } +} +EXPORT_SYMBOL(vlan_vid_del); + +int vlan_vids_add_by_dev(struct net_device *dev, + const struct net_device *by_dev) +{ + struct vlan_vid_info *vid_info; + struct vlan_info *vlan_info; + int err; + + ASSERT_RTNL(); + + vlan_info = rtnl_dereference(by_dev->vlan_info); + if (!vlan_info) + return 0; + + list_for_each_entry(vid_info, &vlan_info->vid_list, list) { + err = vlan_vid_add(dev, vid_info->vid); + if (err) + goto unwind; + } + return 0; + +unwind: + list_for_each_entry_continue_reverse(vid_info, + &vlan_info->vid_list, + list) { + vlan_vid_del(dev, vid_info->vid); + } + + return err; +} +EXPORT_SYMBOL(vlan_vids_add_by_dev); + +void vlan_vids_del_by_dev(struct net_device *dev, + const struct net_device *by_dev) +{ + struct vlan_vid_info *vid_info; + struct vlan_info *vlan_info; + + ASSERT_RTNL(); + + vlan_info = rtnl_dereference(by_dev->vlan_info); + if (!vlan_info) + return; + + list_for_each_entry(vid_info, &vlan_info->vid_list, list) + vlan_vid_del(dev, vid_info->vid); +} +EXPORT_SYMBOL(vlan_vids_del_by_dev); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index bc252862458..9988d4abb37 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -33,6 +33,7 @@ #include "vlan.h" #include "vlanproc.h" #include <linux/if_vlan.h> +#include <linux/netpoll.h> /* * Rebuild the Ethernet MAC header. This is called after an ARP @@ -72,7 +73,7 @@ vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb) { struct vlan_priority_tci_mapping *mp; - mp = vlan_dev_info(dev)->egress_priority_map[(skb->priority & 0xF)]; + mp = vlan_dev_priv(dev)->egress_priority_map[(skb->priority & 0xF)]; while (mp) { if (mp->priority == skb->priority) { return mp->vlan_qos; /* This should already be shifted @@ -103,10 +104,10 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, u16 vlan_tci = 0; int rc; - if (!(vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR)) { + if (!(vlan_dev_priv(dev)->flags & VLAN_FLAG_REORDER_HDR)) { vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN); - vlan_tci = vlan_dev_info(dev)->vlan_id; + vlan_tci = vlan_dev_priv(dev)->vlan_id; vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); vhdr->h_vlan_TCI = htons(vlan_tci); @@ -129,7 +130,7 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, saddr = dev->dev_addr; /* Now make the underlying real hard header */ - dev = vlan_dev_info(dev)->real_dev; + dev = vlan_dev_priv(dev)->real_dev; rc = dev_hard_header(skb, dev, type, daddr, saddr, len + vhdrlen); if (rc > 0) rc += vhdrlen; @@ -149,27 +150,29 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs... */ if (veth->h_vlan_proto != htons(ETH_P_8021Q) || - vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR) { + vlan_dev_priv(dev)->flags & VLAN_FLAG_REORDER_HDR) { u16 vlan_tci; - vlan_tci = vlan_dev_info(dev)->vlan_id; + vlan_tci = vlan_dev_priv(dev)->vlan_id; vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); skb = __vlan_hwaccel_put_tag(skb, vlan_tci); } - skb_set_dev(skb, vlan_dev_info(dev)->real_dev); + skb_set_dev(skb, vlan_dev_priv(dev)->real_dev); len = skb->len; + if (netpoll_tx_running(dev)) + return skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); ret = dev_queue_xmit(skb); if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) { struct vlan_pcpu_stats *stats; - stats = this_cpu_ptr(vlan_dev_info(dev)->vlan_pcpu_stats); + stats = this_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats); u64_stats_update_begin(&stats->syncp); stats->tx_packets++; stats->tx_bytes += len; u64_stats_update_end(&stats->syncp); } else { - this_cpu_inc(vlan_dev_info(dev)->vlan_pcpu_stats->tx_dropped); + this_cpu_inc(vlan_dev_priv(dev)->vlan_pcpu_stats->tx_dropped); } return ret; @@ -180,7 +183,7 @@ static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu) /* TODO: gotta make sure the underlying layer can handle it, * maybe an IFF_VLAN_CAPABLE flag for devices? */ - if (vlan_dev_info(dev)->real_dev->mtu < new_mtu) + if (vlan_dev_priv(dev)->real_dev->mtu < new_mtu) return -ERANGE; dev->mtu = new_mtu; @@ -191,7 +194,7 @@ static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu) void vlan_dev_set_ingress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio) { - struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); if (vlan->ingress_priority_map[vlan_prio & 0x7] && !skb_prio) vlan->nr_ingress_mappings--; @@ -204,7 +207,7 @@ void vlan_dev_set_ingress_priority(const struct net_device *dev, int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio) { - struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct vlan_priority_tci_mapping *mp = NULL; struct vlan_priority_tci_mapping *np; u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; @@ -241,7 +244,7 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, /* Flags are defined in the vlan_flags enum in include/linux/if_vlan.h file. */ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) { - struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); u32 old_flags = vlan->flags; if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | @@ -261,12 +264,12 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) void vlan_dev_get_realdev_name(const struct net_device *dev, char *result) { - strncpy(result, vlan_dev_info(dev)->real_dev->name, 23); + strncpy(result, vlan_dev_priv(dev)->real_dev->name, 23); } static int vlan_dev_open(struct net_device *dev) { - struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct net_device *real_dev = vlan->real_dev; int err; @@ -313,7 +316,7 @@ out: static int vlan_dev_stop(struct net_device *dev) { - struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct net_device *real_dev = vlan->real_dev; dev_mc_unsync(real_dev, dev); @@ -332,7 +335,7 @@ static int vlan_dev_stop(struct net_device *dev) static int vlan_dev_set_mac_address(struct net_device *dev, void *p) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; struct sockaddr *addr = p; int err; @@ -358,7 +361,7 @@ out: static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; const struct net_device_ops *ops = real_dev->netdev_ops; struct ifreq ifrr; int err = -EOPNOTSUPP; @@ -383,7 +386,7 @@ static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; const struct net_device_ops *ops = real_dev->netdev_ops; int err = 0; @@ -397,7 +400,7 @@ static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa) static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid, struct scatterlist *sgl, unsigned int sgc) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; const struct net_device_ops *ops = real_dev->netdev_ops; int rc = 0; @@ -409,7 +412,7 @@ static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid, static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; const struct net_device_ops *ops = real_dev->netdev_ops; int len = 0; @@ -421,7 +424,7 @@ static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid) static int vlan_dev_fcoe_enable(struct net_device *dev) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; const struct net_device_ops *ops = real_dev->netdev_ops; int rc = -EINVAL; @@ -432,7 +435,7 @@ static int vlan_dev_fcoe_enable(struct net_device *dev) static int vlan_dev_fcoe_disable(struct net_device *dev) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; const struct net_device_ops *ops = real_dev->netdev_ops; int rc = -EINVAL; @@ -443,7 +446,7 @@ static int vlan_dev_fcoe_disable(struct net_device *dev) static int vlan_dev_fcoe_get_wwn(struct net_device *dev, u64 *wwn, int type) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; const struct net_device_ops *ops = real_dev->netdev_ops; int rc = -EINVAL; @@ -455,7 +458,7 @@ static int vlan_dev_fcoe_get_wwn(struct net_device *dev, u64 *wwn, int type) static int vlan_dev_fcoe_ddp_target(struct net_device *dev, u16 xid, struct scatterlist *sgl, unsigned int sgc) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; const struct net_device_ops *ops = real_dev->netdev_ops; int rc = 0; @@ -468,7 +471,7 @@ static int vlan_dev_fcoe_ddp_target(struct net_device *dev, u16 xid, static void vlan_dev_change_rx_flags(struct net_device *dev, int change) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; if (dev->flags & IFF_UP) { if (change & IFF_ALLMULTI) @@ -480,8 +483,8 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change) static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) { - dev_mc_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev); - dev_uc_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev); + dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); + dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); } /* @@ -519,7 +522,7 @@ static const struct net_device_ops vlan_netdev_ops; static int vlan_dev_init(struct net_device *dev) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; int subclass = 0; netif_carrier_off(dev); @@ -568,8 +571,8 @@ static int vlan_dev_init(struct net_device *dev) vlan_dev_set_lockdep_class(dev, subclass); - vlan_dev_info(dev)->vlan_pcpu_stats = alloc_percpu(struct vlan_pcpu_stats); - if (!vlan_dev_info(dev)->vlan_pcpu_stats) + vlan_dev_priv(dev)->vlan_pcpu_stats = alloc_percpu(struct vlan_pcpu_stats); + if (!vlan_dev_priv(dev)->vlan_pcpu_stats) return -ENOMEM; return 0; @@ -578,7 +581,7 @@ static int vlan_dev_init(struct net_device *dev) static void vlan_dev_uninit(struct net_device *dev) { struct vlan_priority_tci_mapping *pm; - struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); int i; free_percpu(vlan->vlan_pcpu_stats); @@ -591,18 +594,17 @@ static void vlan_dev_uninit(struct net_device *dev) } } -static u32 vlan_dev_fix_features(struct net_device *dev, u32 features) +static netdev_features_t vlan_dev_fix_features(struct net_device *dev, + netdev_features_t features) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; u32 old_features = features; - features &= real_dev->features; features &= real_dev->vlan_features; + features |= NETIF_F_RXCSUM; + features &= real_dev->features; features |= old_features & NETIF_F_SOFT_FEATURES; - - if (dev_ethtool_get_rx_csum(real_dev)) - features |= NETIF_F_RXCSUM; features |= NETIF_F_LLTX; return features; @@ -611,7 +613,7 @@ static u32 vlan_dev_fix_features(struct net_device *dev, u32 features) static int vlan_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - const struct vlan_dev_info *vlan = vlan_dev_info(dev); + const struct vlan_dev_priv *vlan = vlan_dev_priv(dev); return __ethtool_get_settings(vlan->real_dev, cmd); } @@ -627,7 +629,7 @@ static void vlan_ethtool_get_drvinfo(struct net_device *dev, static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { - if (vlan_dev_info(dev)->vlan_pcpu_stats) { + if (vlan_dev_priv(dev)->vlan_pcpu_stats) { struct vlan_pcpu_stats *p; u32 rx_errors = 0, tx_dropped = 0; int i; @@ -636,7 +638,7 @@ static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, st u64 rxpackets, rxbytes, rxmulticast, txpackets, txbytes; unsigned int start; - p = per_cpu_ptr(vlan_dev_info(dev)->vlan_pcpu_stats, i); + p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i); do { start = u64_stats_fetch_begin_bh(&p->syncp); rxpackets = p->rx_packets; @@ -661,6 +663,57 @@ static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, st return stats; } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void vlan_dev_poll_controller(struct net_device *dev) +{ + return; +} + +static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo) +{ + struct vlan_dev_priv *info = vlan_dev_priv(dev); + struct net_device *real_dev = info->real_dev; + struct netpoll *netpoll; + int err = 0; + + netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL); + err = -ENOMEM; + if (!netpoll) + goto out; + + netpoll->dev = real_dev; + strlcpy(netpoll->dev_name, real_dev->name, IFNAMSIZ); + + err = __netpoll_setup(netpoll); + if (err) { + kfree(netpoll); + goto out; + } + + info->netpoll = netpoll; + +out: + return err; +} + +static void vlan_dev_netpoll_cleanup(struct net_device *dev) +{ + struct vlan_dev_priv *info = vlan_dev_priv(dev); + struct netpoll *netpoll = info->netpoll; + + if (!netpoll) + return; + + info->netpoll = NULL; + + /* Wait for transmitting packets to finish before freeing. */ + synchronize_rcu_bh(); + + __netpoll_cleanup(netpoll); + kfree(netpoll); +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + static const struct ethtool_ops vlan_ethtool_ops = { .get_settings = vlan_ethtool_get_settings, .get_drvinfo = vlan_ethtool_get_drvinfo, @@ -689,6 +742,11 @@ static const struct net_device_ops vlan_netdev_ops = { .ndo_fcoe_get_wwn = vlan_dev_fcoe_get_wwn, .ndo_fcoe_ddp_target = vlan_dev_fcoe_ddp_target, #endif +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = vlan_dev_poll_controller, + .ndo_netpoll_setup = vlan_dev_netpoll_setup, + .ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup, +#endif .ndo_fix_features = vlan_dev_fix_features, }; diff --git a/net/8021q/vlan_gvrp.c b/net/8021q/vlan_gvrp.c in |