/*
* Copyright (c) 2001-2020 Mellanox Technologies, Ltd. 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
* 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 "utils/bullseye.h"
#include "vma/util/utils.h"
#include "vma/dev/rfs_mc.h"
#include "vma/dev/ring_simple.h"
#define MODULE_NAME "rfs_mc"
rfs_mc::rfs_mc(flow_tuple *flow_spec_5t, ring_slave *p_ring, rfs_rule_filter* rule_filter /*= NULL*/, int flow_tag_id /*=0*/):
rfs (flow_spec_5t, p_ring, rule_filter, flow_tag_id)
{
BULLSEYE_EXCLUDE_BLOCK_START
if (!m_flow_tuple.is_udp_mc()) {
throw_vma_exception("rfs_mc called with non mc destination ip");
}
BULLSEYE_EXCLUDE_BLOCK_END
if (m_p_ring->is_simple() && !prepare_flow_spec()) {
throw_vma_exception("IB multicast offload is not supported");
}
}
bool rfs_mc::prepare_flow_spec()
{
ring_simple* p_ring = dynamic_cast<ring_simple*>(m_p_ring);
if (!p_ring) {
rfs_logpanic("Incompatible ring type");
}
transport_type_t type = p_ring->get_transport_type();
/*
* todo note that ring is not locked here.
* we touch members that should not change during the ring life.
* the ring will not be deleted as we increased refcnt.
* if one of these assumptions change, we must lock.
*/
attach_flow_data_t* p_attach_flow_data = NULL;
switch (type) {
case VMA_TRANSPORT_IB:
{
attach_flow_data_ib_v2_t* attach_flow_data_ib_v2 = NULL;
if (0 == p_ring->m_p_qp_mgr->get_underly_qpn()) {
// IB MC flow steering is done only on L2 --> need to zero other fields to get correct behaviour
// CX3 HW does not support L3+L4 MC flow steering rule
#ifdef DEFINED_IBV_FLOW_SPEC_IB
attach_flow_data_ib_v1_t* attach_flow_data_ib_v1 = NULL;
attach_flow_data_ib_v1 = new attach_flow_data_ib_v1_t(p_ring->m_p_qp_mgr);
uint8_t dst_gid[16];
create_mgid_from_ipv4_mc_ip(dst_gid, p_ring->m_p_qp_mgr->get_partiton(), m_flow_tuple.get_dst_ip());
ibv_flow_spec_ib_set_by_dst_gid(&(attach_flow_data_ib_v1->ibv_flow_attr.ib),
dst_gid);
p_attach_flow_data = (attach_flow_data_t*)attach_flow_data_ib_v1;
break;
#else
return false;
#endif
}
attach_flow_data_ib_v2 = new attach_flow_data_ib_v2_t(p_ring->m_p_qp_mgr);
ibv_flow_spec_ipv4_set(&(attach_flow_data_ib_v2->ibv_flow_attr.ipv4),
m_flow_tuple.get_dst_ip(),
0);
ibv_flow_spec_tcp_udp_set(&(attach_flow_data_ib_v2->ibv_flow_attr.tcp_udp),
(m_flow_tuple.get_protocol() == PROTO_TCP),
m_flow_tuple.get_dst_port(),
m_flow_tuple.get_src_port());
p_attach_flow_data = (attach_flow_data_t*)attach_flow_data_ib_v2;
break;
}
case VMA_TRANSPORT_ETH:
{
attach_flow_data_eth_ipv4_tcp_udp_t* attach_flow_data_eth = NULL;
attach_flow_data_eth = new attach_flow_data_eth_ipv4_tcp_udp_t(p_ring->m_p_qp_mgr);
uint8_t dst_mac[6];
create_multicast_mac_from_ip(dst_mac, m_flow_tuple.get_dst_ip());
ibv_flow_spec_eth_set(&(attach_flow_data_eth->ibv_flow_attr.eth),
dst_mac,
htons(p_ring->m_p_qp_mgr->get_partiton()));
if (safe_mce_sys().eth_mc_l2_only_rules) {
ibv_flow_spec_ipv4_set(&(attach_flow_data_eth->ibv_flow_attr.ipv4), 0, 0);
ibv_flow_spec_tcp_udp_set(&(attach_flow_data_eth->ibv_flow_attr.tcp_udp), 0, 0, 0);
p_attach_flow_data = (attach_flow_data_t*)attach_flow_data_eth;
break;
}
ibv_flow_spec_ipv4_set(&(attach_flow_data_eth->ibv_flow_attr.ipv4),
m_flow_tuple.get_dst_ip(),
0);
ibv_flow_spec_tcp_udp_set(&(attach_flow_data_eth->ibv_flow_attr.tcp_udp),
(m_flow_tuple.get_protocol() == PROTO_TCP),
m_flow_tuple.get_dst_port(),
m_flow_tuple.get_src_port());
if (m_flow_tag_id) { // Will not attach flow_tag spec to rule for tag_id==0
ibv_flow_spec_flow_tag_set(&attach_flow_data_eth->ibv_flow_attr.flow_tag, m_flow_tag_id);
attach_flow_data_eth->ibv_flow_attr.add_flow_tag_spec();
rfs_logdbg("Adding flow_tag spec to MC rule, num_of_specs: %d flow_tag_id: %d",
attach_flow_data_eth->ibv_flow_attr.attr.num_of_specs, m_flow_tag_id);
}
p_attach_flow_data = (attach_flow_data_t*)attach_flow_data_eth;
break;
}
BULLSEYE_EXCLUDE_BLOCK_START
default:
rfs_logpanic("Incompatible transport type = %d", type);
return false;
break;
BULLSEYE_EXCLUDE_BLOCK_END
}
m_attach_flow_data_vector.push_back(p_attach_flow_data);
return true;
}
bool rfs_mc::rx_dispatch_packet(mem_buf_desc_t* p_rx_wc_buf_desc, void* pv_fd_ready_array)
{
// Dispatching: Notify new packet to all registered receivers
p_rx_wc_buf_desc->reset_ref_count();
p_rx_wc_buf_desc->inc_ref_count();
for (uint32_t i=0; i < m_n_sinks_list_entries; ++i) {
if (m_sinks_list[i]) {
m_sinks_list[i]->rx_input_cb(p_rx_wc_buf_desc, pv_fd_ready_array);
}
}
// Check packet ref_count to see if any receivers are interested in this packet
if (p_rx_wc_buf_desc->dec_ref_count() > 1) {
// The sink will be responsible to return the buffer to CQ for reuse
return true;
}
// Reuse this data buffer & mem_buf_desc
return false;
}