blob: 7a9b83e741853e85c5c87ad066669cbc9d3234f5 [file] [log] [blame] [edit]
/*
* 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 <unistd.h>
#include <arpa/inet.h>
#include <ccan/ilog.h>
#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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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;
}
#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)
{
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:
if (action->dest_tbl->dmn != dmn) {
dr_dbg(dmn, "Destination table belongs to a different domain\n");
goto out_invalid_arg;
}
if (action->dest_tbl->level <= matcher->tbl->level) {
dr_dbg(dmn, "Destination table level should be higher than source table\n");
goto out_invalid_arg;
}
attr.final_icm_addr = rx_rule ?
action->dest_tbl->rx.s_anchor->chunk->icm_addr :
action->dest_tbl->tx.s_anchor->chunk->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;
}
attr.decap_index = action->rewrite.index;
attr.decap_actions = action->rewrite.num_of_actions;
attr.decap_with_vlan =
attr.decap_actions == WITH_VLAN_NUM_HW_ACTIONS;
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;
}
attr.modify_index = action->rewrite.index;
attr.modify_actions = action->rewrite.num_of_actions;
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 = action->reformat.dvo->object_id;
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;
}
attr.hit_gvmi = action->vport.caps->vhca_gvmi;
if (rx_rule) {
/* Loopback on WIRE vport is not supported */
if (action->vport.caps->num == WIRE_PORT)
goto out_invalid_arg;
attr.final_icm_addr = action->vport.caps->icm_address_rx;
} else {
attr.final_icm_addr = 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++;
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 == MAX_VLANS) {
errno = ENOTSUP;
return ENOTSUP;
}
attr.vlans.headers[attr.vlans.count++] = 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;
}
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_6DX) {
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) || 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;
}
#define ACTION_CACHE_LINE_SIZE 64
static int
dr_action_create_reformat_action(struct mlx5dv_dr_domain *dmn,
size_t data_sz, void *data,
struct mlx5dv_dr_action *action)
{
struct mlx5dv_devx_obj *obj;
switch (action->action_type) {
case DR_ACTION_TYP_L2_TO_TNL_L2:
case DR_ACTION_TYP_L2_TO_TNL_L3:
{
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;
}
case DR_ACTION_TYP_TNL_L2_TO_L2:
{
return 0;
}
case DR_ACTION_TYP_TNL_L3_TO_L2:
{
uint8_t hw_actions[ACTION_CACHE_LINE_SIZE] = {};
int ret;
ret = dr_ste_set_action_decap_l3_list(dmn->ste_ctx,
data, data_sz,
hw_actions,
ACTION_CACHE_LINE_SIZE,
&action->rewrite.num_of_actions);
if (ret) {
dr_dbg(dmn, "Failed creating decap l3 action list\n");
return ret;
}
action->rewrite.chunk = dr_icm_alloc_chunk(dmn->action_icm_pool,
DR_CHUNK_SIZE_8);
if (!action->rewrite.chunk) {
dr_dbg(dmn, "Failed allocating modify header chunk\n");
return errno;
}
action->rewrite.data = (void *)hw_actions;
action->rewrite.index = (action->rewrite.chunk->icm_addr -
dmn->info.caps.hdr_modify_icm_addr) /
ACTION_CACHE_LINE_SIZE;
ret = dr_send_postsend_action(dmn, action);
if (ret) {
dr_dbg(dmn, "Writing decap l3 actions to ICM failed\n");
dr_icm_free_chunk(action->rewrite.chunk);
return ret;
}
return 0;
}
default:
dr_dbg(dmn, "Reformat type is not supported %d\n", action->action_type);
errno = ENOTSUP;
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;
}
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 dynamic_chunck_size;
struct dr_icm_chunk *chunk;
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;
dynamic_chunck_size = ilog32(num_hw_actions - 1);
/* HW modify action index granularity is at least 64B */
dynamic_chunck_size = max_t(uint32_t, dynamic_chunck_size,
DR_CHUNK_SIZE_8);
chunk = dr_icm_alloc_chunk(dmn->action_icm_pool, dynamic_chunck_size);
if (!chunk)
goto free_hw_actions;
action->rewrite.chunk = chunk;
action->rewrite.data = (uint8_t *)hw_actions;
action->rewrite.num_of_actions = num_hw_actions;
action->rewrite.index = (chunk->icm_addr -
dmn->info.caps.hdr_modify_icm_addr) /
ACTION_CACHE_LINE_SIZE;
ret = dr_send_postsend_action(dmn, action);
if (ret)
goto free_chunk;
return 0;
free_chunk:
dr_icm_free_chunk(chunk);
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, tx_icm_addr;
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) {
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;
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, icm_tx;
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_icm_free_chunk(action->rewrite.chunk);
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
mlx5dv_devx_obj_destroy(action->reformat.dvo);
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 {
dr_icm_free_chunk(action->rewrite.chunk);
free(action->rewrite.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;
default:
break;
}
free(action);
return 0;
}