]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/core/ethtool.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[net-next-2.6.git] / net / core / ethtool.c
index 75e4ffeb8cc99dae9c03c07c39962b17c4453f43..7a85367b3c2f8010af24bbd6b6f4249698f9d78d 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);
@@ -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 = kmalloc(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)
@@ -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;
        }