/* * Copyright (c) 2019, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "mlx5dv_dr.h" #include "dr_ste.h" enum dr_action_domain { DR_ACTION_DOMAIN_NIC_INGRESS, DR_ACTION_DOMAIN_NIC_EGRESS, DR_ACTION_DOMAIN_FDB_INGRESS, DR_ACTION_DOMAIN_FDB_EGRESS, DR_ACTION_DOMAIN_MAX, }; enum dr_action_valid_state { DR_ACTION_STATE_ERR, DR_ACTION_STATE_NO_ACTION, DR_ACTION_STATE_ENCAP, DR_ACTION_STATE_DECAP, DR_ACTION_STATE_MODIFY_HDR, DR_ACTION_STATE_POP_VLAN, DR_ACTION_STATE_PUSH_VLAN, DR_ACTION_STATE_NON_TERM, DR_ACTION_STATE_TERM, DR_ACTION_STATE_ASO, DR_ACTION_STATE_MAX, }; static const enum dr_action_valid_state next_action_state[DR_ACTION_DOMAIN_MAX] [DR_ACTION_STATE_MAX] [DR_ACTION_TYP_MAX] = { [DR_ACTION_DOMAIN_NIC_INGRESS] = { [DR_ACTION_STATE_NO_ACTION] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TNL_L2_TO_L2] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_TNL_L3_TO_L2] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_DECAP] = { [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_MODIFY_HDR] = { [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_POP_VLAN] = { [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_PUSH_VLAN] = { [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_NON_TERM] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TNL_L2_TO_L2] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_TNL_L3_TO_L2] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_ASO] = { [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_ENCAP] = { [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_TERM] = { [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_TERM, }, }, [DR_ACTION_DOMAIN_NIC_EGRESS] = { [DR_ACTION_STATE_NO_ACTION] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_ENCAP] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_MODIFY_HDR] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_POP_VLAN] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_PUSH_VLAN] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_NON_TERM] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_ASO] = { [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_TERM] = { [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_TERM, }, }, [DR_ACTION_DOMAIN_FDB_INGRESS] = { [DR_ACTION_STATE_NO_ACTION] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TNL_L2_TO_L2] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_TNL_L3_TO_L2] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_DECAP] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_ENCAP] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_MODIFY_HDR] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_POP_VLAN] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_PUSH_VLAN] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, }, [DR_ACTION_STATE_NON_TERM] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TNL_L2_TO_L2] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_TNL_L3_TO_L2] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_ASO] = { [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_TERM] = { [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_TERM, }, }, [DR_ACTION_DOMAIN_FDB_EGRESS] = { [DR_ACTION_STATE_NO_ACTION] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_ENCAP] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_MODIFY_HDR] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_POP_VLAN] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_PUSH_VLAN] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_NON_TERM] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_METER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_MISS] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_ASO] = { [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_L2_TO_TNL_L3] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_DEST_ARRAY] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_FIRST_HIT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ASO_CT] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_ROOT_FT] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_TERM] = { [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_TERM, }, }, }; static enum mlx5dv_flow_action_packet_reformat_type dr_action_type_to_reformat_enum(enum dr_action_type action_type) { switch (action_type) { case DR_ACTION_TYP_TNL_L2_TO_L2: return MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TUNNEL_TO_L2; case DR_ACTION_TYP_L2_TO_TNL_L2: return MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL; case DR_ACTION_TYP_TNL_L3_TO_L2: return MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L3_TUNNEL_TO_L2; case DR_ACTION_TYP_L2_TO_TNL_L3: return MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L3_TUNNEL; default: assert(false); return 0; } } static enum dr_action_type dr_action_reformat_to_action_type(enum mlx5dv_flow_action_packet_reformat_type type) { switch (type) { case MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TUNNEL_TO_L2: return DR_ACTION_TYP_TNL_L2_TO_L2; case MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL: return DR_ACTION_TYP_L2_TO_TNL_L2; case MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L3_TUNNEL_TO_L2: return DR_ACTION_TYP_TNL_L3_TO_L2; case MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L3_TUNNEL: return DR_ACTION_TYP_L2_TO_TNL_L3; default: assert(false); return 0; } } /* Apply the actions on the rule STE array starting from the last_ste. * Actions might require more than one STE, new_num_stes will return * the new size of the STEs array, rule with actions. */ static void dr_actions_apply(struct mlx5dv_dr_domain *dmn, enum dr_domain_nic_type nic_type, uint8_t *action_type_set, uint8_t *last_ste, struct dr_ste_actions_attr *attr, uint32_t *new_num_stes) { struct dr_ste_ctx *ste_ctx = dmn->ste_ctx; uint32_t added_stes = 0; if (nic_type == DR_DOMAIN_NIC_TYPE_RX) dr_ste_set_actions_rx(ste_ctx, action_type_set, last_ste, attr, &added_stes); else dr_ste_set_actions_tx(ste_ctx, action_type_set, last_ste, attr, &added_stes); *new_num_stes += added_stes; } static enum dr_action_domain dr_action_get_action_domain(enum mlx5dv_dr_domain_type domain, enum dr_domain_nic_type nic_type) { if (domain == MLX5DV_DR_DOMAIN_TYPE_NIC_RX) { return DR_ACTION_DOMAIN_NIC_INGRESS; } else if (domain == MLX5DV_DR_DOMAIN_TYPE_NIC_TX) { return DR_ACTION_DOMAIN_NIC_EGRESS; } else { /* FDB domain */ if (nic_type == DR_DOMAIN_NIC_TYPE_RX) return DR_ACTION_DOMAIN_FDB_INGRESS; else return DR_ACTION_DOMAIN_FDB_EGRESS; } } static int dr_action_validate_and_get_next_state(enum dr_action_domain action_domain, uint32_t action_type, uint32_t *state) { uint32_t cur_state = *state; /* Check action state machine is valid */ *state = next_action_state[action_domain][cur_state][action_type]; if (*state == DR_ACTION_STATE_ERR) { errno = EOPNOTSUPP; return errno; } return 0; } static int dr_action_send_modify_header_args(struct mlx5dv_dr_action *action, uint8_t send_ring_idx) { int ret; if (!(action->rewrite.args_send_qp & (1 << send_ring_idx))) { ret = dr_send_postsend_args(action->rewrite.dmn, dr_arg_get_object_id(action->rewrite.ptrn_arg.arg), action->rewrite.param.num_of_actions, action->rewrite.param.data, send_ring_idx); if (ret) { dr_dbg(action->rewrite.dmn, "Failed writing args object\n"); return ret; } action->rewrite.args_send_qp |= 1 << send_ring_idx; } return 0; } #define WITH_VLAN_NUM_HW_ACTIONS 6 int dr_actions_build_ste_arr(struct mlx5dv_dr_matcher *matcher, struct dr_matcher_rx_tx *nic_matcher, struct mlx5dv_dr_action *actions[], uint32_t num_actions, uint8_t *ste_arr, uint32_t *new_hw_ste_arr_sz, struct cross_dmn_params *cross_dmn_p, uint8_t send_ring_idx) { struct dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn; bool rx_rule = nic_dmn->type == DR_DOMAIN_NIC_TYPE_RX; struct mlx5dv_dr_action *cross_dmn_action = NULL; struct mlx5dv_dr_domain *dmn = matcher->tbl->dmn; uint8_t action_type_set[DR_ACTION_TYP_MAX] = {}; uint32_t state = DR_ACTION_STATE_NO_ACTION; struct dr_ste_actions_attr attr = {}; enum dr_action_domain action_domain; uint8_t *last_ste; int i; attr.dmn = dmn; attr.gvmi = dmn->info.caps.gvmi; attr.hit_gvmi = dmn->info.caps.gvmi; attr.final_icm_addr = nic_dmn->default_icm_addr; action_domain = dr_action_get_action_domain(dmn->type, nic_dmn->type); attr.aso_ste_loc = -1; for (i = 0; i < num_actions; i++) { struct mlx5dv_dr_action *action; int max_actions_type = 1; uint32_t action_type; action = actions[i]; action_type = action->action_type; switch (action_type) { case DR_ACTION_TYP_DROP: attr.final_icm_addr = nic_dmn->drop_icm_addr; attr.hit_gvmi = nic_dmn->drop_icm_addr >> 48; break; case DR_ACTION_TYP_FT: { struct mlx5dv_dr_table *dest_tbl = action->dest_tbl; if (dest_tbl->dmn != dmn) { dr_dbg(dmn, "Destination table belongs to a different domain\n"); goto out_invalid_arg; } if (dest_tbl->level <= matcher->tbl->level) dr_dbg(dmn, "Destination table level not higher than source\n"); attr.final_icm_addr = rx_rule ? dr_icm_pool_get_chunk_icm_addr(dest_tbl->rx.s_anchor->chunk) : dr_icm_pool_get_chunk_icm_addr(dest_tbl->tx.s_anchor->chunk); break; } case DR_ACTION_TYP_ROOT_FT: if (action->root_tbl.tbl->dmn != dmn) { dr_dbg(dmn, "Destination anchor belongs to a different domain\n"); goto out_invalid_arg; } attr.final_icm_addr = rx_rule ? action->root_tbl.rx_icm_addr : action->root_tbl.tx_icm_addr; break; case DR_ACTION_TYP_QP: if (action->dest_qp.is_qp) attr.final_icm_addr = to_mqp(action->dest_qp.qp)->tir_icm_addr; else attr.final_icm_addr = action->dest_qp.devx_tir->rx_icm_addr; if (!attr.final_icm_addr) { dr_dbg(dmn, "Unsupported TIR/QP for action\n"); goto out_invalid_arg; } break; case DR_ACTION_TYP_CTR: attr.ctr_id = action->ctr.devx_obj->object_id + action->ctr.offset; break; case DR_ACTION_TYP_ASO_CT: if (dmn != action->aso.dmn) { if (!action->aso.devx_obj->priv) { dr_dbg(dmn, "ASO CT devx priv object is not initialized\n"); goto out_invalid_arg; } struct dr_aso_cross_dmn_arrays *cross_dmn_arrays = (struct dr_aso_cross_dmn_arrays *) action->aso.devx_obj->priv; if (atomic_fetch_add(&cross_dmn_arrays->rule_htbl[action->aso.offset]->ste_arr->refcount, 1) > 1) { dr_dbg(dmn, "ASO CT cross GVMI action is in use by another rule\n"); atomic_fetch_sub(&cross_dmn_arrays->rule_htbl[action->aso.offset]->ste_arr->refcount, 1); errno = EBUSY; goto out_errno; } dr_ste_get(cross_dmn_arrays->action_htbl[action->aso.offset]->ste_arr); cross_dmn_p->cross_dmn_action = action; cross_dmn_action = action; } attr.aso = &action->aso; break; case DR_ACTION_TYP_ASO_FLOW_METER: case DR_ACTION_TYP_ASO_FIRST_HIT: if (dmn->ctx != action->aso.devx_obj->context) { dr_dbg(dmn, "ASO belongs to a different IB ctx\n"); goto out_invalid_arg; } attr.aso = &action->aso; break; case DR_ACTION_TYP_TAG: attr.flow_tag = action->flow_tag; break; case DR_ACTION_TYP_MISS: case DR_ACTION_TYP_TNL_L2_TO_L2: break; case DR_ACTION_TYP_TNL_L3_TO_L2: if (action->rewrite.is_root_level) { dr_dbg(dmn, "Root decap L3 action cannot be used on current table\n"); goto out_invalid_arg; } if (action->rewrite.ptrn_arg.ptrn && action->rewrite.ptrn_arg.arg) { attr.decap_index = dr_arg_get_object_id(action->rewrite.ptrn_arg.arg); attr.decap_actions = action->rewrite.ptrn_arg.ptrn->rewrite_param.num_of_actions; attr.decap_pat_idx = action->rewrite.ptrn_arg.ptrn->rewrite_param.index; if (dmn->info.use_mqs) { if (dr_action_send_modify_header_args(action, send_ring_idx)) goto out_errno; } } else { attr.decap_index = action->rewrite.param.index; attr.decap_actions = action->rewrite.param.num_of_actions; attr.decap_with_vlan = attr.decap_actions == WITH_VLAN_NUM_HW_ACTIONS; attr.decap_pat_idx = DR_INVALID_PATTERN_INDEX; } break; case DR_ACTION_TYP_MODIFY_HDR: if (action->rewrite.is_root_level) { dr_dbg(dmn, "Root modify header action cannot be used on current table\n"); goto out_invalid_arg; } if (action->rewrite.single_action_opt) { attr.modify_actions = action->rewrite.param.num_of_actions; attr.single_modify_action = action->rewrite.param.data; } else { if (action->rewrite.ptrn_arg.ptrn && action->rewrite.ptrn_arg.arg) { attr.modify_index = dr_arg_get_object_id(action->rewrite.ptrn_arg.arg); attr.modify_pat_idx = action->rewrite.ptrn_arg.ptrn->rewrite_param.index; attr.modify_actions = action->rewrite.ptrn_arg.ptrn->rewrite_param. num_of_actions; if (dmn->info.use_mqs) { if (dr_action_send_modify_header_args(action, send_ring_idx)) goto out_errno; } } else { attr.modify_actions = action->rewrite.param.num_of_actions; attr.modify_index = action->rewrite.param.index; attr.modify_pat_idx = DR_INVALID_PATTERN_INDEX; } } break; case DR_ACTION_TYP_L2_TO_TNL_L2: case DR_ACTION_TYP_L2_TO_TNL_L3: if (action->reformat.is_root_level) { dr_dbg(dmn, "Root encap action cannot be used on current table\n"); goto out_invalid_arg; } if (rx_rule && !(dmn->ste_ctx->actions_caps & DR_STE_CTX_ACTION_CAP_RX_ENCAP)) { dr_dbg(dmn, "Device doesn't support Encap on RX\n"); goto out_invalid_arg; } attr.reformat_size = action->reformat.reformat_size; attr.reformat_id = dr_actions_reformat_get_id(action); attr.prio_tag_required = dmn->info.caps.prio_tag_required; break; case DR_ACTION_TYP_METER: if (action->meter.next_ft->dmn != dmn) { dr_dbg(dmn, "Next table belongs to a different domain\n"); goto out_invalid_arg; } if (action->meter.next_ft->level <= matcher->tbl->level) { dr_dbg(dmn, "Next table level should he higher than source table\n"); goto out_invalid_arg; } attr.final_icm_addr = rx_rule ? action->meter.rx_icm_addr : action->meter.tx_icm_addr; break; case DR_ACTION_TYP_SAMPLER: if (action->sampler.dmn != dmn) { dr_dbg(dmn, "Sampler belongs to a different domain\n"); goto out_invalid_arg; } if (action->sampler.sampler_default->next_ft->level <= matcher->tbl->level) { dr_dbg(dmn, "Sampler next table level should he higher than source table\n"); goto out_invalid_arg; } if (rx_rule) { attr.final_icm_addr = action->sampler.sampler_default->rx_icm_addr; } else { attr.final_icm_addr = (action->sampler.sampler_restore) ? action->sampler.sampler_restore->tx_icm_addr : action->sampler.sampler_default->tx_icm_addr; } break; case DR_ACTION_TYP_VPORT: if (action->vport.dmn != dmn) { dr_dbg(dmn, "Destination vport belongs to a different domain\n"); goto out_invalid_arg; } if (unlikely(rx_rule && action->vport.caps->num == WIRE_PORT)) { if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_RX) { dr_dbg(dmn, "Forwarding to uplink vport on RX is not allowed\n"); goto out_invalid_arg; } /* silently drop the packets for RX side of FDB */ attr.final_icm_addr = nic_dmn->drop_icm_addr; attr.hit_gvmi = nic_dmn->drop_icm_addr >> 48; } else { attr.hit_gvmi = action->vport.caps->vhca_gvmi; attr.final_icm_addr = rx_rule ? action->vport.caps->icm_address_rx : action->vport.caps->icm_address_tx; } break; case DR_ACTION_TYP_DEST_ARRAY: if (action->dest_array.dmn != dmn) { dr_dbg(dmn, "Destination array belongs to a different domain\n"); goto out_invalid_arg; } attr.final_icm_addr = rx_rule ? action->dest_array.rx_icm_addr : action->dest_array.tx_icm_addr; break; case DR_ACTION_TYP_POP_VLAN: if (!rx_rule && !(dmn->ste_ctx->actions_caps & DR_STE_CTX_ACTION_CAP_TX_POP)) { dr_dbg(dmn, "Device doesn't support POP VLAN action on TX\n"); goto out_invalid_arg; } max_actions_type = MAX_VLANS; attr.vlans.count_pop++; break; case DR_ACTION_TYP_PUSH_VLAN: if (rx_rule && !(dmn->ste_ctx->actions_caps & DR_STE_CTX_ACTION_CAP_RX_PUSH)) { dr_dbg(dmn, "Device doesn't support PUSH VLAN action on RX\n"); goto out_invalid_arg; } max_actions_type = MAX_VLANS; if (attr.vlans.count_push == MAX_VLANS) { errno = ENOTSUP; return ENOTSUP; } attr.vlans.headers[attr.vlans.count_push++] = action->push_vlan.vlan_hdr; break; default: goto out_invalid_arg; } /* Check action duplication */ if (++action_type_set[action_type] > max_actions_type) { dr_dbg(dmn, "Action type %d supports only max %d time(s)\n", action_type, max_actions_type); goto out_invalid_arg; } /* Check action state machine is valid */ if (dr_action_validate_and_get_next_state(action_domain, action_type, &state)) { dr_dbg(dmn, "Invalid action sequence provided\n"); goto out_errno; } } *new_hw_ste_arr_sz = nic_matcher->num_of_builders; last_ste = ste_arr + DR_STE_SIZE * (nic_matcher->num_of_builders - 1); dr_actions_apply(dmn, nic_dmn->type, action_type_set, last_ste, &attr, new_hw_ste_arr_sz); if (attr.aso_ste_loc != -1) cross_dmn_p->cross_dmn_loc = attr.aso_ste_loc; return 0; out_invalid_arg: errno = EINVAL; out_errno: if (cross_dmn_action) { struct dr_aso_cross_dmn_arrays *cross_dmn_arrays = (struct dr_aso_cross_dmn_arrays *) cross_dmn_action->aso.devx_obj->priv; atomic_fetch_sub(&cross_dmn_arrays->rule_htbl[cross_dmn_action->aso.offset]->ste_arr->refcount, 1); atomic_fetch_sub(&cross_dmn_arrays->action_htbl[cross_dmn_action->aso.offset]->ste_arr->refcount, 1); } return errno; } int dr_actions_build_attr(struct mlx5dv_dr_matcher *matcher, struct mlx5dv_dr_action *actions[], size_t num_actions, struct mlx5dv_flow_action_attr *attr, struct mlx5_flow_action_attr_aux *attr_aux) { struct mlx5dv_dr_domain *dmn = matcher->tbl->dmn; int i; for (i = 0; i < num_actions; i++) { switch (actions[i]->action_type) { case DR_ACTION_TYP_FT: if (actions[i]->dest_tbl->dmn != dmn) { dr_dbg(dmn, "Destination table belongs to a different domain\n"); errno = EINVAL; return errno; } attr[i].type = MLX5DV_FLOW_ACTION_DEST_DEVX; attr[i].obj = actions[i]->dest_tbl->devx_obj; break; case DR_ACTION_TYP_DEST_ARRAY: if (actions[i]->dest_array.dmn != dmn) { dr_dbg(dmn, "Destination array belongs to a different domain\n"); errno = EINVAL; return errno; } attr[i].type = MLX5DV_FLOW_ACTION_DEST_DEVX; attr[i].obj = actions[i]->dest_array.devx_tbl->ft_dvo; break; case DR_ACTION_TYP_TNL_L2_TO_L2: case DR_ACTION_TYP_L2_TO_TNL_L2: case DR_ACTION_TYP_TNL_L3_TO_L2: case DR_ACTION_TYP_L2_TO_TNL_L3: attr[i].type = MLX5DV_FLOW_ACTION_IBV_FLOW_ACTION; attr[i].action = actions[i]->reformat.flow_action; break; case DR_ACTION_TYP_MODIFY_HDR: attr[i].type = MLX5DV_FLOW_ACTION_IBV_FLOW_ACTION; attr[i].action = actions[i]->rewrite.flow_action; break; case DR_ACTION_TYP_QP: if (actions[i]->dest_qp.is_qp) { attr[i].type = MLX5DV_FLOW_ACTION_DEST_IBV_QP; attr[i].qp = actions[i]->dest_qp.qp; } else { attr[i].type = MLX5DV_FLOW_ACTION_DEST_DEVX; attr[i].obj = actions[i]->dest_qp.devx_tir; } break; case DR_ACTION_TYP_CTR: attr[i].type = MLX5DV_FLOW_ACTION_COUNTERS_DEVX; attr[i].obj = actions[i]->ctr.devx_obj; if (actions[i]->ctr.offset) { attr_aux[i].type = MLX5_FLOW_ACTION_COUNTER_OFFSET; attr_aux[i].offset = actions[i]->ctr.offset; } break; case DR_ACTION_TYP_TAG: attr[i].type = MLX5DV_FLOW_ACTION_TAG; attr[i].tag_value = actions[i]->flow_tag; break; case DR_ACTION_TYP_MISS: attr[i].type = MLX5DV_FLOW_ACTION_DEFAULT_MISS; break; case DR_ACTION_TYP_DROP: attr[i].type = MLX5DV_FLOW_ACTION_DROP; break; default: dr_dbg(dmn, "Found unsupported action type: %d\n", actions[i]->action_type); errno = ENOTSUP; return errno; } } return 0; } static struct mlx5dv_dr_action * dr_action_create_generic(enum dr_action_type action_type) { struct mlx5dv_dr_action *action; action = calloc(1, sizeof(struct mlx5dv_dr_action)); if (!action) { errno = ENOMEM; return NULL; } action->action_type = action_type; atomic_init(&action->refcount, 1); return action; } struct mlx5dv_dr_action *mlx5dv_dr_action_create_drop(void) { return dr_action_create_generic(DR_ACTION_TYP_DROP); } struct mlx5dv_dr_action *mlx5dv_dr_action_create_default_miss(void) { return dr_action_create_generic(DR_ACTION_TYP_MISS); } struct mlx5dv_dr_action * mlx5dv_dr_action_create_dest_ibv_qp(struct ibv_qp *ibqp) { struct mlx5dv_dr_action *action; if (ibqp->qp_type != IBV_QPT_RAW_PACKET) { errno = EINVAL; return NULL; } action = dr_action_create_generic(DR_ACTION_TYP_QP); if (!action) return NULL; action->dest_qp.is_qp = true; action->dest_qp.qp = ibqp; return action; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_dest_devx_tir(struct mlx5dv_devx_obj *devx_obj) { struct mlx5dv_dr_action *action; if (devx_obj->type != MLX5_DEVX_TIR) { errno = EINVAL; return NULL; } action = dr_action_create_generic(DR_ACTION_TYP_QP); if (!action) return NULL; action->dest_qp.devx_tir = devx_obj; return action; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_dest_table(struct mlx5dv_dr_table *tbl) { struct mlx5dv_dr_action *action; atomic_fetch_add(&tbl->refcount, 1); if (dr_is_root_table(tbl)) { dr_dbg(tbl->dmn, "Root table cannot be used as a destination\n"); errno = EINVAL; goto dec_ref; } action = dr_action_create_generic(DR_ACTION_TYP_FT); if (!action) goto dec_ref; action->dest_tbl = tbl; return action; dec_ref: atomic_fetch_sub(&tbl->refcount, 1); return NULL; } static int dr_action_create_dest_root_table(struct mlx5dv_dr_action *action) { struct mlx5dv_dr_domain *dmn = action->root_tbl.tbl->dmn; struct dr_devx_flow_dest_info dest_info = {}; struct dr_devx_flow_table_attr ft_attr = {}; struct dr_devx_flow_group_attr fg_attr = {}; struct dr_devx_flow_fte_attr fte_attr = {}; int ret; switch (dmn->type) { case MLX5DV_DR_DOMAIN_TYPE_FDB: ft_attr.type = FS_FT_FDB; break; case MLX5DV_DR_DOMAIN_TYPE_NIC_RX: ft_attr.type = FS_FT_NIC_RX; break; case MLX5DV_DR_DOMAIN_TYPE_NIC_TX: ft_attr.type = FS_FT_NIC_TX; break; default: errno = EOPNOTSUPP; return errno; } fte_attr.dest_arr = &dest_info; fte_attr.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; fte_attr.dest_arr[0].type = MLX5_FLOW_DEST_TYPE_FT; fte_attr.dest_arr[0].ft_id = action->root_tbl.sa->id; fte_attr.dest_size = 1; action->root_tbl.devx_tbl = dr_devx_create_always_hit_ft(dmn->ctx, &ft_attr, &fg_attr, &fte_attr); if (!action->root_tbl.devx_tbl) return errno; ret = dr_devx_query_flow_table(action->root_tbl.devx_tbl->ft_dvo, ft_attr.type, &action->root_tbl.rx_icm_addr, &action->root_tbl.tx_icm_addr); if (ret) goto destroy_devx_tbl; return 0; destroy_devx_tbl: dr_devx_destroy_always_hit_ft(action->root_tbl.devx_tbl); return errno; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_dest_root_table(struct mlx5dv_dr_table *tbl, uint16_t priority) { struct mlx5dv_steering_anchor_attr attr = {}; enum mlx5_ib_uapi_flow_table_type ft_type; struct mlx5dv_steering_anchor *sa; struct mlx5dv_dr_action *action; int err; if (!dr_is_root_table(tbl)) { dr_dbg(tbl->dmn, "Supported only on root flow tables\n"); errno = EINVAL; return NULL; } if (tbl->dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_RX) ft_type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_NIC_RX; else if (tbl->dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_TX) ft_type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_NIC_TX; else ft_type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_FDB; attr.priority = priority; attr.ft_type = ft_type; sa = mlx5dv_create_steering_anchor(tbl->dmn->ctx, &attr); if (!sa) return NULL; action = dr_action_create_generic(DR_ACTION_TYP_ROOT_FT); if (!action) goto free_steering_anchor; action->root_tbl.sa = sa; action->root_tbl.tbl = tbl; err = dr_action_create_dest_root_table(action); if (err) goto free_action; atomic_fetch_add(&tbl->refcount, 1); return action; free_action: free(action); free_steering_anchor: mlx5dv_destroy_steering_anchor(sa); return NULL; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_flow_counter(struct mlx5dv_devx_obj *devx_obj, uint32_t offset) { struct mlx5dv_dr_action *action; if (devx_obj->type != MLX5_DEVX_FLOW_COUNTER) { errno = EINVAL; return NULL; } action = dr_action_create_generic(DR_ACTION_TYP_CTR); if (!action) return NULL; action->ctr.devx_obj = devx_obj; action->ctr.offset = offset; return action; } static int dr_action_aso_first_hit_init(struct mlx5dv_dr_action *action, uint32_t offset, uint32_t flags, uint8_t return_reg_c) { if (!check_comp_mask(flags, MLX5DV_DR_ACTION_FLAGS_ASO_FIRST_HIT_SET)) { errno = EINVAL; return errno; } if ((offset / MLX5_ASO_FIRST_HIT_NUM_PER_OBJ) >= (1 << action->aso.devx_obj->log_obj_range)) { errno = EINVAL; return errno; } if ((return_reg_c > 5) || (return_reg_c % 2 == 0)) { errno = EINVAL; return errno; } action->aso.offset = offset; action->aso.first_hit.set = flags & MLX5DV_DR_ACTION_FLAGS_ASO_FIRST_HIT_SET; action->aso.dest_reg_id = return_reg_c; return 0; } static int dr_action_aso_flow_meter_init(struct mlx5dv_dr_action *action, uint32_t offset, uint32_t flags, uint8_t return_reg_c) { if (!flags || (flags > MLX5DV_DR_ACTION_FLAGS_ASO_FLOW_METER_UNDEFINED)) { errno = EINVAL; return errno; } if ((offset / MLX5_ASO_FLOW_METER_NUM_PER_OBJ) >= (1 << action->aso.devx_obj->log_obj_range)) { errno = EINVAL; return errno; } if ((return_reg_c > 5) || (return_reg_c % 2 == 0)) { errno = EINVAL; return errno; } switch (flags) { case MLX5DV_DR_ACTION_FLAGS_ASO_FLOW_METER_RED: action->aso.flow_meter.initial_color = MLX5_IFC_ASO_FLOW_METER_INITIAL_COLOR_RED; break; case MLX5DV_DR_ACTION_FLAGS_ASO_FLOW_METER_YELLOW: action->aso.flow_meter.initial_color = MLX5_IFC_ASO_FLOW_METER_INITIAL_COLOR_YELLOW; break; case MLX5DV_DR_ACTION_FLAGS_ASO_FLOW_METER_GREEN: action->aso.flow_meter.initial_color = MLX5_IFC_ASO_FLOW_METER_INITIAL_COLOR_GREEN; break; case MLX5DV_DR_ACTION_FLAGS_ASO_FLOW_METER_UNDEFINED: action->aso.flow_meter.initial_color = MLX5_IFC_ASO_FLOW_METER_INITIAL_COLOR_UNDEFINED; break; default: errno = EINVAL; return errno; } action->aso.offset = offset; action->aso.dest_reg_id = return_reg_c; return 0; } static int dr_action_aso_ct_init(struct mlx5dv_dr_action *action, uint32_t offset, uint32_t flags, uint8_t return_reg_c) { if (!flags || (flags > MLX5DV_DR_ACTION_FLAGS_ASO_CT_DIRECTION_RESPONDER)) goto err_invalid; if ((offset / MLX5_ASO_CT_NUM_PER_OBJ) >= (1 << action->aso.devx_obj->log_obj_range)) goto err_invalid; if ((return_reg_c > 5) || (return_reg_c % 2 == 0)) goto err_invalid; if (flags == MLX5DV_DR_ACTION_FLAGS_ASO_CT_DIRECTION_INITIATOR) action->aso.ct.direction = MLX5_IFC_ASO_CT_DIRECTION_INITIATOR; else action->aso.ct.direction = MLX5_IFC_ASO_CT_DIRECTION_RESPONDER; action->aso.offset = offset; action->aso.dest_reg_id = return_reg_c; return 0; err_invalid: errno = EINVAL; return errno; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_aso(struct mlx5dv_dr_domain *dmn, struct mlx5dv_devx_obj *devx_obj, uint32_t offset, uint32_t flags, uint8_t return_reg_c) { struct mlx5dv_dr_action *action = NULL; if (!dmn->info.supp_sw_steering || dmn->info.caps.sw_format_ver == MLX5_HW_CONNECTX_5) { errno = EOPNOTSUPP; return NULL; } if (devx_obj->type == MLX5_DEVX_ASO_FIRST_HIT) { action = dr_action_create_generic(DR_ACTION_TYP_ASO_FIRST_HIT); if (!action) return NULL; action->aso.devx_obj = devx_obj; if (dr_action_aso_first_hit_init(action, offset, flags, return_reg_c)) goto out_free; } else if (devx_obj->type == MLX5_DEVX_ASO_FLOW_METER) { action = dr_action_create_generic(DR_ACTION_TYP_ASO_FLOW_METER); if (!action) return NULL; action->aso.devx_obj = devx_obj; if (dr_action_aso_flow_meter_init(action, offset, flags, return_reg_c)) goto out_free; } else if (devx_obj->type == MLX5_DEVX_ASO_CT) { action = dr_action_create_generic(DR_ACTION_TYP_ASO_CT); if (!action) return NULL; action->aso.devx_obj = devx_obj; if (dr_action_aso_ct_init(action, offset, flags, return_reg_c)) goto out_free; } else { errno = EOPNOTSUPP; return NULL; } action->aso.dmn = dmn; return action; out_free: free(action); return NULL; } static int dr_action_aso_ct_modify(struct mlx5dv_dr_action *action, uint32_t offset, uint32_t flags, uint8_t return_reg_c) { if (action->aso.devx_obj->priv == NULL) return dr_action_aso_ct_init(action, offset, flags, return_reg_c); if (action->aso.dest_reg_id != return_reg_c) { dr_dbg(action->aso.dmn, "Invalid parameters for a cross gvmi action\n"); errno = EOPNOTSUPP; return errno; } if (flags > MLX5DV_DR_ACTION_FLAGS_ASO_CT_DIRECTION_RESPONDER) { errno = EOPNOTSUPP; return errno; } if ((flags == MLX5DV_DR_ACTION_FLAGS_ASO_CT_DIRECTION_INITIATOR && action->aso.ct.direction != MLX5_IFC_ASO_CT_DIRECTION_INITIATOR) || (flags == MLX5DV_DR_ACTION_FLAGS_ASO_CT_DIRECTION_RESPONDER && action->aso.ct.direction != MLX5_IFC_ASO_CT_DIRECTION_RESPONDER)) { errno = EOPNOTSUPP; return errno; } action->aso.offset = offset; return 0; } int mlx5dv_dr_action_modify_aso(struct mlx5dv_dr_action *action, uint32_t offset, uint32_t flags, uint8_t return_reg_c) { if (action->action_type == DR_ACTION_TYP_ASO_FIRST_HIT) return dr_action_aso_first_hit_init(action, offset, flags, return_reg_c); else if (action->action_type == DR_ACTION_TYP_ASO_FLOW_METER) return dr_action_aso_flow_meter_init(action, offset, flags, return_reg_c); else if (action->action_type == DR_ACTION_TYP_ASO_CT) return dr_action_aso_ct_modify(action, offset, flags, return_reg_c); errno = EINVAL; return errno; } struct mlx5dv_dr_action *mlx5dv_dr_action_create_tag(uint32_t tag_value) { struct mlx5dv_dr_action *action; action = dr_action_create_generic(DR_ACTION_TYP_TAG); if (!action) return NULL; action->flow_tag = tag_value & 0xffffff; return action; } static int dr_action_create_reformat_action_root(struct mlx5dv_dr_domain *dmn, size_t data_sz, void *data, struct mlx5dv_dr_action *action) { enum mlx5dv_flow_action_packet_reformat_type reformat_type; struct ibv_flow_action *flow_action; enum mlx5dv_flow_table_type type; if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_RX) type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_NIC_RX; else if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_TX) type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_NIC_TX; else type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_FDB; reformat_type = dr_action_type_to_reformat_enum(action->action_type); flow_action = mlx5dv_create_flow_action_packet_reformat(dmn->ctx, data_sz, data, reformat_type, type); if (!flow_action) return errno; action->reformat.flow_action = flow_action; return 0; } static int dr_action_verify_reformat_params(enum mlx5dv_flow_action_packet_reformat_type reformat_type, struct mlx5dv_dr_domain *dmn, size_t data_sz, void *data) { if ((!data && data_sz) || (data && !data_sz) || (dr_domain_is_support_sw_encap(dmn) && (data_sz > dmn->info.caps.max_encap_size)) || reformat_type > MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L3_TUNNEL) { dr_dbg(dmn, "Invalid reformat parameter!\n"); goto out_err; } if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_FDB) return 0; if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_RX) { if (reformat_type != MLX5_IB_UAPI_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TUNNEL_TO_L2 && reformat_type != MLX5_IB_UAPI_FLOW_ACTION_PACKET_REFORMAT_TYPE_L3_TUNNEL_TO_L2) { dr_dbg(dmn, "Action reformat type not support on RX domain\n"); goto out_err; } } else if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_TX) { if (reformat_type != MLX5_IB_UAPI_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL && reformat_type != MLX5_IB_UAPI_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L3_TUNNEL) { dr_dbg(dmn, "Action reformat type not support on TX domain\n"); goto out_err; } } return 0; out_err: errno = EINVAL; return errno; } static int dr_action_create_sw_reformat(struct mlx5dv_dr_domain *dmn, struct mlx5dv_dr_action *action, size_t data_sz, void *data) { uint8_t *reformat_data; int ret; reformat_data = calloc(1, data_sz); if (!reformat_data) { errno = ENOMEM; return errno; } memcpy(reformat_data, data, data_sz); action->reformat.data = reformat_data; action->reformat.reformat_size = data_sz; ret = dr_ste_alloc_encap(action); if (ret) goto free_reformat_data; return 0; free_reformat_data: free(reformat_data); action->reformat.data = NULL; return ret; } static void dr_action_destroy_sw_reformat(struct mlx5dv_dr_action *action) { dr_ste_free_encap(action); free(action->reformat.data); } static int dr_action_create_devx_reformat(struct mlx5dv_dr_domain *dmn, struct mlx5dv_dr_action *action, size_t data_sz, void *data) { struct mlx5dv_devx_obj *obj; enum reformat_type rt; if (action->action_type == DR_ACTION_TYP_L2_TO_TNL_L2) rt = MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL; else rt = MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL; obj = dr_devx_create_reformat_ctx(dmn->ctx, rt, data_sz, data); if (!obj) return errno; action->reformat.dvo = obj; action->reformat.reformat_size = data_sz; return 0; } static void dr_action_destroy_devx_reformat(struct mlx5dv_dr_action *action) { mlx5dv_devx_obj_destroy(action->reformat.dvo); } static int dr_action_create_reformat_action(struct mlx5dv_dr_domain *dmn, size_t data_sz, void *data, struct mlx5dv_dr_action *action) { uint8_t *hw_actions; switch (action->action_type) { case DR_ACTION_TYP_L2_TO_TNL_L2: case DR_ACTION_TYP_L2_TO_TNL_L3: { if (dr_domain_is_support_sw_encap(dmn) && !dr_action_create_sw_reformat(dmn, action, data_sz, data)) return 0; /* When failed creating sw encap, fallback to * use devx to try again. */ if (dr_action_create_devx_reformat(dmn, action, data_sz, data)) return errno; return 0; } case DR_ACTION_TYP_TNL_L2_TO_L2: { return 0; } case DR_ACTION_TYP_TNL_L3_TO_L2: { int ret; hw_actions = calloc(1, ACTION_CACHE_LINE_SIZE); if (!hw_actions) { errno = ENOMEM; return errno; } ret = dr_ste_set_action_decap_l3_list(dmn->ste_ctx, data, data_sz, hw_actions, ACTION_CACHE_LINE_SIZE, &action->rewrite.param.num_of_actions); if (ret) { dr_dbg(dmn, "Failed creating decap l3 action list\n"); goto free_hw_actions; } action->rewrite.param.data = hw_actions; action->rewrite.dmn = dmn; ret = dr_ste_alloc_modify_hdr(action); if (ret) { dr_dbg(dmn, "Failed prepare reformat data\n"); goto free_hw_actions; } return 0; } default: dr_dbg(dmn, "Reformat type is not supported %d\n", action->action_type); errno = ENOTSUP; return errno; } free_hw_actions: free(hw_actions); return errno; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_packet_reformat(struct mlx5dv_dr_domain *dmn, uint32_t flags, enum mlx5dv_flow_action_packet_reformat_type reformat_type, size_t data_sz, void *data) { struct mlx5dv_dr_action *action; enum dr_action_type action_type; int ret; atomic_fetch_add(&dmn->refcount, 1); if (!check_comp_mask(flags, MLX5DV_DR_ACTION_FLAGS_ROOT_LEVEL)) { errno = EINVAL; goto dec_ref; } if (!dmn->info.supp_sw_steering && !(flags & MLX5DV_DR_ACTION_FLAGS_ROOT_LEVEL)) { dr_dbg(dmn, "Only root actions are supported on current domain\n"); errno = EOPNOTSUPP; goto dec_ref; } /* General checks */ ret = dr_action_verify_reformat_params(reformat_type, dmn, data_sz, data); if (ret) goto dec_ref; action_type = dr_action_reformat_to_action_type(reformat_type); action = dr_action_create_generic(action_type); if (!action) goto dec_ref; action->reformat.dmn = dmn; /* Create the action according to the table type */ if (flags & MLX5DV_DR_ACTION_FLAGS_ROOT_LEVEL) { action->reformat.is_root_level = true; ret = dr_action_create_reformat_action_root(dmn, data_sz, data, action); } else { action->reformat.is_root_level = false; ret = dr_action_create_reformat_action(dmn, data_sz, data, action); } if (ret) { dr_dbg(dmn, "Failed creating reformat action %d\n", ret); goto free_action; } return action; free_action: free(action); dec_ref: atomic_fetch_sub(&dmn->refcount, 1); return NULL; } struct mlx5dv_dr_action *mlx5dv_dr_action_create_pop_vlan(void) { return dr_action_create_generic(DR_ACTION_TYP_POP_VLAN); } struct mlx5dv_dr_action *mlx5dv_dr_action_create_push_vlan(struct mlx5dv_dr_domain *dmn, __be32 vlan_hdr) { uint32_t vlan_hdr_h = be32toh(vlan_hdr); uint16_t ethertype = vlan_hdr_h >> 16; struct mlx5dv_dr_action *action; if (ethertype != SVLAN_ETHERTYPE && ethertype != CVLAN_ETHERTYPE) { dr_dbg(dmn, "Invalid vlan ethertype\n"); errno = EINVAL; return NULL; } action = dr_action_create_generic(DR_ACTION_TYP_PUSH_VLAN); if (!action) return NULL; action->push_vlan.vlan_hdr = vlan_hdr_h; return action; } uint32_t dr_actions_reformat_get_id(struct mlx5dv_dr_action *action) { if (action->reformat.chunk) return action->reformat.index; return action->reformat.dvo->object_id; } static int dr_action_modify_sw_to_hw_add(struct mlx5dv_dr_domain *dmn, __be64 *sw_action, __be64 *hw_action, const struct dr_ste_action_modify_field **ret_hw_info) { const struct dr_ste_action_modify_field *hw_action_info; uint8_t max_length; uint16_t sw_field; uint32_t data; /* Get SW modify action data */ sw_field = DEVX_GET(set_action_in, sw_action, field); data = DEVX_GET(set_action_in, sw_action, data); /* Convert SW data to HW modify action format */ hw_action_info = dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, &dmn->info.caps, sw_field); if (!hw_action_info) { dr_dbg(dmn, "Modify ADD action invalid field given\n"); errno = EINVAL; return errno; } max_length = hw_action_info->end - hw_action_info->start + 1; dr_ste_set_action_add(dmn->ste_ctx, hw_action, hw_action_info->hw_field, hw_action_info->start, max_length, data); *ret_hw_info = hw_action_info; return 0; } static int dr_action_modify_sw_to_hw_set(struct mlx5dv_dr_domain *dmn, __be64 *sw_action, __be64 *hw_action, const struct dr_ste_action_modify_field **ret_hw_info) { const struct dr_ste_action_modify_field *hw_action_info; uint8_t offset, length, max_length; uint16_t sw_field; uint32_t data; /* Get SW modify action data */ sw_field = DEVX_GET(set_action_in, sw_action, field); offset = DEVX_GET(set_action_in, sw_action, offset); length = DEVX_GET(set_action_in, sw_action, length); data = DEVX_GET(set_action_in, sw_action, data); /* Convert SW data to HW modify action format */ hw_action_info = dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, &dmn->info.caps, sw_field); if (!hw_action_info) { dr_dbg(dmn, "Modify SET action invalid field given\n"); errno = EINVAL; return errno; } /* Based on device specification value of 0 means 32 */ length = length ? length : 32; max_length = hw_action_info->end - hw_action_info->start + 1; if (length + offset > max_length) { dr_dbg(dmn, "Modify action length + offset exceeds limit\n"); errno = EINVAL; return errno; } dr_ste_set_action_set(dmn->ste_ctx, hw_action, hw_action_info->hw_field, hw_action_info->start + offset, length, data); *ret_hw_info = hw_action_info; return 0; } static int dr_action_modify_sw_to_hw_copy(struct mlx5dv_dr_domain *dmn, __be64 *sw_action, __be64 *hw_action, const struct dr_ste_action_modify_field **ret_dst_hw_info, const struct dr_ste_action_modify_field **ret_src_hw_info) { uint8_t src_offset, dst_offset, src_max_length, dst_max_length, length; const struct dr_ste_action_modify_field *src_hw_action_info; const struct dr_ste_action_modify_field *dst_hw_action_info; uint16_t src_field, dst_field; /* Get SW modify action data */ src_field = DEVX_GET(copy_action_in, sw_action, src_field); dst_field = DEVX_GET(copy_action_in, sw_action, dst_field); src_offset = DEVX_GET(copy_action_in, sw_action, src_offset); dst_offset = DEVX_GET(copy_action_in, sw_action, dst_offset); length = DEVX_GET(copy_action_in, sw_action, length); /* Convert SW data to HW modify action format */ src_hw_action_info = dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, &dmn->info.caps, src_field); dst_hw_action_info = dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, &dmn->info.caps, dst_field); if (!src_hw_action_info || !dst_hw_action_info) { dr_dbg(dmn, "Modify COPY action invalid src/dst field given\n"); errno = EINVAL; return errno; } /* Based on device specification value of 0 means 32 */ length = length ? length : 32; src_max_length = src_hw_action_info->end - src_hw_action_info->start + 1; dst_max_length = dst_hw_action_info->end - dst_hw_action_info->start + 1; if (length + src_offset > src_max_length || length + dst_offset > dst_max_length) { dr_dbg(dmn, "Modify action length exceeds limit\n"); errno = EINVAL; return errno; } dr_ste_set_action_copy(dmn->ste_ctx, hw_action, dst_hw_action_info->hw_field, dst_hw_action_info->start + dst_offset, length, src_hw_action_info->hw_field, src_hw_action_info->start + src_offset); *ret_dst_hw_info = dst_hw_action_info; *ret_src_hw_info = src_hw_action_info; return 0; } static int dr_action_modify_sw_to_hw(struct mlx5dv_dr_domain *dmn, __be64 *sw_action, __be64 *hw_action, const struct dr_ste_action_modify_field **ret_dst_hw_info, const struct dr_ste_action_modify_field **ret_src_hw_info) { uint8_t action = DEVX_GET(set_action_in, sw_action, action_type); int ret = 0; *hw_action = 0; *ret_src_hw_info = NULL; switch (action) { case MLX5_ACTION_TYPE_SET: ret = dr_action_modify_sw_to_hw_set(dmn, sw_action, hw_action, ret_dst_hw_info); break; case MLX5_ACTION_TYPE_ADD: ret = dr_action_modify_sw_to_hw_add(dmn, sw_action, hw_action, ret_dst_hw_info); break; case MLX5_ACTION_TYPE_COPY: ret = dr_action_modify_sw_to_hw_copy(dmn, sw_action, hw_action, ret_dst_hw_info, ret_src_hw_info); break; default: dr_dbg(dmn, "Unsupported action type %d for modify action\n", action); errno = EOPNOTSUPP; ret = errno; break; } return ret; } static int dr_action_modify_check_field_limitation_set(struct mlx5dv_dr_action *action, const __be64 *sw_action) { uint16_t sw_field = DEVX_GET(set_action_in, sw_action, field); struct mlx5dv_dr_domain *dmn = action->rewrite.dmn; if (sw_field == MLX5_ACTION_IN_FIELD_OUT_METADATA_REGA) { action->rewrite.allow_rx = false; if (dmn->type != MLX5DV_DR_DOMAIN_TYPE_NIC_TX) { dr_dbg(dmn, "Unsupported field %d for RX/FDB set action\n", sw_field); errno = EINVAL; return errno; } } else if (sw_field == MLX5_ACTION_IN_FIELD_OUT_METADATA_REGB) { action->rewrite.allow_tx = false; if (dmn->type != MLX5DV_DR_DOMAIN_TYPE_NIC_RX) { dr_dbg(dmn, "Unsupported field %d for TX/FDB set action\n", sw_field); errno = EINVAL; return errno; } } if (!action->rewrite.allow_rx && !action->rewrite.allow_tx) { dr_dbg(dmn, "Modify SET actions not supported on both RX and TX\n"); errno = EINVAL; return errno; } return 0; } static int dr_action_modify_check_field_limitation_add(struct mlx5dv_dr_action *action, const __be64 *sw_action) { uint16_t sw_field = DEVX_GET(add_action_in, sw_action, field); if (sw_field != MLX5_ACTION_IN_FIELD_OUT_IP_TTL && sw_field != MLX5_ACTION_IN_FIELD_OUT_IPV6_HOPLIMIT && sw_field != MLX5_ACTION_IN_FIELD_OUT_TCP_SEQ_NUM && sw_field != MLX5_ACTION_IN_FIELD_OUT_TCP_ACK_NUM) { dr_dbg(action->rewrite.dmn, "Unsupported field %d for ADD action\n", sw_field); errno = EINVAL; return errno; } return 0; } static int dr_action_modify_check_field_limitation_copy(struct mlx5dv_dr_action *action, const __be64 *sw_action) { struct mlx5dv_dr_domain *dmn = action->rewrite.dmn; uint16_t sw_fields[2]; int i; sw_fields[0] = DEVX_GET(copy_action_in, sw_action, src_field); sw_fields[1] = DEVX_GET(copy_action_in, sw_action, dst_field); for (i = 0; i < 2; i++) { if (sw_fields[i] == MLX5_ACTION_IN_FIELD_OUT_METADATA_REGA) { action->rewrite.allow_rx = false; if (dmn->type != MLX5DV_DR_DOMAIN_TYPE_NIC_TX) { dr_dbg(dmn, "Unsupported field %d for RX/FDB COPY action\n", sw_fields[i]); errno = EINVAL; return errno; } } else if (sw_fields[i] == MLX5_ACTION_IN_FIELD_OUT_METADATA_REGB) { action->rewrite.allow_tx = false; if (dmn->type != MLX5DV_DR_DOMAIN_TYPE_NIC_RX) { dr_dbg(dmn, "Unsupported field %d for TX/FDB COPY action\n", sw_fields[i]); errno = EINVAL; return errno; } } } if (!action->rewrite.allow_rx && !action->rewrite.allow_tx) { dr_dbg(dmn, "Modify actions combination is not supported on both RX and TX\n"); errno = EINVAL; return errno; } return 0; } static int dr_action_modify_check_field_limitation(struct mlx5dv_dr_action *action, const __be64 *sw_action) { uint8_t action_type = DEVX_GET(set_action_in, sw_action, action_type); struct mlx5dv_dr_domain *dmn = action->rewrite.dmn; int ret; switch (action_type) { case MLX5_ACTION_TYPE_SET: ret = dr_action_modify_check_field_limitation_set(action, sw_action); break; case MLX5_ACTION_TYPE_ADD: ret = dr_action_modify_check_field_limitation_add(action, sw_action); break; case MLX5_ACTION_TYPE_COPY: ret = dr_action_modify_check_field_limitation_copy(action, sw_action); break; default: dr_dbg(dmn, "Unsupported modify action %d\n", action_type); errno = EOPNOTSUPP; ret = errno; break; } return ret; } static int dr_actions_convert_modify_header(struct mlx5dv_dr_action *action, uint32_t max_hw_actions, uint32_t num_sw_actions, __be64 sw_actions[], __be64 hw_actions[], uint32_t *num_hw_actions) { const struct dr_ste_action_modify_field *hw_dst_action_info; const struct dr_ste_action_modify_field *hw_src_action_info; struct mlx5dv_dr_domain *dmn = action->rewrite.dmn; int ret, i, hw_idx = 0; uint16_t hw_field = 0; uint32_t l3_type = 0; uint32_t l4_type = 0; __be64 *sw_action; __be64 hw_action; action->rewrite.allow_rx = true; action->rewrite.allow_tx = true; for (i = 0; i < num_sw_actions; i++) { sw_action = &sw_actions[i]; ret = dr_action_modify_check_field_limitation(action, sw_action); if (ret) return ret; /* Convert SW action to HW action */ ret = dr_action_modify_sw_to_hw(dmn, sw_action, &hw_action, &hw_dst_action_info, &hw_src_action_info); if (ret) return ret; /* Due to a HW limitation we cannot modify 2 different L3 types */ if (l3_type && hw_dst_action_info->l3_type && (hw_dst_action_info->l3_type != l3_type)) { dr_dbg(dmn, "Action list can't support two different L3 types\n"); errno = ENOTSUP; return errno; } if (hw_dst_action_info->l3_type) l3_type = hw_dst_action_info->l3_type; /* Due to a HW limitation we cannot modify two different L4 types */ if (l4_type && hw_dst_action_info->l4_type && (hw_dst_action_info->l4_type != l4_type)) { dr_dbg(dmn, "Action list can't support two different L4 types\n"); errno = EINVAL; return errno; } if (hw_dst_action_info->l4_type) l4_type = hw_dst_action_info->l4_type; /* HW reads and executes two actions at once this means we * need to create a gap if two actions access the same field */ if ((hw_idx % 2) && (hw_field == hw_dst_action_info->hw_field || (hw_src_action_info && hw_field == hw_src_action_info->hw_field))) { /* Check if after gap insertion the total number of HW * modify actions doesn't exceeds the limit */ hw_idx++; if ((num_sw_actions + hw_idx - i) >= max_hw_actions) { dr_dbg(dmn, "Modify header action number exceeds HW limit\n"); errno = EINVAL; return errno; } } hw_field = hw_dst_action_info->hw_field; hw_actions[hw_idx] = hw_action; hw_idx++; } *num_hw_actions = hw_idx; return 0; } static int dr_action_create_modify_action_root(struct mlx5dv_dr_domain *dmn, size_t actions_sz, __be64 actions[], struct mlx5dv_dr_action *action) { struct ibv_flow_action *flow_action; enum mlx5dv_flow_table_type type; if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_RX) type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_NIC_RX; else if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_NIC_TX) type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_NIC_TX; else type = MLX5_IB_UAPI_FLOW_TABLE_TYPE_FDB; flow_action = mlx5dv_create_flow_action_modify_header(dmn->ctx, actions_sz, (__force uint64_t *)actions, type); if (!flow_action) return errno; action->rewrite.flow_action = flow_action; return 0; } static int dr_action_create_modify_action(struct mlx5dv_dr_domain *dmn, size_t actions_sz, __be64 actions[], struct mlx5dv_dr_action *action) { uint32_t num_hw_actions; uint32_t num_sw_actions; __be64 *hw_actions; int ret; num_sw_actions = actions_sz / DR_MODIFY_ACTION_SIZE; if (num_sw_actions == 0) { dr_dbg(dmn, "Invalid number of actions %u\n", num_sw_actions); errno = EINVAL; return errno; } hw_actions = calloc(1, 2 * num_sw_actions * DR_MODIFY_ACTION_SIZE); if (!hw_actions) { errno = ENOMEM; return errno; } ret = dr_actions_convert_modify_header(action, 2 * num_sw_actions, num_sw_actions, actions, hw_actions, &num_hw_actions); if (ret) goto free_hw_actions; action->rewrite.param.data = (uint8_t *)hw_actions; action->rewrite.param.num_of_actions = num_hw_actions; if (num_hw_actions == 1 && (dmn->ste_ctx->actions_caps & DR_STE_CTX_ACTION_CAP_MODIFY_HDR_INLINE)) { action->rewrite.single_action_opt = true; return 0; } ret = dr_ste_alloc_modify_hdr(action); if (ret) goto free_hw_actions; return 0; free_hw_actions: free(hw_actions); return errno; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_modify_header(struct mlx5dv_dr_domain *dmn, uint32_t flags, size_t actions_sz, __be64 actions[]) { struct mlx5dv_dr_action *action; int ret = 0; atomic_fetch_add(&dmn->refcount, 1); if (!check_comp_mask(flags, MLX5DV_DR_ACTION_FLAGS_ROOT_LEVEL)) { errno = EINVAL; goto dec_ref; } if (actions_sz % DR_MODIFY_ACTION_SIZE) { dr_dbg(dmn, "Invalid modify actions size provided\n"); errno = EINVAL; goto dec_ref; } if (!dmn->info.supp_sw_steering && !(flags & MLX5DV_DR_ACTION_FLAGS_ROOT_LEVEL)) { dr_dbg(dmn, "Only root actions are supported on current domain\n"); errno = EOPNOTSUPP; goto dec_ref; } action = dr_action_create_generic(DR_ACTION_TYP_MODIFY_HDR); if (!action) goto dec_ref; action->rewrite.dmn = dmn; /* Create the action according to the table type */ if (flags & MLX5DV_DR_ACTION_FLAGS_ROOT_LEVEL) { action->rewrite.is_root_level = true; ret = dr_action_create_modify_action_root(dmn, actions_sz, actions, action); } else { action->rewrite.is_root_level = false; ret = dr_action_create_modify_action(dmn, actions_sz, actions, action); } if (ret) { dr_dbg(dmn, "Failed creating modify header action %d\n", ret); goto free_action; } return action; free_action: free(action); dec_ref: atomic_fetch_sub(&dmn->refcount, 1); return NULL; } int mlx5dv_dr_action_modify_flow_meter(struct mlx5dv_dr_action *action, struct mlx5dv_dr_flow_meter_attr *attr, __be64 modify_field_select) { int ret; if (action->action_type != DR_ACTION_TYP_METER) { errno = EINVAL; return errno; } ret = dr_devx_modify_meter(action->meter.devx_obj, attr, modify_field_select); return ret; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_flow_meter(struct mlx5dv_dr_flow_meter_attr *attr) { struct mlx5dv_dr_domain *dmn = attr->next_table->dmn; uint64_t rx_icm_addr = 0, tx_icm_addr = 0; struct mlx5dv_devx_obj *devx_obj; struct mlx5dv_dr_action *action; int ret; if (!dmn->info.supp_sw_steering) { dr_dbg(dmn, "Meter action is not supported on current domain\n"); errno = EOPNOTSUPP; return NULL; } if (dr_is_root_table(attr->next_table)) { dr_dbg(dmn, "Next table cannot be root\n"); errno = EOPNOTSUPP; return NULL; } devx_obj = dr_devx_create_meter(dmn->ctx, attr); if (!devx_obj) return NULL; ret = dr_devx_query_meter(devx_obj, &rx_icm_addr, &tx_icm_addr); if (ret) goto destroy_obj; action = dr_action_create_generic(DR_ACTION_TYP_METER); if (!action) goto destroy_obj; action->meter.devx_obj = devx_obj; action->meter.next_ft = attr->next_table; action->meter.rx_icm_addr = rx_icm_addr; action->meter.tx_icm_addr = tx_icm_addr; atomic_fetch_add(&attr->next_table->refcount, 1); return action; destroy_obj: mlx5dv_devx_obj_destroy(devx_obj); return NULL; } struct mlx5dv_dr_action *mlx5dv_dr_action_create_dest_vport(struct mlx5dv_dr_domain *dmn, uint32_t vport) { struct mlx5dv_dr_action *action; struct dr_devx_vport_cap *vport_cap; if (!dmn->info.supp_sw_steering || dmn->type != MLX5DV_DR_DOMAIN_TYPE_FDB) { dr_dbg(dmn, "Domain doesn't support vport actions\n"); errno = EOPNOTSUPP; return NULL; } /* vport number is limited to 16 bit */ if (vport > WIRE_PORT) { dr_dbg(dmn, "The vport number is out of range\n"); errno = EINVAL; return NULL; } vport_cap = dr_vports_table_get_vport_cap(&dmn->info.caps, vport); if (!vport_cap) { dr_dbg(dmn, "Failed to get vport %d caps\n", vport); return NULL; } action = dr_action_create_generic(DR_ACTION_TYP_VPORT); if (!action) return NULL; action->vport.dmn = dmn; action->vport.caps = vport_cap; return action; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_dest_ib_port(struct mlx5dv_dr_domain *dmn, uint32_t ib_port) { struct dr_devx_vport_cap *vport_cap; struct mlx5dv_dr_action *action; if (!dmn->info.supp_sw_steering || dmn->type != MLX5DV_DR_DOMAIN_TYPE_FDB) { dr_dbg(dmn, "Domain doesn't support ib_port actions\n"); errno = EOPNOTSUPP; return NULL; } vport_cap = dr_vports_table_get_ib_port_cap(&dmn->info.caps, ib_port); if (!vport_cap) { dr_dbg(dmn, "Failed to get ib_port %d caps\n", ib_port); errno = EINVAL; return NULL; } action = dr_action_create_generic(DR_ACTION_TYP_VPORT); if (!action) return NULL; action->vport.dmn = dmn; action->vport.caps = vport_cap; return action; } static int dr_action_convert_to_fte_dest(struct mlx5dv_dr_domain *dmn, struct mlx5dv_dr_action *dest, struct mlx5dv_dr_action *dest_reformat, struct dr_devx_flow_fte_attr *fte_attr) { struct dr_devx_flow_dest_info *dest_info = &fte_attr->dest_arr[fte_attr->dest_size]; switch (dest->action_type) { case DR_ACTION_TYP_MISS: if (dmn->type != MLX5DV_DR_DOMAIN_TYPE_FDB) goto err_exit; fte_attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; dest_info->type = MLX5_FLOW_DEST_TYPE_VPORT; if (dmn->info.caps.is_ecpf) dest_info->vport_num = ECPF_PORT; break; case DR_ACTION_TYP_VPORT: if (dmn->type != MLX5DV_DR_DOMAIN_TYPE_FDB) goto err_exit; fte_attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; dest_info->type = MLX5_FLOW_DEST_TYPE_VPORT; dest_info->vport_num = dest->vport.caps->num; break; case DR_ACTION_TYP_QP: fte_attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; dest_info->type = MLX5_FLOW_DEST_TYPE_TIR; if (dest->dest_qp.is_qp) dest_info->tir_num = to_mqp(dest->dest_qp.qp)->tirn; else dest_info->tir_num = dest->dest_qp.devx_tir->object_id; break; case DR_ACTION_TYP_CTR: fte_attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; dest_info->type = MLX5_FLOW_DEST_TYPE_COUNTER; dest_info->counter_id = dest->ctr.devx_obj->object_id + dest->ctr.offset; break; case DR_ACTION_TYP_FT: fte_attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; dest_info->type = MLX5_FLOW_DEST_TYPE_FT; dest_info->ft_id = dest->dest_tbl->devx_obj->object_id; break; default: goto err_exit; } if (dest_reformat) { int ret = 0; switch (dest_reformat->action_type) { case DR_ACTION_TYP_L2_TO_TNL_L2: case DR_ACTION_TYP_L2_TO_TNL_L3: if (dest_reformat->reformat.is_root_level) goto err_exit; dr_domain_lock(dmn); if (!dest_reformat->reformat.dvo) { ret = dr_action_create_devx_reformat(dmn, dest_reformat, dest_reformat->reformat.reformat_size, dest_reformat->reformat.data); } dr_domain_unlock(dmn); if (ret) goto err_exit; fte_attr->extended_dest = true; dest_info->has_reformat = true; dest_info->reformat_id = dest_reformat->reformat.dvo->object_id; break; default: goto err_exit; } } fte_attr->dest_size++; return 0; err_exit: errno = EOPNOTSUPP; return errno; } static struct dr_devx_tbl_with_refs * dr_action_create_sampler_term_tbl(struct mlx5dv_dr_domain *dmn, struct mlx5dv_dr_flow_sampler_attr *attr) { struct dr_devx_flow_table_attr ft_attr = {}; struct dr_devx_flow_group_attr fg_attr = {}; struct dr_devx_flow_fte_attr fte_attr = {}; struct dr_devx_flow_dest_info *dest_info; struct dr_devx_tbl_with_refs *term_tbl; struct mlx5dv_dr_action **ref_actions; uint32_t ref_index = 0; uint32_t tbl_type; uint32_t i; tbl_type = attr->default_next_table->table_type; dest_info = calloc(attr->num_sample_actions, sizeof(struct dr_devx_flow_dest_info)); if (!dest_info) { errno = ENOMEM; return NULL; } term_tbl = calloc(1, sizeof(struct dr_devx_tbl_with_refs)); if (!term_tbl) { errno = ENOMEM; goto free_dest_info; } ref_actions = calloc(attr->num_sample_actions, sizeof(struct mlx5dv_dr_action *)); if (!ref_actions) { errno = ENOMEM; goto free_term_tbl; } ft_attr.type = tbl_type; ft_attr.level = dmn->info.caps.max_ft_level - 1; ft_attr.term_tbl = true; fte_attr.dest_arr = dest_info; for (i = 0; i < attr->num_sample_actions; i++) { enum dr_action_type action_type = attr->sample_actions[i]->action_type; atomic_fetch_add(&attr->sample_actions[i]->refcount, 1); ref_actions[ref_index++] = attr->sample_actions[i]; switch (action_type) { case DR_ACTION_TYP_MISS: case DR_ACTION_TYP_VPORT: if (dr_action_convert_to_fte_dest(dmn, attr->sample_actions[i], NULL, &fte_attr)) goto free_ref_actions; break; case DR_ACTION_TYP_QP: case DR_ACTION_TYP_CTR: if (tbl_type != FS_FT_NIC_RX) { errno = EOPNOTSUPP; goto free_ref_actions; } if (dr_action_convert_to_fte_dest(dmn, attr->sample_actions[i], NULL, &fte_attr)) goto free_ref_actions; break; case DR_ACTION_TYP_TAG: if (tbl_type != FS_FT_NIC_RX) { errno = EOPNOTSUPP; goto free_ref_actions; } fte_attr.flow_tag = attr->sample_actions[i]->flow_tag; break; default: errno = EOPNOTSUPP; goto free_ref_actions; } } term_tbl->devx_tbl = dr_devx_create_always_hit_ft(dmn->ctx, &ft_attr, &fg_attr, &fte_attr); if (!term_tbl->devx_tbl) goto free_ref_actions; term_tbl->ref_actions = ref_actions; term_tbl->ref_actions_num = attr->num_sample_actions; free(dest_info); return term_tbl; free_ref_actions: for (i = 0; i < ref_index; i++) atomic_fetch_sub(&ref_actions[i]->refcount, 1); free(ref_actions); free_term_tbl: free(term_tbl); free_dest_info: free(dest_info); return NULL; } static void dr_action_destroy_sampler_term_tbl(struct dr_devx_tbl_with_refs *term_tbl) { uint32_t i; dr_devx_destroy_always_hit_ft(term_tbl->devx_tbl); for (i = 0; i < term_tbl->ref_actions_num; i++) atomic_fetch_sub(&term_tbl->ref_actions[i]->refcount, 1); free(term_tbl->ref_actions); free(term_tbl); } static struct dr_flow_sampler * dr_action_create_sampler(struct mlx5dv_dr_domain *dmn, struct mlx5dv_dr_flow_sampler_attr *attr, struct dr_devx_tbl_with_refs *term_tbl, struct dr_flow_sampler_restore_tbl *restore) { struct dr_devx_flow_sampler_attr sampler_attr = {}; struct dr_flow_sampler *sampler; uint64_t icm_rx = 0, icm_tx = 0; int ret; sampler = calloc(1, sizeof(struct dr_flow_sampler)); if (!sampler) { errno = ENOMEM; return NULL; } sampler->next_ft = restore ? restore->tbl : attr->default_next_table; atomic_fetch_add(&sampler->next_ft->refcount, 1); /* Sampler HW level equals to term_tbl HW level, need to set ignore level */ sampler_attr.ignore_flow_level = true; sampler_attr.sample_ratio = attr->sample_ratio; sampler_attr.table_type = term_tbl->devx_tbl->type; sampler_attr.level = term_tbl->devx_tbl->level; sampler_attr.sample_table_id = term_tbl->devx_tbl->ft_dvo->object_id; sampler_attr.default_next_table_id = sampler->next_ft->devx_obj->object_id; sampler->devx_obj = dr_devx_create_flow_sampler(dmn->ctx, &sampler_attr); if (!sampler->devx_obj) goto dec_next_ft_ref; ret = dr_devx_query_flow_sampler(sampler->devx_obj, &icm_rx, &icm_tx); if (ret) goto destroy_sampler_dvo; sampler->rx_icm_addr = icm_rx; sampler->tx_icm_addr = icm_tx; return sampler; destroy_sampler_dvo: mlx5dv_devx_obj_destroy(sampler->devx_obj); dec_next_ft_ref: atomic_fetch_sub(&sampler->next_ft->refcount, 1); free(sampler); return NULL; } static void dr_action_destroy_sampler(struct dr_flow_sampler *sampler) { mlx5dv_devx_obj_destroy(sampler->devx_obj); atomic_fetch_sub(&sampler->next_ft->refcount, 1); free(sampler); } static struct dr_flow_sampler_restore_tbl * dr_action_create_sampler_restore_tbl(struct mlx5dv_dr_domain *dmn, struct mlx5dv_dr_flow_sampler_attr *attr) { struct mlx5dv_flow_match_parameters *mask; struct dr_flow_sampler_restore_tbl *restore; uint32_t action_field; uint32_t action_type; uint32_t mask_size; action_type = DEVX_GET(set_action_in, &(attr->action), action_type); action_field = DEVX_GET(set_action_in, &(attr->action), field); /* Currently only support restore of setting Reg_C0 */ if (action_type != MLX5_ACTION_TYPE_SET || action_field != MLX5_ACTION_IN_FIELD_OUT_METADATA_REGC_0) { errno = EOPNOTSUPP; return NULL; } mask_size = sizeof(struct mlx5dv_flow_match_parameters) + sizeof(struct dr_match_param); mask = calloc(1, mask_size); if (!mask) { errno = ENOMEM; return NULL; } mask->match_sz = sizeof(struct dr_match_param); restore = calloc(1, sizeof(struct dr_flow_sampler_restore_tbl)); if (!restore) { errno = ENOMEM; goto free_mask; } restore->tbl = mlx5dv_dr_table_create(dmn, attr->default_next_table->level - 1); if (!restore->tbl) goto free_restore; restore->matcher = mlx5dv_dr_matcher_create(restore->tbl, 0, 0, mask); if (!restore->matcher) goto destroy_restore_tbl; restore->num_of_actions = 2; restore->actions = calloc(restore->num_of_actions, sizeof(struct mlx5dv_dr_action *)); if (!restore->actions) { errno = ENOMEM; goto destroy_restore_matcher; } restore->actions[0] = mlx5dv_dr_action_create_modify_header(dmn, 0, DR_MODIFY_ACTION_SIZE, &(attr->action)); if (!restore->actions[0]) goto free_action_list; restore->actions[1] = mlx5dv_dr_action_create_dest_table(attr->default_next_table); if (!restore->actions[1]) goto destroy_modify_hdr_action; restore->rule = mlx5dv_dr_rule_create(restore->matcher, mask, restore->num_of_actions, restore->actions); if (!restore->rule) goto destroy_dest_action; free(mask); return restore; destroy_dest_action: mlx5dv_dr_action_destroy(restore->actions[1]); destroy_modify_hdr_action: mlx5dv_dr_action_destroy(restore->actions[0]); free_action_list: free(restore->actions); destroy_restore_matcher: mlx5dv_dr_matcher_destroy(restore->matcher); destroy_restore_tbl: mlx5dv_dr_table_destroy(restore->tbl); free_restore: free(restore); free_mask: free(mask); return NULL; } static void dr_action_destroy_sampler_restore_tbl(struct dr_flow_sampler_restore_tbl *restore) { uint32_t i; mlx5dv_dr_rule_destroy(restore->rule); for (i = 0; i < restore->num_of_actions; i++) mlx5dv_dr_action_destroy(restore->actions[i]); free(restore->actions); mlx5dv_dr_matcher_destroy(restore->matcher); mlx5dv_dr_table_destroy(restore->tbl); free(restore); } struct mlx5dv_dr_action * mlx5dv_dr_action_create_flow_sampler(struct mlx5dv_dr_flow_sampler_attr *attr) { struct mlx5dv_dr_action *action; struct mlx5dv_dr_domain *dmn; bool restore = false; dmn = attr->default_next_table->dmn; if (!dmn || !attr->default_next_table || attr->sample_ratio == 0 || !attr->sample_actions || attr->num_sample_actions == 0) { errno = EINVAL; return NULL; } if (dmn->type != MLX5DV_DR_DOMAIN_TYPE_NIC_RX && dmn->type != MLX5DV_DR_DOMAIN_TYPE_FDB) { errno = EOPNOTSUPP; return NULL; } if (dmn->type == MLX5DV_DR_DOMAIN_TYPE_FDB && dmn->info.caps.sw_format_ver == MLX5_HW_CONNECTX_5) restore = true; atomic_fetch_add(&dmn->refcount, 1); action = dr_action_create_generic(DR_ACTION_TYP_SAMPLER); if (!action) goto dec_ref; action->sampler.dmn = dmn; action->sampler.term_tbl = dr_action_create_sampler_term_tbl(dmn, attr); if (!action->sampler.term_tbl) goto free_action; action->sampler.sampler_default = dr_action_create_sampler(dmn, attr, action->sampler.term_tbl, NULL); if (!action->sampler.sampler_default) goto destroy_term_tbl; if (restore) { struct dr_flow_sampler *sampler_restore; action->sampler.restore_tbl = dr_action_create_sampler_restore_tbl(dmn, attr); if (!action->sampler.restore_tbl) goto destroy_sampler_default; sampler_restore = dr_action_create_sampler(dmn, attr, action->sampler.term_tbl, action->sampler.restore_tbl); if (!sampler_restore) goto destroy_restore; action->sampler.sampler_restore = sampler_restore; } return action; destroy_restore: if (action->sampler.restore_tbl) dr_action_destroy_sampler_restore_tbl(action->sampler.restore_tbl); destroy_sampler_default: dr_action_destroy_sampler(action->sampler.sampler_default); destroy_term_tbl: dr_action_destroy_sampler_term_tbl(action->sampler.term_tbl); free_action: free(action); dec_ref: atomic_fetch_sub(&dmn->refcount, 1); return NULL; } static int dr_action_add_action_member(struct list_head *ref_list, struct mlx5dv_dr_action *action) { struct dr_rule_action_member *action_mem; action_mem = calloc(1, sizeof(*action_mem)); if (!action_mem) { errno = ENOMEM; return errno; } action_mem->action = action; list_node_init(&action_mem->list); list_add_tail(ref_list, &action_mem->list); atomic_fetch_add(&action_mem->action->refcount, 1); return 0; } static void dr_action_remove_action_members(struct list_head *ref_list) { struct dr_rule_action_member *action_mem; struct dr_rule_action_member *tmp; list_for_each_safe(ref_list, action_mem, tmp, list) { list_del(&action_mem->list); atomic_fetch_sub(&action_mem->action->refcount, 1); free(action_mem); } } static int dr_action_create_dest_array_tbl(struct mlx5dv_dr_action *action, size_t num_dest, struct mlx5dv_dr_action_dest_attr *dests[]) { struct mlx5dv_dr_domain *dmn = action->dest_array.dmn; struct dr_devx_flow_table_attr ft_attr = {}; struct dr_devx_flow_group_attr fg_attr = {}; struct dr_devx_flow_fte_attr fte_attr = {}; uint32_t i; int ret; switch (dmn->type) { case MLX5DV_DR_DOMAIN_TYPE_FDB: ft_attr.type = FS_FT_FDB; ft_attr.level = dmn->info.caps.max_ft_level - 1; break; case MLX5DV_DR_DOMAIN_TYPE_NIC_RX: ft_attr.type = FS_FT_NIC_RX; ft_attr.level = MLX5_MULTI_PATH_FT_MAX_LEVEL - 1; break; default: errno = EOPNOTSUPP; return errno; } fte_attr.dest_arr = calloc(num_dest, sizeof(struct dr_devx_flow_dest_info)); if (!fte_attr.dest_arr) { errno = ENOMEM; return errno; } for (i = 0; i < num_dest; i++) { struct mlx5dv_dr_action *reformat_action; struct mlx5dv_dr_action *dest_action; switch (dests[i]->type) { case MLX5DV_DR_ACTION_DEST_REFORMAT: dest_action = dests[i]->dest_reformat->dest; reformat_action = dests[i]->dest_reformat->reformat; ft_attr.reformat_en = true; break; case MLX5DV_DR_ACTION_DEST: dest_action = dests[i]->dest; reformat_action = NULL; break; default: errno = EINVAL; goto clear_actions_list; } switch (dest_action->action_type) { case DR_ACTION_TYP_MISS: case DR_ACTION_TYP_VPORT: case DR_ACTION_TYP_QP: case DR_ACTION_TYP_CTR: case DR_ACTION_TYP_FT: if (dr_action_add_action_member(&action->dest_array.actions_list, dest_action)) goto clear_actions_list; break; default: errno = EOPNOTSUPP; goto clear_actions_list; } if (reformat_action) if (dr_action_add_action_member(&action->dest_array.actions_list, reformat_action)) goto clear_actions_list; if (dr_action_convert_to_fte_dest(dmn, dest_action, reformat_action, &fte_attr)) goto clear_actions_list; } action->dest_array.devx_tbl = dr_devx_create_always_hit_ft(dmn->ctx, &ft_attr, &fg_attr, &fte_attr); if (!action->dest_array.devx_tbl) goto clear_actions_list; ret = dr_devx_query_flow_table(action->dest_array.devx_tbl->ft_dvo, ft_attr.type, &action->dest_array.rx_icm_addr, &action->dest_array.tx_icm_addr); if (ret) goto destroy_devx_tbl; free(fte_attr.dest_arr); return 0; destroy_devx_tbl: dr_devx_destroy_always_hit_ft(action->dest_array.devx_tbl); clear_actions_list: dr_action_remove_action_members(&action->dest_array.actions_list); free(fte_attr.dest_arr); return errno; } struct mlx5dv_dr_action * mlx5dv_dr_action_create_dest_array(struct mlx5dv_dr_domain *dmn, size_t num_dest, struct mlx5dv_dr_action_dest_attr *dests[]) { struct mlx5dv_dr_action *action; if (num_dest <= 1) { errno = EINVAL; return NULL; } atomic_fetch_add(&dmn->refcount, 1); action = dr_action_create_generic(DR_ACTION_TYP_DEST_ARRAY); if (!action) goto dec_ref; action->dest_array.dmn = dmn; list_head_init(&action->dest_array.actions_list); if (dr_action_create_dest_array_tbl(action, num_dest, dests)) goto free_action; return action; free_action: free(action); dec_ref: atomic_fetch_sub(&dmn->refcount, 1); return NULL; } int mlx5dv_dr_action_destroy(struct mlx5dv_dr_action *action) { if (atomic_load(&action->refcount) > 1) return EBUSY; switch (action->action_type) { case DR_ACTION_TYP_FT: atomic_fetch_sub(&action->dest_tbl->refcount, 1); break; case DR_ACTION_TYP_TNL_L2_TO_L2: if (action->reformat.is_root_level) mlx5_destroy_flow_action(action->reformat.flow_action); atomic_fetch_sub(&action->reformat.dmn->refcount, 1); break; case DR_ACTION_TYP_TNL_L3_TO_L2: if (action->reformat.is_root_level) { mlx5_destroy_flow_action(action->reformat.flow_action); } else { dr_ste_free_modify_hdr(action); free(action->rewrite.param.data); } atomic_fetch_sub(&action->reformat.dmn->refcount, 1); break; case DR_ACTION_TYP_L2_TO_TNL_L2: case DR_ACTION_TYP_L2_TO_TNL_L3: if (action->reformat.is_root_level) { mlx5_destroy_flow_action(action->reformat.flow_action); } else { if (action->reformat.chunk) dr_action_destroy_sw_reformat(action); if (action->reformat.dvo) dr_action_destroy_devx_reformat(action); } atomic_fetch_sub(&action->reformat.dmn->refcount, 1); break; case DR_ACTION_TYP_MODIFY_HDR: if (action->rewrite.is_root_level) { mlx5_destroy_flow_action(action->rewrite.flow_action); } else { if (!action->rewrite.single_action_opt) dr_ste_free_modify_hdr(action); free(action->rewrite.param.data); } atomic_fetch_sub(&action->rewrite.dmn->refcount, 1); break; case DR_ACTION_TYP_METER: mlx5dv_devx_obj_destroy(action->meter.devx_obj); atomic_fetch_sub(&action->meter.next_ft->refcount, 1); break; case DR_ACTION_TYP_SAMPLER: if (action->sampler.sampler_restore) { dr_action_destroy_sampler(action->sampler.sampler_restore); dr_action_destroy_sampler_restore_tbl(action->sampler.restore_tbl); } dr_action_destroy_sampler(action->sampler.sampler_default); dr_action_destroy_sampler_term_tbl(action->sampler.term_tbl); atomic_fetch_sub(&action->sampler.dmn->refcount, 1); break; case DR_ACTION_TYP_DEST_ARRAY: dr_devx_destroy_always_hit_ft(action->dest_array.devx_tbl); dr_action_remove_action_members(&action->dest_array.actions_list); atomic_fetch_sub(&action->dest_array.dmn->refcount, 1); break; case DR_ACTION_TYP_ROOT_FT: dr_devx_destroy_always_hit_ft(action->root_tbl.devx_tbl); mlx5dv_destroy_steering_anchor(action->root_tbl.sa); atomic_fetch_sub(&action->root_tbl.tbl->refcount, 1); break; default: break; } free(action); return 0; }