diff options
author | Sandeep Gopalpet <Sandeep.Kumar@freescale.com> | 2009-11-02 02:03:40 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-03 02:40:59 -0500 |
commit | 7a8b3372e29ff58ebdf94def26703afabd287f11 (patch) | |
tree | 7d4ae47bb2ddc7de481fb4da4a4f9f31e808e1bf /drivers/net/gianfar_ethtool.c | |
parent | 46ceb60ca80fa07703bc6eb8f4651f900dff5a82 (diff) |
gianfar: Basic Support for programming hash rules
This patch provides basic hash rules programming via the ethtool
interface.
Signed-off-by: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/gianfar_ethtool.c')
-rw-r--r-- | drivers/net/gianfar_ethtool.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 562f6c20f591..1010367695e4 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c | |||
@@ -645,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | |||
645 | } | 645 | } |
646 | #endif | 646 | #endif |
647 | 647 | ||
648 | static int gfar_ethflow_to_class(int flow_type, u64 *class) | ||
649 | { | ||
650 | switch (flow_type) { | ||
651 | case TCP_V4_FLOW: | ||
652 | *class = CLASS_CODE_TCP_IPV4; | ||
653 | break; | ||
654 | case UDP_V4_FLOW: | ||
655 | *class = CLASS_CODE_UDP_IPV4; | ||
656 | break; | ||
657 | case AH_V4_FLOW: | ||
658 | case ESP_V4_FLOW: | ||
659 | *class = CLASS_CODE_AH_ESP_IPV4; | ||
660 | break; | ||
661 | case SCTP_V4_FLOW: | ||
662 | *class = CLASS_CODE_SCTP_IPV4; | ||
663 | break; | ||
664 | case TCP_V6_FLOW: | ||
665 | *class = CLASS_CODE_TCP_IPV6; | ||
666 | break; | ||
667 | case UDP_V6_FLOW: | ||
668 | *class = CLASS_CODE_UDP_IPV6; | ||
669 | break; | ||
670 | case AH_V6_FLOW: | ||
671 | case ESP_V6_FLOW: | ||
672 | *class = CLASS_CODE_AH_ESP_IPV6; | ||
673 | break; | ||
674 | case SCTP_V6_FLOW: | ||
675 | *class = CLASS_CODE_SCTP_IPV6; | ||
676 | break; | ||
677 | default: | ||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | return 1; | ||
682 | } | ||
683 | |||
684 | static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) | ||
685 | { | ||
686 | u32 fcr = 0x0, fpr = FPR_FILER_MASK; | ||
687 | |||
688 | if (ethflow & RXH_L2DA) { | ||
689 | fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH | | ||
690 | RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; | ||
691 | ftp_rqfpr[priv->cur_filer_idx] = fpr; | ||
692 | ftp_rqfcr[priv->cur_filer_idx] = fcr; | ||
693 | gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); | ||
694 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
695 | |||
696 | fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH | | ||
697 | RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; | ||
698 | ftp_rqfpr[priv->cur_filer_idx] = fpr; | ||
699 | ftp_rqfcr[priv->cur_filer_idx] = fcr; | ||
700 | gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); | ||
701 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
702 | } | ||
703 | |||
704 | if (ethflow & RXH_VLAN) { | ||
705 | fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH | | ||
706 | RQFCR_AND | RQFCR_HASHTBL_0; | ||
707 | gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); | ||
708 | ftp_rqfpr[priv->cur_filer_idx] = fpr; | ||
709 | ftp_rqfcr[priv->cur_filer_idx] = fcr; | ||
710 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
711 | } | ||
712 | |||
713 | if (ethflow & RXH_IP_SRC) { | ||
714 | fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | | ||
715 | RQFCR_AND | RQFCR_HASHTBL_0; | ||
716 | ftp_rqfpr[priv->cur_filer_idx] = fpr; | ||
717 | ftp_rqfcr[priv->cur_filer_idx] = fcr; | ||
718 | gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); | ||
719 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
720 | } | ||
721 | |||
722 | if (ethflow & (RXH_IP_DST)) { | ||
723 | fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | | ||
724 | RQFCR_AND | RQFCR_HASHTBL_0; | ||
725 | ftp_rqfpr[priv->cur_filer_idx] = fpr; | ||
726 | ftp_rqfcr[priv->cur_filer_idx] = fcr; | ||
727 | gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); | ||
728 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
729 | } | ||
730 | |||
731 | if (ethflow & RXH_L3_PROTO) { | ||
732 | fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH | | ||
733 | RQFCR_AND | RQFCR_HASHTBL_0; | ||
734 | ftp_rqfpr[priv->cur_filer_idx] = fpr; | ||
735 | ftp_rqfcr[priv->cur_filer_idx] = fcr; | ||
736 | gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); | ||
737 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
738 | } | ||
739 | |||
740 | if (ethflow & RXH_L4_B_0_1) { | ||
741 | fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | | ||
742 | RQFCR_AND | RQFCR_HASHTBL_0; | ||
743 | ftp_rqfpr[priv->cur_filer_idx] = fpr; | ||
744 | ftp_rqfcr[priv->cur_filer_idx] = fcr; | ||
745 | gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); | ||
746 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
747 | } | ||
748 | |||
749 | if (ethflow & RXH_L4_B_2_3) { | ||
750 | fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | | ||
751 | RQFCR_AND | RQFCR_HASHTBL_0; | ||
752 | ftp_rqfpr[priv->cur_filer_idx] = fpr; | ||
753 | ftp_rqfcr[priv->cur_filer_idx] = fcr; | ||
754 | gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); | ||
755 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
756 | } | ||
757 | } | ||
758 | |||
759 | static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class) | ||
760 | { | ||
761 | unsigned int last_rule_idx = priv->cur_filer_idx; | ||
762 | unsigned int cmp_rqfpr; | ||
763 | unsigned int local_rqfpr[MAX_FILER_IDX + 1]; | ||
764 | unsigned int local_rqfcr[MAX_FILER_IDX + 1]; | ||
765 | int i = 0x0, k = 0x0; | ||
766 | int j = MAX_FILER_IDX, l = 0x0; | ||
767 | |||
768 | switch (class) { | ||
769 | case TCP_V4_FLOW: | ||
770 | cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP; | ||
771 | break; | ||
772 | case UDP_V4_FLOW: | ||
773 | cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP; | ||
774 | break; | ||
775 | case TCP_V6_FLOW: | ||
776 | cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP; | ||
777 | break; | ||
778 | case UDP_V6_FLOW: | ||
779 | cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP; | ||
780 | break; | ||
781 | case IPV4_FLOW: | ||
782 | cmp_rqfpr = RQFPR_IPV4; | ||
783 | case IPV6_FLOW: | ||
784 | cmp_rqfpr = RQFPR_IPV6; | ||
785 | break; | ||
786 | default: | ||
787 | printk(KERN_ERR "Right now this class is not supported\n"); | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | for (i = 0; i < MAX_FILER_IDX + 1; i++) { | ||
792 | local_rqfpr[j] = ftp_rqfpr[i]; | ||
793 | local_rqfcr[j] = ftp_rqfcr[i]; | ||
794 | j--; | ||
795 | if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE | | ||
796 | RQFCR_CLE |RQFCR_AND)) && | ||
797 | (ftp_rqfpr[i] == cmp_rqfpr)) | ||
798 | break; | ||
799 | } | ||
800 | |||
801 | if (i == MAX_FILER_IDX + 1) { | ||
802 | printk(KERN_ERR "No parse rule found, "); | ||
803 | printk(KERN_ERR "can't create hash rules\n"); | ||
804 | return 0; | ||
805 | } | ||
806 | |||
807 | /* If a match was found, then it begins the starting of a cluster rule | ||
808 | * if it was already programmed, we need to overwrite these rules | ||
809 | */ | ||
810 | for (l = i+1; l < MAX_FILER_IDX; l++) { | ||
811 | if ((ftp_rqfcr[l] & RQFCR_CLE) && | ||
812 | !(ftp_rqfcr[l] & RQFCR_AND)) { | ||
813 | ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT | | ||
814 | RQFCR_HASHTBL_0 | RQFCR_PID_MASK; | ||
815 | ftp_rqfpr[l] = FPR_FILER_MASK; | ||
816 | gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]); | ||
817 | break; | ||
818 | } | ||
819 | |||
820 | if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND)) | ||
821 | continue; | ||
822 | else { | ||
823 | local_rqfpr[j] = ftp_rqfpr[l]; | ||
824 | local_rqfcr[j] = ftp_rqfcr[l]; | ||
825 | j--; | ||
826 | } | ||
827 | } | ||
828 | |||
829 | priv->cur_filer_idx = l - 1; | ||
830 | last_rule_idx = l; | ||
831 | |||
832 | /* hash rules */ | ||
833 | ethflow_to_filer_rules(priv, ethflow); | ||
834 | |||
835 | /* Write back the popped out rules again */ | ||
836 | for (k = j+1; k < MAX_FILER_IDX; k++) { | ||
837 | ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k]; | ||
838 | ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k]; | ||
839 | gfar_write_filer(priv, priv->cur_filer_idx, | ||
840 | local_rqfcr[k], local_rqfpr[k]); | ||
841 | if (!priv->cur_filer_idx) | ||
842 | break; | ||
843 | priv->cur_filer_idx = priv->cur_filer_idx - 1; | ||
844 | } | ||
845 | |||
846 | return 1; | ||
847 | } | ||
848 | |||
849 | static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd) | ||
850 | { | ||
851 | u64 class; | ||
852 | |||
853 | if (!gfar_ethflow_to_class(cmd->flow_type, &class)) | ||
854 | return -EINVAL; | ||
855 | |||
856 | if (class < CLASS_CODE_USER_PROG1 || | ||
857 | class > CLASS_CODE_SCTP_IPV6) | ||
858 | return -EINVAL; | ||
859 | |||
860 | /* write the filer rules here */ | ||
861 | if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type)) | ||
862 | return -1; | ||
863 | |||
864 | return 0; | ||
865 | } | ||
866 | |||
867 | static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd) | ||
868 | { | ||
869 | struct gfar_private *priv = netdev_priv(dev); | ||
870 | int ret = 0; | ||
871 | |||
872 | switch(cmd->cmd) { | ||
873 | case ETHTOOL_SRXFH: | ||
874 | ret = gfar_set_hash_opts(priv, cmd); | ||
875 | break; | ||
876 | default: | ||
877 | ret = -EINVAL; | ||
878 | } | ||
879 | |||
880 | return ret; | ||
881 | } | ||
882 | |||
648 | const struct ethtool_ops gfar_ethtool_ops = { | 883 | const struct ethtool_ops gfar_ethtool_ops = { |
649 | .get_settings = gfar_gsettings, | 884 | .get_settings = gfar_gsettings, |
650 | .set_settings = gfar_ssettings, | 885 | .set_settings = gfar_ssettings, |
@@ -670,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = { | |||
670 | .get_wol = gfar_get_wol, | 905 | .get_wol = gfar_get_wol, |
671 | .set_wol = gfar_set_wol, | 906 | .set_wol = gfar_set_wol, |
672 | #endif | 907 | #endif |
908 | .set_rxnfc = gfar_set_nfc, | ||
673 | }; | 909 | }; |