phonet: use for_each_set_bit()
[safe/jmp/linux-2.6] / net / core / ethtool.c
index 6ec73d3..f4cb6b6 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/errno.h>
 #include <linux/ethtool.h>
 #include <linux/netdevice.h>
+#include <linux/bitops.h>
 #include <asm/uaccess.h>
 
 /*
@@ -134,16 +135,24 @@ u32 ethtool_op_get_flags(struct net_device *dev)
 
 int ethtool_op_set_flags(struct net_device *dev, u32 data)
 {
-       if (data & ETH_FLAG_LRO)
-               dev->features |= NETIF_F_LRO;
-       else
-               dev->features &= ~NETIF_F_LRO;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+       unsigned long features = dev->features;
 
-       if (data & ETH_FLAG_NTUPLE)
-               dev->features |= NETIF_F_NTUPLE;
+       if (data & ETH_FLAG_LRO)
+               features |= NETIF_F_LRO;
        else
-               dev->features &= ~NETIF_F_NTUPLE;
+               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;
+       }
 
+       dev->features = features;
        return 0;
 }
 
@@ -163,7 +172,7 @@ EXPORT_SYMBOL(ethtool_ntuple_flush);
 
 static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
 {
-       struct ethtool_cmd cmd = { ETHTOOL_GSET };
+       struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET };
        int err;
 
        if (!dev->ethtool_ops->get_settings)
@@ -191,7 +200,7 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
        return dev->ethtool_ops->set_settings(dev, &cmd);
 }
 
-static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr)
+static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_drvinfo info;
        const struct ethtool_ops *ops = dev->ethtool_ops;
@@ -203,6 +212,10 @@ static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr)
        info.cmd = ETHTOOL_GDRVINFO;
        ops->get_drvinfo(dev, &info);
 
+       /*
+        * this method of obtaining string set info is deprecated;
+        * Use ETHTOOL_GSSET_INFO instead.
+        */
        if (ops->get_sset_count) {
                int rc;
 
@@ -226,7 +239,67 @@ static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr)
        return 0;
 }
 
-static int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr)
+static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
+                                          void __user *useraddr)
+{
+       struct ethtool_sset_info info;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+       u64 sset_mask;
+       int i, idx = 0, n_bits = 0, ret, rc;
+       u32 *info_buf = NULL;
+
+       if (!ops->get_sset_count)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&info, useraddr, sizeof(info)))
+               return -EFAULT;
+
+       /* store copy of mask, because we zero struct later on */
+       sset_mask = info.sset_mask;
+       if (!sset_mask)
+               return 0;
+
+       /* calculate size of return buffer */
+       n_bits = hweight64(sset_mask);
+
+       memset(&info, 0, sizeof(info));
+       info.cmd = ETHTOOL_GSSET_INFO;
+
+       info_buf = kzalloc(n_bits * sizeof(u32), GFP_USER);
+       if (!info_buf)
+               return -ENOMEM;
+
+       /*
+        * fill return buffer based on input bitmask and successful
+        * get_sset_count return
+        */
+       for (i = 0; i < 64; i++) {
+               if (!(sset_mask & (1ULL << i)))
+                       continue;
+
+               rc = ops->get_sset_count(dev, i);
+               if (rc >= 0) {
+                       info.sset_mask |= (1ULL << i);
+                       info_buf[idx++] = rc;
+               }
+       }
+
+       ret = -EFAULT;
+       if (copy_to_user(useraddr, &info, sizeof(info)))
+               goto out;
+
+       useraddr += offsetof(struct ethtool_sset_info, data);
+       if (copy_to_user(useraddr, info_buf, idx * sizeof(u32)))
+               goto out;
+
+       ret = 0;
+
+out:
+       kfree(info_buf);
+       return ret;
+}
+
+static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_rxnfc cmd;
 
@@ -239,7 +312,7 @@ static int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr)
        return dev->ethtool_ops->set_rxnfc(dev, &cmd);
 }
 
-static int ethtool_get_rxnfc(struct net_device *dev, void __user *useraddr)
+static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_rxnfc info;
        const struct ethtool_ops *ops = dev->ethtool_ops;
@@ -283,18 +356,17 @@ err_out:
        return ret;
 }
 
-static int __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
-                                  struct ethtool_rx_ntuple_flow_spec *spec)
+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)
 {
-       struct ethtool_rx_ntuple_flow_spec_container *fsc;
 
        /* don't add filters forever */
-       if (list->count >= ETHTOOL_MAX_NTUPLE_LIST_ENTRY)
-               return 0;
-
-       fsc = kmalloc(sizeof(*fsc), GFP_ATOMIC);
-       if (!fsc)
-               return -ENOMEM;
+       if (list->count >= ETHTOOL_MAX_NTUPLE_LIST_ENTRY) {
+               /* free the container */
+               kfree(fsc);
+               return;
+       }
 
        /* Copy the whole filter over */
        fsc->fs.flow_type = spec->flow_type;
@@ -310,35 +382,41 @@ static int __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
        /* add to the list */
        list_add_tail_rcu(&fsc->list, &list->list);
        list->count++;
-
-       return 0;
 }
 
-static int ethtool_set_rx_ntuple(struct net_device *dev, void __user *useraddr)
+static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_rx_ntuple cmd;
        const struct ethtool_ops *ops = dev->ethtool_ops;
+       struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL;
        int ret;
 
-       if (!ops->set_rx_ntuple)
-               return -EOPNOTSUPP;
-
        if (!(dev->features & NETIF_F_NTUPLE))
                return -EINVAL;
 
        if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
                return -EFAULT;
 
-       ret = ops->set_rx_ntuple(dev, &cmd);
-
        /*
         * Cache filter in dev struct for GET operation only if
         * the underlying driver doesn't have its own GET operation, and
-        * only if the filter was added successfully.
+        * only if the filter was added successfully.  First make sure we
+        * can allocate the filter, then continue if successful.
         */
-       if (!ops->get_rx_ntuple && !ret)
-               if (__rx_ntuple_filter_add(&dev->ethtool_ntuple_list, &cmd.fs))
+       if (!ops->get_rx_ntuple) {
+               fsc = kmalloc(sizeof(*fsc), GFP_ATOMIC);
+               if (!fsc)
                        return -ENOMEM;
+       }
+
+       ret = ops->set_rx_ntuple(dev, &cmd);
+       if (ret) {
+               kfree(fsc);
+               return ret;
+       }
+
+       if (!ops->get_rx_ntuple)
+               __rx_ntuple_filter_add(&dev->ethtool_ntuple_list, &cmd.fs, fsc);
 
        return ret;
 }
@@ -631,9 +709,6 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
        if (copy_from_user(&reset, useraddr, sizeof(reset)))
                return -EFAULT;
 
-       /* Clear ethtool n-tuple list */
-       ethtool_ntuple_flush(dev);
-
        ret = dev->ethtool_ops->reset(dev, &reset.data);
        if (ret)
                return ret;
@@ -645,7 +720,7 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
 
 static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
 {
-       struct ethtool_wolinfo wol = { ETHTOOL_GWOL };
+       struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
 
        if (!dev->ethtool_ops->get_wol)
                return -EOPNOTSUPP;
@@ -777,9 +852,9 @@ static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
        return ret;
 }
 
-static int ethtool_get_coalesce(struct net_device *dev, void __user *useraddr)
+static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev, void __user *useraddr)
 {
-       struct ethtool_coalesce coalesce = { ETHTOOL_GCOALESCE };
+       struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
 
        if (!dev->ethtool_ops->get_coalesce)
                return -EOPNOTSUPP;
@@ -791,7 +866,7 @@ static int ethtool_get_coalesce(struct net_device *dev, void __user *useraddr)
        return 0;
 }
 
-static int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr)
+static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_coalesce coalesce;
 
@@ -806,7 +881,7 @@ static int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr)
 
 static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
 {
-       struct ethtool_ringparam ringparam = { ETHTOOL_GRINGPARAM };
+       struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM };
 
        if (!dev->ethtool_ops->get_ringparam)
                return -EOPNOTSUPP;
@@ -1160,7 +1235,7 @@ static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
 static int ethtool_get_value(struct net_device *dev, char __user *useraddr,
                             u32 cmd, u32 (*actor)(struct net_device *))
 {
-       struct ethtool_value edata = { cmd };
+       struct ethtool_value edata = { .cmd = cmd };
 
        if (!actor)
                return -EOPNOTSUPP;
@@ -1201,7 +1276,7 @@ static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
        return actor(dev, edata.data);
 }
 
-static int ethtool_flash_device(struct net_device *dev, char __user *useraddr)
+static noinline_for_stack int ethtool_flash_device(struct net_device *dev, char __user *useraddr)
 {
        struct ethtool_flash efl;
 
@@ -1248,6 +1323,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GPERMADDR:
        case ETHTOOL_GUFO:
        case ETHTOOL_GGSO:
+       case ETHTOOL_GGRO:
        case ETHTOOL_GFLAGS:
        case ETHTOOL_GPFLAGS:
        case ETHTOOL_GRXFH:
@@ -1439,6 +1515,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GRXNTUPLE:
                rc = ethtool_get_rx_ntuple(dev, useraddr);
                break;
+       case ETHTOOL_GSSET_INFO:
+               rc = ethtool_get_sset_info(dev, useraddr);
+               break;
        default:
                rc = -EOPNOTSUPP;
        }