On Sep 8, 2014, at 5:06 PM, Ben Pfaff <b...@nicira.com> wrote:

> On Fri, Sep 05, 2014 at 04:05:13PM -0700, Jarno Rajahalme wrote:
>> When a whole field of a key value is ignored, skip it when formatting
>> the key, and allow it to be left out when parsing the key from a
>> string.  However, when the unmasked bits have non-zero values (as in
>> keys received from a datapath), or when the 'verbose' formatting is
>> requested those are still formatted, as it may help in debugging.
>> 
>> Now the named key fields can also be given in arbitrary order.
>> Duplicate field values are not checked for, so the last one will
>> remain in effect.
>> 
>> Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
> 
> This makes the formatting and parsing code less disastery.  Thank you.
> 

(snip)

> There's still some nastiness around the difference between an
> all-one-bits mask and a null mask.  It takes some real care to read
> the code to see that it is correct, and I'm sure that it took at least
> as much care to write it.  Can we do something about that?  One way
> would be to always supply a mask, one that is all-one-bits if there
> would otherwise be no mask.  That could be done externally to the
> format_*() functions, or it could be internally.
> 
> Here's one approach for the internal version:
> 
>    static void
>    format_eth(struct ds *ds, const char *name, const uint8_t 
> key[ETH_ADDR_LEN],
>               const uint8_t (*mask_)[ETH_ADDR_LEN], bool verbose)
>    {
>        const uint8_t *mask = mask_ ? *mask_ : eth_addr_broadcast;
>        if (verbose || !eth_addr_is_zero(key) || !eth_addr_is_zero(mask)) {
>            if (!eth_mask_is_exact(mask)) { /* Partially masked. */
>                ds_put_format(ds, "%s=", name);
>                eth_format_masked(key, mask, ds);
>                ds_put_char(ds, ',');
>            } else { /* Fully masked. */
>                ds_put_format(ds, "%s="ETH_ADDR_FMT",", name, 
> ETH_ADDR_ARGS(key));
>            }
>        }
>    }
> 
> The external version would be more like:
> 
>    const uint8_t big_all_one_bits[] = { [ 0...255 ] = 0xff };
>    #define MASK(PTR, FIELD) \
>            (OVS_TYPEOF(&PTR->FIELD)) (PTR ? &PTR->FIELD : (const void *) 
> big_all_one_bits)
> 
> though I see some defects in that already.
> 

How about this variation (I realized that ‘verbose’ covers the case when the 
key includes non-masked bits):

diff --git a/lib/odp-util.c b/lib/odp-util.c
index b5c8c2b..ea724da 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -1251,14 +1251,17 @@ static void
 format_eth(struct ds *ds, const char *name, const uint8_t key[ETH_ADDR_LEN],
            const uint8_t (*mask)[ETH_ADDR_LEN], bool verbose)
 {
-    if (verbose || !eth_addr_is_zero(key)
-        || !mask || !eth_addr_is_zero(*mask)) {
-        if (mask && !eth_mask_is_exact(*mask)) { /* Partially masked. */
+    bool mask_empty = mask && eth_addr_is_zero(*mask);
+
+    if (verbose || !mask_empty) {
+        bool mask_full = !mask || eth_mask_is_exact(*mask);
+
+        if (mask_full) {
+            ds_put_format(ds, "%s="ETH_ADDR_FMT",", name, ETH_ADDR_ARGS(key));
+        } else {
             ds_put_format(ds, "%s=", name);
             eth_format_masked(key, *mask, ds);
             ds_put_char(ds, ',');
-        } else { /* Fully masked. */
-            ds_put_format(ds, "%s="ETH_ADDR_FMT",", name, ETH_ADDR_ARGS(key));
         }
     }
 }

(I’ll wait for your response before changing them all…)

> In format_ipv6_label(), I personally find that
>    if (mask && (~*mask & htonl(IPV6_LABEL_MASK))) { /* Partially masked. */
> is easier to understand written as:
>    if (mask && (*mask & htonl(~IPV6_LABEL_MASK))) { /* Partially masked. */
> or
>    if (mask && (*mask & ~htonl(IPV6_LABEL_MASK))) { /* Partially masked. */
> Similarly format_tun_flags().
> 

> In format_u8x(), I personally find that:
>        if (mask && ~*mask & UINT8_MAX) { /* Partially masked. */
> is easier to understand written as:
>        if (mask && *mask != UINT8_MAX) { /* Partially masked. */
> Similarly in format_u8u(), format_be16(), format_frag().
> 

Will do.

> format_frag() is kind of funny because masking doesn't really make
> sense there; individual bits aren't too meaningful.  I guess what
> you've done is about as good as we can get.
> 

I learned about that later after I had done that. Since an enum value is not 
partially maskable we should always print it without a mask (or not at all when 
the mask is zero).

> There may be some common ground with mf_format() and mf_parse() in
> lib/meta-flow.c.
> 

I’ll have a look.

> On scan_eth(), the 's' parameter is marked OVS_UNUSED, though it is
> used.
> 
> I don't recommend marking scan_tcp_flags() inline, because that will
> suppress a "function unused" warning if it ever becomes unused.
> 

These are remnants from debugging. I actually used the inline keyword to 
suppress the “function unused” warning!

> scan_tcp_flags() should probably pass NULL as the mask to
> parse_flags() if its own 'mask' argument is NULL.
> 

Will check.

> In *_bf(), what does "bf" stand for?
> 

“bitfield”, did not want to make the name longer…

> 
> I noticed that a lot of the macros could better protect their
> arguments, e.g. in the definition of SCAN_PUT_ATTR, parentheses around
> ATTR here:
> +    if (ATTR == OVS_KEY_ATTR_TUNNEL) {                          \
> and around DATA here:
> +        tun_key_to_attr(BUF, (const struct flow_tnl *)(void *)&DATA); \
> +    } else {                                                    \
> and ATTR and DATA (twice) here:
> +        nl_msg_put_unspec(BUF, ATTR, &DATA, sizeof DATA);       \
> +    }
> 

Will fix.

> I am not sure whether this is the approach that I would have ended up
> with, but whether yes or no, it is a huge improvement in
> maintainability.  Thank you.
> 

Thanks for the review. I’ll wait for your response on the first point before 
pushing this.

  Jarno

> Acked-by: Ben Pfaff <b...@nicira.com>

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to