summaryrefslogtreecommitdiff
path: root/ofproto/ofproto-dpif-upcall.c
diff options
context:
space:
mode:
Diffstat (limited to 'ofproto/ofproto-dpif-upcall.c')
-rw-r--r--ofproto/ofproto-dpif-upcall.c103
1 files changed, 86 insertions, 17 deletions
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 3c80a0c03..01bc382b5 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -152,6 +152,8 @@ enum upcall_type {
struct upcall {
struct ofproto_dpif *ofproto; /* Parent ofproto. */
+ const struct recirc_id_node *recirc; /* Recirculation context. */
+ bool have_recirc_ref; /* Reference held on recirc ctx? */
/* The flow and packet are only required to be constant when using
* dpif-netdev. If a modification is absolutely necessary, a const cast
@@ -229,6 +231,10 @@ struct udpif_key {
struct odputil_keybuf buf;
struct nlattr nla;
} keybuf, maskbuf;
+
+ /* Recirculation IDs with references held by the ukey. */
+ unsigned n_recircs;
+ uint32_t recircs[]; /* 'n_recircs' id's for which references are held. */
};
/* Datapath operation with optional ukey attached. */
@@ -271,7 +277,7 @@ static void upcall_unixctl_dump_wait(struct unixctl_conn *conn, int argc,
static void upcall_unixctl_purge(struct unixctl_conn *conn, int argc,
const char *argv[], void *aux);
-static struct udpif_key *ukey_create_from_upcall(const struct upcall *);
+static struct udpif_key *ukey_create_from_upcall(struct upcall *);
static int ukey_create_from_dpif_flow(const struct udpif *,
const struct dpif_flow *,
struct udpif_key **);
@@ -737,6 +743,8 @@ udpif_revalidator(void *arg)
if (leader) {
uint64_t reval_seq;
+ recirc_run(); /* Recirculation cleanup. */
+
reval_seq = seq_read(udpif->reval_seq);
last_reval_seq = reval_seq;
@@ -903,6 +911,8 @@ upcall_receive(struct upcall *upcall, const struct dpif_backer *backer,
return error;
}
+ upcall->recirc = NULL;
+ upcall->have_recirc_ref = false;
upcall->flow = flow;
upcall->packet = packet;
upcall->ufid = ufid;
@@ -942,9 +952,21 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
if (upcall->type == DPIF_UC_MISS) {
xin.resubmit_stats = &stats;
+
+ if (xin.recirc) {
+ /* We may install a datapath flow only if we get a reference to the
+ * recirculation context (otherwise we could have recirculation
+ * upcalls using recirculation ID for which no context can be
+ * found). We may still execute the flow's actions even if we
+ * don't install the flow. */
+ upcall->recirc = xin.recirc;
+ upcall->have_recirc_ref = recirc_id_node_try_ref_rcu(xin.recirc);
+ }
} else {
- /* For non-miss upcalls, there's a flow in the datapath which this
- * packet was accounted to. Presumably the revalidators will deal
+ /* For non-miss upcalls, we are either executing actions (one of which
+ * is an userspace action) for an upcall, in which case the stats have
+ * already been taken care of, or there's a flow in the datapath which
+ * this packet was accounted to. Presumably the revalidators will deal
* with pushing its stats eventually. */
}
@@ -1005,8 +1027,13 @@ upcall_uninit(struct upcall *upcall)
xlate_out_uninit(&upcall->xout);
}
ofpbuf_uninit(&upcall->put_actions);
- if (!upcall->ukey_persists) {
- ukey_delete__(upcall->ukey);
+ if (upcall->ukey) {
+ if (!upcall->ukey_persists) {
+ ukey_delete__(upcall->ukey);
+ }
+ } else if (upcall->have_recirc_ref) {
+ /* The reference was transferred to the ukey if one was created. */
+ recirc_id_node_unref(upcall->recirc);
}
}
}
@@ -1056,10 +1083,16 @@ upcall_cb(const struct dp_packet *packet, const struct flow *flow, ovs_u128 *ufi
goto out;
}
- if (upcall.ukey && !ukey_install(udpif, upcall.ukey)) {
+ /* Prevent miss flow installation if the key has recirculation ID but we
+ * were not able to get a reference on it. */
+ if (type == DPIF_UC_MISS && upcall.recirc && !upcall.have_recirc_ref) {
error = ENOSPC;
+ goto out;
}
+ if (upcall.ukey && !ukey_install(udpif, upcall.ukey)) {
+ error = ENOSPC;
+ }
out:
if (!error) {
upcall.ukey_persists = true;
@@ -1189,8 +1222,12 @@ handle_upcalls(struct udpif *udpif, struct upcall *upcalls,
* - The datapath already has too many flows.
*
* - We received this packet via some flow installed in the kernel
- * already. */
- if (may_put && upcall->type == DPIF_UC_MISS) {
+ * already.
+ *
+ * - Upcall was a recirculation but we do not have a reference to
+ * to the recirculation ID. */
+ if (may_put && upcall->type == DPIF_UC_MISS &&
+ (!upcall->recirc || upcall->have_recirc_ref)) {
struct udpif_key *ukey = upcall->ukey;
upcall->ukey_persists = true;
@@ -1277,10 +1314,13 @@ ukey_create__(const struct nlattr *key, size_t key_len,
const struct nlattr *mask, size_t mask_len,
bool ufid_present, const ovs_u128 *ufid,
const int pmd_id, const struct ofpbuf *actions,
- uint64_t dump_seq, uint64_t reval_seq, long long int used)
+ uint64_t dump_seq, uint64_t reval_seq, long long int used,
+ const struct recirc_id_node *key_recirc, struct xlate_out *xout)
OVS_NO_THREAD_SAFETY_ANALYSIS
{
- struct udpif_key *ukey = xmalloc(sizeof *ukey);
+ unsigned n_recircs = (key_recirc ? 1 : 0) + (xout ? xout->n_recircs : 0);
+ struct udpif_key *ukey = xmalloc(sizeof *ukey +
+ n_recircs * sizeof *ukey->recircs);
memcpy(&ukey->keybuf, key, key_len);
ukey->key = &ukey->keybuf.nla;
@@ -1303,11 +1343,22 @@ ukey_create__(const struct nlattr *key, size_t key_len,
ukey->stats.used = used;
ukey->xcache = NULL;
+ ukey->n_recircs = n_recircs;
+ if (key_recirc) {
+ ukey->recircs[0] = key_recirc->id;
+ }
+ if (xout && xout->n_recircs) {
+ const uint32_t *act_recircs = xlate_out_get_recircs(xout);
+
+ memcpy(ukey->recircs + (key_recirc ? 1 : 0), act_recircs,
+ xout->n_recircs * sizeof *ukey->recircs);
+ xlate_out_take_recircs(xout);
+ }
return ukey;
}
static struct udpif_key *
-ukey_create_from_upcall(const struct upcall *upcall)
+ukey_create_from_upcall(struct upcall *upcall)
{
struct odputil_keybuf keystub, maskstub;
struct ofpbuf keybuf, maskbuf;
@@ -1337,7 +1388,9 @@ ukey_create_from_upcall(const struct upcall *upcall)
return ukey_create__(keybuf.data, keybuf.size, maskbuf.data, maskbuf.size,
true, upcall->ufid, upcall->pmd_id,
&upcall->put_actions, upcall->dump_seq,
- upcall->reval_seq, 0);
+ upcall->reval_seq, 0,
+ upcall->have_recirc_ref ? upcall->recirc : NULL,
+ &upcall->xout);
}
static int
@@ -1349,12 +1402,15 @@ ukey_create_from_dpif_flow(const struct udpif *udpif,
struct ofpbuf actions;
uint64_t dump_seq, reval_seq;
uint64_t stub[DPIF_FLOW_BUFSIZE / 8];
+ const struct nlattr *a;
+ unsigned int left;
- if (!flow->key_len) {
+ if (!flow->key_len || !flow->actions_len) {
struct ofpbuf buf;
int err;
- /* If the key was not provided by the datapath, fetch the full flow. */
+ /* If the key or actions were not provided by the datapath, fetch the
+ * full flow. */
ofpbuf_use_stack(&buf, &stub, sizeof stub);
err = dpif_flow_get(udpif->dpif, NULL, 0, &flow->ufid,
flow->pmd_id, &buf, &full_flow);
@@ -1363,13 +1419,23 @@ ukey_create_from_dpif_flow(const struct udpif *udpif,
}
flow = &full_flow;
}
+
+ /* Check the flow actions for recirculation action. As recirculation
+ * relies on OVS userspace internal state, we need to delete all old
+ * datapath flows with recirculation upon OVS restart. */
+ NL_ATTR_FOR_EACH_UNSAFE (a, left, flow->actions, flow->actions_len) {
+ if (nl_attr_type(a) == OVS_ACTION_ATTR_RECIRC) {
+ return EINVAL;
+ }
+ }
+
dump_seq = seq_read(udpif->dump_seq);
reval_seq = seq_read(udpif->reval_seq);
ofpbuf_use_const(&actions, &flow->actions, flow->actions_len);
*ukey = ukey_create__(flow->key, flow->key_len,
flow->mask, flow->mask_len, flow->ufid_present,
&flow->ufid, flow->pmd_id, &actions, dump_seq,
- reval_seq, flow->stats.used);
+ reval_seq, flow->stats.used, NULL, NULL);
return 0;
}
@@ -1512,6 +1578,9 @@ ukey_delete__(struct udpif_key *ukey)
OVS_NO_THREAD_SAFETY_ANALYSIS
{
if (ukey) {
+ for (int i = 0; i < ukey->n_recircs; i++) {
+ recirc_free_id(ukey->recircs[i]);
+ }
xlate_cache_delete(ukey->xcache);
ofpbuf_delete(ukey->actions);
ovs_mutex_destroy(&ukey->mutex);
@@ -1776,8 +1845,8 @@ push_ukey_ops__(struct udpif *udpif, struct ukey_op *ops, size_t n_ops)
continue;
}
- error = xlate_lookup(udpif->backer, &flow, &ofproto,
- NULL, NULL, &netflow, &ofp_in_port);
+ error = xlate_lookup(udpif->backer, &flow, &ofproto, NULL, NULL,
+ &netflow, &ofp_in_port);
if (!error) {
struct xlate_in xin;