@@ -143,6 +143,9 @@ struct ovs_vport_stats {
/* Allow datapath to associate multiple Netlink PIDs to each vport */
#define OVS_DP_F_VPORT_PIDS (1 << 1)
+/* Allow tc offload recirc sharing */
+#define OVS_DP_F_TC_RECIRC_SHARING (1 << 2)
+
/* Fixed logical ports. */
#define OVSP_LOCAL ((__u32)0)
@@ -7535,6 +7535,7 @@ const struct dpif_class dpif_netdev_class = {
dpif_netdev_run,
dpif_netdev_wait,
dpif_netdev_get_stats,
+ NULL, /* set_features */
dpif_netdev_port_add,
dpif_netdev_port_del,
dpif_netdev_port_set_config,
@@ -193,6 +193,7 @@ struct dpif_handler {
struct dpif_netlink {
struct dpif dpif;
int dp_ifindex;
+ uint32_t user_features;
/* Upcall messages. */
struct fat_rwlock upcall_lock;
@@ -334,15 +335,26 @@ dpif_netlink_open(const struct dpif_class *class OVS_UNUSED, const char *name,
/* Create or look up datapath. */
dpif_netlink_dp_init(&dp_request);
+ upcall_pid = 0;
+ dp_request.upcall_pid = &upcall_pid;
+ dp_request.name = name;
+
if (create) {
dp_request.cmd = OVS_DP_CMD_NEW;
- upcall_pid = 0;
- dp_request.upcall_pid = &upcall_pid;
} else {
+ dp_request.cmd = OVS_DP_CMD_GET;
+
+ error = dpif_netlink_dp_transact(&dp_request, &dp, &buf);
+ if (error) {
+ return error;
+ }
+ dp_request.user_features = dp.user_features;
+ ofpbuf_delete(buf);
+
/* Use OVS_DP_CMD_SET to report user features */
dp_request.cmd = OVS_DP_CMD_SET;
}
- dp_request.name = name;
+
dp_request.user_features |= OVS_DP_F_UNALIGNED;
dp_request.user_features |= OVS_DP_F_VPORT_PIDS;
error = dpif_netlink_dp_transact(&dp_request, &dp, &buf);
@@ -368,6 +380,7 @@ open_dpif(const struct dpif_netlink_dp *dp, struct dpif **dpifp)
dp->dp_ifindex, dp->dp_ifindex);
dpif->dp_ifindex = dp->dp_ifindex;
+ dpif->user_features = dp->user_features;
*dpifp = &dpif->dpif;
return 0;
@@ -664,6 +677,31 @@ dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
return error;
}
+static int
+dpif_netlink_set_features(struct dpif *dpif_, uint32_t new_features)
+{
+ struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
+ struct dpif_netlink_dp request, reply;
+ struct ofpbuf *bufp;
+ int error;
+
+ dpif_netlink_dp_init(&request);
+ request.cmd = OVS_DP_CMD_SET;
+ request.dp_ifindex = dpif->dp_ifindex;
+ request.user_features = dpif->user_features | new_features;
+
+ error = dpif_netlink_dp_transact(&request, &reply, &bufp);
+ if (!error) {
+ dpif->user_features = reply.user_features;
+ ofpbuf_delete(bufp);
+ if (!(dpif->user_features & new_features)) {
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return error;
+}
+
static const char *
get_vport_type(const struct dpif_netlink_vport *vport)
{
@@ -1991,7 +2029,6 @@ static int
parse_flow_put(struct dpif_netlink *dpif, struct dpif_flow_put *put)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
- const struct dpif_class *dpif_class = dpif->dpif.dpif_class;
struct match match;
odp_port_t in_port;
const struct nlattr *nla;
@@ -2013,7 +2050,7 @@ parse_flow_put(struct dpif_netlink *dpif, struct dpif_flow_put *put)
}
in_port = match.flow.in_port.odp_port;
- dev = netdev_ports_get(in_port, dpif_class);
+ dev = netdev_ports_get(in_port, dpif->dpif.dpif_class);
if (!dev) {
return EOPNOTSUPP;
}
@@ -2026,7 +2063,7 @@ parse_flow_put(struct dpif_netlink *dpif, struct dpif_flow_put *put)
odp_port_t out_port;
out_port = nl_attr_get_odp_port(nla);
- outdev = netdev_ports_get(out_port, dpif_class);
+ outdev = netdev_ports_get(out_port, dpif->dpif.dpif_class);
if (!outdev) {
err = EOPNOTSUPP;
goto out;
@@ -2042,7 +2079,7 @@ parse_flow_put(struct dpif_netlink *dpif, struct dpif_flow_put *put)
}
}
- info.dpif_class = dpif_class;
+ info.dpif = &dpif->dpif;
info.tp_dst_port = dst_port;
info.tunnel_csum_on = csum_on;
err = netdev_flow_put(dev, &match,
@@ -3884,6 +3921,7 @@ const struct dpif_class dpif_netlink_class = {
dpif_netlink_run,
NULL, /* wait */
dpif_netlink_get_stats,
+ dpif_netlink_set_features,
dpif_netlink_port_add,
dpif_netlink_port_del,
NULL, /* port_set_config */
@@ -4201,6 +4239,9 @@ dpif_netlink_dp_from_ofpbuf(struct dpif_netlink_dp *dp, const struct ofpbuf *buf
[OVS_DP_ATTR_MEGAFLOW_STATS] = {
NL_POLICY_FOR(struct ovs_dp_megaflow_stats),
.optional = true },
+ [OVS_DP_ATTR_USER_FEATURES] = {
+ .type = NL_A_U32,
+ .optional = true },
};
dpif_netlink_dp_init(dp);
@@ -4229,6 +4270,10 @@ dpif_netlink_dp_from_ofpbuf(struct dpif_netlink_dp *dp, const struct ofpbuf *buf
dp->megaflow_stats = nl_attr_get(a[OVS_DP_ATTR_MEGAFLOW_STATS]);
}
+ if (a[OVS_DP_ATTR_USER_FEATURES]) {
+ dp->user_features = nl_attr_get_u32(a[OVS_DP_ATTR_USER_FEATURES]);
+ }
+
return 0;
}
@@ -188,6 +188,8 @@ struct dpif_class {
/* Retrieves statistics for 'dpif' into 'stats'. */
int (*get_stats)(const struct dpif *dpif, struct dpif_dp_stats *stats);
+ int (*set_features)(struct dpif *dpif, uint32_t user_features);
+
/* Adds 'netdev' as a new port in 'dpif'. If '*port_no' is not
* ODPP_NONE, attempts to use that as the port's port number.
*
@@ -543,6 +543,15 @@ dpif_get_dp_stats(const struct dpif *dpif, struct dpif_dp_stats *stats)
return error;
}
+int
+dpif_set_features(struct dpif *dpif, uint32_t new_features)
+{
+ int error = dpif->dpif_class->set_features(dpif, new_features);
+
+ log_operation(dpif, "set_features", error);
+ return error;
+}
+
const char *
dpif_port_open_type(const char *datapath_type, const char *port_type)
{
@@ -435,6 +435,8 @@ struct dpif_dp_stats {
};
int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *);
+int dpif_set_features(struct dpif *, uint32_t new_features);
+
/* Port operations. */
@@ -38,6 +38,7 @@
#include "tc.h"
#include "unaligned.h"
#include "util.h"
+#include "dpif-provider.h"
VLOG_DEFINE_THIS_MODULE(netdev_offload_tc);
@@ -1354,6 +1355,25 @@ flower_match_to_tun_opt(struct tc_flower *flower, const struct flow_tnl *tnl,
flower->mask.tunnel.metadata.present.len = tnl->metadata.present.len;
}
+static bool
+recirc_id_sharing_support(struct dpif *dpif)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ static bool supported = false;
+ int err;
+
+ if (ovsthread_once_start(&once)) {
+ err = dpif_set_features(dpif, OVS_DP_F_TC_RECIRC_SHARING);
+ supported = err ? false : true;
+ if (supported) {
+ VLOG_INFO("probe tc: tc recirc id sharing with OvS datapath is supported.");
+ }
+ ovsthread_once_done(&once);
+ }
+
+ return supported;
+}
+
static int
netdev_tc_flow_put(struct netdev *netdev, struct match *match,
struct nlattr *actions, size_t actions_len,
@@ -1371,7 +1391,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
uint32_t block_id = 0;
struct nlattr *nla;
struct tc_id id;
- uint32_t chain;
+ uint32_t chain = 0;
size_t left;
int prio = 0;
int ifindex;
@@ -1386,7 +1406,13 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
memset(&flower, 0, sizeof flower);
- chain = key->recirc_id;
+ if (key->recirc_id) {
+ if (recirc_id_sharing_support(info->dpif)) {
+ chain = key->recirc_id;
+ } else {
+ return EOPNOTSUPP;
+ }
+ }
mask->recirc_id = 0;
if (flow_tnl_dst_is_set(&key->tunnel)) {
@@ -1634,7 +1660,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
action = &flower.actions[flower.action_count];
if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
odp_port_t port = nl_attr_get_odp_port(nla);
- struct netdev *outdev = netdev_ports_get(port, info->dpif_class);
+ struct netdev *outdev = netdev_ports_get(port,
+ info->dpif->dpif_class);
action->out.ifindex_out = netdev_get_ifindex(outdev);
action->out.ingress = is_internal_port(netdev_get_type(outdev));
@@ -1700,6 +1727,9 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
action->ct.clear = true;
flower.action_count++;
} else if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) {
+ if (!recirc_id_sharing_support(info->dpif)) {
+ return EOPNOTSUPP;
+ }
action->type = TC_ACT_GOTO;
action->chain = nl_attr_get_u32(nla);
flower.action_count++;
@@ -62,7 +62,7 @@ struct netdev_flow_dump {
/* Flow offloading. */
struct offload_info {
- const struct dpif_class *dpif_class;
+ struct dpif *dpif;
ovs_be16 tp_dst_port; /* Destination port for tunnel in SET action */
uint8_t tunnel_csum_on; /* Tunnel header with checksum */