Call the netdev-offload flow put/del/get APIs through the dpif-offload-tc layer, and remove them from the netdev-offload layer.
Note that we still need to remove the dependency on the netdev-offload infrastructure for initializing and locating netdevs. This will be addressed in a separate patch. Signed-off-by: Eelco Chaudron <[email protected]> --- v2 changes: - Fix indentation in dpif_offload_tc_parse_flow_get(). --- lib/dpif-netlink.c | 2 +- lib/dpif-offload-tc.c | 241 ++++++++++++++++++++++++++++++++++++++++ lib/netdev-offload-tc.c | 37 +++--- lib/netdev-offload-tc.h | 9 ++ lib/netdev-offload.h | 1 + 5 files changed, 269 insertions(+), 21 deletions(-) diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index e161930be..165f76ace 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -2216,7 +2216,7 @@ out: * Change flags to create the flow in kernel */ put->flags &= ~DPIF_FP_MODIFY; put->flags |= DPIF_FP_CREATE; - } else if (del_err != ENOENT) { + } else if (del_err != ENOENT && del_err != EOPNOTSUPP) { VLOG_ERR_RL(&rl, "failed to delete offloaded flow: %s", ovs_strerror(del_err)); /* stop proccesing the flow in kernel */ diff --git a/lib/dpif-offload-tc.c b/lib/dpif-offload-tc.c index 26dc77cbe..fb45951ad 100644 --- a/lib/dpif-offload-tc.c +++ b/lib/dpif-offload-tc.c @@ -40,6 +40,7 @@ struct dpif_offload_tc { /* Configuration specific variables. */ struct ovsthread_once once_enable; /* Track first-time enablement. */ + bool recirc_id_shared; }; /* tc's flow dump specific data structures. */ @@ -184,6 +185,11 @@ dpif_offload_tc_open(const struct dpif_offload_class *offload_class, offload_tc->port_mgr = dpif_offload_port_mgr_init(); offload_tc->once_enable = (struct ovsthread_once) \ OVSTHREAD_ONCE_INITIALIZER; + offload_tc->recirc_id_shared = !!(dpif_get_features(dpif) + & OVS_DP_F_TC_RECIRC_SHARING); + + VLOG_DBG("Datapath %s recirculation id sharing ", + offload_tc->recirc_id_shared ? "supports" : "does not support"); dpif_offload_tc_meter_init(); @@ -587,6 +593,240 @@ dpif_offload_tc_flow_dump_thread_destroy( free(thread); } +static int +dpif_offload_tc_parse_flow_put(struct dpif_offload_tc *offload_tc, + struct dpif *dpif, struct dpif_flow_put *put) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); + struct dpif_offload_port_mgr_port *port; + const struct nlattr *nla; + struct offload_info info; + struct match match; + odp_port_t in_port; + size_t left; + int err; + + info.tc_modify_flow_deleted = false; + info.tc_modify_flow = false; + + if (put->flags & DPIF_FP_PROBE) { + return EOPNOTSUPP; + } + + err = parse_key_and_mask_to_match(put->key, put->key_len, put->mask, + put->mask_len, &match); + if (err) { + return err; + } + + in_port = match.flow.in_port.odp_port; + port = dpif_offload_port_mgr_find_by_odp_port(offload_tc->port_mgr, + in_port); + if (!port) { + return EOPNOTSUPP; + } + + /* Check the output port for a tunnel. */ + NL_ATTR_FOR_EACH (nla, left, put->actions, put->actions_len) { + if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) { + struct dpif_offload_port_mgr_port *mgr_port; + odp_port_t out_port; + + out_port = nl_attr_get_odp_port(nla); + mgr_port = dpif_offload_port_mgr_find_by_odp_port( + offload_tc->port_mgr, out_port); + + if (!mgr_port) { + err = EOPNOTSUPP; + goto out; + } + } + } + + info.recirc_id_shared_with_tc = offload_tc->recirc_id_shared; + + err = netdev_offload_tc_flow_put(port->netdev, &match, + CONST_CAST(struct nlattr *, put->actions), + put->actions_len, + CONST_CAST(ovs_u128 *, put->ufid), + &info, put->stats); + + if (!err) { + if (put->flags & DPIF_FP_MODIFY && !info.tc_modify_flow) { + struct dpif_op *opp; + struct dpif_op op; + + op.type = DPIF_OP_FLOW_DEL; + op.flow_del.key = put->key; + op.flow_del.key_len = put->key_len; + op.flow_del.ufid = put->ufid; + op.flow_del.pmd_id = put->pmd_id; + op.flow_del.stats = NULL; + op.flow_del.terse = false; + + opp = &op; + dpif_operate(dpif, &opp, 1, DPIF_OFFLOAD_NEVER); + } + + VLOG_DBG("added flow"); + } else if (err != EEXIST) { + struct netdev *oor_netdev = NULL; + enum vlog_level level; + + if (err == ENOSPC + && dpif_offload_is_offload_rebalance_policy_enabled()) { + /* + * We need to set OOR on the input netdev (i.e, 'dev') for the + * flow. But if the flow has a tunnel attribute (i.e, decap + * action, with a virtual device like a VxLAN interface as its + * in-port), then lookup and set OOR on the underlying tunnel + * (real) netdev. */ + oor_netdev = flow_get_tunnel_netdev(&match.flow.tunnel); + if (!oor_netdev) { + /* Not a 'tunnel' flow. */ + oor_netdev = port->netdev; + } + netdev_set_hw_info(oor_netdev, HW_INFO_TYPE_OOR, true); + } + level = (err == ENOSPC || err == EOPNOTSUPP) ? VLL_DBG : VLL_ERR; + VLOG_RL(&rl, level, "failed to offload flow: %s: %s", + ovs_strerror(err), + (oor_netdev ? netdev_get_name(oor_netdev) : + netdev_get_name(port->netdev))); + } + +out: + if (err && err != EEXIST && (put->flags & DPIF_FP_MODIFY)) { + /* Modified rule can't be offloaded, try and delete from HW. */ + int del_err = 0; + + if (!info.tc_modify_flow_deleted) { + del_err = netdev_offload_tc_flow_del(put->ufid, put->stats); + } + + if (!del_err) { + /* Delete from hw success, so old flow was offloaded. + * Change flags to create the flow at the dpif level. */ + put->flags &= ~DPIF_FP_MODIFY; + put->flags |= DPIF_FP_CREATE; + } else if (del_err != ENOENT) { + VLOG_ERR_RL(&rl, "failed to delete offloaded flow: %s", + ovs_strerror(del_err)); + /* Stop processing the flow in kernel. */ + err = 0; + } + } + + return err; +} + +static int +dpif_offload_tc_parse_flow_get(struct dpif_offload_tc *offload_tc, + struct dpif_flow_get *get) +{ + struct dpif_offload_port_mgr_port *port; + struct dpif_flow *dpif_flow = get->flow; + struct odputil_keybuf maskbuf; + struct odputil_keybuf keybuf; + struct odputil_keybuf actbuf; + struct ofpbuf key, mask, act; + struct dpif_flow_stats stats; + struct dpif_flow_attrs attrs; + uint64_t act_buf[1024 / 8]; + struct nlattr *actions; + struct match match; + struct ofpbuf buf; + int err = ENOENT; + + ofpbuf_use_stack(&buf, &act_buf, sizeof act_buf); + + DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload_tc->port_mgr) { + if (!netdev_offload_tc_flow_get(port->netdev, &match, &actions, + get->ufid, &stats, &attrs, &buf)) { + err = 0; + break; + } + } + + if (err) { + return err; + } + + VLOG_DBG("found flow from netdev, translating to dpif flow"); + + ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); + ofpbuf_use_stack(&act, &actbuf, sizeof actbuf); + ofpbuf_use_stack(&mask, &maskbuf, sizeof maskbuf); + dpif_offload_tc_netdev_match_to_dpif_flow(&match, &key, &mask, actions, + &stats, &attrs, + (ovs_u128 *) get->ufid, + dpif_flow, + false); + ofpbuf_put(get->buffer, nl_attr_get(actions), nl_attr_get_size(actions)); + dpif_flow->actions = ofpbuf_at(get->buffer, 0, 0); + dpif_flow->actions_len = nl_attr_get_size(actions); + + return 0; +} + +static void +dpif_offload_tc_operate(struct dpif *dpif, const struct dpif_offload *offload, + struct dpif_op **ops, size_t n_ops) +{ + struct dpif_offload_tc *offload_tc = dpif_offload_tc_cast(offload); + + for (size_t i = 0; i < n_ops; i++) { + struct dpif_op *op = ops[i]; + int error = EOPNOTSUPP; + + if (op->error >= 0) { + continue; + } + + switch (op->type) { + case DPIF_OP_FLOW_PUT: { + struct dpif_flow_put *put = &op->flow_put; + + if (!put->ufid) { + break; + } + + error = dpif_offload_tc_parse_flow_put(offload_tc, dpif, put); + break; + } + case DPIF_OP_FLOW_DEL: { + struct dpif_flow_del *del = &op->flow_del; + + if (!del->ufid) { + break; + } + + error = netdev_offload_tc_flow_del(del->ufid, del->stats); + break; + } + case DPIF_OP_FLOW_GET: { + struct dpif_flow_get *get = &op->flow_get; + + if (!get->ufid) { + break; + } + + error = dpif_offload_tc_parse_flow_get(offload_tc, get); + break; + } + case DPIF_OP_EXECUTE: + break; + } /* End of 'switch (op->type)'. */ + + if (error != EOPNOTSUPP && error != ENOENT) { + /* If the operation is unsupported or the entry was not found, + * we are skipping this flow operation. Otherwise, it was + * processed and we should report the result. */ + op->error = error; + } + } +} + struct dpif_offload_class dpif_offload_tc_class = { .type = "tc", .impl_type = DPIF_OFFLOAD_IMPL_HW_ONLY, @@ -609,6 +849,7 @@ struct dpif_offload_class dpif_offload_tc_class = { .flow_dump_destroy = dpif_offload_tc_flow_dump_destroy, .flow_dump_thread_create = dpif_offload_tc_flow_dump_thread_create, .flow_dump_thread_destroy = dpif_offload_tc_flow_dump_thread_destroy, + .operate = dpif_offload_tc_operate, .flow_get_n_offloaded = dpif_offload_tc_flow_get_n_offloaded, .meter_set = dpif_offload_tc_meter_set, .meter_get = dpif_offload_tc_meter_get, diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 5c9c16bbc..4a8bc3431 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -2300,11 +2300,11 @@ netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower, return 0; } -static int -netdev_tc_flow_put(struct netdev *netdev, struct match *match, - struct nlattr *actions, size_t actions_len, - const ovs_u128 *ufid, struct offload_info *info, - struct dpif_flow_stats *stats) +int +netdev_offload_tc_flow_put(struct netdev *netdev, struct match *match, + struct nlattr *actions, size_t actions_len, + const ovs_u128 *ufid, struct offload_info *info, + struct dpif_flow_stats *stats) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev); @@ -2672,6 +2672,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, if (get_ufid_tc_mapping(ufid, &id) == 0) { VLOG_DBG_RL(&rl, "updating old handle: %d prio: %d", id.handle, id.prio); + info->tc_modify_flow = true; info->tc_modify_flow_deleted = !del_filter_and_ufid_mapping( &id, ufid, &adjust_stats); } @@ -2720,14 +2721,14 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, return err; } -static int -netdev_tc_flow_get(struct netdev *netdev, - struct match *match, - struct nlattr **actions, - const ovs_u128 *ufid, - struct dpif_flow_stats *stats, - struct dpif_flow_attrs *attrs, - struct ofpbuf *buf) +int +netdev_offload_tc_flow_get(struct netdev *netdev, + struct match *match, + struct nlattr **actions, + const ovs_u128 *ufid, + struct dpif_flow_stats *stats, + struct dpif_flow_attrs *attrs, + struct ofpbuf *buf) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); struct tc_flower flower; @@ -2777,10 +2778,9 @@ netdev_tc_flow_get(struct netdev *netdev, return 0; } -static int -netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED, - const ovs_u128 *ufid, - struct dpif_flow_stats *stats) +int +netdev_offload_tc_flow_del(const ovs_u128 *ufid, + struct dpif_flow_stats *stats) { struct tcf_id id; int error; @@ -3465,8 +3465,5 @@ dpif_offload_tc_meter_del(const struct dpif_offload *offload OVS_UNUSED, const struct netdev_flow_api netdev_offload_tc = { .type = "linux_tc", - .flow_put = netdev_tc_flow_put, - .flow_get = netdev_tc_flow_get, - .flow_del = netdev_tc_flow_del, .init_flow_api = netdev_tc_init_flow_api, }; diff --git a/lib/netdev-offload-tc.h b/lib/netdev-offload-tc.h index 77bad5dce..5bab36176 100644 --- a/lib/netdev-offload-tc.h +++ b/lib/netdev-offload-tc.h @@ -42,6 +42,15 @@ bool netdev_offload_tc_flow_dump_next(struct netdev_tc_flow_dump *, struct dpif_flow_attrs *, ovs_u128 *ufid, struct ofpbuf *rbuffer, struct ofpbuf *wbuffer); +int netdev_offload_tc_flow_put(struct netdev *, struct match *, + struct nlattr *actions, size_t actions_len, + const ovs_u128 *ufid, struct offload_info *, + struct dpif_flow_stats *); +int netdev_offload_tc_flow_del(const ovs_u128 *ufid, struct dpif_flow_stats *); +int netdev_offload_tc_flow_get(struct netdev *, struct match *, + struct nlattr **actions, const ovs_u128 *ufid, + struct dpif_flow_stats *, + struct dpif_flow_attrs *, struct ofpbuf *); void dpif_offload_tc_meter_init(void); int dpif_offload_tc_meter_set(const struct dpif_offload *, ofproto_meter_id, struct ofputil_meter_config *); diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h index 4cdf7102f..0dfebae1b 100644 --- a/lib/netdev-offload.h +++ b/lib/netdev-offload.h @@ -71,6 +71,7 @@ struct offload_info { */ uint32_t flow_mark; + bool tc_modify_flow; /* Indicates tc modified the flow. */ bool tc_modify_flow_deleted; /* Indicate the tc modify flow put success * to delete the original flow. */ odp_port_t orig_in_port; /* Originating in_port for tnl flows. */ -- 2.50.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
