*
* Additional mlx4-specific constraints on supported fields:
*
- * - No support for partial masks.
+ * - No support for partial masks, except in the specific case of matching
+ * all multicast traffic (@p spec->dst and @p mask->dst equal to
+ * 01:00:00:00:00:00).
* - Not providing @p item->spec or providing an empty @p mask->dst is
* *only* supported if the rule doesn't specify additional matching
* criteria (i.e. rule is promiscuous-like).
goto error;
} else if (!sum_dst) {
flow->promisc = 1;
+ } else if (sum_dst == 1 && mask->dst.addr_bytes[0] == 1) {
+ if (!(spec->dst.addr_bytes[0] & 1)) {
+ msg = "mlx4 does not support the explicit"
+ " exclusion of all multicast traffic";
+ goto error;
+ }
+ flow->allmulti = 1;
} else if (sum_dst != (UINT8_C(0xff) * ETHER_ADDR_LEN)) {
msg = "mlx4 does not support matching partial"
" Ethernet fields";
flow->ibv_attr->type = IBV_FLOW_ATTR_ALL_DEFAULT;
return 0;
}
+ if (flow->allmulti) {
+ flow->ibv_attr->type = IBV_FLOW_ATTR_MC_DEFAULT;
+ return 0;
+ }
++flow->ibv_attr->num_of_specs;
eth = (void *)((uintptr_t)flow->ibv_attr + flow->ibv_attr_size);
*eth = (struct ibv_flow_spec_eth) {
flow->internal = 1;
continue;
}
- if (flow->promisc) {
+ if (flow->promisc || flow->allmulti) {
msg = "mlx4 does not support additional matching"
" criteria combined with indiscriminate"
" matching on Ethernet headers";
return 0;
}
+/**
+ * Helper function to determine the next configured VLAN filter.
+ *
+ * @param priv
+ * Pointer to private structure.
+ * @param vlan
+ * VLAN ID to use as a starting point.
+ *
+ * @return
+ * Next configured VLAN ID or a high value (>= 4096) if there is none.
+ */
+static uint16_t
+mlx4_flow_internal_next_vlan(struct priv *priv, uint16_t vlan)
+{
+ while (vlan < 4096) {
+ if (priv->dev->data->vlan_filter_conf.ids[vlan / 64] &
+ (UINT64_C(1) << (vlan % 64)))
+ return vlan;
+ ++vlan;
+ }
+ return vlan;
+}
+
/**
* Generate internal flow rules.
*
+ * Various flow rules are created depending on the mode the device is in:
+ *
+ * 1. Promiscuous: port MAC + catch-all (VLAN filtering is ignored).
+ * 2. All multicast: port MAC/VLAN + catch-all multicast.
+ * 3. Otherwise: port MAC/VLAN + broadcast MAC/VLAN.
+ *
+ * About MAC flow rules:
+ *
* - MAC flow rules are generated from @p dev->data->mac_addrs
* (@p priv->mac array).
* - An additional flow rule for Ethernet broadcasts is also generated.
+ * - All these are per-VLAN if @p dev->data->dev_conf.rxmode.hw_vlan_filter
+ * is enabled and VLAN filters are configured.
*
* @param priv
* Pointer to private structure.
const struct rte_flow_item_eth eth_mask = {
.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
};
+ const struct rte_flow_item_eth eth_allmulti = {
+ .dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+ };
+ struct rte_flow_item_vlan vlan_spec;
+ const struct rte_flow_item_vlan vlan_mask = {
+ .tci = RTE_BE16(0x0fff),
+ };
struct rte_flow_item pattern[] = {
{
.type = MLX4_FLOW_ITEM_TYPE_INTERNAL,
.spec = ð_spec,
.mask = ð_mask,
},
+ {
+ /* Replaced with VLAN if filtering is enabled. */
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
{
.type = RTE_FLOW_ITEM_TYPE_END,
},
},
};
struct ether_addr *rule_mac = ð_spec.dst;
+ rte_be16_t *rule_vlan =
+ priv->dev->data->dev_conf.rxmode.hw_vlan_filter &&
+ !priv->dev->data->promiscuous ?
+ &vlan_spec.tci :
+ NULL;
+ int broadcast =
+ !priv->dev->data->promiscuous &&
+ !priv->dev->data->all_multicast;
+ uint16_t vlan = 0;
struct rte_flow *flow;
unsigned int i;
int err = 0;
- for (i = 0; i != RTE_DIM(priv->mac) + 1; ++i) {
+ /*
+ * Set up VLAN item if filtering is enabled and at least one VLAN
+ * filter is configured.
+ */
+ if (rule_vlan) {
+ vlan = mlx4_flow_internal_next_vlan(priv, 0);
+ if (vlan < 4096) {
+ pattern[2] = (struct rte_flow_item){
+ .type = RTE_FLOW_ITEM_TYPE_VLAN,
+ .spec = &vlan_spec,
+ .mask = &vlan_mask,
+ };
+next_vlan:
+ *rule_vlan = rte_cpu_to_be_16(vlan);
+ } else {
+ rule_vlan = NULL;
+ }
+ }
+ for (i = 0; i != RTE_DIM(priv->mac) + broadcast; ++i) {
const struct ether_addr *mac;
/* Broadcasts are handled by an extra iteration. */
assert(flow->ibv_attr->type == IBV_FLOW_ATTR_NORMAL);
assert(flow->ibv_attr->num_of_specs == 1);
assert(eth->type == IBV_FLOW_SPEC_ETH);
+ if (rule_vlan &&
+ (eth->val.vlan_tag != *rule_vlan ||
+ eth->mask.vlan_tag != RTE_BE16(0x0fff)))
+ continue;
+ if (!rule_vlan && eth->mask.vlan_tag)
+ continue;
for (j = 0; j != sizeof(mac->addr_bytes); ++j)
if (eth->val.dst_mac[j] != mac->addr_bytes[j] ||
eth->mask.dst_mac[j] != UINT8_C(0xff) ||
actions, error);
if (!flow) {
err = -rte_errno;
- break;
+ goto error;
}
}
flow->select = 1;
flow->mac = 1;
}
- /* Clear selection and clean up stale MAC flow rules. */
+ if (rule_vlan) {
+ vlan = mlx4_flow_internal_next_vlan(priv, vlan + 1);
+ if (vlan < 4096)
+ goto next_vlan;
+ }
+ /* Take care of promiscuous and all multicast flow rules. */
+ if (!broadcast) {
+ for (flow = LIST_FIRST(&priv->flows);
+ flow && flow->internal;
+ flow = LIST_NEXT(flow, next)) {
+ if (priv->dev->data->promiscuous) {
+ if (flow->promisc)
+ break;
+ } else {
+ assert(priv->dev->data->all_multicast);
+ if (flow->allmulti)
+ break;
+ }
+ }
+ if (!flow || !flow->internal) {
+ /* Not found, create a new flow rule. */
+ if (priv->dev->data->promiscuous) {
+ pattern[1].spec = NULL;
+ pattern[1].mask = NULL;
+ } else {
+ assert(priv->dev->data->all_multicast);
+ pattern[1].spec = ð_allmulti;
+ pattern[1].mask = ð_allmulti;
+ }
+ pattern[2] = pattern[3];
+ flow = mlx4_flow_create(priv->dev, &attr, pattern,
+ actions, error);
+ if (!flow) {
+ err = -rte_errno;
+ goto error;
+ }
+ }
+ assert(flow->promisc || flow->allmulti);
+ flow->select = 1;
+ }
+error:
+ /* Clear selection and clean up stale internal flow rules. */
flow = LIST_FIRST(&priv->flows);
while (flow && flow->internal) {
struct rte_flow *next = LIST_NEXT(flow, next);
- if (flow->mac && !flow->select)
+ if (!flow->select)
claim_zero(mlx4_flow_destroy(priv->dev, flow, error));
else
flow->select = 0;