aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
diff options
context:
space:
mode:
authorHadar Hen Zion <hadarh@mellanox.co.il>2012-07-05 00:03:49 -0400
committerDavid S. Miller <davem@davemloft.net>2012-07-07 19:23:06 -0400
commit820672812f8284143f933da8ccc60e296230d25d (patch)
treead14607c7c82cee5478398c7c5498df1018d1a90 /drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
parent592e49dda8122ab621cdc59cc429bdb968ee6364 (diff)
net/mlx4_en: Manage flow steering rules with ethtool
Implement the ethtool APIs for attaching L2/L3/L4 based flow steering rules to the netdevice RX rings. Added set_rxnfc callback and enhanced the existing get_rxnfc callback. Signed-off-by: Hadar Hen Zion <hadarh@mellanox.co.il> Signed-off-by: Amir Vadai <amirv@mellanox.com> Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlx4/en_ethtool.c')
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index 72901ce2b088..3e72a2076fb5 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -38,6 +38,10 @@
38#include "mlx4_en.h" 38#include "mlx4_en.h"
39#include "en_port.h" 39#include "en_port.h"
40 40
41#define EN_ETHTOOL_QP_ATTACH (1ull << 63)
42#define EN_ETHTOOL_MAC_MASK 0xffffffffffffULL
43#define EN_ETHTOOL_SHORT_MASK cpu_to_be16(0xffff)
44#define EN_ETHTOOL_WORD_MASK cpu_to_be32(0xffffffff)
41 45
42static void 46static void
43mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) 47mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo)
@@ -599,16 +603,369 @@ static int mlx4_en_set_rxfh_indir(struct net_device *dev,
599 return err; 603 return err;
600} 604}
601 605
606#define all_zeros_or_all_ones(field) \
607 ((field) == 0 || (field) == (__force typeof(field))-1)
608
609static int mlx4_en_validate_flow(struct net_device *dev,
610 struct ethtool_rxnfc *cmd)
611{
612 struct ethtool_usrip4_spec *l3_mask;
613 struct ethtool_tcpip4_spec *l4_mask;
614 struct ethhdr *eth_mask;
615 u64 full_mac = ~0ull;
616 u64 zero_mac = 0;
617
618 if (cmd->fs.location >= MAX_NUM_OF_FS_RULES)
619 return -EINVAL;
620
621 switch (cmd->fs.flow_type & ~FLOW_EXT) {
622 case TCP_V4_FLOW:
623 case UDP_V4_FLOW:
624 if (cmd->fs.m_u.tcp_ip4_spec.tos)
625 return -EINVAL;
626 l4_mask = &cmd->fs.m_u.tcp_ip4_spec;
627 /* don't allow mask which isn't all 0 or 1 */
628 if (!all_zeros_or_all_ones(l4_mask->ip4src) ||
629 !all_zeros_or_all_ones(l4_mask->ip4dst) ||
630 !all_zeros_or_all_ones(l4_mask->psrc) ||
631 !all_zeros_or_all_ones(l4_mask->pdst))
632 return -EINVAL;
633 break;
634 case IP_USER_FLOW:
635 l3_mask = &cmd->fs.m_u.usr_ip4_spec;
636 if (l3_mask->l4_4_bytes || l3_mask->tos || l3_mask->proto ||
637 cmd->fs.h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4 ||
638 (!l3_mask->ip4src && !l3_mask->ip4dst) ||
639 !all_zeros_or_all_ones(l3_mask->ip4src) ||
640 !all_zeros_or_all_ones(l3_mask->ip4dst))
641 return -EINVAL;
642 break;
643 case ETHER_FLOW:
644 eth_mask = &cmd->fs.m_u.ether_spec;
645 /* source mac mask must not be set */
646 if (memcmp(eth_mask->h_source, &zero_mac, ETH_ALEN))
647 return -EINVAL;
648
649 /* dest mac mask must be ff:ff:ff:ff:ff:ff */
650 if (memcmp(eth_mask->h_dest, &full_mac, ETH_ALEN))
651 return -EINVAL;
652
653 if (!all_zeros_or_all_ones(eth_mask->h_proto))
654 return -EINVAL;
655 break;
656 default:
657 return -EINVAL;
658 }
659
660 if ((cmd->fs.flow_type & FLOW_EXT)) {
661 if (cmd->fs.m_ext.vlan_etype ||
662 !(cmd->fs.m_ext.vlan_tci == 0 ||
663 cmd->fs.m_ext.vlan_tci == cpu_to_be16(0xfff)))
664 return -EINVAL;
665 }
666
667 return 0;
668}
669
670static int add_ip_rule(struct mlx4_en_priv *priv,
671 struct ethtool_rxnfc *cmd,
672 struct list_head *list_h)
673{
674 struct mlx4_spec_list *spec_l3;
675 struct ethtool_usrip4_spec *l3_mask = &cmd->fs.m_u.usr_ip4_spec;
676
677 spec_l3 = kzalloc(sizeof *spec_l3, GFP_KERNEL);
678 if (!spec_l3) {
679 en_err(priv, "Fail to alloc ethtool rule.\n");
680 return -ENOMEM;
681 }
682
683 spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4;
684 spec_l3->ipv4.src_ip = cmd->fs.h_u.usr_ip4_spec.ip4src;
685 if (l3_mask->ip4src)
686 spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK;
687 spec_l3->ipv4.dst_ip = cmd->fs.h_u.usr_ip4_spec.ip4dst;
688 if (l3_mask->ip4dst)
689 spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK;
690 list_add_tail(&spec_l3->list, list_h);
691
692 return 0;
693}
694
695static int add_tcp_udp_rule(struct mlx4_en_priv *priv,
696 struct ethtool_rxnfc *cmd,
697 struct list_head *list_h, int proto)
698{
699 struct mlx4_spec_list *spec_l3;
700 struct mlx4_spec_list *spec_l4;
701 struct ethtool_tcpip4_spec *l4_mask = &cmd->fs.m_u.tcp_ip4_spec;
702
703 spec_l3 = kzalloc(sizeof *spec_l3, GFP_KERNEL);
704 spec_l4 = kzalloc(sizeof *spec_l4, GFP_KERNEL);
705 if (!spec_l4 || !spec_l3) {
706 en_err(priv, "Fail to alloc ethtool rule.\n");
707 kfree(spec_l3);
708 kfree(spec_l4);
709 return -ENOMEM;
710 }
711
712 spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4;
713
714 if (proto == TCP_V4_FLOW) {
715 spec_l4->id = MLX4_NET_TRANS_RULE_ID_TCP;
716 spec_l3->ipv4.src_ip = cmd->fs.h_u.tcp_ip4_spec.ip4src;
717 spec_l3->ipv4.dst_ip = cmd->fs.h_u.tcp_ip4_spec.ip4dst;
718 spec_l4->tcp_udp.src_port = cmd->fs.h_u.tcp_ip4_spec.psrc;
719 spec_l4->tcp_udp.dst_port = cmd->fs.h_u.tcp_ip4_spec.pdst;
720 } else {
721 spec_l4->id = MLX4_NET_TRANS_RULE_ID_UDP;
722 spec_l3->ipv4.src_ip = cmd->fs.h_u.udp_ip4_spec.ip4src;
723 spec_l3->ipv4.dst_ip = cmd->fs.h_u.udp_ip4_spec.ip4dst;
724 spec_l4->tcp_udp.src_port = cmd->fs.h_u.udp_ip4_spec.psrc;
725 spec_l4->tcp_udp.dst_port = cmd->fs.h_u.udp_ip4_spec.pdst;
726 }
727
728 if (l4_mask->ip4src)
729 spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK;
730 if (l4_mask->ip4dst)
731 spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK;
732
733 if (l4_mask->psrc)
734 spec_l4->tcp_udp.src_port_msk = EN_ETHTOOL_SHORT_MASK;
735 if (l4_mask->pdst)
736 spec_l4->tcp_udp.dst_port_msk = EN_ETHTOOL_SHORT_MASK;
737
738 list_add_tail(&spec_l3->list, list_h);
739 list_add_tail(&spec_l4->list, list_h);
740
741 return 0;
742}
743
744static int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev,
745 struct ethtool_rxnfc *cmd,
746 struct list_head *rule_list_h)
747{
748 int err;
749 u64 mac;
750 __be64 be_mac;
751 struct ethhdr *eth_spec;
752 struct mlx4_en_priv *priv = netdev_priv(dev);
753 struct mlx4_spec_list *spec_l2;
754 __be64 mac_msk = cpu_to_be64(EN_ETHTOOL_MAC_MASK << 16);
755
756 err = mlx4_en_validate_flow(dev, cmd);
757 if (err)
758 return err;
759
760 spec_l2 = kzalloc(sizeof *spec_l2, GFP_KERNEL);
761 if (!spec_l2)
762 return -ENOMEM;
763
764 mac = priv->mac & EN_ETHTOOL_MAC_MASK;
765 be_mac = cpu_to_be64(mac << 16);
766
767 spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH;
768 memcpy(spec_l2->eth.dst_mac_msk, &mac_msk, ETH_ALEN);
769 if ((cmd->fs.flow_type & ~FLOW_EXT) != ETHER_FLOW)
770 memcpy(spec_l2->eth.dst_mac, &be_mac, ETH_ALEN);
771
772 if ((cmd->fs.flow_type & FLOW_EXT) && cmd->fs.m_ext.vlan_tci) {
773 spec_l2->eth.vlan_id = cmd->fs.h_ext.vlan_tci;
774 spec_l2->eth.vlan_id_msk = cpu_to_be16(0xfff);
775 }
776
777 list_add_tail(&spec_l2->list, rule_list_h);
778
779 switch (cmd->fs.flow_type & ~FLOW_EXT) {
780 case ETHER_FLOW:
781 eth_spec = &cmd->fs.h_u.ether_spec;
782 memcpy(&spec_l2->eth.dst_mac, eth_spec->h_dest, ETH_ALEN);
783 spec_l2->eth.ether_type = eth_spec->h_proto;
784 if (eth_spec->h_proto)
785 spec_l2->eth.ether_type_enable = 1;
786 break;
787 case IP_USER_FLOW:
788 err = add_ip_rule(priv, cmd, rule_list_h);
789 break;
790 case TCP_V4_FLOW:
791 err = add_tcp_udp_rule(priv, cmd, rule_list_h, TCP_V4_FLOW);
792 break;
793 case UDP_V4_FLOW:
794 err = add_tcp_udp_rule(priv, cmd, rule_list_h, UDP_V4_FLOW);
795 break;
796 }
797
798 return err;
799}
800
801static int mlx4_en_flow_replace(struct net_device *dev,
802 struct ethtool_rxnfc *cmd)
803{
804 int err;
805 struct mlx4_en_priv *priv = netdev_priv(dev);
806 struct ethtool_flow_id *loc_rule;
807 struct mlx4_spec_list *spec, *tmp_spec;
808 u32 qpn;
809 u64 reg_id;
810
811 struct mlx4_net_trans_rule rule = {
812 .queue_mode = MLX4_NET_TRANS_Q_FIFO,
813 .exclusive = 0,
814 .allow_loopback = 1,
815 .promisc_mode = MLX4_FS_PROMISC_NONE,
816 };
817
818 rule.port = priv->port;
819 rule.priority = MLX4_DOMAIN_ETHTOOL | cmd->fs.location;
820 INIT_LIST_HEAD(&rule.list);
821
822 /* Allow direct QP attaches if the EN_ETHTOOL_QP_ATTACH flag is set */
823 if (cmd->fs.ring_cookie == RX_CLS_FLOW_DISC)
824 return -EINVAL;
825 else if (cmd->fs.ring_cookie & EN_ETHTOOL_QP_ATTACH) {
826 qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1);
827 } else {
828 if (cmd->fs.ring_cookie >= priv->rx_ring_num) {
829 en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist.\n",
830 cmd->fs.ring_cookie);
831 return -EINVAL;
832 }
833 qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn;
834 if (!qpn) {
835 en_warn(priv, "rxnfc: RX ring (%llu) is inactive.\n",
836 cmd->fs.ring_cookie);
837 return -EINVAL;
838 }
839 }
840 rule.qpn = qpn;
841 err = mlx4_en_ethtool_to_net_trans_rule(dev, cmd, &rule.list);
842 if (err)
843 goto out_free_list;
844
845 loc_rule = &priv->ethtool_rules[cmd->fs.location];
846 if (loc_rule->id) {
847 err = mlx4_flow_detach(priv->mdev->dev, loc_rule->id);
848 if (err) {
849 en_err(priv, "Fail to detach network rule at location %d. registration id = %llx\n",
850 cmd->fs.location, loc_rule->id);
851 goto out_free_list;
852 }
853 loc_rule->id = 0;
854 memset(&loc_rule->flow_spec, 0,
855 sizeof(struct ethtool_rx_flow_spec));
856 }
857 err = mlx4_flow_attach(priv->mdev->dev, &rule, &reg_id);
858 if (err) {
859 en_err(priv, "Fail to attach network rule at location %d.\n",
860 cmd->fs.location);
861 goto out_free_list;
862 }
863 loc_rule->id = reg_id;
864 memcpy(&loc_rule->flow_spec, &cmd->fs,
865 sizeof(struct ethtool_rx_flow_spec));
866
867out_free_list:
868 list_for_each_entry_safe(spec, tmp_spec, &rule.list, list) {
869 list_del(&spec->list);
870 kfree(spec);
871 }
872 return err;
873}
874
875static int mlx4_en_flow_detach(struct net_device *dev,
876 struct ethtool_rxnfc *cmd)
877{
878 int err = 0;
879 struct ethtool_flow_id *rule;
880 struct mlx4_en_priv *priv = netdev_priv(dev);
881
882 if (cmd->fs.location >= MAX_NUM_OF_FS_RULES)
883 return -EINVAL;
884
885 rule = &priv->ethtool_rules[cmd->fs.location];
886 if (!rule->id) {
887 err = -ENOENT;
888 goto out;
889 }
890
891 err = mlx4_flow_detach(priv->mdev->dev, rule->id);
892 if (err) {
893 en_err(priv, "Fail to detach network rule at location %d. registration id = 0x%llx\n",
894 cmd->fs.location, rule->id);
895 goto out;
896 }
897 rule->id = 0;
898 memset(&rule->flow_spec, 0, sizeof(struct ethtool_rx_flow_spec));
899out:
900 return err;
901
902}
903
904static int mlx4_en_get_flow(struct net_device *dev, struct ethtool_rxnfc *cmd,
905 int loc)
906{
907 int err = 0;
908 struct ethtool_flow_id *rule;
909 struct mlx4_en_priv *priv = netdev_priv(dev);
910
911 if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES)
912 return -EINVAL;
913
914 rule = &priv->ethtool_rules[loc];
915 if (rule->id)
916 memcpy(&cmd->fs, &rule->flow_spec,
917 sizeof(struct ethtool_rx_flow_spec));
918 else
919 err = -ENOENT;
920
921 return err;
922}
923
924static int mlx4_en_get_num_flows(struct mlx4_en_priv *priv)
925{
926
927 int i, res = 0;
928 for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) {
929 if (priv->ethtool_rules[i].id)
930 res++;
931 }
932 return res;
933
934}
935
602static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 936static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
603 u32 *rule_locs) 937 u32 *rule_locs)
604{ 938{
605 struct mlx4_en_priv *priv = netdev_priv(dev); 939 struct mlx4_en_priv *priv = netdev_priv(dev);
940 struct mlx4_en_dev *mdev = priv->mdev;
606 int err = 0; 941 int err = 0;
942 int i = 0, priority = 0;
943
944 if ((cmd->cmd == ETHTOOL_GRXCLSRLCNT ||
945 cmd->cmd == ETHTOOL_GRXCLSRULE ||
946 cmd->cmd == ETHTOOL_GRXCLSRLALL) &&
947 mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED)
948 return -EINVAL;
607 949
608 switch (cmd->cmd) { 950 switch (cmd->cmd) {
609 case ETHTOOL_GRXRINGS: 951 case ETHTOOL_GRXRINGS:
610 cmd->data = priv->rx_ring_num; 952 cmd->data = priv->rx_ring_num;
611 break; 953 break;
954 case ETHTOOL_GRXCLSRLCNT:
955 cmd->rule_cnt = mlx4_en_get_num_flows(priv);
956 break;
957 case ETHTOOL_GRXCLSRULE:
958 err = mlx4_en_get_flow(dev, cmd, cmd->fs.location);
959 break;
960 case ETHTOOL_GRXCLSRLALL:
961 while ((!err || err == -ENOENT) && priority < cmd->rule_cnt) {
962 err = mlx4_en_get_flow(dev, cmd, i);
963 if (!err)
964 rule_locs[priority++] = i;
965 i++;
966 }
967 err = 0;
968 break;
612 default: 969 default:
613 err = -EOPNOTSUPP; 970 err = -EOPNOTSUPP;
614 break; 971 break;
@@ -617,6 +974,30 @@ static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
617 return err; 974 return err;
618} 975}
619 976
977static int mlx4_en_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
978{
979 int err = 0;
980 struct mlx4_en_priv *priv = netdev_priv(dev);
981 struct mlx4_en_dev *mdev = priv->mdev;
982
983 if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED)
984 return -EINVAL;
985
986 switch (cmd->cmd) {
987 case ETHTOOL_SRXCLSRLINS:
988 err = mlx4_en_flow_replace(dev, cmd);
989 break;
990 case ETHTOOL_SRXCLSRLDEL:
991 err = mlx4_en_flow_detach(dev, cmd);
992 break;
993 default:
994 en_warn(priv, "Unsupported ethtool command. (%d)\n", cmd->cmd);
995 return -EINVAL;
996 }
997
998 return err;
999}
1000
620const struct ethtool_ops mlx4_en_ethtool_ops = { 1001const struct ethtool_ops mlx4_en_ethtool_ops = {
621 .get_drvinfo = mlx4_en_get_drvinfo, 1002 .get_drvinfo = mlx4_en_get_drvinfo,
622 .get_settings = mlx4_en_get_settings, 1003 .get_settings = mlx4_en_get_settings,
@@ -637,6 +1018,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
637 .get_ringparam = mlx4_en_get_ringparam, 1018 .get_ringparam = mlx4_en_get_ringparam,
638 .set_ringparam = mlx4_en_set_ringparam, 1019 .set_ringparam = mlx4_en_set_ringparam,
639 .get_rxnfc = mlx4_en_get_rxnfc, 1020 .get_rxnfc = mlx4_en_get_rxnfc,
1021 .set_rxnfc = mlx4_en_set_rxnfc,
640 .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size, 1022 .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size,
641 .get_rxfh_indir = mlx4_en_get_rxfh_indir, 1023 .get_rxfh_indir = mlx4_en_get_rxfh_indir,
642 .set_rxfh_indir = mlx4_en_set_rxfh_indir, 1024 .set_rxfh_indir = mlx4_en_set_rxfh_indir,