|
42 | 42 | #include <net/tc_act/tc_mirred.h>
|
43 | 43 | #include <net/tc_act/tc_vlan.h>
|
44 | 44 | #include <net/tc_act/tc_tunnel_key.h>
|
| 45 | +#include <net/tc_act/tc_pedit.h> |
45 | 46 | #include <net/vxlan.h>
|
46 | 47 | #include "en.h"
|
47 | 48 | #include "en_tc.h"
|
@@ -72,6 +73,8 @@ struct mlx5e_tc_flow {
|
72 | 73 |
|
73 | 74 | struct mlx5e_tc_flow_parse_attr {
|
74 | 75 | struct mlx5_flow_spec spec;
|
| 76 | + int num_mod_hdr_actions; |
| 77 | + void *mod_hdr_actions; |
75 | 78 | };
|
76 | 79 |
|
77 | 80 | enum {
|
@@ -675,6 +678,276 @@ static int parse_cls_flower(struct mlx5e_priv *priv,
|
675 | 678 | return err;
|
676 | 679 | }
|
677 | 680 |
|
| 681 | +struct pedit_headers { |
| 682 | + struct ethhdr eth; |
| 683 | + struct iphdr ip4; |
| 684 | + struct ipv6hdr ip6; |
| 685 | + struct tcphdr tcp; |
| 686 | + struct udphdr udp; |
| 687 | +}; |
| 688 | + |
| 689 | +static int pedit_header_offsets[] = { |
| 690 | + [TCA_PEDIT_KEY_EX_HDR_TYPE_ETH] = offsetof(struct pedit_headers, eth), |
| 691 | + [TCA_PEDIT_KEY_EX_HDR_TYPE_IP4] = offsetof(struct pedit_headers, ip4), |
| 692 | + [TCA_PEDIT_KEY_EX_HDR_TYPE_IP6] = offsetof(struct pedit_headers, ip6), |
| 693 | + [TCA_PEDIT_KEY_EX_HDR_TYPE_TCP] = offsetof(struct pedit_headers, tcp), |
| 694 | + [TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = offsetof(struct pedit_headers, udp), |
| 695 | +}; |
| 696 | + |
| 697 | +#define pedit_header(_ph, _htype) ((void *)(_ph) + pedit_header_offsets[_htype]) |
| 698 | + |
| 699 | +static int set_pedit_val(u8 hdr_type, u32 mask, u32 val, u32 offset, |
| 700 | + struct pedit_headers *masks, |
| 701 | + struct pedit_headers *vals) |
| 702 | +{ |
| 703 | + u32 *curr_pmask, *curr_pval; |
| 704 | + |
| 705 | + if (hdr_type >= __PEDIT_HDR_TYPE_MAX) |
| 706 | + goto out_err; |
| 707 | + |
| 708 | + curr_pmask = (u32 *)(pedit_header(masks, hdr_type) + offset); |
| 709 | + curr_pval = (u32 *)(pedit_header(vals, hdr_type) + offset); |
| 710 | + |
| 711 | + if (*curr_pmask & mask) /* disallow acting twice on the same location */ |
| 712 | + goto out_err; |
| 713 | + |
| 714 | + *curr_pmask |= mask; |
| 715 | + *curr_pval |= (val & mask); |
| 716 | + |
| 717 | + return 0; |
| 718 | + |
| 719 | +out_err: |
| 720 | + return -EOPNOTSUPP; |
| 721 | +} |
| 722 | + |
| 723 | +struct mlx5_fields { |
| 724 | + u8 field; |
| 725 | + u8 size; |
| 726 | + u32 offset; |
| 727 | +}; |
| 728 | + |
| 729 | +static struct mlx5_fields fields[] = { |
| 730 | + {MLX5_ACTION_IN_FIELD_OUT_DMAC_47_16, 4, offsetof(struct pedit_headers, eth.h_dest[0])}, |
| 731 | + {MLX5_ACTION_IN_FIELD_OUT_DMAC_15_0, 2, offsetof(struct pedit_headers, eth.h_dest[4])}, |
| 732 | + {MLX5_ACTION_IN_FIELD_OUT_SMAC_47_16, 4, offsetof(struct pedit_headers, eth.h_source[0])}, |
| 733 | + {MLX5_ACTION_IN_FIELD_OUT_SMAC_15_0, 2, offsetof(struct pedit_headers, eth.h_source[4])}, |
| 734 | + {MLX5_ACTION_IN_FIELD_OUT_ETHERTYPE, 2, offsetof(struct pedit_headers, eth.h_proto)}, |
| 735 | + |
| 736 | + {MLX5_ACTION_IN_FIELD_OUT_IP_DSCP, 1, offsetof(struct pedit_headers, ip4.tos)}, |
| 737 | + {MLX5_ACTION_IN_FIELD_OUT_IP_TTL, 1, offsetof(struct pedit_headers, ip4.ttl)}, |
| 738 | + {MLX5_ACTION_IN_FIELD_OUT_SIPV4, 4, offsetof(struct pedit_headers, ip4.saddr)}, |
| 739 | + {MLX5_ACTION_IN_FIELD_OUT_DIPV4, 4, offsetof(struct pedit_headers, ip4.daddr)}, |
| 740 | + |
| 741 | + {MLX5_ACTION_IN_FIELD_OUT_SIPV6_127_96, 4, offsetof(struct pedit_headers, ip6.saddr.s6_addr32[0])}, |
| 742 | + {MLX5_ACTION_IN_FIELD_OUT_SIPV6_95_64, 4, offsetof(struct pedit_headers, ip6.saddr.s6_addr32[1])}, |
| 743 | + {MLX5_ACTION_IN_FIELD_OUT_SIPV6_63_32, 4, offsetof(struct pedit_headers, ip6.saddr.s6_addr32[2])}, |
| 744 | + {MLX5_ACTION_IN_FIELD_OUT_SIPV6_31_0, 4, offsetof(struct pedit_headers, ip6.saddr.s6_addr32[3])}, |
| 745 | + {MLX5_ACTION_IN_FIELD_OUT_DIPV6_127_96, 4, offsetof(struct pedit_headers, ip6.daddr.s6_addr32[0])}, |
| 746 | + {MLX5_ACTION_IN_FIELD_OUT_DIPV6_95_64, 4, offsetof(struct pedit_headers, ip6.daddr.s6_addr32[1])}, |
| 747 | + {MLX5_ACTION_IN_FIELD_OUT_DIPV6_63_32, 4, offsetof(struct pedit_headers, ip6.daddr.s6_addr32[2])}, |
| 748 | + {MLX5_ACTION_IN_FIELD_OUT_DIPV6_31_0, 4, offsetof(struct pedit_headers, ip6.daddr.s6_addr32[3])}, |
| 749 | + |
| 750 | + {MLX5_ACTION_IN_FIELD_OUT_TCP_SPORT, 2, offsetof(struct pedit_headers, tcp.source)}, |
| 751 | + {MLX5_ACTION_IN_FIELD_OUT_TCP_DPORT, 2, offsetof(struct pedit_headers, tcp.dest)}, |
| 752 | + {MLX5_ACTION_IN_FIELD_OUT_TCP_FLAGS, 1, offsetof(struct pedit_headers, tcp.ack_seq) + 5}, |
| 753 | + |
| 754 | + {MLX5_ACTION_IN_FIELD_OUT_UDP_SPORT, 2, offsetof(struct pedit_headers, udp.source)}, |
| 755 | + {MLX5_ACTION_IN_FIELD_OUT_UDP_DPORT, 2, offsetof(struct pedit_headers, udp.dest)}, |
| 756 | +}; |
| 757 | + |
| 758 | +/* On input attr->num_mod_hdr_actions tells how many HW actions can be parsed at |
| 759 | + * max from the SW pedit action. On success, it says how many HW actions were |
| 760 | + * actually parsed. |
| 761 | + */ |
| 762 | +static int offload_pedit_fields(struct pedit_headers *masks, |
| 763 | + struct pedit_headers *vals, |
| 764 | + struct mlx5e_tc_flow_parse_attr *parse_attr) |
| 765 | +{ |
| 766 | + struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals; |
| 767 | + int i, action_size, nactions, max_actions, first, last; |
| 768 | + void *s_masks_p, *a_masks_p, *vals_p; |
| 769 | + u32 s_mask, a_mask, val; |
| 770 | + struct mlx5_fields *f; |
| 771 | + u8 cmd, field_bsize; |
| 772 | + unsigned long mask; |
| 773 | + void *action; |
| 774 | + |
| 775 | + set_masks = &masks[TCA_PEDIT_KEY_EX_CMD_SET]; |
| 776 | + add_masks = &masks[TCA_PEDIT_KEY_EX_CMD_ADD]; |
| 777 | + set_vals = &vals[TCA_PEDIT_KEY_EX_CMD_SET]; |
| 778 | + add_vals = &vals[TCA_PEDIT_KEY_EX_CMD_ADD]; |
| 779 | + |
| 780 | + action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto); |
| 781 | + action = parse_attr->mod_hdr_actions; |
| 782 | + max_actions = parse_attr->num_mod_hdr_actions; |
| 783 | + nactions = 0; |
| 784 | + |
| 785 | + for (i = 0; i < ARRAY_SIZE(fields); i++) { |
| 786 | + f = &fields[i]; |
| 787 | + /* avoid seeing bits set from previous iterations */ |
| 788 | + s_mask = a_mask = mask = val = 0; |
| 789 | + |
| 790 | + s_masks_p = (void *)set_masks + f->offset; |
| 791 | + a_masks_p = (void *)add_masks + f->offset; |
| 792 | + |
| 793 | + memcpy(&s_mask, s_masks_p, f->size); |
| 794 | + memcpy(&a_mask, a_masks_p, f->size); |
| 795 | + |
| 796 | + if (!s_mask && !a_mask) /* nothing to offload here */ |
| 797 | + continue; |
| 798 | + |
| 799 | + if (s_mask && a_mask) { |
| 800 | + printk(KERN_WARNING "mlx5: can't set and add to the same HW field (%x)\n", f->field); |
| 801 | + return -EOPNOTSUPP; |
| 802 | + } |
| 803 | + |
| 804 | + if (nactions == max_actions) { |
| 805 | + printk(KERN_WARNING "mlx5: parsed %d pedit actions, can't do more\n", nactions); |
| 806 | + return -EOPNOTSUPP; |
| 807 | + } |
| 808 | + |
| 809 | + if (s_mask) { |
| 810 | + cmd = MLX5_ACTION_TYPE_SET; |
| 811 | + mask = s_mask; |
| 812 | + vals_p = (void *)set_vals + f->offset; |
| 813 | + /* clear to denote we consumed this field */ |
| 814 | + memset(s_masks_p, 0, f->size); |
| 815 | + } else { |
| 816 | + cmd = MLX5_ACTION_TYPE_ADD; |
| 817 | + mask = a_mask; |
| 818 | + vals_p = (void *)add_vals + f->offset; |
| 819 | + /* clear to denote we consumed this field */ |
| 820 | + memset(a_masks_p, 0, f->size); |
| 821 | + } |
| 822 | + |
| 823 | + memcpy(&val, vals_p, f->size); |
| 824 | + |
| 825 | + field_bsize = f->size * BITS_PER_BYTE; |
| 826 | + first = find_first_bit(&mask, field_bsize); |
| 827 | + last = find_last_bit(&mask, field_bsize); |
| 828 | + if (first > 0 || last != (field_bsize - 1)) { |
| 829 | + printk(KERN_WARNING "mlx5: partial rewrite (mask %lx) is currently not offloaded\n", |
| 830 | + mask); |
| 831 | + return -EOPNOTSUPP; |
| 832 | + } |
| 833 | + |
| 834 | + MLX5_SET(set_action_in, action, action_type, cmd); |
| 835 | + MLX5_SET(set_action_in, action, field, f->field); |
| 836 | + |
| 837 | + if (cmd == MLX5_ACTION_TYPE_SET) { |
| 838 | + MLX5_SET(set_action_in, action, offset, 0); |
| 839 | + /* length is num of bits to be written, zero means length of 32 */ |
| 840 | + MLX5_SET(set_action_in, action, length, field_bsize); |
| 841 | + } |
| 842 | + |
| 843 | + if (field_bsize == 32) |
| 844 | + MLX5_SET(set_action_in, action, data, ntohl(val)); |
| 845 | + else if (field_bsize == 16) |
| 846 | + MLX5_SET(set_action_in, action, data, ntohs(val)); |
| 847 | + else if (field_bsize == 8) |
| 848 | + MLX5_SET(set_action_in, action, data, val); |
| 849 | + |
| 850 | + action += action_size; |
| 851 | + nactions++; |
| 852 | + } |
| 853 | + |
| 854 | + parse_attr->num_mod_hdr_actions = nactions; |
| 855 | + return 0; |
| 856 | +} |
| 857 | + |
| 858 | +static int alloc_mod_hdr_actions(struct mlx5e_priv *priv, |
| 859 | + const struct tc_action *a, int namespace, |
| 860 | + struct mlx5e_tc_flow_parse_attr *parse_attr) |
| 861 | +{ |
| 862 | + int nkeys, action_size, max_actions; |
| 863 | + |
| 864 | + nkeys = tcf_pedit_nkeys(a); |
| 865 | + action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto); |
| 866 | + |
| 867 | + if (namespace == MLX5_FLOW_NAMESPACE_FDB) /* FDB offloading */ |
| 868 | + max_actions = MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, max_modify_header_actions); |
| 869 | + else /* namespace is MLX5_FLOW_NAMESPACE_KERNEL - NIC offloading */ |
| 870 | + max_actions = MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, max_modify_header_actions); |
| 871 | + |
| 872 | + /* can get up to crazingly 16 HW actions in 32 bits pedit SW key */ |
| 873 | + max_actions = min(max_actions, nkeys * 16); |
| 874 | + |
| 875 | + parse_attr->mod_hdr_actions = kcalloc(max_actions, action_size, GFP_KERNEL); |
| 876 | + if (!parse_attr->mod_hdr_actions) |
| 877 | + return -ENOMEM; |
| 878 | + |
| 879 | + parse_attr->num_mod_hdr_actions = max_actions; |
| 880 | + return 0; |
| 881 | +} |
| 882 | + |
| 883 | +static const struct pedit_headers zero_masks = {}; |
| 884 | + |
| 885 | +static int parse_tc_pedit_action(struct mlx5e_priv *priv, |
| 886 | + const struct tc_action *a, int namespace, |
| 887 | + struct mlx5e_tc_flow_parse_attr *parse_attr) |
| 888 | +{ |
| 889 | + struct pedit_headers masks[__PEDIT_CMD_MAX], vals[__PEDIT_CMD_MAX], *cmd_masks; |
| 890 | + int nkeys, i, err = -EOPNOTSUPP; |
| 891 | + u32 mask, val, offset; |
| 892 | + u8 cmd, htype; |
| 893 | + |
| 894 | + nkeys = tcf_pedit_nkeys(a); |
| 895 | + |
| 896 | + memset(masks, 0, sizeof(struct pedit_headers) * __PEDIT_CMD_MAX); |
| 897 | + memset(vals, 0, sizeof(struct pedit_headers) * __PEDIT_CMD_MAX); |
| 898 | + |
| 899 | + for (i = 0; i < nkeys; i++) { |
| 900 | + htype = tcf_pedit_htype(a, i); |
| 901 | + cmd = tcf_pedit_cmd(a, i); |
| 902 | + err = -EOPNOTSUPP; /* can't be all optimistic */ |
| 903 | + |
| 904 | + if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) { |
| 905 | + printk(KERN_WARNING "mlx5: legacy pedit isn't offloaded\n"); |
| 906 | + goto out_err; |
| 907 | + } |
| 908 | + |
| 909 | + if (cmd != TCA_PEDIT_KEY_EX_CMD_SET && cmd != TCA_PEDIT_KEY_EX_CMD_ADD) { |
| 910 | + printk(KERN_WARNING "mlx5: pedit cmd %d isn't offloaded\n", cmd); |
| 911 | + goto out_err; |
| 912 | + } |
| 913 | + |
| 914 | + mask = tcf_pedit_mask(a, i); |
| 915 | + val = tcf_pedit_val(a, i); |
| 916 | + offset = tcf_pedit_offset(a, i); |
| 917 | + |
| 918 | + err = set_pedit_val(htype, ~mask, val, offset, &masks[cmd], &vals[cmd]); |
| 919 | + if (err) |
| 920 | + goto out_err; |
| 921 | + } |
| 922 | + |
| 923 | + err = alloc_mod_hdr_actions(priv, a, namespace, parse_attr); |
| 924 | + if (err) |
| 925 | + goto out_err; |
| 926 | + |
| 927 | + err = offload_pedit_fields(masks, vals, parse_attr); |
| 928 | + if (err < 0) |
| 929 | + goto out_dealloc_parsed_actions; |
| 930 | + |
| 931 | + for (cmd = 0; cmd < __PEDIT_CMD_MAX; cmd++) { |
| 932 | + cmd_masks = &masks[cmd]; |
| 933 | + if (memcmp(cmd_masks, &zero_masks, sizeof(zero_masks))) { |
| 934 | + printk(KERN_WARNING "mlx5: attempt to offload an unsupported field (cmd %d)\n", |
| 935 | + cmd); |
| 936 | + print_hex_dump(KERN_WARNING, "mask: ", DUMP_PREFIX_ADDRESS, |
| 937 | + 16, 1, cmd_masks, sizeof(zero_masks), true); |
| 938 | + err = -EOPNOTSUPP; |
| 939 | + goto out_dealloc_parsed_actions; |
| 940 | + } |
| 941 | + } |
| 942 | + |
| 943 | + return 0; |
| 944 | + |
| 945 | +out_dealloc_parsed_actions: |
| 946 | + kfree(parse_attr->mod_hdr_actions); |
| 947 | +out_err: |
| 948 | + return err; |
| 949 | +} |
| 950 | + |
678 | 951 | static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
|
679 | 952 | struct mlx5e_tc_flow_parse_attr *parse_attr,
|
680 | 953 | struct mlx5e_tc_flow *flow)
|
|
0 commit comments