vlan: allow TSO setting on vlan interfaces
[linux-2.6.git] / net / 8021q / vlan_dev.c
index b5249c5..a1b8171 100644 (file)
@@ -166,6 +166,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
 
        rx_stats = per_cpu_ptr(vlan_dev_info(skb->dev)->vlan_rx_stats,
                               smp_processor_id());
+       u64_stats_update_begin(&rx_stats->syncp);
        rx_stats->rx_packets++;
        rx_stats->rx_bytes += skb->len;
 
@@ -182,7 +183,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
                break;
 
        case PACKET_MULTICAST:
-               rx_stats->multicast++;
+               rx_stats->rx_multicast++;
                break;
 
        case PACKET_OTHERHOST:
@@ -197,6 +198,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
        default:
                break;
        }
+       u64_stats_update_end(&rx_stats->syncp);
 
        vlan_set_encap_proto(skb, vhdr);
 
@@ -327,7 +329,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
        len = skb->len;
        ret = dev_queue_xmit(skb);
 
-       if (likely(ret == NET_XMIT_SUCCESS)) {
+       if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
                txq->tx_packets++;
                txq->tx_bytes += len;
        } else
@@ -353,7 +355,7 @@ static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
        len = skb->len;
        ret = dev_queue_xmit(skb);
 
-       if (likely(ret == NET_XMIT_SUCCESS)) {
+       if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
                txq->tx_packets++;
                txq->tx_bytes += len;
        } else
@@ -708,7 +710,8 @@ static int vlan_dev_init(struct net_device *dev)
        netif_carrier_off(dev);
 
        /* IFF_BROADCAST|IFF_MULTICAST; ??? */
-       dev->flags  = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI);
+       dev->flags  = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
+                                         IFF_MASTER | IFF_SLAVE);
        dev->iflink = real_dev->ifindex;
        dev->state  = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) |
                                          (1<<__LINK_STATE_DORMANT))) |
@@ -800,37 +803,65 @@ 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)
+static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
-       struct net_device_stats *stats = &dev->stats;
-
-       dev_txq_stats_fold(dev, stats);
+       dev_txq_stats_fold(dev, (struct net_device_stats *)stats);
 
        if (vlan_dev_info(dev)->vlan_rx_stats) {
-               struct vlan_rx_stats *p, rx = {0};
+               struct vlan_rx_stats *p, accum = {0};
                int i;
 
                for_each_possible_cpu(i) {
+                       u64 rxpackets, rxbytes, rxmulticast;
+                       unsigned int start;
+
                        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;
+                       do {
+                               start = u64_stats_fetch_begin_bh(&p->syncp);
+                               rxpackets       = p->rx_packets;
+                               rxbytes         = p->rx_bytes;
+                               rxmulticast     = p->rx_multicast;
+                       } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+                       accum.rx_packets += rxpackets;
+                       accum.rx_bytes   += rxbytes;
+                       accum.rx_multicast += rxmulticast;
+                       /* rx_errors is an ulong, not protected by syncp */
+                       accum.rx_errors  += p->rx_errors;
                }
-               stats->rx_packets = rx.rx_packets;
-               stats->rx_bytes   = rx.rx_bytes;
-               stats->rx_errors  = rx.rx_errors;
-               stats->multicast  = rx.multicast;
+               stats->rx_packets = accum.rx_packets;
+               stats->rx_bytes   = accum.rx_bytes;
+               stats->rx_errors  = accum.rx_errors;
+               stats->multicast  = accum.rx_multicast;
        }
        return stats;
 }
 
+static int vlan_ethtool_set_tso(struct net_device *dev, u32 data)
+{
+       if (data) {
+               struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+
+               /* Underlying device must support TSO for VLAN-tagged packets
+                * and must have TSO enabled now.
+                */
+               if (!(real_dev->vlan_features & NETIF_F_TSO))
+                       return -EOPNOTSUPP;
+               if (!(real_dev->features & NETIF_F_TSO))
+                       return -EINVAL;
+               dev->features |= NETIF_F_TSO;
+       } else {
+               dev->features &= ~NETIF_F_TSO;
+       }
+       return 0;
+}
+
 static const struct ethtool_ops vlan_ethtool_ops = {
        .get_settings           = vlan_ethtool_get_settings,
        .get_drvinfo            = vlan_ethtool_get_drvinfo,
        .get_link               = ethtool_op_get_link,
        .get_rx_csum            = vlan_ethtool_get_rx_csum,
        .get_flags              = vlan_ethtool_get_flags,
+       .set_tso                = vlan_ethtool_set_tso,
 };
 
 static const struct net_device_ops vlan_netdev_ops = {
@@ -847,7 +878,7 @@ 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,
+       .ndo_get_stats64        = vlan_dev_get_stats64,
 #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,
@@ -871,7 +902,7 @@ 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,
+       .ndo_get_stats64        = vlan_dev_get_stats64,
 #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,
@@ -896,7 +927,7 @@ static const struct net_device_ops vlan_netdev_ops_sq = {
        .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,
+       .ndo_get_stats64        = vlan_dev_get_stats64,
 #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,
@@ -921,7 +952,7 @@ static const struct net_device_ops vlan_netdev_accel_ops_sq = {
        .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,
+       .ndo_get_stats64        = vlan_dev_get_stats64,
 #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,