X25: Move interrupt flag to bitfield
[safe/jmp/linux-2.6] / net / core / ethtool.c
index 794cf57..1a7db92 100644 (file)
@@ -17,7 +17,9 @@
 #include <linux/errno.h>
 #include <linux/ethtool.h>
 #include <linux/netdevice.h>
-#include <asm/uaccess.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
 
 /*
  * Some useful ethtool_ops methods that're device independent.
@@ -29,6 +31,7 @@ u32 ethtool_op_get_link(struct net_device *dev)
 {
        return netif_carrier_ok(dev) ? 1 : 0;
 }
+EXPORT_SYMBOL(ethtool_op_get_link);
 
 u32 ethtool_op_get_rx_csum(struct net_device *dev)
 {
@@ -61,6 +64,7 @@ int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data)
 
        return 0;
 }
+EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
 
 int ethtool_op_set_tx_ipv6_csum(struct net_device *dev, u32 data)
 {
@@ -71,11 +75,13 @@ int ethtool_op_set_tx_ipv6_csum(struct net_device *dev, u32 data)
 
        return 0;
 }
+EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum);
 
 u32 ethtool_op_get_sg(struct net_device *dev)
 {
        return (dev->features & NETIF_F_SG) != 0;
 }
+EXPORT_SYMBOL(ethtool_op_get_sg);
 
 int ethtool_op_set_sg(struct net_device *dev, u32 data)
 {
@@ -86,11 +92,13 @@ int ethtool_op_set_sg(struct net_device *dev, u32 data)
 
        return 0;
 }
+EXPORT_SYMBOL(ethtool_op_set_sg);
 
 u32 ethtool_op_get_tso(struct net_device *dev)
 {
        return (dev->features & NETIF_F_TSO) != 0;
 }
+EXPORT_SYMBOL(ethtool_op_get_tso);
 
 int ethtool_op_set_tso(struct net_device *dev, u32 data)
 {
@@ -101,11 +109,13 @@ int ethtool_op_set_tso(struct net_device *dev, u32 data)
 
        return 0;
 }
+EXPORT_SYMBOL(ethtool_op_set_tso);
 
 u32 ethtool_op_get_ufo(struct net_device *dev)
 {
        return (dev->features & NETIF_F_UFO) != 0;
 }
+EXPORT_SYMBOL(ethtool_op_get_ufo);
 
 int ethtool_op_set_ufo(struct net_device *dev, u32 data)
 {
@@ -115,12 +125,13 @@ int ethtool_op_set_ufo(struct net_device *dev, u32 data)
                dev->features &= ~NETIF_F_UFO;
        return 0;
 }
+EXPORT_SYMBOL(ethtool_op_set_ufo);
 
 /* the following list of flags are the same as their associated
  * NETIF_F_xxx values in include/linux/netdevice.h
  */
 static const u32 flags_dup_features =
-       (ETH_FLAG_LRO | ETH_FLAG_NTUPLE);
+       (ETH_FLAG_LRO | ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH);
 
 u32 ethtool_op_get_flags(struct net_device *dev)
 {
@@ -131,27 +142,36 @@ u32 ethtool_op_get_flags(struct net_device *dev)
 
        return dev->features & flags_dup_features;
 }
+EXPORT_SYMBOL(ethtool_op_get_flags);
 
 int ethtool_op_set_flags(struct net_device *dev, u32 data)
 {
        const struct ethtool_ops *ops = dev->ethtool_ops;
+       unsigned long features = dev->features;
 
        if (data & ETH_FLAG_LRO)
-               dev->features |= NETIF_F_LRO;
+               features |= NETIF_F_LRO;
        else
-               dev->features &= ~NETIF_F_LRO;
+               features &= ~NETIF_F_LRO;
 
        if (data & ETH_FLAG_NTUPLE) {
                if (!ops->set_rx_ntuple)
                        return -EOPNOTSUPP;
-               dev->features |= NETIF_F_NTUPLE;
+               features |= NETIF_F_NTUPLE;
        } else {
                /* safe to clear regardless */
-               dev->features &= ~NETIF_F_NTUPLE;
+               features &= ~NETIF_F_NTUPLE;
        }
 
+       if (data & ETH_FLAG_RXHASH)
+               features |= NETIF_F_RXHASH;
+       else
+               features &= ~NETIF_F_RXHASH;
+
+       dev->features = features;
        return 0;
 }
+EXPORT_SYMBOL(ethtool_op_set_flags);
 
 void ethtool_ntuple_flush(struct net_device *dev)
 {
@@ -197,7 +217,8 @@ 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;
@@ -209,6 +230,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;
 
@@ -232,7 +257,68 @@ 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;
 
@@ -245,7 +331,8 @@ 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;
@@ -290,8 +377,8 @@ err_out:
 }
 
 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 *spec,
+                       struct ethtool_rx_ntuple_flow_spec_container *fsc)
 {
 
        /* don't add filters forever */
@@ -317,7 +404,8 @@ static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
        list->count++;
 }
 
-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;
@@ -442,125 +530,125 @@ static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr)
                case UDP_V4_FLOW:
                case SCTP_V4_FLOW:
                        sprintf(p, "\tSrc IP addr: 0x%x\n",
-                               fsc->fs.h_u.tcp_ip4_spec.ip4src);
+                               fsc->fs.h_u.tcp_ip4_spec.ip4src);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tSrc IP mask: 0x%x\n",
-                               fsc->fs.m_u.tcp_ip4_spec.ip4src);
+                               fsc->fs.m_u.tcp_ip4_spec.ip4src);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest IP addr: 0x%x\n",
-                               fsc->fs.h_u.tcp_ip4_spec.ip4dst);
+                               fsc->fs.h_u.tcp_ip4_spec.ip4dst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest IP mask: 0x%x\n",
-                               fsc->fs.m_u.tcp_ip4_spec.ip4dst);
+                               fsc->fs.m_u.tcp_ip4_spec.ip4dst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tSrc Port: %d, mask: 0x%x\n",
-                               fsc->fs.h_u.tcp_ip4_spec.psrc,
-                               fsc->fs.m_u.tcp_ip4_spec.psrc);
+                               fsc->fs.h_u.tcp_ip4_spec.psrc,
+                               fsc->fs.m_u.tcp_ip4_spec.psrc);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest Port: %d, mask: 0x%x\n",
-                               fsc->fs.h_u.tcp_ip4_spec.pdst,
-                               fsc->fs.m_u.tcp_ip4_spec.pdst);
+                               fsc->fs.h_u.tcp_ip4_spec.pdst,
+                               fsc->fs.m_u.tcp_ip4_spec.pdst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tTOS: %d, mask: 0x%x\n",
-                               fsc->fs.h_u.tcp_ip4_spec.tos,
-                               fsc->fs.m_u.tcp_ip4_spec.tos);
+                               fsc->fs.h_u.tcp_ip4_spec.tos,
+                               fsc->fs.m_u.tcp_ip4_spec.tos);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        break;
                case AH_ESP_V4_FLOW:
                case ESP_V4_FLOW:
                        sprintf(p, "\tSrc IP addr: 0x%x\n",
-                               fsc->fs.h_u.ah_ip4_spec.ip4src);
+                               fsc->fs.h_u.ah_ip4_spec.ip4src);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tSrc IP mask: 0x%x\n",
-                               fsc->fs.m_u.ah_ip4_spec.ip4src);
+                               fsc->fs.m_u.ah_ip4_spec.ip4src);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest IP addr: 0x%x\n",
-                               fsc->fs.h_u.ah_ip4_spec.ip4dst);
+                               fsc->fs.h_u.ah_ip4_spec.ip4dst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest IP mask: 0x%x\n",
-                               fsc->fs.m_u.ah_ip4_spec.ip4dst);
+                               fsc->fs.m_u.ah_ip4_spec.ip4dst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tSPI: %d, mask: 0x%x\n",
-                               fsc->fs.h_u.ah_ip4_spec.spi,
-                               fsc->fs.m_u.ah_ip4_spec.spi);
+                               fsc->fs.h_u.ah_ip4_spec.spi,
+                               fsc->fs.m_u.ah_ip4_spec.spi);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tTOS: %d, mask: 0x%x\n",
-                               fsc->fs.h_u.ah_ip4_spec.tos,
-                               fsc->fs.m_u.ah_ip4_spec.tos);
+                               fsc->fs.h_u.ah_ip4_spec.tos,
+                               fsc->fs.m_u.ah_ip4_spec.tos);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        break;
                case IP_USER_FLOW:
                        sprintf(p, "\tSrc IP addr: 0x%x\n",
-                               fsc->fs.h_u.raw_ip4_spec.ip4src);
+                               fsc->fs.h_u.raw_ip4_spec.ip4src);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tSrc IP mask: 0x%x\n",
-                               fsc->fs.m_u.raw_ip4_spec.ip4src);
+                               fsc->fs.m_u.raw_ip4_spec.ip4src);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest IP addr: 0x%x\n",
-                               fsc->fs.h_u.raw_ip4_spec.ip4dst);
+                               fsc->fs.h_u.raw_ip4_spec.ip4dst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest IP mask: 0x%x\n",
-                               fsc->fs.m_u.raw_ip4_spec.ip4dst);
+                               fsc->fs.m_u.raw_ip4_spec.ip4dst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        break;
                case IPV4_FLOW:
                        sprintf(p, "\tSrc IP addr: 0x%x\n",
-                               fsc->fs.h_u.usr_ip4_spec.ip4src);
+                               fsc->fs.h_u.usr_ip4_spec.ip4src);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tSrc IP mask: 0x%x\n",
-                               fsc->fs.m_u.usr_ip4_spec.ip4src);
+                               fsc->fs.m_u.usr_ip4_spec.ip4src);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest IP addr: 0x%x\n",
-                               fsc->fs.h_u.usr_ip4_spec.ip4dst);
+                               fsc->fs.h_u.usr_ip4_spec.ip4dst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tDest IP mask: 0x%x\n",
-                               fsc->fs.m_u.usr_ip4_spec.ip4dst);
+                               fsc->fs.m_u.usr_ip4_spec.ip4dst);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tL4 bytes: 0x%x, mask: 0x%x\n",
-                               fsc->fs.h_u.usr_ip4_spec.l4_4_bytes,
-                               fsc->fs.m_u.usr_ip4_spec.l4_4_bytes);
+                               fsc->fs.h_u.usr_ip4_spec.l4_4_bytes,
+                               fsc->fs.m_u.usr_ip4_spec.l4_4_bytes);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tTOS: %d, mask: 0x%x\n",
-                               fsc->fs.h_u.usr_ip4_spec.tos,
-                               fsc->fs.m_u.usr_ip4_spec.tos);
+                               fsc->fs.h_u.usr_ip4_spec.tos,
+                               fsc->fs.m_u.usr_ip4_spec.tos);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tIP Version: %d, mask: 0x%x\n",
-                               fsc->fs.h_u.usr_ip4_spec.ip_ver,
-                               fsc->fs.m_u.usr_ip4_spec.ip_ver);
+                               fsc->fs.h_u.usr_ip4_spec.ip_ver,
+                               fsc->fs.m_u.usr_ip4_spec.ip_ver);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        sprintf(p, "\tProtocol: %d, mask: 0x%x\n",
-                               fsc->fs.h_u.usr_ip4_spec.proto,
-                               fsc->fs.m_u.usr_ip4_spec.proto);
+                               fsc->fs.h_u.usr_ip4_spec.proto,
+                               fsc->fs.m_u.usr_ip4_spec.proto);
                        p += ETH_GSTRING_LEN;
                        num_strings++;
                        break;
                };
                sprintf(p, "\tVLAN: %d, mask: 0x%x\n",
-                       fsc->fs.vlan_tag, fsc->fs.vlan_tag_mask);
+                       fsc->fs.vlan_tag, fsc->fs.vlan_tag_mask);
                p += ETH_GSTRING_LEN;
                num_strings++;
                sprintf(p, "\tUser-defined: 0x%Lx\n", fsc->fs.data);
@@ -573,7 +661,7 @@ static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr)
                        sprintf(p, "\tAction: Drop\n");
                else
                        sprintf(p, "\tAction: Direct to queue %d\n",
-                               fsc->fs.action);
+                               fsc->fs.action);
                p += ETH_GSTRING_LEN;
                num_strings++;
 unknown_filter:
@@ -642,9 +730,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;
@@ -788,7 +873,8 @@ 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 = { .cmd = ETHTOOL_GCOALESCE };
 
@@ -802,7 +888,8 @@ 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;
 
@@ -906,6 +993,7 @@ static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr)
 
        return dev->ethtool_ops->set_tx_csum(dev, edata.data);
 }
+EXPORT_SYMBOL(ethtool_op_set_tx_csum);
 
 static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
 {
@@ -977,7 +1065,7 @@ static int ethtool_get_gso(struct net_device *dev, char __user *useraddr)
 
        edata.data = dev->features & NETIF_F_GSO;
        if (copy_to_user(useraddr, &edata, sizeof(edata)))
-                return -EFAULT;
+               return -EFAULT;
        return 0;
 }
 
@@ -1000,7 +1088,7 @@ static int ethtool_get_gro(struct net_device *dev, char __user *useraddr)
 
        edata.data = dev->features & NETIF_F_GRO;
        if (copy_to_user(useraddr, &edata, sizeof(edata)))
-                return -EFAULT;
+               return -EFAULT;
        return 0;
 }
 
@@ -1212,7 +1300,8 @@ 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;
 
@@ -1241,11 +1330,11 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        if (!dev->ethtool_ops)
                return -EOPNOTSUPP;
 
-       if (copy_from_user(&ethcmd, useraddr, sizeof (ethcmd)))
+       if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
                return -EFAULT;
 
        /* Allow some commands to be done by anyone */
-       switch(ethcmd) {
+       switch (ethcmd) {
        case ETHTOOL_GDRVINFO:
        case ETHTOOL_GMSGLVL:
        case ETHTOOL_GCOALESCE:
@@ -1259,6 +1348,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:
@@ -1272,10 +1362,11 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
                        return -EPERM;
        }
 
-       if (dev->ethtool_ops->begin)
-               if ((rc = dev->ethtool_ops->begin(dev)) < 0)
+       if (dev->ethtool_ops->begin) {
+               rc = dev->ethtool_ops->begin(dev);
+               if (rc  < 0)
                        return rc;
-
+       }
        old_features = dev->features;
 
        switch (ethcmd) {
@@ -1450,6 +1541,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;
        }
@@ -1462,16 +1556,3 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
 
        return rc;
 }
-
-EXPORT_SYMBOL(ethtool_op_get_link);
-EXPORT_SYMBOL(ethtool_op_get_sg);
-EXPORT_SYMBOL(ethtool_op_get_tso);
-EXPORT_SYMBOL(ethtool_op_set_sg);
-EXPORT_SYMBOL(ethtool_op_set_tso);
-EXPORT_SYMBOL(ethtool_op_set_tx_csum);
-EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
-EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum);
-EXPORT_SYMBOL(ethtool_op_set_ufo);
-EXPORT_SYMBOL(ethtool_op_get_ufo);
-EXPORT_SYMBOL(ethtool_op_set_flags);
-EXPORT_SYMBOL(ethtool_op_get_flags);