aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVenkata Duvvuru <VenkatKumar.Duvvuru@Emulex.Com>2014-04-21 06:07:59 -0400
committerDavid S. Miller <davem@davemloft.net>2014-04-22 21:27:57 -0400
commit3de0b592394d17b2c41a261a6a493a521213f299 (patch)
tree34dbd689895d3c808fc25feb3c881a6f378236fe
parent862aa49164812e8da2d5b96323ed2b680f255e71 (diff)
ethtool: Support for configurable RSS hash key
This ethtool patch primarily copies the ioctl command data structures from/to the User space and invokes the driver hook. Signed-off-by: Venkat Duvvuru <VenkatKumar.Duvvuru@Emulex.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/ethtool.h13
-rw-r--r--include/uapi/linux/ethtool.h32
-rw-r--r--net/core/ethtool.c221
3 files changed, 252 insertions, 14 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 0a114d05f68d..212f537fc686 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -154,13 +154,23 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
154 * @reset: Reset (part of) the device, as specified by a bitmask of 154 * @reset: Reset (part of) the device, as specified by a bitmask of
155 * flags from &enum ethtool_reset_flags. Returns a negative 155 * flags from &enum ethtool_reset_flags. Returns a negative
156 * error code or zero. 156 * error code or zero.
157 * @get_rxfh_key_size: Get the size of the RX flow hash key.
158 * Returns zero if not supported for this specific device.
157 * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table. 159 * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table.
158 * Returns zero if not supported for this specific device. 160 * Returns zero if not supported for this specific device.
159 * @get_rxfh_indir: Get the contents of the RX flow hash indirection table. 161 * @get_rxfh_indir: Get the contents of the RX flow hash indirection table.
160 * Will not be called if @get_rxfh_indir_size returns zero. 162 * Will not be called if @get_rxfh_indir_size returns zero.
163 * @get_rxfh: Get the contents of the RX flow hash indirection table and hash
164 * key.
165 * Will not be called if @get_rxfh_indir_size and @get_rxfh_key_size
166 * returns zero.
161 * Returns a negative error code or zero. 167 * Returns a negative error code or zero.
162 * @set_rxfh_indir: Set the contents of the RX flow hash indirection table. 168 * @set_rxfh_indir: Set the contents of the RX flow hash indirection table.
163 * Will not be called if @get_rxfh_indir_size returns zero. 169 * Will not be called if @get_rxfh_indir_size returns zero.
170 * @set_rxfh: Set the contents of the RX flow hash indirection table and
171 * hash key.
172 * Will not be called if @get_rxfh_indir_size and @get_rxfh_key_size
173 * returns zero.
164 * Returns a negative error code or zero. 174 * Returns a negative error code or zero.
165 * @get_channels: Get number of channels. 175 * @get_channels: Get number of channels.
166 * @set_channels: Set number of channels. Returns a negative error code or 176 * @set_channels: Set number of channels. Returns a negative error code or
@@ -232,7 +242,10 @@ struct ethtool_ops {
232 int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); 242 int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *);
233 int (*flash_device)(struct net_device *, struct ethtool_flash *); 243 int (*flash_device)(struct net_device *, struct ethtool_flash *);
234 int (*reset)(struct net_device *, u32 *); 244 int (*reset)(struct net_device *, u32 *);
245 u32 (*get_rxfh_key_size)(struct net_device *);
235 u32 (*get_rxfh_indir_size)(struct net_device *); 246 u32 (*get_rxfh_indir_size)(struct net_device *);
247 int (*get_rxfh)(struct net_device *, u32 *, u8 *);
248 int (*set_rxfh)(struct net_device *, u32 *, u8 *);
236 int (*get_rxfh_indir)(struct net_device *, u32 *); 249 int (*get_rxfh_indir)(struct net_device *, u32 *);
237 int (*set_rxfh_indir)(struct net_device *, const u32 *); 250 int (*set_rxfh_indir)(struct net_device *, const u32 *);
238 void (*get_channels)(struct net_device *, struct ethtool_channels *); 251 void (*get_channels)(struct net_device *, struct ethtool_channels *);
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index fd161e91b6d7..d47d31d6fa0e 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -847,6 +847,35 @@ struct ethtool_rxfh_indir {
847}; 847};
848 848
849/** 849/**
850 * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key.
851 * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH
852 * @rss_context: RSS context identifier.
853 * @indir_size: On entry, the array size of the user buffer, which may be zero.
854 * On return from %ETHTOOL_GRSSH, the array size of the hardware
855 * indirection table.
856 * @key_size: On entry, the array size of the user buffer in bytes,
857 * which may be zero.
858 * On return from %ETHTOOL_GRSSH, the size of the RSS hash key.
859 * @rsvd: Reserved for future extensions.
860 * @rss_config: RX ring/queue index for each hash value i.e., indirection table
861 * of size @indir_size followed by hash key of size @key_size.
862 *
863 * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the
864 * size should be returned. For %ETHTOOL_SRSSH, a @indir_size of 0xDEADBEEF
865 * means that indir table setting is not requested and a @indir_size of zero
866 * means the indir table should be reset to default values. This last feature
867 * is not supported by the original implementations.
868 */
869struct ethtool_rxfh {
870 __u32 cmd;
871 __u32 rss_context;
872 __u32 indir_size;
873 __u32 key_size;
874 __u32 rsvd[2];
875 __u32 rss_config[0];
876};
877
878/**
850 * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter 879 * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter
851 * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW 880 * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW
852 * @h_u: Flow field values to match (dependent on @flow_type) 881 * @h_u: Flow field values to match (dependent on @flow_type)
@@ -1118,6 +1147,9 @@ enum ethtool_sfeatures_retval_bits {
1118#define ETHTOOL_GEEE 0x00000044 /* Get EEE settings */ 1147#define ETHTOOL_GEEE 0x00000044 /* Get EEE settings */
1119#define ETHTOOL_SEEE 0x00000045 /* Set EEE settings */ 1148#define ETHTOOL_SEEE 0x00000045 /* Set EEE settings */
1120 1149
1150#define ETHTOOL_GRSSH 0x00000046 /* Get RX flow hash configuration */
1151#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */
1152
1121/* compatibility with older code */ 1153/* compatibility with older code */
1122#define SPARC_ETH_GSET ETHTOOL_GSET 1154#define SPARC_ETH_GSET ETHTOOL_GSET
1123#define SPARC_ETH_SSET ETHTOOL_SSET 1155#define SPARC_ETH_SSET ETHTOOL_SSET
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 640ba0e5831c..1d72786ef866 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -557,6 +557,23 @@ err_out:
557 return ret; 557 return ret;
558} 558}
559 559
560static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
561 struct ethtool_rxnfc *rx_rings,
562 u32 size)
563{
564 int ret = 0, i;
565
566 if (copy_from_user(indir, useraddr, size * sizeof(indir[0])))
567 ret = -EFAULT;
568
569 /* Validate ring indices */
570 for (i = 0; i < size; i++) {
571 if (indir[i] >= rx_rings->data)
572 ret = -EINVAL;
573 }
574 return ret;
575}
576
560static 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,
561 void __user *useraddr) 578 void __user *useraddr)
562{ 579{
@@ -613,6 +630,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
613 u32 *indir; 630 u32 *indir;
614 const struct ethtool_ops *ops = dev->ethtool_ops; 631 const struct ethtool_ops *ops = dev->ethtool_ops;
615 int ret; 632 int ret;
633 u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]);
616 634
617 if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir || 635 if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir ||
618 !ops->get_rxnfc) 636 !ops->get_rxnfc)
@@ -643,28 +661,196 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
643 for (i = 0; i < dev_size; i++) 661 for (i = 0; i < dev_size; i++)
644 indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); 662 indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
645 } else { 663 } else {
646 if (copy_from_user(indir, 664 ret = ethtool_copy_validate_indir(indir,
647 useraddr + 665 useraddr + ringidx_offset,
648 offsetof(struct ethtool_rxfh_indir, 666 &rx_rings,
649 ring_index[0]), 667 dev_size);
650 dev_size * sizeof(indir[0]))) { 668 if (ret)
669 goto out;
670 }
671
672 ret = ops->set_rxfh_indir(dev, indir);
673
674out:
675 kfree(indir);
676 return ret;
677}
678
679static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
680 void __user *useraddr)
681{
682 int ret;
683 const struct ethtool_ops *ops = dev->ethtool_ops;
684 u32 user_indir_size = 0, user_key_size = 0;
685 u32 dev_indir_size = 0, dev_key_size = 0;
686 u32 total_size;
687 u32 indir_offset, indir_bytes;
688 u32 key_offset;
689 u32 *indir = NULL;
690 u8 *hkey = NULL;
691 u8 *rss_config;
692
693 if (!(dev->ethtool_ops->get_rxfh_indir_size ||
694 dev->ethtool_ops->get_rxfh_key_size) ||
695 !dev->ethtool_ops->get_rxfh)
696 return -EOPNOTSUPP;
697
698 if (ops->get_rxfh_indir_size)
699 dev_indir_size = ops->get_rxfh_indir_size(dev);
700
701 indir_offset = offsetof(struct ethtool_rxfh, indir_size);
702
703 if (copy_from_user(&user_indir_size,
704 useraddr + indir_offset,
705 sizeof(user_indir_size)))
706 return -EFAULT;
707
708 if (copy_to_user(useraddr + indir_offset,
709 &dev_indir_size, sizeof(dev_indir_size)))
710 return -EFAULT;
711
712 if (ops->get_rxfh_key_size)
713 dev_key_size = ops->get_rxfh_key_size(dev);
714
715 if ((dev_key_size + dev_indir_size) == 0)
716 return -EOPNOTSUPP;
717
718 key_offset = offsetof(struct ethtool_rxfh, key_size);
719
720 if (copy_from_user(&user_key_size,
721 useraddr + key_offset,
722 sizeof(user_key_size)))
723 return -EFAULT;
724
725 if (copy_to_user(useraddr + key_offset,
726 &dev_key_size, sizeof(dev_key_size)))
727 return -EFAULT;
728
729 /* If the user buffer size is 0, this is just a query for the
730 * device table size and key size. Otherwise, if the User size is
731 * not equal to device table size or key size it's an error.
732 */
733 if (!user_indir_size && !user_key_size)
734 return 0;
735
736 if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
737 (user_key_size && (user_key_size != dev_key_size)))
738 return -EINVAL;
739
740 indir_bytes = user_indir_size * sizeof(indir[0]);
741 total_size = indir_bytes + user_key_size;
742 rss_config = kzalloc(total_size, GFP_USER);
743 if (!rss_config)
744 return -ENOMEM;
745
746 if (user_indir_size)
747 indir = (u32 *)rss_config;
748
749 if (user_key_size)
750 hkey = rss_config + indir_bytes;
751
752 ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey);
753 if (!ret) {
754 if (copy_to_user(useraddr +
755 offsetof(struct ethtool_rxfh, rss_config[0]),
756 rss_config, total_size))
651 ret = -EFAULT; 757 ret = -EFAULT;
758 }
759
760 kfree(rss_config);
761
762 return ret;
763}
764
765static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
766 void __user *useraddr)
767{
768 int ret;
769 const struct ethtool_ops *ops = dev->ethtool_ops;
770 struct ethtool_rxnfc rx_rings;
771 u32 user_indir_size = 0, dev_indir_size = 0, i;
772 u32 user_key_size = 0, dev_key_size = 0;
773 u32 *indir = NULL, indir_bytes = 0;
774 u8 *hkey = NULL;
775 u8 *rss_config;
776 u32 indir_offset, key_offset;
777 u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
778
779 if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) ||
780 !ops->get_rxnfc || !ops->set_rxfh)
781 return -EOPNOTSUPP;
782
783 if (ops->get_rxfh_indir_size)
784 dev_indir_size = ops->get_rxfh_indir_size(dev);
785
786 indir_offset = offsetof(struct ethtool_rxfh, indir_size);
787 if (copy_from_user(&user_indir_size,
788 useraddr + indir_offset,
789 sizeof(user_indir_size)))
790 return -EFAULT;
791
792 if (ops->get_rxfh_key_size)
793 dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev);
794
795 if ((dev_key_size + dev_indir_size) == 0)
796 return -EOPNOTSUPP;
797
798 key_offset = offsetof(struct ethtool_rxfh, key_size);
799 if (copy_from_user(&user_key_size,
800 useraddr + key_offset,
801 sizeof(user_key_size)))
802 return -EFAULT;
803
804 /* If either indir or hash key is valid, proceed further.
805 */
806 if ((user_indir_size && ((user_indir_size != 0xDEADBEEF) &&
807 user_indir_size != dev_indir_size)) ||
808 (user_key_size && (user_key_size != dev_key_size)))
809 return -EINVAL;
810
811 if (user_indir_size != 0xDEADBEEF)
812 indir_bytes = dev_indir_size * sizeof(indir[0]);
813
814 rss_config = kzalloc(indir_bytes + user_key_size, GFP_USER);
815 if (!rss_config)
816 return -ENOMEM;
817
818 rx_rings.cmd = ETHTOOL_GRXRINGS;
819 ret = ops->get_rxnfc(dev, &rx_rings, NULL);
820 if (ret)
821 goto out;
822
823 /* user_indir_size == 0 means reset the indir table to default.
824 * user_indir_size == 0xDEADBEEF means indir setting is not requested.
825 */
826 if (user_indir_size && user_indir_size != 0xDEADBEEF) {
827 indir = (u32 *)rss_config;
828 ret = ethtool_copy_validate_indir(indir,
829 useraddr + rss_cfg_offset,
830 &rx_rings,
831 user_indir_size);
832 if (ret)
652 goto out; 833 goto out;
653 } 834 } else if (user_indir_size == 0) {
835 indir = (u32 *)rss_config;
836 for (i = 0; i < dev_indir_size; i++)
837 indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
838 }
654 839
655 /* Validate ring indices */ 840 if (user_key_size) {
656 for (i = 0; i < dev_size; i++) { 841 hkey = rss_config + indir_bytes;
657 if (indir[i] >= rx_rings.data) { 842 if (copy_from_user(hkey,
658 ret = -EINVAL; 843 useraddr + rss_cfg_offset + indir_bytes,
659 goto out; 844 user_key_size)) {
660 } 845 ret = -EFAULT;
846 goto out;
661 } 847 }
662 } 848 }
663 849
664 ret = ops->set_rxfh_indir(dev, indir); 850 ret = ops->set_rxfh(dev, indir, hkey);
665 851
666out: 852out:
667 kfree(indir); 853 kfree(rss_config);
668 return ret; 854 return ret;
669} 855}
670 856
@@ -1491,6 +1677,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1491 case ETHTOOL_GRXCLSRULE: 1677 case ETHTOOL_GRXCLSRULE:
1492 case ETHTOOL_GRXCLSRLALL: 1678 case ETHTOOL_GRXCLSRLALL:
1493 case ETHTOOL_GRXFHINDIR: 1679 case ETHTOOL_GRXFHINDIR:
1680 case ETHTOOL_GRSSH:
1494 case ETHTOOL_GFEATURES: 1681 case ETHTOOL_GFEATURES:
1495 case ETHTOOL_GCHANNELS: 1682 case ETHTOOL_GCHANNELS:
1496 case ETHTOOL_GET_TS_INFO: 1683 case ETHTOOL_GET_TS_INFO:
@@ -1628,6 +1815,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1628 case ETHTOOL_SRXFHINDIR: 1815 case ETHTOOL_SRXFHINDIR:
1629 rc = ethtool_set_rxfh_indir(dev, useraddr); 1816 rc = ethtool_set_rxfh_indir(dev, useraddr);
1630 break; 1817 break;
1818 case ETHTOOL_GRSSH:
1819 rc = ethtool_get_rxfh(dev, useraddr);
1820 break;
1821 case ETHTOOL_SRSSH:
1822 rc = ethtool_set_rxfh(dev, useraddr);
1823 break;
1631 case ETHTOOL_GFEATURES: 1824 case ETHTOOL_GFEATURES:
1632 rc = ethtool_get_features(dev, useraddr); 1825 rc = ethtool_get_features(dev, useraddr);
1633 break; 1826 break;