]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/tun.c
tun: TUNSETFEATURES to set gso features.
[linux-2.6-omap-h63xx.git] / drivers / net / tun.c
index 3bb991fd2b512b9bd20bb22066982e03f3b3245a..a314955e6994ca940e28d2c4bea02e8366bfbcae 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))