diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc/igc_ethtool.c')
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ethtool.c | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index eff37a6c0afa..25d14fc82bf8 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c | |||
@@ -2,6 +2,7 @@ | |||
2 | /* Copyright (c) 2018 Intel Corporation */ | 2 | /* Copyright (c) 2018 Intel Corporation */ |
3 | 3 | ||
4 | /* ethtool support for igc */ | 4 | /* ethtool support for igc */ |
5 | #include <linux/if_vlan.h> | ||
5 | #include <linux/pm_runtime.h> | 6 | #include <linux/pm_runtime.h> |
6 | 7 | ||
7 | #include "igc.h" | 8 | #include "igc.h" |
@@ -643,6 +644,605 @@ static int igc_set_coalesce(struct net_device *netdev, | |||
643 | return 0; | 644 | return 0; |
644 | } | 645 | } |
645 | 646 | ||
647 | #define ETHER_TYPE_FULL_MASK ((__force __be16)~0) | ||
648 | static int igc_get_ethtool_nfc_entry(struct igc_adapter *adapter, | ||
649 | struct ethtool_rxnfc *cmd) | ||
650 | { | ||
651 | struct ethtool_rx_flow_spec *fsp = &cmd->fs; | ||
652 | struct igc_nfc_filter *rule = NULL; | ||
653 | |||
654 | /* report total rule count */ | ||
655 | cmd->data = IGC_MAX_RXNFC_FILTERS; | ||
656 | |||
657 | hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) { | ||
658 | if (fsp->location <= rule->sw_idx) | ||
659 | break; | ||
660 | } | ||
661 | |||
662 | if (!rule || fsp->location != rule->sw_idx) | ||
663 | return -EINVAL; | ||
664 | |||
665 | if (rule->filter.match_flags) { | ||
666 | fsp->flow_type = ETHER_FLOW; | ||
667 | fsp->ring_cookie = rule->action; | ||
668 | if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) { | ||
669 | fsp->h_u.ether_spec.h_proto = rule->filter.etype; | ||
670 | fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK; | ||
671 | } | ||
672 | if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) { | ||
673 | fsp->flow_type |= FLOW_EXT; | ||
674 | fsp->h_ext.vlan_tci = rule->filter.vlan_tci; | ||
675 | fsp->m_ext.vlan_tci = htons(VLAN_PRIO_MASK); | ||
676 | } | ||
677 | if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) { | ||
678 | ether_addr_copy(fsp->h_u.ether_spec.h_dest, | ||
679 | rule->filter.dst_addr); | ||
680 | /* As we only support matching by the full | ||
681 | * mask, return the mask to userspace | ||
682 | */ | ||
683 | eth_broadcast_addr(fsp->m_u.ether_spec.h_dest); | ||
684 | } | ||
685 | if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) { | ||
686 | ether_addr_copy(fsp->h_u.ether_spec.h_source, | ||
687 | rule->filter.src_addr); | ||
688 | /* As we only support matching by the full | ||
689 | * mask, return the mask to userspace | ||
690 | */ | ||
691 | eth_broadcast_addr(fsp->m_u.ether_spec.h_source); | ||
692 | } | ||
693 | |||
694 | return 0; | ||
695 | } | ||
696 | return -EINVAL; | ||
697 | } | ||
698 | |||
699 | static int igc_get_ethtool_nfc_all(struct igc_adapter *adapter, | ||
700 | struct ethtool_rxnfc *cmd, | ||
701 | u32 *rule_locs) | ||
702 | { | ||
703 | struct igc_nfc_filter *rule; | ||
704 | int cnt = 0; | ||
705 | |||
706 | /* report total rule count */ | ||
707 | cmd->data = IGC_MAX_RXNFC_FILTERS; | ||
708 | |||
709 | hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) { | ||
710 | if (cnt == cmd->rule_cnt) | ||
711 | return -EMSGSIZE; | ||
712 | rule_locs[cnt] = rule->sw_idx; | ||
713 | cnt++; | ||
714 | } | ||
715 | |||
716 | cmd->rule_cnt = cnt; | ||
717 | |||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | static int igc_get_rss_hash_opts(struct igc_adapter *adapter, | ||
722 | struct ethtool_rxnfc *cmd) | ||
723 | { | ||
724 | cmd->data = 0; | ||
725 | |||
726 | /* Report default options for RSS on igc */ | ||
727 | switch (cmd->flow_type) { | ||
728 | case TCP_V4_FLOW: | ||
729 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | ||
730 | /* Fall through */ | ||
731 | case UDP_V4_FLOW: | ||
732 | if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV4_UDP) | ||
733 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | ||
734 | /* Fall through */ | ||
735 | case SCTP_V4_FLOW: | ||
736 | /* Fall through */ | ||
737 | case AH_ESP_V4_FLOW: | ||
738 | /* Fall through */ | ||
739 | case AH_V4_FLOW: | ||
740 | /* Fall through */ | ||
741 | case ESP_V4_FLOW: | ||
742 | /* Fall through */ | ||
743 | case IPV4_FLOW: | ||
744 | cmd->data |= RXH_IP_SRC | RXH_IP_DST; | ||
745 | break; | ||
746 | case TCP_V6_FLOW: | ||
747 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | ||
748 | /* Fall through */ | ||
749 | case UDP_V6_FLOW: | ||
750 | if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV6_UDP) | ||
751 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | ||
752 | /* Fall through */ | ||
753 | case SCTP_V6_FLOW: | ||
754 | /* Fall through */ | ||
755 | case AH_ESP_V6_FLOW: | ||
756 | /* Fall through */ | ||
757 | case AH_V6_FLOW: | ||
758 | /* Fall through */ | ||
759 | case ESP_V6_FLOW: | ||
760 | /* Fall through */ | ||
761 | case IPV6_FLOW: | ||
762 | cmd->data |= RXH_IP_SRC | RXH_IP_DST; | ||
763 | break; | ||
764 | default: | ||
765 | return -EINVAL; | ||
766 | } | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static int igc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, | ||
772 | u32 *rule_locs) | ||
773 | { | ||
774 | struct igc_adapter *adapter = netdev_priv(dev); | ||
775 | int ret = -EOPNOTSUPP; | ||
776 | |||
777 | switch (cmd->cmd) { | ||
778 | case ETHTOOL_GRXRINGS: | ||
779 | cmd->data = adapter->num_rx_queues; | ||
780 | ret = 0; | ||
781 | break; | ||
782 | case ETHTOOL_GRXCLSRLCNT: | ||
783 | cmd->rule_cnt = adapter->nfc_filter_count; | ||
784 | ret = 0; | ||
785 | break; | ||
786 | case ETHTOOL_GRXCLSRULE: | ||
787 | ret = igc_get_ethtool_nfc_entry(adapter, cmd); | ||
788 | break; | ||
789 | case ETHTOOL_GRXCLSRLALL: | ||
790 | ret = igc_get_ethtool_nfc_all(adapter, cmd, rule_locs); | ||
791 | break; | ||
792 | case ETHTOOL_GRXFH: | ||
793 | ret = igc_get_rss_hash_opts(adapter, cmd); | ||
794 | break; | ||
795 | default: | ||
796 | break; | ||
797 | } | ||
798 | |||
799 | return ret; | ||
800 | } | ||
801 | |||
802 | #define UDP_RSS_FLAGS (IGC_FLAG_RSS_FIELD_IPV4_UDP | \ | ||
803 | IGC_FLAG_RSS_FIELD_IPV6_UDP) | ||
804 | static int igc_set_rss_hash_opt(struct igc_adapter *adapter, | ||
805 | struct ethtool_rxnfc *nfc) | ||
806 | { | ||
807 | u32 flags = adapter->flags; | ||
808 | |||
809 | /* RSS does not support anything other than hashing | ||
810 | * to queues on src and dst IPs and ports | ||
811 | */ | ||
812 | if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | | ||
813 | RXH_L4_B_0_1 | RXH_L4_B_2_3)) | ||
814 | return -EINVAL; | ||
815 | |||
816 | switch (nfc->flow_type) { | ||
817 | case TCP_V4_FLOW: | ||
818 | case TCP_V6_FLOW: | ||
819 | if (!(nfc->data & RXH_IP_SRC) || | ||
820 | !(nfc->data & RXH_IP_DST) || | ||
821 | !(nfc->data & RXH_L4_B_0_1) || | ||
822 | !(nfc->data & RXH_L4_B_2_3)) | ||
823 | return -EINVAL; | ||
824 | break; | ||
825 | case UDP_V4_FLOW: | ||
826 | if (!(nfc->data & RXH_IP_SRC) || | ||
827 | !(nfc->data & RXH_IP_DST)) | ||
828 | return -EINVAL; | ||
829 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | ||
830 | case 0: | ||
831 | flags &= ~IGC_FLAG_RSS_FIELD_IPV4_UDP; | ||
832 | break; | ||
833 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | ||
834 | flags |= IGC_FLAG_RSS_FIELD_IPV4_UDP; | ||
835 | break; | ||
836 | default: | ||
837 | return -EINVAL; | ||
838 | } | ||
839 | break; | ||
840 | case UDP_V6_FLOW: | ||
841 | if (!(nfc->data & RXH_IP_SRC) || | ||
842 | !(nfc->data & RXH_IP_DST)) | ||
843 | return -EINVAL; | ||
844 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | ||
845 | case 0: | ||
846 | flags &= ~IGC_FLAG_RSS_FIELD_IPV6_UDP; | ||
847 | break; | ||
848 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | ||
849 | flags |= IGC_FLAG_RSS_FIELD_IPV6_UDP; | ||
850 | break; | ||
851 | default: | ||
852 | return -EINVAL; | ||
853 | } | ||
854 | break; | ||
855 | case AH_ESP_V4_FLOW: | ||
856 | case AH_V4_FLOW: | ||
857 | case ESP_V4_FLOW: | ||
858 | case SCTP_V4_FLOW: | ||
859 | case AH_ESP_V6_FLOW: | ||
860 | case AH_V6_FLOW: | ||
861 | case ESP_V6_FLOW: | ||
862 | case SCTP_V6_FLOW: | ||
863 | if (!(nfc->data & RXH_IP_SRC) || | ||
864 | !(nfc->data & RXH_IP_DST) || | ||
865 | (nfc->data & RXH_L4_B_0_1) || | ||
866 | (nfc->data & RXH_L4_B_2_3)) | ||
867 | return -EINVAL; | ||
868 | break; | ||
869 | default: | ||
870 | return -EINVAL; | ||
871 | } | ||
872 | |||
873 | /* if we changed something we need to update flags */ | ||
874 | if (flags != adapter->flags) { | ||
875 | struct igc_hw *hw = &adapter->hw; | ||
876 | u32 mrqc = rd32(IGC_MRQC); | ||
877 | |||
878 | if ((flags & UDP_RSS_FLAGS) && | ||
879 | !(adapter->flags & UDP_RSS_FLAGS)) | ||
880 | dev_err(&adapter->pdev->dev, | ||
881 | "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); | ||
882 | |||
883 | adapter->flags = flags; | ||
884 | |||
885 | /* Perform hash on these packet types */ | ||
886 | mrqc |= IGC_MRQC_RSS_FIELD_IPV4 | | ||
887 | IGC_MRQC_RSS_FIELD_IPV4_TCP | | ||
888 | IGC_MRQC_RSS_FIELD_IPV6 | | ||
889 | IGC_MRQC_RSS_FIELD_IPV6_TCP; | ||
890 | |||
891 | mrqc &= ~(IGC_MRQC_RSS_FIELD_IPV4_UDP | | ||
892 | IGC_MRQC_RSS_FIELD_IPV6_UDP); | ||
893 | |||
894 | if (flags & IGC_FLAG_RSS_FIELD_IPV4_UDP) | ||
895 | mrqc |= IGC_MRQC_RSS_FIELD_IPV4_UDP; | ||
896 | |||
897 | if (flags & IGC_FLAG_RSS_FIELD_IPV6_UDP) | ||
898 | mrqc |= IGC_MRQC_RSS_FIELD_IPV6_UDP; | ||
899 | |||
900 | wr32(IGC_MRQC, mrqc); | ||
901 | } | ||
902 | |||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | static int igc_rxnfc_write_etype_filter(struct igc_adapter *adapter, | ||
907 | struct igc_nfc_filter *input) | ||
908 | { | ||
909 | struct igc_hw *hw = &adapter->hw; | ||
910 | u8 i; | ||
911 | u32 etqf; | ||
912 | u16 etype; | ||
913 | |||
914 | /* find an empty etype filter register */ | ||
915 | for (i = 0; i < MAX_ETYPE_FILTER; ++i) { | ||
916 | if (!adapter->etype_bitmap[i]) | ||
917 | break; | ||
918 | } | ||
919 | if (i == MAX_ETYPE_FILTER) { | ||
920 | dev_err(&adapter->pdev->dev, "ethtool -N: etype filters are all used.\n"); | ||
921 | return -EINVAL; | ||
922 | } | ||
923 | |||
924 | adapter->etype_bitmap[i] = true; | ||
925 | |||
926 | etqf = rd32(IGC_ETQF(i)); | ||
927 | etype = ntohs(input->filter.etype & ETHER_TYPE_FULL_MASK); | ||
928 | |||
929 | etqf |= IGC_ETQF_FILTER_ENABLE; | ||
930 | etqf &= ~IGC_ETQF_ETYPE_MASK; | ||
931 | etqf |= (etype & IGC_ETQF_ETYPE_MASK); | ||
932 | |||
933 | etqf &= ~IGC_ETQF_QUEUE_MASK; | ||
934 | etqf |= ((input->action << IGC_ETQF_QUEUE_SHIFT) | ||
935 | & IGC_ETQF_QUEUE_MASK); | ||
936 | etqf |= IGC_ETQF_QUEUE_ENABLE; | ||
937 | |||
938 | wr32(IGC_ETQF(i), etqf); | ||
939 | |||
940 | input->etype_reg_index = i; | ||
941 | |||
942 | return 0; | ||
943 | } | ||
944 | |||
945 | static int igc_rxnfc_write_vlan_prio_filter(struct igc_adapter *adapter, | ||
946 | struct igc_nfc_filter *input) | ||
947 | { | ||
948 | struct igc_hw *hw = &adapter->hw; | ||
949 | u8 vlan_priority; | ||
950 | u16 queue_index; | ||
951 | u32 vlapqf; | ||
952 | |||
953 | vlapqf = rd32(IGC_VLAPQF); | ||
954 | vlan_priority = (ntohs(input->filter.vlan_tci) & VLAN_PRIO_MASK) | ||
955 | >> VLAN_PRIO_SHIFT; | ||
956 | queue_index = (vlapqf >> (vlan_priority * 4)) & IGC_VLAPQF_QUEUE_MASK; | ||
957 | |||
958 | /* check whether this vlan prio is already set */ | ||
959 | if (vlapqf & IGC_VLAPQF_P_VALID(vlan_priority) && | ||
960 | queue_index != input->action) { | ||
961 | dev_err(&adapter->pdev->dev, "ethtool rxnfc set vlan prio filter failed.\n"); | ||
962 | return -EEXIST; | ||
963 | } | ||
964 | |||
965 | vlapqf |= IGC_VLAPQF_P_VALID(vlan_priority); | ||
966 | vlapqf |= IGC_VLAPQF_QUEUE_SEL(vlan_priority, input->action); | ||
967 | |||
968 | wr32(IGC_VLAPQF, vlapqf); | ||
969 | |||
970 | return 0; | ||
971 | } | ||
972 | |||
973 | int igc_add_filter(struct igc_adapter *adapter, struct igc_nfc_filter *input) | ||
974 | { | ||
975 | struct igc_hw *hw = &adapter->hw; | ||
976 | int err = -EINVAL; | ||
977 | |||
978 | if (hw->mac.type == igc_i225 && | ||
979 | !(input->filter.match_flags & ~IGC_FILTER_FLAG_SRC_MAC_ADDR)) { | ||
980 | dev_err(&adapter->pdev->dev, | ||
981 | "i225 doesn't support flow classification rules specifying only source addresses.\n"); | ||
982 | return -EOPNOTSUPP; | ||
983 | } | ||
984 | |||
985 | if (input->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) { | ||
986 | err = igc_rxnfc_write_etype_filter(adapter, input); | ||
987 | if (err) | ||
988 | return err; | ||
989 | } | ||
990 | |||
991 | if (input->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) { | ||
992 | err = igc_add_mac_steering_filter(adapter, | ||
993 | input->filter.dst_addr, | ||
994 | input->action, 0); | ||
995 | err = min_t(int, err, 0); | ||
996 | if (err) | ||
997 | return err; | ||
998 | } | ||
999 | |||
1000 | if (input->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) { | ||
1001 | err = igc_add_mac_steering_filter(adapter, | ||
1002 | input->filter.src_addr, | ||
1003 | input->action, | ||
1004 | IGC_MAC_STATE_SRC_ADDR); | ||
1005 | err = min_t(int, err, 0); | ||
1006 | if (err) | ||
1007 | return err; | ||
1008 | } | ||
1009 | |||
1010 | if (input->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) | ||
1011 | err = igc_rxnfc_write_vlan_prio_filter(adapter, input); | ||
1012 | |||
1013 | return err; | ||
1014 | } | ||
1015 | |||
1016 | static void igc_clear_etype_filter_regs(struct igc_adapter *adapter, | ||
1017 | u16 reg_index) | ||
1018 | { | ||
1019 | struct igc_hw *hw = &adapter->hw; | ||
1020 | u32 etqf = rd32(IGC_ETQF(reg_index)); | ||
1021 | |||
1022 | etqf &= ~IGC_ETQF_QUEUE_ENABLE; | ||
1023 | etqf &= ~IGC_ETQF_QUEUE_MASK; | ||
1024 | etqf &= ~IGC_ETQF_FILTER_ENABLE; | ||
1025 | |||
1026 | wr32(IGC_ETQF(reg_index), etqf); | ||
1027 | |||
1028 | adapter->etype_bitmap[reg_index] = false; | ||
1029 | } | ||
1030 | |||
1031 | static void igc_clear_vlan_prio_filter(struct igc_adapter *adapter, | ||
1032 | u16 vlan_tci) | ||
1033 | { | ||
1034 | struct igc_hw *hw = &adapter->hw; | ||
1035 | u8 vlan_priority; | ||
1036 | u32 vlapqf; | ||
1037 | |||
1038 | vlan_priority = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; | ||
1039 | |||
1040 | vlapqf = rd32(IGC_VLAPQF); | ||
1041 | vlapqf &= ~IGC_VLAPQF_P_VALID(vlan_priority); | ||
1042 | vlapqf &= ~IGC_VLAPQF_QUEUE_SEL(vlan_priority, | ||
1043 | IGC_VLAPQF_QUEUE_MASK); | ||
1044 | |||
1045 | wr32(IGC_VLAPQF, vlapqf); | ||
1046 | } | ||
1047 | |||
1048 | int igc_erase_filter(struct igc_adapter *adapter, struct igc_nfc_filter *input) | ||
1049 | { | ||
1050 | if (input->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) | ||
1051 | igc_clear_etype_filter_regs(adapter, | ||
1052 | input->etype_reg_index); | ||
1053 | |||
1054 | if (input->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) | ||
1055 | igc_clear_vlan_prio_filter(adapter, | ||
1056 | ntohs(input->filter.vlan_tci)); | ||
1057 | |||
1058 | if (input->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) | ||
1059 | igc_del_mac_steering_filter(adapter, input->filter.src_addr, | ||
1060 | input->action, | ||
1061 | IGC_MAC_STATE_SRC_ADDR); | ||
1062 | |||
1063 | if (input->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) | ||
1064 | igc_del_mac_steering_filter(adapter, input->filter.dst_addr, | ||
1065 | input->action, 0); | ||
1066 | |||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | static int igc_update_ethtool_nfc_entry(struct igc_adapter *adapter, | ||
1071 | struct igc_nfc_filter *input, | ||
1072 | u16 sw_idx) | ||
1073 | { | ||
1074 | struct igc_nfc_filter *rule, *parent; | ||
1075 | int err = -EINVAL; | ||
1076 | |||
1077 | parent = NULL; | ||
1078 | rule = NULL; | ||
1079 | |||
1080 | hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) { | ||
1081 | /* hash found, or no matching entry */ | ||
1082 | if (rule->sw_idx >= sw_idx) | ||
1083 | break; | ||
1084 | parent = rule; | ||
1085 | } | ||
1086 | |||
1087 | /* if there is an old rule occupying our place remove it */ | ||
1088 | if (rule && rule->sw_idx == sw_idx) { | ||
1089 | if (!input) | ||
1090 | err = igc_erase_filter(adapter, rule); | ||
1091 | |||
1092 | hlist_del(&rule->nfc_node); | ||
1093 | kfree(rule); | ||
1094 | adapter->nfc_filter_count--; | ||
1095 | } | ||
1096 | |||
1097 | /* If no input this was a delete, err should be 0 if a rule was | ||
1098 | * successfully found and removed from the list else -EINVAL | ||
1099 | */ | ||
1100 | if (!input) | ||
1101 | return err; | ||
1102 | |||
1103 | /* initialize node */ | ||
1104 | INIT_HLIST_NODE(&input->nfc_node); | ||
1105 | |||
1106 | /* add filter to the list */ | ||
1107 | if (parent) | ||
1108 | hlist_add_behind(&input->nfc_node, &parent->nfc_node); | ||
1109 | else | ||
1110 | hlist_add_head(&input->nfc_node, &adapter->nfc_filter_list); | ||
1111 | |||
1112 | /* update counts */ | ||
1113 | adapter->nfc_filter_count++; | ||
1114 | |||
1115 | return 0; | ||
1116 | } | ||
1117 | |||
1118 | static int igc_add_ethtool_nfc_entry(struct igc_adapter *adapter, | ||
1119 | struct ethtool_rxnfc *cmd) | ||
1120 | { | ||
1121 | struct net_device *netdev = adapter->netdev; | ||
1122 | struct ethtool_rx_flow_spec *fsp = | ||
1123 | (struct ethtool_rx_flow_spec *)&cmd->fs; | ||
1124 | struct igc_nfc_filter *input, *rule; | ||
1125 | int err = 0; | ||
1126 | |||
1127 | if (!(netdev->hw_features & NETIF_F_NTUPLE)) | ||
1128 | return -EOPNOTSUPP; | ||
1129 | |||
1130 | /* Don't allow programming if the action is a queue greater than | ||
1131 | * the number of online Rx queues. | ||
1132 | */ | ||
1133 | if (fsp->ring_cookie == RX_CLS_FLOW_DISC || | ||
1134 | fsp->ring_cookie >= adapter->num_rx_queues) { | ||
1135 | dev_err(&adapter->pdev->dev, "ethtool -N: The specified action is invalid\n"); | ||
1136 | return -EINVAL; | ||
1137 | } | ||
1138 | |||
1139 | /* Don't allow indexes to exist outside of available space */ | ||
1140 | if (fsp->location >= IGC_MAX_RXNFC_FILTERS) { | ||
1141 | dev_err(&adapter->pdev->dev, "Location out of range\n"); | ||
1142 | return -EINVAL; | ||
1143 | } | ||
1144 | |||
1145 | if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW) | ||
1146 | return -EINVAL; | ||
1147 | |||
1148 | input = kzalloc(sizeof(*input), GFP_KERNEL); | ||
1149 | if (!input) | ||
1150 | return -ENOMEM; | ||
1151 | |||
1152 | if (fsp->m_u.ether_spec.h_proto == ETHER_TYPE_FULL_MASK) { | ||
1153 | input->filter.etype = fsp->h_u.ether_spec.h_proto; | ||
1154 | input->filter.match_flags = IGC_FILTER_FLAG_ETHER_TYPE; | ||
1155 | } | ||
1156 | |||
1157 | /* Only support matching addresses by the full mask */ | ||
1158 | if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_source)) { | ||
1159 | input->filter.match_flags |= IGC_FILTER_FLAG_SRC_MAC_ADDR; | ||
1160 | ether_addr_copy(input->filter.src_addr, | ||
1161 | fsp->h_u.ether_spec.h_source); | ||
1162 | } | ||
1163 | |||
1164 | /* Only support matching addresses by the full mask */ | ||
1165 | if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_dest)) { | ||
1166 | input->filter.match_flags |= IGC_FILTER_FLAG_DST_MAC_ADDR; | ||
1167 | ether_addr_copy(input->filter.dst_addr, | ||
1168 | fsp->h_u.ether_spec.h_dest); | ||
1169 | } | ||
1170 | |||
1171 | if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) { | ||
1172 | if (fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK)) { | ||
1173 | err = -EINVAL; | ||
1174 | goto err_out; | ||
1175 | } | ||
1176 | input->filter.vlan_tci = fsp->h_ext.vlan_tci; | ||
1177 | input->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI; | ||
1178 | } | ||
1179 | |||
1180 | input->action = fsp->ring_cookie; | ||
1181 | input->sw_idx = fsp->location; | ||
1182 | |||
1183 | spin_lock(&adapter->nfc_lock); | ||
1184 | |||
1185 | hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) { | ||
1186 | if (!memcmp(&input->filter, &rule->filter, | ||
1187 | sizeof(input->filter))) { | ||
1188 | err = -EEXIST; | ||
1189 | dev_err(&adapter->pdev->dev, | ||
1190 | "ethtool: this filter is already set\n"); | ||
1191 | goto err_out_w_lock; | ||
1192 | } | ||
1193 | } | ||
1194 | |||
1195 | err = igc_add_filter(adapter, input); | ||
1196 | if (err) | ||
1197 | goto err_out_w_lock; | ||
1198 | |||
1199 | igc_update_ethtool_nfc_entry(adapter, input, input->sw_idx); | ||
1200 | |||
1201 | spin_unlock(&adapter->nfc_lock); | ||
1202 | return 0; | ||
1203 | |||
1204 | err_out_w_lock: | ||
1205 | spin_unlock(&adapter->nfc_lock); | ||
1206 | err_out: | ||
1207 | kfree(input); | ||
1208 | return err; | ||
1209 | } | ||
1210 | |||
1211 | static int igc_del_ethtool_nfc_entry(struct igc_adapter *adapter, | ||
1212 | struct ethtool_rxnfc *cmd) | ||
1213 | { | ||
1214 | struct ethtool_rx_flow_spec *fsp = | ||
1215 | (struct ethtool_rx_flow_spec *)&cmd->fs; | ||
1216 | int err; | ||
1217 | |||
1218 | spin_lock(&adapter->nfc_lock); | ||
1219 | err = igc_update_ethtool_nfc_entry(adapter, NULL, fsp->location); | ||
1220 | spin_unlock(&adapter->nfc_lock); | ||
1221 | |||
1222 | return err; | ||
1223 | } | ||
1224 | |||
1225 | static int igc_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) | ||
1226 | { | ||
1227 | struct igc_adapter *adapter = netdev_priv(dev); | ||
1228 | int ret = -EOPNOTSUPP; | ||
1229 | |||
1230 | switch (cmd->cmd) { | ||
1231 | case ETHTOOL_SRXFH: | ||
1232 | ret = igc_set_rss_hash_opt(adapter, cmd); | ||
1233 | break; | ||
1234 | case ETHTOOL_SRXCLSRLINS: | ||
1235 | ret = igc_add_ethtool_nfc_entry(adapter, cmd); | ||
1236 | break; | ||
1237 | case ETHTOOL_SRXCLSRLDEL: | ||
1238 | ret = igc_del_ethtool_nfc_entry(adapter, cmd); | ||
1239 | default: | ||
1240 | break; | ||
1241 | } | ||
1242 | |||
1243 | return ret; | ||
1244 | } | ||
1245 | |||
646 | void igc_write_rss_indir_tbl(struct igc_adapter *adapter) | 1246 | void igc_write_rss_indir_tbl(struct igc_adapter *adapter) |
647 | { | 1247 | { |
648 | struct igc_hw *hw = &adapter->hw; | 1248 | struct igc_hw *hw = &adapter->hw; |
@@ -1013,6 +1613,8 @@ static const struct ethtool_ops igc_ethtool_ops = { | |||
1013 | .set_pauseparam = igc_set_pauseparam, | 1613 | .set_pauseparam = igc_set_pauseparam, |
1014 | .get_coalesce = igc_get_coalesce, | 1614 | .get_coalesce = igc_get_coalesce, |
1015 | .set_coalesce = igc_set_coalesce, | 1615 | .set_coalesce = igc_set_coalesce, |
1616 | .get_rxnfc = igc_get_rxnfc, | ||
1617 | .set_rxnfc = igc_set_rxnfc, | ||
1016 | .get_rxfh_indir_size = igc_get_rxfh_indir_size, | 1618 | .get_rxfh_indir_size = igc_get_rxfh_indir_size, |
1017 | .get_rxfh = igc_get_rxfh, | 1619 | .get_rxfh = igc_get_rxfh, |
1018 | .set_rxfh = igc_set_rxfh, | 1620 | .set_rxfh = igc_set_rxfh, |