[ovs-dev] [port-renumbering 8/8] bridge: Support changing port numbers.

Ben Pfaff blp at nicira.com
Thu Dec 12 06:10:50 UTC 2013


On Wed, Dec 11, 2013 at 02:17:56PM -0800, Jarno Rajahalme wrote:
> 
> On Dec 10, 2013, at 11:20 PM, Ben Pfaff <blp at nicira.com> wrote:
> 
> > Feature #21293.
> > Requested-by: Anuprem Chalvadi <achalvadi at vmware.com>
> > Signed-off-by: Ben Pfaff <blp at nicira.com>
> > ---
> > NEWS                 |    3 ++
> > vswitchd/bridge.c    |   93 ++++++++++++++++++++++++++++++++++++++++++++------
> > vswitchd/vswitch.xml |   21 +++++++-----
> > 3 files changed, 99 insertions(+), 18 deletions(-)
> > 
> > diff --git a/NEWS b/NEWS
> > index 8556083..ce968f2 100644
> > --- a/NEWS
> > +++ b/NEWS
> > @@ -14,6 +14,9 @@ Post-v2.0.0
> >      * OVS limits the OpenFlow port numbers it assigns to port 32767 and
> >        below, leaving port numbers above that range free for assignment
> >        by the controller.
> > +     * ovs-vswitchd now honors changes to the "ofport_request" column
> > +       in the Interface table by changing the port's OpenFlow port
> > +       number.
> >    - ovs-vswitchd.conf.db.5 man page will contain graphviz/dot
> >      diagram only if graphviz package was installed at the build time.
> >    - Support for Linux kernels up to 3.11
> > diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
> > index 581f87c..131b800 100644
> > --- a/vswitchd/bridge.c
> > +++ b/vswitchd/bridge.c
> > @@ -75,6 +75,7 @@ struct iface {
> >     char *name;                 /* Host network device name. */
> >     struct netdev *netdev;      /* Network device. */
> >     ofp_port_t ofp_port;        /* OpenFlow port number. */
> > +    ofp_port_t requested_ofp_port; /* Port number requested previously. */
> > 
> >     /* These members are valid only within bridge_reconfigure(). */
> >     const char *type;           /* Usually same as cfg->type. */
> > @@ -247,6 +248,8 @@ static void iface_refresh_cfm_stats(struct iface *);
> > static void iface_refresh_stats(struct iface *);
> > static void iface_refresh_status(struct iface *);
> > static bool iface_is_synthetic(const struct iface *);
> > +static ofp_port_t iface_get_requested_ofp_port(
> > +    const struct ovsrec_interface *);
> > static ofp_port_t iface_pick_ofport(const struct ovsrec_interface *);
> > 
> > /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
> > @@ -636,6 +639,7 @@ bridge_delete_ports(struct bridge *br)
> >     n = allocated = 0;
> > 
> >     OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
> > +        ofp_port_t requested_ofp_port;
> >         struct iface *iface;
> > 
> >         iface = iface_lookup(br, ofproto_port.name);
> > @@ -657,6 +661,43 @@ bridge_delete_ports(struct bridge *br)
> >             goto delete;
> >         }
> > 
> > +        /* If the requested OpenFlow port for 'iface' changed, and it's not
> > +         * already the correct port, then we might want to temporarily delete
> > +         * this interface, so we can add it back again with the new OpenFlow
> > +         * port number. */
> > +        requested_ofp_port = iface_get_requested_ofp_port(iface->cfg);
> > +        if (requested_ofp_port != iface->requested_ofp_port &&
> > +            requested_ofp_port != OFPP_NONE &&
> > +            requested_ofp_port != iface->ofp_port) {
> 
> It would seem that this test should fail if the face requested a port
> number that it did not get (for any reason), and is still requesting
> the same port number, but the offending other port was deleted and the
> (originally) requested port number would now be available. However,
> when testing this, this seems to work:
> 
> - create ports p1, p2
> - request port number 10 for p1 -> p1 changes to 10
> - request port number 10 for p2 -> p2 does not change
> - request port number 11 for p1 -> p1 changes to 11 AND p2 changes to 10
> 
> Is it so that the requested port number is not stored unless it is
> also gotten?
> 
>  Maybe this is intended behavior to avoid surprisingly changing port
>  numbers, even if the change would be towards the wanted
>  configuration?

My intent here was actually to avoid churn in some theoretical ofproto
implementation which limits (or entirely fixes) the port numbers that
certain ports can have, by only trying a given ofport_request once until
it changes.  But that may not be a real problem (I don't know of any
ofprotos that actually work that way), whereas the problem you describe
is one that I would like to avoid in reality.

I folded this in:

diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 37f5812..f3b03af 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -75,7 +75,6 @@ struct iface {
     char *name;                 /* Host network device name. */
     struct netdev *netdev;      /* Network device. */
     ofp_port_t ofp_port;        /* OpenFlow port number. */
-    ofp_port_t requested_ofp_port; /* Port number requested previously. */
 
     /* These members are valid only within bridge_reconfigure(). */
     const char *type;           /* Usually same as cfg->type. */
@@ -684,7 +683,7 @@ bridge_delete_or_reconfigure_ports(struct bridge *br)
          * this interface, so we can add it back again with the new OpenFlow
          * port number. */
         requested_ofp_port = iface_get_requested_ofp_port(iface->cfg);
-        if (requested_ofp_port != iface->requested_ofp_port &&
+        if (iface->ofp_port != OFPP_LOCAL &&
             requested_ofp_port != OFPP_NONE &&
             requested_ofp_port != iface->ofp_port) {
             ofp_port_t victim_request;
@@ -1494,7 +1493,6 @@ iface_create(struct bridge *br, const struct ovsrec_interface *iface_cfg,
     iface->port = port;
     iface->name = xstrdup(iface_cfg->name);
     iface->ofp_port = ofp_port;
-    iface->requested_ofp_port = iface_get_requested_ofp_port(iface_cfg);
     iface->netdev = netdev;
     iface->type = iface_get_type(iface_cfg, br->cfg);
     iface->cfg = iface_cfg;

> > static ofp_port_t
> > -iface_pick_ofport(const struct ovsrec_interface *cfg)
> > +iface_validate_ofport__(size_t n, int64_t *ofport)
> > +{
> > +    return (n && *ofport >= 1 && *ofport < OFPP_MAX
> > +            ? u16_to_ofp(*ofport)
> > +            : OFPP_NONE);
> 
> Are the outer parenthesis required?

Only by the style guide:

      Do parenthesize a subexpression that must be split across more than
    one line, e.g.:

        *idxp = ((l1_idx << PORT_ARRAY_L1_SHIFT)
                 | (l2_idx << PORT_ARRAY_L2_SHIFT)
                 | (l3_idx << PORT_ARRAY_L3_SHIFT));

In turn that was inspired by this advice from the GNU coding standards,
but I rewrote it to be editor agnostic:

       Insert extra parentheses so that Emacs will indent the code properly.
    For example, the following indentation looks nice if you do it by hand,

         v = rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000
             + rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000;

    but Emacs would alter it.  Adding a set of parentheses produces
    something that looks equally nice, and which Emacs will preserve:

         v = (rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000
              + rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000);

> It would be nice to have test cases that validate the above behaviors. 

I've now written one, and I'll fold it in:

AT_SETUP([ofproto - ofport_request])
OVS_VSWITCHD_START
ADD_OF_PORTS([br0], [1], [2], [3])

set_and_check_specific_ofports () {
    ovs-vsctl set Interface p1 ofport_request="$1" -- \
	      set Interface p2 ofport_request="$2" -- \
	      set Interface p3 ofport_request="$3"
    ofports=`ovs-vsctl get Interface p1 ofport -- \
		       get Interface p2 ofport -- \
		       get Interface p3 ofport`
    AT_CHECK_UNQUOTED([echo $ofports], [0], [$1 $2 $3
])
}
for pre in      '1 2 3' '1 3 2' '2 1 3' '2 3 1' '3 1 2' '3 2 1'; do
    for post in '1 2 3' '1 3 2' '2 1 3' '2 3 1' '3 1 2' '3 2 1'; do
        echo -----------------------------------------------------------
        echo "Check changing port numbers from $pre to $post"
	set_and_check_ofports $pre
	set_and_check_ofports $post
    done
done

ovs-vsctl del-port p3

set_and_check_poorly_specified_ofports () {
    ovs-vsctl set Interface p1 ofport_request="$1" -- \
	      set Interface p2 ofport_request="$2"
    p1=`ovs-vsctl get Interface p1 ofport`
    p2=`ovs-vsctl get Interface p2 ofport`
    echo $p1 $p2

    AT_CHECK([test "$p1" != "$p2"])
    if test "$1" = "$2" && test "$1" != '[[]]'; then
        # One port number must be the requested one.
	AT_CHECK([test "$p1" = "$1" || test "$p2" = "$1"])
	# The other port number must be different (already tested above).
    else
        AT_CHECK([test "$1" = '[[]]' || test "$p1" == "$1"])
        AT_CHECK([test "$2" = '[[]]' || test "$p2" == "$2"])
    fi
}
for pre in      '1 2' '[[]] 2' '1 [[]]' '[[]] [[]]' '2 1' '[[]] 1' '2 [[]]' \
                '1 1' '2 2'; do
    for post in '1 2' '[[]] 2' '1 [[]]' '[[]] [[]]' '2 1' '[[]] 1' '2 [[]]' \
                '1 1' '2 2'; do
        echo -----------------------------------------------------------
        echo "Check changing port numbers from $pre to $post"
        set_and_check_poorly_specified_ofports $pre
        set_and_check_poorly_specified_ofports $post
    done
done
OVS_VSWITCHD_STOP
AT_CLEANUP

I have a feeling that we will uncover some more bugs here, because this
code is some of the thorniest in OVS.  I'm going to push this in a
minute anyway.  We can fix the bugs as they are reported, and I hope to
add tests for them this time.



More information about the dev mailing list