Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/upstream-linus
[pandora-kernel.git] / net / core / ethtool.c
index 75e4ffe..8451ab4 100644 (file)
@@ -144,31 +144,13 @@ u32 ethtool_op_get_flags(struct net_device *dev)
 }
 EXPORT_SYMBOL(ethtool_op_get_flags);
 
-int ethtool_op_set_flags(struct net_device *dev, u32 data)
+int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported)
 {
-       const struct ethtool_ops *ops = dev->ethtool_ops;
-       unsigned long features = dev->features;
-
-       if (data & ETH_FLAG_LRO)
-               features |= NETIF_F_LRO;
-       else
-               features &= ~NETIF_F_LRO;
-
-       if (data & ETH_FLAG_NTUPLE) {
-               if (!ops->set_rx_ntuple)
-                       return -EOPNOTSUPP;
-               features |= NETIF_F_NTUPLE;
-       } else {
-               /* safe to clear regardless */
-               features &= ~NETIF_F_NTUPLE;
-       }
-
-       if (data & ETH_FLAG_RXHASH)
-               features |= NETIF_F_RXHASH;
-       else
-               features &= ~NETIF_F_RXHASH;
+       if (data & ~supported)
+               return -EINVAL;
 
-       dev->features = features;
+       dev->features = ((dev->features & ~flags_dup_features) |
+                        (data & flags_dup_features));
        return 0;
 }
 EXPORT_SYMBOL(ethtool_op_set_flags);
@@ -366,7 +348,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
        if (info.cmd == ETHTOOL_GRXCLSRLALL) {
                if (info.rule_cnt > 0) {
                        if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
-                               rule_buf = kmalloc(info.rule_cnt * sizeof(u32),
+                               rule_buf = kzalloc(info.rule_cnt * sizeof(u32),
                                                   GFP_USER);
                        if (!rule_buf)
                                return -ENOMEM;
@@ -395,6 +377,80 @@ err_out:
        return ret;
 }
 
+static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
+                                                    void __user *useraddr)
+{
+       struct ethtool_rxfh_indir *indir;
+       u32 table_size;
+       size_t full_size;
+       int ret;
+
+       if (!dev->ethtool_ops->get_rxfh_indir)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&table_size,
+                          useraddr + offsetof(struct ethtool_rxfh_indir, size),
+                          sizeof(table_size)))
+               return -EFAULT;
+
+       if (table_size >
+           (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
+               return -ENOMEM;
+       full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
+       indir = kzalloc(full_size, GFP_USER);
+       if (!indir)
+               return -ENOMEM;
+
+       indir->cmd = ETHTOOL_GRXFHINDIR;
+       indir->size = table_size;
+       ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
+       if (ret)
+               goto out;
+
+       if (copy_to_user(useraddr, indir, full_size))
+               ret = -EFAULT;
+
+out:
+       kfree(indir);
+       return ret;
+}
+
+static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
+                                                    void __user *useraddr)
+{
+       struct ethtool_rxfh_indir *indir;
+       u32 table_size;
+       size_t full_size;
+       int ret;
+
+       if (!dev->ethtool_ops->set_rxfh_indir)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&table_size,
+                          useraddr + offsetof(struct ethtool_rxfh_indir, size),
+                          sizeof(table_size)))
+               return -EFAULT;
+
+       if (table_size >
+           (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
+               return -ENOMEM;
+       full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
+       indir = kmalloc(full_size, GFP_USER);
+       if (!indir)
+               return -ENOMEM;
+
+       if (copy_from_user(indir, useraddr, full_size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       ret = dev->ethtool_ops->set_rxfh_indir(dev, indir);
+
+out:
+       kfree(indir);
+       return ret;
+}
+
 static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
                        struct ethtool_rx_ntuple_flow_spec *spec,
                        struct ethtool_rx_ntuple_flow_spec_container *fsc)
@@ -482,7 +538,7 @@ static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr)
 
        gstrings.len = ret;
 
-       data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
+       data = kzalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
        if (!data)
                return -ENOMEM;
 
@@ -719,7 +775,7 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
        if (regs.len > reglen)
                regs.len = reglen;
 
-       regbuf = kmalloc(reglen, GFP_USER);
+       regbuf = kzalloc(reglen, GFP_USER);
        if (!regbuf)
                return -ENOMEM;
 
@@ -1563,6 +1619,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GSSET_INFO:
                rc = ethtool_get_sset_info(dev, useraddr);
                break;
+       case ETHTOOL_GRXFHINDIR:
+               rc = ethtool_get_rxfh_indir(dev, useraddr);
+               break;
+       case ETHTOOL_SRXFHINDIR:
+               rc = ethtool_set_rxfh_indir(dev, useraddr);
+               break;
        default:
                rc = -EOPNOTSUPP;
        }