@@ -93,6 +93,10 @@ extern u16 vlan_dev_vlan_id(const struct net_device *dev);
extern bool vlan_do_receive(struct sk_buff **skb);
extern struct sk_buff *vlan_untag(struct sk_buff *skb);
+extern bool vlan_hw_buggy(const struct net_device *dev);
+extern int vlan_vid_add_hw(struct net_device *dev, unsigned short vid);
+extern int vlan_vid_del_hw(struct net_device *dev, unsigned short vid);
+
extern int vlan_vid_add(struct net_device *dev, unsigned short vid);
extern void vlan_vid_del(struct net_device *dev, unsigned short vid);
@@ -155,6 +159,23 @@ static inline bool vlan_uses_dev(const struct net_device *dev)
{
return false;
}
+
+static inline bool vlan_hw_buggy(const struct net_device *dev)
+{
+ return false;
+}
+
+static inline int vlan_vid_add_hw(struct net_device *dev,
+ unsigned short vid)
+{
+ return 0;
+}
+
+static inline int vlan_vid_del_hw(struct net_device *dev,
+ unsigned short vid)
+{
+ return 0;
+}
#endif
/**
@@ -117,15 +117,13 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id)
{
const char *name = real_dev->name;
- const struct net_device_ops *ops = real_dev->netdev_ops;
if (real_dev->features & NETIF_F_VLAN_CHALLENGED) {
pr_info("VLANs not supported on %s\n", name);
return -EOPNOTSUPP;
}
- if ((real_dev->features & NETIF_F_HW_VLAN_FILTER) &&
- (!ops->ndo_vlan_rx_add_vid || !ops->ndo_vlan_rx_kill_vid)) {
+ if (vlan_hw_buggy(real_dev)) {
pr_info("Device %s has buggy VLAN hw accel\n", name);
return -EOPNOTSUPP;
}
@@ -145,6 +145,63 @@ err_free:
return NULL;
}
+/**
+ * vlan_hw_buggy - Check to see if VLAN hw acceleration is supported.
+ * @dev: netdevice of the lowerdev/hw nic
+ *
+ * Checks to see if HW and driver report VLAN acceleration correctly.
+ */
+bool vlan_hw_buggy(const struct net_device *dev)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
+ (!ops->ndo_vlan_rx_add_vid || !ops->ndo_vlan_rx_kill_vid))
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(vlan_hw_buggy);
+
+/**
+ * vlan_vid_add_hw - Add the VLAN vid to the HW filter
+ * @dev: netdevice of the lowerdev/hw nic
+ * @vid: vlan id.
+ *
+ * Inserts the vid into the HW vlan filter table if hw supports it.
+ */
+int vlan_vid_add_hw(struct net_device *dev, unsigned short vid)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+ int err = 0;
+
+ if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
+ ops->ndo_vlan_rx_add_vid)
+ err = ops->ndo_vlan_rx_add_vid(dev, vid);
+
+ return err;
+}
+EXPORT_SYMBOL(vlan_vid_add_hw);
+
+/**
+ * vlan_vid_del_hw - Delete the VLAN vid from the HW filter
+ * @dev: netdevice of the lowerdev/hw nic
+ * @vid: vlan id.
+ *
+ * Delete the vid from the HW vlan filter table if hw supports it.
+ */
+int vlan_vid_del_hw(struct net_device *dev, unsigned short vid)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+ int err = 0;
+
+ if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
+ ops->ndo_vlan_rx_kill_vid)
+ err = ops->ndo_vlan_rx_add_vid(dev, vid);
+
+ return err;
+}
+EXPORT_SYMBOL(vlan_vid_del_hw);
/*
* vlan info and vid list
@@ -216,7 +273,6 @@ 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;
@@ -224,13 +280,10 @@ static int __vlan_vid_add(struct vlan_info *vlan_info, unsigned short 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;
- }
+ err = vlan_vid_add_hw(dev, vid);
+ if (err) {
+ kfree(vid_info);
+ return err;
}
list_add(&vid_info->list, &vlan_info->vid_list);
vlan_info->nr_vids++;
@@ -278,17 +331,13 @@ 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);
- }
+ err = vlan_vid_del_hw(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);
Wrap VLAN hardware acceleration calls into separate functions. This way other code can re-use it. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> --- include/linux/if_vlan.h | 21 ++++++++++++ net/8021q/vlan.c | 4 +-- net/8021q/vlan_core.c | 81 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 87 insertions(+), 19 deletions(-)