aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-06-03 02:07:02 -0400
committerDavid S. Miller <davem@davemloft.net>2014-06-03 02:07:02 -0400
commit014b20133bcd442db554c2d2d86181b34cd15b66 (patch)
tree3402034d4bba5628d21c98258634c58d5bb46bb7 /net
parenta68ab98e6c7ab0955babcdc45ca446886f3bfb25 (diff)
parentf062a3844845d267e3716cbc188ad502a15898b7 (diff)
Merge branch 'ethtool-rssh-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh/net-next
Ben Hutchings says: ==================== Pull request: Fixes for new ethtool RSS commands This addresses several problems I previously identified with the new ETHTOOL_{G,S}RSSH commands: 1. Missing validation of reserved parameters 2. Vague documentation 3. Use of unnamed magic number 4. No consolidation with existing driver operations I don't currently have access to suitable network hardware, but have tested these changes with a dummy driver that can support various combinations of operations and sizes, together with (a) Debian's ethtool 3.13 (b) ethtool 3.14 with the submitted patch to use ETHTOOL_{G,S}RSSH and minor adjustment for fixes 1 and 3. v2: Update RSS operations in vmxnet3 too ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/ethtool.c110
1 files changed, 48 insertions, 62 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index aa8978ac47d2..17cb912793fa 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -561,19 +561,17 @@ static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
561 struct ethtool_rxnfc *rx_rings, 561 struct ethtool_rxnfc *rx_rings,
562 u32 size) 562 u32 size)
563{ 563{
564 int ret = 0, i; 564 int i;
565 565
566 if (copy_from_user(indir, useraddr, size * sizeof(indir[0]))) 566 if (copy_from_user(indir, useraddr, size * sizeof(indir[0])))
567 ret = -EFAULT; 567 return -EFAULT;
568 568
569 /* Validate ring indices */ 569 /* Validate ring indices */
570 for (i = 0; i < size; i++) { 570 for (i = 0; i < size; i++)
571 if (indir[i] >= rx_rings->data) { 571 if (indir[i] >= rx_rings->data)
572 ret = -EINVAL; 572 return -EINVAL;
573 break; 573
574 } 574 return 0;
575 }
576 return ret;
577} 575}
578 576
579static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, 577static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
@@ -584,7 +582,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
584 int ret; 582 int ret;
585 583
586 if (!dev->ethtool_ops->get_rxfh_indir_size || 584 if (!dev->ethtool_ops->get_rxfh_indir_size ||
587 !dev->ethtool_ops->get_rxfh_indir) 585 !dev->ethtool_ops->get_rxfh)
588 return -EOPNOTSUPP; 586 return -EOPNOTSUPP;
589 dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); 587 dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
590 if (dev_size == 0) 588 if (dev_size == 0)
@@ -610,7 +608,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
610 if (!indir) 608 if (!indir)
611 return -ENOMEM; 609 return -ENOMEM;
612 610
613 ret = dev->ethtool_ops->get_rxfh_indir(dev, indir); 611 ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL);
614 if (ret) 612 if (ret)
615 goto out; 613 goto out;
616 614
@@ -634,7 +632,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
634 int ret; 632 int ret;
635 u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]); 633 u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]);
636 634
637 if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir || 635 if (!ops->get_rxfh_indir_size || !ops->set_rxfh ||
638 !ops->get_rxnfc) 636 !ops->get_rxnfc)
639 return -EOPNOTSUPP; 637 return -EOPNOTSUPP;
640 638
@@ -671,7 +669,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
671 goto out; 669 goto out;
672 } 670 }
673 671
674 ret = ops->set_rxfh_indir(dev, indir); 672 ret = ops->set_rxfh(dev, indir, NULL);
675 673
676out: 674out:
677 kfree(indir); 675 kfree(indir);
@@ -683,11 +681,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
683{ 681{
684 int ret; 682 int ret;
685 const struct ethtool_ops *ops = dev->ethtool_ops; 683 const struct ethtool_ops *ops = dev->ethtool_ops;
686 u32 user_indir_size = 0, user_key_size = 0; 684 u32 user_indir_size, user_key_size;
687 u32 dev_indir_size = 0, dev_key_size = 0; 685 u32 dev_indir_size = 0, dev_key_size = 0;
686 struct ethtool_rxfh rxfh;
688 u32 total_size; 687 u32 total_size;
689 u32 indir_offset, indir_bytes; 688 u32 indir_bytes;
690 u32 key_offset;
691 u32 *indir = NULL; 689 u32 *indir = NULL;
692 u8 *hkey = NULL; 690 u8 *hkey = NULL;
693 u8 *rss_config; 691 u8 *rss_config;
@@ -699,33 +697,24 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
699 697
700 if (ops->get_rxfh_indir_size) 698 if (ops->get_rxfh_indir_size)
701 dev_indir_size = ops->get_rxfh_indir_size(dev); 699 dev_indir_size = ops->get_rxfh_indir_size(dev);
702
703 indir_offset = offsetof(struct ethtool_rxfh, indir_size);
704
705 if (copy_from_user(&user_indir_size,
706 useraddr + indir_offset,
707 sizeof(user_indir_size)))
708 return -EFAULT;
709
710 if (copy_to_user(useraddr + indir_offset,
711 &dev_indir_size, sizeof(dev_indir_size)))
712 return -EFAULT;
713
714 if (ops->get_rxfh_key_size) 700 if (ops->get_rxfh_key_size)
715 dev_key_size = ops->get_rxfh_key_size(dev); 701 dev_key_size = ops->get_rxfh_key_size(dev);
716 702
717 if ((dev_key_size + dev_indir_size) == 0) 703 if ((dev_key_size + dev_indir_size) == 0)
718 return -EOPNOTSUPP; 704 return -EOPNOTSUPP;
719 705
720 key_offset = offsetof(struct ethtool_rxfh, key_size); 706 if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
721
722 if (copy_from_user(&user_key_size,
723 useraddr + key_offset,
724 sizeof(user_key_size)))
725 return -EFAULT; 707 return -EFAULT;
708 user_indir_size = rxfh.indir_size;
709 user_key_size = rxfh.key_size;
726 710
727 if (copy_to_user(useraddr + key_offset, 711 /* Check that reserved fields are 0 for now */
728 &dev_key_size, sizeof(dev_key_size))) 712 if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
713 return -EINVAL;
714
715 rxfh.indir_size = dev_indir_size;
716 rxfh.key_size = dev_key_size;
717 if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
729 return -EFAULT; 718 return -EFAULT;
730 719
731 /* If the user buffer size is 0, this is just a query for the 720 /* If the user buffer size is 0, this is just a query for the
@@ -770,12 +759,11 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
770 int ret; 759 int ret;
771 const struct ethtool_ops *ops = dev->ethtool_ops; 760 const struct ethtool_ops *ops = dev->ethtool_ops;
772 struct ethtool_rxnfc rx_rings; 761 struct ethtool_rxnfc rx_rings;
773 u32 user_indir_size = 0, dev_indir_size = 0, i; 762 struct ethtool_rxfh rxfh;
774 u32 user_key_size = 0, dev_key_size = 0; 763 u32 dev_indir_size = 0, dev_key_size = 0, i;
775 u32 *indir = NULL, indir_bytes = 0; 764 u32 *indir = NULL, indir_bytes = 0;
776 u8 *hkey = NULL; 765 u8 *hkey = NULL;
777 u8 *rss_config; 766 u8 *rss_config;
778 u32 indir_offset, key_offset;
779 u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); 767 u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
780 768
781 if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) || 769 if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) ||
@@ -784,36 +772,33 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
784 772
785 if (ops->get_rxfh_indir_size) 773 if (ops->get_rxfh_indir_size)
786 dev_indir_size = ops->get_rxfh_indir_size(dev); 774 dev_indir_size = ops->get_rxfh_indir_size(dev);
787
788 indir_offset = offsetof(struct ethtool_rxfh, indir_size);
789 if (copy_from_user(&user_indir_size,
790 useraddr + indir_offset,
791 sizeof(user_indir_size)))
792 return -EFAULT;
793
794 if (ops->get_rxfh_key_size) 775 if (ops->get_rxfh_key_size)
795 dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev); 776 dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev);
796
797 if ((dev_key_size + dev_indir_size) == 0) 777 if ((dev_key_size + dev_indir_size) == 0)
798 return -EOPNOTSUPP; 778 return -EOPNOTSUPP;
799 779
800 key_offset = offsetof(struct ethtool_rxfh, key_size); 780 if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
801 if (copy_from_user(&user_key_size,
802 useraddr + key_offset,
803 sizeof(user_key_size)))
804 return -EFAULT; 781 return -EFAULT;
805 782
783 /* Check that reserved fields are 0 for now */
784 if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
785 return -EINVAL;
786
806 /* If either indir or hash key is valid, proceed further. 787 /* If either indir or hash key is valid, proceed further.
788 * It is not valid to request that both be unchanged.
807 */ 789 */
808 if ((user_indir_size && ((user_indir_size != 0xDEADBEEF) && 790 if ((rxfh.indir_size &&
809 user_indir_size != dev_indir_size)) || 791 rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
810 (user_key_size && (user_key_size != dev_key_size))) 792 rxfh.indir_size != dev_indir_size) ||
793 (rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
794 (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
795 rxfh.key_size == 0))
811 return -EINVAL; 796 return -EINVAL;
812 797
813 if (user_indir_size != 0xDEADBEEF) 798 if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
814 indir_bytes = dev_indir_size * sizeof(indir[0]); 799 indir_bytes = dev_indir_size * sizeof(indir[0]);
815 800
816 rss_config = kzalloc(indir_bytes + user_key_size, GFP_USER); 801 rss_config = kzalloc(indir_bytes + rxfh.key_size, GFP_USER);
817 if (!rss_config) 802 if (!rss_config)
818 return -ENOMEM; 803 return -ENOMEM;
819 804
@@ -822,28 +807,29 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
822 if (ret) 807 if (ret)
823 goto out; 808 goto out;
824 809
825 /* user_indir_size == 0 means reset the indir table to default. 810 /* rxfh.indir_size == 0 means reset the indir table to default.
826 * user_indir_size == 0xDEADBEEF means indir setting is not requested. 811 * rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged.
827 */ 812 */
828 if (user_indir_size && user_indir_size != 0xDEADBEEF) { 813 if (rxfh.indir_size &&
814 rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) {
829 indir = (u32 *)rss_config; 815 indir = (u32 *)rss_config;
830 ret = ethtool_copy_validate_indir(indir, 816 ret = ethtool_copy_validate_indir(indir,
831 useraddr + rss_cfg_offset, 817 useraddr + rss_cfg_offset,
832 &rx_rings, 818 &rx_rings,
833 user_indir_size); 819 rxfh.indir_size);
834 if (ret) 820 if (ret)
835 goto out; 821 goto out;
836 } else if (user_indir_size == 0) { 822 } else if (rxfh.indir_size == 0) {
837 indir = (u32 *)rss_config; 823 indir = (u32 *)rss_config;
838 for (i = 0; i < dev_indir_size; i++) 824 for (i = 0; i < dev_indir_size; i++)
839 indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); 825 indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
840 } 826 }
841 827
842 if (user_key_size) { 828 if (rxfh.key_size) {
843 hkey = rss_config + indir_bytes; 829 hkey = rss_config + indir_bytes;
844 if (copy_from_user(hkey, 830 if (copy_from_user(hkey,
845 useraddr + rss_cfg_offset + indir_bytes, 831 useraddr + rss_cfg_offset + indir_bytes,
846 user_key_size)) { 832 rxfh.key_size)) {
847 ret = -EFAULT; 833 ret = -EFAULT;
848 goto out; 834 goto out;
849 } 835 }