tun: TUNSETFEATURES to set gso features.
Rusty Russell [Thu, 3 Jul 2008 10:46:16 +0000 (03:46 -0700)]
ethtool is useful for setting (some) device fields, but it's
root-only.  Finer feature control is available through a tun-specific
ioctl.

(Includes Mark McLoughlin <markmc@redhat.com>'s fix to hold rtnl sem).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Acked-by: Max Krasnyansky <maxk@qualcomm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

drivers/net/tun.c
include/linux/if_tun.h

index 3bb991f..a314955 100644 (file)
@@ -611,6 +611,46 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
        return err;
 }
 
+/* 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)
+{
+       unsigned int 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));
+
+       if (arg & TUN_F_CSUM) {
+               features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
+               arg &= ~TUN_F_CSUM;
+
+               if (arg & (TUN_F_TSO4|TUN_F_TSO6)) {
+                       if (arg & TUN_F_TSO_ECN) {
+                               features |= NETIF_F_TSO_ECN;
+                               arg &= ~TUN_F_TSO_ECN;
+                       }
+                       if (arg & TUN_F_TSO4)
+                               features |= NETIF_F_TSO;
+                       if (arg & TUN_F_TSO6)
+                               features |= NETIF_F_TSO6;
+                       arg &= ~(TUN_F_TSO4|TUN_F_TSO6);
+               }
+       }
+
+       /* This gives the user a way to test for new features in future by
+        * trying to set them. */
+       if (arg)
+               return -EINVAL;
+
+       dev->features = features;
+       if (old_features != dev->features)
+               netdev_features_change(dev);
+
+       return 0;
+}
+
 static int tun_chr_ioctl(struct inode *inode, struct file *file,
                         unsigned int cmd, unsigned long arg)
 {
@@ -715,6 +755,15 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
                break;
 #endif
 
+       case TUNSETOFFLOAD:
+       {
+               int ret;
+               rtnl_lock();
+               ret = set_offload(tun->dev, arg);
+               rtnl_unlock();
+               return ret;
+       }
+
        case SIOCGIFFLAGS:
                ifr.ifr_flags = tun->if_flags;
                if (copy_to_user( argp, &ifr, sizeof ifr))
index 94f76a1..3f0a099 100644 (file)
@@ -41,6 +41,7 @@
 #define TUNSETLINK    _IOW('T', 205, int)
 #define TUNSETGROUP   _IOW('T', 206, int)
 #define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
 
 /* TUNSETIFF ifr flags */
 #define IFF_TUN                0x0001
 #define IFF_NO_PI      0x1000
 #define IFF_ONE_QUEUE  0x2000
 
+/* Features for GSO (TUNSETOFFLOAD). */
+#define TUN_F_CSUM     0x01    /* You can hand me unchecksummed packets. */
+#define TUN_F_TSO4     0x02    /* I can handle TSO for IPv4 packets */
+#define TUN_F_TSO6     0x04    /* I can handle TSO for IPv6 packets */
+#define TUN_F_TSO_ECN  0x08    /* I can handle TSO with ECN bits. */
+
 struct tun_pi {
        unsigned short flags;
        __be16 proto;