@@ -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 */
@@ -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,
@@ -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;
@@ -3464,8 +3464,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,
};
@@ -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 *);
@@ -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. */
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 <echaudro@redhat.com> --- 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(-)