net: congestion notifications are not dropped packets
[safe/jmp/linux-2.6] / net / 8021q / vlan_dev.c
index 6e695ac..55be908 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -140,7 +141,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
                  struct packet_type *ptype, struct net_device *orig_dev)
 {
        struct vlan_hdr *vhdr;
-       struct net_device_stats *stats;
+       struct vlan_rx_stats *rx_stats;
        u16 vlan_id;
        u16 vlan_tci;
 
@@ -163,9 +164,10 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
                goto err_unlock;
        }
 
-       stats = &skb->dev->stats;
-       stats->rx_packets++;
-       stats->rx_bytes += skb->len;
+       rx_stats = per_cpu_ptr(vlan_dev_info(skb->dev)->vlan_rx_stats,
+                              smp_processor_id());
+       rx_stats->rx_packets++;
+       rx_stats->rx_bytes += skb->len;
 
        skb_pull_rcsum(skb, VLAN_HLEN);
 
@@ -180,7 +182,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
                break;
 
        case PACKET_MULTICAST:
-               stats->multicast++;
+               rx_stats->multicast++;
                break;
 
        case PACKET_OTHERHOST:
@@ -200,7 +202,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
 
        skb = vlan_check_reorder_header(skb);
        if (!skb) {
-               stats->rx_errors++;
+               rx_stats->rx_errors++;
                goto err_unlock;
        }
 
@@ -262,11 +264,10 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
                vhdr->h_vlan_TCI = htons(vlan_tci);
 
                /*
-                *  Set the protocol type. For a packet of type ETH_P_802_3 we
-                *  put the length in here instead. It is up to the 802.2
-                *  layer to carry protocol information.
+                *  Set the protocol type. For a packet of type ETH_P_802_3/2 we
+                *  put the length in here instead.
                 */
-               if (type != ETH_P_802_3)
+               if (type != ETH_P_802_3 && type != ETH_P_802_2)
                        vhdr->h_vlan_encapsulated_proto = htons(type);
                else
                        vhdr->h_vlan_encapsulated_proto = htons(len);
@@ -288,10 +289,14 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
        return rc;
 }
 
-static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
+                                           struct net_device *dev)
 {
-       struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
+       int i = skb_get_queue_mapping(skb);
+       struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
        struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
+       unsigned int len;
+       int ret;
 
        /* Handle non-VLAN frames if they are sent to us, for example by DHCP.
         *
@@ -317,30 +322,52 @@ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        vlan_dev_info(dev)->cnt_inc_headroom_on_tx++;
        }
 
-       txq->tx_packets++;
-       txq->tx_bytes += skb->len;
 
-       skb->dev = vlan_dev_info(dev)->real_dev;
-       dev_queue_xmit(skb);
-       return NETDEV_TX_OK;
+       skb_set_dev(skb, vlan_dev_info(dev)->real_dev);
+       len = skb->len;
+       ret = dev_queue_xmit(skb);
+
+       if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
+               txq->tx_packets++;
+               txq->tx_bytes += len;
+       } else
+               txq->tx_dropped++;
+
+       return ret;
 }
 
-static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
-                                           struct net_device *dev)
+static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
+                                                   struct net_device *dev)
 {
-       struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
+       int i = skb_get_queue_mapping(skb);
+       struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
        u16 vlan_tci;
+       unsigned int len;
+       int ret;
 
        vlan_tci = vlan_dev_info(dev)->vlan_id;
        vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
        skb = __vlan_hwaccel_put_tag(skb, vlan_tci);
 
-       txq->tx_packets++;
-       txq->tx_bytes += skb->len;
-
        skb->dev = vlan_dev_info(dev)->real_dev;
-       dev_queue_xmit(skb);
-       return NETDEV_TX_OK;
+       len = skb->len;
+       ret = dev_queue_xmit(skb);
+
+       if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
+               txq->tx_packets++;
+               txq->tx_bytes += len;
+       } else
+               txq->tx_dropped++;
+
+       return ret;
+}
+
+static u16 vlan_dev_select_queue(struct net_device *dev, struct sk_buff *skb)
+{
+       struct net_device *rdev = vlan_dev_info(dev)->real_dev;
+       const struct net_device_ops *ops = rdev->netdev_ops;
+
+       return ops->ndo_select_queue(rdev, skb);
 }
 
 static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu)
@@ -375,7 +402,7 @@ int vlan_dev_set_egress_priority(const struct net_device *dev,
        struct vlan_dev_info *vlan = vlan_dev_info(dev);
        struct vlan_priority_tci_mapping *mp = NULL;
        struct vlan_priority_tci_mapping *np;
-       u32 vlan_qos = (vlan_prio << 13) & 0xE000;
+       u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK;
 
        /* See if a priority mapping exists.. */
        mp = vlan->egress_priority_map[skb_prio & 0xF];
@@ -412,7 +439,8 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask)
        struct vlan_dev_info *vlan = vlan_dev_info(dev);
        u32 old_flags = vlan->flags;
 
-       if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP))
+       if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP |
+                    VLAN_FLAG_LOOSE_BINDING))
                return -EINVAL;
 
        vlan->flags = (old_flags & ~mask) | (flags & mask);
@@ -437,11 +465,12 @@ static int vlan_dev_open(struct net_device *dev)
        struct net_device *real_dev = vlan->real_dev;
        int err;
 
-       if (!(real_dev->flags & IFF_UP))
+       if (!(real_dev->flags & IFF_UP) &&
+           !(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
                return -ENETDOWN;
 
        if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) {
-               err = dev_unicast_add(real_dev, dev->dev_addr);
+               err = dev_uc_add(real_dev, dev->dev_addr);
                if (err < 0)
                        goto out;
        }
@@ -470,7 +499,7 @@ clear_allmulti:
                dev_set_allmulti(real_dev, -1);
 del_unicast:
        if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
-               dev_unicast_delete(real_dev, dev->dev_addr);
+               dev_uc_del(real_dev, dev->dev_addr);
 out:
        netif_carrier_off(dev);
        return err;
@@ -485,14 +514,14 @@ static int vlan_dev_stop(struct net_device *dev)
                vlan_gvrp_request_leave(dev);
 
        dev_mc_unsync(real_dev, dev);
-       dev_unicast_unsync(real_dev, dev);
+       dev_uc_unsync(real_dev, dev);
        if (dev->flags & IFF_ALLMULTI)
                dev_set_allmulti(real_dev, -1);
        if (dev->flags & IFF_PROMISC)
                dev_set_promiscuity(real_dev, -1);
 
        if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
-               dev_unicast_delete(real_dev, dev->dev_addr);
+               dev_uc_del(real_dev, dev->dev_addr);
 
        netif_carrier_off(dev);
        return 0;
@@ -511,13 +540,13 @@ static int vlan_dev_set_mac_address(struct net_device *dev, void *p)
                goto out;
 
        if (compare_ether_addr(addr->sa_data, real_dev->dev_addr)) {
-               err = dev_unicast_add(real_dev, addr->sa_data);
+               err = dev_uc_add(real_dev, addr->sa_data);
                if (err < 0)
                        return err;
        }
 
        if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
-               dev_unicast_delete(real_dev, dev->dev_addr);
+               dev_uc_del(real_dev, dev->dev_addr);
 
 out:
        memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
@@ -586,6 +615,39 @@ static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid)
 
        return len;
 }
+
+static int vlan_dev_fcoe_enable(struct net_device *dev)
+{
+       struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+       const struct net_device_ops *ops = real_dev->netdev_ops;
+       int rc = -EINVAL;
+
+       if (ops->ndo_fcoe_enable)
+               rc = ops->ndo_fcoe_enable(real_dev);
+       return rc;
+}
+
+static int vlan_dev_fcoe_disable(struct net_device *dev)
+{
+       struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+       const struct net_device_ops *ops = real_dev->netdev_ops;
+       int rc = -EINVAL;
+
+       if (ops->ndo_fcoe_disable)
+               rc = ops->ndo_fcoe_disable(real_dev);
+       return rc;
+}
+
+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;
+       const struct net_device_ops *ops = real_dev->netdev_ops;
+       int rc = -EINVAL;
+
+       if (ops->ndo_fcoe_get_wwn)
+               rc = ops->ndo_fcoe_get_wwn(real_dev, wwn, type);
+       return rc;
+}
 #endif
 
 static void vlan_dev_change_rx_flags(struct net_device *dev, int change)
@@ -601,7 +663,7 @@ 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_unicast_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev);
+       dev_uc_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev);
 }
 
 /*
@@ -635,7 +697,8 @@ static const struct header_ops vlan_header_ops = {
        .parse   = eth_header_parse,
 };
 
-static const struct net_device_ops vlan_netdev_ops, vlan_netdev_accel_ops;
+static const struct net_device_ops vlan_netdev_ops, vlan_netdev_accel_ops,
+                   vlan_netdev_ops_sq, vlan_netdev_accel_ops_sq;
 
 static int vlan_dev_init(struct net_device *dev)
 {
@@ -669,17 +732,28 @@ static int vlan_dev_init(struct net_device *dev)
        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->netdev_ops         = &vlan_netdev_accel_ops;
+               if (real_dev->netdev_ops->ndo_select_queue)
+                       dev->netdev_ops = &vlan_netdev_accel_ops_sq;
+               else
+                       dev->netdev_ops = &vlan_netdev_accel_ops;
        } else {
                dev->header_ops      = &vlan_header_ops;
                dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN;
-               dev->netdev_ops         = &vlan_netdev_ops;
+               if (real_dev->netdev_ops->ndo_select_queue)
+                       dev->netdev_ops = &vlan_netdev_ops_sq;
+               else
+                       dev->netdev_ops = &vlan_netdev_ops;
        }
 
        if (is_vlan_dev(real_dev))
                subclass = 1;
 
        vlan_dev_set_lockdep_class(dev, subclass);
+
+       vlan_dev_info(dev)->vlan_rx_stats = alloc_percpu(struct vlan_rx_stats);
+       if (!vlan_dev_info(dev)->vlan_rx_stats)
+               return -ENOMEM;
+
        return 0;
 }
 
@@ -689,6 +763,8 @@ static void vlan_dev_uninit(struct net_device *dev)
        struct vlan_dev_info *vlan = vlan_dev_info(dev);
        int i;
 
+       free_percpu(vlan->vlan_rx_stats);
+       vlan->vlan_rx_stats = NULL;
        for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) {
                while ((pm = vlan->egress_priority_map[i]) != NULL) {
                        vlan->egress_priority_map[i] = pm->next;
@@ -724,6 +800,31 @@ static u32 vlan_ethtool_get_flags(struct net_device *dev)
        return dev_ethtool_get_flags(vlan->real_dev);
 }
 
+static struct net_device_stats *vlan_dev_get_stats(struct net_device *dev)
+{
+       struct net_device_stats *stats = &dev->stats;
+
+       dev_txq_stats_fold(dev, stats);
+
+       if (vlan_dev_info(dev)->vlan_rx_stats) {
+               struct vlan_rx_stats *p, rx = {0};
+               int i;
+
+               for_each_possible_cpu(i) {
+                       p = per_cpu_ptr(vlan_dev_info(dev)->vlan_rx_stats, i);
+                       rx.rx_packets += p->rx_packets;
+                       rx.rx_bytes   += p->rx_bytes;
+                       rx.rx_errors  += p->rx_errors;
+                       rx.multicast  += p->multicast;
+               }
+               stats->rx_packets = rx.rx_packets;
+               stats->rx_bytes   = rx.rx_bytes;
+               stats->rx_errors  = rx.rx_errors;
+               stats->multicast  = rx.multicast;
+       }
+       return stats;
+}
+
 static const struct ethtool_ops vlan_ethtool_ops = {
        .get_settings           = vlan_ethtool_get_settings,
        .get_drvinfo            = vlan_ethtool_get_drvinfo,
@@ -746,9 +847,13 @@ static const struct net_device_ops vlan_netdev_ops = {
        .ndo_change_rx_flags    = vlan_dev_change_rx_flags,
        .ndo_do_ioctl           = vlan_dev_ioctl,
        .ndo_neigh_setup        = vlan_dev_neigh_setup,
+       .ndo_get_stats          = vlan_dev_get_stats,
 #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
        .ndo_fcoe_ddp_setup     = vlan_dev_fcoe_ddp_setup,
        .ndo_fcoe_ddp_done      = vlan_dev_fcoe_ddp_done,
+       .ndo_fcoe_enable        = vlan_dev_fcoe_enable,
+       .ndo_fcoe_disable       = vlan_dev_fcoe_disable,
+       .ndo_fcoe_get_wwn       = vlan_dev_fcoe_get_wwn,
 #endif
 };
 
@@ -766,9 +871,63 @@ static const struct net_device_ops vlan_netdev_accel_ops = {
        .ndo_change_rx_flags    = vlan_dev_change_rx_flags,
        .ndo_do_ioctl           = vlan_dev_ioctl,
        .ndo_neigh_setup        = vlan_dev_neigh_setup,
+       .ndo_get_stats          = vlan_dev_get_stats,
+#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+       .ndo_fcoe_ddp_setup     = vlan_dev_fcoe_ddp_setup,
+       .ndo_fcoe_ddp_done      = vlan_dev_fcoe_ddp_done,
+       .ndo_fcoe_enable        = vlan_dev_fcoe_enable,
+       .ndo_fcoe_disable       = vlan_dev_fcoe_disable,
+       .ndo_fcoe_get_wwn       = vlan_dev_fcoe_get_wwn,
+#endif
+};
+
+static const struct net_device_ops vlan_netdev_ops_sq = {
+       .ndo_select_queue       = vlan_dev_select_queue,
+       .ndo_change_mtu         = vlan_dev_change_mtu,
+       .ndo_init               = vlan_dev_init,
+       .ndo_uninit             = vlan_dev_uninit,
+       .ndo_open               = vlan_dev_open,
+       .ndo_stop               = vlan_dev_stop,
+       .ndo_start_xmit =  vlan_dev_hard_start_xmit,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = vlan_dev_set_mac_address,
+       .ndo_set_rx_mode        = vlan_dev_set_rx_mode,
+       .ndo_set_multicast_list = vlan_dev_set_rx_mode,
+       .ndo_change_rx_flags    = vlan_dev_change_rx_flags,
+       .ndo_do_ioctl           = vlan_dev_ioctl,
+       .ndo_neigh_setup        = vlan_dev_neigh_setup,
+       .ndo_get_stats          = vlan_dev_get_stats,
+#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+       .ndo_fcoe_ddp_setup     = vlan_dev_fcoe_ddp_setup,
+       .ndo_fcoe_ddp_done      = vlan_dev_fcoe_ddp_done,
+       .ndo_fcoe_enable        = vlan_dev_fcoe_enable,
+       .ndo_fcoe_disable       = vlan_dev_fcoe_disable,
+       .ndo_fcoe_get_wwn       = vlan_dev_fcoe_get_wwn,
+#endif
+};
+
+static const struct net_device_ops vlan_netdev_accel_ops_sq = {
+       .ndo_select_queue       = vlan_dev_select_queue,
+       .ndo_change_mtu         = vlan_dev_change_mtu,
+       .ndo_init               = vlan_dev_init,
+       .ndo_uninit             = vlan_dev_uninit,
+       .ndo_open               = vlan_dev_open,
+       .ndo_stop               = vlan_dev_stop,
+       .ndo_start_xmit =  vlan_dev_hwaccel_hard_start_xmit,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = vlan_dev_set_mac_address,
+       .ndo_set_rx_mode        = vlan_dev_set_rx_mode,
+       .ndo_set_multicast_list = vlan_dev_set_rx_mode,
+       .ndo_change_rx_flags    = vlan_dev_change_rx_flags,
+       .ndo_do_ioctl           = vlan_dev_ioctl,
+       .ndo_neigh_setup        = vlan_dev_neigh_setup,
+       .ndo_get_stats          = vlan_dev_get_stats,
 #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
        .ndo_fcoe_ddp_setup     = vlan_dev_fcoe_ddp_setup,
        .ndo_fcoe_ddp_done      = vlan_dev_fcoe_ddp_done,
+       .ndo_fcoe_enable        = vlan_dev_fcoe_enable,
+       .ndo_fcoe_disable       = vlan_dev_fcoe_disable,
+       .ndo_fcoe_get_wwn       = vlan_dev_fcoe_get_wwn,
 #endif
 };