> -----Original Message-----
> From: Ilya Maximets <[email protected]>
> Sent: Friday 4 February 2022 12:11
> To: Ferriter, Cian <[email protected]>; [email protected]
> Cc: [email protected]; Van Haaren, Harry <[email protected]>
> Subject: Re: [PATCH] dpif-netdev-dpcls: Make subtable reprobe thread-safe.
> 
> On 2/2/22 15:51, Cian Ferriter wrote:
> > The subtable search function can be used at any time by a PMD thread.
> > Setting the subtable search function should be done atomically to
> > prevent garbage data from being read.
> >
> > A dpcls_subtable_lookup_reprobe() call can happen at the same time that
> > DPCLS subtables are being sorted. This could lead to modifications done
> > by the reprobe() to be lost. Prevent this from happening by locking on
> > pmd->flow_mutex. After this change both the reprobe function and a
> > subtable sort will share the flow_mutex preventing modifications by
> > either one from being lost.
> >
> > Fixes: 3d018c3ea79d ("dpif-netdev: add subtable lookup prio set command.")
> > Reported-by: Ilya Maximets <[email protected]>
> > Reported-at: 
> > https://mail.openvswitch.org/pipermail/ovs-dev/2022-January/390757.html
> > Signed-off-by: Cian Ferriter <[email protected]>
> 
> Hi, Cian.  Thanks for working on this!
> I didn't test that, but see some comments inline.
> 
> Best regards, Ilya Maximets.
> 

Hi Ilya, thanks for the review. The explanations around pvector_publish were 
very helpful!

I sent a new version of the patch:
http://patchwork.ozlabs.org/project/openvswitch/patch/[email protected]/

> > ---
> >  lib/dpif-netdev-private-dpcls.h |  2 +-
> >  lib/dpif-netdev.c               | 23 +++++++++++++++++++----
> >  2 files changed, 20 insertions(+), 5 deletions(-)
> >
> > diff --git a/lib/dpif-netdev-private-dpcls.h 
> > b/lib/dpif-netdev-private-dpcls.h
> > index 7c4a840cb..1b4d69605 100644
> > --- a/lib/dpif-netdev-private-dpcls.h
> > +++ b/lib/dpif-netdev-private-dpcls.h
> > @@ -84,7 +84,7 @@ struct dpcls_subtable {
> >       * property of the subtable (eg: only 3 bits of miniflow metadata is
> >       * used for the lookup) then this can point at an optimized version of
> >       * the lookup function for this particular subtable. */
> > -    dpcls_subtable_lookup_func lookup_func;
> > +    ATOMIC(dpcls_subtable_lookup_func) lookup_func;
> 
> Maybe a comment update saying why it's atomic?
> 

Good idea, it's good to document the reason.

Updated this in the v2.

> >
> >      /* Caches the masks to match a packet to, reducing runtime 
> > calculations. */
> >      uint64_t *mf_masks;
> > diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> > index e28e0b554..9ca6d9842 100644
> > --- a/lib/dpif-netdev.c
> > +++ b/lib/dpif-netdev.c
> > @@ -1074,7 +1074,9 @@ dpif_netdev_subtable_lookup_set(struct unixctl_conn 
> > *conn, int argc
> OVS_UNUSED,
> >                  if (!cls) {
> >                      continue;
> >                  }
> > +                ovs_mutex_lock(&pmd->flow_mutex);
> >                  uint32_t subtbl_changes = 
> > dpcls_subtable_lookup_reprobe(cls);
> > +                ovs_mutex_unlock(&pmd->flow_mutex);
> >                  if (subtbl_changes) {
> >                      lookup_dpcls_changed++;
> >                      lookup_subtable_changed += subtbl_changes;
> > @@ -9736,9 +9738,14 @@ dpcls_create_subtable(struct dpcls *cls, const 
> > struct netdev_flow_key *mask)
> >
> >      /* Get the preferred subtable search function for this (u0,u1) 
> > subtable.
> >       * The function is guaranteed to always return a valid implementation, 
> > and
> > -     * possibly an ISA optimized, and/or specialized implementation.
> > +     * possibly an ISA optimized, and/or specialized implementation. 
> > Initialize
> > +     * the subtable search function atomically.
> 
> Again, the comment 'why?' would be helpful.  OTOH, the comment in the 
> structure
> itself might be enough.
> 

I think you're right. The WHY is more important than me just saying WHAT we are 
doing.

Hopefully the code says WHAT we are doing, and we say WHY! :)

Updated this in the v2.

> >       */
> > -    subtable->lookup_func = dpcls_subtable_get_best_impl(unit0, unit1);
> > +    dpcls_subtable_lookup_func best_func = 
> > dpcls_subtable_get_best_impl(unit0,
> > +                                                                        
> > unit1);
> > +    atomic_uintptr_t *subtable_func = (void *) &subtable->lookup_func;
> > +    atomic_init(subtable_func, (uintptr_t) best_func);
> 
> This pattern looks a bit weird, but for some reason it's used across 
> avx512-related
> parts of the code.  Something like this should work just fine:
> 
>     atomic_init(&subtable->lookup_func,
>                 dpcls_subtable_get_best_impl(unit0, unit1));
> 

Above works perfectly. I'm using your line in v2. Feel free to add your 
Co-authored-by if that makes sense/is appropriate.

I started the weird looking pattern when making the DPIF implementation 
function pointer (netdev_input_func) atomic. I remember trying to simplify the 
pattern, but not quite getting what I wanted.
I'll go back and check if the other atomic function pointer code can be 
simplified to look like what you suggested.

> > +
> 
> Extra empty line is not needed.
> 

Ooops, fixed in v2.

> >
> >      cmap_insert(&cls->subtables_map, &subtable->cmap_node, mask->hash);
> >      /* Add the new subtable at the end of the pvector (with no hits yet) */
> > @@ -9779,10 +9786,18 @@ dpcls_subtable_lookup_reprobe(struct dpcls *cls)
> >          uint32_t u0_bits = subtable->mf_bits_set_unit0;
> >          uint32_t u1_bits = subtable->mf_bits_set_unit1;
> >          void *old_func = subtable->lookup_func;
> > -        subtable->lookup_func = dpcls_subtable_get_best_impl(u0_bits, 
> > u1_bits);
> > +
> > +        /* Set the best impl to the subtable lookup function atomically. */
> 
> This comment doesn't seem very useful.
> 

Agreed, updated in the next version to explain why we are setting atomically.

> > +        dpcls_subtable_lookup_func best_func =
> > +            dpcls_subtable_get_best_impl(u0_bits, u1_bits);
> > +        atomic_uintptr_t *subtable_func = (void *) 
> > &subtable->lookup_func;> +
> atomic_store_relaxed(subtable_func, (uintptr_t) best_func);
> 
> Same here.
> 
>         atomic_store_relaxed(&subtable->lookup_func,
>                              dpcls_subtable_get_best_impl(u0_bits, u1_bits));
> 

Fixed this to use your suggested code in the v2.

> > +
> 
> Please, add an empty line between declarations of variables and the rest of
> the code instead.
> 

Will do in the v2.

> >          subtables_changed += (old_func != subtable->lookup_func);
> >      }
> > -    pvector_publish(pvec);
> > +    if (subtables_changed) {
> > +        pvector_publish(pvec);
> 
> This still does nothing useful.  It's harmless, because we're under the
> flow_mutex now, but it does nothing, because we're still not modifying
> the vector, we're modifying the data to which pointers in the vector are
> pointing to.
> 
> In the ideal world the vector and data it points to should be immutable.
> So, we'll need to clone the implementation of the vector, remove and destroy
> all the elements we would like to change, create modified versions and
> add them to the vector.  After that we can publish it.  In this scenario
> everything will be protected by rcu and no atomics required.  But that
> sounds like a waste of time for this particular use case, so we're
> creating a workaround by using atomics and modifying the data while other
> threads are using it.  But we're not changing the vector itself.
> 

Thanks for the detailed explanation, this was really helpful! After reading the 
above and looking at the other uses of pvector_publish, this makes more sense 
to me. I'll remove the pvector_publish for v2.

> Would be nice to have a comment though, why this function requires holding
> a flow_mutex.
> 

I'll add to the comment about this function in the v2.

> > +    }
> >
> >      return subtables_changed;
> >  }

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to