diff options
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r-- | net/core/ethtool.c | 88 |
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 | */ | ||
495 | static 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 | |||
482 | static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, | 521 | static 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(ðcmd, useraddr, sizeof(ethcmd))) | 1449 | if (copy_from_user(ðcmd, 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: |