net: tun: convert to hw_features
authorMichał Mirosław <mirq-linux@rere.qmqm.pl>
Tue, 19 Apr 2011 06:13:10 +0000 (06:13 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 20 Apr 2011 08:30:45 +0000 (01:30 -0700)
This changes offload setting behaviour to what I think is correct:
 - offloads set via ethtool mean what admin wants to use (by default
   he wants 'em all)
 - offloads set via ioctl() mean what userspace is expecting to get
   (this limits which admin wishes are granted)
 - TUN_NOCHECKSUM is ignored, as it might cause broken packets when
   forwarded (ip_summed == CHECKSUM_UNNECESSARY means that checksum
   was verified, not that it can be ignored)

If TUN_NOCHECKSUM is implemented, it should set skb->csum_* and
skb->ip_summed (= CHECKSUM_PARTIAL) for known protocols and let others
be verified by kernel when necessary.

TUN_NOCHECKSUM handling was introduced by commit
f43798c27684ab925adde7d8acc34c78c6e50df8:

    tun: Allow GSO using virtio_net_hdr

Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/tun.c

index f5e9ac0..ade3cf9 100644 (file)
@@ -123,6 +123,9 @@ struct tun_struct {
        gid_t                   group;
 
        struct net_device       *dev;
+       u32                     set_features;
+#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \
+                         NETIF_F_TSO6|NETIF_F_UFO)
        struct fasync_struct    *fasync;
 
        struct tap_filter       txflt;
@@ -451,12 +454,20 @@ tun_net_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
+static u32 tun_net_fix_features(struct net_device *dev, u32 features)
+{
+       struct tun_struct *tun = netdev_priv(dev);
+
+       return (features & tun->set_features) | (features & ~TUN_USER_FEATURES);
+}
+
 static const struct net_device_ops tun_netdev_ops = {
        .ndo_uninit             = tun_net_uninit,
        .ndo_open               = tun_net_open,
        .ndo_stop               = tun_net_close,
        .ndo_start_xmit         = tun_net_xmit,
        .ndo_change_mtu         = tun_net_change_mtu,
+       .ndo_fix_features       = tun_net_fix_features,
 };
 
 static const struct net_device_ops tap_netdev_ops = {
@@ -465,6 +476,7 @@ static const struct net_device_ops tap_netdev_ops = {
        .ndo_stop               = tun_net_close,
        .ndo_start_xmit         = tun_net_xmit,
        .ndo_change_mtu         = tun_net_change_mtu,
+       .ndo_fix_features       = tun_net_fix_features,
        .ndo_set_multicast_list = tun_net_mclist,
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
@@ -628,8 +640,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
                        kfree_skb(skb);
                        return -EINVAL;
                }
-       } else if (tun->flags & TUN_NOCHECKSUM)
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
 
        switch (tun->flags & TUN_TYPE_MASK) {
        case TUN_TUN_DEV:
@@ -1094,6 +1105,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
                                goto err_free_sk;
                }
 
+               dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
+                       TUN_USER_FEATURES;
+               dev->features = dev->hw_features;
+
                err = register_netdevice(tun->dev);
                if (err < 0)
                        goto err_free_sk;
@@ -1158,18 +1173,12 @@ static int tun_get_iff(struct net *net, struct tun_struct *tun,
 
 /* This is like a cut-down ethtool ops, except done via tun fd so no
  * privs required. */
-static int set_offload(struct net_device *dev, unsigned long arg)
+static int set_offload(struct tun_struct *tun, unsigned long arg)
 {
-       u32 old_features, features;
-
-       old_features = dev->features;
-       /* Unset features, set them as we chew on the arg. */
-       features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST
-                                   |NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6
-                                   |NETIF_F_UFO));
+       u32 features = 0;
 
        if (arg & TUN_F_CSUM) {
-               features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
+               features |= NETIF_F_HW_CSUM;
                arg &= ~TUN_F_CSUM;
 
                if (arg & (TUN_F_TSO4|TUN_F_TSO6)) {
@@ -1195,9 +1204,8 @@ static int set_offload(struct net_device *dev, unsigned long arg)
        if (arg)
                return -EINVAL;
 
-       dev->features = features;
-       if (old_features != dev->features)
-               netdev_features_change(dev);
+       tun->set_features = features;
+       netdev_update_features(tun->dev);
 
        return 0;
 }
@@ -1262,12 +1270,9 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
 
        case TUNSETNOCSUM:
                /* Disable/Enable checksum */
-               if (arg)
-                       tun->flags |= TUN_NOCHECKSUM;
-               else
-                       tun->flags &= ~TUN_NOCHECKSUM;
 
-               tun_debug(KERN_INFO, tun, "checksum %s\n",
+               /* [unimplemented] */
+               tun_debug(KERN_INFO, tun, "ignored: set checksum %s\n",
                          arg ? "disabled" : "enabled");
                break;
 
@@ -1316,7 +1321,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
                break;
 #endif
        case TUNSETOFFLOAD:
-               ret = set_offload(tun->dev, arg);
+               ret = set_offload(tun, arg);
                break;
 
        case TUNSETTXFILTER:
@@ -1595,30 +1600,12 @@ static void tun_set_msglevel(struct net_device *dev, u32 value)
 #endif
 }
 
-static u32 tun_get_rx_csum(struct net_device *dev)
-{
-       struct tun_struct *tun = netdev_priv(dev);
-       return (tun->flags & TUN_NOCHECKSUM) == 0;
-}
-
-static int tun_set_rx_csum(struct net_device *dev, u32 data)
-{
-       struct tun_struct *tun = netdev_priv(dev);
-       if (data)
-               tun->flags &= ~TUN_NOCHECKSUM;
-       else
-               tun->flags |= TUN_NOCHECKSUM;
-       return 0;
-}
-
 static const struct ethtool_ops tun_ethtool_ops = {
        .get_settings   = tun_get_settings,
        .get_drvinfo    = tun_get_drvinfo,
        .get_msglevel   = tun_get_msglevel,
        .set_msglevel   = tun_set_msglevel,
        .get_link       = ethtool_op_get_link,
-       .get_rx_csum    = tun_get_rx_csum,
-       .set_rx_csum    = tun_set_rx_csum
 };