[ovs-dev] [recirc fix V2] recirculation: Map recirc_id to ofproto_dpif.
Alex Wang
alexw at nicira.com
Mon Dec 22 21:13:49 UTC 2014
Thx for the review,
Forgot to mention, that I'll add (both in code and in commit log) that this
will not fix the case where the recv_bridge is not the same as
recirc_bridge, and rules matching in_port are installed on recirc_bridge.
(since in that case, the in_port is set to OFPP_NONE)
Thanks,
Alex Wang,
On Mon, Dec 22, 2014 at 11:39 AM, Andy Zhou <azhou at nicira.com> wrote:
> Acked-by: Andy Zhou <azhou at nicira.com>
>
> On Sun, Dec 21, 2014 at 9:58 AM, Alex Wang <alexw at nicira.com> wrote:
> > After commit 0c7812e5e (recirculation: Do not drop packet when
> > there is no match from internal table.), if flow keys are modified
> > before the recirculation action (e.g. set vlan ID), the miss
> > handling of recirc'ed packets may not reach the intended
> > 'ofproto_dpif' which has rules looking up the 'recirc_id's,
> > causing drops.
> >
> > This commit adds an unittest that captures this bug. Moreover,
> > to solve this bug, this commit checks mapping between 'recirc_id'
> > and the corresponding 'ofproto_dpif', and makes sure that the
> > miss handling of recirc'ed packets are done with the correct
> > 'ofproto_dpif'.
> >
> > Signed-off-by: Alex Wang <alexw at nicira.com>
> >
> > ---
> > PATCH->V2:
> > - move the recirc_ofproto check to xlate_lookup_ofproto_().
> > - use VLOG_ERR to notify recirc_id leak.
> > - add check for recirc_id_alloc failure.
> > - clarify the unittest.
> > ---
> > ofproto/ofproto-dpif-xlate.c | 48 +++++++++++++++++++---------
> > ofproto/ofproto-dpif.c | 71
> ++++++++++++++++++++++++++++++++++++++++--
> > ofproto/ofproto-dpif.h | 2 ++
> > tests/ofproto-dpif.at | 58 ++++++++++++++++++++++++++++++++++
> > 4 files changed, 162 insertions(+), 17 deletions(-)
> >
> > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> > index 4cef550..cee6d32 100644
> > --- a/ofproto/ofproto-dpif-xlate.c
> > +++ b/ofproto/ofproto-dpif-xlate.c
> > @@ -980,18 +980,41 @@ static struct ofproto_dpif *
> > xlate_lookup_ofproto_(const struct dpif_backer *backer, const struct
> flow *flow,
> > ofp_port_t *ofp_in_port, const struct xport
> **xportp)
> > {
> > + struct ofproto_dpif *recv_ofproto = NULL;
> > + struct ofproto_dpif *recirc_ofproto = NULL;
> > const struct xport *xport;
> > + ofp_port_t in_port = OFPP_NONE;
> >
> > *xportp = xport = xlate_lookup_xport(backer, flow);
> >
> > if (xport) {
> > - if (ofp_in_port) {
> > - *ofp_in_port = xport->ofp_port;
> > + recv_ofproto = xport->xbridge->ofproto;
> > + in_port = xport->ofp_port;
> > + }
> > +
> > + /* When recirc_id is set in 'flow', checks whether the ofproto_dpif
> that
> > + * corresponds to the recirc_id is same as the receiving bridge.
> If they
> > + * are the same, uses the 'recv_ofproto' and keeps the
> 'ofp_in_port' as
> > + * assigned. Otherwise, uses the 'recirc_ofproto' that owns
> recirc_id and
> > + * assigns OFPP_NONE to 'ofp_in_port'. Doing this is in that, for
> the
> > + * latter case, post recirculation flow are not allowed to cross a
> patch
> > + * port, and the rules that match the recirculated packets are
> installed
> > + * only to the 'recirc_ofproto'.
> > + */
> > + if (recv_ofproto && flow->recirc_id) {
> > + recirc_ofproto = ofproto_dpif_recirc_get_ofproto(backer,
> > +
> flow->recirc_id);
> > + if (recv_ofproto != recirc_ofproto) {
> > + *xportp = xport = NULL;
> > + in_port = OFPP_NONE;
> > }
> > - return xport->xbridge->ofproto;
> > }
> >
> > - return NULL;
> > + if (ofp_in_port) {
> > + *ofp_in_port = in_port;
> > + }
> > +
> > + return xport ? recv_ofproto : recirc_ofproto;
> > }
> >
> > /* Given a datapath and flow metadata ('backer', and 'flow'
> respectively)
> > @@ -1012,9 +1035,7 @@ xlate_lookup_ofproto(const struct dpif_backer
> *backer, const struct flow *flow,
> > * pointers until quiescing, for longer term use additional references
> must
> > * be taken.
> > *
> > - * '*ofp_in_port' is set to OFPP_NONE if 'flow''s in_port does not
> exist.
> > - *
> > - * Returns 0 if successful, ENODEV if the parsed flow has no associated
> ofport.
> > + * Returns 0 if successful, ENODEV if the parsed flow has no associated
> ofproto.
> > */
> > int
> > xlate_lookup(const struct dpif_backer *backer, const struct flow *flow,
> > @@ -1027,11 +1048,7 @@ xlate_lookup(const struct dpif_backer *backer,
> const struct flow *flow,
> >
> > ofproto = xlate_lookup_ofproto_(backer, flow, ofp_in_port, &xport);
> >
> > - if (ofp_in_port && !xport) {
> > - *ofp_in_port = OFPP_NONE;
> > - }
> > -
> > - if (!xport) {
> > + if (!ofproto) {
> > return ENODEV;
> > }
> >
> > @@ -1040,16 +1057,17 @@ xlate_lookup(const struct dpif_backer *backer,
> const struct flow *flow,
> > }
> >
> > if (ipfix) {
> > - *ipfix = xport->xbridge->ipfix;
> > + *ipfix = xport ? xport->xbridge->ipfix : NULL;
> > }
> >
> > if (sflow) {
> > - *sflow = xport->xbridge->sflow;
> > + *sflow = xport ? xport->xbridge->sflow : NULL;
> > }
> >
> > if (netflow) {
> > - *netflow = xport->xbridge->netflow;
> > + *netflow = xport ? xport->xbridge->netflow : NULL;
> > }
> > +
> > return 0;
> > }
> >
> > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> > index 81c2c6d..fd73e37 100644
> > --- a/ofproto/ofproto-dpif.c
> > +++ b/ofproto/ofproto-dpif.c
> > @@ -251,6 +251,13 @@ COVERAGE_DEFINE(rev_flow_table);
> > COVERAGE_DEFINE(rev_mac_learning);
> > COVERAGE_DEFINE(rev_mcast_snooping);
> >
> > +/* Stores mapping between 'recirc_id' and 'ofproto-dpif'. */
> > +struct dpif_backer_recirc_node {
> > + struct cmap_node cmap_node;
> > + struct ofproto_dpif *ofproto;
> > + uint32_t recirc_id;
> > +};
> > +
> > /* All datapaths of a given type share a single dpif backer instance. */
> > struct dpif_backer {
> > char *type;
> > @@ -269,6 +276,8 @@ struct dpif_backer {
> >
> > /* Recirculation. */
> > struct recirc_id_pool *rid_pool; /* Recirculation ID pool. */
> > + struct cmap recirc_map; /* Map of 'recirc_id's to
> 'ofproto's. */
> > + struct ovs_mutex recirc_mutex; /* Protects 'recirc_map'. */
> > bool enable_recirc; /* True if the datapath supports
> recirculation */
> >
> > /* True if the datapath supports unique flow identifiers */
> > @@ -844,6 +853,26 @@ dealloc(struct ofproto *ofproto_)
> > free(ofproto);
> > }
> >
> > +/* Called when 'ofproto' is destructed. Clears all the 'recirc_id's
> > + * owned by 'ofproto'. */
> > +static void
> > +dpif_backer_recirc_clear_ofproto(struct dpif_backer *backer,
> > + struct ofproto_dpif *ofproto)
> > +{
> > + struct dpif_backer_recirc_node *node;
> > +
> > + ovs_mutex_lock(&backer->recirc_mutex);
> > + CMAP_FOR_EACH (node, cmap_node, &backer->recirc_map) {
> > + if (node->ofproto == ofproto) {
> > + VLOG_ERR("recirc_id %"PRIu32", not freed when ofproto (%s) "
> > + "is destructed", node->recirc_id, ofproto->up.name
> );
> > + cmap_remove(&backer->recirc_map, &node->cmap_node,
> > + node->recirc_id);
> > + }
> > + }
> > + ovs_mutex_unlock(&backer->recirc_mutex);
> > +}
> > +
> > static void
> > close_dpif_backer(struct dpif_backer *backer)
> > {
> > @@ -860,6 +889,8 @@ close_dpif_backer(struct dpif_backer *backer)
> > hmap_destroy(&backer->odp_to_ofport_map);
> > shash_find_and_delete(&all_dpif_backers, backer->type);
> > recirc_id_pool_destroy(backer->rid_pool);
> > + cmap_destroy(&backer->recirc_map);
> > + ovs_mutex_destroy(&backer->recirc_mutex);
> > free(backer->type);
> > free(backer->dp_version_string);
> > dpif_close(backer->dpif);
> > @@ -975,6 +1006,8 @@ open_dpif_backer(const char *type, struct
> dpif_backer **backerp)
> > backer->masked_set_action = check_masked_set_action(backer);
> > backer->enable_ufid = check_ufid(backer);
> > backer->rid_pool = recirc_id_pool_create();
> > + ovs_mutex_init(&backer->recirc_mutex);
> > + cmap_init(&backer->recirc_map);
> >
> > backer->enable_tnl_push_pop =
> dpif_supports_tnl_push_pop(backer->dpif);
> > atomic_count_init(&backer->tnl_count, 0);
> > @@ -1423,6 +1456,8 @@ destruct(struct ofproto *ofproto_)
> > }
> > guarded_list_destroy(&ofproto->pins);
> >
> > + dpif_backer_recirc_clear_ofproto(ofproto->backer, ofproto);
> > +
> > mbridge_unref(ofproto->mbridge);
> >
> > netflow_unref(ofproto->netflow);
> > @@ -5376,20 +5411,52 @@ odp_port_to_ofp_port(const struct ofproto_dpif
> *ofproto, odp_port_t odp_port)
> > }
> > }
> >
> > +struct ofproto_dpif *
> > +ofproto_dpif_recirc_get_ofproto(const struct dpif_backer *backer,
> > + uint32_t recirc_id)
> > +{
> > + struct dpif_backer_recirc_node *node;
> > +
> > + node = CONTAINER_OF(cmap_find(&backer->recirc_map, recirc_id),
> > + struct dpif_backer_recirc_node, cmap_node);
> > +
> > + return node ? node->ofproto : NULL;
> > +}
> > +
> > uint32_t
> > ofproto_dpif_alloc_recirc_id(struct ofproto_dpif *ofproto)
> > {
> > struct dpif_backer *backer = ofproto->backer;
> > + uint32_t recirc_id = recirc_id_alloc(backer->rid_pool);
> >
> > - return recirc_id_alloc(backer->rid_pool);
> > + if (recirc_id) {
> > + struct dpif_backer_recirc_node *node = xmalloc(sizeof *node);
> > +
> > + node->recirc_id = recirc_id;
> > + node->ofproto = ofproto;
> > +
> > + ovs_mutex_lock(&backer->recirc_mutex);
> > + cmap_insert(&backer->recirc_map, &node->cmap_node,
> node->recirc_id);
> > + ovs_mutex_unlock(&backer->recirc_mutex);
> > + }
> > +
> > + return recirc_id;
> > }
> >
> > void
> > ofproto_dpif_free_recirc_id(struct ofproto_dpif *ofproto, uint32_t
> recirc_id)
> > {
> > struct dpif_backer *backer = ofproto->backer;
> > + struct dpif_backer_recirc_node *node;
> >
> > - recirc_id_free(backer->rid_pool, recirc_id);
> > + node = CONTAINER_OF(cmap_find(&backer->recirc_map, recirc_id),
> > + struct dpif_backer_recirc_node, cmap_node);
> > + if (node) {
> > + ovs_mutex_lock(&backer->recirc_mutex);
> > + cmap_remove(&backer->recirc_map, &node->cmap_node,
> node->recirc_id);
> > + ovs_mutex_unlock(&backer->recirc_mutex);
> > + recirc_id_free(backer->rid_pool, node->recirc_id);
> > + }
> > }
> >
> > int
> > diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> > index 1170c33..13a47cf 100644
> > --- a/ofproto/ofproto-dpif.h
> > +++ b/ofproto/ofproto-dpif.h
> > @@ -219,6 +219,8 @@ struct ofport_dpif *odp_port_to_ofport(const struct
> dpif_backer *, odp_port_t);
> > * work the same way as regular flows.
> > */
> >
> > +struct ofproto_dpif *ofproto_dpif_recirc_get_ofproto(const struct
> dpif_backer *ofproto,
> > + uint32_t
> recirc_id);
> > uint32_t ofproto_dpif_alloc_recirc_id(struct ofproto_dpif *ofproto);
> > void ofproto_dpif_free_recirc_id(struct ofproto_dpif *ofproto, uint32_t
> recirc_id);
> > int ofproto_dpif_add_internal_flow(struct ofproto_dpif *,
> > diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> > index 54a5209..55c3e90 100644
> > --- a/tests/ofproto-dpif.at
> > +++ b/tests/ofproto-dpif.at
> > @@ -144,6 +144,64 @@ AT_CHECK([ovs-appctl dpif/dump-flows br1 |grep tcp
> > br1_flows.txt])
> > AT_CHECK([test `grep in_port.4 br1_flows.txt |wc -l` -gt 24])
> > AT_CHECK([test `grep in_port.5 br1_flows.txt |wc -l` -gt 24])
> > AT_CHECK([test `grep in_port.6 br1_flows.txt |wc -l` -gt 24])
> > +
> > +OVS_VSWITCHD_STOP()
> > +AT_CLEANUP
> > +
> > +# Makes sure recirculation does not change the way packet is handled.
> > +AT_SETUP([ofproto-dpif, balance-tcp bonding, different recirc flow ])
> > +OVS_VSWITCHD_START(
> > + [add-bond br0 bond0 p1 p2 bond_mode=balance-tcp lacp=active \
> > + other-config:lacp-time=fast
> other-config:bond-rebalance-interval=0 -- \
> > + set interface p1 type=dummy
> options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \
> > + set interface p2 type=dummy
> options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \
> > + add-br br1 -- \
> > + set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
> > + set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
> > + fail-mode=standalone -- \
> > + add-bond br1 bond1 p3 p4 bond_mode=balance-tcp lacp=active \
> > + other-config:lacp-time=fast
> other-config:bond-rebalance-interval=0 -- \
> > + set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock
> ofport_request=3 -- \
> > + set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock
> ofport_request=4 -- \
> > + add-port br1 br1- -- set interface br1- type=patch options:peer=br1+
> ofport_request=100 -- \
> > + add-br br-int -- \
> > + set bridge br-int other-config:hwaddr=aa:77:aa:77:00:00 -- \
> > + set bridge br-int datapath-type=dummy other-config:datapath-id=1235 \
> > + fail-mode=secure -- \
> > + add-port br-int br1+ -- set interface br1+ type=patch
> options:peer=br1- ofport_request=101 -- \
> > + add-port br-int p5 -- set interface p5 ofport_request=5 type=dummy
> > +])
> > +AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
> > +])
> > +
> > +# The dl_vlan flow should not be ever matched,
> > +# since recirculation should not change the flow handling.
> > +AT_DATA([flows.txt], [dnl
> > +table=0 priority=1 in_port=5 actions=mod_vlan_vid:1,output(101)
> > +table=0 priority=2 in_port=5 dl_vlan=1 actions=drop
> > +])
> > +AT_CHECK([ovs-ofctl add-flows br-int flows.txt])
> > +
> > +# Sends a packet to trigger recirculation.
> > +# Should generate recirc_id(0x12d),dp_hash(0xc1261ba2/0xff).
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p5
> "in_port(5),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1)"])
> > +
> > +# Collects flow stats.
> > +AT_CHECK([ovs-appctl revalidator/purge], [0])
> > +
> > +# Checks the flow stats in br1, should only be one flow with non-zero
> > +# 'n_packets' from internal table.
> > +AT_CHECK([ovs-appctl bridge/dump-flows br1 | ofctl_strip | grep --
> "n_packets" | grep -- "table_id" | sed -e 's/output:[[0-9]]\+/output/'],
> [0], [dnl
> > +table_id=254, n_packets=1, n_bytes=64,
> priority=20,recirc_id=0x12d,dp_hash=0xa2/0xff,actions=output
> > +])
> > +
> > +# Checks the flow stats in br-int, should be only one match.
> > +AT_CHECK([ovs-ofctl dump-flows br-int | ofctl_strip | sort], [0], [dnl
> > + n_packets=1, n_bytes=60, priority=1,in_port=5
> actions=mod_vlan_vid:1,output:101
> > + priority=2,in_port=5,dl_vlan=1 actions=drop
> > +NXST_FLOW reply:
> > +])
> > +
> > OVS_VSWITCHD_STOP()
> > AT_CLEANUP
> >
> > --
> > 1.7.9.5
> >
>
More information about the dev
mailing list