+
+/* Add a high priority flow that loops representor packets to VF */
+int
+enic_fm_add_rep2vf_flow(struct enic_vf_representor *vf)
+{
+ struct fm_tcam_match_entry *fm_tcam_entry;
+ struct rte_flow *flow0, *flow1;
+ struct fm_action *fm_action;
+ struct rte_flow_error error;
+ struct rte_flow_attr attrs;
+ struct fm_action_op fm_op;
+ struct enic_flowman *fm;
+ struct enic *pf;
+ uint8_t tag;
+
+ pf = vf->pf;
+ fm = pf->fm;
+ tag = fm->vf_rep_tag;
+ enic_fm_open_scratch(fm);
+ fm_tcam_entry = &fm->tcam_entry;
+ fm_action = &fm->action;
+ /* Egress rule: match WQ ID and tag+hairpin */
+ fm_tcam_entry->ftm_data.fk_wq_id = vf->pf_wq_idx;
+ fm_tcam_entry->ftm_mask.fk_wq_id = 0xffff;
+ fm_tcam_entry->ftm_flags |= FMEF_COUNTER;
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_TAG;
+ fm_op.tag.tag = tag;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_EG_HAIRPIN;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_END;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ attrs.group = 0;
+ attrs.ingress = 0;
+ attrs.egress = 1;
+ attrs.priority = FM_HIGHEST_PRIORITY;
+ flow0 = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,
+ &attrs, &error);
+ enic_fm_close_scratch(fm);
+ if (flow0 == NULL) {
+ ENICPMD_LOG(ERR, "Cannot create flow 0 for representor->VF");
+ return -EINVAL;
+ }
+ LIST_INSERT_HEAD(&pf->flows, flow0, next);
+ /* Make this flow internal, so the user app cannot delete it */
+ flow0->internal = 1;
+ ENICPMD_LOG(DEBUG, "representor->VF %d flow created: wq %d -> tag %d hairpin",
+ vf->vf_id, vf->pf_wq_idx, tag);
+
+ /* Ingress: steer hairpinned to VF RQ 0 */
+ enic_fm_open_scratch(fm);
+ fm_tcam_entry->ftm_flags |= FMEF_COUNTER;
+ fm_tcam_entry->ftm_data.fk_hdrset[0].fk_metadata |= FKM_EG_HAIRPINNED;
+ fm_tcam_entry->ftm_mask.fk_hdrset[0].fk_metadata |= FKM_EG_HAIRPINNED;
+ fm_tcam_entry->ftm_data.fk_packet_tag = tag;
+ fm_tcam_entry->ftm_mask.fk_packet_tag = 0xff;
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_RQ_STEER;
+ fm_op.rq_steer.rq_index = 0;
+ fm_op.rq_steer.vnic_handle = vf->enic.fm_vnic_handle;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_END;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ attrs.group = 0;
+ attrs.ingress = 1;
+ attrs.egress = 0;
+ attrs.priority = FM_HIGHEST_PRIORITY;
+ flow1 = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,
+ &attrs, &error);
+ enic_fm_close_scratch(fm);
+ if (flow1 == NULL) {
+ ENICPMD_LOG(ERR, "Cannot create flow 1 for representor->VF");
+ enic_fm_flow_destroy(pf->rte_dev, flow0, &error);
+ return -EINVAL;
+ }
+ LIST_INSERT_HEAD(&pf->flows, flow1, next);
+ flow1->internal = 1;
+ ENICPMD_LOG(DEBUG, "representor->VF %d flow created: tag %d hairpinned -> VF RQ %d",
+ vf->vf_id, tag, fm_op.rq_steer.rq_index);
+ vf->rep2vf_flow[0] = flow0;
+ vf->rep2vf_flow[1] = flow1;
+ /* Done with this tag, use a different one next time */
+ fm->vf_rep_tag++;
+ return 0;
+}
+
+/*
+ * Add a low priority flow that matches all packets from VF and loops them
+ * back to the representor.
+ */
+int
+enic_fm_add_vf2rep_flow(struct enic_vf_representor *vf)
+{
+ struct fm_tcam_match_entry *fm_tcam_entry;
+ struct rte_flow *flow0, *flow1;
+ struct fm_action *fm_action;
+ struct rte_flow_error error;
+ struct rte_flow_attr attrs;
+ struct fm_action_op fm_op;
+ struct enic_flowman *fm;
+ struct enic *pf;
+ uint8_t tag;
+
+ pf = vf->pf;
+ fm = pf->fm;
+ tag = fm->vf_rep_tag;
+ enic_fm_open_scratch(fm);
+ fm_tcam_entry = &fm->tcam_entry;
+ fm_action = &fm->action;
+ /* Egress rule: match-any and tag+hairpin */
+ fm_tcam_entry->ftm_data.fk_wq_id = 0;
+ fm_tcam_entry->ftm_mask.fk_wq_id = 0xffff;
+ fm_tcam_entry->ftm_data.fk_wq_vnic = vf->enic.fm_vnic_handle;
+ fm_tcam_entry->ftm_flags |= FMEF_COUNTER;
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_TAG;
+ fm_op.tag.tag = tag;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_EG_HAIRPIN;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_END;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ attrs.group = 0;
+ attrs.ingress = 0;
+ attrs.egress = 1;
+ attrs.priority = FM_LOWEST_PRIORITY;
+ flow0 = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,
+ &attrs, &error);
+ enic_fm_close_scratch(fm);
+ if (flow0 == NULL) {
+ ENICPMD_LOG(ERR, "Cannot create flow 0 for VF->representor");
+ return -EINVAL;
+ }
+ LIST_INSERT_HEAD(&pf->flows, flow0, next);
+ /* Make this flow internal, so the user app cannot delete it */
+ flow0->internal = 1;
+ ENICPMD_LOG(DEBUG, "VF %d->representor flow created: wq %d (low prio) -> tag %d hairpin",
+ vf->vf_id, fm_tcam_entry->ftm_data.fk_wq_id, tag);
+
+ /* Ingress: steer hairpinned to VF rep RQ */
+ enic_fm_open_scratch(fm);
+ fm_tcam_entry->ftm_flags |= FMEF_COUNTER;
+ fm_tcam_entry->ftm_data.fk_hdrset[0].fk_metadata |= FKM_EG_HAIRPINNED;
+ fm_tcam_entry->ftm_mask.fk_hdrset[0].fk_metadata |= FKM_EG_HAIRPINNED;
+ fm_tcam_entry->ftm_data.fk_packet_tag = tag;
+ fm_tcam_entry->ftm_mask.fk_packet_tag = 0xff;
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_RQ_STEER;
+ fm_op.rq_steer.rq_index = vf->pf_rq_sop_idx;
+ fm_op.rq_steer.vnic_handle = pf->fm_vnic_handle;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ memset(&fm_op, 0, sizeof(fm_op));
+ fm_op.fa_op = FMOP_END;
+ enic_fm_append_action_op(fm, &fm_op, &error);
+ attrs.group = 0;
+ attrs.ingress = 1;
+ attrs.egress = 0;
+ attrs.priority = FM_HIGHEST_PRIORITY;
+ flow1 = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,
+ &attrs, &error);
+ enic_fm_close_scratch(fm);
+ if (flow1 == NULL) {
+ ENICPMD_LOG(ERR, "Cannot create flow 1 for VF->representor");
+ enic_fm_flow_destroy(pf->rte_dev, flow0, &error);
+ return -EINVAL;
+ }
+ LIST_INSERT_HEAD(&pf->flows, flow1, next);
+ flow1->internal = 1;
+ ENICPMD_LOG(DEBUG, "VF %d->representor flow created: tag %d hairpinned -> PF RQ %d",
+ vf->vf_id, tag, vf->pf_rq_sop_idx);
+ vf->vf2rep_flow[0] = flow0;
+ vf->vf2rep_flow[1] = flow1;
+ /* Done with this tag, use a different one next time */
+ fm->vf_rep_tag++;
+ return 0;
+}
+
+/* Destroy representor flows created by enic_fm_add_{rep2vf,vf2rep}_flow */
+static void
+delete_rep_flows(struct enic *enic)
+{
+ struct enic_vf_representor *vf;
+ struct rte_flow_error error;
+ struct rte_eth_dev *dev;
+ uint32_t i;
+
+ RTE_ASSERT(enic_is_vf_rep(enic));
+ vf = VF_ENIC_TO_VF_REP(enic);
+ dev = vf->pf->rte_dev;
+ for (i = 0; i < ARRAY_SIZE(vf->vf2rep_flow); i++) {
+ if (vf->vf2rep_flow[i])
+ enic_fm_flow_destroy(dev, vf->vf2rep_flow[i], &error);
+ }
+ for (i = 0; i < ARRAY_SIZE(vf->rep2vf_flow); i++) {
+ if (vf->rep2vf_flow[i])
+ enic_fm_flow_destroy(dev, vf->rep2vf_flow[i], &error);
+ }
+}
+
+static struct enic_flowman *
+begin_fm(struct enic *enic)
+{
+ struct enic_vf_representor *vf;
+ struct enic_flowman *fm;
+
+ /* Representor uses PF flowman */
+ if (enic_is_vf_rep(enic)) {
+ vf = VF_ENIC_TO_VF_REP(enic);
+ fm = vf->pf->fm;
+ } else {
+ fm = enic->fm;
+ }
+ /* Save the API caller and lock if representors exist */
+ if (fm) {
+ if (fm->owner_enic->switchdev_mode)
+ rte_spinlock_lock(&fm->lock);
+ fm->user_enic = enic;
+ }
+ return fm;
+}
+
+static void
+end_fm(struct enic_flowman *fm)
+{
+ fm->user_enic = NULL;
+ if (fm->owner_enic->switchdev_mode)
+ rte_spinlock_unlock(&fm->lock);
+}