@@ -453,6 +453,9 @@ struct dp_netdev_flow {
struct ovs_refcount ref_cnt;
bool dead;
+ bool has_mark; /* A flag to tell whether this flow has a
+ valid mark asscoiated with it. */
+ uint32_t mark; /* Unique flow mark assiged to a flow */
/* Statistics. */
struct dp_netdev_flow_stats stats;
@@ -1833,6 +1836,82 @@ dp_netdev_pmd_find_dpcls(struct dp_netdev_pmd_thread *pmd,
return cls;
}
+/*
+ * An array to map from the flow mark (the index) to dp netdev flow.
+ * Initial size is set to 1024, it will be resized when it's not
+ * big enough.
+ */
+struct flow_mark_map {
+ size_t max;
+ struct ovs_mutex mutex;
+ struct id_pool *mark_pool;
+ struct dp_netdev_flow **flows;
+};
+
+static struct flow_mark_map flow_mark_map = {
+ .max = 1024,
+ .mutex = OVS_MUTEX_INITIALIZER,
+};
+
+static bool
+dp_netdev_alloc_flow_mark(uint32_t *mark)
+{
+ bool succeed = true;
+
+ ovs_mutex_lock(&flow_mark_map.mutex);
+
+ /* Haven't initiated yet, do it here */
+ if (!flow_mark_map.flows) {
+ flow_mark_map.flows = xzalloc(sizeof(struct dp_netdev_flow *) *
+ flow_mark_map.max);
+ flow_mark_map.mark_pool = id_pool_create(0, UINT32_MAX / 2);
+ }
+
+ do {
+ if (!id_pool_alloc_id(flow_mark_map.mark_pool, mark)) {
+ succeed = false;
+ break;
+ }
+
+ if (*mark >= flow_mark_map.max) {
+ flow_mark_map.flows = xrealloc(flow_mark_map.flows,
+ flow_mark_map.max * 2 *
+ sizeof(struct dp_netdev_flow *));
+ memset(&flow_mark_map.flows[flow_mark_map.max], 0,
+ flow_mark_map.max * sizeof(struct dp_netdev_flow *));
+ flow_mark_map.max *= 2;
+
+ break;
+ }
+ } while (flow_mark_map.flows[*mark]);
+
+ ovs_mutex_unlock(&flow_mark_map.mutex);
+
+ return succeed;
+}
+
+static void
+dp_netdev_install_flow_mark_map(const uint32_t mark,
+ struct dp_netdev_flow *netdev_flow)
+{
+ ovs_mutex_lock(&flow_mark_map.mutex);
+ if (mark < flow_mark_map.max) {
+ flow_mark_map.flows[mark] = netdev_flow;
+ }
+ ovs_mutex_unlock(&flow_mark_map.mutex);
+}
+
+static void
+dp_netdev_remove_flow_mark_map(const uint32_t mark)
+{
+ ovs_mutex_lock(&flow_mark_map.mutex);
+ id_pool_free_id(flow_mark_map.mark_pool, mark);
+ if (mark < flow_mark_map.max) {
+ flow_mark_map.flows[mark] = NULL;
+ }
+ ovs_mutex_unlock(&flow_mark_map.mutex);
+}
+
static void
dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd,
struct dp_netdev_flow *flow)
@@ -1846,6 +1925,10 @@ dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd,
ovs_assert(cls != NULL);
dpcls_remove(cls, &flow->cr);
cmap_remove(&pmd->flow_table, node, dp_netdev_flow_hash(&flow->ufid));
+ if (flow->has_mark) {
+ dp_netdev_remove_flow_mark_map(flow->mark);
+ flow->has_mark = false;
+ }
flow->dead = true;
dp_netdev_flow_unref(flow);
@@ -2425,6 +2508,55 @@ out:
return error;
}
+static void
+try_netdev_flow_put(struct dp_netdev_pmd_thread *pmd, odp_port_t in_port,
+ struct dp_netdev_flow *flow, struct match *match,
+ const ovs_u128 *ufid, const struct nlattr *actions,
+ size_t actions_len)
+{
+ struct offload_info info;
+ struct dp_netdev_port *port;
+ bool modification = flow->has_mark;
+ const char *op = modification ? "modify" : "install";
+ int ret;
+
+ port = dp_netdev_lookup_port(pmd->dp, in_port);
+ if (!port) {
+ return;
+ }
+
+ if (modification) {
+ info.flow_mark = flow->mark;
+ } else {
+ if (!netdev_is_flow_api_enabled()) {
+ return;
+ }
+
+ if (!dp_netdev_alloc_flow_mark(&info.flow_mark)) {
+ VLOG_ERR("failed to allocate flow mark!\n");
+ return;
+ }
+ }
+
+ ret = netdev_flow_put(port->netdev, match,
+ CONST_CAST(struct nlattr *, actions),
+ actions_len, ufid, &info, NULL);
+ if (ret) {
+ VLOG_ERR("failed to %s netdev flow with mark %u\n",
+ op, info.flow_mark);
+ return;
+ }
+
+ if (!modification) {
+ flow->has_mark = true;
+ flow->mark = info.flow_mark;
+ dp_netdev_install_flow_mark_map(info.flow_mark, flow);
+ }
+
+ VLOG_INFO("succeed to %s netdev flow with mark %u\n",
+ op, flow->mark);
+}
+
static struct dp_netdev_flow *
dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
struct match *match, const ovs_u128 *ufid,
@@ -2460,6 +2592,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
memset(&flow->stats, 0, sizeof flow->stats);
flow->dead = false;
flow->batch = NULL;
+ flow->has_mark = false;
*CONST_CAST(unsigned *, &flow->pmd_id) = pmd->core_id;
*CONST_CAST(struct flow *, &flow->flow) = match->flow;
*CONST_CAST(ovs_u128 *, &flow->ufid) = *ufid;
@@ -2475,6 +2608,9 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
cmap_insert(&pmd->flow_table, CONST_CAST(struct cmap_node *, &flow->node),
dp_netdev_flow_hash(&flow->ufid));
+ try_netdev_flow_put(pmd, in_port, flow, match, ufid,
+ actions, actions_len);
+
if (OVS_UNLIKELY(!VLOG_DROP_DBG((&upcall_rl)))) {
struct ds ds = DS_EMPTY_INITIALIZER;
struct ofpbuf key_buf, mask_buf;
@@ -2555,6 +2691,7 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd,
if (put->flags & DPIF_FP_MODIFY) {
struct dp_netdev_actions *new_actions;
struct dp_netdev_actions *old_actions;
+ odp_port_t in_port = netdev_flow->flow.in_port.odp_port;
new_actions = dp_netdev_actions_create(put->actions,
put->actions_len);
@@ -2562,6 +2699,9 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd,
old_actions = dp_netdev_flow_get_actions(netdev_flow);
ovsrcu_set(&netdev_flow->actions, new_actions);
+ try_netdev_flow_put(pmd, in_port, netdev_flow, match, ufid,
+ put->actions, put->actions_len);
+
if (stats) {
get_dpif_flow_stats(netdev_flow, stats);
}
@@ -188,6 +188,12 @@ void netdev_send_wait(struct netdev *, int qid);
struct offload_info {
const struct dpif_class *dpif_class;
ovs_be16 tp_dst_port; /* Destination port for tunnel in SET action */
+
+ /*
+ * The flow mark id assigened to the flow. If any pkts hit the flow,
+ * it will be in the pkt meta data.
+ */
+ uint32_t flow_mark;
};
struct dpif_class;
struct netdev_flow_dump;