aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r--net/core/ethtool.c88
1 files changed, 70 insertions, 18 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 7a85367b3c2f..7d7e572cedc7 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -19,6 +19,7 @@
19#include <linux/netdevice.h> 19#include <linux/netdevice.h>
20#include <linux/bitops.h> 20#include <linux/bitops.h>
21#include <linux/uaccess.h> 21#include <linux/uaccess.h>
22#include <linux/vmalloc.h>
22#include <linux/slab.h> 23#include <linux/slab.h>
23 24
24/* 25/*
@@ -205,18 +206,24 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
205 struct ethtool_drvinfo info; 206 struct ethtool_drvinfo info;
206 const struct ethtool_ops *ops = dev->ethtool_ops; 207 const struct ethtool_ops *ops = dev->ethtool_ops;
207 208
208 if (!ops->get_drvinfo)
209 return -EOPNOTSUPP;
210
211 memset(&info, 0, sizeof(info)); 209 memset(&info, 0, sizeof(info));
212 info.cmd = ETHTOOL_GDRVINFO; 210 info.cmd = ETHTOOL_GDRVINFO;
213 ops->get_drvinfo(dev, &info); 211 if (ops && ops->get_drvinfo) {
212 ops->get_drvinfo(dev, &info);
213 } else if (dev->dev.parent && dev->dev.parent->driver) {
214 strlcpy(info.bus_info, dev_name(dev->dev.parent),
215 sizeof(info.bus_info));
216 strlcpy(info.driver, dev->dev.parent->driver->name,
217 sizeof(info.driver));
218 } else {
219 return -EOPNOTSUPP;
220 }
214 221
215 /* 222 /*
216 * this method of obtaining string set info is deprecated; 223 * this method of obtaining string set info is deprecated;
217 * Use ETHTOOL_GSSET_INFO instead. 224 * Use ETHTOOL_GSSET_INFO instead.
218 */ 225 */
219 if (ops->get_sset_count) { 226 if (ops && ops->get_sset_count) {
220 int rc; 227 int rc;
221 228
222 rc = ops->get_sset_count(dev, ETH_SS_TEST); 229 rc = ops->get_sset_count(dev, ETH_SS_TEST);
@@ -229,9 +236,9 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
229 if (rc >= 0) 236 if (rc >= 0)
230 info.n_priv_flags = rc; 237 info.n_priv_flags = rc;
231 } 238 }
232 if (ops->get_regs_len) 239 if (ops && ops->get_regs_len)
233 info.regdump_len = ops->get_regs_len(dev); 240 info.regdump_len = ops->get_regs_len(dev);
234 if (ops->get_eeprom_len) 241 if (ops && ops->get_eeprom_len)
235 info.eedump_len = ops->get_eeprom_len(dev); 242 info.eedump_len = ops->get_eeprom_len(dev);
236 243
237 if (copy_to_user(useraddr, &info, sizeof(info))) 244 if (copy_to_user(useraddr, &info, sizeof(info)))
@@ -479,6 +486,38 @@ static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
479 list->count++; 486 list->count++;
480} 487}
481 488
489/*
490 * ethtool does not (or did not) set masks for flow parameters that are
491 * not specified, so if both value and mask are 0 then this must be
492 * treated as equivalent to a mask with all bits set. Implement that
493 * here rather than in drivers.
494 */
495static void rx_ntuple_fix_masks(struct ethtool_rx_ntuple_flow_spec *fs)
496{
497 struct ethtool_tcpip4_spec *entry = &fs->h_u.tcp_ip4_spec;
498 struct ethtool_tcpip4_spec *mask = &fs->m_u.tcp_ip4_spec;
499
500 if (fs->flow_type != TCP_V4_FLOW &&
501 fs->flow_type != UDP_V4_FLOW &&
502 fs->flow_type != SCTP_V4_FLOW)
503 return;
504
505 if (!(entry->ip4src | mask->ip4src))
506 mask->ip4src = htonl(0xffffffff);
507 if (!(entry->ip4dst | mask->ip4dst))
508 mask->ip4dst = htonl(0xffffffff);
509 if (!(entry->psrc | mask->psrc))
510 mask->psrc = htons(0xffff);
511 if (!(entry->pdst | mask->pdst))
512 mask->pdst = htons(0xffff);
513 if (!(entry->tos | mask->tos))
514 mask->tos = 0xff;
515 if (!(fs->vlan_tag | fs->vlan_tag_mask))
516 fs->vlan_tag_mask = 0xffff;
517 if (!(fs->data | fs->data_mask))
518 fs->data_mask = 0xffffffffffffffffULL;
519}
520
482static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, 521static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev,
483 void __user *useraddr) 522 void __user *useraddr)
484{ 523{
@@ -493,6 +532,8 @@ static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev,
493 if (copy_from_user(&cmd, useraddr, sizeof(cmd))) 532 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
494 return -EFAULT; 533 return -EFAULT;
495 534
535 rx_ntuple_fix_masks(&cmd.fs);
536
496 /* 537 /*
497 * Cache filter in dev struct for GET operation only if 538 * Cache filter in dev struct for GET operation only if
498 * the underlying driver doesn't have its own GET operation, and 539 * the underlying driver doesn't have its own GET operation, and
@@ -667,19 +708,19 @@ static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr)
667 break; 708 break;
668 case IP_USER_FLOW: 709 case IP_USER_FLOW:
669 sprintf(p, "\tSrc IP addr: 0x%x\n", 710 sprintf(p, "\tSrc IP addr: 0x%x\n",
670 fsc->fs.h_u.raw_ip4_spec.ip4src); 711 fsc->fs.h_u.usr_ip4_spec.ip4src);
671 p += ETH_GSTRING_LEN; 712 p += ETH_GSTRING_LEN;
672 num_strings++; 713 num_strings++;
673 sprintf(p, "\tSrc IP mask: 0x%x\n", 714 sprintf(p, "\tSrc IP mask: 0x%x\n",
674 fsc->fs.m_u.raw_ip4_spec.ip4src); 715 fsc->fs.m_u.usr_ip4_spec.ip4src);
675 p += ETH_GSTRING_LEN; 716 p += ETH_GSTRING_LEN;
676 num_strings++; 717 num_strings++;
677 sprintf(p, "\tDest IP addr: 0x%x\n", 718 sprintf(p, "\tDest IP addr: 0x%x\n",
678 fsc->fs.h_u.raw_ip4_spec.ip4dst); 719 fsc->fs.h_u.usr_ip4_spec.ip4dst);
679 p += ETH_GSTRING_LEN; 720 p += ETH_GSTRING_LEN;
680 num_strings++; 721 num_strings++;
681 sprintf(p, "\tDest IP mask: 0x%x\n", 722 sprintf(p, "\tDest IP mask: 0x%x\n",
682 fsc->fs.m_u.raw_ip4_spec.ip4dst); 723 fsc->fs.m_u.usr_ip4_spec.ip4dst);
683 p += ETH_GSTRING_LEN; 724 p += ETH_GSTRING_LEN;
684 num_strings++; 725 num_strings++;
685 break; 726 break;
@@ -775,7 +816,7 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
775 if (regs.len > reglen) 816 if (regs.len > reglen)
776 regs.len = reglen; 817 regs.len = reglen;
777 818
778 regbuf = kmalloc(reglen, GFP_USER); 819 regbuf = vmalloc(reglen);
779 if (!regbuf) 820 if (!regbuf)
780 return -ENOMEM; 821 return -ENOMEM;
781 822
@@ -790,7 +831,7 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
790 ret = 0; 831 ret = 0;
791 832
792 out: 833 out:
793 kfree(regbuf); 834 vfree(regbuf);
794 return ret; 835 return ret;
795} 836}
796 837
@@ -1175,8 +1216,11 @@ static int ethtool_set_gro(struct net_device *dev, char __user *useraddr)
1175 return -EFAULT; 1216 return -EFAULT;
1176 1217
1177 if (edata.data) { 1218 if (edata.data) {
1178 if (!dev->ethtool_ops->get_rx_csum || 1219 u32 rxcsum = dev->ethtool_ops->get_rx_csum ?
1179 !dev->ethtool_ops->get_rx_csum(dev)) 1220 dev->ethtool_ops->get_rx_csum(dev) :
1221 ethtool_op_get_rx_csum(dev);
1222
1223 if (!rxcsum)
1180 return -EINVAL; 1224 return -EINVAL;
1181 dev->features |= NETIF_F_GRO; 1225 dev->features |= NETIF_F_GRO;
1182 } else 1226 } else
@@ -1402,14 +1446,22 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1402 if (!dev || !netif_device_present(dev)) 1446 if (!dev || !netif_device_present(dev))
1403 return -ENODEV; 1447 return -ENODEV;
1404 1448
1405 if (!dev->ethtool_ops)
1406 return -EOPNOTSUPP;
1407
1408 if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd))) 1449 if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
1409 return -EFAULT; 1450 return -EFAULT;
1410 1451
1452 if (!dev->ethtool_ops) {
1453 /* ETHTOOL_GDRVINFO does not require any driver support.
1454 * It is also unprivileged and does not change anything,
1455 * so we can take a shortcut to it. */
1456 if (ethcmd == ETHTOOL_GDRVINFO)
1457 return ethtool_get_drvinfo(dev, useraddr);
1458 else
1459 return -EOPNOTSUPP;
1460 }
1461
1411 /* Allow some commands to be done by anyone */ 1462 /* Allow some commands to be done by anyone */
1412 switch (ethcmd) { 1463 switch (ethcmd) {
1464 case ETHTOOL_GSET:
1413 case ETHTOOL_GDRVINFO: 1465 case ETHTOOL_GDRVINFO:
1414 case ETHTOOL_GMSGLVL: 1466 case ETHTOOL_GMSGLVL:
1415 case ETHTOOL_GCOALESCE: 1467 case ETHTOOL_GCOALESCE: